1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include "htmlreqifreader.hxx"
12 #include <comphelper/scopeguard.hxx>
13 #include <filter/msfilter/rtfutil.hxx>
14 #include <rtl/strbuf.hxx>
15 #include <sot/storage.hxx>
16 #include <svtools/parrtf.hxx>
17 #include <svtools/rtfkeywd.hxx>
18 #include <svtools/rtftoken.h>
19 #include <tools/stream.hxx>
20 #include <filter/msfilter/msdffimp.hxx>
21 #include <vcl/cvtgrf.hxx>
23 #include <sal/log.hxx>
24 #include <vcl/FilterConfigItem.hxx>
25 #include <vcl/wmf.hxx>
26 #include <comphelper/propertyvalue.hxx>
27 #include <fmtfsize.hxx>
30 using namespace com::sun::star
;
34 /// RTF parser that just extracts a single OLE2 object from a file.
35 class ReqIfRtfReader
: public SvRTFParser
38 ReqIfRtfReader(SvStream
& rStream
);
39 void NextToken(int nToken
) override
;
40 bool WriteObjectData(SvStream
& rOLE
);
43 bool m_bInObjData
= false;
47 ReqIfRtfReader::ReqIfRtfReader(SvStream
& rStream
)
48 : SvRTFParser(rStream
)
52 void ReqIfRtfReader::NextToken(int nToken
)
61 m_aHex
.append(OUStringToOString(aToken
, RTL_TEXTENCODING_ASCII_US
));
69 bool ReqIfRtfReader::WriteObjectData(SvStream
& rOLE
)
71 return msfilter::rtfutil::ExtractOLE2FromObjdata(m_aHex
.makeStringAndClear(), rOLE
);
74 /// Looks up what OLE1 calls the ClassName, see [MS-OLEDS] 2.3.8 CompObjStream.
75 OString
ExtractOLEClassName(const rtl::Reference
<SotStorage
>& xStorage
)
79 rtl::Reference
<SotStorageStream
> pCompObj
= xStorage
->OpenSotStream(u
"\1CompObj"_ustr
);
84 pCompObj
->SeekRel(28); // Header
85 if (!pCompObj
->good())
89 pCompObj
->ReadUInt32(nData
); // AnsiUserType
90 pCompObj
->SeekRel(nData
);
91 if (!pCompObj
->good())
94 pCompObj
->ReadUInt32(nData
); // AnsiClipboardFormat
95 pCompObj
->SeekRel(nData
);
96 if (!pCompObj
->good())
99 pCompObj
->ReadUInt32(nData
); // Reserved1
100 return read_uInt8s_ToOString(*pCompObj
, nData
- 1); // -1 because it is null-terminated
103 /// Parses the presentation stream of an OLE2 storage.
104 bool ParseOLE2Presentation(SvStream
& rOle2
, sal_uInt32
& nWidth
, sal_uInt32
& nHeight
,
105 SvStream
& rPresentationData
)
107 // See [MS-OLEDS] 2.3.4, OLEPresentationStream
109 rtl::Reference
<SotStorage
> pStorage
= new SotStorage(rOle2
);
110 rtl::Reference
<SotStorageStream
> xOle2Presentation
111 = pStorage
->OpenSotStream(u
"\002OlePres000"_ustr
, StreamMode::STD_READ
);
113 // Read AnsiClipboardFormat.
114 sal_uInt32 nMarkerOrLength
= 0;
115 xOle2Presentation
->ReadUInt32(nMarkerOrLength
);
116 if (nMarkerOrLength
!= 0xffffffff)
117 // FormatOrAnsiString is not present
119 sal_uInt32 nFormatOrAnsiLength
= 0;
120 xOle2Presentation
->ReadUInt32(nFormatOrAnsiLength
);
121 if (nFormatOrAnsiLength
!= 0x00000003) // CF_METAFILEPICT
124 // Read TargetDeviceSize.
125 sal_uInt32 nTargetDeviceSize
= 0;
126 xOle2Presentation
->ReadUInt32(nTargetDeviceSize
);
127 if (nTargetDeviceSize
!= 0x00000004)
128 // TargetDevice is present
131 sal_uInt32 nAspect
= 0;
132 xOle2Presentation
->ReadUInt32(nAspect
);
133 sal_uInt32 nLindex
= 0;
134 xOle2Presentation
->ReadUInt32(nLindex
);
135 sal_uInt32 nAdvf
= 0;
136 xOle2Presentation
->ReadUInt32(nAdvf
);
137 sal_uInt32 nReserved1
= 0;
138 xOle2Presentation
->ReadUInt32(nReserved1
);
139 xOle2Presentation
->ReadUInt32(nWidth
);
140 xOle2Presentation
->ReadUInt32(nHeight
);
141 sal_uInt32 nSize
= 0;
142 xOle2Presentation
->ReadUInt32(nSize
);
145 if (nSize
> xOle2Presentation
->remainingSize())
151 "ParseOLE2Presentation: ignoring potentially broken small preview: size is "
156 std::vector
<char> aBuffer(nSize
);
157 xOle2Presentation
->ReadBytes(aBuffer
.data(), aBuffer
.size());
158 rPresentationData
.WriteBytes(aBuffer
.data(), aBuffer
.size());
164 * Inserts an OLE1 header before an OLE2 storage, assuming that the storage has an Ole10Native
167 OString
InsertOLE1HeaderFromOle10NativeStream(const rtl::Reference
<SotStorage
>& xStorage
,
168 SwOLENode
& rOLENode
, SvStream
& rOle1
)
170 rtl::Reference
<SotStorageStream
> xOle1Stream
171 = xStorage
->OpenSotStream(u
"\1Ole10Native"_ustr
, StreamMode::STD_READ
);
172 sal_uInt32 nOle1Size
= 0;
173 xOle1Stream
->ReadUInt32(nOle1Size
);
176 if (xStorage
->GetClassName() == SvGlobalName(0x0003000A, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46))
178 aClassName
= "PBrush"_ostr
;
182 if (xStorage
->GetClassName()
183 != SvGlobalName(0x0003000C, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46))
185 SAL_WARN("sw.html", "InsertOLE1HeaderFromOle10NativeStream: unexpected class id: "
186 << xStorage
->GetClassName().GetHexName());
188 aClassName
= "Package"_ostr
;
191 // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
194 rOle1
.WriteUInt32(0x00000501);
196 // FormatID is EmbeddedObject.
197 rOle1
.WriteUInt32(0x00000002);
200 rOle1
.WriteUInt32(aClassName
.isEmpty() ? 0 : aClassName
.getLength() + 1);
201 if (!aClassName
.isEmpty())
203 rOle1
.WriteOString(aClassName
);
204 // Null terminated pascal string.
209 rOle1
.WriteUInt32(0);
212 rOle1
.WriteUInt32(0);
215 rOle1
.WriteUInt32(nOle1Size
);
217 // Write the actual native data.
218 rOle1
.WriteStream(*xOle1Stream
, nOle1Size
);
220 // Write Presentation.
221 if (!rOLENode
.GetGraphic())
226 const Graphic
* pGraphic
= rOLENode
.GetGraphic();
227 const Graphic rGraphic
= pGraphic
? *pGraphic
: Graphic();
228 Size aSize
= rOLENode
.GetTwipSize();
229 SvMemoryStream aGraphicStream
;
230 if (GraphicConverter::Export(aGraphicStream
, rGraphic
, ConvertDataFormat::WMF
) != ERRCODE_NONE
)
235 auto pGraphicAry
= static_cast<const sal_uInt8
*>(aGraphicStream
.GetData());
236 sal_uInt64 nPresentationData
= aGraphicStream
.TellEnd();
237 msfilter::rtfutil::StripMetafileHeader(pGraphicAry
, nPresentationData
);
240 rOle1
.WriteUInt32(0x00000501);
241 // FormatID: constant means the ClassName field is present.
242 rOle1
.WriteUInt32(0x00000005);
243 // ClassName: null terminated pascal string.
244 OString
aPresentationClassName("METAFILEPICT"_ostr
);
245 rOle1
.WriteUInt32(aPresentationClassName
.getLength() + 1);
246 rOle1
.WriteOString(aPresentationClassName
);
249 rOle1
.WriteUInt32(aSize
.getWidth());
251 rOle1
.WriteUInt32(aSize
.getHeight() * -1);
252 // PresentationDataSize
253 rOle1
.WriteUInt32(8 + nPresentationData
);
255 rOle1
.WriteUInt16(0x0008);
256 rOle1
.WriteUInt16(0x31b1);
257 rOle1
.WriteUInt16(0x1dd9);
258 rOle1
.WriteUInt16(0x0000);
259 rOle1
.WriteBytes(pGraphicAry
, nPresentationData
);
265 * Writes an OLE1 header and data from rOle2 to rOle1.
267 * In case rOle2 has presentation data, then its size is written to nWidth/nHeight. Otherwise
268 * nWidth/nHeight/pPresentationData/nPresentationData is used for the presentation data.
270 OString
InsertOLE1Header(SvStream
& rOle2
, SvStream
& rOle1
, sal_uInt32
& nWidth
, sal_uInt32
& nHeight
,
271 SwOLENode
& rOLENode
, const sal_uInt8
* pPresentationData
,
272 sal_uInt64 nPresentationData
)
275 rtl::Reference
<SotStorage
> xStorage(new SotStorage(rOle2
));
276 if (xStorage
->GetError() != ERRCODE_NONE
)
279 if (xStorage
->IsStream(u
"\1Ole10Native"_ustr
))
281 return InsertOLE1HeaderFromOle10NativeStream(xStorage
, rOLENode
, rOle1
);
284 OString aClassName
= ExtractOLEClassName(xStorage
);
286 // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
289 rOle1
.WriteUInt32(0x00000501);
291 // FormatID is EmbeddedObject.
292 rOle1
.WriteUInt32(0x00000002);
295 rOle1
.WriteUInt32(aClassName
.isEmpty() ? 0 : aClassName
.getLength() + 1);
296 if (!aClassName
.isEmpty())
298 rOle1
.WriteOString(aClassName
);
299 // Null terminated pascal string.
304 rOle1
.WriteUInt32(0);
307 rOle1
.WriteUInt32(0);
310 rOle1
.WriteUInt32(rOle2
.TellEnd());
312 // Write the actual native data.
314 rOle1
.WriteStream(rOle2
);
316 // Write Presentation.
317 SvMemoryStream aPresentationData
;
319 rOle1
.WriteUInt32(0x00000501);
320 // FormatID: constant means the ClassName field is present.
321 rOle1
.WriteUInt32(0x00000005);
322 // ClassName: null terminated pascal string.
323 OString
aPresentationClassName("METAFILEPICT"_ostr
);
324 rOle1
.WriteUInt32(aPresentationClassName
.getLength() + 1);
325 rOle1
.WriteOString(aPresentationClassName
);
327 const sal_uInt8
* pBytes
= nullptr;
328 sal_uInt64 nBytes
= 0;
329 if (ParseOLE2Presentation(rOle2
, nWidth
, nHeight
, aPresentationData
))
331 // Take presentation data for OLE1 from OLE2.
332 pBytes
= static_cast<const sal_uInt8
*>(aPresentationData
.GetData());
333 nBytes
= aPresentationData
.Tell();
337 // Take presentation data for OLE1 from RTF.
338 pBytes
= pPresentationData
;
339 nBytes
= nPresentationData
;
342 rOle1
.WriteUInt32(nWidth
);
344 rOle1
.WriteUInt32(nHeight
* -1);
345 // PresentationDataSize: size of (reserved fields + pBytes).
346 rOle1
.WriteUInt32(8 + nBytes
);
348 rOle1
.WriteUInt16(0x0008);
349 rOle1
.WriteUInt16(0x31b1);
350 rOle1
.WriteUInt16(0x1dd9);
351 rOle1
.WriteUInt16(0x0000);
352 rOle1
.WriteBytes(pBytes
, nBytes
);
357 /// Writes presentation data with the specified size to rRtf as an RTF hexdump.
358 void WrapOleGraphicInRtf(SvStream
& rRtf
, sal_uInt32 nWidth
, sal_uInt32 nHeight
,
359 const sal_uInt8
* pPresentationData
, sal_uInt64 nPresentationData
)
362 rRtf
.WriteOString("{" OOO_STRING_SVTOOLS_RTF_RESULT
);
365 rRtf
.WriteOString("{" OOO_STRING_SVTOOLS_RTF_PICT
);
367 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_WMETAFILE
"8");
368 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_PICW
);
369 rRtf
.WriteOString(OString::number(nWidth
));
370 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_PICH
);
371 rRtf
.WriteOString(OString::number(nHeight
));
372 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_PICWGOAL
);
373 rRtf
.WriteOString(OString::number(nWidth
));
374 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_PICHGOAL
);
375 rRtf
.WriteOString(OString::number(nHeight
));
376 if (pPresentationData
)
378 rRtf
.WriteOString(SAL_NEWLINE_STRING
);
379 msfilter::rtfutil::WriteHex(pPresentationData
, nPresentationData
, &rRtf
);
383 rRtf
.WriteOString("}");
386 rRtf
.WriteOString("}");
390 namespace SwReqIfReader
392 bool ExtractOleFromRtf(SvStream
& rRtf
, SvStream
& rOle
, bool& bOwnFormat
)
394 // Add missing header/footer.
396 aRtf
.WriteOString("{\\rtf1");
397 aRtf
.WriteStream(rRtf
);
398 aRtf
.WriteOString("}");
401 // Read the RTF markup.
402 tools::SvRef
<ReqIfRtfReader
> xReader(new ReqIfRtfReader(aRtf
));
403 SvParserState eState
= xReader
->CallParser();
404 if (eState
== SvParserState::Error
)
407 // Write the OLE2 data.
408 if (!xReader
->WriteObjectData(rOle
))
411 rtl::Reference
<SotStorage
> pStorage
= new SotStorage(rOle
);
412 OUString aFilterName
= SvxMSDffManager::GetFilterNameFromClassID(pStorage
->GetClassName());
413 bOwnFormat
= !aFilterName
.isEmpty();
416 // Real OLE2 data, we're done.
421 // ODF-in-OLE2 case, extract actual data.
422 SvMemoryStream aMemory
;
423 SvxMSDffManager::ExtractOwnStream(*pStorage
, aMemory
);
426 rOle
.WriteStream(aMemory
);
427 // Stream length is current position + 1.
428 rOle
.SetStreamSize(aMemory
.GetSize() + 1);
433 bool WrapOleInRtf(SvStream
& rOle2
, SvStream
& rRtf
, SwOLENode
& rOLENode
,
434 const SwFrameFormat
& rFormat
)
436 sal_uInt64 nPos
= rOle2
.Tell();
437 comphelper::ScopeGuard
g([&rOle2
, nPos
] { rOle2
.Seek(nPos
); });
439 // Write OLE1 header, then the RTF wrapper.
440 SvMemoryStream aOLE1
;
442 // Prepare presentation data early, so it's available to both OLE1 and RTF.
443 Size aSize
= rFormat
.GetFrameSize().GetSize();
444 sal_uInt32 nWidth
= aSize
.getWidth();
445 sal_uInt32 nHeight
= aSize
.getHeight();
446 const Graphic
* pGraphic
= rOLENode
.GetGraphic();
447 const sal_uInt8
* pPresentationData
= nullptr;
448 sal_uInt64 nPresentationData
= 0;
449 SvMemoryStream aGraphicStream
;
452 uno::Sequence
<beans::PropertyValue
> aFilterData
453 = { comphelper::makePropertyValue(u
"EmbedEMF"_ustr
, false) };
454 FilterConfigItem
aConfigItem(&aFilterData
);
455 if (ConvertGraphicToWMF(*pGraphic
, aGraphicStream
, &aConfigItem
))
457 pPresentationData
= static_cast<const sal_uInt8
*>(aGraphicStream
.GetData());
458 nPresentationData
= aGraphicStream
.TellEnd();
459 msfilter::rtfutil::StripMetafileHeader(pPresentationData
, nPresentationData
);
462 OString aClassName
= InsertOLE1Header(rOle2
, aOLE1
, nWidth
, nHeight
, rOLENode
,
463 pPresentationData
, nPresentationData
);
466 rRtf
.WriteOString("{" OOO_STRING_SVTOOLS_RTF_OBJECT
);
467 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJEMB
);
470 rRtf
.WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJCLASS
" ");
471 rRtf
.WriteOString(aClassName
);
473 rRtf
.WriteOString("}");
476 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJW
);
477 rRtf
.WriteOString(OString::number(nWidth
));
478 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJH
);
479 rRtf
.WriteOString(OString::number(nHeight
));
483 "{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJDATA SAL_NEWLINE_STRING
);
484 msfilter::rtfutil::WriteHex(static_cast<const sal_uInt8
*>(aOLE1
.GetData()), aOLE1
.GetSize(),
487 rRtf
.WriteOString("}");
489 if (pPresentationData
)
491 WrapOleGraphicInRtf(rRtf
, nWidth
, nHeight
, pPresentationData
, nPresentationData
);
495 rRtf
.WriteOString("}");
500 bool WrapGraphicInRtf(const Graphic
& rGraphic
, const SwFrameFormat
& rFormat
, SvStream
& rRtf
)
503 rRtf
.WriteOString("{" OOO_STRING_SVTOOLS_RTF_OBJECT
);
504 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJEMB
);
506 // Object size: as used in the document model (not pixel size)
507 Size aSize
= rFormat
.GetFrameSize().GetSize();
508 sal_uInt32 nWidth
= aSize
.getWidth();
509 sal_uInt32 nHeight
= aSize
.getHeight();
510 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJW
);
511 rRtf
.WriteOString(OString::number(nWidth
));
512 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_OBJH
);
513 rRtf
.WriteOString(OString::number(nHeight
));
514 rRtf
.WriteOString(SAL_NEWLINE_STRING
);
517 rRtf
.WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJCLASS
" ");
518 OString
aClassName("PBrush"_ostr
);
519 rRtf
.WriteOString(aClassName
);
521 rRtf
.WriteOString("}");
522 rRtf
.WriteOString(SAL_NEWLINE_STRING
);
525 rRtf
.WriteOString("{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_OBJDATA
" ");
527 SvMemoryStream aOle1
;
528 // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
530 aOle1
.WriteUInt32(0x00000501);
532 // FormatID is EmbeddedObject.
533 aOle1
.WriteUInt32(0x00000002);
536 aOle1
.WriteUInt32(aClassName
.getLength() + 1);
537 aOle1
.WriteOString(aClassName
);
538 // Null terminated pascal string.
542 aOle1
.WriteUInt32(0);
545 aOle1
.WriteUInt32(0);
548 SvMemoryStream aNativeData
;
550 // Set white background for the semi-transparent pixels.
551 BitmapEx aBitmapEx
= rGraphic
.GetBitmapEx();
552 Bitmap aBitmap
= aBitmapEx
.GetBitmap(/*aTransparentReplaceColor=*/COL_WHITE
);
554 if (aBitmap
.getPixelFormat() != vcl::PixelFormat::N24_BPP
)
556 // More exotic pixel formats cause trouble for ms paint.
557 aBitmap
.Convert(BmpConversion::N24Bit
);
560 if (GraphicConverter::Export(aNativeData
, BitmapEx(aBitmap
), ConvertDataFormat::BMP
)
563 SAL_WARN("sw.html", "WrapGraphicInRtf: bmp conversion failed");
565 aOle1
.WriteUInt32(aNativeData
.TellEnd());
567 // Write the actual native data.
569 aOle1
.WriteStream(aNativeData
);
571 // Prepare presentation data.
572 const sal_uInt8
* pPresentationData
= nullptr;
573 sal_uInt64 nPresentationData
= 0;
574 SvMemoryStream aGraphicStream
;
575 uno::Sequence
<beans::PropertyValue
> aFilterData
576 = { comphelper::makePropertyValue(u
"EmbedEMF"_ustr
, false) };
577 FilterConfigItem
aConfigItem(&aFilterData
);
578 if (ConvertGraphicToWMF(rGraphic
, aGraphicStream
, &aConfigItem
))
580 pPresentationData
= static_cast<const sal_uInt8
*>(aGraphicStream
.GetData());
581 nPresentationData
= aGraphicStream
.TellEnd();
582 msfilter::rtfutil::StripMetafileHeader(pPresentationData
, nPresentationData
);
585 // Write Presentation.
587 aOle1
.WriteUInt32(0x00000501);
588 // FormatID: constant means the ClassName field is present.
589 aOle1
.WriteUInt32(0x00000005);
590 // ClassName: null terminated pascal string.
591 OString
aPresentationClassName("METAFILEPICT"_ostr
);
592 aOle1
.WriteUInt32(aPresentationClassName
.getLength() + 1);
593 aOle1
.WriteOString(aPresentationClassName
);
595 const sal_uInt8
* pBytes
= nullptr;
596 sal_uInt64 nBytes
= 0;
597 // Take presentation data for OLE1 from RTF.
598 pBytes
= pPresentationData
;
599 nBytes
= nPresentationData
;
601 aOle1
.WriteUInt32(nWidth
);
603 aOle1
.WriteUInt32(nHeight
* -1);
604 // PresentationDataSize: size of (reserved fields + pBytes).
605 aOle1
.WriteUInt32(8 + nBytes
);
607 aOle1
.WriteUInt16(0x0008);
608 aOle1
.WriteUInt16(0x31b1);
609 aOle1
.WriteUInt16(0x1dd9);
610 aOle1
.WriteUInt16(0x0000);
611 aOle1
.WriteBytes(pBytes
, nBytes
);
614 msfilter::rtfutil::WriteHex(static_cast<const sal_uInt8
*>(aOle1
.GetData()), aOle1
.GetSize(),
616 rRtf
.WriteOString("}");
617 rRtf
.WriteOString(SAL_NEWLINE_STRING
);
619 rRtf
.WriteOString("{" OOO_STRING_SVTOOLS_RTF_RESULT
);
620 rRtf
.WriteOString("{" OOO_STRING_SVTOOLS_RTF_PICT
);
622 Size
aMapped(rGraphic
.GetPrefSize());
623 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_PICW
);
624 rRtf
.WriteOString(OString::number(aMapped
.Width()));
625 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_PICH
);
626 rRtf
.WriteOString(OString::number(aMapped
.Height()));
628 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_PICWGOAL
);
629 rRtf
.WriteOString(OString::number(nWidth
));
630 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_PICHGOAL
);
631 rRtf
.WriteOString(OString::number(nHeight
));
632 rRtf
.WriteOString(OOO_STRING_SVTOOLS_RTF_WMETAFILE
"8");
633 rRtf
.WriteOString(SAL_NEWLINE_STRING
);
635 if (pPresentationData
)
637 msfilter::rtfutil::WriteHex(pPresentationData
, nPresentationData
, &rRtf
);
638 rRtf
.WriteOString(SAL_NEWLINE_STRING
);
642 rRtf
.WriteOString("}");
645 rRtf
.WriteOString("}");
648 rRtf
.WriteOString("}");
653 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */