diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2021-05-17 13:51:31 +0200 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2021-05-19 09:16:57 +0200 |
commit | 52add3bae28b1e7cda0234110341f3c6e0b06fdc (patch) | |
tree | 074d4396ab72eaed5de7162f6e615df46589e38d | |
parent | 150d0d88b1eeafe7662acd958ec248f0d91d5ce6 (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.
(cherry picked from commit d7d43cf460d66354a40ffa3b34c0f9efcd42d0be)
Conflicts:
vcl/qa/cppunit/pdfexport/pdfexport.cxx
vcl/source/gdi/pdfwriter_impl.cxx
Change-Id: I5a5d51662399814d5554d7c2cb86a6c9a2974012
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/115712
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Ashod Nakashian <ash@collabora.com>
-rw-r--r-- | vcl/qa/cppunit/pdfexport/data/pdf-image-rotate-180.pdf | bin | 0 -> 1319 bytes | |||
-rw-r--r-- | vcl/qa/cppunit/pdfexport/pdfexport.cxx | 72 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 53 |
3 files changed, 104 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 Binary files differnew file mode 100644 index 000000000000..981ca32061cd --- /dev/null +++ b/vcl/qa/cppunit/pdfexport/data/pdf-image-rotate-180.pdf diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx index 5f50661b013e..aa96ee8a0f99 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx @@ -146,6 +146,7 @@ public: void testVersion15(); void testDefaultVersion(); void testMultiPagePDF(); + void testPdfImageRotate180(); CPPUNIT_TEST_SUITE(PdfExportTest); @@ -189,6 +190,7 @@ public: CPPUNIT_TEST(testVersion15); CPPUNIT_TEST(testDefaultVersion); CPPUNIT_TEST(testMultiPagePDF); + CPPUNIT_TEST(testPdfImageRotate180); CPPUNIT_TEST_SUITE_END(); }; @@ -2180,6 +2182,76 @@ void PdfExportTest::testMultiPagePDF() #endif } +void PdfExportTest::testPdfImageRotate180() +{ + // Create an empty document. + uno::Reference<lang::XComponent> xComponent = loadFromDesktop("private:factory/swriter"); + uno::Reference<text::XTextDocument> xTextDocument(xComponent, 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(xComponent, 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(xComponent, uno::UNO_QUERY); + utl::MediaDescriptor aMediaDescriptor; + aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + xStorable->storeToURL(aTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList()); + xComponent->dispose(); + + // Parse the export result. + SvFileStream aFile(aTempFile.GetURL(), StreamMode::READ); + SvMemoryStream aMemory; + aMemory.WriteStream(aFile); + DocumentHolder pPdfDocument(FPDF_LoadMemDocument(aMemory.GetData(), aMemory.GetSize(), /*password=*/nullptr)); + CPPUNIT_ASSERT_EQUAL(1, FPDF_GetPageCount(pPdfDocument.get())); + + // Make sure that the page -> form -> form has a child image. + PageHolder pPdfPage(FPDF_LoadPage(pPdfDocument.get(), /*page_index=*/0)); + CPPUNIT_ASSERT(pPdfPage.get()); + CPPUNIT_ASSERT_EQUAL(1, FPDFPage_CountObjects(pPdfPage.get())); + FPDF_PAGEOBJECT pPageObject = FPDFPage_GetObject(pPdfPage.get(), 0); + CPPUNIT_ASSERT_EQUAL(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(pPageObject)); + // 2: white background and the actual object. + CPPUNIT_ASSERT_EQUAL(2, FPDFFormObj_CountObjects(pPageObject)); + FPDF_PAGEOBJECT pFormObject = FPDFFormObj_GetObject(pPageObject, 1); + CPPUNIT_ASSERT_EQUAL(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(pFormObject)); + CPPUNIT_ASSERT_EQUAL(1, FPDFFormObj_CountObjects(pFormObject)); + + // Check if the inner form object (original page object in the pdf image) has the correct + // rotation. + FPDF_PAGEOBJECT pInnerFormObject = FPDFFormObj_GetObject(pFormObject, 0); + CPPUNIT_ASSERT_EQUAL(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(pInnerFormObject)); + CPPUNIT_ASSERT_EQUAL(1, FPDFFormObj_CountObjects(pInnerFormObject)); + FPDF_PAGEOBJECT pImage = FPDFFormObj_GetObject(pInnerFormObject, 0); + CPPUNIT_ASSERT_EQUAL(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(pImage)); + FS_MATRIX aMatrix; + FPDFFormObj_GetMatrix(pInnerFormObject, &aMatrix); + basegfx::B2DHomMatrix aMat{ aMatrix.a, aMatrix.c, aMatrix.e, aMatrix.b, aMatrix.d, aMatrix.f }; + 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); +} + CPPUNIT_TEST_SUITE_REGISTRATION(PdfExportTest); } diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index cfc8ffeffedc..3cf733449d31 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -8748,29 +8748,40 @@ void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit) long nWidth = aSize.Width(); 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); @@ -8778,9 +8789,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) |