diff options
Diffstat (limited to 'filter/source/pdf/pdfexport.cxx')
-rw-r--r-- | filter/source/pdf/pdfexport.cxx | 1374 |
1 files changed, 1374 insertions, 0 deletions
diff --git a/filter/source/pdf/pdfexport.cxx b/filter/source/pdf/pdfexport.cxx new file mode 100644 index 000000000..7a233aad0 --- /dev/null +++ b/filter/source/pdf/pdfexport.cxx @@ -0,0 +1,1374 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <osl/file.hxx> +#include <tools/debug.hxx> +#include <tools/urlobj.hxx> +#include <tools/poly.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/resmgr.hxx> +#include <utility> +#include <vcl/canvastools.hxx> +#include <vcl/mapmod.hxx> +#include <vcl/gdimtf.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/string.hxx> +#include <comphelper/storagehelper.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <toolkit/awt/vclxdevice.hxx> +#include <unotools/configmgr.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/basemutex.hxx> +#include <officecfg/Office/Common.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> + +#include "pdfexport.hxx" +#include <strings.hrc> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/view/XViewSettingsSupplier.hpp> +#include <com/sun/star/task/XInteractionRequest.hpp> +#include <com/sun/star/task/PDFExportException.hpp> +#include <com/sun/star/io/IOException.hpp> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/security/XCertificate.hpp> +#include <com/sun/star/beans/XMaterialHolder.hpp> +#include <com/sun/star/xml/crypto/SEInitializer.hpp> + +#include <memory> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::graphic; + + +PDFExport::PDFExport( const Reference< XComponent >& rxSrcDoc, + const Reference< task::XStatusIndicator >& rxStatusIndicator, + const Reference< task::XInteractionHandler >& rxIH, + const Reference< XComponentContext >& xContext ) : + mxSrcDoc ( rxSrcDoc ), + mxContext ( xContext ), + mxStatusIndicator ( rxStatusIndicator ), + mxIH ( rxIH ), + mbUseTaggedPDF ( false ), + mnPDFTypeSelection ( 0 ), + mbPDFUACompliance ( false), + mbExportNotes ( true ), + mbExportPlaceholders ( false ), + mbUseReferenceXObject ( false ), + mbExportNotesPages ( false ), + mbExportOnlyNotesPages ( false ), + mbUseTransitionEffects ( true ), + mbExportBookmarks ( true ), + mbExportHiddenSlides ( false ), + mbSinglePageSheets ( false ), + mnOpenBookmarkLevels ( -1 ), + mbUseLosslessCompression ( false ), + mbReduceImageResolution ( true ), + mbSkipEmptyPages ( true ), + mbAddStream ( false ), + mnMaxImageResolution ( 300 ), + mnQuality ( 80 ), + mnFormsFormat ( 0 ), + mbExportFormFields ( true ), + mbAllowDuplicateFieldNames ( false ), + mnProgressValue ( 0 ), + mbRemoveTransparencies ( false ), + + mbIsRedactMode ( false ), + maWatermarkColor ( COL_LIGHTGREEN ), + maWatermarkFontName ( "Helvetica" ), + + mbHideViewerToolbar ( false ), + mbHideViewerMenubar ( false ), + mbHideViewerWindowControls ( false ), + mbFitWindow ( false ), + mbCenterWindow ( false ), + mbOpenInFullScreenMode ( false ), + mbDisplayPDFDocumentTitle ( true ), + mnPDFDocumentMode ( 0 ), + mnPDFDocumentAction ( 0 ), + mnZoom ( 100 ), + mnInitialPage ( 1 ), + mnPDFPageLayout ( 0 ), + + mbEncrypt ( false ), + mbRestrictPermissions ( false ), + mnPrintAllowed ( 2 ), + mnChangesAllowed ( 4 ), + mbCanCopyOrExtract ( true ), + mbCanExtractForAccessibility( true ), + + // #i56629 + mbExportRelativeFsysLinks ( false ), + mnDefaultLinkAction ( 0 ), + mbConvertOOoTargetToPDFTarget( false ), + mbExportBmkToDest ( false ), + mbSignPDF ( false ) +{ +} + + +PDFExport::~PDFExport() +{ +} + + +bool PDFExport::ExportSelection( vcl::PDFWriter& rPDFWriter, + Reference< css::view::XRenderable > const & rRenderable, + const Any& rSelection, + const StringRangeEnumerator& rRangeEnum, + Sequence< PropertyValue >& rRenderOptions, + sal_Int32 nPageCount ) +{ + bool bRet = false; + try + { + Any* pFirstPage = nullptr; + Any* pLastPage = nullptr; + + bool bExportNotesPages = false; + + auto rRenderOptionsRange = asNonConstRange(rRenderOptions); + for( sal_Int32 nData = 0, nDataCount = rRenderOptions.getLength(); nData < nDataCount; ++nData ) + { + if ( rRenderOptions[ nData ].Name == "IsFirstPage" ) + pFirstPage = &rRenderOptionsRange[ nData ].Value; + else if ( rRenderOptions[ nData ].Name == "IsLastPage" ) + pLastPage = &rRenderOptionsRange[ nData ].Value; + else if ( rRenderOptions[ nData ].Name == "ExportNotesPages" ) + rRenderOptionsRange[ nData ].Value >>= bExportNotesPages; + } + + OutputDevice* pOut = rPDFWriter.GetReferenceDevice(); + + if( pOut ) + { + if ( nPageCount ) + { + vcl::PDFExtOutDevData& rPDFExtOutDevData = dynamic_cast<vcl::PDFExtOutDevData&>(*pOut->GetExtOutDevData()); + rPDFExtOutDevData.SetIsExportNotesPages( bExportNotesPages ); + + sal_Int32 nCurrentPage(0); + StringRangeEnumerator::Iterator aIter = rRangeEnum.begin(); + StringRangeEnumerator::Iterator aEnd = rRangeEnum.end(); + while ( aIter != aEnd ) + { + const Sequence< PropertyValue > aRenderer( rRenderable->getRenderer( *aIter, rSelection, rRenderOptions ) ); + awt::Size aPageSize; + + for( const PropertyValue& rProp : aRenderer ) + { + if ( rProp.Name == "PageSize" ) + { + rProp.Value >>= aPageSize; + break; + } + } + + rPDFExtOutDevData.SetCurrentPageNumber( nCurrentPage ); + + GDIMetaFile aMtf; + const MapMode aMapMode( MapUnit::Map100thMM ); + const Size aMtfSize( aPageSize.Width, aPageSize.Height ); + + pOut->Push(); + pOut->EnableOutput( false ); + pOut->SetMapMode( aMapMode ); + + aMtf.SetPrefSize( aMtfSize ); + aMtf.SetPrefMapMode( aMapMode ); + aMtf.Record( pOut ); + + // #i35176# + // IsLastPage property. + const sal_Int32 nCurrentRenderer = *aIter; + ++aIter; + if ( pLastPage && aIter == aEnd ) + *pLastPage <<= true; + + rRenderable->render( nCurrentRenderer, rSelection, rRenderOptions ); + + aMtf.Stop(); + aMtf.WindStart(); + + bool bEmptyPage = false; + if( aMtf.GetActionSize() && + ( !mbSkipEmptyPages || aPageSize.Width || aPageSize.Height ) ) + { + // We convert the whole metafile into a bitmap to get rid of the + // text covered by redaction shapes + if (mbIsRedactMode) + { + try + { + Graphic aGraph(aMtf); + // use antialiasing to improve how graphic objects look + BitmapEx bmp = aGraph.GetBitmapEx(GraphicConversionParameters(Size(0, 0), false, true, false)); + Graphic bgraph(bmp); + aMtf = bgraph.GetGDIMetaFile(); + } + catch(const Exception&) + { + TOOLS_WARN_EXCEPTION("filter.pdf", "Something went wrong while converting metafile to bitmap"); + } + } + + ImplExportPage(rPDFWriter, rPDFExtOutDevData, aMtf); + bRet = true; + } + else + { + bEmptyPage = true; + } + + pOut->Pop(); + + if ( mxStatusIndicator.is() ) + mxStatusIndicator->setValue( mnProgressValue ); + if ( pFirstPage ) + *pFirstPage <<= false; + + ++mnProgressValue; + if (!bEmptyPage) + { + // Calculate the page number in the PDF output, which may be smaller than the page number in + // case of hidden slides or a partial export. + ++nCurrentPage; + } + } + } + else + { + bRet = true; // #i18334# nPageCount == 0, + rPDFWriter.NewPage( 10000, 10000 ); // creating dummy page + rPDFWriter.SetMapMode(MapMode(MapUnit::Map100thMM)); + } + } + } + catch(const RuntimeException &) + { + } + return bRet; +} + +namespace { + +class PDFExportStreamDoc : public vcl::PDFOutputStream +{ +private: + + Reference< XComponent > m_xSrcDoc; + Sequence< beans::NamedValue > m_aPreparedPassword; + +public: + + PDFExportStreamDoc( const Reference< XComponent >& xDoc, const Sequence<beans::NamedValue>& rPwd ) + : m_xSrcDoc( xDoc ), + m_aPreparedPassword( rPwd ) + {} + + virtual void write( const Reference< XOutputStream >& xStream ) override; +}; + +} + +void PDFExportStreamDoc::write( const Reference< XOutputStream >& xStream ) +{ + Reference< css::frame::XStorable > xStore( m_xSrcDoc, UNO_QUERY ); + if( !xStore.is() ) + return; + + Sequence< beans::PropertyValue > aArgs( 2 + (m_aPreparedPassword.hasElements() ? 1 : 0) ); + aArgs.getArray()[0].Name = "FilterName"; + aArgs.getArray()[1].Name = "OutputStream"; + aArgs.getArray()[1].Value <<= xStream; + if( m_aPreparedPassword.hasElements() ) + { + aArgs.getArray()[2].Name = "EncryptionData"; + aArgs.getArray()[2].Value <<= m_aPreparedPassword; + } + + try + { + xStore->storeToURL( "private:stream", aArgs ); + } + catch( const IOException& ) + { + } +} + + +static OUString getMimetypeForDocument( const Reference< XComponentContext >& xContext, + const Reference< XComponent >& xDoc ) noexcept +{ + OUString aDocMimetype; + try + { + // get document service name + Reference< css::frame::XStorable > xStore( xDoc, UNO_QUERY ); + Reference< frame::XModuleManager2 > xModuleManager = frame::ModuleManager::create(xContext); + if( xStore.is() ) + { + OUString aDocServiceName = xModuleManager->identify( Reference< XInterface >( xStore, uno::UNO_QUERY ) ); + if ( !aDocServiceName.isEmpty() ) + { + // get the actual filter name + Reference< lang::XMultiServiceFactory > xConfigProvider = + configuration::theDefaultProvider::get( xContext ); + beans::NamedValue aPathProp; + aPathProp.Name = "nodepath"; + aPathProp.Value <<= OUString( "/org.openoffice.Setup/Office/Factories/" ); + uno::Sequence< uno::Any > aArgs{ uno::Any(aPathProp) }; + + Reference< container::XNameAccess > xSOFConfig( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", aArgs ), + uno::UNO_QUERY ); + + Reference< container::XNameAccess > xApplConfig; + xSOFConfig->getByName( aDocServiceName ) >>= xApplConfig; + if ( xApplConfig.is() ) + { + OUString aFilterName; + xApplConfig->getByName( "ooSetupFactoryActualFilter" ) >>= aFilterName; + if( !aFilterName.isEmpty() ) + { + // find the related type name + OUString aTypeName; + Reference< container::XNameAccess > xFilterFactory( + xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", xContext), + uno::UNO_QUERY ); + + Sequence< beans::PropertyValue > aFilterData; + xFilterFactory->getByName( aFilterName ) >>= aFilterData; + for ( const beans::PropertyValue& rProp : std::as_const(aFilterData) ) + if ( rProp.Name == "Type" ) + rProp.Value >>= aTypeName; + + if ( !aTypeName.isEmpty() ) + { + // find the mediatype + Reference< container::XNameAccess > xTypeDetection( + xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", xContext), + UNO_QUERY ); + + Sequence< beans::PropertyValue > aTypeData; + xTypeDetection->getByName( aTypeName ) >>= aTypeData; + for ( const beans::PropertyValue& rProp : std::as_const(aTypeData) ) + if ( rProp.Name == "MediaType" ) + rProp.Value >>= aDocMimetype; + } + } + } + } + } + } + catch (...) + { + } + return aDocMimetype; +} + +uno::Reference<security::XCertificate> +PDFExport::GetCertificateFromSubjectName(const std::u16string_view& rSubjectName) const +{ + uno::Reference<xml::crypto::XSEInitializer> xSEInitializer + = xml::crypto::SEInitializer::create(mxContext); + uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext + = xSEInitializer->createSecurityContext(OUString()); + uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment + = xSecurityContext->getSecurityEnvironment(); + for (const auto& xCertificate : xSecurityEnvironment->getPersonalCertificates()) + { + if (xCertificate->getSubjectName() == rSubjectName) + { + return xCertificate; + } + } + + return {}; +} + +bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& rFilterData ) +{ + INetURLObject aURL( rFile ); + bool bRet = false; + + std::set< vcl::PDFWriter::ErrorCode > aErrors; + + if( aURL.GetProtocol() != INetProtocol::File ) + { + OUString aTmp; + + if( osl::FileBase::getFileURLFromSystemPath( rFile, aTmp ) == osl::FileBase::E_None ) + aURL = INetURLObject(aTmp); + } + + if( aURL.GetProtocol() == INetProtocol::File ) + { + Reference< XRenderable > xRenderable( mxSrcDoc, UNO_QUERY ); + + if( xRenderable.is() ) + { + rtl::Reference<VCLXDevice> xDevice(new VCLXDevice); + OUString aPageRange; + Any aSelection; + vcl::PDFWriter::PDFWriterContext aContext; + OUString aOpenPassword, aPermissionPassword; + Reference< beans::XMaterialHolder > xEnc; + Sequence< beans::NamedValue > aPreparedPermissionPassword; + + + // getting the string for the creator + OUString aCreator; + Reference< XServiceInfo > xInfo( mxSrcDoc, UNO_QUERY ); + if ( xInfo.is() ) + { + if ( xInfo->supportsService( "com.sun.star.presentation.PresentationDocument" ) ) + aCreator += "Impress"; + else if ( xInfo->supportsService( "com.sun.star.drawing.DrawingDocument" ) ) + aCreator += "Draw"; + else if ( xInfo->supportsService( "com.sun.star.text.TextDocument" ) ) + aCreator += "Writer"; + else if ( xInfo->supportsService( "com.sun.star.sheet.SpreadsheetDocument" ) ) + aCreator += "Calc"; + else if ( xInfo->supportsService( "com.sun.star.formula.FormulaProperties" ) ) + aCreator += "Math"; + } + + Reference< document::XDocumentPropertiesSupplier > xDocumentPropsSupplier( mxSrcDoc, UNO_QUERY ); + if ( xDocumentPropsSupplier.is() ) + { + Reference< document::XDocumentProperties > xDocumentProps( xDocumentPropsSupplier->getDocumentProperties() ); + if ( xDocumentProps.is() ) + { + aContext.DocumentInfo.Title = xDocumentProps->getTitle(); + aContext.DocumentInfo.Author = xDocumentProps->getAuthor(); + aContext.DocumentInfo.Subject = xDocumentProps->getSubject(); + aContext.DocumentInfo.Keywords = ::comphelper::string::convertCommaSeparated(xDocumentProps->getKeywords()); + } + } + // getting the string for the producer + OUString aProducerOverride = officecfg::Office::Common::Save::Document::GeneratorOverride::get(); + if( !aProducerOverride.isEmpty()) + aContext.DocumentInfo.Producer = aProducerOverride; + else + aContext.DocumentInfo.Producer = + utl::ConfigManager::getProductName() + + " " + + utl::ConfigManager::getProductVersion(); + + aContext.DocumentInfo.Creator = aCreator; + + OUString aSignCertificateSubjectName; + for ( const beans::PropertyValue& rProp : rFilterData ) + { + if ( rProp.Name == "PageRange" ) + rProp.Value >>= aPageRange; + else if ( rProp.Name == "Selection" ) + aSelection = rProp.Value; + else if ( rProp.Name == "UseLosslessCompression" ) + rProp.Value >>= mbUseLosslessCompression; + else if ( rProp.Name == "Quality" ) + rProp.Value >>= mnQuality; + else if ( rProp.Name == "ReduceImageResolution" ) + rProp.Value >>= mbReduceImageResolution; + else if ( rProp.Name == "IsSkipEmptyPages" ) + rProp.Value >>= mbSkipEmptyPages; + else if ( rProp.Name == "MaxImageResolution" ) + rProp.Value >>= mnMaxImageResolution; + else if ( rProp.Name == "UseTaggedPDF" ) + rProp.Value >>= mbUseTaggedPDF; + else if ( rProp.Name == "SelectPdfVersion" ) + rProp.Value >>= mnPDFTypeSelection; + else if ( rProp.Name == "PDFUACompliance" ) + rProp.Value >>= mbPDFUACompliance; + else if ( rProp.Name == "ExportNotes" ) + rProp.Value >>= mbExportNotes; + else if ( rProp.Name == "ExportNotesPages" ) + rProp.Value >>= mbExportNotesPages; + else if ( rProp.Name == "ExportOnlyNotesPages" ) + rProp.Value >>= mbExportOnlyNotesPages; + else if ( rProp.Name == "UseTransitionEffects" ) + rProp.Value >>= mbUseTransitionEffects; + else if ( rProp.Name == "ExportFormFields" ) + rProp.Value >>= mbExportFormFields; + else if ( rProp.Name == "FormsType" ) + rProp.Value >>= mnFormsFormat; + else if ( rProp.Name == "AllowDuplicateFieldNames" ) + rProp.Value >>= mbAllowDuplicateFieldNames; + // viewer properties + else if ( rProp.Name == "HideViewerToolbar" ) + rProp.Value >>= mbHideViewerToolbar; + else if ( rProp.Name == "HideViewerMenubar" ) + rProp.Value >>= mbHideViewerMenubar; + else if ( rProp.Name == "HideViewerWindowControls" ) + rProp.Value >>= mbHideViewerWindowControls; + else if ( rProp.Name == "ResizeWindowToInitialPage" ) + rProp.Value >>= mbFitWindow; + else if ( rProp.Name == "CenterWindow" ) + rProp.Value >>= mbCenterWindow; + else if ( rProp.Name == "OpenInFullScreenMode" ) + rProp.Value >>= mbOpenInFullScreenMode; + else if ( rProp.Name == "DisplayPDFDocumentTitle" ) + rProp.Value >>= mbDisplayPDFDocumentTitle; + else if ( rProp.Name == "InitialView" ) + rProp.Value >>= mnPDFDocumentMode; + else if ( rProp.Name == "Magnification" ) + rProp.Value >>= mnPDFDocumentAction; + else if ( rProp.Name == "Zoom" ) + rProp.Value >>= mnZoom; + else if ( rProp.Name == "InitialPage" ) + rProp.Value >>= mnInitialPage; + else if ( rProp.Name == "PageLayout" ) + rProp.Value >>= mnPDFPageLayout; + else if ( rProp.Name == "FirstPageOnLeft" ) + rProp.Value >>= aContext.FirstPageLeft; + else if ( rProp.Name == "IsAddStream" ) + rProp.Value >>= mbAddStream; + else if ( rProp.Name == "Watermark" ) + rProp.Value >>= msWatermark; + else if ( rProp.Name == "WatermarkColor" ) + { + sal_Int32 nColor{}; + if (rProp.Value >>= nColor) + { + maWatermarkColor = Color(ColorTransparency, nColor); + } + } + else if (rProp.Name == "WatermarkFontHeight") + { + sal_Int32 nFontHeight{}; + if (rProp.Value >>= nFontHeight) + { + moWatermarkFontHeight = nFontHeight; + } + } + else if (rProp.Name == "WatermarkRotateAngle") + { + sal_Int32 nRotateAngle{}; + if (rProp.Value >>= nRotateAngle) + { + moWatermarkRotateAngle = Degree10(nRotateAngle); + } + } + else if (rProp.Name == "WatermarkFontName") + { + OUString aFontName{}; + if (rProp.Value >>= aFontName) + { + maWatermarkFontName = aFontName; + } + } + else if ( rProp.Name == "TiledWatermark" ) + rProp.Value >>= msTiledWatermark; + // now all the security related properties... + else if ( rProp.Name == "EncryptFile" ) + rProp.Value >>= mbEncrypt; + else if ( rProp.Name == "DocumentOpenPassword" ) + rProp.Value >>= aOpenPassword; + else if ( rProp.Name == "RestrictPermissions" ) + rProp.Value >>= mbRestrictPermissions; + else if ( rProp.Name == "PermissionPassword" ) + rProp.Value >>= aPermissionPassword; + else if ( rProp.Name == "PreparedPasswords" ) + rProp.Value >>= xEnc; + else if ( rProp.Name == "PreparedPermissionPassword" ) + rProp.Value >>= aPreparedPermissionPassword; + else if ( rProp.Name == "Printing" ) + rProp.Value >>= mnPrintAllowed; + else if ( rProp.Name == "Changes" ) + rProp.Value >>= mnChangesAllowed; + else if ( rProp.Name == "EnableCopyingOfContent" ) + rProp.Value >>= mbCanCopyOrExtract; + else if ( rProp.Name == "EnableTextAccessForAccessibilityTools" ) + rProp.Value >>= mbCanExtractForAccessibility; + // i56629 links extra (relative links and other related stuff) + else if ( rProp.Name == "ExportLinksRelativeFsys" ) + rProp.Value >>= mbExportRelativeFsysLinks; + else if ( rProp.Name == "PDFViewSelection" ) + rProp.Value >>= mnDefaultLinkAction; + else if ( rProp.Name == "ConvertOOoTargetToPDFTarget" ) + rProp.Value >>= mbConvertOOoTargetToPDFTarget; + else if ( rProp.Name == "ExportBookmarksToPDFDestination" ) + rProp.Value >>= mbExportBmkToDest; + else if ( rProp.Name == "ExportBookmarks" ) + rProp.Value >>= mbExportBookmarks; + else if ( rProp.Name == "ExportHiddenSlides" ) + rProp.Value >>= mbExportHiddenSlides; + else if ( rProp.Name == "SinglePageSheets" ) + rProp.Value >>= mbSinglePageSheets; + else if ( rProp.Name == "OpenBookmarkLevels" ) + rProp.Value >>= mnOpenBookmarkLevels; + else if ( rProp.Name == "SignPDF" ) + rProp.Value >>= mbSignPDF; + else if ( rProp.Name == "SignatureLocation" ) + rProp.Value >>= msSignLocation; + else if ( rProp.Name == "SignatureReason" ) + rProp.Value >>= msSignReason; + else if ( rProp.Name == "SignatureContactInfo" ) + rProp.Value >>= msSignContact; + else if ( rProp.Name == "SignaturePassword" ) + rProp.Value >>= msSignPassword; + else if ( rProp.Name == "SignatureCertificate" ) + rProp.Value >>= maSignCertificate; + else if (rProp.Name == "SignCertificateSubjectName") + rProp.Value >>= aSignCertificateSubjectName; + else if ( rProp.Name == "SignatureTSA" ) + rProp.Value >>= msSignTSA; + else if ( rProp.Name == "ExportPlaceholders" ) + rProp.Value >>= mbExportPlaceholders; + else if ( rProp.Name == "UseReferenceXObject" ) + rProp.Value >>= mbUseReferenceXObject; + // Redaction & bitmap related stuff + else if ( rProp.Name == "IsRedactMode" ) + rProp.Value >>= mbIsRedactMode; + } + + if (!maSignCertificate.is() && !aSignCertificateSubjectName.isEmpty()) + { + maSignCertificate = GetCertificateFromSubjectName(aSignCertificateSubjectName); + } + + aContext.URL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri); + + // set the correct version, depending on user request + switch( mnPDFTypeSelection ) + { + default: + case 0: + aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_6; + break; + case 1: + aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_1; + mbUseTaggedPDF = true; // force the tagged PDF as well + mbRemoveTransparencies = true; // does not allow transparencies + mbEncrypt = false; // no encryption + xEnc.clear(); + break; + case 2: + aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_2; + mbUseTaggedPDF = true; // force the tagged PDF as well + mbRemoveTransparencies = false; // does allow transparencies + mbEncrypt = false; // no encryption + xEnc.clear(); + break; + case 3: + aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_3; + mbUseTaggedPDF = true; // force the tagged PDF as well + mbRemoveTransparencies = false; // does allow transparencies + mbEncrypt = false; // no encryption + xEnc.clear(); + break; + case 15: + aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_5; + break; + case 16: + aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_6; + break; + } + + // PDF/UA support + aContext.UniversalAccessibilityCompliance = mbPDFUACompliance; + if (mbPDFUACompliance) + { + mbUseTaggedPDF = true; + } + + // copy in context the values default in the constructor or set by the FilterData sequence of properties + aContext.Tagged = mbUseTaggedPDF; + + // values used in viewer + aContext.HideViewerToolbar = mbHideViewerToolbar; + aContext.HideViewerMenubar = mbHideViewerMenubar; + aContext.HideViewerWindowControls = mbHideViewerWindowControls; + aContext.FitWindow = mbFitWindow; + aContext.CenterWindow = mbCenterWindow; + aContext.OpenInFullScreenMode = mbOpenInFullScreenMode; + aContext.DisplayPDFDocumentTitle = mbDisplayPDFDocumentTitle; + aContext.InitialPage = mnInitialPage-1; + aContext.OpenBookmarkLevels = mnOpenBookmarkLevels; + + switch( mnPDFDocumentMode ) + { + default: + case 0: + aContext.PDFDocumentMode = vcl::PDFWriter::ModeDefault; + break; + case 1: + aContext.PDFDocumentMode = vcl::PDFWriter::UseOutlines; + break; + case 2: + aContext.PDFDocumentMode = vcl::PDFWriter::UseThumbs; + break; + } + switch( mnPDFDocumentAction ) + { + default: + case 0: + aContext.PDFDocumentAction = vcl::PDFWriter::ActionDefault; + break; + case 1: + aContext.PDFDocumentAction = vcl::PDFWriter::FitInWindow; + break; + case 2: + aContext.PDFDocumentAction = vcl::PDFWriter::FitWidth; + break; + case 3: + aContext.PDFDocumentAction = vcl::PDFWriter::FitVisible; + break; + case 4: + aContext.PDFDocumentAction = vcl::PDFWriter::ActionZoom; + aContext.Zoom = mnZoom; + break; + } + + switch( mnPDFPageLayout ) + { + default: + case 0: + aContext.PageLayout = vcl::PDFWriter::DefaultLayout; + break; + case 1: + aContext.PageLayout = vcl::PDFWriter::SinglePage; + break; + case 2: + aContext.PageLayout = vcl::PDFWriter::Continuous; + break; + case 3: + aContext.PageLayout = vcl::PDFWriter::ContinuousFacing; + break; + } + + aContext.FirstPageLeft = false; + + // check if PDF/A, which does not allow encryption + if( aContext.Version != vcl::PDFWriter::PDFVersion::PDF_A_1 ) + { + // set check for permission change password + // if not enabled and no permission password, force permissions to default as if PDF where without encryption + if( mbRestrictPermissions && (xEnc.is() || !aPermissionPassword.isEmpty()) ) + { + mbEncrypt = true; // permission set as desired, done after + } + else + { + // force permission to default + mnPrintAllowed = 2 ; + mnChangesAllowed = 4 ; + mbCanCopyOrExtract = true; + mbCanExtractForAccessibility = true ; + } + + switch( mnPrintAllowed ) + { + case 0: // initialized when aContext is build, means no printing + break; + default: + case 2: + aContext.Encryption.CanPrintFull = true; + [[fallthrough]]; + case 1: + aContext.Encryption.CanPrintTheDocument = true; + break; + } + + switch( mnChangesAllowed ) + { + case 0: // already in struct PDFSecPermissions CTOR + break; + case 1: + aContext.Encryption.CanAssemble = true; + break; + case 2: + aContext.Encryption.CanFillInteractive = true; + break; + case 3: + aContext.Encryption.CanAddOrModify = true; + break; + default: + case 4: + aContext.Encryption.CanModifyTheContent = + aContext.Encryption.CanCopyOrExtract = + aContext.Encryption.CanAddOrModify = + aContext.Encryption.CanFillInteractive = true; + break; + } + + aContext.Encryption.CanCopyOrExtract = mbCanCopyOrExtract; + aContext.Encryption.CanExtractForAccessibility = mbCanExtractForAccessibility; + if( mbEncrypt && ! xEnc.is() ) + xEnc = vcl::PDFWriter::InitEncryption( aPermissionPassword, aOpenPassword ); + if( mbEncrypt && !aPermissionPassword.isEmpty() && ! aPreparedPermissionPassword.hasElements() ) + aPreparedPermissionPassword = comphelper::OStorageHelper::CreatePackageEncryptionData( aPermissionPassword ); + } + // after this point we don't need the legacy clear passwords anymore + // however they are still inside the passed filter data sequence + // which is sadly out of our control + aPermissionPassword.clear(); + aOpenPassword.clear(); + + /* + * FIXME: the entries are only implicitly defined by the resource file. Should there + * ever be an additional form submit format this could get invalid. + */ + switch( mnFormsFormat ) + { + case 1: + aContext.SubmitFormat = vcl::PDFWriter::PDF; + break; + case 2: + aContext.SubmitFormat = vcl::PDFWriter::HTML; + break; + case 3: + aContext.SubmitFormat = vcl::PDFWriter::XML; + break; + default: + case 0: + aContext.SubmitFormat = vcl::PDFWriter::FDF; + break; + } + aContext.AllowDuplicateFieldNames = mbAllowDuplicateFieldNames; + + // get model + Reference< frame::XModel > xModel( mxSrcDoc, UNO_QUERY ); + { + // #i56629: Relative link stuff + // set the base URL of the file: then base URL + aContext.BaseURL = xModel->getURL(); + // relative link option is private to PDF Export filter and limited to local filesystem only + aContext.RelFsys = mbExportRelativeFsysLinks; + // determine the default acton for PDF links + switch( mnDefaultLinkAction ) + { + default: + // default: URI, without fragment conversion (the bookmark in PDF may not work) + case 0: + aContext.DefaultLinkAction = vcl::PDFWriter::URIAction; + break; + case 1: + // view PDF through the reader application + aContext.ForcePDFAction = true; + aContext.DefaultLinkAction = vcl::PDFWriter::LaunchAction; + break; + case 2: + // view PDF through an Internet browser + aContext.DefaultLinkAction = vcl::PDFWriter::URIActionDestination; + break; + } + aContext.ConvertOOoTargetToPDFTarget = mbConvertOOoTargetToPDFTarget; + + // check for Link Launch action, not allowed on PDF/A-1 + // this code chunk checks when the filter is called from scripting + if( aContext.Version == vcl::PDFWriter::PDFVersion::PDF_A_1 && + aContext.DefaultLinkAction == vcl::PDFWriter::LaunchAction ) + { + // force the similar allowed URI action + aContext.DefaultLinkAction = vcl::PDFWriter::URIActionDestination; + // and remove the remote goto action forced on PDF file + aContext.ForcePDFAction = false; + } + } + + aContext.SignPDF = mbSignPDF; + aContext.SignLocation = msSignLocation; + aContext.SignContact = msSignContact; + aContext.SignReason = msSignReason; + aContext.SignPassword = msSignPassword; + aContext.SignCertificate = maSignCertificate; + aContext.SignTSA = msSignTSA; + aContext.UseReferenceXObject = mbUseReferenceXObject; + + // all context data set, time to create the printing device + vcl::PDFWriter aPDFWriter( aContext, xEnc ); + OutputDevice* pOut = aPDFWriter.GetReferenceDevice(); + + DBG_ASSERT( pOut, "PDFExport::Export: no reference device" ); + xDevice->SetOutputDevice(pOut); + + if( mbAddStream ) + { + // export stream + // get mimetype + OUString aSrcMimetype = getMimetypeForDocument( mxContext, mxSrcDoc ); + aPDFWriter.AddStream( aSrcMimetype, + new PDFExportStreamDoc( mxSrcDoc, aPreparedPermissionPassword ) + ); + } + + if ( pOut ) + { + DBG_ASSERT( pOut->GetExtOutDevData() == nullptr, "PDFExport: ExtOutDevData already set!!!" ); + vcl::PDFExtOutDevData aPDFExtOutDevData( *pOut ); + pOut->SetExtOutDevData( &aPDFExtOutDevData ); + aPDFExtOutDevData.SetIsExportNotes( mbExportNotes ); + aPDFExtOutDevData.SetIsExportTaggedPDF( mbUseTaggedPDF ); + aPDFExtOutDevData.SetIsExportTransitionEffects( mbUseTransitionEffects ); + aPDFExtOutDevData.SetIsExportFormFields( mbExportFormFields ); + aPDFExtOutDevData.SetIsExportBookmarks( mbExportBookmarks ); + aPDFExtOutDevData.SetIsExportHiddenSlides( mbExportHiddenSlides ); + aPDFExtOutDevData.SetIsSinglePageSheets( mbSinglePageSheets ); + aPDFExtOutDevData.SetIsLosslessCompression( mbUseLosslessCompression ); + aPDFExtOutDevData.SetCompressionQuality( mnQuality ); + aPDFExtOutDevData.SetIsReduceImageResolution( mbReduceImageResolution ); + aPDFExtOutDevData.SetIsExportNamedDestinations( mbExportBmkToDest ); + + Sequence< PropertyValue > aRenderOptions{ + comphelper::makePropertyValue("RenderDevice", uno::Reference<awt::XDevice>(xDevice)), + comphelper::makePropertyValue("ExportNotesPages", false), + comphelper::makePropertyValue("IsFirstPage", true), + comphelper::makePropertyValue("IsLastPage", false), + comphelper::makePropertyValue("IsSkipEmptyPages", mbSkipEmptyPages), + comphelper::makePropertyValue("PageRange", aPageRange), + comphelper::makePropertyValue("ExportPlaceholders", mbExportPlaceholders), + comphelper::makePropertyValue("SinglePageSheets", mbSinglePageSheets) + }; + Any& rExportNotesValue = aRenderOptions.getArray()[ 1 ].Value; + + if( !aPageRange.isEmpty() || !aSelection.hasValue() ) + { + aSelection = Any(); + aSelection <<= mxSrcDoc; + } + bool bExportNotesPages = false; + bool bReChangeToNormalView = false; + static const OUStringLiteral sShowOnlineLayout( u"ShowOnlineLayout" ); + bool bReHideWhitespace = false; + static const OUStringLiteral sHideWhitespace(u"HideWhitespace"); + uno::Reference< beans::XPropertySet > xViewProperties; + + if ( aCreator == "Writer" ) + { + // #i92835: if Writer is in web layout mode this has to be switched to normal view and back to web view in the end + try + { + Reference< view::XViewSettingsSupplier > xVSettingsSupplier( xModel->getCurrentController(), uno::UNO_QUERY_THROW ); + xViewProperties = xVSettingsSupplier->getViewSettings(); + xViewProperties->getPropertyValue( sShowOnlineLayout ) >>= bReChangeToNormalView; + if( bReChangeToNormalView ) + { + xViewProperties->setPropertyValue( sShowOnlineLayout, uno::Any( false ) ); + } + + // Also, disable hide-whitespace during export. + xViewProperties->getPropertyValue(sHideWhitespace) >>= bReHideWhitespace; + if (bReHideWhitespace) + { + xViewProperties->setPropertyValue(sHideWhitespace, uno::Any(false)); + } + } + catch( const uno::Exception& ) + { + } + + } + + const sal_Int32 nPageCount = xRenderable->getRendererCount( aSelection, aRenderOptions ); + + if ( mbExportNotesPages && aCreator == "Impress" ) + { + uno::Reference< drawing::XShapes > xShapes; // do not allow to export notes when exporting a selection + if ( ! ( aSelection >>= xShapes ) ) + bExportNotesPages = true; + } + const bool bExportPages = !bExportNotesPages || !mbExportOnlyNotesPages; + + if( aPageRange.isEmpty() || mbSinglePageSheets) + { + aPageRange = OUString::number( 1 ) + "-" + OUString::number(nPageCount ); + } + StringRangeEnumerator aRangeEnum( aPageRange, 0, nPageCount-1 ); + + if ( mxStatusIndicator.is() ) + { + std::locale loc(Translate::Create("flt")); + sal_Int32 nTotalPageCount = aRangeEnum.size(); + if ( bExportPages && bExportNotesPages ) + nTotalPageCount *= 2; + mxStatusIndicator->start(Translate::get(PDF_PROGRESS_BAR, loc), nTotalPageCount); + } + + bRet = nPageCount > 0; + + if ( bRet && bExportPages ) + bRet = ExportSelection( aPDFWriter, xRenderable, aSelection, aRangeEnum, aRenderOptions, nPageCount ); + + if ( bRet && bExportNotesPages ) + { + rExportNotesValue <<= true; + bRet = ExportSelection( aPDFWriter, xRenderable, aSelection, aRangeEnum, aRenderOptions, nPageCount ); + } + if ( mxStatusIndicator.is() ) + mxStatusIndicator->end(); + + // if during the export the doc locale was set copy it to PDF writer + const css::lang::Locale& rLoc( aPDFExtOutDevData.GetDocumentLocale() ); + if( !rLoc.Language.isEmpty() ) + aPDFWriter.SetDocumentLocale( rLoc ); + + if( bRet ) + { + aPDFExtOutDevData.PlayGlobalActions( aPDFWriter ); + bRet = aPDFWriter.Emit(); + aErrors = aPDFWriter.GetErrors(); + } + pOut->SetExtOutDevData( nullptr ); + if( bReChangeToNormalView ) + { + try + { + xViewProperties->setPropertyValue( sShowOnlineLayout, uno::Any( true ) ); + } + catch( const uno::Exception& ) + { + } + } + if( bReHideWhitespace ) + { + try + { + xViewProperties->setPropertyValue( sHideWhitespace, uno::Any( true ) ); + } + catch( const uno::Exception& ) + { + } + } + } + } + } + + // show eventual errors during export + showErrors( aErrors ); + + return bRet; +} + + +namespace +{ + +typedef cppu::WeakComponentImplHelper< task::XInteractionRequest > PDFErrorRequestBase; + +class PDFErrorRequest : private cppu::BaseMutex, + public PDFErrorRequestBase +{ + task::PDFExportException maExc; +public: + explicit PDFErrorRequest( task::PDFExportException aExc ); + + // XInteractionRequest + virtual uno::Any SAL_CALL getRequest() override; + virtual uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL getContinuations() override; +}; + + +PDFErrorRequest::PDFErrorRequest( task::PDFExportException aExc ) : + PDFErrorRequestBase( m_aMutex ), + maExc(std::move( aExc )) +{ +} + + +uno::Any SAL_CALL PDFErrorRequest::getRequest() +{ + osl::MutexGuard const guard( m_aMutex ); + + uno::Any aRet; + aRet <<= maExc; + return aRet; +} + + +uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL PDFErrorRequest::getContinuations() +{ + return uno::Sequence< uno::Reference< task::XInteractionContinuation > >(); +} + +} // end anonymous namespace + + +void PDFExport::showErrors( const std::set< vcl::PDFWriter::ErrorCode >& rErrors ) +{ + if( ! rErrors.empty() && mxIH.is() ) + { + task::PDFExportException aExc; + aExc.ErrorCodes = comphelper::containerToSequence<sal_Int32>( rErrors ); + Reference< task::XInteractionRequest > xReq( new PDFErrorRequest( std::move(aExc) ) ); + mxIH->handle( xReq ); + } +} + + +void PDFExport::ImplExportPage( vcl::PDFWriter& rWriter, vcl::PDFExtOutDevData& rPDFExtOutDevData, const GDIMetaFile& rMtf ) +{ + //Rectangle(Point, Size) creates a rectangle off by 1, use Rectangle(long, long, long, long) instead + basegfx::B2DPolygon aSize(tools::Polygon(tools::Rectangle(0, 0, rMtf.GetPrefSize().Width(), rMtf.GetPrefSize().Height())).getB2DPolygon()); + basegfx::B2DPolygon aSizePDF(OutputDevice::LogicToLogic(aSize, rMtf.GetPrefMapMode(), MapMode(MapUnit::MapPoint))); + basegfx::B2DRange aRangePDF(aSizePDF.getB2DRange()); + tools::Rectangle aPageRect( Point(), rMtf.GetPrefSize() ); + + rWriter.NewPage( aRangePDF.getWidth(), aRangePDF.getHeight() ); + rWriter.SetMapMode( rMtf.GetPrefMapMode() ); + + vcl::PDFWriter::PlayMetafileContext aCtx; + GDIMetaFile aMtf; + if( mbRemoveTransparencies ) + { + aCtx.m_bTransparenciesWereRemoved = rWriter.GetReferenceDevice()-> + RemoveTransparenciesFromMetaFile( rMtf, aMtf, mnMaxImageResolution, mnMaxImageResolution, + false, true, mbReduceImageResolution ); + // tdf#134736 if the metafile was replaced then rPDFExtOutDevData's PageSyncData mActions + // all still point to MetaAction indexes in the original metafile that are now invalid. + // Throw them all away in the absence of a way to reposition them to new positions of + // their replacements. + if (aCtx.m_bTransparenciesWereRemoved) + rPDFExtOutDevData.ResetSyncData(); + } + else + { + aMtf = rMtf; + } + aCtx.m_nMaxImageResolution = mbReduceImageResolution ? mnMaxImageResolution : 0; + aCtx.m_bOnlyLosslessCompression = mbUseLosslessCompression; + aCtx.m_nJPEGQuality = mnQuality; + + + rWriter.SetClipRegion( basegfx::B2DPolyPolygon( + basegfx::utils::createPolygonFromRect( vcl::unotools::b2DRectangleFromRectangle(aPageRect) ) ) ); + + rWriter.PlayMetafile( aMtf, aCtx, &rPDFExtOutDevData ); + + rPDFExtOutDevData.ResetSyncData(); + + if (!msWatermark.isEmpty()) + { + ImplWriteWatermark( rWriter, Size(aRangePDF.getWidth(), aRangePDF.getHeight()) ); + } + else if (!msTiledWatermark.isEmpty()) + { + ImplWriteTiledWatermark( rWriter, Size(aRangePDF.getWidth(), aRangePDF.getHeight()) ); + } +} + + +void PDFExport::ImplWriteWatermark( vcl::PDFWriter& rWriter, const Size& rPageSize ) +{ + vcl::Font aFont( maWatermarkFontName, Size( 0, moWatermarkFontHeight ? *moWatermarkFontHeight : 3*rPageSize.Height()/4 ) ); + aFont.SetItalic( ITALIC_NONE ); + aFont.SetWidthType( WIDTH_NORMAL ); + aFont.SetWeight( WEIGHT_NORMAL ); + aFont.SetAlignment( ALIGN_BOTTOM ); + tools::Long nTextWidth = rPageSize.Width(); + if( rPageSize.Width() < rPageSize.Height() ) + { + nTextWidth = rPageSize.Height(); + 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(); + pDev->SetFont( aFont ); + pDev->SetMapMode( MapMode( MapUnit::MapPoint ) ); + int w = 0; + if (moWatermarkFontHeight) + { + w = pDev->GetTextWidth(msWatermark); + } + else + { + while( ( w = pDev->GetTextWidth( msWatermark ) ) > nTextWidth ) + { + if (w == 0) + break; + tools::Long nNewHeight = aFont.GetFontHeight() * nTextWidth / w; + if( nNewHeight == aFont.GetFontHeight() ) + { + nNewHeight--; + if( nNewHeight <= 0 ) + break; + } + aFont.SetFontHeight( nNewHeight ); + pDev->SetFont( aFont ); + } + } + tools::Long nTextHeight = pDev->GetTextHeight(); + // leave some maneuvering room for rounding issues, also + // some fonts go a little outside ascent/descent + nTextHeight += nTextHeight/20; + pDev->Pop(); + + rWriter.Push(); + rWriter.SetMapMode( MapMode( MapUnit::MapPoint ) ); + rWriter.SetFont( aFont ); + rWriter.SetTextColor(maWatermarkColor); + Point aTextPoint; + tools::Rectangle aTextRect; + if( rPageSize.Width() > rPageSize.Height() ) + { + aTextPoint = Point( (rPageSize.Width()-w)/2, + rPageSize.Height()-(rPageSize.Height()-nTextHeight)/2 ); + aTextRect = tools::Rectangle( Point( (rPageSize.Width()-w)/2, + (rPageSize.Height()-nTextHeight)/2 ), + Size( w, nTextHeight ) ); + } + else + { + aTextPoint = Point( (rPageSize.Width()-nTextHeight)/2, + (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 ); + rWriter.EndTransparencyGroup( aTextRect, 50 ); + rWriter.Pop(); +} + +void PDFExport::ImplWriteTiledWatermark( vcl::PDFWriter& rWriter, const Size& rPageSize ) +{ + OUString watermark = msTiledWatermark; + // Maximum number of characters in one line. + // it is set to 21 to make it look like tiled watermarks as online in secure view + const int lineLength = 21; + vcl::Font aFont( "Liberation Sans", Size( 0, 40 ) ); + aFont.SetItalic( ITALIC_NONE ); + aFont.SetWidthType( WIDTH_NORMAL ); + aFont.SetWeight( WEIGHT_NORMAL ); + aFont.SetAlignment( ALIGN_BOTTOM ); + aFont.SetFontHeight(40); + aFont.SetOrientation(450_deg10); + + OutputDevice* pDev = rWriter.GetReferenceDevice(); + pDev->SetFont(aFont); + pDev->Push(); + pDev->SetFont(aFont); + pDev->SetMapMode( MapMode( MapUnit::MapPoint ) ); + int w = 0; + int watermarkcount = ((rPageSize.Width()) / 200)+1; + tools::Long nTextWidth = rPageSize.Width() / (watermarkcount*1.5); + OUString oneLineText = watermark; + + if(watermark.getLength() > lineLength) + oneLineText = watermark.copy(0, lineLength); + + while((w = pDev->GetTextWidth(oneLineText)) > nTextWidth) + { + if(w==0) + break; + + tools::Long nNewHeight = aFont.GetFontHeight() * nTextWidth / w; + aFont.SetFontHeight(nNewHeight); + pDev->SetFont( aFont ); + } + // maximum number of watermark count for the width + if(watermarkcount > 8) + watermarkcount = 8; + + pDev->Pop(); + + rWriter.Push(); + rWriter.SetMapMode( MapMode( MapUnit::MapPoint ) ); + rWriter.SetFont(aFont); + rWriter.SetTextColor( Color(19,20,22) ); + // center watermarks horizontally + Point aTextPoint( (rPageSize.Width()/2) - (((nTextWidth*watermarkcount)+(watermarkcount-1)*nTextWidth)/2), + pDev->GetTextHeight()); + + for( int i = 0; i < watermarkcount; i ++) + { + while(aTextPoint.getY()+pDev->GetTextHeight()*3 <= rPageSize.Height()) + { + tools::Rectangle aTextRect(aTextPoint, Size(nTextWidth*2,pDev->GetTextHeight()*4)); + + pDev->Push(); + rWriter.SetClipRegion(); + rWriter.BeginTransparencyGroup(); + rWriter.SetTextColor( Color(19,20,22) ); + rWriter.DrawText(aTextRect, watermark, DrawTextFlags::MultiLine|DrawTextFlags::Center|DrawTextFlags::VCenter|DrawTextFlags::WordBreak|DrawTextFlags::Bottom); + rWriter.EndTransparencyGroup( aTextRect, 50 ); + pDev->Pop(); + + pDev->Push(); + rWriter.SetClipRegion(); + rWriter.BeginTransparencyGroup(); + rWriter.SetTextColor( Color(236,235,233) ); + rWriter.DrawText(aTextRect, watermark, DrawTextFlags::MultiLine|DrawTextFlags::Center|DrawTextFlags::VCenter|DrawTextFlags::WordBreak|DrawTextFlags::Bottom); + rWriter.EndTransparencyGroup( aTextRect, 50 ); + pDev->Pop(); + + aTextPoint.Move(0, pDev->GetTextHeight()*3); + } + aTextPoint=Point( aTextPoint.getX(), pDev->GetTextHeight() ); + aTextPoint.Move( nTextWidth*1.5, 0 ); + } + + rWriter.Pop(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |