android: Update app-specific/MIME type icons
[LibreOffice.git] / filter / source / pdf / pdfexport.cxx
blobad269b1bf4c451fc8406f405b89a2e5043cfff3a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <osl/file.hxx>
22 #include <tools/debug.hxx>
23 #include <tools/urlobj.hxx>
24 #include <tools/poly.hxx>
25 #include <comphelper/diagnose_ex.hxx>
26 #include <utility>
27 #include <vcl/canvastools.hxx>
28 #include <vcl/mapmod.hxx>
29 #include <vcl/gdimtf.hxx>
30 #include <rtl/ustring.hxx>
31 #include <comphelper/propertyvalue.hxx>
32 #include <comphelper/sequence.hxx>
33 #include <comphelper/string.hxx>
34 #include <comphelper/storagehelper.hxx>
35 #include <basegfx/polygon/b2dpolygon.hxx>
36 #include <basegfx/polygon/b2dpolypolygon.hxx>
37 #include <basegfx/polygon/b2dpolygontools.hxx>
38 #include <toolkit/awt/vclxdevice.hxx>
39 #include <unotools/configmgr.hxx>
40 #include <comphelper/compbase.hxx>
41 #include <officecfg/Office/Common.hxx>
43 #include "pdfexport.hxx"
44 #include <strings.hrc>
46 #include <com/sun/star/beans/XPropertySet.hpp>
47 #include <com/sun/star/configuration/theDefaultProvider.hpp>
48 #include <com/sun/star/awt/XDevice.hpp>
49 #include <com/sun/star/frame/XModel.hpp>
50 #include <com/sun/star/frame/ModuleManager.hpp>
51 #include <com/sun/star/frame/XStorable.hpp>
52 #include <com/sun/star/document/XDocumentProperties.hpp>
53 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
54 #include <com/sun/star/container/XNameAccess.hpp>
55 #include <com/sun/star/view/XViewSettingsSupplier.hpp>
56 #include <com/sun/star/task/XInteractionRequest.hpp>
57 #include <com/sun/star/task/PDFExportException.hpp>
58 #include <com/sun/star/io/IOException.hpp>
59 #include <com/sun/star/io/XOutputStream.hpp>
60 #include <com/sun/star/lang/XServiceInfo.hpp>
61 #include <com/sun/star/drawing/XShapes.hpp>
62 #include <com/sun/star/security/XCertificate.hpp>
63 #include <com/sun/star/beans/XMaterialHolder.hpp>
64 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
66 #include <memory>
68 using namespace ::com::sun::star;
69 using namespace ::com::sun::star::io;
70 using namespace ::com::sun::star::uno;
71 using namespace ::com::sun::star::lang;
72 using namespace ::com::sun::star::beans;
73 using namespace ::com::sun::star::view;
74 using namespace ::com::sun::star::graphic;
77 PDFExport::PDFExport( const Reference< XComponent >& rxSrcDoc,
78 const Reference< task::XStatusIndicator >& rxStatusIndicator,
79 const Reference< task::XInteractionHandler >& rxIH,
80 const Reference< XComponentContext >& xContext ) :
81 mxSrcDoc ( rxSrcDoc ),
82 mxContext ( xContext ),
83 mxStatusIndicator ( rxStatusIndicator ),
84 mxIH ( rxIH ),
85 mbUseTaggedPDF ( false ),
86 mnPDFTypeSelection ( 0 ),
87 mbPDFUACompliance ( false),
88 mbExportNotes ( true ),
89 mbExportNotesInMargin ( false ),
90 mbExportPlaceholders ( false ),
91 mbUseReferenceXObject ( false ),
92 mbExportNotesPages ( false ),
93 mbExportOnlyNotesPages ( false ),
94 mbUseTransitionEffects ( true ),
95 mbExportBookmarks ( true ),
96 mbExportHiddenSlides ( false ),
97 mbSinglePageSheets ( false ),
98 mnOpenBookmarkLevels ( -1 ),
99 mbUseLosslessCompression ( false ),
100 mbReduceImageResolution ( true ),
101 mbSkipEmptyPages ( true ),
102 mbAddStream ( false ),
103 mnMaxImageResolution ( 300 ),
104 mnQuality ( 80 ),
105 mnFormsFormat ( 0 ),
106 mbExportFormFields ( true ),
107 mbAllowDuplicateFieldNames ( false ),
108 mnProgressValue ( 0 ),
109 mbRemoveTransparencies ( false ),
111 mbIsRedactMode ( false ),
112 maWatermarkColor ( COL_LIGHTGREEN ),
113 maWatermarkFontName ( "Helvetica" ),
115 mbHideViewerToolbar ( false ),
116 mbHideViewerMenubar ( false ),
117 mbHideViewerWindowControls ( false ),
118 mbFitWindow ( false ),
119 mbCenterWindow ( false ),
120 mbOpenInFullScreenMode ( false ),
121 mbDisplayPDFDocumentTitle ( true ),
122 mnPDFDocumentMode ( 0 ),
123 mnPDFDocumentAction ( 0 ),
124 mnZoom ( 100 ),
125 mnInitialPage ( 1 ),
126 mnPDFPageLayout ( 0 ),
128 mbEncrypt ( false ),
129 mbRestrictPermissions ( false ),
130 mnPrintAllowed ( 2 ),
131 mnChangesAllowed ( 4 ),
132 mbCanCopyOrExtract ( true ),
133 mbCanExtractForAccessibility( true ),
135 // #i56629
136 mbExportRelativeFsysLinks ( false ),
137 mnDefaultLinkAction ( 0 ),
138 mbConvertOOoTargetToPDFTarget( false ),
139 mbExportBmkToDest ( false ),
140 mbSignPDF ( false )
145 PDFExport::~PDFExport()
150 bool PDFExport::ExportSelection( vcl::PDFWriter& rPDFWriter,
151 Reference< css::view::XRenderable > const & rRenderable,
152 const Any& rSelection,
153 const StringRangeEnumerator& rRangeEnum,
154 Sequence< PropertyValue >& rRenderOptions,
155 sal_Int32 nPageCount )
157 bool bRet = false;
160 Any* pFirstPage = nullptr;
161 Any* pLastPage = nullptr;
163 bool bExportNotesPages = false;
165 auto rRenderOptionsRange = asNonConstRange(rRenderOptions);
166 for( sal_Int32 nData = 0, nDataCount = rRenderOptions.getLength(); nData < nDataCount; ++nData )
168 if ( rRenderOptions[ nData ].Name == "IsFirstPage" )
169 pFirstPage = &rRenderOptionsRange[ nData ].Value;
170 else if ( rRenderOptions[ nData ].Name == "IsLastPage" )
171 pLastPage = &rRenderOptionsRange[ nData ].Value;
172 else if ( rRenderOptions[ nData ].Name == "ExportNotesPages" )
173 rRenderOptionsRange[ nData ].Value >>= bExportNotesPages;
176 OutputDevice* pOut = rPDFWriter.GetReferenceDevice();
178 if( pOut )
180 if ( nPageCount )
182 vcl::PDFExtOutDevData& rPDFExtOutDevData = dynamic_cast<vcl::PDFExtOutDevData&>(*pOut->GetExtOutDevData());
183 rPDFExtOutDevData.SetIsExportNotesPages( bExportNotesPages );
185 sal_Int32 nCurrentPage(0);
186 StringRangeEnumerator::Iterator aIter = rRangeEnum.begin();
187 StringRangeEnumerator::Iterator aEnd = rRangeEnum.end();
188 while ( aIter != aEnd )
190 const Sequence< PropertyValue > aRenderer( rRenderable->getRenderer( *aIter, rSelection, rRenderOptions ) );
191 awt::Size aPageSize;
193 for( const PropertyValue& rProp : aRenderer )
195 if ( rProp.Name == "PageSize" )
197 rProp.Value >>= aPageSize;
198 break;
202 rPDFExtOutDevData.SetCurrentPageNumber( nCurrentPage );
204 GDIMetaFile aMtf;
205 const MapMode aMapMode( MapUnit::Map100thMM );
206 const Size aMtfSize( aPageSize.Width, aPageSize.Height );
208 pOut->Push();
209 pOut->EnableOutput( false );
210 pOut->SetMapMode( aMapMode );
212 aMtf.SetPrefSize( aMtfSize );
213 aMtf.SetPrefMapMode( aMapMode );
214 aMtf.Record( pOut );
216 // #i35176#
217 // IsLastPage property.
218 const sal_Int32 nCurrentRenderer = *aIter;
219 ++aIter;
220 if ( pLastPage && aIter == aEnd )
221 *pLastPage <<= true;
223 rRenderable->render( nCurrentRenderer, rSelection, rRenderOptions );
225 aMtf.Stop();
226 aMtf.WindStart();
228 bool bEmptyPage = false;
229 if( aMtf.GetActionSize() &&
230 ( !mbSkipEmptyPages || aPageSize.Width || aPageSize.Height ) )
232 // We convert the whole metafile into a bitmap to get rid of the
233 // text covered by redaction shapes
234 if (mbIsRedactMode)
238 Graphic aGraph(aMtf);
239 // use antialiasing to improve how graphic objects look
240 BitmapEx bmp = aGraph.GetBitmapEx(GraphicConversionParameters(Size(0, 0), false, true, false));
241 Graphic bgraph(bmp);
242 aMtf = bgraph.GetGDIMetaFile();
244 catch(const Exception&)
246 TOOLS_WARN_EXCEPTION("filter.pdf", "Something went wrong while converting metafile to bitmap");
250 ImplExportPage(rPDFWriter, rPDFExtOutDevData, aMtf);
251 bRet = true;
253 else
255 bEmptyPage = true;
258 pOut->Pop();
260 if ( mxStatusIndicator.is() )
261 mxStatusIndicator->setValue( mnProgressValue );
262 if ( pFirstPage )
263 *pFirstPage <<= false;
265 ++mnProgressValue;
266 if (!bEmptyPage)
268 // Calculate the page number in the PDF output, which may be smaller than the page number in
269 // case of hidden slides or a partial export.
270 ++nCurrentPage;
274 else
276 bRet = true; // #i18334# nPageCount == 0,
277 rPDFWriter.NewPage( 10000, 10000 ); // creating dummy page
278 rPDFWriter.SetMapMode(MapMode(MapUnit::Map100thMM));
282 catch(const RuntimeException &)
285 return bRet;
288 namespace {
290 class PDFExportStreamDoc : public vcl::PDFOutputStream
292 private:
294 Reference< XComponent > m_xSrcDoc;
295 Sequence< beans::NamedValue > m_aPreparedPassword;
297 public:
299 PDFExportStreamDoc( const Reference< XComponent >& xDoc, const Sequence<beans::NamedValue>& rPwd )
300 : m_xSrcDoc( xDoc ),
301 m_aPreparedPassword( rPwd )
304 virtual void write( const Reference< XOutputStream >& xStream ) override;
309 void PDFExportStreamDoc::write( const Reference< XOutputStream >& xStream )
311 Reference< css::frame::XStorable > xStore( m_xSrcDoc, UNO_QUERY );
312 if( !xStore.is() )
313 return;
315 std::vector<beans::PropertyValue> aArgs {
316 comphelper::makePropertyValue("FilterName", OUString()),
317 comphelper::makePropertyValue("OutputStream", xStream),
319 if (m_aPreparedPassword.hasElements())
320 aArgs.push_back(comphelper::makePropertyValue("EncryptionData", m_aPreparedPassword));
324 xStore->storeToURL("private:stream", comphelper::containerToSequence(aArgs));
326 catch( const IOException& )
332 static OUString getMimetypeForDocument( const Reference< XComponentContext >& xContext,
333 const Reference< XComponent >& xDoc ) noexcept
335 OUString aDocMimetype;
338 // get document service name
339 Reference< css::frame::XStorable > xStore( xDoc, UNO_QUERY );
340 Reference< frame::XModuleManager2 > xModuleManager = frame::ModuleManager::create(xContext);
341 if( xStore.is() )
343 OUString aDocServiceName = xModuleManager->identify( Reference< XInterface >( xStore, uno::UNO_QUERY ) );
344 if ( !aDocServiceName.isEmpty() )
346 // get the actual filter name
347 Reference< lang::XMultiServiceFactory > xConfigProvider =
348 configuration::theDefaultProvider::get( xContext );
349 beans::NamedValue aPathProp;
350 aPathProp.Name = "nodepath";
351 aPathProp.Value <<= OUString( "/org.openoffice.Setup/Office/Factories/" );
352 uno::Sequence< uno::Any > aArgs{ uno::Any(aPathProp) };
354 Reference< container::XNameAccess > xSOFConfig(
355 xConfigProvider->createInstanceWithArguments(
356 "com.sun.star.configuration.ConfigurationAccess", aArgs ),
357 uno::UNO_QUERY );
359 Reference< container::XNameAccess > xApplConfig;
360 xSOFConfig->getByName( aDocServiceName ) >>= xApplConfig;
361 if ( xApplConfig.is() )
363 OUString aFilterName;
364 xApplConfig->getByName( "ooSetupFactoryActualFilter" ) >>= aFilterName;
365 if( !aFilterName.isEmpty() )
367 // find the related type name
368 OUString aTypeName;
369 Reference< container::XNameAccess > xFilterFactory(
370 xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", xContext),
371 uno::UNO_QUERY );
373 Sequence< beans::PropertyValue > aFilterData;
374 xFilterFactory->getByName( aFilterName ) >>= aFilterData;
375 for ( const beans::PropertyValue& rProp : std::as_const(aFilterData) )
376 if ( rProp.Name == "Type" )
377 rProp.Value >>= aTypeName;
379 if ( !aTypeName.isEmpty() )
381 // find the mediatype
382 Reference< container::XNameAccess > xTypeDetection(
383 xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", xContext),
384 UNO_QUERY );
386 Sequence< beans::PropertyValue > aTypeData;
387 xTypeDetection->getByName( aTypeName ) >>= aTypeData;
388 for ( const beans::PropertyValue& rProp : std::as_const(aTypeData) )
389 if ( rProp.Name == "MediaType" )
390 rProp.Value >>= aDocMimetype;
397 catch (...)
400 return aDocMimetype;
403 uno::Reference<security::XCertificate>
404 PDFExport::GetCertificateFromSubjectName(const std::u16string_view& rSubjectName) const
406 uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
407 = xml::crypto::SEInitializer::create(mxContext);
408 uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
409 = xSEInitializer->createSecurityContext(OUString());
410 uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment
411 = xSecurityContext->getSecurityEnvironment();
412 for (const auto& xCertificate : xSecurityEnvironment->getPersonalCertificates())
414 if (xCertificate->getSubjectName() == rSubjectName)
416 return xCertificate;
420 return {};
423 bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& rFilterData )
425 INetURLObject aURL( rFile );
426 bool bRet = false;
428 std::set< vcl::PDFWriter::ErrorCode > aErrors;
430 if( aURL.GetProtocol() != INetProtocol::File )
432 OUString aTmp;
434 if( osl::FileBase::getFileURLFromSystemPath( rFile, aTmp ) == osl::FileBase::E_None )
435 aURL = INetURLObject(aTmp);
438 if( aURL.GetProtocol() == INetProtocol::File )
440 Reference< XRenderable > xRenderable( mxSrcDoc, UNO_QUERY );
442 if( xRenderable.is() )
444 rtl::Reference<VCLXDevice> xDevice(new VCLXDevice);
445 OUString aPageRange;
446 Any aSelection;
447 vcl::PDFWriter::PDFWriterContext aContext;
448 OUString aOpenPassword, aPermissionPassword;
449 Reference< beans::XMaterialHolder > xEnc;
450 Sequence< beans::NamedValue > aPreparedPermissionPassword;
453 // getting the string for the creator
454 OUString aCreator;
455 Reference< XServiceInfo > xInfo( mxSrcDoc, UNO_QUERY );
456 if ( xInfo.is() )
458 if ( xInfo->supportsService( "com.sun.star.presentation.PresentationDocument" ) )
459 aCreator += "Impress";
460 else if ( xInfo->supportsService( "com.sun.star.drawing.DrawingDocument" ) )
461 aCreator += "Draw";
462 else if ( xInfo->supportsService( "com.sun.star.text.TextDocument" ) )
463 aCreator += "Writer";
464 else if ( xInfo->supportsService( "com.sun.star.sheet.SpreadsheetDocument" ) )
465 aCreator += "Calc";
466 else if ( xInfo->supportsService( "com.sun.star.formula.FormulaProperties" ) )
467 aCreator += "Math";
470 Reference< document::XDocumentPropertiesSupplier > xDocumentPropsSupplier( mxSrcDoc, UNO_QUERY );
471 if ( xDocumentPropsSupplier.is() )
473 Reference< document::XDocumentProperties > xDocumentProps( xDocumentPropsSupplier->getDocumentProperties() );
474 if ( xDocumentProps.is() )
476 aContext.DocumentInfo.Title = xDocumentProps->getTitle();
477 aContext.DocumentInfo.Author = xDocumentProps->getAuthor();
478 aContext.DocumentInfo.Subject = xDocumentProps->getSubject();
479 aContext.DocumentInfo.Keywords = ::comphelper::string::convertCommaSeparated(xDocumentProps->getKeywords());
483 if (!utl::ConfigManager::IsFuzzing())
485 // getting the string for the producer
486 OUString aProducerOverride = officecfg::Office::Common::Save::Document::GeneratorOverride::get();
487 if (!aProducerOverride.isEmpty())
488 aContext.DocumentInfo.Producer = aProducerOverride;
489 else
490 aContext.DocumentInfo.Producer =
491 utl::ConfigManager::getProductName() +
492 " " +
493 utl::ConfigManager::getProductVersion();
496 aContext.DocumentInfo.Creator = aCreator;
498 OUString aSignCertificateSubjectName;
499 for ( const beans::PropertyValue& rProp : rFilterData )
501 if ( rProp.Name == "PageRange" )
502 rProp.Value >>= aPageRange;
503 else if ( rProp.Name == "Selection" )
504 aSelection = rProp.Value;
505 else if ( rProp.Name == "UseLosslessCompression" )
506 rProp.Value >>= mbUseLosslessCompression;
507 else if ( rProp.Name == "Quality" )
508 rProp.Value >>= mnQuality;
509 else if ( rProp.Name == "ReduceImageResolution" )
510 rProp.Value >>= mbReduceImageResolution;
511 else if ( rProp.Name == "IsSkipEmptyPages" )
512 rProp.Value >>= mbSkipEmptyPages;
513 else if ( rProp.Name == "MaxImageResolution" )
514 rProp.Value >>= mnMaxImageResolution;
515 else if ( rProp.Name == "UseTaggedPDF" )
516 rProp.Value >>= mbUseTaggedPDF;
517 else if ( rProp.Name == "SelectPdfVersion" )
518 rProp.Value >>= mnPDFTypeSelection;
519 else if ( rProp.Name == "PDFUACompliance" )
520 rProp.Value >>= mbPDFUACompliance;
521 else if ( rProp.Name == "ExportNotes" )
522 rProp.Value >>= mbExportNotes;
523 else if ( rProp.Name == "ExportNotesInMargin" )
524 rProp.Value >>= mbExportNotesInMargin;
525 else if ( rProp.Name == "ExportNotesPages" )
526 rProp.Value >>= mbExportNotesPages;
527 else if ( rProp.Name == "ExportOnlyNotesPages" )
528 rProp.Value >>= mbExportOnlyNotesPages;
529 else if ( rProp.Name == "UseTransitionEffects" )
530 rProp.Value >>= mbUseTransitionEffects;
531 else if ( rProp.Name == "ExportFormFields" )
532 rProp.Value >>= mbExportFormFields;
533 else if ( rProp.Name == "FormsType" )
534 rProp.Value >>= mnFormsFormat;
535 else if ( rProp.Name == "AllowDuplicateFieldNames" )
536 rProp.Value >>= mbAllowDuplicateFieldNames;
537 // viewer properties
538 else if ( rProp.Name == "HideViewerToolbar" )
539 rProp.Value >>= mbHideViewerToolbar;
540 else if ( rProp.Name == "HideViewerMenubar" )
541 rProp.Value >>= mbHideViewerMenubar;
542 else if ( rProp.Name == "HideViewerWindowControls" )
543 rProp.Value >>= mbHideViewerWindowControls;
544 else if ( rProp.Name == "ResizeWindowToInitialPage" )
545 rProp.Value >>= mbFitWindow;
546 else if ( rProp.Name == "CenterWindow" )
547 rProp.Value >>= mbCenterWindow;
548 else if ( rProp.Name == "OpenInFullScreenMode" )
549 rProp.Value >>= mbOpenInFullScreenMode;
550 else if ( rProp.Name == "DisplayPDFDocumentTitle" )
551 rProp.Value >>= mbDisplayPDFDocumentTitle;
552 else if ( rProp.Name == "InitialView" )
553 rProp.Value >>= mnPDFDocumentMode;
554 else if ( rProp.Name == "Magnification" )
555 rProp.Value >>= mnPDFDocumentAction;
556 else if ( rProp.Name == "Zoom" )
557 rProp.Value >>= mnZoom;
558 else if ( rProp.Name == "InitialPage" )
559 rProp.Value >>= mnInitialPage;
560 else if ( rProp.Name == "PageLayout" )
561 rProp.Value >>= mnPDFPageLayout;
562 else if ( rProp.Name == "FirstPageOnLeft" )
563 rProp.Value >>= aContext.FirstPageLeft;
564 else if ( rProp.Name == "IsAddStream" )
565 rProp.Value >>= mbAddStream;
566 else if ( rProp.Name == "Watermark" )
567 rProp.Value >>= msWatermark;
568 else if ( rProp.Name == "WatermarkColor" )
570 sal_Int32 nColor{};
571 if (rProp.Value >>= nColor)
573 maWatermarkColor = Color(ColorTransparency, nColor);
576 else if (rProp.Name == "WatermarkFontHeight")
578 sal_Int32 nFontHeight{};
579 if (rProp.Value >>= nFontHeight)
581 moWatermarkFontHeight = nFontHeight;
584 else if (rProp.Name == "WatermarkRotateAngle")
586 sal_Int32 nRotateAngle{};
587 if (rProp.Value >>= nRotateAngle)
589 moWatermarkRotateAngle = Degree10(nRotateAngle);
592 else if (rProp.Name == "WatermarkFontName")
594 OUString aFontName{};
595 if (rProp.Value >>= aFontName)
597 maWatermarkFontName = aFontName;
600 else if ( rProp.Name == "TiledWatermark" )
601 rProp.Value >>= msTiledWatermark;
602 // now all the security related properties...
603 else if ( rProp.Name == "EncryptFile" )
604 rProp.Value >>= mbEncrypt;
605 else if ( rProp.Name == "DocumentOpenPassword" )
606 rProp.Value >>= aOpenPassword;
607 else if ( rProp.Name == "RestrictPermissions" )
608 rProp.Value >>= mbRestrictPermissions;
609 else if ( rProp.Name == "PermissionPassword" )
610 rProp.Value >>= aPermissionPassword;
611 else if ( rProp.Name == "PreparedPasswords" )
612 rProp.Value >>= xEnc;
613 else if ( rProp.Name == "PreparedPermissionPassword" )
614 rProp.Value >>= aPreparedPermissionPassword;
615 else if ( rProp.Name == "Printing" )
616 rProp.Value >>= mnPrintAllowed;
617 else if ( rProp.Name == "Changes" )
618 rProp.Value >>= mnChangesAllowed;
619 else if ( rProp.Name == "EnableCopyingOfContent" )
620 rProp.Value >>= mbCanCopyOrExtract;
621 else if ( rProp.Name == "EnableTextAccessForAccessibilityTools" )
622 rProp.Value >>= mbCanExtractForAccessibility;
623 // i56629 links extra (relative links and other related stuff)
624 else if ( rProp.Name == "ExportLinksRelativeFsys" )
625 rProp.Value >>= mbExportRelativeFsysLinks;
626 else if ( rProp.Name == "PDFViewSelection" )
627 rProp.Value >>= mnDefaultLinkAction;
628 else if ( rProp.Name == "ConvertOOoTargetToPDFTarget" )
629 rProp.Value >>= mbConvertOOoTargetToPDFTarget;
630 else if ( rProp.Name == "ExportBookmarksToPDFDestination" )
631 rProp.Value >>= mbExportBmkToDest;
632 else if ( rProp.Name == "ExportBookmarks" )
633 rProp.Value >>= mbExportBookmarks;
634 else if ( rProp.Name == "ExportHiddenSlides" )
635 rProp.Value >>= mbExportHiddenSlides;
636 else if ( rProp.Name == "SinglePageSheets" )
637 rProp.Value >>= mbSinglePageSheets;
638 else if ( rProp.Name == "OpenBookmarkLevels" )
639 rProp.Value >>= mnOpenBookmarkLevels;
640 else if ( rProp.Name == "SignPDF" )
641 rProp.Value >>= mbSignPDF;
642 else if ( rProp.Name == "SignatureLocation" )
643 rProp.Value >>= msSignLocation;
644 else if ( rProp.Name == "SignatureReason" )
645 rProp.Value >>= msSignReason;
646 else if ( rProp.Name == "SignatureContactInfo" )
647 rProp.Value >>= msSignContact;
648 else if ( rProp.Name == "SignaturePassword" )
649 rProp.Value >>= msSignPassword;
650 else if ( rProp.Name == "SignatureCertificate" )
651 rProp.Value >>= maSignCertificate;
652 else if (rProp.Name == "SignCertificateSubjectName")
653 rProp.Value >>= aSignCertificateSubjectName;
654 else if ( rProp.Name == "SignatureTSA" )
655 rProp.Value >>= msSignTSA;
656 else if ( rProp.Name == "ExportPlaceholders" )
657 rProp.Value >>= mbExportPlaceholders;
658 else if ( rProp.Name == "UseReferenceXObject" )
659 rProp.Value >>= mbUseReferenceXObject;
660 // Redaction & bitmap related stuff
661 else if ( rProp.Name == "IsRedactMode" )
662 rProp.Value >>= mbIsRedactMode;
665 if (!maSignCertificate.is() && !aSignCertificateSubjectName.isEmpty())
667 maSignCertificate = GetCertificateFromSubjectName(aSignCertificateSubjectName);
670 aContext.URL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
672 // set the correct version, depending on user request
673 switch( mnPDFTypeSelection )
675 default:
676 case 0:
677 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_7;
678 break;
679 case 1:
680 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_1;
681 mbUseTaggedPDF = true; // force the tagged PDF as well
682 mbRemoveTransparencies = true; // does not allow transparencies
683 mbEncrypt = false; // no encryption
684 xEnc.clear();
685 break;
686 case 2:
687 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_2;
688 mbUseTaggedPDF = true; // force the tagged PDF as well
689 mbRemoveTransparencies = false; // does allow transparencies
690 mbEncrypt = false; // no encryption
691 xEnc.clear();
692 break;
693 case 3:
694 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_3;
695 mbUseTaggedPDF = true; // force the tagged PDF as well
696 mbRemoveTransparencies = false; // does allow transparencies
697 mbEncrypt = false; // no encryption
698 xEnc.clear();
699 break;
700 case 15:
701 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_5;
702 break;
703 case 16:
704 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_6;
705 break;
706 case 17:
707 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_7;
708 break;
711 // PDF/UA support
712 aContext.UniversalAccessibilityCompliance = mbPDFUACompliance;
713 if (mbPDFUACompliance)
715 // ISO 14289-1:2014, Clause: 7.1
716 mbUseTaggedPDF = true;
717 // ISO 14289-1:2014, Clause: 7.16
718 mbCanExtractForAccessibility = true;
719 // ISO 14289-1:2014, Clause: 7.20
720 mbUseReferenceXObject = false;
723 // copy in context the values default in the constructor or set by the FilterData sequence of properties
724 aContext.Tagged = mbUseTaggedPDF;
726 // values used in viewer
727 aContext.HideViewerToolbar = mbHideViewerToolbar;
728 aContext.HideViewerMenubar = mbHideViewerMenubar;
729 aContext.HideViewerWindowControls = mbHideViewerWindowControls;
730 aContext.FitWindow = mbFitWindow;
731 aContext.CenterWindow = mbCenterWindow;
732 aContext.OpenInFullScreenMode = mbOpenInFullScreenMode;
733 aContext.DisplayPDFDocumentTitle = mbDisplayPDFDocumentTitle;
734 aContext.InitialPage = mnInitialPage-1;
735 aContext.OpenBookmarkLevels = mnOpenBookmarkLevels;
737 switch( mnPDFDocumentMode )
739 default:
740 case 0:
741 aContext.PDFDocumentMode = vcl::PDFWriter::ModeDefault;
742 break;
743 case 1:
744 aContext.PDFDocumentMode = vcl::PDFWriter::UseOutlines;
745 break;
746 case 2:
747 aContext.PDFDocumentMode = vcl::PDFWriter::UseThumbs;
748 break;
750 switch( mnPDFDocumentAction )
752 default:
753 case 0:
754 aContext.PDFDocumentAction = vcl::PDFWriter::ActionDefault;
755 break;
756 case 1:
757 aContext.PDFDocumentAction = vcl::PDFWriter::FitInWindow;
758 break;
759 case 2:
760 aContext.PDFDocumentAction = vcl::PDFWriter::FitWidth;
761 break;
762 case 3:
763 aContext.PDFDocumentAction = vcl::PDFWriter::FitVisible;
764 break;
765 case 4:
766 aContext.PDFDocumentAction = vcl::PDFWriter::ActionZoom;
767 aContext.Zoom = mnZoom;
768 break;
771 switch( mnPDFPageLayout )
773 default:
774 case 0:
775 aContext.PageLayout = vcl::PDFWriter::DefaultLayout;
776 break;
777 case 1:
778 aContext.PageLayout = vcl::PDFWriter::SinglePage;
779 break;
780 case 2:
781 aContext.PageLayout = vcl::PDFWriter::Continuous;
782 break;
783 case 3:
784 aContext.PageLayout = vcl::PDFWriter::ContinuousFacing;
785 break;
788 aContext.FirstPageLeft = false;
790 // check if PDF/A, which does not allow encryption
791 if( aContext.Version != vcl::PDFWriter::PDFVersion::PDF_A_1 )
793 // set check for permission change password
794 // if not enabled and no permission password, force permissions to default as if PDF where without encryption
795 if( mbRestrictPermissions && (xEnc.is() || !aPermissionPassword.isEmpty()) )
797 mbEncrypt = true; // permission set as desired, done after
799 else
801 // force permission to default
802 mnPrintAllowed = 2 ;
803 mnChangesAllowed = 4 ;
804 mbCanCopyOrExtract = true;
805 mbCanExtractForAccessibility = true ;
808 switch( mnPrintAllowed )
810 case 0: // initialized when aContext is build, means no printing
811 break;
812 default:
813 case 2:
814 aContext.Encryption.CanPrintFull = true;
815 [[fallthrough]];
816 case 1:
817 aContext.Encryption.CanPrintTheDocument = true;
818 break;
821 switch( mnChangesAllowed )
823 case 0: // already in struct PDFSecPermissions CTOR
824 break;
825 case 1:
826 aContext.Encryption.CanAssemble = true;
827 break;
828 case 2:
829 aContext.Encryption.CanFillInteractive = true;
830 break;
831 case 3:
832 aContext.Encryption.CanAddOrModify = true;
833 break;
834 default:
835 case 4:
836 aContext.Encryption.CanModifyTheContent =
837 aContext.Encryption.CanCopyOrExtract =
838 aContext.Encryption.CanAddOrModify =
839 aContext.Encryption.CanFillInteractive = true;
840 break;
843 aContext.Encryption.CanCopyOrExtract = mbCanCopyOrExtract;
844 aContext.Encryption.CanExtractForAccessibility = mbCanExtractForAccessibility;
845 if( mbEncrypt && ! xEnc.is() )
846 xEnc = vcl::PDFWriter::InitEncryption( aPermissionPassword, aOpenPassword );
847 if( mbEncrypt && !aPermissionPassword.isEmpty() && ! aPreparedPermissionPassword.hasElements() )
848 aPreparedPermissionPassword = comphelper::OStorageHelper::CreatePackageEncryptionData( aPermissionPassword );
850 // after this point we don't need the legacy clear passwords anymore
851 // however they are still inside the passed filter data sequence
852 // which is sadly out of our control
853 aPermissionPassword.clear();
854 aOpenPassword.clear();
857 * FIXME: the entries are only implicitly defined by the resource file. Should there
858 * ever be an additional form submit format this could get invalid.
860 switch( mnFormsFormat )
862 case 1:
863 aContext.SubmitFormat = vcl::PDFWriter::PDF;
864 break;
865 case 2:
866 aContext.SubmitFormat = vcl::PDFWriter::HTML;
867 break;
868 case 3:
869 aContext.SubmitFormat = vcl::PDFWriter::XML;
870 break;
871 default:
872 case 0:
873 aContext.SubmitFormat = vcl::PDFWriter::FDF;
874 break;
876 aContext.AllowDuplicateFieldNames = mbAllowDuplicateFieldNames;
878 // get model
879 Reference< frame::XModel > xModel( mxSrcDoc, UNO_QUERY );
881 // #i56629: Relative link stuff
882 // set the base URL of the file: then base URL
883 aContext.BaseURL = xModel->getURL();
884 // relative link option is private to PDF Export filter and limited to local filesystem only
885 aContext.RelFsys = mbExportRelativeFsysLinks;
886 // determine the default action for PDF links
887 switch( mnDefaultLinkAction )
889 default:
890 // default: URI, without fragment conversion (the bookmark in PDF may not work)
891 case 0:
892 aContext.DefaultLinkAction = vcl::PDFWriter::URIAction;
893 break;
894 case 1:
895 // view PDF through the reader application
896 aContext.ForcePDFAction = true;
897 aContext.DefaultLinkAction = vcl::PDFWriter::LaunchAction;
898 break;
899 case 2:
900 // view PDF through an Internet browser
901 aContext.DefaultLinkAction = vcl::PDFWriter::URIActionDestination;
902 break;
904 aContext.ConvertOOoTargetToPDFTarget = mbConvertOOoTargetToPDFTarget;
906 // check for Link Launch action, not allowed on PDF/A-1
907 // this code chunk checks when the filter is called from scripting
908 if( aContext.Version == vcl::PDFWriter::PDFVersion::PDF_A_1 &&
909 aContext.DefaultLinkAction == vcl::PDFWriter::LaunchAction )
911 // force the similar allowed URI action
912 aContext.DefaultLinkAction = vcl::PDFWriter::URIActionDestination;
913 // and remove the remote goto action forced on PDF file
914 aContext.ForcePDFAction = false;
918 aContext.SignPDF = mbSignPDF;
919 aContext.SignLocation = msSignLocation;
920 aContext.SignContact = msSignContact;
921 aContext.SignReason = msSignReason;
922 aContext.SignPassword = msSignPassword;
923 aContext.SignCertificate = maSignCertificate;
924 aContext.SignTSA = msSignTSA;
925 aContext.UseReferenceXObject = mbUseReferenceXObject;
927 // all context data set, time to create the printing device
928 vcl::PDFWriter aPDFWriter( aContext, xEnc );
929 OutputDevice* pOut = aPDFWriter.GetReferenceDevice();
931 DBG_ASSERT( pOut, "PDFExport::Export: no reference device" );
932 xDevice->SetOutputDevice(pOut);
934 if( mbAddStream )
936 // export stream
937 // get mimetype
938 OUString aSrcMimetype = getMimetypeForDocument( mxContext, mxSrcDoc );
939 OUString aExt;
940 if (aSrcMimetype == "application/vnd.oasis.opendocument.text")
941 aExt = ".odt";
942 else if (aSrcMimetype == "application/vnd.oasis.opendocument.presentation")
943 aExt = ".odp";
944 else if (aSrcMimetype == "application/vnd.oasis.opendocument.spreadsheet")
945 aExt = ".ods";
946 else if (aSrcMimetype == "application/vnd.oasis.opendocument.graphics")
947 aExt = ".odg";
948 std::unique_ptr<vcl::PDFOutputStream> pStream(new PDFExportStreamDoc(mxSrcDoc, aPreparedPermissionPassword));
949 aPDFWriter.AddAttachedFile("Original" + aExt, aSrcMimetype, u"Embedded original document of this PDF file", std::move(pStream));
952 if ( pOut )
954 DBG_ASSERT( pOut->GetExtOutDevData() == nullptr, "PDFExport: ExtOutDevData already set!!!" );
955 vcl::PDFExtOutDevData aPDFExtOutDevData( *pOut );
956 pOut->SetExtOutDevData( &aPDFExtOutDevData );
957 aPDFExtOutDevData.SetIsExportNotes( mbExportNotes );
958 aPDFExtOutDevData.SetIsExportNotesInMargin( mbExportNotesInMargin );
959 aPDFExtOutDevData.SetIsExportTaggedPDF( mbUseTaggedPDF );
960 aPDFExtOutDevData.SetIsExportTransitionEffects( mbUseTransitionEffects );
961 aPDFExtOutDevData.SetIsExportFormFields( mbExportFormFields );
962 aPDFExtOutDevData.SetIsExportBookmarks( mbExportBookmarks );
963 aPDFExtOutDevData.SetIsExportHiddenSlides( mbExportHiddenSlides );
964 aPDFExtOutDevData.SetIsSinglePageSheets( mbSinglePageSheets );
965 aPDFExtOutDevData.SetIsLosslessCompression( mbUseLosslessCompression );
966 aPDFExtOutDevData.SetCompressionQuality( mnQuality );
967 aPDFExtOutDevData.SetIsReduceImageResolution( mbReduceImageResolution );
968 aPDFExtOutDevData.SetIsExportNamedDestinations( mbExportBmkToDest );
970 Sequence< PropertyValue > aRenderOptions{
971 comphelper::makePropertyValue("RenderDevice", uno::Reference<awt::XDevice>(xDevice)),
972 comphelper::makePropertyValue("ExportNotesPages", false),
973 comphelper::makePropertyValue("IsFirstPage", true),
974 comphelper::makePropertyValue("IsLastPage", false),
975 comphelper::makePropertyValue("IsSkipEmptyPages", mbSkipEmptyPages),
976 comphelper::makePropertyValue("PageRange", aPageRange),
977 comphelper::makePropertyValue("ExportPlaceholders", mbExportPlaceholders),
978 comphelper::makePropertyValue("SinglePageSheets", mbSinglePageSheets),
979 comphelper::makePropertyValue("ExportNotesInMargin", mbExportNotesInMargin)
981 Any& rExportNotesValue = aRenderOptions.getArray()[ 1 ].Value;
983 if( !aPageRange.isEmpty() || !aSelection.hasValue() )
985 aSelection = Any();
986 aSelection <<= mxSrcDoc;
988 bool bExportNotesPages = false;
989 bool bReChangeToNormalView = false;
990 static const OUStringLiteral sShowOnlineLayout( u"ShowOnlineLayout" );
991 bool bReHideWhitespace = false;
992 static const OUStringLiteral sHideWhitespace(u"HideWhitespace");
993 uno::Reference< beans::XPropertySet > xViewProperties;
995 if ( aCreator == "Writer" )
997 // #i92835: if Writer is in web layout mode this has to be switched to normal view and back to web view in the end
1000 Reference< view::XViewSettingsSupplier > xVSettingsSupplier( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
1001 xViewProperties = xVSettingsSupplier->getViewSettings();
1002 xViewProperties->getPropertyValue( sShowOnlineLayout ) >>= bReChangeToNormalView;
1003 if( bReChangeToNormalView )
1005 xViewProperties->setPropertyValue( sShowOnlineLayout, uno::Any( false ) );
1008 // Also, disable hide-whitespace during export.
1009 xViewProperties->getPropertyValue(sHideWhitespace) >>= bReHideWhitespace;
1010 if (bReHideWhitespace)
1012 xViewProperties->setPropertyValue(sHideWhitespace, uno::Any(false));
1015 catch( const uno::Exception& )
1021 const sal_Int32 nPageCount = xRenderable->getRendererCount( aSelection, aRenderOptions );
1023 if ( mbExportNotesPages && aCreator == "Impress" )
1025 uno::Reference< drawing::XShapes > xShapes; // do not allow to export notes when exporting a selection
1026 if ( ! ( aSelection >>= xShapes ) )
1027 bExportNotesPages = true;
1029 const bool bExportPages = !bExportNotesPages || !mbExportOnlyNotesPages;
1031 if( aPageRange.isEmpty() || mbSinglePageSheets)
1033 aPageRange = OUString::number( 1 ) + "-" + OUString::number(nPageCount );
1035 StringRangeEnumerator aRangeEnum( aPageRange, 0, nPageCount-1 );
1037 if ( mxStatusIndicator.is() )
1039 sal_Int32 nTotalPageCount = aRangeEnum.size();
1040 if ( bExportPages && bExportNotesPages )
1041 nTotalPageCount *= 2;
1042 mxStatusIndicator->start(FilterResId(PDF_PROGRESS_BAR), nTotalPageCount);
1045 bRet = nPageCount > 0;
1047 if ( bRet && bExportPages )
1048 bRet = ExportSelection( aPDFWriter, xRenderable, aSelection, aRangeEnum, aRenderOptions, nPageCount );
1050 if ( bRet && bExportNotesPages )
1052 rExportNotesValue <<= true;
1053 bRet = ExportSelection( aPDFWriter, xRenderable, aSelection, aRangeEnum, aRenderOptions, nPageCount );
1055 if ( mxStatusIndicator.is() )
1056 mxStatusIndicator->end();
1058 // if during the export the doc locale was set copy it to PDF writer
1059 const css::lang::Locale& rLoc( aPDFExtOutDevData.GetDocumentLocale() );
1060 if( !rLoc.Language.isEmpty() )
1061 aPDFWriter.SetDocumentLocale( rLoc );
1063 if( bRet )
1065 aPDFExtOutDevData.PlayGlobalActions( aPDFWriter );
1066 bRet = aPDFWriter.Emit();
1067 aErrors = aPDFWriter.GetErrors();
1069 pOut->SetExtOutDevData( nullptr );
1070 if( bReChangeToNormalView )
1074 xViewProperties->setPropertyValue( sShowOnlineLayout, uno::Any( true ) );
1076 catch( const uno::Exception& )
1080 if( bReHideWhitespace )
1084 xViewProperties->setPropertyValue( sHideWhitespace, uno::Any( true ) );
1086 catch( const uno::Exception& )
1094 // show eventual errors during export
1095 showErrors( aErrors );
1097 return bRet;
1101 namespace
1104 typedef comphelper::WeakComponentImplHelper< task::XInteractionRequest > PDFErrorRequestBase;
1106 class PDFErrorRequest : public PDFErrorRequestBase
1108 task::PDFExportException maExc;
1109 public:
1110 explicit PDFErrorRequest( task::PDFExportException aExc );
1112 // XInteractionRequest
1113 virtual uno::Any SAL_CALL getRequest() override;
1114 virtual uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL getContinuations() override;
1118 PDFErrorRequest::PDFErrorRequest( task::PDFExportException aExc ) :
1119 maExc(std::move( aExc ))
1124 uno::Any SAL_CALL PDFErrorRequest::getRequest()
1126 std::unique_lock guard( m_aMutex );
1128 uno::Any aRet;
1129 aRet <<= maExc;
1130 return aRet;
1134 uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL PDFErrorRequest::getContinuations()
1136 return uno::Sequence< uno::Reference< task::XInteractionContinuation > >();
1139 } // end anonymous namespace
1142 void PDFExport::showErrors( const std::set< vcl::PDFWriter::ErrorCode >& rErrors )
1144 if( ! rErrors.empty() && mxIH.is() )
1146 task::PDFExportException aExc;
1147 aExc.ErrorCodes = comphelper::containerToSequence<sal_Int32>( rErrors );
1148 Reference< task::XInteractionRequest > xReq( new PDFErrorRequest( std::move(aExc) ) );
1149 mxIH->handle( xReq );
1154 void PDFExport::ImplExportPage( vcl::PDFWriter& rWriter, vcl::PDFExtOutDevData& rPDFExtOutDevData, const GDIMetaFile& rMtf )
1156 //Rectangle(Point, Size) creates a rectangle off by 1, use Rectangle(long, long, long, long) instead
1157 basegfx::B2DPolygon aSize(tools::Polygon(tools::Rectangle(0, 0, rMtf.GetPrefSize().Width(), rMtf.GetPrefSize().Height())).getB2DPolygon());
1158 basegfx::B2DPolygon aSizePDF(OutputDevice::LogicToLogic(aSize, rMtf.GetPrefMapMode(), MapMode(MapUnit::MapPoint)));
1159 basegfx::B2DRange aRangePDF(aSizePDF.getB2DRange());
1160 tools::Rectangle aPageRect( Point(), rMtf.GetPrefSize() );
1162 rWriter.NewPage( aRangePDF.getWidth(), aRangePDF.getHeight() );
1163 rWriter.SetMapMode( rMtf.GetPrefMapMode() );
1165 vcl::PDFWriter::PlayMetafileContext aCtx;
1166 GDIMetaFile aMtf;
1167 if( mbRemoveTransparencies )
1169 aCtx.m_bTransparenciesWereRemoved = rWriter.GetReferenceDevice()->
1170 RemoveTransparenciesFromMetaFile( rMtf, aMtf, mnMaxImageResolution, mnMaxImageResolution,
1171 false, true, mbReduceImageResolution );
1172 // tdf#134736 if the metafile was replaced then rPDFExtOutDevData's PageSyncData mActions
1173 // all still point to MetaAction indexes in the original metafile that are now invalid.
1174 // Throw them all away in the absence of a way to reposition them to new positions of
1175 // their replacements.
1176 if (aCtx.m_bTransparenciesWereRemoved)
1177 rPDFExtOutDevData.ResetSyncData(&rWriter);
1179 else
1181 aMtf = rMtf;
1183 aCtx.m_nMaxImageResolution = mbReduceImageResolution ? mnMaxImageResolution : 0;
1184 aCtx.m_bOnlyLosslessCompression = mbUseLosslessCompression;
1185 aCtx.m_nJPEGQuality = mnQuality;
1188 rWriter.SetClipRegion( basegfx::B2DPolyPolygon(
1189 basegfx::utils::createPolygonFromRect( vcl::unotools::b2DRectangleFromRectangle(aPageRect) ) ) );
1191 rWriter.PlayMetafile( aMtf, aCtx, &rPDFExtOutDevData );
1193 rPDFExtOutDevData.ResetSyncData(nullptr);
1195 if (!msWatermark.isEmpty())
1197 ImplWriteWatermark( rWriter, Size(aRangePDF.getWidth(), aRangePDF.getHeight()) );
1199 else if (!msTiledWatermark.isEmpty())
1201 ImplWriteTiledWatermark( rWriter, Size(aRangePDF.getWidth(), aRangePDF.getHeight()) );
1206 void PDFExport::ImplWriteWatermark( vcl::PDFWriter& rWriter, const Size& rPageSize )
1208 vcl::Font aFont( maWatermarkFontName, Size( 0, moWatermarkFontHeight ? *moWatermarkFontHeight : 3*rPageSize.Height()/4 ) );
1209 aFont.SetItalic( ITALIC_NONE );
1210 aFont.SetWidthType( WIDTH_NORMAL );
1211 aFont.SetWeight( WEIGHT_NORMAL );
1212 aFont.SetAlignment( ALIGN_BOTTOM );
1213 tools::Long nTextWidth = rPageSize.Width();
1214 if( rPageSize.Width() < rPageSize.Height() )
1216 nTextWidth = rPageSize.Height();
1217 aFont.SetOrientation( 2700_deg10 );
1220 if (moWatermarkRotateAngle)
1222 aFont.SetOrientation(*moWatermarkRotateAngle);
1223 if (rPageSize.Width() < rPageSize.Height())
1225 // Set text width based on the shorter side, so rotation can't push text outside the
1226 // page boundaries.
1227 nTextWidth = rPageSize.Width();
1231 // adjust font height for text to fit
1232 OutputDevice* pDev = rWriter.GetReferenceDevice();
1233 pDev->Push();
1234 pDev->SetFont( aFont );
1235 pDev->SetMapMode( MapMode( MapUnit::MapPoint ) );
1236 int w = 0;
1237 if (moWatermarkFontHeight)
1239 w = pDev->GetTextWidth(msWatermark);
1241 else
1243 while( ( w = pDev->GetTextWidth( msWatermark ) ) > nTextWidth )
1245 if (w == 0)
1246 break;
1247 tools::Long nNewHeight = aFont.GetFontHeight() * nTextWidth / w;
1248 if( nNewHeight == aFont.GetFontHeight() )
1250 nNewHeight--;
1251 if( nNewHeight <= 0 )
1252 break;
1254 aFont.SetFontHeight( nNewHeight );
1255 pDev->SetFont( aFont );
1258 tools::Long nTextHeight = pDev->GetTextHeight();
1259 // leave some maneuvering room for rounding issues, also
1260 // some fonts go a little outside ascent/descent
1261 nTextHeight += nTextHeight/20;
1262 pDev->Pop();
1264 rWriter.Push();
1265 // tdf#152235 tag around the reference to the XObject on the page
1266 sal_Int32 const id = rWriter.EnsureStructureElement();
1267 rWriter.InitStructureElement(id, vcl::PDFWriter::NonStructElement, ::std::u16string_view());
1268 rWriter.BeginStructureElement(id);
1269 rWriter.SetStructureAttribute(vcl::PDFWriter::Type, vcl::PDFWriter::Pagination);
1270 rWriter.SetStructureAttribute(vcl::PDFWriter::Subtype, vcl::PDFWriter::Watermark);
1271 // HACK: this should produce *nothing* itself but is necessary to output
1272 // the Artifact tag here, not inside the XObject
1273 rWriter.DrawPolyLine({});
1274 rWriter.SetMapMode( MapMode( MapUnit::MapPoint ) );
1275 rWriter.SetFont( aFont );
1276 rWriter.SetTextColor(maWatermarkColor);
1277 Point aTextPoint;
1278 tools::Rectangle aTextRect;
1279 if( rPageSize.Width() > rPageSize.Height() )
1281 aTextPoint = Point( (rPageSize.Width()-w)/2,
1282 rPageSize.Height()-(rPageSize.Height()-nTextHeight)/2 );
1283 aTextRect = tools::Rectangle( Point( (rPageSize.Width()-w)/2,
1284 (rPageSize.Height()-nTextHeight)/2 ),
1285 Size( w, nTextHeight ) );
1287 else
1289 aTextPoint = Point( (rPageSize.Width()-nTextHeight)/2,
1290 (rPageSize.Height()-w)/2 );
1291 aTextRect = tools::Rectangle( aTextPoint, Size( nTextHeight, w ) );
1294 if (moWatermarkRotateAngle)
1296 // First set the text's starting point to the center of the page.
1297 tools::Rectangle aPageRectangle(Point(0, 0), rPageSize);
1298 aTextPoint = aPageRectangle.Center();
1299 // Then adjust it so that the text remains centered, based on the rotation angle.
1300 basegfx::B2DPolygon aTextPolygon
1301 = basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle(0, -nTextHeight, w, 0));
1302 basegfx::B2DHomMatrix aMatrix;
1303 aMatrix.rotate(-1 * toRadians(*moWatermarkRotateAngle));
1304 aTextPolygon.transform(aMatrix);
1305 basegfx::B2DPoint aPolygonCenter = aTextPolygon.getB2DRange().getCenter();
1306 aTextPoint.AdjustX(-aPolygonCenter.getX());
1307 aTextPoint.AdjustY(-aPolygonCenter.getY());
1309 aTextRect = aPageRectangle;
1312 rWriter.SetClipRegion();
1313 rWriter.BeginTransparencyGroup();
1314 rWriter.DrawText( aTextPoint, msWatermark );
1315 rWriter.EndTransparencyGroup( aTextRect, 50 );
1316 rWriter.EndStructureElement();
1317 rWriter.Pop();
1320 void PDFExport::ImplWriteTiledWatermark( vcl::PDFWriter& rWriter, const Size& rPageSize )
1322 OUString watermark = msTiledWatermark;
1323 // Maximum number of characters in one line.
1324 // it is set to 21 to make it look like tiled watermarks as online in secure view
1325 const int lineLength = 21;
1326 vcl::Font aFont( "Liberation Sans", Size( 0, 40 ) );
1327 aFont.SetItalic( ITALIC_NONE );
1328 aFont.SetWidthType( WIDTH_NORMAL );
1329 aFont.SetWeight( WEIGHT_NORMAL );
1330 aFont.SetAlignment( ALIGN_BOTTOM );
1331 aFont.SetFontHeight(40);
1332 aFont.SetOrientation(450_deg10);
1334 OutputDevice* pDev = rWriter.GetReferenceDevice();
1335 pDev->SetFont(aFont);
1336 pDev->Push();
1337 pDev->SetFont(aFont);
1338 pDev->SetMapMode( MapMode( MapUnit::MapPoint ) );
1339 int w = 0;
1340 int watermarkcount = ((rPageSize.Width()) / 200)+1;
1341 tools::Long nTextWidth = rPageSize.Width() / (watermarkcount*1.5);
1342 OUString oneLineText = watermark;
1344 if(watermark.getLength() > lineLength)
1345 oneLineText = watermark.copy(0, lineLength);
1347 while((w = pDev->GetTextWidth(oneLineText)) > nTextWidth)
1349 if(w==0)
1350 break;
1352 tools::Long nNewHeight = aFont.GetFontHeight() * nTextWidth / w;
1353 aFont.SetFontHeight(nNewHeight);
1354 pDev->SetFont( aFont );
1356 // maximum number of watermark count for the width
1357 if(watermarkcount > 8)
1358 watermarkcount = 8;
1360 pDev->Pop();
1362 rWriter.Push();
1363 // tdf#152235 tag around the reference to the XObject on the page
1364 sal_Int32 const id = rWriter.EnsureStructureElement();
1365 rWriter.InitStructureElement(id, vcl::PDFWriter::NonStructElement, ::std::u16string_view());
1366 rWriter.BeginStructureElement(id);
1367 rWriter.SetStructureAttribute(vcl::PDFWriter::Type, vcl::PDFWriter::Pagination);
1368 rWriter.SetStructureAttribute(vcl::PDFWriter::Subtype, vcl::PDFWriter::Watermark);
1369 // HACK: this should produce *nothing* itself but is necessary to output
1370 // the Artifact tag here, not inside the XObject
1371 rWriter.DrawPolyLine({});
1372 rWriter.SetMapMode( MapMode( MapUnit::MapPoint ) );
1373 rWriter.SetFont(aFont);
1374 rWriter.SetTextColor( Color(19,20,22) );
1375 // center watermarks horizontally
1376 Point aTextPoint( (rPageSize.Width()/2) - (((nTextWidth*watermarkcount)+(watermarkcount-1)*nTextWidth)/2),
1377 pDev->GetTextHeight());
1379 for( int i = 0; i < watermarkcount; i ++)
1381 while(aTextPoint.getY()+pDev->GetTextHeight()*3 <= rPageSize.Height())
1383 tools::Rectangle aTextRect(aTextPoint, Size(nTextWidth*2,pDev->GetTextHeight()*4));
1385 pDev->Push();
1386 rWriter.SetClipRegion();
1387 rWriter.BeginTransparencyGroup();
1388 rWriter.SetTextColor( Color(19,20,22) );
1389 rWriter.DrawText(aTextRect, watermark, DrawTextFlags::MultiLine|DrawTextFlags::Center|DrawTextFlags::VCenter|DrawTextFlags::WordBreak|DrawTextFlags::Bottom);
1390 rWriter.EndTransparencyGroup( aTextRect, 50 );
1391 pDev->Pop();
1393 pDev->Push();
1394 rWriter.SetClipRegion();
1395 rWriter.BeginTransparencyGroup();
1396 rWriter.SetTextColor( Color(236,235,233) );
1397 rWriter.DrawText(aTextRect, watermark, DrawTextFlags::MultiLine|DrawTextFlags::Center|DrawTextFlags::VCenter|DrawTextFlags::WordBreak|DrawTextFlags::Bottom);
1398 rWriter.EndTransparencyGroup( aTextRect, 50 );
1399 pDev->Pop();
1401 aTextPoint.Move(0, pDev->GetTextHeight()*3);
1403 aTextPoint=Point( aTextPoint.getX(), pDev->GetTextHeight() );
1404 aTextPoint.Move( nTextWidth*1.5, 0 );
1407 rWriter.EndStructureElement();
1408 rWriter.Pop();
1411 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */