include/vcl/filter/pdfdocument.hxx | 4 + vcl/source/filter/ipdf/pdfdocument.cxx | 5 + vcl/source/gdi/pdfwriter_impl.cxx | 92 ++++++++++++++++++++++++++++++--- vcl/source/gdi/pdfwriter_impl.hxx | 8 ++ 4 files changed, 100 insertions(+), 9 deletions(-)
New commits: commit 1f2bccf2d28d4257aa0e325658d35182367b59d9 Author: Miklos Vajna <vmik...@collabora.co.uk> Date: Fri Mar 24 09:46:21 2017 +0100 tdf#106693 vcl PDF export, norefxobj: copy XObject references With this the images inside the PDF image show up correctly. Change-Id: I430502fb6ae9de8111dda7e67db33642ff263317 Reviewed-on: https://gerrit.libreoffice.org/35621 Reviewed-by: Miklos Vajna <vmik...@collabora.co.uk> Tested-by: Jenkins <c...@libreoffice.org> diff --git a/include/vcl/filter/pdfdocument.hxx b/include/vcl/filter/pdfdocument.hxx index 8d362e7e339c..135d30d8d8bb 100644 --- a/include/vcl/filter/pdfdocument.hxx +++ b/include/vcl/filter/pdfdocument.hxx @@ -44,13 +44,14 @@ public: /// Indirect object: something with a unique ID. class VCL_DLLPUBLIC PDFObjectElement : public PDFElement { + /// The document owning this element. PDFDocument& m_rDoc; double m_fObjectValue; double m_fGenerationValue; std::map<OString, PDFElement*> m_aDictionary; /// Position after the '<<' token. sal_uInt64 m_nDictionaryOffset; - /// Length of the dictionary buffer till (before) the '<<' token. + /// Length of the dictionary buffer till (before) the '>>' token. sal_uInt64 m_nDictionaryLength; PDFDictionaryElement* m_pDictionaryElement; /// The contained direct array, if any. @@ -86,6 +87,7 @@ public: std::vector< std::unique_ptr<PDFElement> >& GetStoredElements(); SvMemoryStream* GetStreamBuffer() const; void SetStreamBuffer(std::unique_ptr<SvMemoryStream>& pStreamBuffer); + PDFDocument& GetDocument(); }; /// Array object: a list. diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx index 72996ecd652c..900c5f281863 100644 --- a/vcl/source/filter/ipdf/pdfdocument.cxx +++ b/vcl/source/filter/ipdf/pdfdocument.cxx @@ -2586,6 +2586,11 @@ void PDFObjectElement::SetStreamBuffer(std::unique_ptr<SvMemoryStream>& pStreamB m_pStreamBuffer = std::move(pStreamBuffer); } +PDFDocument& PDFObjectElement::GetDocument() +{ + return m_rDoc; +} + PDFReferenceElement::PDFReferenceElement(PDFDocument& rDoc, int fObjectValue, int fGenerationValue) : m_rDoc(rDoc), m_fObjectValue(fObjectValue), diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index f9e26a785c5b..3b7ee87f5e6c 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -10852,6 +10852,64 @@ void PDFWriterImpl::writeJPG( JPGEmit& rObject ) writeReferenceXObject(rObject.m_aReferenceXObject); } +std::map<OString, sal_Int32> PDFWriterImpl::copyExternalResources(filter::PDFObjectElement& rPage, const OString& rKind) +{ + // A name - object ID map, IDs as they appear in our output, not the + // original ones. + std::map<OString, sal_Int32> aRet; + + // Get the rKind subset of the resource dictionary. + auto pResources = dynamic_cast<filter::PDFDictionaryElement*>(rPage.Lookup("Resources")); + if (!pResources) + return aRet; + + auto pDictionary = dynamic_cast<filter::PDFDictionaryElement*>(pResources->LookupElement(rKind)); + if (!pDictionary) + return aRet; + + const std::map<OString, filter::PDFElement*>& rItems = pDictionary->GetItems(); + for (const auto& rItem : rItems) + { + // For each item copy it over to our output then insert it into aRet. + auto pReference = dynamic_cast<filter::PDFReferenceElement*>(rItem.second); + if (!pReference) + continue; + + filter::PDFObjectElement* pValue = pReference->LookupObject(); + if (!pValue) + continue; + + sal_Int32 nObject = createObject(); + if (!updateObject(nObject)) + continue; + + SvMemoryStream& rDocBuffer = rPage.GetDocument().GetEditBuffer(); + + // When copying over an object copy its dictionary and its stream. + OStringBuffer aLine; + aLine.append(nObject); + aLine.append(" 0 obj\n<<"); + aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + pValue->GetDictionaryOffset(), pValue->GetDictionaryLength()); + aLine.append(">>\nstream\n"); + + filter::PDFStreamElement* pStream = pValue->GetStream(); + if (!pStream) + continue; + + SvMemoryStream& rStream = pStream->GetMemory(); + aLine.append(static_cast<const sal_Char*>(rStream.GetData()), rStream.GetSize()); + aLine.append("\nendstream\nendobj\n\n"); + + // We have the whole object, now write it to the output. + if (!writeBuffer(aLine.getStr(), aLine.getLength())) + continue; + + aRet[rItem.first] = nObject; + } + + return aRet; +} + void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit) { if (rEmit.m_nFormObject <= 0) @@ -10872,12 +10930,6 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit) sal_Int32 nWrappedFormObject = 0; if (!m_aContext.UseReferenceXObject) { - nWrappedFormObject = createObject(); - // Write the form XObject wrapped below. This is a separate object from - // the wrapper, this way there is no need to alter the stream contents. - if (!updateObject(nWrappedFormObject)) - return; - // Parse the PDF data, we need that to write the PDF dictionary of our // object. SvMemoryStream aPDFStream; @@ -10890,24 +10942,48 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit) return; } std::vector<filter::PDFObjectElement*> aPages = aPDFDocument.GetPages(); - if (aPages.empty() || !aPages[0]) + if (aPages.empty()) { SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no pages"); return; } - filter::PDFObjectElement* pPageContents = aPages[0]->LookupObject("Contents"); + filter::PDFObjectElement* pPage = aPages[0]; + if (!pPage) + { + SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no page"); + return; + } + + std::map<OString, sal_Int32> aXObjects = copyExternalResources(*pPage, "XObject"); + OString sXObjects = "/XObject<<"; + for (const auto& rPair : aXObjects) + { + sXObjects += "/" + rPair.first + " " + OString::number(rPair.second) + " 0 R"; + } + sXObjects += ">>"; + + filter::PDFObjectElement* pPageContents = pPage->LookupObject("Contents"); if (!pPageContents) { SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: page has no contents"); return; } + nWrappedFormObject = createObject(); + // Write the form XObject wrapped below. This is a separate object from + // the wrapper, this way there is no need to alter the stream contents. + if (!updateObject(nWrappedFormObject)) + return; + OStringBuffer aLine; aLine.append(nWrappedFormObject); aLine.append(" 0 obj\n"); aLine.append("<< /Type /XObject"); aLine.append(" /Subtype /Form"); + aLine.append(" /Resources <<"); + aLine.append(sXObjects); + aLine.append(">>"); aLine.append(" /BBox [ 0 0 "); aLine.append(aSize.Width()); aLine.append(" "); diff --git a/vcl/source/gdi/pdfwriter_impl.hxx b/vcl/source/gdi/pdfwriter_impl.hxx index b35c44050f46..3185d60d628a 100644 --- a/vcl/source/gdi/pdfwriter_impl.hxx +++ b/vcl/source/gdi/pdfwriter_impl.hxx @@ -90,6 +90,11 @@ class PDFStreamIf; class Matrix3; class PdfBuiltinFontFace; +namespace filter +{ +class PDFObjectElement; +} + class PDFWriterImpl { friend class PDFStreamIf; @@ -847,6 +852,9 @@ i12626 void writeJPG( JPGEmit& rEmit ); /// Writes the form XObject proxy for the image. void writeReferenceXObject(ReferenceXObjectEmit& rEmit); + /// Copies resources of a given kind from an external page to the output, + /// returning what has beeen copied (name) and where (object ID). + std::map<OString, sal_Int32> copyExternalResources(filter::PDFObjectElement& rPage, const OString& rKind); /* tries to find the bitmap by its id and returns its emit data if exists, else creates a new emit data block */ _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits