/* -*- 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 <comphelper/diagnose_ex.hxx>
#include <utility>
#include <vcl/canvastools.hxx>
#include <vcl/mapmod.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/graphic/GraphicMetadata.hxx>
#include <rtl/ustring.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 <comphelper/compbase.hxx>
#include <officecfg/Office/Common.hxx>
#include <sfx2/lokhelper.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/XDocumentProperties2.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/sheet/XSheetRange.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>
#include <rtl/bootstrap.hxx>
#include <config_features.h>
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;
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 ),
mbUseLosslessCompression ( false ),
mbReduceImageResolution ( true ),
mbSkipEmptyPages ( true ),
mnMaxImageResolution ( DefaultPDFImageDPI ),
mnQuality ( DefaultPDFJPEGQuality ),
mnProgressValue ( 0 ),
mbRemoveTransparencies ( false ),
mbIsRedactMode ( false ),
maWatermarkColor ( COL_LIGHTGREEN ),
maWatermarkFontName ( u"Helvetica"_ustr )
{
}
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;
std::vector<beans::PropertyValue> aArgs {
comphelper::makePropertyValue(u"FilterName"_ustr, OUString()),
comphelper::makePropertyValue(u"OutputStream"_ustr, xStream),
};
if (m_aPreparedPassword.hasElements())
aArgs.push_back(comphelper::makePropertyValue(u"EncryptionData"_ustr, m_aPreparedPassword));
try
{
xStore->storeToURL(u"private:stream"_ustr, comphelper::containerToSequence(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 <<= u"/org.openoffice.Setup/Office/Factories/"_ustr;
uno::Sequence< uno::Any > aArgs{ uno::Any(aPathProp) };
Reference< container::XNameAccess > xSOFConfig(
xConfigProvider->createInstanceWithArguments(
u"com.sun.star.configuration.ConfigurationAccess"_ustr, aArgs ),
uno::UNO_QUERY );
Reference< container::XNameAccess > xApplConfig;
xSOFConfig->getByName( aDocServiceName ) >>= xApplConfig;
if ( xApplConfig.is() )
{
OUString aFilterName;
xApplConfig->getByName( u"ooSetupFactoryActualFilter"_ustr ) >>= aFilterName;
if( !aFilterName.isEmpty() )
{
// find the related type name
OUString aTypeName;
Reference< container::XNameAccess > xFilterFactory(
xContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.document.FilterFactory"_ustr, xContext),
uno::UNO_QUERY );
Sequence< beans::PropertyValue > aFilterData;
xFilterFactory->getByName( aFilterName ) >>= aFilterData;
for (const beans::PropertyValue& rProp : aFilterData)
if ( rProp.Name == "Type" )
rProp.Value >>= aTypeName;
if ( !aTypeName.isEmpty() )
{
// find the mediatype
Reference< container::XNameAccess > xTypeDetection(
xContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.document.TypeDetection"_ustr, xContext),
UNO_QUERY );
Sequence< beans::PropertyValue > aTypeData;
xTypeDetection->getByName( aTypeName ) >>= aTypeData;
for (const beans::PropertyValue& rProp : 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() )
{
// The defaults
bool bUseTaggedPDF = false;
sal_Int32 nPDFTypeSelection = 0;
bool bPDFUACompliance = false;
bool bExportNotes = true;
bool bExportNotesInMargin = false;
bool bExportNotesPages = false;
bool bExportOnlyNotesPages = false;
bool bUseTransitionEffects = true;
bool bExportFormFields = false;
sal_Int32 nFormsFormat = 0;
bool bAllowDuplicateFieldNames = false;
bool bHideViewerToolbar = false;
bool bHideViewerMenubar = false;
bool bHideViewerWindowControls = false;
bool bFitWindow = false;
bool bCenterWindow = false;
bool bOpenInFullScreenMode = false;
bool bDisplayPDFDocumentTitle = true;
sal_Int32 nPDFDocumentMode = 0;
sal_Int32 nPDFDocumentAction = 0;
sal_Int32 nZoom = 100;
sal_Int32 nInitialPage = 1;
sal_Int32 nPDFPageLayout = 0;
bool bAddStream = false;
bool bEncrypt = false;
bool bRestrictPermissions = false;
sal_Int32 nPrintAllowed = 2;
sal_Int32 nChangesAllowed = 4;
bool bCanCopyOrExtract = true;
bool bCanExtractForAccessibility = true;
// #i56629
bool bExportRelativeFsysLinks = false;
sal_Int32 nDefaultLinkAction = 0;
bool bConvertOOoTargetToPDFTarget = false;
bool bExportBmkToDest = false;
bool bExportBookmarks = true;
bool bExportHiddenSlides = false;
bool bSinglePageSheets = false;
sal_Int32 nOpenBookmarkLevels = -1;
bool bSignPDF = false;
OUString sSignLocation, sSignReason, sSignContact, sSignPassword;
css::uno::Reference<css::security::XCertificate> aSignCertificate;
OUString sSignTSA;
bool bExportPlaceholders = false;
bool bUseReferenceXObject = false;
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;
std::optional<PropertyValue> oMathTitleRow;
std::optional<PropertyValue> oMathFormulaText;
std::optional<PropertyValue> oMathBorder;
std::optional<PropertyValue> oMathPrintFormat;
std::optional<PropertyValue> oMathPrintScale;
// getting the string for the creator
OUString aCreator;
Reference< XServiceInfo > xInfo( mxSrcDoc, UNO_QUERY );
if ( xInfo.is() )
{
if ( xInfo->supportsService( u"com.sun.star.presentation.PresentationDocument"_ustr ) )
aCreator = u"Impress"_ustr;
else if ( xInfo->supportsService( u"com.sun.star.drawing.DrawingDocument"_ustr ) )
aCreator = u"Draw"_ustr;
else if ( xInfo->supportsService( u"com.sun.star.text.TextDocument"_ustr ) )
aCreator = u"Writer"_ustr;
else if ( xInfo->supportsService( u"com.sun.star.sheet.SpreadsheetDocument"_ustr ) )
aCreator = u"Calc"_ustr;
else if ( xInfo->supportsService( u"com.sun.star.formula.FormulaProperties"_ustr ) )
aCreator = u"Math"_ustr;
}
Reference< document::XDocumentPropertiesSupplier > xDocumentPropsSupplier( mxSrcDoc, UNO_QUERY );
if ( xDocumentPropsSupplier.is() )
{
Reference< document::XDocumentProperties2 > xDocumentProps( xDocumentPropsSupplier->getDocumentProperties(), UNO_QUERY );
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());
aContext.DocumentInfo.ModificationDate
= xDocumentProps->getEditingCycles() < 1
? xDocumentProps->getCreationDate()
: xDocumentProps->getModificationDate();
aContext.DocumentInfo.Contributor = xDocumentProps->getContributor();
aContext.DocumentInfo.Coverage = xDocumentProps->getCoverage();
aContext.DocumentInfo.Identifier = xDocumentProps->getIdentifier();
aContext.DocumentInfo.Publisher = xDocumentProps->getPublisher();
aContext.DocumentInfo.Relation = xDocumentProps->getRelation();
aContext.DocumentInfo.Rights = xDocumentProps->getRights();
aContext.DocumentInfo.Source = xDocumentProps->getSource();
aContext.DocumentInfo.Type = xDocumentProps->getType();
}
}
if (!comphelper::IsFuzzing())
{
OUString arch;
auto const ok = rtl::Bootstrap::get(u"_ARCH"_ustr, arch);
assert(ok); (void) ok;
// 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::getAboutBoxProductVersion() +
" (" + arch + ")"
#if HAVE_FEATURE_COMMUNITY_FLAVOR
" / LibreOffice Community"
#endif
;
}
aContext.DocumentInfo.Creator = aCreator;
OUString aSignCertificateSubjectName;
OUString aSignCertificateCertPem;
OUString aSignCertificateKeyPem;
OUString aSignCertificateCaPem;
for ( const beans::PropertyValue& rProp : rFilterData )
{
if ( rProp.Name == "PageRange" )
rProp.Value >>= aPageRange;
else if ( rProp.Name == "SheetRange" )
{
Reference< frame::XController > xController( Reference< frame::XModel >( mxSrcDoc, UNO_QUERY_THROW )->getCurrentController() );
Reference< sheet::XSheetRange > xView( xController, UNO_QUERY);
OUString aSheetRange;
rProp.Value >>= aSheetRange;
aSelection = xView->getSelectionFromString(aSheetRange);
}
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 >>= bUseTaggedPDF;
else if ( rProp.Name == "SelectPdfVersion" )
rProp.Value >>= nPDFTypeSelection;
else if ( rProp.Name == "PDFUACompliance" )
rProp.Value >>= bPDFUACompliance;
else if ( rProp.Name == "ExportNotes" )
rProp.Value >>= bExportNotes;
else if ( rProp.Name == "ExportNotesInMargin" )
rProp.Value >>= bExportNotesInMargin;
else if ( rProp.Name == "ExportNotesPages" )
rProp.Value >>= bExportNotesPages;
else if ( rProp.Name == "ExportOnlyNotesPages" )
rProp.Value >>= bExportOnlyNotesPages;
else if ( rProp.Name == "UseTransitionEffects" )
rProp.Value >>= bUseTransitionEffects;
else if ( rProp.Name == "ExportFormFields" )
rProp.Value >>= bExportFormFields;
else if ( rProp.Name == "FormsType" )
rProp.Value >>= nFormsFormat;
else if ( rProp.Name == "AllowDuplicateFieldNames" )
rProp.Value >>= bAllowDuplicateFieldNames;
// viewer properties
else if ( rProp.Name == "HideViewerToolbar" )
rProp.Value >>= bHideViewerToolbar;
else if ( rProp.Name == "HideViewerMenubar" )
rProp.Value >>= bHideViewerMenubar;
else if ( rProp.Name == "HideViewerWindowControls" )
rProp.Value >>= bHideViewerWindowControls;
else if ( rProp.Name == "ResizeWindowToInitialPage" )
rProp.Value >>= bFitWindow;
else if ( rProp.Name == "CenterWindow" )
rProp.Value >>= bCenterWindow;
else if ( rProp.Name == "OpenInFullScreenMode" )
rProp.Value >>= bOpenInFullScreenMode;
else if ( rProp.Name == "DisplayPDFDocumentTitle" )
rProp.Value >>= bDisplayPDFDocumentTitle;
else if ( rProp.Name == "InitialView" )
rProp.Value >>= nPDFDocumentMode;
else if ( rProp.Name == "Magnification" )
rProp.Value >>= nPDFDocumentAction;
else if ( rProp.Name == "Zoom" )
rProp.Value >>= nZoom;
else if ( rProp.Name == "InitialPage" )
rProp.Value >>= nInitialPage;
else if ( rProp.Name == "PageLayout" )
rProp.Value >>= nPDFPageLayout;
else if ( rProp.Name == "FirstPageOnLeft" )
rProp.Value >>= aContext.FirstPageLeft;
else if ( rProp.Name == "IsAddStream" )
rProp.Value >>= bAddStream;
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 >>= bEncrypt;
else if ( rProp.Name == "DocumentOpenPassword" )
rProp.Value >>= aOpenPassword;
else if ( rProp.Name == "RestrictPermissions" )
rProp.Value >>= bRestrictPermissions;
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 >>= nPrintAllowed;
else if ( rProp.Name == "Changes" )
rProp.Value >>= nChangesAllowed;
else if ( rProp.Name == "EnableCopyingOfContent" )
rProp.Value >>= bCanCopyOrExtract;
else if ( rProp.Name == "EnableTextAccessForAccessibilityTools" )
rProp.Value >>= bCanExtractForAccessibility;
// i56629 links extra (relative links and other related stuff)
else if ( rProp.Name == "ExportLinksRelativeFsys" )
rProp.Value >>= bExportRelativeFsysLinks;
else if ( rProp.Name == "PDFViewSelection" )
rProp.Value >>= nDefaultLinkAction;
else if ( rProp.Name == "ConvertOOoTargetToPDFTarget" )
rProp.Value >>= bConvertOOoTargetToPDFTarget;
else if ( rProp.Name == "ExportBookmarksToPDFDestination" )
rProp.Value >>= bExportBmkToDest;
else if ( rProp.Name == "ExportBookmarks" )
rProp.Value >>= bExportBookmarks;
else if ( rProp.Name == "ExportHiddenSlides" )
rProp.Value >>= bExportHiddenSlides;
else if ( rProp.Name == "SinglePageSheets" )
rProp.Value >>= bSinglePageSheets;
else if ( rProp.Name == "OpenBookmarkLevels" )
rProp.Value >>= nOpenBookmarkLevels;
else if ( rProp.Name == "SignPDF" )
rProp.Value >>= bSignPDF;
else if ( rProp.Name == "SignatureLocation" )
rProp.Value >>= sSignLocation;
else if ( rProp.Name == "SignatureReason" )
rProp.Value >>= sSignReason;
else if ( rProp.Name == "SignatureContactInfo" )
rProp.Value >>= sSignContact;
else if ( rProp.Name == "SignaturePassword" )
rProp.Value >>= sSignPassword;
else if ( rProp.Name == "SignatureCertificate" )
rProp.Value >>= aSignCertificate;
else if (rProp.Name == "SignCertificateSubjectName")
rProp.Value >>= aSignCertificateSubjectName;
else if (rProp.Name == "SignCertificateCertPem")
rProp.Value >>= aSignCertificateCertPem;
else if (rProp.Name == "SignCertificateKeyPem")
rProp.Value >>= aSignCertificateKeyPem;
else if (rProp.Name == "SignCertificateCaPem")
rProp.Value >>= aSignCertificateCaPem;
else if ( rProp.Name == "SignatureTSA" )
rProp.Value >>= sSignTSA;
else if ( rProp.Name == "ExportPlaceholders" )
rProp.Value >>= bExportPlaceholders;
else if ( rProp.Name == "UseReferenceXObject" )
rProp.Value >>= bUseReferenceXObject;
// Redaction & bitmap related stuff
else if ( rProp.Name == "IsRedactMode" )
rProp.Value >>= mbIsRedactMode;
// Math-specific render options
else if (rProp.Name == "TitleRow")
oMathTitleRow = rProp;
else if (rProp.Name == "FormulaText")
oMathFormulaText = rProp;
else if (rProp.Name == "Border")
oMathBorder = rProp;
else if (rProp.Name == "PrintFormat")
oMathPrintFormat = rProp;
else if (rProp.Name == "PrintScale")
oMathPrintScale = rProp;
}
if (!aSignCertificate.is() && !aSignCertificateSubjectName.isEmpty())
{
aSignCertificate = GetCertificateFromSubjectName(aSignCertificateSubjectName);
}
if (!aSignCertificate.is())
{
// Still no signing certificate configured, see if we got a ca/cert/key in PEM
// format:
if (!aSignCertificateCaPem.isEmpty())
{
std::string aSignatureCa(aSignCertificateCaPem.toUtf8());
std::vector<std::string> aCerts = SfxLokHelper::extractCertificates(aSignatureCa);
SfxLokHelper::addCertificates(aCerts);
}
if (!aSignCertificateCertPem.isEmpty() && !aSignCertificateKeyPem.isEmpty())
{
std::string aSignatureCert(aSignCertificateCertPem.toUtf8());
std::string aSignatureKey(aSignCertificateKeyPem.toUtf8());
aSignCertificate = SfxLokHelper::getSigningCertificate(aSignatureCert, aSignatureKey);
}
}
aContext.URL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
// set the correct version, depending on user request
switch( nPDFTypeSelection )
{
default:
case 0:
aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_7;
break;
case 1:
aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_1;
bUseTaggedPDF = true; // force the tagged PDF as well
mbRemoveTransparencies = true; // does not allow transparencies
bEncrypt = false; // no encryption
xEnc.clear();
break;
case 2:
aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_2;
bUseTaggedPDF = true; // force the tagged PDF as well
mbRemoveTransparencies = false; // does allow transparencies
bEncrypt = false; // no encryption
xEnc.clear();
break;
case 3:
aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_3;
bUseTaggedPDF = true; // force the tagged PDF as well
mbRemoveTransparencies = false; // does allow transparencies
bEncrypt = false; // no encryption
xEnc.clear();
break;
case 4:
// TODO - determine what is allowed for PDFA/4
aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_4;
bUseTaggedPDF = true; // force the tagged PDF as well
mbRemoveTransparencies = false; // does allow transparencies
bEncrypt = 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;
case 17:
aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_7;
break;
case 20:
aContext.Version = vcl::PDFWriter::PDFVersion::PDF_2_0;
break;
}
// PDF/UA support
aContext.UniversalAccessibilityCompliance = bPDFUACompliance;
if (bPDFUACompliance)
{
// ISO 14289-1:2014, Clause: 7.1
bUseTaggedPDF = true;
// ISO 14289-1:2014, Clause: 7.16
bCanExtractForAccessibility = true;
// ISO 14289-1:2014, Clause: 7.20
bUseReferenceXObject = false;
}
// copy in context the values default in the constructor or set by the FilterData sequence of properties
aContext.Tagged = bUseTaggedPDF;
// values used in viewer
aContext.HideViewerToolbar = bHideViewerToolbar;
aContext.HideViewerMenubar = bHideViewerMenubar;
aContext.HideViewerWindowControls = bHideViewerWindowControls;
aContext.FitWindow = bFitWindow;
aContext.CenterWindow = bCenterWindow;
aContext.OpenInFullScreenMode = bOpenInFullScreenMode;
aContext.DisplayPDFDocumentTitle = bDisplayPDFDocumentTitle;
aContext.InitialPage = nInitialPage-1;
aContext.OpenBookmarkLevels = nOpenBookmarkLevels;
switch( nPDFDocumentMode )
{
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( nPDFDocumentAction )
{
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 = nZoom;
break;
}
switch( nPDFPageLayout )
{
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( bRestrictPermissions && (xEnc.is() || !aPermissionPassword.isEmpty()) )
{
bEncrypt = true; // permission set as desired, done after
}
else
{
// force permission to default
nPrintAllowed = 2 ;
nChangesAllowed = 4 ;
bCanCopyOrExtract = true;
bCanExtractForAccessibility = true ;
}
switch( nPrintAllowed )
{
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( nChangesAllowed )
{
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 = bCanCopyOrExtract;
aContext.Encryption.CanExtractForAccessibility = bCanExtractForAccessibility;
if( bEncrypt && ! xEnc.is() )
xEnc = vcl::PDFWriter::InitEncryption( aPermissionPassword, aOpenPassword );
if( bEncrypt && !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( nFormsFormat )
{
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 = bAllowDuplicateFieldNames;
// 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 = bExportRelativeFsysLinks;
// determine the default action for PDF links
switch( nDefaultLinkAction )
{
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 = bConvertOOoTargetToPDFTarget;
// 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 = bSignPDF;
aContext.SignLocation = sSignLocation;
aContext.SignContact = sSignContact;
aContext.SignReason = sSignReason;
aContext.SignPassword = sSignPassword;
aContext.SignCertificate = std::move(aSignCertificate);
aContext.SignTSA = sSignTSA;
aContext.UseReferenceXObject = bUseReferenceXObject;
// 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( bAddStream )
{
// export stream
// get mimetype
OUString aSrcMimetype = getMimetypeForDocument( mxContext, mxSrcDoc );
OUString aExt;
if (aSrcMimetype == "application/vnd.oasis.opendocument.text")
aExt = ".odt";
else if (aSrcMimetype == "application/vnd.oasis.opendocument.presentation")
aExt = ".odp";
else if (aSrcMimetype == "application/vnd.oasis.opendocument.spreadsheet")
aExt = ".ods";
else if (aSrcMimetype == "application/vnd.oasis.opendocument.graphics")
aExt = ".odg";
std::unique_ptr<vcl::PDFOutputStream> pStream(new PDFExportStreamDoc(mxSrcDoc, aPreparedPermissionPassword));
aPDFWriter.AddAttachedFile("Original" + aExt, aSrcMimetype, u"Embedded original document of this PDF file"_ustr, std::move(pStream));
}
if ( pOut )
{
DBG_ASSERT( pOut->GetExtOutDevData() == nullptr, "PDFExport: ExtOutDevData already set!!!" );
vcl::PDFExtOutDevData aPDFExtOutDevData( *pOut );
pOut->SetExtOutDevData( &aPDFExtOutDevData );
aPDFExtOutDevData.SetIsExportNotes( bExportNotes );
aPDFExtOutDevData.SetIsExportNotesInMargin( bExportNotesInMargin );
aPDFExtOutDevData.SetIsExportTaggedPDF( bUseTaggedPDF );
aPDFExtOutDevData.SetIsExportTransitionEffects( bUseTransitionEffects );
aPDFExtOutDevData.SetIsExportFormFields( bExportFormFields );
aPDFExtOutDevData.SetIsExportBookmarks( bExportBookmarks );
aPDFExtOutDevData.SetIsExportHiddenSlides( bExportHiddenSlides );
aPDFExtOutDevData.SetIsSinglePageSheets( bSinglePageSheets );
aPDFExtOutDevData.SetIsLosslessCompression( mbUseLosslessCompression );
aPDFExtOutDevData.SetCompressionQuality( mnQuality );
aPDFExtOutDevData.SetIsReduceImageResolution( mbReduceImageResolution );
aPDFExtOutDevData.SetIsExportNamedDestinations( bExportBmkToDest );
std::vector<PropertyValue> aRenderOptionsVector{
comphelper::makePropertyValue(u"RenderDevice"_ustr, uno::Reference<awt::XDevice>(xDevice)),
comphelper::makePropertyValue(u"ExportNotesPages"_ustr, false),
comphelper::makePropertyValue(u"IsFirstPage"_ustr, true),
comphelper::makePropertyValue(u"IsLastPage"_ustr, false),
comphelper::makePropertyValue(u"IsSkipEmptyPages"_ustr, mbSkipEmptyPages),
comphelper::makePropertyValue(u"PageRange"_ustr, aPageRange),
comphelper::makePropertyValue(u"ExportPlaceholders"_ustr, bExportPlaceholders),
comphelper::makePropertyValue(u"SinglePageSheets"_ustr, bSinglePageSheets),
comphelper::makePropertyValue(u"ExportNotesInMargin"_ustr, bExportNotesInMargin)
};
if (oMathTitleRow)
aRenderOptionsVector.push_back(*oMathTitleRow);
if (oMathFormulaText)
aRenderOptionsVector.push_back(*oMathFormulaText);
if (oMathBorder)
aRenderOptionsVector.push_back(*oMathBorder);
if (oMathPrintFormat)
aRenderOptionsVector.push_back(*oMathPrintFormat);
if (oMathPrintScale)
aRenderOptionsVector.push_back(*oMathPrintScale);
Sequence aRenderOptions = comphelper::containerToSequence(aRenderOptionsVector);
Any& rExportNotesValue = aRenderOptions.getArray()[ 1 ].Value;
if( !aPageRange.isEmpty() || !aSelection.hasValue() )
{
aSelection = Any();
aSelection <<= mxSrcDoc;
}
bool bReChangeToNormalView = false;
static constexpr OUString sShowOnlineLayout( u"ShowOnlineLayout"_ustr );
bool bReHideWhitespace = false;
static constexpr OUString sHideWhitespace(u"HideWhitespace"_ustr);
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 ( bExportNotesPages && aCreator == "Impress" )
{
uno::Reference< drawing::XShapes > xShapes; // do not allow to export notes when exporting a selection
if ( aSelection >>= xShapes )
bExportNotesPages = false;
}
else
bExportNotesPages = false;
const bool bExportPages = !bExportNotesPages || !bExportOnlyNotesPages;
if( aPageRange.isEmpty() || bSinglePageSheets)
{
aPageRange = OUString::number( 1 ) + "-" + OUString::number(nPageCount );
}
StringRangeEnumerator aRangeEnum( aPageRange, 0, nPageCount-1 );
if ( mxStatusIndicator.is() )
{
sal_Int32 nTotalPageCount = aRangeEnum.size();
if ( bExportPages && bExportNotesPages )
nTotalPageCount *= 2;
mxStatusIndicator->start(FilterResId(PDF_PROGRESS_BAR), 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 comphelper::WeakComponentImplHelper< task::XInteractionRequest > PDFErrorRequestBase;
class PDFErrorRequest : 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 ) :
maExc(std::move( aExc ))
{
}
uno::Any SAL_CALL PDFErrorRequest::getRequest()
{
std::unique_lock 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(&rWriter);
}
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(nullptr);
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();
// tdf#152235 tag around the reference to the XObject on the page
sal_Int32 const id = rWriter.EnsureStructureElement();
rWriter.InitStructureElement(id, vcl::PDFWriter::NonStructElement, ::std::u16string_view());
rWriter.BeginStructureElement(id);
rWriter.SetStructureAttribute(vcl::PDFWriter::Type, vcl::PDFWriter::Pagination);
rWriter.SetStructureAttribute(vcl::PDFWriter::Subtype, vcl::PDFWriter::Watermark);
// HACK: this should produce *nothing* itself but is necessary to output
// the Artifact tag here, not inside the XObject
rWriter.DrawPolyLine({});
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.EndStructureElement();
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( u"Liberation Sans"_ustr, 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();
// tdf#152235 tag around the reference to the XObject on the page
sal_Int32 const id = rWriter.EnsureStructureElement();
rWriter.InitStructureElement(id, vcl::PDFWriter::NonStructElement, ::std::u16string_view());
rWriter.BeginStructureElement(id);
rWriter.SetStructureAttribute(vcl::PDFWriter::Type, vcl::PDFWriter::Pagination);
rWriter.SetStructureAttribute(vcl::PDFWriter::Subtype, vcl::PDFWriter::Watermark);
// HACK: this should produce *nothing* itself but is necessary to output
// the Artifact tag here, not inside the XObject
rWriter.DrawPolyLine({});
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.EndStructureElement();
rWriter.Pop();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V547 Expression is always true.
↑ V547 Expression 'bPDFUACompliance' is always false.
↑ V547 Expression 'bAddStream' is always false.
↑ V547 Expression 'bReChangeToNormalView' is always false.
↑ V547 Expression 'bReHideWhitespace' is always false.
↑ V547 Expression 'bReChangeToNormalView' is always false.
↑ V547 Expression 'bReHideWhitespace' is always false.
↑ V560 A part of conditional expression is always false.
↑ V560 A part of conditional expression is always false.
↑ V560 A part of conditional expression is always true: !bExportNotesPages.
↑ V560 A part of conditional expression is always true: !bExportOnlyNotesPages.
↑ V785 Constant expression in switch statement.
↑ V785 Constant expression in switch statement.
↑ V785 Constant expression in switch statement.
↑ V785 Constant expression in switch statement.
↑ V785 Constant expression in switch statement.
↑ V785 Constant expression in switch statement.
↑ V785 Constant expression in switch statement.
↑ V785 Constant expression in switch statement.
↑ V1037 Two or more case-branches perform the same actions. Check lines: 754, 792
↑ V1048 The 'nPrintAllowed' variable was assigned the same value.
↑ V1048 The 'nChangesAllowed' variable was assigned the same value.
↑ V1048 The 'bCanCopyOrExtract' variable was assigned the same value.
↑ V1048 The 'bCanExtractForAccessibility' variable was assigned the same value.
↑ V1048 The 'aContext.Encryption.CanCopyOrExtract' variable was assigned the same value.