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 <filter/msfilter/rtfutil.hxx>
11 #include <rtl/strbuf.hxx>
12 #include <sal/log.hxx>
13 #include <osl/diagnose.h>
14 #include <svtools/rtfkeywd.hxx>
15 #include <rtl/character.hxx>
16 #include <tools/stream.hxx>
17 #include <sot/storage.hxx>
22 * If rOle1 is native OLE1 data of size nOle1Size, wraps it in an OLE2 container.
24 * The OLE2 root's CLSID is set based on rClassName.
26 void WrapOle1InOle2(SvStream
& rOle1
, sal_uInt32 nOle1Size
, SvStream
& rOle2
,
27 const OString
& rClassName
)
29 tools::SvRef
<SotStorage
> pStorage
= new SotStorage(rOle2
);
30 OString aAnsiUserType
;
32 if (rClassName
== "PBrush")
34 aAnsiUserType
= "Bitmap Image";
35 aName
= SvGlobalName(0x0003000A, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46);
39 if (!rClassName
.isEmpty() && rClassName
!= "Package")
41 SAL_WARN("filter.ms", "WrapOle1InOle2: unexpected class name: '" << rClassName
<< "'");
43 aAnsiUserType
= "OLE Package";
44 aName
= SvGlobalName(0x0003000C, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46);
46 pStorage
->SetClass(aName
, SotClipboardFormatId::NONE
, "");
48 // [MS-OLEDS] 2.3.7 CompObjHeader
49 tools::SvRef
<SotStorageStream
> pCompObj
= pStorage
->OpenSotStream("\1CompObj");
51 pCompObj
->WriteUInt32(0xfffe0001);
53 pCompObj
->WriteUInt32(0x00000a03);
55 pCompObj
->WriteUInt32(0xffffffff);
56 pCompObj
->WriteUInt32(0x0003000c);
57 pCompObj
->WriteUInt32(0x00000000);
58 pCompObj
->WriteUInt32(0x000000c0);
59 pCompObj
->WriteUInt32(0x46000000);
60 // Rest of CompObjStream
62 pCompObj
->WriteUInt32(aAnsiUserType
.getLength() + 1);
63 pCompObj
->WriteOString(aAnsiUserType
);
64 pCompObj
->WriteChar(0);
65 // AnsiClipboardFormat
66 pCompObj
->WriteUInt32(0x00000000);
68 pCompObj
->WriteUInt32(rClassName
.getLength() + 1);
69 pCompObj
->WriteOString(rClassName
);
70 pCompObj
->WriteChar(0);
72 pCompObj
->WriteUInt32(0x71B239F4);
74 pCompObj
->WriteUInt32(0x00000000);
75 // UnicodeClipboardFormat
76 pCompObj
->WriteUInt32(0x00000000);
78 pCompObj
->WriteUInt32(0x00000000);
82 // [MS-OLEDS] 2.3.6 OLENativeStream
83 tools::SvRef
<SotStorageStream
> pOleNative
= pStorage
->OpenSotStream("\1Ole10Native");
85 pOleNative
->WriteUInt32(nOle1Size
);
86 pOleNative
->WriteStream(rOle1
, nOle1Size
);
96 namespace msfilter::rtfutil
98 OString
OutHex(sal_uLong nHex
, sal_uInt8 nLen
)
100 char aNToABuf
[] = "0000000000000000";
102 OSL_ENSURE(nLen
< sizeof(aNToABuf
), "nLen is too big");
103 if (nLen
>= sizeof(aNToABuf
))
104 nLen
= (sizeof(aNToABuf
) - 1);
106 // Set pointer to the buffer end
107 char* pStr
= aNToABuf
+ (sizeof(aNToABuf
) - 1);
108 for (sal_uInt8 n
= 0; n
< nLen
; ++n
)
110 *(--pStr
) = static_cast<char>(nHex
& 0xf) + 48;
118 // Ideally, this function should work on (sal_uInt32) Unicode scalar values
119 // instead of (sal_Unicode) UTF-16 code units. However, at least "Rich Text
120 // Format (RTF) Specification Version 1.9.1" available at
121 // <https://www.microsoft.com/en-us/download/details.aspx?id=10725> does not
122 // look like it allows non-BMP Unicode characters >= 0x10000 in the \uN notation
123 // (it only talks about "Unicode character", but then explains how values of N
124 // greater than 32767 will be expressed as negative signed 16-bit numbers, so
125 // that smells like \uN is limited to BMP).
126 // However the "Mathematics" section has an example that shows the code point
127 // U+1D44E being encoded as UTF-16 surrogate pair "\u-10187?\u-9138?", so
128 // sal_Unicode actually works fine here.
129 OString
OutChar(sal_Unicode c
, int* pUCMode
, rtl_TextEncoding eDestEnc
, bool* pSuccess
,
135 const char* pStr
= nullptr;
136 // 0x0b instead of \n, etc because of the replacements in SwWW8AttrIter::GetSnippet()
141 pStr
= OOO_STRING_SVTOOLS_RTF_LINE
;
144 pStr
= OOO_STRING_SVTOOLS_RTF_TAB
;
150 aBuf
.append(static_cast<char>(c
));
153 // non-breaking space
157 // non-breaking hyphen
165 if (c
>= ' ' && c
<= '~')
166 aBuf
.append(static_cast<char>(c
));
169 OUString
sBuf(&c
, 1);
172 *pSuccess
&= sBuf
.convertToString(&sConverted
, eDestEnc
,
173 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
174 | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
);
176 sBuf
.convertToString(&sConverted
, eDestEnc
, OUSTRING_TO_OSTRING_CVTFLAGS
);
177 const sal_Int32 nLen
= sConverted
.getLength();
179 if (pUCMode
&& bUnicode
)
181 if (*pUCMode
!= nLen
)
183 aBuf
.append("\\uc" + OString::number(nLen
));
184 // #i47831# add an additional whitespace, so that "document whitespaces" are not ignored.
188 aBuf
.append("\\u" + OString::number(static_cast<sal_Int32
>(c
)));
191 for (sal_Int32 nI
= 0; nI
< nLen
; ++nI
)
193 aBuf
.append("\\'" + OutHex(sConverted
[nI
], 2));
210 return aBuf
.makeStringAndClear();
213 OString
OutString(std::u16string_view rStr
, rtl_TextEncoding eDestEnc
, bool bUnicode
)
217 for (size_t n
= 0; n
< rStr
.size(); ++n
)
218 aBuf
.append(OutChar(rStr
[n
], &nUCMode
, eDestEnc
, nullptr, bUnicode
));
222 OOO_STRING_SVTOOLS_RTF_UC
+ OString::number(sal_Int32(1))
223 + " "); // #i47831# add an additional whitespace, so that "document whitespaces" are not ignored.;
225 return aBuf
.makeStringAndClear();
228 /// Checks if lossless conversion of the string to eDestEnc is possible or not.
229 static bool TryOutString(std::u16string_view rStr
, rtl_TextEncoding eDestEnc
)
232 for (size_t n
= 0; n
< rStr
.size(); ++n
)
235 OutChar(rStr
[n
], &nUCMode
, eDestEnc
, &bRet
);
242 OString
OutStringUpr(std::string_view pToken
, std::u16string_view rStr
, rtl_TextEncoding eDestEnc
)
244 if (TryOutString(rStr
, eDestEnc
))
245 return OString::Concat("{") + pToken
+ " " + OutString(rStr
, eDestEnc
) + "}";
247 return OString::Concat("{" OOO_STRING_SVTOOLS_RTF_UPR
"{") + pToken
+ " "
248 + OutString(rStr
, eDestEnc
, /*bUnicode =*/false)
249 + "}{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_UD
"{" + pToken
+ " "
250 + OutString(rStr
, eDestEnc
) + "}}}";
256 if (rtl::isAsciiDigit(static_cast<unsigned char>(ch
)))
260 if (ch
>= 'a' && ch
<= 'f')
262 else if (ch
>= 'A' && ch
<= 'F')
271 OString
WriteHex(const sal_uInt8
* pData
, sal_uInt32 nSize
, SvStream
* pStream
, sal_uInt32 nLimit
)
275 sal_uInt32 nBreak
= 0;
276 for (sal_uInt32 i
= 0; i
< nSize
; i
++)
278 OString sNo
= OString::number(pData
[i
], 16);
279 if (sNo
.getLength() < 2)
282 pStream
->WriteChar('0');
287 pStream
->WriteOString(sNo
);
290 if (++nBreak
== nLimit
)
293 pStream
->WriteOString(SAL_NEWLINE_STRING
);
295 aRet
.append(SAL_NEWLINE_STRING
);
300 return aRet
.makeStringAndClear();
303 bool ExtractOLE2FromObjdata(const OString
& rObjdata
, SvStream
& rOle2
)
305 SvMemoryStream aStream
;
309 // Feed the destination text to a stream.
310 for (int i
= 0; i
< rObjdata
.getLength(); ++i
)
312 char ch
= rObjdata
[i
];
313 if (ch
!= 0x0d && ch
!= 0x0a)
316 sal_Int8 parsed
= msfilter::rtfutil::AsHex(ch
);
323 aStream
.WriteChar(b
);
330 // Skip ObjectHeader, see [MS-OLEDS] 2.2.4.
336 aStream
.ReadUInt32(nData
); // OLEVersion
337 aStream
.ReadUInt32(nData
); // FormatID
338 aStream
.ReadUInt32(nData
); // ClassName
342 // -1 because it is null-terminated.
343 aClassName
= read_uInt8s_ToOString(aStream
, nData
- 1);
344 // Skip null-termination.
347 aStream
.ReadUInt32(nData
); // TopicName
348 aStream
.SeekRel(nData
);
349 aStream
.ReadUInt32(nData
); // ItemName
350 aStream
.SeekRel(nData
);
351 aStream
.ReadUInt32(nData
); // NativeDataSize
356 sal_uInt64 nPos
= aStream
.Tell();
357 sal_uInt8 aSignature
[8];
358 aStream
.ReadBytes(aSignature
, SAL_N_ELEMENTS(aSignature
));
360 const sal_uInt8 aOle2Signature
[8] = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
361 // Don't use Storage::IsStorageFile() here, that would seek to the start of the stream,
362 // where the magic will always mismatch.
363 if (std::memcmp(aSignature
, aOle2Signature
, SAL_N_ELEMENTS(aSignature
)) == 0)
366 rOle2
.WriteStream(aStream
, nData
);
370 SvMemoryStream aStorage
;
371 WrapOle1InOle2(aStream
, nData
, aStorage
, aClassName
);
372 rOle2
.WriteStream(aStorage
);
379 bool StripMetafileHeader(const sal_uInt8
*& rpGraphicAry
, sal_uInt64
& rSize
)
381 if (rpGraphicAry
&& (rSize
> 0x22))
383 if ((rpGraphicAry
[0] == 0xd7) && (rpGraphicAry
[1] == 0xcd) && (rpGraphicAry
[2] == 0xc6)
384 && (rpGraphicAry
[3] == 0x9a))
386 // we have to get rid of the metafileheader
396 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */