diff --git a/filter/CppunitTest_filter_pdf.mk b/filter/CppunitTest_filter_pdf.mk index e551ae6b9d07..912b84e0edb4 100644 --- a/filter/CppunitTest_filter_pdf.mk +++ b/filter/CppunitTest_filter_pdf.mk @@ -20,6 +20,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,filter_pdf, \ )) $(eval $(call gb_CppunitTest_use_libraries,filter_pdf, \ + basegfx \ comphelper \ cppu \ cppuhelper \ diff --git a/filter/qa/pdf.cxx b/filter/qa/pdf.cxx index 24014571b331..b631a3f8a4a8 100644 --- a/filter/qa/pdf.cxx +++ b/filter/qa/pdf.cxx @@ -20,6 +20,7 @@ #include #include #include +#include using namespace ::com::sun::star; @@ -276,6 +277,58 @@ CPPUNIT_TEST_FIXTURE(Test, testWatermarkFontName) // i.e. the font name was sans, could not specify an explicit name. CPPUNIT_ASSERT_EQUAL(aExpectedFontName, aFontName); } + +CPPUNIT_TEST_FIXTURE(Test, testWatermarkRotateAngle) +{ + // Given an empty Writer document: + std::shared_ptr pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + return; + mxComponent.set(loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument")); + + // When exporting that as PDF with a rotated watermark: + uno::Reference xFactory = getMultiServiceFactory(); + uno::Reference xFilter( + xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY); + uno::Reference xExporter(xFilter, uno::UNO_QUERY); + xExporter->setSourceDocument(mxComponent); + SvMemoryStream aStream; + uno::Reference xOutputStream(new utl::OStreamWrapper(aStream)); + // 45.0 degrees, counter-clockwise. + sal_Int32 nExpectedRotateAngle = 45; + uno::Sequence aFilterData{ + comphelper::makePropertyValue("Watermark", OUString("X")), + comphelper::makePropertyValue("WatermarkRotateAngle", nExpectedRotateAngle * 10), + }; + uno::Sequence aDescriptor{ + comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")), + comphelper::makePropertyValue("FilterData", aFilterData), + comphelper::makePropertyValue("OutputStream", xOutputStream), + }; + xFilter->filter(aDescriptor); + + // Then make sure that the watermark rotation angle is correct: + std::unique_ptr pPdfDocument + = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); + CPPUNIT_ASSERT(pPdfDocument); + std::unique_ptr pPage = pPdfDocument->openPage(0); + CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount()); + std::unique_ptr pPageObject = pPage->getObject(0); + CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount()); + std::unique_ptr pFormObject = pPageObject->getFormObject(0); + basegfx::B2DHomMatrix aMatrix = pFormObject->getMatrix(); + basegfx::B2DTuple aScale; + basegfx::B2DTuple aTranslate; + double fRotate{}; + double fShearX{}; + aMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + sal_Int32 nActualRotateAngle = NormAngle360(basegfx::rad2deg<1>(fRotate)); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 45 + // - Actual : 270 + // i.e. the rotation angle was 270 for an A4 page, not the requested 45 degrees. + CPPUNIT_ASSERT_EQUAL(nExpectedRotateAngle, nActualRotateAngle); +} } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/filter/source/pdf/pdfexport.cxx b/filter/source/pdf/pdfexport.cxx index 5bb6f0baa2fe..8204d6da86b3 100644 --- a/filter/source/pdf/pdfexport.cxx +++ b/filter/source/pdf/pdfexport.cxx @@ -577,6 +577,14 @@ bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& moWatermarkFontHeight = nFontHeight; } } + else if (rProp.Name == "WatermarkRotateAngle") + { + sal_Int32 nRotateAngle{}; + if (rProp.Value >>= nRotateAngle) + { + moWatermarkRotateAngle = Degree10(nRotateAngle); + } + } else if (rProp.Name == "WatermarkFontName") { OUString aFontName{}; @@ -1189,6 +1197,17 @@ void PDFExport::ImplWriteWatermark( vcl::PDFWriter& rWriter, const Size& rPageSi aFont.SetOrientation( 2700_deg10 ); } + if (moWatermarkRotateAngle) + { + aFont.SetOrientation(*moWatermarkRotateAngle); + if (rPageSize.Width() < rPageSize.Height()) + { + // Set text width based on the shorter side, so rotation can't push text outside the + // page boundaries. + nTextWidth = rPageSize.Width(); + } + } + // adjust font height for text to fit OutputDevice* pDev = rWriter.GetReferenceDevice(); pDev->Push(); @@ -1242,6 +1261,25 @@ void PDFExport::ImplWriteWatermark( vcl::PDFWriter& rWriter, const Size& rPageSi (rPageSize.Height()-w)/2 ); aTextRect = tools::Rectangle( aTextPoint, Size( nTextHeight, w ) ); } + + if (moWatermarkRotateAngle) + { + // First set the text's starting point to the center of the page. + tools::Rectangle aPageRectangle(Point(0, 0), rPageSize); + aTextPoint = aPageRectangle.Center(); + // Then adjust it so that the text remains centered, based on the rotation angle. + basegfx::B2DPolygon aTextPolygon + = basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle(0, -nTextHeight, w, 0)); + basegfx::B2DHomMatrix aMatrix; + aMatrix.rotate(-1 * toRadians(*moWatermarkRotateAngle)); + aTextPolygon.transform(aMatrix); + basegfx::B2DPoint aPolygonCenter = aTextPolygon.getB2DRange().getCenter(); + aTextPoint.AdjustX(-aPolygonCenter.getX()); + aTextPoint.AdjustY(-aPolygonCenter.getY()); + + aTextRect = aPageRectangle; + } + rWriter.SetClipRegion(); rWriter.BeginTransparencyGroup(); rWriter.DrawText( aTextPoint, msWatermark ); diff --git a/filter/source/pdf/pdfexport.hxx b/filter/source/pdf/pdfexport.hxx index 40ac7d3e2f9a..dfd371c3177b 100644 --- a/filter/source/pdf/pdfexport.hxx +++ b/filter/source/pdf/pdfexport.hxx @@ -76,6 +76,7 @@ private: Color maWatermarkColor; std::optional moWatermarkFontHeight; OUString maWatermarkFontName; + std::optional moWatermarkRotateAngle; OUString msTiledWatermark; // these variable are here only to have a location in filter/pdf to set the default