LibreOffice Module sw (master)  1
htmlreqifreader.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  */
9 
10 #include "htmlreqifreader.hxx"
11 
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>
21 #include <vcl/cvtgrf.hxx>
22 #include <ndole.hxx>
23 
24 namespace
25 {
27 class ReqIfRtfReader : public SvRTFParser
28 {
29 public:
30  ReqIfRtfReader(SvStream& rStream);
31  void NextToken(int nToken) override;
32  bool WriteObjectData(SvStream& rOLE);
33 
34 private:
35  bool m_bInObjData = false;
36  OStringBuffer m_aHex;
37 };
38 
39 ReqIfRtfReader::ReqIfRtfReader(SvStream& rStream)
40  : SvRTFParser(rStream)
41 {
42 }
43 
44 void ReqIfRtfReader::NextToken(int nToken)
45 {
46  switch (nToken)
47  {
48  case '}':
49  m_bInObjData = false;
50  break;
51  case RTF_TEXTTOKEN:
52  if (m_bInObjData)
53  m_aHex.append(OUStringToOString(aToken, RTL_TEXTENCODING_ASCII_US));
54  break;
55  case RTF_OBJDATA:
56  m_bInObjData = true;
57  break;
58  }
59 }
60 
61 bool ReqIfRtfReader::WriteObjectData(SvStream& rOLE)
62 {
63  return msfilter::rtfutil::ExtractOLE2FromObjdata(m_aHex.makeStringAndClear(), rOLE);
64 }
65 
67 OString ExtractOLEClassName(const tools::SvRef<SotStorage>& xStorage)
68 {
69  OString aRet;
70 
71  SotStorageStream* pCompObj = xStorage->OpenSotStream("\1CompObj");
72  if (!pCompObj)
73  return aRet;
74 
75  pCompObj->Seek(0);
76  pCompObj->SeekRel(28); // Header
77  if (!pCompObj->good())
78  return aRet;
79 
80  sal_uInt32 nData;
81  pCompObj->ReadUInt32(nData); // AnsiUserType
82  pCompObj->SeekRel(nData);
83  if (!pCompObj->good())
84  return aRet;
85 
86  pCompObj->ReadUInt32(nData); // AnsiClipboardFormat
87  pCompObj->SeekRel(nData);
88  if (!pCompObj->good())
89  return aRet;
90 
91  pCompObj->ReadUInt32(nData); // Reserved1
92  return read_uInt8s_ToOString(*pCompObj, nData - 1); // -1 because it is null-terminated
93 }
94 
96 bool ParseOLE2Presentation(SvStream& rOle2, sal_uInt32& nWidth, sal_uInt32& nHeight,
97  SvStream& rPresentationData)
98 {
99  // See [MS-OLEDS] 2.3.4, OLEPresentationStream
100  rOle2.Seek(0);
101  tools::SvRef<SotStorage> pStorage = new SotStorage(rOle2);
102  tools::SvRef<SotStorageStream> xOle2Presentation
103  = pStorage->OpenSotStream("\002OlePres000", StreamMode::STD_READ);
104 
105  // Read AnsiClipboardFormat.
106  sal_uInt32 nMarkerOrLength = 0;
107  xOle2Presentation->ReadUInt32(nMarkerOrLength);
108  if (nMarkerOrLength != 0xffffffff)
109  // FormatOrAnsiString is not present
110  return false;
111  sal_uInt32 nFormatOrAnsiLength = 0;
112  xOle2Presentation->ReadUInt32(nFormatOrAnsiLength);
113  if (nFormatOrAnsiLength != 0x00000003) // CF_METAFILEPICT
114  return false;
115 
116  // Read TargetDeviceSize.
117  sal_uInt32 nTargetDeviceSize = 0;
118  xOle2Presentation->ReadUInt32(nTargetDeviceSize);
119  if (nTargetDeviceSize != 0x00000004)
120  // TargetDevice is present
121  return false;
122 
123  sal_uInt32 nAspect = 0;
124  xOle2Presentation->ReadUInt32(nAspect);
125  sal_uInt32 nLindex = 0;
126  xOle2Presentation->ReadUInt32(nLindex);
127  sal_uInt32 nAdvf = 0;
128  xOle2Presentation->ReadUInt32(nAdvf);
129  sal_uInt32 nReserved1 = 0;
130  xOle2Presentation->ReadUInt32(nReserved1);
131  xOle2Presentation->ReadUInt32(nWidth);
132  xOle2Presentation->ReadUInt32(nHeight);
133  sal_uInt32 nSize = 0;
134  xOle2Presentation->ReadUInt32(nSize);
135 
136  // Read Data.
137  if (nSize > xOle2Presentation->remainingSize())
138  return false;
139  std::vector<char> aBuffer(nSize);
140  xOle2Presentation->ReadBytes(aBuffer.data(), aBuffer.size());
141  rPresentationData.WriteBytes(aBuffer.data(), aBuffer.size());
142 
143  return true;
144 }
145 
150 OString InsertOLE1HeaderFromOle10NativeStream(tools::SvRef<SotStorage>& xStorage,
151  SwOLENode& rOLENode, SvStream& rOle1)
152 {
154  = xStorage->OpenSotStream("\1Ole10Native", StreamMode::STD_READ);
155  sal_uInt32 nOle1Size = 0;
156  xOle1Stream->ReadUInt32(nOle1Size);
157 
158  OString aClassName("Package");
159 
160  // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
161  rOle1.Seek(0);
162  // OLEVersion.
163  rOle1.WriteUInt32(0x00000501);
164 
165  // FormatID is EmbeddedObject.
166  rOle1.WriteUInt32(0x00000002);
167 
168  // ClassName
169  rOle1.WriteUInt32(aClassName.isEmpty() ? 0 : aClassName.getLength() + 1);
170  if (!aClassName.isEmpty())
171  {
172  rOle1.WriteOString(aClassName);
173  // Null terminated pascal string.
174  rOle1.WriteChar(0);
175  }
176 
177  // TopicName.
178  rOle1.WriteUInt32(0);
179 
180  // ItemName.
181  rOle1.WriteUInt32(0);
182 
183  // NativeDataSize
184  rOle1.WriteUInt32(nOle1Size);
185 
186  // Write the actual native data.
187  rOle1.WriteStream(*xOle1Stream, nOle1Size);
188 
189  // Write Presentation.
190  if (!rOLENode.GetGraphic())
191  {
192  return aClassName;
193  }
194 
195  const Graphic& rGraphic = *rOLENode.GetGraphic();
196  Size aSize = rOLENode.GetTwipSize();
197  SvMemoryStream aGraphicStream;
198  if (GraphicConverter::Export(aGraphicStream, rGraphic, ConvertDataFormat::WMF) != ERRCODE_NONE)
199  {
200  return aClassName;
201  }
202 
203  auto pGraphicAry = static_cast<const sal_uInt8*>(aGraphicStream.GetData());
204  sal_uInt64 nPresentationData = aGraphicStream.TellEnd();
205  msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nPresentationData);
206 
207  // OLEVersion.
208  rOle1.WriteUInt32(0x00000501);
209  // FormatID: constant means the ClassName field is present.
210  rOle1.WriteUInt32(0x00000005);
211  // ClassName: null terminated pascal string.
212  OString aPresentationClassName("METAFILEPICT");
213  rOle1.WriteUInt32(aPresentationClassName.getLength() + 1);
214  rOle1.WriteOString(aPresentationClassName);
215  rOle1.WriteChar(0);
216  // Width.
217  rOle1.WriteUInt32(aSize.getWidth());
218  // Height.
219  rOle1.WriteUInt32(aSize.getHeight() * -1);
220  // PresentationDataSize
221  rOle1.WriteUInt32(8 + nPresentationData);
222  // Reserved1-4.
223  rOle1.WriteUInt16(0x0008);
224  rOle1.WriteUInt16(0x31b1);
225  rOle1.WriteUInt16(0x1dd9);
226  rOle1.WriteUInt16(0x0000);
227  rOle1.WriteBytes(pGraphicAry, nPresentationData);
228 
229  return aClassName;
230 }
231 
233 OString InsertOLE1Header(SvStream& rOle2, SvStream& rOle1, sal_uInt32& nWidth, sal_uInt32& nHeight,
234  SwOLENode& rOLENode)
235 {
236  rOle2.Seek(0);
237  tools::SvRef<SotStorage> xStorage(new SotStorage(rOle2));
238  if (xStorage->GetError() != ERRCODE_NONE)
239  return OString();
240 
241  if (xStorage->IsStream("\1Ole10Native"))
242  {
243  return InsertOLE1HeaderFromOle10NativeStream(xStorage, rOLENode, rOle1);
244  }
245 
246  OString aClassName = ExtractOLEClassName(xStorage);
247 
248  // Write ObjectHeader, see [MS-OLEDS] 2.2.4.
249  rOle1.Seek(0);
250  // OLEVersion.
251  rOle1.WriteUInt32(0x00000501);
252 
253  // FormatID is EmbeddedObject.
254  rOle1.WriteUInt32(0x00000002);
255 
256  // ClassName
257  rOle1.WriteUInt32(aClassName.isEmpty() ? 0 : aClassName.getLength() + 1);
258  if (!aClassName.isEmpty())
259  {
260  rOle1.WriteOString(aClassName);
261  // Null terminated pascal string.
262  rOle1.WriteChar(0);
263  }
264 
265  // TopicName.
266  rOle1.WriteUInt32(0);
267 
268  // ItemName.
269  rOle1.WriteUInt32(0);
270 
271  // NativeDataSize
272  rOle1.WriteUInt32(rOle2.TellEnd());
273 
274  // Write the actual native data.
275  rOle2.Seek(0);
276  rOle1.WriteStream(rOle2);
277 
278  // Write Presentation.
279  SvMemoryStream aPresentationData;
280  if (ParseOLE2Presentation(rOle2, nWidth, nHeight, aPresentationData))
281  {
282  // OLEVersion.
283  rOle1.WriteUInt32(0x00000501);
284  // FormatID: constant means the ClassName field is present.
285  rOle1.WriteUInt32(0x00000005);
286  // ClassName: null terminated pascal string.
287  OString aPresentationClassName("METAFILEPICT");
288  rOle1.WriteUInt32(aPresentationClassName.getLength() + 1);
289  rOle1.WriteOString(aPresentationClassName);
290  rOle1.WriteChar(0);
291  // Width.
292  rOle1.WriteUInt32(nWidth);
293  // Height.
294  rOle1.WriteUInt32(nHeight * -1);
295  // PresentationDataSize
296  sal_uInt32 nPresentationData = aPresentationData.Tell();
297  rOle1.WriteUInt32(8 + nPresentationData);
298  // Reserved1-4.
299  rOle1.WriteUInt16(0x0008);
300  rOle1.WriteUInt16(0x31b1);
301  rOle1.WriteUInt16(0x1dd9);
302  rOle1.WriteUInt16(0x0000);
303  aPresentationData.Seek(0);
304  rOle1.WriteStream(aPresentationData, nPresentationData);
305  }
306 
307  return aClassName;
308 }
309 
311 void WrapOleGraphicInRtf(SvStream& rRtf, const SwOLENode& rOLENode, const Graphic& rGraphic)
312 {
313  // Start result.
314  rRtf.WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_RESULT);
315 
316  // Start pict.
317  rRtf.WriteCharPtr("{" OOO_STRING_SVTOOLS_RTF_PICT);
318 
319  rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_WMETAFILE "8");
320  Size aSize(rOLENode.GetTwipSize());
321  rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICW);
322  rRtf.WriteOString(OString::number(aSize.getWidth()));
323  rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICH);
324  rRtf.WriteOString(OString::number(aSize.getHeight()));
325  rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICWGOAL);
326  rRtf.WriteOString(OString::number(aSize.getWidth()));
327  rRtf.WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PICHGOAL);
328  rRtf.WriteOString(OString::number(aSize.getHeight()));
329  SvMemoryStream aGraphicStream;
330  if (GraphicConverter::Export(aGraphicStream, rGraphic, ConvertDataFormat::WMF) == ERRCODE_NONE)
331  {
332  auto pGraphicAry = static_cast<const sal_uInt8*>(aGraphicStream.GetData());
333  sal_uInt64 nSize = aGraphicStream.TellEnd();
334  msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nSize);
335  rRtf.WriteCharPtr(SAL_NEWLINE_STRING);
336  msfilter::rtfutil::WriteHex(pGraphicAry, nSize, &rRtf);
337  }
338 
339  // End pict.
340  rRtf.WriteCharPtr("}");
341 
342  // End result.
343  rRtf.WriteCharPtr("}");
344 }
345 }
346 
347 namespace SwReqIfReader
348 {
349 bool ExtractOleFromRtf(SvStream& rRtf, SvStream& rOle, bool& bOwnFormat)
350 {
351  // Add missing header/footer.
352  SvMemoryStream aRtf;
353  aRtf.WriteOString("{\\rtf1");
354  aRtf.WriteStream(rRtf);
355  aRtf.WriteOString("}");
356  aRtf.Seek(0);
357 
358  // Read the RTF markup.
359  tools::SvRef<ReqIfRtfReader> xReader(new ReqIfRtfReader(aRtf));
360  SvParserState eState = xReader->CallParser();
361  if (eState == SvParserState::Error)
362  return false;
363 
364  // Write the OLE2 data.
365  if (!xReader->WriteObjectData(rOle))
366  return false;
367 
368  tools::SvRef<SotStorage> pStorage = new SotStorage(rOle);
369  OUString aFilterName = SvxMSDffManager::GetFilterNameFromClassID(pStorage->GetClassName());
370  bOwnFormat = !aFilterName.isEmpty();
371  if (!bOwnFormat)
372  {
373  // Real OLE2 data, we're done.
374  rOle.Seek(0);
375  return true;
376  }
377 
378  // ODF-in-OLE2 case, extract actual data.
379  SvMemoryStream aMemory;
380  SvxMSDffManager::ExtractOwnStream(*pStorage, aMemory);
381  rOle.Seek(0);
382  aMemory.Seek(0);
383  rOle.WriteStream(aMemory);
384  // Stream length is current position + 1.
385  rOle.SetStreamSize(aMemory.GetSize() + 1);
386  rOle.Seek(0);
387  return true;
388 }
389 
390 bool WrapOleInRtf(SvStream& rOle2, SvStream& rRtf, SwOLENode& rOLENode)
391 {
392  sal_uInt64 nPos = rOle2.Tell();
393  comphelper::ScopeGuard g([&rOle2, nPos] { rOle2.Seek(nPos); });
394 
395  // Write OLE1 header, then the RTF wrapper.
396  SvMemoryStream aOLE1;
397  sal_uInt32 nWidth = 0;
398  sal_uInt32 nHeight = 0;
399  OString aClassName = InsertOLE1Header(rOle2, aOLE1, nWidth, nHeight, rOLENode);
400 
401  // Start object.
404 
405  // Start objclass.
407  rRtf.WriteOString(aClassName);
408  // End objclass.
409  rRtf.WriteCharPtr("}");
410 
411  // Object size.
413  rRtf.WriteOString(OString::number(nWidth));
415  rRtf.WriteOString(OString::number(nHeight));
416 
417  // Start objdata.
418  rRtf.WriteCharPtr(
420  msfilter::rtfutil::WriteHex(static_cast<const sal_uInt8*>(aOLE1.GetData()), aOLE1.GetSize(),
421  &rRtf);
422  // End objdata.
423  rRtf.WriteCharPtr("}");
424 
425  if (const Graphic* pGraphic = rOLENode.GetGraphic())
426  WrapOleGraphicInRtf(rRtf, rOLENode, *pGraphic);
427 
428  // End object.
429  rRtf.WriteCharPtr("}");
430 
431  return true;
432 }
433 
434 bool WrapGraphicInRtf(const Graphic& rGraphic, const Size& rLogicSize, SvStream& rRtf)
435 {
437 
438  GfxLink aLink = rGraphic.GetGfxLink();
439  const sal_uInt8* pGraphicAry = aLink.GetData();
440  sal_uInt64 nSize = aLink.GetDataSize();
441  OString aBlipType;
442  bool bIsWMF = false;
443  switch (aLink.GetType())
444  {
445  case GfxLinkType::NativeBmp:
446  aBlipType = OOO_STRING_SVTOOLS_RTF_WBITMAP;
447  break;
448  case GfxLinkType::NativeJpg:
450  break;
451  case GfxLinkType::NativePng:
452  aBlipType = OOO_STRING_SVTOOLS_RTF_PNGBLIP;
453  break;
454  case GfxLinkType::NativeWmf:
455  if (aLink.IsEMF())
456  aBlipType = OOO_STRING_SVTOOLS_RTF_EMFBLIP;
457  else
458  {
460  bIsWMF = true;
461  }
462  break;
463  default:
464  break;
465  }
466 
467  if (aBlipType.isEmpty())
468  return false;
469 
470  rRtf.WriteOString(aBlipType);
471 
472  Size aMapped(rGraphic.GetPrefSize());
474  rRtf.WriteOString(OString::number(aMapped.Width()));
476  rRtf.WriteOString(OString::number(aMapped.Height()));
477 
479  rRtf.WriteOString(OString::number(rLogicSize.Width()));
481  rRtf.WriteOString(OString::number(rLogicSize.Height()));
482 
483  if (bIsWMF)
484  {
485  rRtf.WriteOString(OString::number(8));
486  msfilter::rtfutil::StripMetafileHeader(pGraphicAry, nSize);
487  }
489 
490  msfilter::rtfutil::WriteHex(pGraphicAry, nSize, &rRtf);
492 
493  // End pict.
494  rRtf.WriteCharPtr("}");
495  return true;
496 }
497 }
498 
499 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
long Width() const
bool SetStreamSize(sal_uInt64 nSize)
#define OOO_STRING_SVTOOLS_RTF_PNGBLIP
SvStream & WriteUInt16(sal_uInt16 nUInt16)
#define OOO_STRING_SVTOOLS_RTF_OBJH
long Height() const
bool WrapOleInRtf(SvStream &rOle2, SvStream &rRtf, SwOLENode &rOLENode)
Wraps an OLE2 container binary in an RTF fragment.
virtual sal_uInt64 TellEnd()
ErrCode GetError() const
bool ExtractOLE2FromObjdata(const OString &rObjdata, SvStream &rOle2)
#define OOO_STRING_SVTOOLS_RTF_PICT
sal_uInt64 Seek(sal_uInt64 nPos)
bool IsStream(const OUString &rEleName) const
SvStream & WriteOString(const OString &rStr)
#define OOO_STRING_SVTOOLS_RTF_PICH
SvStream & WriteCharPtr(const char *pBuf)
SvParserState
virtual sal_uInt64 TellEnd() override
bool StripMetafileHeader(const sal_uInt8 *&rpGraphicAry, sal_uInt64 &rSize)
#define OOO_STRING_SVTOOLS_RTF_WBITMAP
#define OOO_STRING_SVTOOLS_RTF_OBJDATA
SvStream & WriteUInt32(sal_uInt32 nUInt32)
OString WriteHex(const sal_uInt8 *pData, sal_uInt32 nSize, SvStream *pStream=nullptr, sal_uInt32 nLimit=64)
RTF_OBJDATA
static OUString GetFilterNameFromClassID(const SvGlobalName &aGlobName)
bool WrapGraphicInRtf(const Graphic &rGraphic, const Size &rLogicSize, SvStream &rRtf)
Wraps an image in an RTF fragment.
#define OOO_STRING_SVTOOLS_RTF_PICWGOAL
SvGlobalName GetClassName()
SotStorageStream * OpenSotStream(const OUString &rEleName, StreamMode=StreamMode::STD_READWRITE)
virtual void NextToken(int nToken)=0
const Graphic * GetGraphic()
Definition: ndole.cxx:242
sal_uInt64 GetSize()
std::size_t WriteBytes(const void *pData, std::size_t nSize)
#define OOO_STRING_SVTOOLS_RTF_EMFBLIP
GfxLink GetGfxLink() const
virtual Size GetTwipSize() const override
Definition: ndole.cxx:420
SvStream & WriteStream(SvStream &rStream)
#define OOO_STRING_SVTOOLS_RTF_PICW
Size GetPrefSize() const
#define OOO_STRING_SVTOOLS_RTF_OBJEMB
OString OUStringToOString(const OUString &str, ConnectionSettings const *settings)
static ErrCode Export(SvStream &rOStm, const Graphic &rGraphic, ConvertDataFormat nFormat)
#define OOO_STRING_SVTOOLS_RTF_IGNORE
std::unique_ptr< char[]> aBuffer
#define ERRCODE_NONE
unsigned char sal_uInt8
#define OOO_STRING_SVTOOLS_RTF_OBJCLASS
bool ExtractOleFromRtf(SvStream &rRtf, SvStream &rOle, bool &bOwnFormat)
Extracts an OLE2 container binary from an RTF fragment.
sal_uInt64 Tell() const
#define OOO_STRING_SVTOOLS_RTF_PICHGOAL
SvStream & WriteChar(char nChar)
#define OOO_STRING_SVTOOLS_RTF_WMETAFILE
long getHeight() const
long getWidth() const
RTF_TEXTTOKEN
OString read_uInt8s_ToOString(SvStream &rStrm, std::size_t nLen)
#define OOO_STRING_SVTOOLS_RTF_OBJECT
#define OOO_STRING_SVTOOLS_RTF_OBJW
#define SAL_NEWLINE_STRING
sal_uInt16 nPos
const void * GetData()
#define OOO_STRING_SVTOOLS_RTF_JPEGBLIP
static void ExtractOwnStream(SotStorage &rSrcStg, SvMemoryStream &rMemStream)