[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-5.3' - 22 commits - filter/source filter/uiconfig include/vcl include/xmlsecurity sw/qa vcl/CppunitTest_vcl_pdfexport.mk vcl/Library_vcl.mk vcl/qa vcl/source writerfilter/source xmlsecurity/CppunitTest_xmlsecurity_pdfsigning.mk xmlsecurity/qa xmlsecurity/source xmlsecurity/workben
Miklos Vajna
vmiklos at collabora.co.uk
Fri Mar 31 09:21:44 UTC 2017
filter/source/pdf/impdialog.cxx | 5
filter/source/pdf/impdialog.hxx | 2
filter/source/pdf/pdfexport.cxx | 4
filter/source/pdf/pdfexport.hxx | 1
filter/uiconfig/ui/pdfgeneralpage.ui | 17
include/vcl/filter/pdfdocument.hxx | 378 ++
include/vcl/pdfwriter.hxx | 5
include/xmlsecurity/pdfio/pdfdocument.hxx | 299 -
sw/qa/extras/rtfimport/data/tdf106694.rtf | 10
sw/qa/extras/rtfimport/rtfimport.cxx | 8
vcl/CppunitTest_vcl_pdfexport.mk | 2
vcl/Library_vcl.mk | 1
vcl/qa/cppunit/pdfexport/data/tdf106693.odt |binary
vcl/qa/cppunit/pdfexport/pdfexport.cxx | 111
vcl/source/filter/ipdf/pdfdocument.cxx | 3005 ++++++++++++++++
vcl/source/gdi/pdfwriter_impl.cxx | 365 +
vcl/source/gdi/pdfwriter_impl.hxx | 13
writerfilter/source/rtftok/rtfsprm.cxx | 20
xmlsecurity/CppunitTest_xmlsecurity_pdfsigning.mk | 1
xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx | 22
xmlsecurity/source/helper/pdfsignaturehelper.cxx | 10
xmlsecurity/source/pdfio/pdfdocument.cxx | 4044 +++-------------------
xmlsecurity/workben/pdfverify.cxx | 8
23 files changed, 4500 insertions(+), 3831 deletions(-)
New commits:
commit 3a8ba7cf23ebc89711a249a5105110ea8fdab713
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Thu Mar 30 17:04:19 2017 +0200
vcl PDF export, norefxobj: add test for this
Assert two important properties:
- the pdf image is described using the form xobject markup (not the
reference xobject one)
- the form xobject refers to a vector image, not to a bitmap one
Change-Id: I94b88976c1e5392758d56254143fbeeeeba51412
Reviewed-on: https://gerrit.libreoffice.org/35901
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit 932f6a8f37fbd99fc2ed16aa37966658d388c975)
diff --git a/vcl/qa/cppunit/pdfexport/data/tdf106693.odt b/vcl/qa/cppunit/pdfexport/data/tdf106693.odt
new file mode 100644
index 000000000000..a2c18037833e
Binary files /dev/null and b/vcl/qa/cppunit/pdfexport/data/tdf106693.odt differ
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index fb0bd8b3c597..ede50077b62c 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -47,6 +47,8 @@ public:
void testTdf105093();
/// Tests export of non-PDF images.
void testTdf106206();
+ /// Tests export of PDF images without reference XObjects.
+ void testTdf106693();
#endif
CPPUNIT_TEST_SUITE(PdfExportTest);
@@ -55,6 +57,7 @@ public:
CPPUNIT_TEST(testTdf105461);
CPPUNIT_TEST(testTdf105093);
CPPUNIT_TEST(testTdf106206);
+ CPPUNIT_TEST(testTdf106693);
#endif
CPPUNIT_TEST_SUITE_END();
};
@@ -118,6 +121,59 @@ void PdfExportTest::testTdf106059()
CPPUNIT_ASSERT(pReferenceXObject->Lookup("Ref"));
}
+void PdfExportTest::testTdf106693()
+{
+ // Import the bugdoc and export as PDF.
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf106693.odt";
+ mxComponent = loadFromDesktop(aURL);
+ CPPUNIT_ASSERT(mxComponent.is());
+
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::TempFile aTempFile;
+ aTempFile.EnableKillingFile();
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Parse the export result.
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(aTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // Assert that the XObject in the page resources dictionary is a form XObject.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ // The document has one page.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+ vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
+ CPPUNIT_ASSERT(pResources);
+ auto pXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
+ CPPUNIT_ASSERT(pXObjects);
+ // The page has one image.
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects->GetItems().size());
+ vcl::filter::PDFObjectElement* pXObject = pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
+ CPPUNIT_ASSERT(pXObject);
+ // The image is a form XObject.
+ auto pSubtype = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pSubtype);
+ CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype->GetValue());
+ // This failed: UseReferenceXObject was ignored and Ref was always created.
+ CPPUNIT_ASSERT(!pXObject->Lookup("Ref"));
+
+ // Assert that the form object refers to an inner form object, not a
+ // bitmap.
+ auto pInnerResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject->Lookup("Resources"));
+ CPPUNIT_ASSERT(pInnerResources);
+ auto pInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pInnerResources->LookupElement("XObject"));
+ CPPUNIT_ASSERT(pInnerXObjects);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pInnerXObjects->GetItems().size());
+ vcl::filter::PDFObjectElement* pInnerXObject = pInnerXObjects->LookupObject(pInnerXObjects->GetItems().begin()->first);
+ CPPUNIT_ASSERT(pInnerXObject);
+ auto pInnerSubtype = dynamic_cast<vcl::filter::PDFNameElement*>(pInnerXObject->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pInnerSubtype);
+ // This failed: this was Image (bitmap), not Form (vector).
+ CPPUNIT_ASSERT_EQUAL(OString("Form"), pInnerSubtype->GetValue());
+}
+
void PdfExportTest::testTdf105461()
{
// Import the bugdoc and export as PDF.
commit d6397d5b1bdfddf3724d0c3e2621f7960674322a
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Wed Mar 29 17:25:55 2017 +0200
vcl PDF export, norefxobj: copy each object only once
Even if they are referenced multiple times. This is especially important
as objects can refer to each other, creating a cyclic graph. But it also
makes the output a tiny bit smaller.
Change-Id: I561ac319683a19a797282fe259cc68f3a4c50c3e
Reviewed-on: https://gerrit.libreoffice.org/35855
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit 92ddc0409c8d3276183afdee543d28e1c307c2c7)
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index d132b3ff887e..51eed7262bbe 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11697,9 +11697,16 @@ void PDFWriterImpl::writeJPG( JPGEmit& rObject )
writeReferenceXObject(rObject.m_aReferenceXObject);
}
-sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter::PDFObjectElement& rObject)
+sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter::PDFObjectElement& rObject, std::map<sal_Int32, sal_Int32>& rCopiedResources)
{
+ auto it = rCopiedResources.find(rObject.GetObjectValue());
+ if (it != rCopiedResources.end())
+ // This resource was already copied once, nothing to do.
+ return it->second;
+
sal_Int32 nObject = createObject();
+ // Remember what is the ID of this object in our output.
+ rCopiedResources[rObject.GetObjectValue()] = nObject;
SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::copyExternalResource: " << rObject.GetObjectValue() << " -> " << nObject);
OStringBuffer aLine;
@@ -11722,7 +11729,7 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
if (pReferenced)
{
// Copy the referenced object.
- sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced);
+ sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced, rCopiedResources);
sal_uInt64 nReferenceStart = pDictionary->GetKeyOffset(rItem.first) + rItem.first.getLength();
sal_uInt64 nReferenceEnd = pDictionary->GetKeyOffset(rItem.first) + pDictionary->GetKeyValueLength(rItem.first);
@@ -11784,7 +11791,7 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
if (pReferenced)
{
// Copy the referenced object.
- sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced);
+ sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced, rCopiedResources);
sal_uInt64 nReferenceStart = pReference->GetObjectElement().GetLocation();
sal_uInt64 nReferenceEnd = pReference->GetOffset();
@@ -11833,7 +11840,7 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
return nObject;
}
-OString PDFWriterImpl::copyExternalResources(filter::PDFObjectElement& rPage, const OString& rKind)
+OString PDFWriterImpl::copyExternalResources(filter::PDFObjectElement& rPage, const OString& rKind, std::map<sal_Int32, sal_Int32>& rCopiedResources)
{
// A name - object ID map, IDs as they appear in our output, not the
// original ones.
@@ -11863,7 +11870,7 @@ OString PDFWriterImpl::copyExternalResources(filter::PDFObjectElement& rPage, co
continue;
// Then copying over an object copy its dictionary and its stream.
- sal_Int32 nObject = copyExternalResource(rDocBuffer, *pValue);
+ sal_Int32 nObject = copyExternalResource(rDocBuffer, *pValue, rCopiedResources);
aRet[rItem.first] = nObject;
}
@@ -11947,8 +11954,10 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
"Font",
"XObject"
};
+ // Maps from source object id (PDF image) to target object id (export result).
+ std::map<sal_Int32, sal_Int32> aCopiedResources;
for (const auto& rKey : aKeys)
- aLine.append(copyExternalResources(*pPage, rKey));
+ aLine.append(copyExternalResources(*pPage, rKey, aCopiedResources));
aLine.append(">>");
aLine.append(" /BBox [ 0 0 ");
aLine.append(aSize.Width());
diff --git a/vcl/source/gdi/pdfwriter_impl.hxx b/vcl/source/gdi/pdfwriter_impl.hxx
index 2329cbdec0bb..e1750ab8b73b 100644
--- a/vcl/source/gdi/pdfwriter_impl.hxx
+++ b/vcl/source/gdi/pdfwriter_impl.hxx
@@ -861,10 +861,10 @@ i12626
void writeReferenceXObject(ReferenceXObjectEmit& rEmit);
/// Copies resources of a given kind from an external page to the output,
/// returning what has to be included in the new resource dictionary.
- OString copyExternalResources(filter::PDFObjectElement& rPage, const OString& rKind);
+ OString copyExternalResources(filter::PDFObjectElement& rPage, const OString& rKind, std::map<sal_Int32, sal_Int32>& rCopiedResources);
/// Copies a single resource from an external document, returns the new
/// object ID in our document.
- sal_Int32 copyExternalResource(SvMemoryStream& rDocBuffer, filter::PDFObjectElement& rObject);
+ sal_Int32 copyExternalResource(SvMemoryStream& rDocBuffer, filter::PDFObjectElement& rObject, std::map<sal_Int32, sal_Int32>& rCopiedResources);
/* tries to find the bitmap by its id and returns its emit data if exists,
else creates a new emit data block */
commit 10ec3d39c381547bbdf03f7ec3eebe7005ffdfe7
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Wed Mar 29 14:24:02 2017 +0200
vcl PDF export, norefxobj: avoid replacement bitmap
The whole point of "no reference XObjects" is knowing this vector markup
is supported everywhere, so no need to provide a fallback bitmap.
It was already unreferenced, but now it's not even written to the file,
making the PDF export result smaller.
Change-Id: Idf766c8eeded4235ebea49d13698a13c6b60f014
Reviewed-on: https://gerrit.libreoffice.org/35841
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit 30102ded91b9ecfea172ffc6443154230ee37cbd)
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 7096e197296e..d132b3ff887e 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11619,6 +11619,12 @@ bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
void PDFWriterImpl::writeJPG( JPGEmit& rObject )
{
+ if (rObject.m_aReferenceXObject.m_aPDFData.hasElements() && !m_aContext.UseReferenceXObject)
+ {
+ writeReferenceXObject(rObject.m_aReferenceXObject);
+ return;
+ }
+
CHECK_RETURN2( rObject.m_pStream );
CHECK_RETURN2( updateObject( rObject.m_nObject ) );
@@ -12080,6 +12086,12 @@ namespace
bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
{
+ if (rObject.m_aReferenceXObject.m_aPDFData.hasElements() && !m_aContext.UseReferenceXObject)
+ {
+ writeReferenceXObject(rObject.m_aReferenceXObject);
+ return true;
+ }
+
CHECK_RETURN( updateObject( rObject.m_nObject ) );
Bitmap aBitmap;
@@ -12552,7 +12564,8 @@ const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx
m_aBitmaps.push_front( BitmapEmit() );
m_aBitmaps.front().m_aID = aID;
m_aBitmaps.front().m_aBitmap = aBitmap;
- m_aBitmaps.front().m_nObject = createObject();
+ if (!rGraphic.getPdfData().hasElements() || m_aContext.UseReferenceXObject)
+ m_aBitmaps.front().m_nObject = createObject();
createEmbeddedFile(rGraphic, m_aBitmaps.front().m_aReferenceXObject, m_aBitmaps.front().m_nObject);
it = m_aBitmaps.begin();
}
commit a1b536a3b2bc327e2c8e7817a34d067412713142
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Wed Mar 29 09:47:09 2017 +0200
vcl PDF export, norefxobj: add UI for this
Disable the "use reference XObjects" (old behavior) by default, but keep
it as an option in case someone wants it.
Summary till the help is updated: the old way is simpler code, so it's
always correct, but really only Acrobat supports that markup. The new
way is supported by all readers, but more complex, so it's more likely
it goes wrong.
Change-Id: I4769474f29d98412be496a0aa4e8254ae4f0919e
Reviewed-on: https://gerrit.libreoffice.org/35826
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit 9c944b0d1bff9a0ab1b7e8454c9ac5e7194aa533)
diff --git a/filter/source/pdf/impdialog.cxx b/filter/source/pdf/impdialog.cxx
index 52bae8de8f12..5a76a2178190 100644
--- a/filter/source/pdf/impdialog.cxx
+++ b/filter/source/pdf/impdialog.cxx
@@ -82,6 +82,7 @@ ImpPDFTabDialog::ImpPDFTabDialog(vcl::Window* pParent, Sequence< PropertyValue >
mbUseTaggedPDF( false ),
mbExportNotes( true ),
mbViewPDF( false ),
+ mbUseReferenceXObject( false ),
mbExportNotesPages( false ),
mbExportOnlyNotesPages( false ),
mbUseTransitionEffects( false ),
@@ -469,6 +470,7 @@ Sequence< PropertyValue > ImpPDFTabDialog::GetFilterData()
aRet.push_back(comphelper::makePropertyValue("SignaturePassword", msSignPassword));
aRet.push_back(comphelper::makePropertyValue("SignatureCertificate", maSignCertificate));
aRet.push_back(comphelper::makePropertyValue("SignatureTSA", msSignTSA));
+ aRet.push_back(comphelper::makePropertyValue("UseReferenceXObject", mbUseReferenceXObject));
return comphelper::concatSequences(maConfigItem.GetFilterData(), comphelper::containerToSequence(aRet));
}
@@ -511,6 +513,7 @@ ImpPDFTabGeneralPage::ImpPDFTabGeneralPage(vcl::Window* pParent, const SfxItemSe
get(mpCbExportEmptyPages, "emptypages");
get(mpCbExportPlaceholders, "exportplaceholders" );
get(mpCbViewPDF, "viewpdf");
+ get(mpCbUseReferenceXObject, "usereferencexobject");
get(mpCbWatermark, "watermark");
get(mpFtWatermark, "watermarklabel");
@@ -546,6 +549,7 @@ void ImpPDFTabGeneralPage::dispose()
mpCbExportHiddenSlides.clear();
mpCbExportNotes.clear();
mpCbViewPDF.clear();
+ mpCbUseReferenceXObject.clear();
mpCbExportNotesPages.clear();
mpCbExportOnlyNotesPages.clear();
mpCbExportEmptyPages.clear();
@@ -676,6 +680,7 @@ void ImpPDFTabGeneralPage::GetFilterConfigItem( ImpPDFTabDialog* paParent )
paParent->mnMaxImageResolution = mpCoReduceImageResolution->GetText().toInt32();
paParent->mbExportNotes = mpCbExportNotes->IsChecked();
paParent->mbViewPDF = mpCbViewPDF->IsChecked();
+ paParent->mbUseReferenceXObject = mpCbUseReferenceXObject->IsChecked();
if ( mbIsPresentation )
{
paParent->mbExportNotesPages = mpCbExportNotesPages->IsChecked();
diff --git a/filter/source/pdf/impdialog.hxx b/filter/source/pdf/impdialog.hxx
index c2ae742744db..29d4b43b0c85 100644
--- a/filter/source/pdf/impdialog.hxx
+++ b/filter/source/pdf/impdialog.hxx
@@ -104,6 +104,7 @@ protected:
sal_Int32 mnPDFTypeSelection;
bool mbExportNotes;
bool mbViewPDF;
+ bool mbUseReferenceXObject;
bool mbExportNotesPages;
bool mbExportOnlyNotesPages;
bool mbUseTransitionEffects;
@@ -220,6 +221,7 @@ class ImpPDFTabGeneralPage : public SfxTabPage
VclPtr<CheckBox> mpCbExportHiddenSlides;
VclPtr<CheckBox> mpCbExportNotes;
VclPtr<CheckBox> mpCbViewPDF;
+ VclPtr<CheckBox> mpCbUseReferenceXObject;
VclPtr<CheckBox> mpCbExportNotesPages;
VclPtr<CheckBox> mpCbExportOnlyNotesPages;
diff --git a/filter/source/pdf/pdfexport.cxx b/filter/source/pdf/pdfexport.cxx
index 8d4e61ccf6f4..e62fc108c86c 100644
--- a/filter/source/pdf/pdfexport.cxx
+++ b/filter/source/pdf/pdfexport.cxx
@@ -47,7 +47,6 @@
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/compbase.hxx>
#include <cppuhelper/basemutex.hxx>
-#include <officecfg/Office/Common.hxx>
#include "pdfexport.hxx"
#include "impdialog.hxx"
@@ -96,6 +95,7 @@ PDFExport::PDFExport( const Reference< XComponent >& rxSrcDoc,
mbExportNotes ( true ),
mbExportPlaceholders ( false ),
mbViewPDF ( true ),
+ mbUseReferenceXObject ( false ),
mbExportNotesPages ( false ),
mbExportOnlyNotesPages ( false ),
mbUseTransitionEffects ( true ),
@@ -560,6 +560,8 @@ bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >&
rFilterData[ nData ].Value >>= msSignTSA;
else if ( rFilterData[ nData ].Name == "ExportPlaceholders" )
rFilterData[ nData ].Value >>= mbExportPlaceholders;
+ else if ( rFilterData[ nData ].Name == "UseReferenceXObject" )
+ rFilterData[ nData ].Value >>= mbUseReferenceXObject;
}
aContext.URL = aURL.GetMainURL(INetURLObject::DECODE_TO_IURI);
@@ -783,8 +785,7 @@ bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >&
aContext.SignPassword = msSignPassword;
aContext.SignCertificate = maSignCertificate;
aContext.SignTSA = msSignTSA;
- // Not using reference XObjects is experimental for now.
- aContext.UseReferenceXObject = !officecfg::Office::Common::Misc::ExperimentalMode::get();
+ aContext.UseReferenceXObject = mbUseReferenceXObject;
// all context data set, time to create the printing device
std::unique_ptr<vcl::PDFWriter> pPDFWriter(new vcl::PDFWriter( aContext, xEnc ));
diff --git a/filter/source/pdf/pdfexport.hxx b/filter/source/pdf/pdfexport.hxx
index 5b9391ad60e9..4051cd88ff21 100644
--- a/filter/source/pdf/pdfexport.hxx
+++ b/filter/source/pdf/pdfexport.hxx
@@ -46,6 +46,7 @@ private:
bool mbExportNotes;
bool mbExportPlaceholders;
bool mbViewPDF;
+ bool mbUseReferenceXObject;
bool mbExportNotesPages;
bool mbExportOnlyNotesPages;
bool mbUseTransitionEffects;
diff --git a/filter/uiconfig/ui/pdfgeneralpage.ui b/filter/uiconfig/ui/pdfgeneralpage.ui
index 836fd2504ffd..8301c7c1af81 100644
--- a/filter/uiconfig/ui/pdfgeneralpage.ui
+++ b/filter/uiconfig/ui/pdfgeneralpage.ui
@@ -427,7 +427,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="top_padding">6</property>
- <property name="left_padding">12</property>
+ <property name="left_padding">13</property>
<child>
<object class="GtkGrid" id="grid10">
<property name="visible">True</property>
@@ -638,6 +638,21 @@
</packing>
</child>
<child>
+ <object class="GtkCheckButton" id="usereferencexobject">
+ <property name="label" translatable="yes">Use reference XObjects</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">13</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkCheckButton" id="hiddenpages">
<property name="label" translatable="yes">Export _hidden pages</property>
<property name="visible">True</property>
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index 4e80f08d9e45..fb0bd8b3c597 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -13,6 +13,7 @@
#include <com/sun/star/frame/XStorable.hpp>
#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
#include <cppuhelper/implbase.hxx>
#include <test/bootstrapfixture.hxx>
#include <unotest/macros_test.hxx>
@@ -87,6 +88,12 @@ void PdfExportTest::testTdf106059()
aTempFile.EnableKillingFile();
utl::MediaDescriptor aMediaDescriptor;
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ // Explicitly enable the usage of the reference XObject markup.
+ uno::Sequence<beans::PropertyValue> aFilterData =
+ {
+ comphelper::makePropertyValue("UseReferenceXObject", true)
+ };
+ aMediaDescriptor["FilterData"] <<= aFilterData;
xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
// Parse the export result.
commit 2d298ce44645861db5510480b45e1ef9e439fa50
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Tue Mar 28 17:55:06 2017 +0200
vcl PDF export, norefxobj: have the list of keys to copy at one place
To avoid repeting ourselves.
Change-Id: I39667620b9cf391251327c8f66ad8b9649ead36f
Reviewed-on: https://gerrit.libreoffice.org/35810
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit f86c3fd8e95f378061d57b77d1c700e076996086)
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 40c41235351c..7096e197296e 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11917,11 +11917,6 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
return;
}
- OString sColorSpaces = copyExternalResources(*pPage, "ColorSpace");
- OString sExtGStates = copyExternalResources(*pPage, "ExtGState");
- OString sFonts = copyExternalResources(*pPage, "Font");
- OString sXObjects = copyExternalResources(*pPage, "XObject");
-
filter::PDFObjectElement* pPageContents = pPage->LookupObject("Contents");
if (!pPageContents)
{
@@ -11932,8 +11927,6 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
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);
@@ -11941,10 +11934,15 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
aLine.append("<< /Type /XObject");
aLine.append(" /Subtype /Form");
aLine.append(" /Resources <<");
- aLine.append(sColorSpaces);
- aLine.append(sExtGStates);
- aLine.append(sFonts);
- aLine.append(sXObjects);
+ static const std::initializer_list<OString> aKeys =
+ {
+ "ColorSpace",
+ "ExtGState",
+ "Font",
+ "XObject"
+ };
+ for (const auto& rKey : aKeys)
+ aLine.append(copyExternalResources(*pPage, rKey));
aLine.append(">>");
aLine.append(" /BBox [ 0 0 ");
aLine.append(aSize.Width());
@@ -11976,6 +11974,8 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
// Copy the original page stream to the form XObject stream.
aLine.append(static_cast<const sal_Char*>(rPageStream.GetData()), rPageStream.GetSize());
aLine.append("\nendstream\nendobj\n\n");
+ if (!updateObject(nWrappedFormObject))
+ return;
CHECK_RETURN2(writeBuffer(aLine.getStr(), aLine.getLength()));
}
commit 7bf1b20b8e890b66ae878a56661b04d77801b17d
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Tue Mar 28 16:14:11 2017 +0200
tdf#106693 vcl PDF export, norefxobj: handle multiple refs in copied arrays
Also fix confusion about dictionaries in arrays and arrays in
dictionaries.
Change-Id: I0d71d5796b1eb4f89e3fd9a5b1f807d2a7340a35
Reviewed-on: https://gerrit.libreoffice.org/35806
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit 30608c66374f8effa9d534f7f9a22d41daa9770f)
diff --git a/include/vcl/filter/pdfdocument.hxx b/include/vcl/filter/pdfdocument.hxx
index d6b44e88d027..4bed3c32737a 100644
--- a/include/vcl/filter/pdfdocument.hxx
+++ b/include/vcl/filter/pdfdocument.hxx
@@ -109,8 +109,10 @@ class VCL_DLLPUBLIC PDFArrayElement : public PDFElement
/// Location after the '[' token.
sal_uInt64 m_nOffset = 0;
std::vector<PDFElement*> m_aElements;
+ /// The object that contains this array.
+ PDFObjectElement* m_pObject;
public:
- PDFArrayElement();
+ PDFArrayElement(PDFObjectElement* pObject);
bool Read(SvStream& rStream) override;
void PushBack(PDFElement* pElement);
const std::vector<PDFElement*>& GetElements();
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx
index 3e59ac1f63dd..b50105565e3c 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -938,7 +938,7 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
}
case '[':
{
- auto pArr = new PDFArrayElement();
+ auto pArr = new PDFArrayElement(pObject);
rElements.push_back(std::unique_ptr<PDFElement>(pArr));
if (nDictionaryDepth == 0 && nArrayDepth == 0)
{
@@ -962,9 +962,10 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
case ']':
{
rElements.push_back(std::unique_ptr<PDFElement>(new PDFEndArrayElement()));
- pArray = nullptr;
- rStream.SeekRel(-1);
--nArrayDepth;
+ if (nArrayDepth == 0)
+ pArray = nullptr;
+ rStream.SeekRel(-1);
if (nDictionaryDepth == 0 && nArrayDepth == 0)
{
if (pObject)
@@ -2124,18 +2125,25 @@ size_t PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement
PDFArrayElement* pArray = nullptr;
sal_uInt64 nDictionaryOffset = 0;
int nDictionaryDepth = 0;
+ // Toplevel dictionary found (not inside an array).
+ bool bDictionaryFound = false;
+ // Toplevel array found (not inside a dictionary).
+ bool bArrayFound = false;
for (size_t i = nIndex; i < rElements.size(); ++i)
{
// Dictionary tokens can be nested, track enter/leave.
if (auto pDictionary = dynamic_cast<PDFDictionaryElement*>(rElements[i].get()))
{
+ bDictionaryFound = true;
if (++nDictionaryDepth == 1)
{
// First dictionary start, track start offset.
nDictionaryOffset = pDictionary->m_nLocation;
if (pThisObject)
{
- pThisObject->SetDictionary(pDictionary);
+ if (!bArrayFound)
+ // The the toplevel dictionary of the object.
+ pThisObject->SetDictionary(pDictionary);
pThisDictionary = pDictionary;
pThisObject->SetDictionaryOffset(nDictionaryOffset);
}
@@ -2186,7 +2194,11 @@ size_t PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement
else
{
if (pArray)
- pArray->PushBack(pName);
+ {
+ if (bDictionaryFound)
+ // Array inside dictionary.
+ pArray->PushBack(pName);
+ }
else
{
// Name-name key-value.
@@ -2205,6 +2217,7 @@ size_t PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement
auto pArr = dynamic_cast<PDFArrayElement*>(rElements[i].get());
if (pArr)
{
+ bArrayFound = true;
pArray = pArr;
continue;
}
@@ -2245,7 +2258,9 @@ size_t PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement
}
else
{
- pArray->PushBack(pReference);
+ if (bDictionaryFound)
+ // Array inside dictionary.
+ pArray->PushBack(pReference);
}
aNumbers.clear();
continue;
@@ -2928,7 +2943,10 @@ bool PDFEndObjectElement::Read(SvStream& /*rStream*/)
return true;
}
-PDFArrayElement::PDFArrayElement() = default;
+PDFArrayElement::PDFArrayElement(PDFObjectElement* pObject)
+ : m_pObject(pObject)
+{
+}
bool PDFArrayElement::Read(SvStream& rStream)
{
@@ -2948,6 +2966,8 @@ bool PDFArrayElement::Read(SvStream& rStream)
void PDFArrayElement::PushBack(PDFElement* pElement)
{
+ if (m_pObject)
+ SAL_INFO("vcl.filter", "PDFArrayElement::PushBack: object is " << m_pObject->GetObjectValue());
m_aElements.push_back(pElement);
}
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 0d1eee6f753a..40c41235351c 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11694,6 +11694,7 @@ void PDFWriterImpl::writeJPG( JPGEmit& rObject )
sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter::PDFObjectElement& rObject)
{
sal_Int32 nObject = createObject();
+ SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::copyExternalResource: " << rObject.GetObjectValue() << " -> " << nObject);
OStringBuffer aLine;
aLine.append(nObject);
@@ -11766,7 +11767,8 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
const std::vector<filter::PDFElement*>& rElements = pArray->GetElements();
bool bDone = false;
- // Complex case: can't copy the array byte array as is, as it contains a reference.
+ // Complex case: can't copy the array byte array as is, as it may contain references.
+ sal_uInt64 nCopyStart = 0;
for (const auto pElement : rElements)
{
auto pReference = dynamic_cast<filter::PDFReferenceElement*>(pElement);
@@ -11778,28 +11780,37 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
// Copy the referenced object.
sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced);
- sal_uInt64 nArrStart = rObject.GetArrayOffset();
sal_uInt64 nReferenceStart = pReference->GetObjectElement().GetLocation();
sal_uInt64 nReferenceEnd = pReference->GetOffset();
- sal_uInt64 nArrEnd = nArrStart + rObject.GetArrayLength();
+ sal_uInt64 nOffset = 0;
+ if (nCopyStart == 0)
+ // Array start -> reference start.
+ nOffset = rObject.GetArrayOffset();
+ else
+ // Previous reference end -> reference start.
+ nOffset = nCopyStart;
+ aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nOffset, nReferenceStart - nOffset);
- // Array start -> reference start.
- aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nArrStart, nReferenceStart - nArrStart);
// Write the updated reference.
aLine.append(" ");
aLine.append(nRef);
aLine.append(" 0 R");
- // Reference end -> array end.
- aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nReferenceEnd, nArrEnd - nReferenceEnd);
+ // Start copying here next time.
+ nCopyStart = nReferenceEnd;
bDone = true;
- break;
}
}
}
- // Can copy it as-is.
- if (!bDone)
+ if (bDone)
+ {
+ // Copy the last part here, in the complex case.
+ sal_uInt64 nArrEnd = rObject.GetArrayOffset() + rObject.GetArrayLength();
+ aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nCopyStart, nArrEnd - nCopyStart);
+ }
+ else
+ // Can copy it as-is.
aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + rObject.GetArrayOffset(), rObject.GetArrayLength());
aLine.append("]\n");
commit a4c24675a14d324e9103f36300c099f25c2fdc21
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Tue Mar 28 12:10:03 2017 +0200
tdf#106693 vcl PDF export, norefxobj: copy nested arrays correctly
When copying an array we're only interested in the start/end position of
the outermost array, otherwise only part of the array is copied.
Change-Id: I9f5cb5e3ed395142fd82db34e1153ddfdf9f0eb3
Reviewed-on: https://gerrit.libreoffice.org/35797
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit 3ea5e3401e567bfe956817fd5abd17530da664f5)
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx
index 6b2bfae3e989..3e59ac1f63dd 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -875,6 +875,8 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
bool bInStartXRef = false;
// Dictionary depth, so we know when we're outside any dictionaries.
int nDictionaryDepth = 0;
+ // Array depth, only the offset/length of the toplevel array is tracked.
+ int nArrayDepth = 0;
// Last seen array token that's outside any dictionaries.
PDFArrayElement* pArray = nullptr;
while (true)
@@ -938,7 +940,7 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
{
auto pArr = new PDFArrayElement();
rElements.push_back(std::unique_ptr<PDFElement>(pArr));
- if (nDictionaryDepth == 0)
+ if (nDictionaryDepth == 0 && nArrayDepth == 0)
{
// The array is attached directly, inform the object.
pArray = pArr;
@@ -948,6 +950,7 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
pObject->SetArrayOffset(rStream.Tell());
}
}
+ ++nArrayDepth;
rStream.SeekRel(-1);
if (!rElements.back()->Read(rStream))
{
@@ -961,7 +964,8 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
rElements.push_back(std::unique_ptr<PDFElement>(new PDFEndArrayElement()));
pArray = nullptr;
rStream.SeekRel(-1);
- if (nDictionaryDepth == 0)
+ --nArrayDepth;
+ if (nDictionaryDepth == 0 && nArrayDepth == 0)
{
if (pObject)
{
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 45655260a27e..0d1eee6f753a 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11906,6 +11906,8 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
return;
}
+ OString sColorSpaces = copyExternalResources(*pPage, "ColorSpace");
+ OString sExtGStates = copyExternalResources(*pPage, "ExtGState");
OString sFonts = copyExternalResources(*pPage, "Font");
OString sXObjects = copyExternalResources(*pPage, "XObject");
@@ -11928,6 +11930,8 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
aLine.append("<< /Type /XObject");
aLine.append(" /Subtype /Form");
aLine.append(" /Resources <<");
+ aLine.append(sColorSpaces);
+ aLine.append(sExtGStates);
aLine.append(sFonts);
aLine.append(sXObjects);
aLine.append(">>");
commit 2c98939545d80198d37e2df23d5342105b50a557
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Tue Mar 28 09:06:09 2017 +0200
tdf#106694 RTF import: fix missing paragraph tab position
The problem here was that while in general paragraph style / direct
formatting deduplication is supposed to happen in the tokenizer,
paragraph tab positions is an exception, and dmapper expects to see the
duplicated tokens.
Fix the problem by introducing a blacklist that contains tokens not to
deduplicate.
Change-Id: I1cca53e99cfdb082df389ff295f3447cc8f9d3b8
Reviewed-on: https://gerrit.libreoffice.org/35790
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit fea174753b1c6b0882aebb044bf1a1eef6fa50e0)
diff --git a/sw/qa/extras/rtfimport/data/tdf106694.rtf b/sw/qa/extras/rtfimport/data/tdf106694.rtf
new file mode 100644
index 000000000000..9abcb205bbeb
--- /dev/null
+++ b/sw/qa/extras/rtfimport/data/tdf106694.rtf
@@ -0,0 +1,10 @@
+{\rtf1\ansi\deflang3081\ftnbj\uc1\deff0
+{\colortbl ;\red255\green255\blue255 ;\red0\green0\blue0 ;\red54\green95\blue145 ;\red79\green129\blue188 ;\red255\green0\blue0 ;\red255\green255\blue128 ;\red128\green0\blue0 ;\red127\green127\blue127 ;\red35\green62\blue95 ;\red63\green63\blue63 ;\red95\green95\blue95 ;\red47\green47\blue47 ;\red0\green64\blue128 ;\red79\green79\blue79 ;\red111\green111\blue111 ;\red0\green0\blue255 ;\red239\green239\blue239 ;\red192\green1\blue1 ;}
+{\stylesheet
+{\f0\fs24 Normal;}
+{\s22\snext0\f1\fs18\b\tqr\tldot\tx8280\fi0\li0\ri720\sb120\sa40\sl0 TOC 1
+;}
+}
+\pard\ssparaaux0\s22\tqr\tldot\tx8280\ri720\sb120\sa40\ql\outlinelevel0\plain\f0\fs24\plain\f2\fs18\hich\f2\dbch\f2\loch\f2\fs18\b
+Model Detail\tab 2\par
+}
diff --git a/sw/qa/extras/rtfimport/rtfimport.cxx b/sw/qa/extras/rtfimport/rtfimport.cxx
index 79f82d72dedb..8fee2ab2fcd1 100644
--- a/sw/qa/extras/rtfimport/rtfimport.cxx
+++ b/sw/qa/extras/rtfimport/rtfimport.cxx
@@ -2752,6 +2752,14 @@ DECLARE_RTFIMPORT_TEST(testTdf105729, "tdf105729.rtf")
CPPUNIT_ASSERT_EQUAL(style::ParagraphAdjust_CENTER, static_cast<style::ParagraphAdjust>(getProperty<sal_Int16>(getParagraph(1), "ParaAdjust")));
}
+DECLARE_RTFIMPORT_TEST(testTdf106694, "tdf106694.rtf")
+{
+ auto aTabs = getProperty< uno::Sequence<style::TabStop> >(getParagraph(1), "ParaTabStops");
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), aTabs.getLength());
+ // This was 0, tab position was incorrect, looked like it was missing.
+ CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(14605), aTabs[0].Position);
+}
+
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/rtftok/rtfsprm.cxx b/writerfilter/source/rtftok/rtfsprm.cxx
index da7bc815c6ca..7afee14a4189 100644
--- a/writerfilter/source/rtftok/rtfsprm.cxx
+++ b/writerfilter/source/rtftok/rtfsprm.cxx
@@ -153,6 +153,23 @@ static RTFValue::Pointer_t getDefaultSPRM(Id const id)
}
}
+/// Is it problematic to deduplicate this SPRM?
+static bool isSPRMDeduplicateBlacklist(Id nId)
+{
+ switch (nId)
+ {
+ case NS_ooxml::LN_CT_TabStop_val:
+ case NS_ooxml::LN_CT_TabStop_leader:
+ case NS_ooxml::LN_CT_TabStop_pos:
+ // See the NS_ooxml::LN_CT_PPrBase_tabs handler in DomainMapper,
+ // deduplication is explicitly not wanted for these tokens.
+ return true;
+
+ default:
+ return false;
+ }
+}
+
/// Does the clone / deduplication of a single sprm.
static void cloneAndDeduplicateSprm(std::pair<Id, RTFValue::Pointer_t>& rSprm, RTFSprms& ret)
{
@@ -161,7 +178,8 @@ static void cloneAndDeduplicateSprm(std::pair<Id, RTFValue::Pointer_t>& rSprm, R
{
if (rSprm.second->equals(*pValue))
{
- ret.erase(rSprm.first); // duplicate to style
+ if (!isSPRMDeduplicateBlacklist(rSprm.first))
+ ret.erase(rSprm.first); // duplicate to style
}
else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty())
{
commit 5a71e1e80def5bac7931e580ffc2203659802947
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Mon Mar 27 15:39:04 2017 +0200
tdf#106693 vcl PDF export, norefxobj: handle multiple refs in copied dicts
When copying font definitions the dictionary has multiple values where
the type is a reference. Improve PDFWriterImpl::copyExternalResource(),
so that multiple references are copied correctly as well.
With this the bugdoc (from comment 5) text appears in the output.
Reviewed-on: https://gerrit.libreoffice.org/35760
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit 2ba9d58d5978c94352c4c6cf9c47aa3de79d05fe)
Change-Id: I2343e616d8b36e3cdcbd4e713bd3f7fa7bce6d3b
diff --git a/include/vcl/filter/pdfdocument.hxx b/include/vcl/filter/pdfdocument.hxx
index 9ccbb43d0225..d6b44e88d027 100644
--- a/include/vcl/filter/pdfdocument.hxx
+++ b/include/vcl/filter/pdfdocument.hxx
@@ -80,10 +80,12 @@ public:
sal_uInt64 GetDictionaryOffset();
void SetDictionaryLength(sal_uInt64 nDictionaryLength);
sal_uInt64 GetDictionaryLength();
- PDFDictionaryElement* GetDictionary() const;
+ PDFDictionaryElement* GetDictionary();
void SetDictionary(PDFDictionaryElement* pDictionaryElement);
/// Get access to the parsed key-value items from the object dictionary.
const std::map<OString, PDFElement*>& GetDictionaryItems() const;
+ /// Same as GetDictionaryItems(), but entries are sorted by file offset.
+ std::vector< std::pair<OString, PDFElement*> > GetDictionaryItemsByOffset();
void SetArray(PDFArrayElement* pArrayElement);
void SetStream(PDFStreamElement* pStreamElement);
/// Access to the stream of the object, if it has any.
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx
index 4b5c8b18ea45..6b2bfae3e989 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -2443,8 +2443,10 @@ sal_uInt64 PDFObjectElement::GetArrayLength()
return m_nArrayLength;
}
-PDFDictionaryElement* PDFObjectElement::GetDictionary() const
+PDFDictionaryElement* PDFObjectElement::GetDictionary()
{
+ if (m_aDictionary.empty())
+ PDFDictionaryElement::Parse(m_rDoc.GetElements(), this, m_aDictionary);
return m_pDictionaryElement;
}
@@ -2453,6 +2455,25 @@ void PDFObjectElement::SetDictionary(PDFDictionaryElement* pDictionaryElement)
m_pDictionaryElement = pDictionaryElement;
}
+std::vector< std::pair<OString, PDFElement*> > PDFObjectElement::GetDictionaryItemsByOffset()
+{
+ std::vector< std::pair<OString, PDFElement*> > aRet;
+
+ for (const auto& rItem : m_aDictionary)
+ aRet.push_back(rItem);
+
+ PDFDictionaryElement* pDictionary = GetDictionary();
+ if (!pDictionary)
+ return aRet;
+
+ std::sort(aRet.begin(), aRet.end(), [pDictionary](const std::pair<OString, PDFElement*>& a, const std::pair<OString, PDFElement*>& b) -> bool
+ {
+ return pDictionary->GetKeyOffset(a.first) < pDictionary->GetKeyOffset(b.first);
+ });
+
+ return aRet;
+}
+
const std::map<OString, PDFElement*>& PDFObjectElement::GetDictionaryItems() const
{
return m_aDictionary;
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 794c79595bbb..45655260a27e 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11702,10 +11702,11 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
{
aLine.append("<<");
- // Complex case: can't copy the dictionary byte array as is, as it contains a reference.
+ // Complex case: can't copy the dictionary byte array as is, as it may contain references.
bool bDone = false;
- const std::map<OString, filter::PDFElement*>& rItems = rObject.GetDictionaryItems();
- for (const auto& rItem : rItems)
+ std::vector< std::pair<OString, filter::PDFElement*> > aItems = rObject.GetDictionaryItemsByOffset();
+ sal_uInt64 nCopyStart = 0;
+ for (const auto& rItem : aItems)
{
auto pReference = dynamic_cast<filter::PDFReferenceElement*>(rItem.second);
if (pReference)
@@ -11716,27 +11717,36 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
// Copy the referenced object.
sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced);
- sal_uInt64 nDictStart = rObject.GetDictionaryOffset();
sal_uInt64 nReferenceStart = pDictionary->GetKeyOffset(rItem.first) + rItem.first.getLength();
sal_uInt64 nReferenceEnd = pDictionary->GetKeyOffset(rItem.first) + pDictionary->GetKeyValueLength(rItem.first);
- sal_uInt64 nDictEnd = nDictStart + rObject.GetDictionaryLength();
- // Dict start -> reference start.
- aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nDictStart, nReferenceStart - nDictStart);
+ sal_uInt64 nOffset = 0;
+ if (nCopyStart == 0)
+ // Dict start -> reference start.
+ nOffset = rObject.GetDictionaryOffset();
+ else
+ // Previous reference end -> reference start.
+ nOffset = nCopyStart;
+ aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nOffset, nReferenceStart - nOffset);
// Write the updated reference.
aLine.append(" ");
aLine.append(nRef);
aLine.append(" 0 R");
- // Reference end -> dict end.
- aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nReferenceEnd, nDictEnd - nReferenceEnd);
+ // Start copying here next time.
+ nCopyStart = nReferenceEnd;
bDone = true;
- break;
}
}
}
- // Can copy it as-is.
- if (!bDone)
+ if (bDone)
+ {
+ // Copy the last part here, in the complex case.
+ sal_uInt64 nDictEnd = rObject.GetDictionaryOffset() + rObject.GetDictionaryLength();
+ aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nCopyStart, nDictEnd - nCopyStart);
+ }
+ else
+ // Can copy it as-is.
aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + rObject.GetDictionaryOffset(), rObject.GetDictionaryLength());
aLine.append(">>\n");
@@ -11896,6 +11906,7 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
return;
}
+ OString sFonts = copyExternalResources(*pPage, "Font");
OString sXObjects = copyExternalResources(*pPage, "XObject");
filter::PDFObjectElement* pPageContents = pPage->LookupObject("Contents");
@@ -11917,6 +11928,7 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
aLine.append("<< /Type /XObject");
aLine.append(" /Subtype /Form");
aLine.append(" /Resources <<");
+ aLine.append(sFonts);
aLine.append(sXObjects);
aLine.append(">>");
aLine.append(" /BBox [ 0 0 ");
commit d3ebd862bb8e0d398af4fe004c042deff5dc029c
Author: Stephan Bergmann <sbergman at redhat.com>
Date: Wed Mar 22 10:03:40 2017 +0100
Missing check for rStream.IsEof()
...after a324099538916eae7f7239d32fd98ec8018cbb72 "xmlsecurity PDF signing: only
write incremental xref in an incremental update" inserted the 'if' before the
'while (!rStream.IsEof())'
Change-Id: Ib527894031f356c3d6df40b70259469ef4c338de
(cherry picked from commit e8aaaa52fa5abe4a70224ab6e6eee6265b0d61c8)
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx
index f2de0bec68f1..4b5c8b18ea45 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -1927,6 +1927,10 @@ bool PDFNumberElement::Read(SvStream& rStream)
m_nOffset = rStream.Tell();
char ch;
rStream.ReadChar(ch);
+ if (rStream.IsEof())
+ {
+ return false;
+ }
if (!isdigit(ch) && ch != '-' && ch != '.')
{
rStream.SeekRel(-1);
commit ff2ad09cb3117ff0654875aa30d5320e001bd8be
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Mon Mar 27 14:21:53 2017 +0200
vcl PDF export, norefxobj: improve ref handling in dicts
When copying objects referenced from the page stream support references
in any item value, not just for one single item key.
Also move the dictionary entry generator code to
PDFWriterImpl::copyExternalResources(), so other keys can be copied
without code duplication.
Change-Id: I4004e82014cec915c66a8a9d3aed2155fa2452ef
Reviewed-on: https://gerrit.libreoffice.org/35755
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit 06d073695c764744d308c74f80c40a317255fc05)
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 3fedd1668512..794c79595bbb 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11705,11 +11705,9 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
// Complex case: can't copy the dictionary byte array as is, as it contains a reference.
bool bDone = false;
const std::map<OString, filter::PDFElement*>& rItems = rObject.GetDictionaryItems();
- OString aReferenceName("ColorSpace");
- auto it = rItems.find(aReferenceName);
- if (it != rItems.end())
+ for (const auto& rItem : rItems)
{
- auto pReference = dynamic_cast<filter::PDFReferenceElement*>(it->second);
+ auto pReference = dynamic_cast<filter::PDFReferenceElement*>(rItem.second);
if (pReference)
{
filter::PDFObjectElement* pReferenced = pReference->LookupObject();
@@ -11719,8 +11717,8 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced);
sal_uInt64 nDictStart = rObject.GetDictionaryOffset();
- sal_uInt64 nReferenceStart = pDictionary->GetKeyOffset(aReferenceName) + aReferenceName.getLength();
- sal_uInt64 nReferenceEnd = pDictionary->GetKeyOffset(aReferenceName) + pDictionary->GetKeyValueLength(aReferenceName);
+ sal_uInt64 nReferenceStart = pDictionary->GetKeyOffset(rItem.first) + rItem.first.getLength();
+ sal_uInt64 nReferenceEnd = pDictionary->GetKeyOffset(rItem.first) + pDictionary->GetKeyValueLength(rItem.first);
sal_uInt64 nDictEnd = nDictStart + rObject.GetDictionaryLength();
// Dict start -> reference start.
aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nDictStart, nReferenceStart - nDictStart);
@@ -11732,6 +11730,7 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nReferenceEnd, nDictEnd - nReferenceEnd);
bDone = true;
+ break;
}
}
}
@@ -11807,7 +11806,7 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
return nObject;
}
-std::map<OString, sal_Int32> PDFWriterImpl::copyExternalResources(filter::PDFObjectElement& rPage, const OString& rKind)
+OString PDFWriterImpl::copyExternalResources(filter::PDFObjectElement& rPage, const OString& rKind)
{
// A name - object ID map, IDs as they appear in our output, not the
// original ones.
@@ -11816,11 +11815,11 @@ std::map<OString, sal_Int32> PDFWriterImpl::copyExternalResources(filter::PDFObj
// Get the rKind subset of the resource dictionary.
auto pResources = dynamic_cast<filter::PDFDictionaryElement*>(rPage.Lookup("Resources"));
if (!pResources)
- return aRet;
+ return OString();
auto pDictionary = dynamic_cast<filter::PDFDictionaryElement*>(pResources->LookupElement(rKind));
if (!pDictionary)
- return aRet;
+ return OString();
SvMemoryStream& rDocBuffer = rPage.GetDocument().GetEditBuffer();
@@ -11841,7 +11840,15 @@ std::map<OString, sal_Int32> PDFWriterImpl::copyExternalResources(filter::PDFObj
aRet[rItem.first] = nObject;
}
- return aRet;
+ // Build the dictionary entry string.
+ OString sRet = "/" + rKind + "<<";
+ for (const auto& rPair : aRet)
+ {
+ sRet += "/" + rPair.first + " " + OString::number(rPair.second) + " 0 R";
+ }
+ sRet += ">>";
+
+ return sRet;
}
void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
@@ -11889,13 +11896,7 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
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 += ">>";
+ OString sXObjects = copyExternalResources(*pPage, "XObject");
filter::PDFObjectElement* pPageContents = pPage->LookupObject("Contents");
if (!pPageContents)
diff --git a/vcl/source/gdi/pdfwriter_impl.hxx b/vcl/source/gdi/pdfwriter_impl.hxx
index ea1c0cd2892e..2329cbdec0bb 100644
--- a/vcl/source/gdi/pdfwriter_impl.hxx
+++ b/vcl/source/gdi/pdfwriter_impl.hxx
@@ -860,8 +860,8 @@ i12626
/// 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);
+ /// returning what has to be included in the new resource dictionary.
+ OString copyExternalResources(filter::PDFObjectElement& rPage, const OString& rKind);
/// Copies a single resource from an external document, returns the new
/// object ID in our document.
sal_Int32 copyExternalResource(SvMemoryStream& rDocBuffer, filter::PDFObjectElement& rObject);
commit 034168971a4f542fb89bbe564943297d3e4ba317
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Mon Mar 27 09:17:30 2017 +0200
tdf#106693 vcl PDF export, norefxobj: copy array objects
So far only the dictionary and the stream of the object was copied, see
if it has an array, and take care of that as well.
Also check if the array contains a reference and act accordingly.
Change-Id: I7f3bb12ec0bbc6f6e1db4f43625c7768b862c895
Reviewed-on: https://gerrit.libreoffice.org/35744
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit 044e8d795276cc495c1f796a14ad36e6a5f9cdb9)
diff --git a/include/vcl/filter/pdfdocument.hxx b/include/vcl/filter/pdfdocument.hxx
index fbfb81ed10a2..9ccbb43d0225 100644
--- a/include/vcl/filter/pdfdocument.hxx
+++ b/include/vcl/filter/pdfdocument.hxx
@@ -32,6 +32,7 @@ class PDFDocument;
class PDFDictionaryElement;
class PDFArrayElement;
class PDFStreamElement;
+class PDFNumberElement;
/// A byte range in a PDF file.
class VCL_DLLPUBLIC PDFElement
@@ -54,6 +55,10 @@ class VCL_DLLPUBLIC PDFObjectElement : public PDFElement
/// Length of the dictionary buffer till (before) the '>>' token.
sal_uInt64 m_nDictionaryLength;
PDFDictionaryElement* m_pDictionaryElement;
+ /// Position after the '[' token, if m_pArrayElement is set.
+ sal_uInt64 m_nArrayOffset;
+ /// Length of the array buffer till (before) the ']' token.
+ sal_uInt64 m_nArrayLength;
/// The contained direct array, if any.
PDFArrayElement* m_pArrayElement;
/// The stream of this object, used when this is an object stream.
@@ -83,6 +88,10 @@ public:
void SetStream(PDFStreamElement* pStreamElement);
/// Access to the stream of the object, if it has any.
PDFStreamElement* GetStream() const;
+ void SetArrayOffset(sal_uInt64 nArrayOffset);
+ sal_uInt64 GetArrayOffset();
+ void SetArrayLength(sal_uInt64 nArrayLength);
+ sal_uInt64 GetArrayLength();
PDFArrayElement* GetArray() const;
/// Parse objects stored in this object stream.
void ParseStoredObjects();
@@ -113,9 +122,11 @@ class VCL_DLLPUBLIC PDFReferenceElement : public PDFElement
int m_fGenerationValue;
/// Location after the 'R' token.
sal_uInt64 m_nOffset = 0;
+ /// The element providing the object number.
+ PDFNumberElement& m_rObject;
public:
- PDFReferenceElement(PDFDocument& rDoc, int fObjectValue, int fGenerationValue);
+ PDFReferenceElement(PDFDocument& rDoc, PDFNumberElement& rObject, PDFNumberElement& rGeneration);
bool Read(SvStream& rStream) override;
/// Assuming the reference points to a number object, return its value.
double LookupNumber(SvStream& rStream) const;
@@ -124,6 +135,7 @@ public:
int GetObjectValue() const;
int GetGenerationValue() const;
sal_uInt64 GetOffset() const;
+ PDFNumberElement& GetObjectElement() const;
};
/// Stream object: a byte array with a known length.
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx
index a364c0a19612..f2de0bec68f1 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -943,7 +943,10 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
// The array is attached directly, inform the object.
pArray = pArr;
if (pObject)
+ {
pObject->SetArray(pArray);
+ pObject->SetArrayOffset(rStream.Tell());
+ }
}
rStream.SeekRel(-1);
if (!rElements.back()->Read(rStream))
@@ -958,6 +961,13 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
rElements.push_back(std::unique_ptr<PDFElement>(new PDFEndArrayElement()));
pArray = nullptr;
rStream.SeekRel(-1);
+ if (nDictionaryDepth == 0)
+ {
+ if (pObject)
+ {
+ pObject->SetArrayLength(rStream.Tell() - pObject->GetArrayOffset());
+ }
+ }
if (!rElements.back()->Read(rStream))
{
SAL_WARN("vcl.filter", "PDFDocument::Tokenize: PDFEndArrayElement::Read() failed");
@@ -1048,7 +1058,7 @@ bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode, std::vector< s
}
else
{
- rElements.push_back(std::unique_ptr<PDFElement>(new PDFReferenceElement(*this, pObjectNumber->GetValue(), pGenerationNumber->GetValue())));
+ rElements.push_back(std::unique_ptr<PDFElement>(new PDFReferenceElement(*this, *pObjectNumber, *pGenerationNumber)));
if (pArray)
// Reference is part of a direct (non-dictionary) array, inform the array.
pArray->PushBack(rElements.back().get());
@@ -2061,6 +2071,8 @@ PDFObjectElement::PDFObjectElement(PDFDocument& rDoc, double fObjectValue, doubl
m_nDictionaryOffset(0),
m_nDictionaryLength(0),
m_pDictionaryElement(nullptr),
+ m_nArrayOffset(0),
+ m_nArrayLength(0),
m_pArrayElement(nullptr),
m_pStreamElement(nullptr)
{
@@ -2361,6 +2373,16 @@ sal_uInt64 PDFObjectElement::GetDictionaryOffset()
return m_nDictionaryOffset;
}
+void PDFObjectElement::SetArrayOffset(sal_uInt64 nArrayOffset)
+{
+ m_nArrayOffset = nArrayOffset;
+}
+
+sal_uInt64 PDFObjectElement::GetArrayOffset()
+{
+ return m_nArrayOffset;
+}
+
void PDFDictionaryElement::SetKeyOffset(const OString& rKey, sal_uInt64 nOffset)
{
m_aDictionaryKeyOffset[rKey] = nOffset;
@@ -2407,6 +2429,16 @@ sal_uInt64 PDFObjectElement::GetDictionaryLength()
return m_nDictionaryLength;
}
+void PDFObjectElement::SetArrayLength(sal_uInt64 nArrayLength)
+{
+ m_nArrayLength = nArrayLength;
+}
+
+sal_uInt64 PDFObjectElement::GetArrayLength()
+{
+ return m_nArrayLength;
+}
+
PDFDictionaryElement* PDFObjectElement::GetDictionary() const
{
return m_pDictionaryElement;
@@ -2592,11 +2624,17 @@ PDFDocument& PDFObjectElement::GetDocument()
return m_rDoc;
}
-PDFReferenceElement::PDFReferenceElement(PDFDocument& rDoc, int fObjectValue, int fGenerationValue)
+PDFReferenceElement::PDFReferenceElement(PDFDocument& rDoc, PDFNumberElement& rObject, PDFNumberElement& rGeneration)
: m_rDoc(rDoc),
- m_fObjectValue(fObjectValue),
- m_fGenerationValue(fGenerationValue)
+ m_fObjectValue(rObject.GetValue()),
+ m_fGenerationValue(rGeneration.GetValue()),
+ m_rObject(rObject)
+{
+}
+
+PDFNumberElement& PDFReferenceElement::GetObjectElement() const
{
+ return m_rObject;
}
bool PDFReferenceElement::Read(SvStream& rStream)
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 1d43bbde8dc0..3fedd1668512 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11751,6 +11751,51 @@ sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter
aLine.append("\nendstream\n");
}
+ if (filter::PDFArrayElement* pArray = rObject.GetArray())
+ {
+ aLine.append("[");
+
+ const std::vector<filter::PDFElement*>& rElements = pArray->GetElements();
+ bool bDone = false;
+ // Complex case: can't copy the array byte array as is, as it contains a reference.
+ for (const auto pElement : rElements)
+ {
+ auto pReference = dynamic_cast<filter::PDFReferenceElement*>(pElement);
+ if (pReference)
+ {
+ filter::PDFObjectElement* pReferenced = pReference->LookupObject();
+ if (pReferenced)
+ {
+ // Copy the referenced object.
+ sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced);
+
+ sal_uInt64 nArrStart = rObject.GetArrayOffset();
+ sal_uInt64 nReferenceStart = pReference->GetObjectElement().GetLocation();
+ sal_uInt64 nReferenceEnd = pReference->GetOffset();
+ sal_uInt64 nArrEnd = nArrStart + rObject.GetArrayLength();
+
+ // Array start -> reference start.
+ aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nArrStart, nReferenceStart - nArrStart);
+ // Write the updated reference.
+ aLine.append(" ");
+ aLine.append(nRef);
+ aLine.append(" 0 R");
+ // Reference end -> array end.
+ aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nReferenceEnd, nArrEnd - nReferenceEnd);
+
+ bDone = true;
+ break;
+ }
+ }
+ }
+
+ // Can copy it as-is.
+ if (!bDone)
+ aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + rObject.GetArrayOffset(), rObject.GetArrayLength());
+
+ aLine.append("]\n");
+ }
+
aLine.append("endobj\n\n");
// We have the whole object, now write it to the output.
commit 4cbe0413408f0214f9a4dc8c0253571e941c8083
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Fri Mar 24 15:16:32 2017 +0100
tdf#106693 vcl PDF export, norefxobj: update XObject refs
Start copying referenced objects recursively, and also take care of
updating references to the object IDs as they appear in our output.
With this, the 4th image referenced from the PDF image has a correctly
updated reference in its dictionary's ColorSpace key.
Change-Id: I8b49701c1f60bd0ef5a097b24ce59164554c44fa
Reviewed-on: https://gerrit.libreoffice.org/35653
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit f135a8bdeba15cf72dd31c7d613d335bbfc7017b)
diff --git a/include/vcl/filter/pdfdocument.hxx b/include/vcl/filter/pdfdocument.hxx
index 135d30d8d8bb..fbfb81ed10a2 100644
--- a/include/vcl/filter/pdfdocument.hxx
+++ b/include/vcl/filter/pdfdocument.hxx
@@ -77,6 +77,8 @@ public:
sal_uInt64 GetDictionaryLength();
PDFDictionaryElement* GetDictionary() const;
void SetDictionary(PDFDictionaryElement* pDictionaryElement);
+ /// Get access to the parsed key-value items from the object dictionary.
+ const std::map<OString, PDFElement*>& GetDictionaryItems() const;
void SetArray(PDFArrayElement* pArrayElement);
void SetStream(PDFStreamElement* pStreamElement);
/// Access to the stream of the object, if it has any.
@@ -109,6 +111,8 @@ class VCL_DLLPUBLIC PDFReferenceElement : public PDFElement
PDFDocument& m_rDoc;
int m_fObjectValue;
int m_fGenerationValue;
+ /// Location after the 'R' token.
+ sal_uInt64 m_nOffset = 0;
public:
PDFReferenceElement(PDFDocument& rDoc, int fObjectValue, int fGenerationValue);
@@ -119,6 +123,7 @@ public:
PDFObjectElement* LookupObject();
int GetObjectValue() const;
int GetGenerationValue() const;
+ sal_uInt64 GetOffset() const;
};
/// Stream object: a byte array with a known length.
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx
index e8a34e14e7a8..a364c0a19612 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -2217,7 +2217,10 @@ size_t PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement
{
rDictionary[aName] = pReference;
if (pThisDictionary)
+ {
pThisDictionary->SetKeyOffset(aName, nNameOffset);
+ pThisDictionary->SetKeyValueLength(aName, pReference->GetOffset() - nNameOffset);
+ }
aName.clear();
}
else
@@ -2414,6 +2417,11 @@ void PDFObjectElement::SetDictionary(PDFDictionaryElement* pDictionaryElement)
m_pDictionaryElement = pDictionaryElement;
}
+const std::map<OString, PDFElement*>& PDFObjectElement::GetDictionaryItems() const
+{
+ return m_aDictionary;
+}
+
void PDFObjectElement::SetArray(PDFArrayElement* pArrayElement)
{
m_pArrayElement = pArrayElement;
@@ -2591,12 +2599,18 @@ PDFReferenceElement::PDFReferenceElement(PDFDocument& rDoc, int fObjectValue, in
{
}
-bool PDFReferenceElement::Read(SvStream& /*rStream*/)
+bool PDFReferenceElement::Read(SvStream& rStream)
{
SAL_INFO("vcl.filter", "PDFReferenceElement::Read: " << m_fObjectValue << " " << m_fGenerationValue << " R");
+ m_nOffset = rStream.Tell();
return true;
}
+sal_uInt64 PDFReferenceElement::GetOffset() const
+{
+ return m_nOffset;
+}
+
double PDFReferenceElement::LookupNumber(SvStream& rStream) const
{
size_t nOffset = m_rDoc.GetObjectOffset(m_fObjectValue);
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 841f53e6d3be..1d43bbde8dc0 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11691,6 +11691,77 @@ void PDFWriterImpl::writeJPG( JPGEmit& rObject )
writeReferenceXObject(rObject.m_aReferenceXObject);
}
+sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter::PDFObjectElement& rObject)
+{
+ sal_Int32 nObject = createObject();
+
+ OStringBuffer aLine;
+ aLine.append(nObject);
+ aLine.append(" 0 obj\n");
+ if (filter::PDFDictionaryElement* pDictionary = rObject.GetDictionary())
+ {
+ aLine.append("<<");
+
+ // Complex case: can't copy the dictionary byte array as is, as it contains a reference.
+ bool bDone = false;
+ const std::map<OString, filter::PDFElement*>& rItems = rObject.GetDictionaryItems();
+ OString aReferenceName("ColorSpace");
+ auto it = rItems.find(aReferenceName);
+ if (it != rItems.end())
+ {
+ auto pReference = dynamic_cast<filter::PDFReferenceElement*>(it->second);
+ if (pReference)
+ {
+ filter::PDFObjectElement* pReferenced = pReference->LookupObject();
+ if (pReferenced)
+ {
+ // Copy the referenced object.
+ sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced);
+
+ sal_uInt64 nDictStart = rObject.GetDictionaryOffset();
+ sal_uInt64 nReferenceStart = pDictionary->GetKeyOffset(aReferenceName) + aReferenceName.getLength();
+ sal_uInt64 nReferenceEnd = pDictionary->GetKeyOffset(aReferenceName) + pDictionary->GetKeyValueLength(aReferenceName);
+ sal_uInt64 nDictEnd = nDictStart + rObject.GetDictionaryLength();
+ // Dict start -> reference start.
+ aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nDictStart, nReferenceStart - nDictStart);
+ // Write the updated reference.
+ aLine.append(" ");
+ aLine.append(nRef);
+ aLine.append(" 0 R");
+ // Reference end -> dict end.
+ aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nReferenceEnd, nDictEnd - nReferenceEnd);
+
+ bDone = true;
+ }
+ }
+ }
+
+ // Can copy it as-is.
+ if (!bDone)
+ aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + rObject.GetDictionaryOffset(), rObject.GetDictionaryLength());
+
+ aLine.append(">>\n");
+ }
+
+ if (filter::PDFStreamElement* pStream = rObject.GetStream())
+ {
+ aLine.append("stream\n");
+ SvMemoryStream& rStream = pStream->GetMemory();
+ aLine.append(static_cast<const sal_Char*>(rStream.GetData()), rStream.GetSize());
+ aLine.append("\nendstream\n");
+ }
+
+ aLine.append("endobj\n\n");
+
+ // We have the whole object, now write it to the output.
+ if (!updateObject(nObject))
+ return -1;
+ if (!writeBuffer(aLine.getStr(), aLine.getLength()))
+ return -1;
+
+ return nObject;
+}
+
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
@@ -11706,6 +11777,8 @@ std::map<OString, sal_Int32> PDFWriterImpl::copyExternalResources(filter::PDFObj
if (!pDictionary)
return aRet;
+ SvMemoryStream& rDocBuffer = rPage.GetDocument().GetEditBuffer();
+
const std::map<OString, filter::PDFElement*>& rItems = pDictionary->GetItems();
for (const auto& rItem : rItems)
{
@@ -11718,31 +11791,8 @@ std::map<OString, sal_Int32> PDFWriterImpl::copyExternalResources(filter::PDFObj
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;
-
+ // Then copying over an object copy its dictionary and its stream.
+ sal_Int32 nObject = copyExternalResource(rDocBuffer, *pValue);
aRet[rItem.first] = nObject;
}
diff --git a/vcl/source/gdi/pdfwriter_impl.hxx b/vcl/source/gdi/pdfwriter_impl.hxx
index cc69f58f1567..ea1c0cd2892e 100644
--- a/vcl/source/gdi/pdfwriter_impl.hxx
+++ b/vcl/source/gdi/pdfwriter_impl.hxx
@@ -862,6 +862,9 @@ i12626
/// 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);
+ /// Copies a single resource from an external document, returns the new
+ /// object ID in our document.
+ sal_Int32 copyExternalResource(SvMemoryStream& rDocBuffer, filter::PDFObjectElement& rObject);
/* tries to find the bitmap by its id and returns its emit data if exists,
else creates a new emit data block */
commit 095073cf908b01636bfe6a9fe4224040728de976
Author: Miklos Vajna <vmiklos at 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 <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit 1f2bccf2d28d4257aa0e325658d35182367b59d9)
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 0e458e053a24..e8a34e14e7a8 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -2579,6 +2579,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 5b949be60c07..841f53e6d3be 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11691,6 +11691,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)
@@ -11711,12 +11769,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;
@@ -11729,24 +11781,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 2bffdf275e4d..cc69f58f1567 100644
--- a/vcl/source/gdi/pdfwriter_impl.hxx
+++ b/vcl/source/gdi/pdfwriter_impl.hxx
@@ -72,6 +72,11 @@ class PDFStreamIf;
class Matrix3;
class PdfBuiltinFontFace;
+namespace filter
+{
+class PDFObjectElement;
+}
+
class PDFWriterImpl
{
friend class PDFStreamIf;
@@ -854,6 +859,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 */
commit cef6c73ad4b83d17ee3750a37e3134366dfb6687
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Thu Mar 23 14:26:42 2017 +0100
tdf#106693 vcl PDF export, norefxobj: handle compressed page stream
Since we want to avoid re-compressing the page stream create two form
XObjects: one that resets the graphic state to the default (e.g. line
width) and an other one that contains the original page stream as-is.
With this PDF images where the page stream is compressed are handled
correctly.
Change-Id: Ib44dae2e167e4d5604a0a3a3cf91e09795137343
(cherry picked from commit d0c24cbb027130f3781bfc3475dd225190afd560)
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 1133cb9b16ff..5b949be60c07 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11696,10 +11696,6 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
if (rEmit.m_nFormObject <= 0)
return;
- OStringBuffer aLine;
- if (!updateObject(rEmit.m_nFormObject))
- return;
-
// Count /Matrix and /BBox.
// vcl::ImportPDF() works with 96 DPI so use the same values here, too.
sal_Int32 nOldDPIX = getReferenceDevice()->GetDPIX();
@@ -11712,15 +11708,92 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
double fScaleX = 1.0 / aSize.Width();
double fScaleY = 1.0 / aSize.Height();
+ 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;
+ aPDFStream.WriteBytes(rEmit.m_aPDFData.getArray(), rEmit.m_aPDFData.getLength());
+ aPDFStream.Seek(0);
+ filter::PDFDocument aPDFDocument;
+ if (!aPDFDocument.Read(aPDFStream))
+ {
+ SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: reading the PDF document failed");
+ return;
+ }
+ std::vector<filter::PDFObjectElement*> aPages = aPDFDocument.GetPages();
+ if (aPages.empty() || !aPages[0])
+ {
+ SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no pages");
+ return;
+ }
+
+ filter::PDFObjectElement* pPageContents = aPages[0]->LookupObject("Contents");
+ if (!pPageContents)
+ {
+ SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: page has no contents");
+ return;
+ }
+
+ OStringBuffer aLine;
+ aLine.append(nWrappedFormObject);
+ aLine.append(" 0 obj\n");
+ aLine.append("<< /Type /XObject");
+ aLine.append(" /Subtype /Form");
+ aLine.append(" /BBox [ 0 0 ");
+ aLine.append(aSize.Width());
+ aLine.append(" ");
+ aLine.append(aSize.Height());
+ aLine.append(" ]");
+
+ auto pFilter = dynamic_cast<filter::PDFNameElement*>(pPageContents->Lookup("Filter"));
+ if (pFilter)
+ {
+ aLine.append(" /Filter /");
+ aLine.append(pFilter->GetValue());
+ }
+
+ aLine.append(" /Length ");
+
+ filter::PDFStreamElement* pPageStream = pPageContents->GetStream();
+ if (!pPageStream)
+ {
+ SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: contents has no stream");
+ return;
+ }
+
+ SvMemoryStream& rPageStream = pPageStream->GetMemory();
+
+ aLine.append(static_cast<sal_Int32>(rPageStream.GetSize()));
+
+ aLine.append(">>\nstream\n");
+ // Copy the original page stream to the form XObject stream.
+ aLine.append(static_cast<const sal_Char*>(rPageStream.GetData()), rPageStream.GetSize());
+ aLine.append("\nendstream\nendobj\n\n");
+ CHECK_RETURN2(writeBuffer(aLine.getStr(), aLine.getLength()));
+ }
+
+ OStringBuffer aLine;
+ if (!updateObject(rEmit.m_nFormObject))
+ return;
+
// Now have all the info to write the form XObject.
aLine.append(rEmit.m_nFormObject);
aLine.append(" 0 obj\n");
aLine.append("<< /Type /XObject");
aLine.append(" /Subtype /Form");
aLine.append(" /Resources << /XObject<</Im");
- aLine.append(rEmit.m_nBitmapObject);
+ sal_Int32 nObject = m_aContext.UseReferenceXObject ? rEmit.m_nBitmapObject : nWrappedFormObject;
+ aLine.append(nObject);
aLine.append(" ");
- aLine.append(rEmit.m_nBitmapObject);
+ aLine.append(nObject);
aLine.append(" 0 R>> >>");
aLine.append(" /Matrix [ ");
appendDouble(fScaleX, aLine);
@@ -11759,44 +11832,13 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
}
else
{
- // No reference XObject, include the original page stream.
// Reset line width to the default.
aStream.append(" 1 w\n");
- SvMemoryStream aPDFStream;
- aPDFStream.WriteBytes(rEmit.m_aPDFData.getArray(), rEmit.m_aPDFData.getLength());
- aPDFStream.Seek(0);
- filter::PDFDocument aPDFDocument;
- if (!aPDFDocument.Read(aPDFStream))
- {
- SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: reading the PDF document failed");
- return;
- }
- std::vector<filter::PDFObjectElement*> aPages = aPDFDocument.GetPages();
- if (aPages.empty() || !aPages[0])
- {
- SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no pages");
- return;
- }
-
- filter::PDFObjectElement* pPageContents = aPages[0]->LookupObject("Contents");
- if (!pPageContents)
- {
- SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: page has no contents");
- return;
- }
-
- filter::PDFStreamElement* pPageStream = pPageContents->GetStream();
- if (!pPageStream)
- {
- SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: contents has no stream");
- return;
- }
-
- SvMemoryStream& rPageStream = pPageStream->GetMemory();
-
- // Copy the original page stream to the end of the form XObject stream.
- aStream.append(static_cast<const sal_Char*>(rPageStream.GetData()), rPageStream.GetSize());
- aStream.append("\n");
+ // No reference XObject, draw the form XObject containing the original
+ // page stream.
+ aStream.append("/Im");
+ aStream.append(nWrappedFormObject);
+ aStream.append(" Do\n");
}
aStream.append("Q");
aLine.append(aStream.getLength());
commit 4ec52adb338c32060adef0314b3816bf0dd4f7db
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Thu Mar 23 10:35:12 2017 +0100
tdf#106693 vcl PDF export, norefxobj: copy page stream
This gives correct result in very simple cases when the page stream is
not compressed and it references no other objects from the original
file.
Change-Id: I11ed50180a256bdb5c587fd8927d21c925100a4d
Reviewed-on: https://gerrit.libreoffice.org/35580
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit d27818c1f34b01190bf419c34d6a174f3cad7894)
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index b47efb7decaf..1133cb9b16ff 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -60,6 +60,7 @@
#include <vcl/strhelper.hxx>
#include <vcl/svapp.hxx>
#include <vcl/virdev.hxx>
+#include <vcl/filter/pdfdocument.hxx>
#include "fontsubset.hxx"
#include "outdev.h"
@@ -11744,13 +11745,59 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
OStringBuffer aStream;
aStream.append("q ");
- aStream.append(aSize.Width());
- aStream.append(" 0 0 ");
- aStream.append(aSize.Height());
- aStream.append(" 0 0 cm\n");
- aStream.append("/Im");
- aStream.append(rEmit.m_nBitmapObject);
- aStream.append(" Do\n");
+ if (m_aContext.UseReferenceXObject)
+ {
+ // Reference XObject markup is used, just refer to the fallback bitmap
+ // here.
+ aStream.append(aSize.Width());
+ aStream.append(" 0 0 ");
+ aStream.append(aSize.Height());
+ aStream.append(" 0 0 cm\n");
+ aStream.append("/Im");
+ aStream.append(rEmit.m_nBitmapObject);
+ aStream.append(" Do\n");
+ }
+ else
+ {
+ // No reference XObject, include the original page stream.
+ // Reset line width to the default.
+ aStream.append(" 1 w\n");
+ SvMemoryStream aPDFStream;
+ aPDFStream.WriteBytes(rEmit.m_aPDFData.getArray(), rEmit.m_aPDFData.getLength());
+ aPDFStream.Seek(0);
+ filter::PDFDocument aPDFDocument;
+ if (!aPDFDocument.Read(aPDFStream))
+ {
+ SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: reading the PDF document failed");
+ return;
+ }
+ std::vector<filter::PDFObjectElement*> aPages = aPDFDocument.GetPages();
+ if (aPages.empty() || !aPages[0])
+ {
+ SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no pages");
+ return;
+ }
+
+ filter::PDFObjectElement* pPageContents = aPages[0]->LookupObject("Contents");
+ if (!pPageContents)
+ {
+ SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: page has no contents");
+ return;
+ }
+
+ filter::PDFStreamElement* pPageStream = pPageContents->GetStream();
+ if (!pPageStream)
+ {
+ SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: contents has no stream");
+ return;
+ }
+
+ SvMemoryStream& rPageStream = pPageStream->GetMemory();
+
+ // Copy the original page stream to the end of the form XObject stream.
+ aStream.append(static_cast<const sal_Char*>(rPageStream.GetData()), rPageStream.GetSize());
+ aStream.append("\n");
+ }
aStream.append("Q");
aLine.append(aStream.getLength());
@@ -12096,6 +12143,8 @@ void PDFWriterImpl::createEmbeddedFile(const Graphic& rGraphic, ReferenceXObject
rEmit.m_nEmbeddedObject = m_aEmbeddedFiles.back().m_nObject;
}
+ else
+ rEmit.m_aPDFData = rGraphic.getPdfData();
rEmit.m_nFormObject = createObject();
rEmit.m_aPixelSize = rGraphic.GetBitmap().GetPrefSize();
diff --git a/vcl/source/gdi/pdfwriter_impl.hxx b/vcl/source/gdi/pdfwriter_impl.hxx
index ff2165a2f5bb..2bffdf275e4d 100644
--- a/vcl/source/gdi/pdfwriter_impl.hxx
+++ b/vcl/source/gdi/pdfwriter_impl.hxx
@@ -201,6 +201,8 @@ public:
sal_Int32 m_nBitmapObject;
/// Size of the bitmap replacement, in pixels.
Size m_aPixelSize;
+ /// PDF data from the graphic object, if not writing a reference XObject.
+ css::uno::Sequence<sal_Int8> m_aPDFData;
ReferenceXObjectEmit()
: m_nFormObject(0),
commit 7de3c19ce685f9719a18859c9240d48e27b55dec
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date: Wed Mar 22 16:30:35 2017 +0100
tdf#106693 vcl PDF export: initial UseReferenceXObject option
It's still on, but in experimental mode start work towards the ability
to not use that markup.
Change-Id: Idf11c0e0a3c61ad93af331346ec7107304f6dc0f
Reviewed-on: https://gerrit.libreoffice.org/35538
Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
Tested-by: Jenkins <ci at libreoffice.org>
(cherry picked from commit 5f4826d89bfa1398b16fc85cf593ff58ce5e36a9)
diff --git a/filter/source/pdf/pdfexport.cxx b/filter/source/pdf/pdfexport.cxx
index 307d39eff691..8d4e61ccf6f4 100644
--- a/filter/source/pdf/pdfexport.cxx
+++ b/filter/source/pdf/pdfexport.cxx
@@ -47,6 +47,7 @@
#include <cppuhelper/exc_hlp.hxx>
#include <cppuhelper/compbase.hxx>
#include <cppuhelper/basemutex.hxx>
+#include <officecfg/Office/Common.hxx>
#include "pdfexport.hxx"
#include "impdialog.hxx"
@@ -782,6 +783,8 @@ bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >&
aContext.SignPassword = msSignPassword;
aContext.SignCertificate = maSignCertificate;
aContext.SignTSA = msSignTSA;
+ // Not using reference XObjects is experimental for now.
+ aContext.UseReferenceXObject = !officecfg::Office::Common::Misc::ExperimentalMode::get();
// all context data set, time to create the printing device
std::unique_ptr<vcl::PDFWriter> pPDFWriter(new vcl::PDFWriter( aContext, xEnc ));
diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx
index b54ce53891b2..f4dba18efc84 100644
--- a/include/vcl/pdfwriter.hxx
+++ b/include/vcl/pdfwriter.hxx
@@ -637,6 +637,8 @@ The following structure describes the permissions used in PDF security
PDFWriter::ColorMode ColorMode;
css::uno::Reference< css::security::XCertificate> SignCertificate;
OUString SignTSA;
+ /// Use reference XObject markup for PDF images.
+ bool UseReferenceXObject;
PDFWriterContext() :
RelFsys( false ), //i56629, i49415?, i64585?
@@ -666,7 +668,8 @@ The following structure describes the permissions used in PDF security
DPIx( 0 ),
DPIy( 0 ),
ColorMode( PDFWriter::DrawColor ),
- SignCertificate( nullptr )
+ SignCertificate( nullptr ),
+ UseReferenceXObject( false )
{}
};
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index b5b371e3b577..b47efb7decaf 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -11692,7 +11692,7 @@ void PDFWriterImpl::writeJPG( JPGEmit& rObject )
void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
{
- if (rEmit.m_nFormObject <= 0 || rEmit.m_nEmbeddedObject <= 0)
+ if (rEmit.m_nFormObject <= 0)
return;
OStringBuffer aLine;
@@ -11732,10 +11732,13 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
aLine.append(aSize.Height());
aLine.append(" ]\n");
- // Write the reference dictionary.
- aLine.append("/Ref<< /F << /Type /Filespec /F (<embedded file>) /EF << /F ");
- aLine.append(rEmit.m_nEmbeddedObject);
- aLine.append(" 0 R >> >> /Page 0 >>\n");
+ if (m_aContext.UseReferenceXObject && rEmit.m_nEmbeddedObject > 0)
+ {
+ // Write the reference dictionary.
+ aLine.append("/Ref<< /F << /Type /Filespec /F (<embedded file>) /EF << /F ");
+ aLine.append(rEmit.m_nEmbeddedObject);
+ aLine.append(" 0 R >> >> /Page 0 >>\n");
+ }
aLine.append("/Length ");
@@ -12084,13 +12087,17 @@ void PDFWriterImpl::createEmbeddedFile(const Graphic& rGraphic, ReferenceXObject
if (!rGraphic.getPdfData().hasElements())
return;
... etc. - the rest is truncated
More information about the Libreoffice-commits
mailing list