bump product version to 7.2.5.1
[LibreOffice.git] / vcl / source / gdi / pdfobjectcopier.cxx
blob35dae59237eead007bd05730746beb2b65507e8d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
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"
22 namespace vcl
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();
36 if (pReferenced)
38 // Copy the referenced object.
39 sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced, rCopiedResources);
41 // Write the updated reference.
42 rLine.append(nRef);
43 rLine.append(" 0 R");
46 else if (auto pInputArray = dynamic_cast<filter::PDFArrayElement*>(pInputElement))
48 rLine.append("[ ");
49 for (auto const& pElement : pInputArray->GetElements())
51 copyRecursively(rLine, pElement, rDocBuffer, rCopiedResources);
52 rLine.append(" ");
54 rLine.append("] ");
56 else if (auto pInputDictionary = dynamic_cast<filter::PDFDictionaryElement*>(pInputElement))
58 rLine.append("<< ");
59 for (auto const& pPair : pInputDictionary->GetItems())
61 rLine.append("/");
62 rLine.append(pPair.first);
63 rLine.append(" ");
64 copyRecursively(rLine, pPair.second, rDocBuffer, rCopiedResources);
65 rLine.append(" ");
67 rLine.append(">> ");
69 else
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.
83 return it->second;
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();
93 if (!pObjectStream)
95 pObjectStream = &rDocBuffer;
98 OStringBuffer aLine;
99 aLine.append(nObject);
100 aLine.append(" 0 obj\n");
102 if (rObject.GetDictionary())
104 aLine.append("<< ");
105 bool bFirst = true;
106 for (auto const& rPair : rObject.GetDictionaryItems())
108 if (bFirst)
109 bFirst = false;
110 else
111 aLine.append(" ");
113 aLine.append("/");
114 aLine.append(rPair.first);
115 aLine.append(" ");
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())
132 aLine.append("[ ");
134 const std::vector<filter::PDFElement*>& rElements = pArray->GetElements();
136 bool bFirst = true;
137 for (auto const& pElement : rElements)
139 if (bFirst)
140 bFirst = false;
141 else
142 aLine.append(" ");
143 copyRecursively(aLine, pElement, rDocBuffer, rCopiedResources);
145 aLine.append("]\n");
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);
152 aLine.append("\n");
155 aLine.append("endobj\n\n");
157 // We have the whole object, now write it to the output.
158 if (!m_rContainer.updateObject(nObject))
159 return -1;
160 if (!m_rContainer.writeBuffer(aLine.getStr(), aLine.getLength()))
161 return -1;
163 return nObject;
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
171 // original ones.
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();
189 if (!pReferenced)
191 return OString();
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();
208 if (aItems.empty())
209 return OString();
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);
217 if (!pReference)
218 continue;
220 filter::PDFObjectElement* pValue = pReference->LookupObject();
221 if (!pValue)
222 continue;
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");
235 sRet.append(">>");
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));
257 rLine.append(">>");
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();
266 if (!pPageStream)
268 SAL_WARN("vcl.pdfwriter", "PDFObjectCopier::copyPageStreams: contents has no stream");
269 continue;
272 SvMemoryStream& rPageStream = pPageStream->GetMemory();
274 auto pFilter = dynamic_cast<filter::PDFNameElement*>(pContent->Lookup("Filter"));
275 if (pFilter)
277 if (pFilter->GetValue() != "FlateDecode")
279 continue;
282 SvMemoryStream aMemoryStream;
283 ZCodec aZCodec;
284 rPageStream.Seek(0);
285 aZCodec.BeginCompression();
286 aZCodec.Decompress(rPageStream, aMemoryStream);
287 if (!aZCodec.EndCompression())
289 SAL_WARN("vcl.pdfwriter", "PDFObjectCopier::copyPageStreams: decompression failed");
290 continue;
293 rStream.WriteBytes(aMemoryStream.GetData(), aMemoryStream.GetSize());
295 else
297 rStream.WriteBytes(rPageStream.GetData(), rPageStream.GetSize());
301 rCompressed = PDFWriterImpl::compressStream(&rStream);
303 return rStream.Tell();
307 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */