summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2021-05-17 13:51:31 +0200
committerMiklos Vajna <vmiklos@collabora.com>2021-05-17 15:24:57 +0200
commitd7d43cf460d66354a40ffa3b34c0f9efcd42d0be (patch)
tree82ddf81c36fd7801f06e87d0ff208891ca6dea42
parent6aec5d5a3a26bd973e46b6c593373e2f03f51a37 (diff)
vcl PDF export: fix re-exporting PDF images with arbitrary page-level rotation
Building on top of commit bd520b177637d4b7d9d93733103cff17a3c91b0a (vcl PDF export: fix re-exporting PDF images with page-level rotation, 2019-11-06), this was already working for 90 degrees, now generalize this to work with 180 degrees as well. Change-Id: I5a5d51662399814d5554d7c2cb86a6c9a2974012 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/115705 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins
-rw-r--r--vcl/qa/cppunit/pdfexport/data/pdf-image-rotate-180.pdfbin0 -> 1319 bytes
-rw-r--r--vcl/qa/cppunit/pdfexport/pdfexport.cxx67
-rw-r--r--vcl/source/gdi/pdfwriter_impl.cxx53
3 files changed, 99 insertions, 21 deletions
diff --git a/vcl/qa/cppunit/pdfexport/data/pdf-image-rotate-180.pdf b/vcl/qa/cppunit/pdfexport/data/pdf-image-rotate-180.pdf
new file mode 100644
index 000000000000..981ca32061cd
--- /dev/null
+++ b/vcl/qa/cppunit/pdfexport/data/pdf-image-rotate-180.pdf
Binary files differ
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index 8e089ded9d95..a3575e415d4f 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -2794,6 +2794,73 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfUaMetadata)
CPPUNIT_ASSERT_EQUAL(OString("1"), aPdfUaPart);
}
+CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfImageRotate180)
+{
+ // Create an empty document.
+ mxComponent = loadFromDesktop("private:factory/swriter");
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+
+ // Insert the PDF image.
+ uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xGraphicObject(
+ xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
+ OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "pdf-image-rotate-180.pdf";
+ xGraphicObject->setPropertyValue("GraphicURL", uno::makeAny(aURL));
+ uno::Reference<drawing::XShape> xShape(xGraphicObject, uno::UNO_QUERY);
+ xShape->setSize(awt::Size(1000, 1000));
+ uno::Reference<text::XTextContent> xTextContent(xGraphicObject, uno::UNO_QUERY);
+ xText->insertTextContent(xCursor->getStart(), xTextContent, /*bAbsorb=*/false);
+
+ // Save as PDF.
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ utl::MediaDescriptor aMediaDescriptor;
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Parse the export result.
+ SvFileStream aFile(maTempFile.GetURL(), StreamMode::READ);
+ maMemory.WriteStream(aFile);
+ std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPDFium->openDocument(maMemory.GetData(), maMemory.GetSize());
+ CPPUNIT_ASSERT(pPdfDocument);
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+
+ // Make sure that the page -> form -> form has a child image.
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pPageObject->getType());
+ // 2: white background and the actual object.
+ CPPUNIT_ASSERT_EQUAL(2, pPageObject->getFormObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject = pPageObject->getFormObject(1);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pFormObject->getType());
+ CPPUNIT_ASSERT_EQUAL(1, pFormObject->getFormObjectCount());
+
+ // Check if the inner form object (original page object in the pdf image) has the correct
+ // rotation.
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pInnerFormObject = pFormObject->getFormObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pInnerFormObject->getType());
+ CPPUNIT_ASSERT_EQUAL(1, pInnerFormObject->getFormObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pImage = pInnerFormObject->getFormObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pImage->getType());
+ basegfx::B2DHomMatrix aMat = pInnerFormObject->getMatrix();
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate = 0;
+ double fShearX = 0;
+ aMat.decompose(aScale, aTranslate, fRotate, fShearX);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: -1
+ // - Actual : 1
+ // i.e. the 180 degrees rotation didn't happen (via a combination of horizontal + vertical
+ // flip).
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, aScale.getX(), 0.01);
+}
+
} // end anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 564e35dee051..3fa3a10d31a0 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -8514,29 +8514,40 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
tools::Long nWidth = aSize.Width();
tools::Long nHeight = aSize.Height();
+ basegfx::B2DRange aBBox(0, 0, aSize.Width(), aSize.Height());
if (auto pRotate = dynamic_cast<filter::PDFNumberElement*>(pPage->Lookup("Rotate")))
{
// The original page was rotated, then construct a transformation matrix which does the
// same with our form object.
- if (rtl::math::approxEqual(pRotate->GetValue(), 90))
- {
- std::swap(nWidth, nHeight);
- basegfx::B2DHomMatrix aMat;
- aMat.rotate(basegfx::deg2rad(pRotate->GetValue()));
- // Rotate around the origo (bottom left corner) counter-clockwise, then translate
- // horizontally to effectively keep the bottom left corner unchanged.
- aLine.append(" /Matrix [ ");
- aLine.append(aMat.get(0, 0));
- aLine.append(" ");
- aLine.append(aMat.get(0, 1));
- aLine.append(" ");
- aLine.append(aMat.get(1, 0));
- aLine.append(" ");
- aLine.append(aMat.get(1, 1));
- aLine.append(" 0 ");
- aLine.append(nWidth);
- aLine.append(" ] ");
- }
+ sal_Int32 nRotAngle = static_cast<sal_Int32>(pRotate->GetValue()) % 360;
+ // /Rotate is clockwise, matrix rotate is counter-clockwise.
+ sal_Int32 nAngle = -1 * nRotAngle;
+
+ // The bounding box just rotates.
+ basegfx::B2DHomMatrix aBBoxMat;
+ aBBoxMat.rotate(basegfx::deg2rad(pRotate->GetValue()));
+ aBBox.transform(aBBoxMat);
+
+ // Now transform the object: rotate around the center and make sure that the rotation
+ // doesn't affect the aspect ratio.
+ basegfx::B2DHomMatrix aMat;
+ aMat.translate(-0.5 * aBBox.getWidth(), -0.5 * aBBox.getHeight());
+ aMat.rotate(basegfx::deg2rad(nAngle));
+ aMat.translate(0.5 * nWidth, 0.5 * nHeight);
+
+ aLine.append(" /Matrix [ ");
+ aLine.append(aMat.a());
+ aLine.append(" ");
+ aLine.append(aMat.b());
+ aLine.append(" ");
+ aLine.append(aMat.c());
+ aLine.append(" ");
+ aLine.append(aMat.d());
+ aLine.append(" ");
+ aLine.append(aMat.e());
+ aLine.append(" ");
+ aLine.append(aMat.f());
+ aLine.append(" ] ");
}
PDFObjectCopier aCopier(*this);
@@ -8544,9 +8555,9 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
aCopier.copyPageResources(pPage, aLine, rResources);
aLine.append(" /BBox [ 0 0 ");
- aLine.append(nWidth);
+ aLine.append(aBBox.getWidth());
aLine.append(" ");
- aLine.append(nHeight);
+ aLine.append(aBBox.getHeight());
aLine.append(" ]");
if (!g_bDebugDisableCompression)