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