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 <pdf/objectcopier.hxx>
12 #include <rtl/strbuf.hxx>
13 #include <sal/log.hxx>
14 #include <sal/types.h>
15 #include <tools/stream.hxx>
16 #include <tools/zcodec.hxx>
17 #include <vcl/filter/pdfdocument.hxx>
18 #include <vcl/filter/pdfobjectcontainer.hxx>
20 #include "pdfwriter_impl.hxx"
24 PDFObjectCopier::PDFObjectCopier(PDFObjectContainer
& rContainer
)
25 : m_rContainer(rContainer
)
29 void PDFObjectCopier::copyRecursively(OStringBuffer
& rLine
, filter::PDFElement
* pInputElement
,
30 SvMemoryStream
& rDocBuffer
,
31 std::map
<sal_Int32
, sal_Int32
>& rCopiedResources
)
33 if (auto pReference
= dynamic_cast<filter::PDFReferenceElement
*>(pInputElement
))
35 filter::PDFObjectElement
* pReferenced
= pReference
->LookupObject();
38 // Copy the referenced object.
39 sal_Int32 nRef
= copyExternalResource(rDocBuffer
, *pReferenced
, rCopiedResources
);
41 // Write the updated reference.
46 else if (auto pInputArray
= dynamic_cast<filter::PDFArrayElement
*>(pInputElement
))
49 for (auto const& pElement
: pInputArray
->GetElements())
51 copyRecursively(rLine
, pElement
, rDocBuffer
, rCopiedResources
);
56 else if (auto pInputDictionary
= dynamic_cast<filter::PDFDictionaryElement
*>(pInputElement
))
59 for (auto const& pPair
: pInputDictionary
->GetItems())
62 rLine
.append(pPair
.first
);
64 copyRecursively(rLine
, pPair
.second
, rDocBuffer
, rCopiedResources
);
71 pInputElement
->writeString(rLine
);
75 sal_Int32
PDFObjectCopier::copyExternalResource(SvMemoryStream
& rDocBuffer
,
76 filter::PDFObjectElement
& rObject
,
77 std::map
<sal_Int32
, sal_Int32
>& rCopiedResources
)
79 auto it
= rCopiedResources
.find(rObject
.GetObjectValue());
80 if (it
!= rCopiedResources
.end())
82 // This resource was already copied once, nothing to do.
86 sal_Int32 nObject
= m_rContainer
.createObject();
87 // Remember what is the ID of this object in our output.
88 rCopiedResources
[rObject
.GetObjectValue()] = nObject
;
89 SAL_INFO("vcl.pdfwriter", "PDFObjectCopier::copyExternalResource: " << rObject
.GetObjectValue()
90 << " -> " << nObject
);
92 SvMemoryStream
* pObjectStream
= rObject
.GetStreamBuffer();
95 pObjectStream
= &rDocBuffer
;
99 aLine
.append(nObject
);
100 aLine
.append(" 0 obj\n");
102 if (rObject
.GetDictionary())
106 for (auto const& rPair
: rObject
.GetDictionaryItems())
114 aLine
.append(rPair
.first
);
116 copyRecursively(aLine
, rPair
.second
, rDocBuffer
, rCopiedResources
);
119 aLine
.append(" >>\n");
122 if (filter::PDFStreamElement
* pStream
= rObject
.GetStream())
124 aLine
.append("stream\n");
125 SvMemoryStream
& rStream
= pStream
->GetMemory();
126 aLine
.append(static_cast<const char*>(rStream
.GetData()), rStream
.GetSize());
127 aLine
.append("\nendstream\n");
130 if (filter::PDFArrayElement
* pArray
= rObject
.GetArray())
134 const std::vector
<filter::PDFElement
*>& rElements
= pArray
->GetElements();
137 for (auto const& pElement
: rElements
)
143 copyRecursively(aLine
, pElement
, rDocBuffer
, rCopiedResources
);
148 // If the object has a number element outside a dictionary or array, copy that.
149 if (filter::PDFNumberElement
* pNumber
= rObject
.GetNumberElement())
151 pNumber
->writeString(aLine
);
155 aLine
.append("endobj\n\n");
157 // We have the whole object, now write it to the output.
158 if (!m_rContainer
.updateObject(nObject
))
160 if (!m_rContainer
.writeBuffer(aLine
.getStr(), aLine
.getLength()))
166 OString
PDFObjectCopier::copyExternalResources(filter::PDFObjectElement
& rPage
,
167 const OString
& rKind
,
168 std::map
<sal_Int32
, sal_Int32
>& rCopiedResources
)
170 // A name - object ID map, IDs as they appear in our output, not the
172 std::map
<OString
, sal_Int32
> aRet
;
174 // Get the rKind subset of the resource dictionary.
175 std::map
<OString
, filter::PDFElement
*> aItems
;
176 if (auto pResources
= dynamic_cast<filter::PDFDictionaryElement
*>(rPage
.Lookup("Resources")))
178 // Resources is a direct dictionary.
179 filter::PDFElement
* pLookup
= pResources
->LookupElement(rKind
);
180 if (auto pDictionary
= dynamic_cast<filter::PDFDictionaryElement
*>(pLookup
))
182 // rKind is an inline dictionary.
183 aItems
= pDictionary
->GetItems();
185 else if (auto pReference
= dynamic_cast<filter::PDFReferenceElement
*>(pLookup
))
187 // rKind refers to a dictionary.
188 filter::PDFObjectElement
* pReferenced
= pReference
->LookupObject();
194 aItems
= pReferenced
->GetDictionaryItems();
197 else if (filter::PDFObjectElement
* pPageResources
= rPage
.LookupObject("Resources"))
199 // Resources is an indirect object.
200 filter::PDFElement
* pValue
= pPageResources
->Lookup(rKind
);
201 if (auto pDictionary
= dynamic_cast<filter::PDFDictionaryElement
*>(pValue
))
202 // Kind is a direct dictionary.
203 aItems
= pDictionary
->GetItems();
204 else if (filter::PDFObjectElement
* pObject
= pPageResources
->LookupObject(rKind
))
205 // Kind is an indirect object.
206 aItems
= pObject
->GetDictionaryItems();
211 SvMemoryStream
& rDocBuffer
= rPage
.GetDocument().GetEditBuffer();
213 for (const auto& rItem
: aItems
)
215 // For each item copy it over to our output then insert it into aRet.
216 auto pReference
= dynamic_cast<filter::PDFReferenceElement
*>(rItem
.second
);
220 filter::PDFObjectElement
* pValue
= pReference
->LookupObject();
224 // Then copying over an object copy its dictionary and its stream.
225 sal_Int32 nObject
= copyExternalResource(rDocBuffer
, *pValue
, rCopiedResources
);
226 aRet
[rItem
.first
] = nObject
;
229 // Build the dictionary entry string.
230 OStringBuffer
sRet("/" + rKind
+ "<<");
231 for (const auto& rPair
: aRet
)
233 sRet
.append("/" + rPair
.first
+ " " + OString::number(rPair
.second
) + " 0 R");
237 return sRet
.makeStringAndClear();
240 void PDFObjectCopier::copyPageResources(filter::PDFObjectElement
* pPage
, OStringBuffer
& rLine
)
242 // Maps from source object id (PDF image) to target object id (export result).
243 std::map
<sal_Int32
, sal_Int32
> aCopiedResources
;
244 copyPageResources(pPage
, rLine
, aCopiedResources
);
247 void PDFObjectCopier::copyPageResources(filter::PDFObjectElement
* pPage
, OStringBuffer
& rLine
,
248 std::map
<sal_Int32
, sal_Int32
>& rCopiedResources
)
250 rLine
.append(" /Resources <<");
251 static const std::initializer_list
<OString
> aKeys
252 = { "ColorSpace", "ExtGState", "Font", "XObject", "Shading" };
253 for (const auto& rKey
: aKeys
)
255 rLine
.append(copyExternalResources(*pPage
, rKey
, rCopiedResources
));
260 sal_Int32
PDFObjectCopier::copyPageStreams(std::vector
<filter::PDFObjectElement
*>& rContentStreams
,
261 SvMemoryStream
& rStream
, bool& rCompressed
)
263 for (auto pContent
: rContentStreams
)
265 filter::PDFStreamElement
* pPageStream
= pContent
->GetStream();
268 SAL_WARN("vcl.pdfwriter", "PDFObjectCopier::copyPageStreams: contents has no stream");
272 SvMemoryStream
& rPageStream
= pPageStream
->GetMemory();
274 auto pFilter
= dynamic_cast<filter::PDFNameElement
*>(pContent
->Lookup("Filter"));
277 if (pFilter
->GetValue() != "FlateDecode")
282 SvMemoryStream aMemoryStream
;
285 aZCodec
.BeginCompression();
286 aZCodec
.Decompress(rPageStream
, aMemoryStream
);
287 if (!aZCodec
.EndCompression())
289 SAL_WARN("vcl.pdfwriter", "PDFObjectCopier::copyPageStreams: decompression failed");
293 rStream
.WriteBytes(aMemoryStream
.GetData(), aMemoryStream
.GetSize());
297 rStream
.WriteBytes(rPageStream
.GetData(), rPageStream
.GetSize());
301 rCompressed
= PDFWriterImpl::compressStream(&rStream
);
303 return rStream
.Tell();
307 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */