xref: /core/filter/source/pdf/pdfexport.cxx (revision 0e2409e5)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
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>
42 
43 #include "pdfexport.hxx"
44 #include <strings.hrc>
45 
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/XDocumentProperties2.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/sheet/XSheetRange.hpp>
63 #include <com/sun/star/security/XCertificate.hpp>
64 #include <com/sun/star/beans/XMaterialHolder.hpp>
65 #include <com/sun/star/xml/crypto/SEInitializer.hpp>
66 
67 #include <memory>
68 
69 #include <rtl/bootstrap.hxx>
70 #include <config_features.h>
71 
72 using namespace ::com::sun::star;
73 using namespace ::com::sun::star::io;
74 using namespace ::com::sun::star::uno;
75 using namespace ::com::sun::star::lang;
76 using namespace ::com::sun::star::beans;
77 using namespace ::com::sun::star::view;
78 
79 
PDFExport(const Reference<XComponent> & rxSrcDoc,const Reference<task::XStatusIndicator> & rxStatusIndicator,const Reference<task::XInteractionHandler> & rxIH,const Reference<XComponentContext> & xContext)80 PDFExport::PDFExport( const Reference< XComponent >& rxSrcDoc,
81                       const Reference< task::XStatusIndicator >& rxStatusIndicator,
82                       const Reference< task::XInteractionHandler >& rxIH,
83                       const Reference< XComponentContext >& xContext ) :
84     mxSrcDoc                    ( rxSrcDoc ),
85     mxContext                   ( xContext ),
86     mxStatusIndicator           ( rxStatusIndicator ),
87     mxIH                        ( rxIH ),
88     mbUseLosslessCompression    ( false ),
89     mbReduceImageResolution     ( true ),
90     mbSkipEmptyPages            ( true ),
91     mnMaxImageResolution        ( 300 ),
92     mnQuality                   ( 80 ),
93     mnProgressValue             ( 0 ),
94     mbRemoveTransparencies      ( false ),
95 
96     mbIsRedactMode              ( false ),
97     maWatermarkColor            ( COL_LIGHTGREEN ),
98     maWatermarkFontName         ( u"Helvetica"_ustr )
99 {
100 }
101 
102 
~PDFExport()103 PDFExport::~PDFExport()
104 {
105 }
106 
107 
ExportSelection(vcl::PDFWriter & rPDFWriter,Reference<css::view::XRenderable> const & rRenderable,const Any & rSelection,const StringRangeEnumerator & rRangeEnum,Sequence<PropertyValue> & rRenderOptions,sal_Int32 nPageCount)108 bool PDFExport::ExportSelection( vcl::PDFWriter& rPDFWriter,
109     Reference< css::view::XRenderable > const & rRenderable,
110     const Any& rSelection,
111     const StringRangeEnumerator& rRangeEnum,
112     Sequence< PropertyValue >& rRenderOptions,
113     sal_Int32 nPageCount )
114 {
115     bool        bRet = false;
116     try
117     {
118         Any* pFirstPage = nullptr;
119         Any* pLastPage = nullptr;
120 
121         bool bExportNotesPages = false;
122 
123         auto rRenderOptionsRange = asNonConstRange(rRenderOptions);
124         for( sal_Int32 nData = 0, nDataCount = rRenderOptions.getLength(); nData < nDataCount; ++nData )
125         {
126             if ( rRenderOptions[ nData ].Name == "IsFirstPage" )
127                 pFirstPage = &rRenderOptionsRange[ nData ].Value;
128             else if ( rRenderOptions[ nData ].Name == "IsLastPage" )
129                 pLastPage = &rRenderOptionsRange[ nData ].Value;
130             else if ( rRenderOptions[ nData ].Name == "ExportNotesPages" )
131                 rRenderOptionsRange[ nData ].Value >>= bExportNotesPages;
132         }
133 
134         OutputDevice* pOut = rPDFWriter.GetReferenceDevice();
135 
136         if( pOut )
137         {
138             if ( nPageCount )
139             {
140                 vcl::PDFExtOutDevData& rPDFExtOutDevData = dynamic_cast<vcl::PDFExtOutDevData&>(*pOut->GetExtOutDevData());
141                 rPDFExtOutDevData.SetIsExportNotesPages( bExportNotesPages );
142 
143                 sal_Int32 nCurrentPage(0);
144                 StringRangeEnumerator::Iterator aIter = rRangeEnum.begin();
145                 StringRangeEnumerator::Iterator aEnd  = rRangeEnum.end();
146                 while ( aIter != aEnd )
147                 {
148                     const Sequence< PropertyValue > aRenderer( rRenderable->getRenderer( *aIter, rSelection, rRenderOptions ) );
149                     awt::Size                   aPageSize;
150 
151                     for( const PropertyValue& rProp : aRenderer )
152                     {
153                         if ( rProp.Name == "PageSize" )
154                         {
155                             rProp.Value >>= aPageSize;
156                             break;
157                         }
158                     }
159 
160                     rPDFExtOutDevData.SetCurrentPageNumber( nCurrentPage );
161 
162                     GDIMetaFile                 aMtf;
163                     const MapMode               aMapMode( MapUnit::Map100thMM );
164                     const Size                  aMtfSize( aPageSize.Width, aPageSize.Height );
165 
166                     pOut->Push();
167                     pOut->EnableOutput( false );
168                     pOut->SetMapMode( aMapMode );
169 
170                     aMtf.SetPrefSize( aMtfSize );
171                     aMtf.SetPrefMapMode( aMapMode );
172                     aMtf.Record( pOut );
173 
174                     // #i35176#
175                     // IsLastPage property.
176                     const sal_Int32 nCurrentRenderer = *aIter;
177                     ++aIter;
178                     if ( pLastPage && aIter == aEnd )
179                         *pLastPage <<= true;
180 
181                     rRenderable->render( nCurrentRenderer, rSelection, rRenderOptions );
182 
183                     aMtf.Stop();
184                     aMtf.WindStart();
185 
186                     bool bEmptyPage = false;
187                     if( aMtf.GetActionSize() &&
188                              ( !mbSkipEmptyPages || aPageSize.Width || aPageSize.Height ) )
189                     {
190                         // We convert the whole metafile into a bitmap to get rid of the
191                         // text covered by redaction shapes
192                         if (mbIsRedactMode)
193                         {
194                             try
195                             {
196                                 Graphic aGraph(aMtf);
197                                 // use antialiasing to improve how graphic objects look
198                                 BitmapEx bmp = aGraph.GetBitmapEx(GraphicConversionParameters(Size(0, 0), false, true, false));
199                                 Graphic bgraph(bmp);
200                                 aMtf = bgraph.GetGDIMetaFile();
201                             }
202                             catch(const Exception&)
203                             {
204                                 TOOLS_WARN_EXCEPTION("filter.pdf", "Something went wrong while converting metafile to bitmap");
205                             }
206                         }
207 
208                         ImplExportPage(rPDFWriter, rPDFExtOutDevData, aMtf);
209                         bRet = true;
210                     }
211                     else
212                     {
213                         bEmptyPage = true;
214                     }
215 
216                     pOut->Pop();
217 
218                     if ( mxStatusIndicator.is() )
219                         mxStatusIndicator->setValue( mnProgressValue );
220                     if ( pFirstPage )
221                         *pFirstPage <<= false;
222 
223                     ++mnProgressValue;
224                     if (!bEmptyPage)
225                     {
226                         // Calculate the page number in the PDF output, which may be smaller than the page number in
227                         // case of hidden slides or a partial export.
228                         ++nCurrentPage;
229                     }
230                 }
231             }
232             else
233             {
234                 bRet = true;                            // #i18334# nPageCount == 0,
235                 rPDFWriter.NewPage( 10000, 10000 );     // creating dummy page
236                 rPDFWriter.SetMapMode(MapMode(MapUnit::Map100thMM));
237             }
238         }
239     }
240     catch(const RuntimeException &)
241     {
242     }
243     return bRet;
244 }
245 
246 namespace {
247 
248 class PDFExportStreamDoc : public vcl::PDFOutputStream
249 {
250 private:
251 
252     Reference< XComponent >             m_xSrcDoc;
253     Sequence< beans::NamedValue >       m_aPreparedPassword;
254 
255 public:
256 
PDFExportStreamDoc(const Reference<XComponent> & xDoc,const Sequence<beans::NamedValue> & rPwd)257     PDFExportStreamDoc( const Reference< XComponent >& xDoc, const Sequence<beans::NamedValue>& rPwd )
258     : m_xSrcDoc( xDoc ),
259       m_aPreparedPassword( rPwd )
260     {}
261 
262     virtual void write( const Reference< XOutputStream >& xStream ) override;
263 };
264 
265 }
266 
write(const Reference<XOutputStream> & xStream)267 void PDFExportStreamDoc::write( const Reference< XOutputStream >& xStream )
268 {
269     Reference< css::frame::XStorable > xStore( m_xSrcDoc, UNO_QUERY );
270     if( !xStore.is() )
271         return;
272 
273     std::vector<beans::PropertyValue> aArgs {
274         comphelper::makePropertyValue(u"FilterName"_ustr, OUString()),
275         comphelper::makePropertyValue(u"OutputStream"_ustr, xStream),
276     };
277     if (m_aPreparedPassword.hasElements())
278         aArgs.push_back(comphelper::makePropertyValue(u"EncryptionData"_ustr, m_aPreparedPassword));
279 
280     try
281     {
282         xStore->storeToURL(u"private:stream"_ustr, comphelper::containerToSequence(aArgs));
283     }
284     catch( const IOException& )
285     {
286     }
287 }
288 
289 
getMimetypeForDocument(const Reference<XComponentContext> & xContext,const Reference<XComponent> & xDoc)290 static OUString getMimetypeForDocument( const Reference< XComponentContext >& xContext,
291                                         const Reference< XComponent >& xDoc ) noexcept
292 {
293     OUString aDocMimetype;
294     try
295     {
296         // get document service name
297         Reference< css::frame::XStorable > xStore( xDoc, UNO_QUERY );
298         Reference< frame::XModuleManager2 > xModuleManager = frame::ModuleManager::create(xContext);
299         if( xStore.is() )
300         {
301             OUString aDocServiceName = xModuleManager->identify( Reference< XInterface >( xStore, uno::UNO_QUERY ) );
302             if ( !aDocServiceName.isEmpty() )
303             {
304                 // get the actual filter name
305                 Reference< lang::XMultiServiceFactory > xConfigProvider =
306                     configuration::theDefaultProvider::get( xContext );
307                 beans::NamedValue aPathProp;
308                 aPathProp.Name = "nodepath";
309                 aPathProp.Value <<= u"/org.openoffice.Setup/Office/Factories/"_ustr;
310                 uno::Sequence< uno::Any > aArgs{ uno::Any(aPathProp) };
311 
312                 Reference< container::XNameAccess > xSOFConfig(
313                     xConfigProvider->createInstanceWithArguments(
314                         u"com.sun.star.configuration.ConfigurationAccess"_ustr, aArgs ),
315                     uno::UNO_QUERY );
316 
317                 Reference< container::XNameAccess > xApplConfig;
318                 xSOFConfig->getByName( aDocServiceName ) >>= xApplConfig;
319                 if ( xApplConfig.is() )
320                 {
321                     OUString aFilterName;
322                     xApplConfig->getByName( u"ooSetupFactoryActualFilter"_ustr ) >>= aFilterName;
323                     if( !aFilterName.isEmpty() )
324                     {
325                         // find the related type name
326                         OUString aTypeName;
327                         Reference< container::XNameAccess > xFilterFactory(
328                             xContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.document.FilterFactory"_ustr, xContext),
329                             uno::UNO_QUERY );
330 
331                         Sequence< beans::PropertyValue > aFilterData;
332                         xFilterFactory->getByName( aFilterName ) >>= aFilterData;
333                         for (const beans::PropertyValue& rProp : aFilterData)
334                             if ( rProp.Name == "Type" )
335                                 rProp.Value >>= aTypeName;
336 
337                         if ( !aTypeName.isEmpty() )
338                         {
339                             // find the mediatype
340                             Reference< container::XNameAccess > xTypeDetection(
341                                 xContext->getServiceManager()->createInstanceWithContext(u"com.sun.star.document.TypeDetection"_ustr, xContext),
342                                 UNO_QUERY );
343 
344                             Sequence< beans::PropertyValue > aTypeData;
345                             xTypeDetection->getByName( aTypeName ) >>= aTypeData;
346                             for (const beans::PropertyValue& rProp : aTypeData)
347                                 if ( rProp.Name == "MediaType" )
348                                     rProp.Value >>= aDocMimetype;
349                         }
350                     }
351                 }
352             }
353         }
354     }
355     catch (...)
356     {
357     }
358     return aDocMimetype;
359 }
360 
361 uno::Reference<security::XCertificate>
GetCertificateFromSubjectName(const std::u16string_view & rSubjectName) const362 PDFExport::GetCertificateFromSubjectName(const std::u16string_view& rSubjectName) const
363 {
364     uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
365         = xml::crypto::SEInitializer::create(mxContext);
366     uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
367         = xSEInitializer->createSecurityContext(OUString());
368     uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment
369         = xSecurityContext->getSecurityEnvironment();
370     for (const auto& xCertificate : xSecurityEnvironment->getPersonalCertificates())
371     {
372         if (xCertificate->getSubjectName() == rSubjectName)
373         {
374             return xCertificate;
375         }
376     }
377 
378     return {};
379 }
380 
Export(const OUString & rFile,const Sequence<PropertyValue> & rFilterData)381 bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& rFilterData )
382 {
383     INetURLObject   aURL( rFile );
384     bool        bRet = false;
385 
386     std::set< vcl::PDFWriter::ErrorCode > aErrors;
387 
388     if( aURL.GetProtocol() != INetProtocol::File )
389     {
390         OUString aTmp;
391 
392         if( osl::FileBase::getFileURLFromSystemPath( rFile, aTmp ) == osl::FileBase::E_None )
393             aURL = INetURLObject(aTmp);
394     }
395 
396     if( aURL.GetProtocol() == INetProtocol::File )
397     {
398         Reference< XRenderable > xRenderable( mxSrcDoc, UNO_QUERY );
399 
400         if( xRenderable.is() )
401         {
402             // The defaults
403             bool bUseTaggedPDF = false;
404             sal_Int32 nPDFTypeSelection = 0;
405             bool bPDFUACompliance = false;
406             bool bExportNotes = true;
407             bool bExportNotesInMargin = false;
408             bool bExportNotesPages = false;
409             bool bExportOnlyNotesPages = false;
410             bool bUseTransitionEffects = true;
411             bool bExportFormFields = true;
412             sal_Int32 nFormsFormat = 0;
413             bool bAllowDuplicateFieldNames = false;
414             bool bHideViewerToolbar = false;
415             bool bHideViewerMenubar = false;
416             bool bHideViewerWindowControls = false;
417             bool bFitWindow = false;
418             bool bCenterWindow = false;
419             bool bOpenInFullScreenMode = false;
420             bool bDisplayPDFDocumentTitle = true;
421             sal_Int32 nPDFDocumentMode = 0;
422             sal_Int32 nPDFDocumentAction = 0;
423             sal_Int32 nZoom = 100;
424             sal_Int32 nInitialPage = 1;
425             sal_Int32 nPDFPageLayout = 0;
426             bool bAddStream = false;
427             bool bEncrypt = false;
428             bool bRestrictPermissions = false;
429             sal_Int32 nPrintAllowed = 2;
430             sal_Int32 nChangesAllowed = 4;
431             bool bCanCopyOrExtract = true;
432             bool bCanExtractForAccessibility = true;
433             // #i56629
434             bool bExportRelativeFsysLinks = false;
435             sal_Int32 nDefaultLinkAction = 0;
436             bool bConvertOOoTargetToPDFTarget = false;
437             bool bExportBmkToDest = false;
438             bool bExportBookmarks = true;
439             bool bExportHiddenSlides = false;
440             bool bSinglePageSheets = false;
441             sal_Int32 nOpenBookmarkLevels = -1;
442             bool bSignPDF = false;
443             OUString sSignLocation, sSignReason, sSignContact, sSignPassword;
444             css::uno::Reference<css::security::XCertificate> aSignCertificate;
445             OUString sSignTSA;
446             bool bExportPlaceholders = false;
447             bool bUseReferenceXObject = false;
448 
449             rtl::Reference<VCLXDevice>  xDevice(new VCLXDevice);
450             OUString                    aPageRange;
451             Any                         aSelection;
452             vcl::PDFWriter::PDFWriterContext aContext;
453             OUString aOpenPassword, aPermissionPassword;
454             Reference< beans::XMaterialHolder > xEnc;
455             Sequence< beans::NamedValue > aPreparedPermissionPassword;
456             std::optional<PropertyValue> oMathTitleRow;
457             std::optional<PropertyValue> oMathFormulaText;
458             std::optional<PropertyValue> oMathBorder;
459             std::optional<PropertyValue> oMathPrintFormat;
460             std::optional<PropertyValue> oMathPrintScale;
461 
462             // getting the string for the creator
463             OUString aCreator;
464             Reference< XServiceInfo > xInfo( mxSrcDoc, UNO_QUERY );
465             if ( xInfo.is() )
466             {
467                 if ( xInfo->supportsService( u"com.sun.star.presentation.PresentationDocument"_ustr ) )
468                     aCreator = u"Impress"_ustr;
469                 else if ( xInfo->supportsService( u"com.sun.star.drawing.DrawingDocument"_ustr ) )
470                     aCreator = u"Draw"_ustr;
471                 else if ( xInfo->supportsService( u"com.sun.star.text.TextDocument"_ustr ) )
472                     aCreator = u"Writer"_ustr;
473                 else if ( xInfo->supportsService( u"com.sun.star.sheet.SpreadsheetDocument"_ustr ) )
474                     aCreator = u"Calc"_ustr;
475                 else if ( xInfo->supportsService( u"com.sun.star.formula.FormulaProperties"_ustr  ) )
476                     aCreator = u"Math"_ustr;
477             }
478 
479             Reference< document::XDocumentPropertiesSupplier > xDocumentPropsSupplier( mxSrcDoc, UNO_QUERY );
480             if ( xDocumentPropsSupplier.is() )
481             {
482                 Reference< document::XDocumentProperties2 > xDocumentProps( xDocumentPropsSupplier->getDocumentProperties(), UNO_QUERY );
483                 if ( xDocumentProps.is() )
484                 {
485                     aContext.DocumentInfo.Title = xDocumentProps->getTitle();
486                     aContext.DocumentInfo.Author = xDocumentProps->getAuthor();
487                     aContext.DocumentInfo.Subject = xDocumentProps->getSubject();
488                     aContext.DocumentInfo.Keywords = ::comphelper::string::convertCommaSeparated(xDocumentProps->getKeywords());
489                     aContext.DocumentInfo.ModificationDate
490                         = xDocumentProps->getEditingCycles() < 1
491                               ? xDocumentProps->getCreationDate()
492                               : xDocumentProps->getModificationDate();
493                     aContext.DocumentInfo.Contributor = xDocumentProps->getContributor();
494                     aContext.DocumentInfo.Coverage = xDocumentProps->getCoverage();
495                     aContext.DocumentInfo.Identifier = xDocumentProps->getIdentifier();
496                     aContext.DocumentInfo.Publisher = xDocumentProps->getPublisher();
497                     aContext.DocumentInfo.Relation = xDocumentProps->getRelation();
498                     aContext.DocumentInfo.Rights = xDocumentProps->getRights();
499                     aContext.DocumentInfo.Source = xDocumentProps->getSource();
500                     aContext.DocumentInfo.Type = xDocumentProps->getType();
501                 }
502             }
503 
504             if (!comphelper::IsFuzzing())
505             {
506                 OUString arch;
507                 auto const ok = rtl::Bootstrap::get(u"_ARCH"_ustr, arch);
508                 assert(ok); (void) ok;
509                 // getting the string for the producer
510                 OUString aProducerOverride = officecfg::Office::Common::Save::Document::GeneratorOverride::get();
511                 if (!aProducerOverride.isEmpty())
512                     aContext.DocumentInfo.Producer = aProducerOverride;
513                 else
514                     aContext.DocumentInfo.Producer =
515                         utl::ConfigManager::getProductName() +
516                         " " +
517                         utl::ConfigManager::getAboutBoxProductVersion() +
518                         " (" + arch + ")"
519 #if HAVE_FEATURE_COMMUNITY_FLAVOR
520                         " / LibreOffice Community"
521 #endif
522                         ;
523             }
524 
525             aContext.DocumentInfo.Creator = aCreator;
526 
527             OUString aSignCertificateSubjectName;
528             for ( const beans::PropertyValue& rProp : rFilterData )
529             {
530                 if ( rProp.Name == "PageRange" )
531                     rProp.Value >>= aPageRange;
532                 else if ( rProp.Name == "SheetRange" )
533                 {
534                     Reference< frame::XController > xController( Reference< frame::XModel >( mxSrcDoc, UNO_QUERY_THROW )->getCurrentController() );
535                     Reference< sheet::XSheetRange > xView( xController, UNO_QUERY);
536                     OUString aSheetRange;
537                     rProp.Value >>= aSheetRange;
538                     aSelection = xView->getSelectionFromString(aSheetRange);
539                 }
540                 else if ( rProp.Name == "Selection" )
541                     aSelection = rProp.Value;
542                 else if ( rProp.Name == "UseLosslessCompression" )
543                     rProp.Value >>= mbUseLosslessCompression;
544                 else if ( rProp.Name == "Quality" )
545                     rProp.Value >>= mnQuality;
546                 else if ( rProp.Name == "ReduceImageResolution" )
547                     rProp.Value >>= mbReduceImageResolution;
548                 else if ( rProp.Name == "IsSkipEmptyPages" )
549                     rProp.Value >>= mbSkipEmptyPages;
550                 else if ( rProp.Name == "MaxImageResolution" )
551                     rProp.Value >>= mnMaxImageResolution;
552                 else if ( rProp.Name == "UseTaggedPDF" )
553                     rProp.Value >>= bUseTaggedPDF;
554                 else if ( rProp.Name == "SelectPdfVersion" )
555                     rProp.Value >>= nPDFTypeSelection;
556                 else if ( rProp.Name == "PDFUACompliance" )
557                     rProp.Value >>= bPDFUACompliance;
558                 else if ( rProp.Name == "ExportNotes" )
559                     rProp.Value >>= bExportNotes;
560                 else if ( rProp.Name == "ExportNotesInMargin" )
561                     rProp.Value >>= bExportNotesInMargin;
562                 else if ( rProp.Name == "ExportNotesPages" )
563                     rProp.Value >>= bExportNotesPages;
564                 else if ( rProp.Name == "ExportOnlyNotesPages" )
565                     rProp.Value >>= bExportOnlyNotesPages;
566                 else if ( rProp.Name == "UseTransitionEffects" )
567                     rProp.Value >>= bUseTransitionEffects;
568                 else if ( rProp.Name == "ExportFormFields" )
569                     rProp.Value >>= bExportFormFields;
570                 else if ( rProp.Name == "FormsType" )
571                     rProp.Value >>= nFormsFormat;
572                 else if ( rProp.Name == "AllowDuplicateFieldNames" )
573                     rProp.Value >>= bAllowDuplicateFieldNames;
574                 // viewer properties
575                 else if ( rProp.Name == "HideViewerToolbar" )
576                     rProp.Value >>= bHideViewerToolbar;
577                 else if ( rProp.Name == "HideViewerMenubar" )
578                     rProp.Value >>= bHideViewerMenubar;
579                 else if ( rProp.Name == "HideViewerWindowControls" )
580                     rProp.Value >>= bHideViewerWindowControls;
581                 else if ( rProp.Name == "ResizeWindowToInitialPage" )
582                     rProp.Value >>= bFitWindow;
583                 else if ( rProp.Name == "CenterWindow" )
584                     rProp.Value >>= bCenterWindow;
585                 else if ( rProp.Name == "OpenInFullScreenMode" )
586                     rProp.Value >>= bOpenInFullScreenMode;
587                 else if ( rProp.Name == "DisplayPDFDocumentTitle" )
588                     rProp.Value >>= bDisplayPDFDocumentTitle;
589                 else if ( rProp.Name == "InitialView" )
590                     rProp.Value >>= nPDFDocumentMode;
591                 else if ( rProp.Name == "Magnification" )
592                     rProp.Value >>= nPDFDocumentAction;
593                 else if ( rProp.Name == "Zoom" )
594                     rProp.Value >>= nZoom;
595                 else if ( rProp.Name == "InitialPage" )
596                     rProp.Value >>= nInitialPage;
597                 else if ( rProp.Name == "PageLayout" )
598                     rProp.Value >>= nPDFPageLayout;
599                 else if ( rProp.Name == "FirstPageOnLeft" )
600                     rProp.Value >>= aContext.FirstPageLeft;
601                 else if ( rProp.Name == "IsAddStream" )
602                     rProp.Value >>= bAddStream;
603                 else if ( rProp.Name == "Watermark" )
604                     rProp.Value >>= msWatermark;
605                 else if ( rProp.Name == "WatermarkColor" )
606                 {
607                     sal_Int32 nColor{};
608                     if (rProp.Value >>= nColor)
609                     {
610                         maWatermarkColor = Color(ColorTransparency, nColor);
611                     }
612                 }
613                 else if (rProp.Name == "WatermarkFontHeight")
614                 {
615                     sal_Int32 nFontHeight{};
616                     if (rProp.Value >>= nFontHeight)
617                     {
618                         moWatermarkFontHeight = nFontHeight;
619                     }
620                 }
621                 else if (rProp.Name == "WatermarkRotateAngle")
622                 {
623                     sal_Int32 nRotateAngle{};
624                     if (rProp.Value >>= nRotateAngle)
625                     {
626                         moWatermarkRotateAngle = Degree10(nRotateAngle);
627                     }
628                 }
629                 else if (rProp.Name == "WatermarkFontName")
630                 {
631                     OUString aFontName{};
632                     if (rProp.Value >>= aFontName)
633                     {
634                         maWatermarkFontName = aFontName;
635                     }
636                 }
637                 else if ( rProp.Name == "TiledWatermark" )
638                     rProp.Value >>= msTiledWatermark;
639                 // now all the security related properties...
640                 else if ( rProp.Name == "EncryptFile" )
641                     rProp.Value >>= bEncrypt;
642                 else if ( rProp.Name == "DocumentOpenPassword" )
643                     rProp.Value >>= aOpenPassword;
644                 else if ( rProp.Name == "RestrictPermissions" )
645                     rProp.Value >>= bRestrictPermissions;
646                 else if ( rProp.Name == "PermissionPassword" )
647                     rProp.Value >>= aPermissionPassword;
648                 else if ( rProp.Name == "PreparedPasswords" )
649                     rProp.Value >>= xEnc;
650                 else if ( rProp.Name == "PreparedPermissionPassword" )
651                     rProp.Value >>= aPreparedPermissionPassword;
652                 else if ( rProp.Name == "Printing" )
653                     rProp.Value >>= nPrintAllowed;
654                 else if ( rProp.Name == "Changes" )
655                     rProp.Value >>= nChangesAllowed;
656                 else if ( rProp.Name == "EnableCopyingOfContent" )
657                     rProp.Value >>= bCanCopyOrExtract;
658                 else if ( rProp.Name == "EnableTextAccessForAccessibilityTools" )
659                     rProp.Value >>= bCanExtractForAccessibility;
660                 // i56629 links extra (relative links and other related stuff)
661                 else if ( rProp.Name == "ExportLinksRelativeFsys" )
662                     rProp.Value >>= bExportRelativeFsysLinks;
663                 else if ( rProp.Name == "PDFViewSelection" )
664                     rProp.Value >>= nDefaultLinkAction;
665                 else if ( rProp.Name == "ConvertOOoTargetToPDFTarget" )
666                     rProp.Value >>= bConvertOOoTargetToPDFTarget;
667                 else if ( rProp.Name == "ExportBookmarksToPDFDestination" )
668                     rProp.Value >>= bExportBmkToDest;
669                 else if ( rProp.Name == "ExportBookmarks" )
670                     rProp.Value >>= bExportBookmarks;
671                 else if ( rProp.Name == "ExportHiddenSlides" )
672                     rProp.Value >>= bExportHiddenSlides;
673                 else if ( rProp.Name == "SinglePageSheets" )
674                     rProp.Value >>= bSinglePageSheets;
675                 else if ( rProp.Name == "OpenBookmarkLevels" )
676                     rProp.Value >>= nOpenBookmarkLevels;
677                 else if ( rProp.Name == "SignPDF" )
678                     rProp.Value >>= bSignPDF;
679                 else if ( rProp.Name == "SignatureLocation" )
680                     rProp.Value >>= sSignLocation;
681                 else if ( rProp.Name == "SignatureReason" )
682                     rProp.Value >>= sSignReason;
683                 else if ( rProp.Name == "SignatureContactInfo" )
684                     rProp.Value >>= sSignContact;
685                 else if ( rProp.Name == "SignaturePassword" )
686                     rProp.Value >>= sSignPassword;
687                 else if ( rProp.Name == "SignatureCertificate" )
688                     rProp.Value >>= aSignCertificate;
689                 else if (rProp.Name == "SignCertificateSubjectName")
690                     rProp.Value >>= aSignCertificateSubjectName;
691                 else if ( rProp.Name == "SignatureTSA" )
692                     rProp.Value >>= sSignTSA;
693                 else if ( rProp.Name == "ExportPlaceholders" )
694                     rProp.Value >>= bExportPlaceholders;
695                 else if ( rProp.Name == "UseReferenceXObject" )
696                     rProp.Value >>= bUseReferenceXObject;
697                 // Redaction & bitmap related stuff
698                 else if ( rProp.Name == "IsRedactMode" )
699                     rProp.Value >>= mbIsRedactMode;
700                 // Math-specific render options
701                 else if (rProp.Name == "TitleRow")
702                     oMathTitleRow = rProp;
703                 else if (rProp.Name == "FormulaText")
704                     oMathFormulaText = rProp;
705                 else if (rProp.Name == "Border")
706                     oMathBorder = rProp;
707                 else if (rProp.Name == "PrintFormat")
708                     oMathPrintFormat = rProp;
709                 else if (rProp.Name == "PrintScale")
710                     oMathPrintScale = rProp;
711             }
712 
713             if (!aSignCertificate.is() && !aSignCertificateSubjectName.isEmpty())
714             {
715                 aSignCertificate = GetCertificateFromSubjectName(aSignCertificateSubjectName);
716             }
717 
718             aContext.URL        = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
719 
720             // set the correct version, depending on user request
721             switch( nPDFTypeSelection )
722             {
723             default:
724             case 0:
725                 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_7;
726                 break;
727             case 1:
728                 aContext.Version    = vcl::PDFWriter::PDFVersion::PDF_A_1;
729                 bUseTaggedPDF = true;           // force the tagged PDF as well
730                 mbRemoveTransparencies = true;  // does not allow transparencies
731                 bEncrypt = false;               // no encryption
732                 xEnc.clear();
733                 break;
734             case 2:
735                 aContext.Version    = vcl::PDFWriter::PDFVersion::PDF_A_2;
736                 bUseTaggedPDF = true;           // force the tagged PDF as well
737                 mbRemoveTransparencies = false; // does allow transparencies
738                 bEncrypt = false;               // no encryption
739                 xEnc.clear();
740                 break;
741             case 3:
742                 aContext.Version    = vcl::PDFWriter::PDFVersion::PDF_A_3;
743                 bUseTaggedPDF = true;           // force the tagged PDF as well
744                 mbRemoveTransparencies = false; // does allow transparencies
745                 bEncrypt = false;               // no encryption
746                 xEnc.clear();
747                 break;
748             case 15:
749                 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_5;
750                 break;
751             case 16:
752                 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_6;
753                 break;
754             case 17:
755                 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_7;
756                 break;
757             }
758 
759             // PDF/UA support
760             aContext.UniversalAccessibilityCompliance = bPDFUACompliance;
761             if (bPDFUACompliance)
762             {
763                 // ISO 14289-1:2014, Clause: 7.1
764                 bUseTaggedPDF = true;
765                 // ISO 14289-1:2014, Clause: 7.16
766                 bCanExtractForAccessibility = true;
767                 // ISO 14289-1:2014, Clause: 7.20
768                 bUseReferenceXObject = false;
769             }
770 
771             // copy in context the values default in the constructor or set by the FilterData sequence of properties
772             aContext.Tagged     = bUseTaggedPDF;
773 
774             // values used in viewer
775             aContext.HideViewerToolbar          = bHideViewerToolbar;
776             aContext.HideViewerMenubar          = bHideViewerMenubar;
777             aContext.HideViewerWindowControls   = bHideViewerWindowControls;
778             aContext.FitWindow                  = bFitWindow;
779             aContext.CenterWindow               = bCenterWindow;
780             aContext.OpenInFullScreenMode       = bOpenInFullScreenMode;
781             aContext.DisplayPDFDocumentTitle    = bDisplayPDFDocumentTitle;
782             aContext.InitialPage                = nInitialPage-1;
783             aContext.OpenBookmarkLevels         = nOpenBookmarkLevels;
784 
785             switch( nPDFDocumentMode )
786             {
787                 default:
788                 case 0:
789                     aContext.PDFDocumentMode = vcl::PDFWriter::ModeDefault;
790                     break;
791                 case 1:
792                     aContext.PDFDocumentMode = vcl::PDFWriter::UseOutlines;
793                     break;
794                 case 2:
795                     aContext.PDFDocumentMode = vcl::PDFWriter::UseThumbs;
796                     break;
797             }
798             switch( nPDFDocumentAction )
799             {
800                 default:
801                 case 0:
802                     aContext.PDFDocumentAction = vcl::PDFWriter::ActionDefault;
803                     break;
804                 case 1:
805                     aContext.PDFDocumentAction = vcl::PDFWriter::FitInWindow;
806                     break;
807                 case 2:
808                     aContext.PDFDocumentAction = vcl::PDFWriter::FitWidth;
809                     break;
810                 case 3:
811                     aContext.PDFDocumentAction = vcl::PDFWriter::FitVisible;
812                     break;
813                 case 4:
814                     aContext.PDFDocumentAction = vcl::PDFWriter::ActionZoom;
815                     aContext.Zoom = nZoom;
816                     break;
817             }
818 
819             switch( nPDFPageLayout )
820             {
821                 default:
822                 case 0:
823                     aContext.PageLayout = vcl::PDFWriter::DefaultLayout;
824                     break;
825                 case 1:
826                     aContext.PageLayout = vcl::PDFWriter::SinglePage;
827                     break;
828                 case 2:
829                     aContext.PageLayout = vcl::PDFWriter::Continuous;
830                     break;
831                 case 3:
832                     aContext.PageLayout = vcl::PDFWriter::ContinuousFacing;
833                     break;
834             }
835 
836             aContext.FirstPageLeft = false;
837 
838             // check if PDF/A, which does not allow encryption
839             if( aContext.Version != vcl::PDFWriter::PDFVersion::PDF_A_1 )
840             {
841                 // set check for permission change password
842                 // if not enabled and no permission password, force permissions to default as if PDF where without encryption
843                 if( bRestrictPermissions && (xEnc.is() || !aPermissionPassword.isEmpty()) )
844                 {
845                     bEncrypt = true; // permission set as desired, done after
846                 }
847                 else
848                 {
849                     // force permission to default
850                     nPrintAllowed                  = 2 ;
851                     nChangesAllowed                = 4 ;
852                     bCanCopyOrExtract              = true;
853                     bCanExtractForAccessibility    = true ;
854                 }
855 
856                 switch( nPrintAllowed )
857                 {
858                 case 0: // initialized when aContext is build, means no printing
859                     break;
860                 default:
861                 case 2:
862                     aContext.Encryption.CanPrintFull            = true;
863                     [[fallthrough]];
864                 case 1:
865                     aContext.Encryption.CanPrintTheDocument     = true;
866                     break;
867                 }
868 
869                 switch( nChangesAllowed )
870                 {
871                 case 0: // already in struct PDFSecPermissions CTOR
872                     break;
873                 case 1:
874                     aContext.Encryption.CanAssemble             = true;
875                     break;
876                 case 2:
877                     aContext.Encryption.CanFillInteractive      = true;
878                     break;
879                 case 3:
880                     aContext.Encryption.CanAddOrModify          = true;
881                     break;
882                 default:
883                 case 4:
884                     aContext.Encryption.CanModifyTheContent     =
885                         aContext.Encryption.CanCopyOrExtract    =
886                         aContext.Encryption.CanAddOrModify      =
887                         aContext.Encryption.CanFillInteractive  = true;
888                     break;
889                 }
890 
891                 aContext.Encryption.CanCopyOrExtract                = bCanCopyOrExtract;
892                 aContext.Encryption.CanExtractForAccessibility  = bCanExtractForAccessibility;
893                 if( bEncrypt && ! xEnc.is() )
894                     xEnc = vcl::PDFWriter::InitEncryption( aPermissionPassword, aOpenPassword );
895                 if( bEncrypt && !aPermissionPassword.isEmpty() && ! aPreparedPermissionPassword.hasElements() )
896                     aPreparedPermissionPassword = comphelper::OStorageHelper::CreatePackageEncryptionData( aPermissionPassword );
897             }
898             // after this point we don't need the legacy clear passwords anymore
899             // however they are still inside the passed filter data sequence
900             // which is sadly out of our control
901             aPermissionPassword.clear();
902             aOpenPassword.clear();
903 
904             /*
905             * FIXME: the entries are only implicitly defined by the resource file. Should there
906             * ever be an additional form submit format this could get invalid.
907             */
908             switch( nFormsFormat )
909             {
910                 case 1:
911                     aContext.SubmitFormat = vcl::PDFWriter::PDF;
912                     break;
913                 case 2:
914                     aContext.SubmitFormat = vcl::PDFWriter::HTML;
915                     break;
916                 case 3:
917                     aContext.SubmitFormat = vcl::PDFWriter::XML;
918                     break;
919                 default:
920                 case 0:
921                     aContext.SubmitFormat = vcl::PDFWriter::FDF;
922                     break;
923             }
924             aContext.AllowDuplicateFieldNames = bAllowDuplicateFieldNames;
925 
926             // get model
927             Reference< frame::XModel > xModel( mxSrcDoc, UNO_QUERY );
928             {
929                 // #i56629: Relative link stuff
930                 // set the base URL of the file: then base URL
931                 aContext.BaseURL = xModel->getURL();
932                 // relative link option is private to PDF Export filter and limited to local filesystem only
933                 aContext.RelFsys = bExportRelativeFsysLinks;
934                 // determine the default action for PDF links
935                 switch( nDefaultLinkAction )
936                 {
937                 default:
938                     // default: URI, without fragment conversion (the bookmark in PDF may not work)
939                 case 0:
940                     aContext.DefaultLinkAction = vcl::PDFWriter::URIAction;
941                     break;
942                 case 1:
943                     // view PDF through the reader application
944                     aContext.ForcePDFAction = true;
945                     aContext.DefaultLinkAction = vcl::PDFWriter::LaunchAction;
946                     break;
947                 case 2:
948                     // view PDF through an Internet browser
949                     aContext.DefaultLinkAction = vcl::PDFWriter::URIActionDestination;
950                     break;
951                 }
952                 aContext.ConvertOOoTargetToPDFTarget = bConvertOOoTargetToPDFTarget;
953 
954                 // check for Link Launch action, not allowed on PDF/A-1
955                 // this code chunk checks when the filter is called from scripting
956                 if( aContext.Version == vcl::PDFWriter::PDFVersion::PDF_A_1 &&
957                     aContext.DefaultLinkAction == vcl::PDFWriter::LaunchAction )
958                 {
959                     // force the similar allowed URI action
960                     aContext.DefaultLinkAction = vcl::PDFWriter::URIActionDestination;
961                     // and remove the remote goto action forced on PDF file
962                     aContext.ForcePDFAction = false;
963                 }
964             }
965 
966             aContext.SignPDF = bSignPDF;
967             aContext.SignLocation = sSignLocation;
968             aContext.SignContact = sSignContact;
969             aContext.SignReason = sSignReason;
970             aContext.SignPassword = sSignPassword;
971             aContext.SignCertificate = aSignCertificate;
972             aContext.SignTSA = sSignTSA;
973             aContext.UseReferenceXObject = bUseReferenceXObject;
974 
975             // all context data set, time to create the printing device
976             vcl::PDFWriter aPDFWriter( aContext, xEnc );
977             OutputDevice*  pOut = aPDFWriter.GetReferenceDevice();
978 
979             DBG_ASSERT( pOut, "PDFExport::Export: no reference device" );
980             xDevice->SetOutputDevice(pOut);
981 
982             if( bAddStream )
983             {
984                 // export stream
985                 // get mimetype
986                 OUString aSrcMimetype = getMimetypeForDocument( mxContext, mxSrcDoc );
987                 OUString aExt;
988                 if (aSrcMimetype == "application/vnd.oasis.opendocument.text")
989                     aExt = ".odt";
990                 else if (aSrcMimetype == "application/vnd.oasis.opendocument.presentation")
991                     aExt = ".odp";
992                 else if (aSrcMimetype == "application/vnd.oasis.opendocument.spreadsheet")
993                     aExt = ".ods";
994                 else if (aSrcMimetype == "application/vnd.oasis.opendocument.graphics")
995                     aExt = ".odg";
996                 std::unique_ptr<vcl::PDFOutputStream> pStream(new PDFExportStreamDoc(mxSrcDoc, aPreparedPermissionPassword));
997                 aPDFWriter.AddAttachedFile("Original" + aExt, aSrcMimetype, u"Embedded original document of this PDF file"_ustr, std::move(pStream));
998             }
999 
1000             if ( pOut )
1001             {
1002                 DBG_ASSERT( pOut->GetExtOutDevData() == nullptr, "PDFExport: ExtOutDevData already set!!!" );
1003                 vcl::PDFExtOutDevData aPDFExtOutDevData( *pOut );
1004                 pOut->SetExtOutDevData( &aPDFExtOutDevData );
1005                 aPDFExtOutDevData.SetIsExportNotes( bExportNotes );
1006                 aPDFExtOutDevData.SetIsExportNotesInMargin( bExportNotesInMargin );
1007                 aPDFExtOutDevData.SetIsExportTaggedPDF( bUseTaggedPDF );
1008                 aPDFExtOutDevData.SetIsExportTransitionEffects( bUseTransitionEffects );
1009                 aPDFExtOutDevData.SetIsExportFormFields( bExportFormFields );
1010                 aPDFExtOutDevData.SetIsExportBookmarks( bExportBookmarks );
1011                 aPDFExtOutDevData.SetIsExportHiddenSlides( bExportHiddenSlides );
1012                 aPDFExtOutDevData.SetIsSinglePageSheets( bSinglePageSheets );
1013                 aPDFExtOutDevData.SetIsLosslessCompression( mbUseLosslessCompression );
1014                 aPDFExtOutDevData.SetCompressionQuality( mnQuality );
1015                 aPDFExtOutDevData.SetIsReduceImageResolution( mbReduceImageResolution );
1016                 aPDFExtOutDevData.SetIsExportNamedDestinations( bExportBmkToDest );
1017 
1018                 std::vector<PropertyValue> aRenderOptionsVector{
1019                     comphelper::makePropertyValue(u"RenderDevice"_ustr, uno::Reference<awt::XDevice>(xDevice)),
1020                     comphelper::makePropertyValue(u"ExportNotesPages"_ustr, false),
1021                     comphelper::makePropertyValue(u"IsFirstPage"_ustr, true),
1022                     comphelper::makePropertyValue(u"IsLastPage"_ustr, false),
1023                     comphelper::makePropertyValue(u"IsSkipEmptyPages"_ustr, mbSkipEmptyPages),
1024                     comphelper::makePropertyValue(u"PageRange"_ustr, aPageRange),
1025                     comphelper::makePropertyValue(u"ExportPlaceholders"_ustr, bExportPlaceholders),
1026                     comphelper::makePropertyValue(u"SinglePageSheets"_ustr, bSinglePageSheets),
1027                     comphelper::makePropertyValue(u"ExportNotesInMargin"_ustr, bExportNotesInMargin)
1028                 };
1029                 if (oMathTitleRow)
1030                     aRenderOptionsVector.push_back(*oMathTitleRow);
1031                 if (oMathFormulaText)
1032                     aRenderOptionsVector.push_back(*oMathFormulaText);
1033                 if (oMathBorder)
1034                     aRenderOptionsVector.push_back(*oMathBorder);
1035                 if (oMathPrintFormat)
1036                     aRenderOptionsVector.push_back(*oMathPrintFormat);
1037                 if (oMathPrintScale)
1038                     aRenderOptionsVector.push_back(*oMathPrintScale);
1039                 Sequence aRenderOptions = comphelper::containerToSequence(aRenderOptionsVector);
1040                 Any& rExportNotesValue = aRenderOptions.getArray()[ 1 ].Value;
1041 
1042                 if( !aPageRange.isEmpty() || !aSelection.hasValue() )
1043                 {
1044                     aSelection = Any();
1045                     aSelection <<= mxSrcDoc;
1046                 }
1047                 bool bReChangeToNormalView = false;
1048                 static constexpr OUString sShowOnlineLayout( u"ShowOnlineLayout"_ustr );
1049                 bool bReHideWhitespace = false;
1050                 static constexpr OUString sHideWhitespace(u"HideWhitespace"_ustr);
1051                 uno::Reference< beans::XPropertySet > xViewProperties;
1052 
1053                 if ( aCreator == "Writer" )
1054                 {
1055                     // #i92835: if Writer is in web layout mode this has to be switched to normal view and back to web view in the end
1056                     try
1057                     {
1058                         Reference< view::XViewSettingsSupplier > xVSettingsSupplier( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
1059                         xViewProperties = xVSettingsSupplier->getViewSettings();
1060                         xViewProperties->getPropertyValue( sShowOnlineLayout ) >>= bReChangeToNormalView;
1061                         if( bReChangeToNormalView )
1062                         {
1063                             xViewProperties->setPropertyValue( sShowOnlineLayout, uno::Any( false ) );
1064                         }
1065 
1066                         // Also, disable hide-whitespace during export.
1067                         xViewProperties->getPropertyValue(sHideWhitespace) >>= bReHideWhitespace;
1068                         if (bReHideWhitespace)
1069                         {
1070                             xViewProperties->setPropertyValue(sHideWhitespace, uno::Any(false));
1071                         }
1072                     }
1073                     catch( const uno::Exception& )
1074                     {
1075                     }
1076 
1077                 }
1078 
1079                 const sal_Int32 nPageCount = xRenderable->getRendererCount( aSelection, aRenderOptions );
1080 
1081                 if ( bExportNotesPages && aCreator == "Impress" )
1082                 {
1083                     uno::Reference< drawing::XShapes > xShapes;     // do not allow to export notes when exporting a selection
1084                     if ( aSelection >>= xShapes )
1085                         bExportNotesPages = false;
1086                 }
1087                 else
1088                     bExportNotesPages = false;
1089                 const bool bExportPages = !bExportNotesPages || !bExportOnlyNotesPages;
1090 
1091                 if( aPageRange.isEmpty() || bSinglePageSheets)
1092                 {
1093                     aPageRange = OUString::number( 1 ) + "-" + OUString::number(nPageCount );
1094                 }
1095                 StringRangeEnumerator aRangeEnum( aPageRange, 0, nPageCount-1 );
1096 
1097                 if ( mxStatusIndicator.is() )
1098                 {
1099                     sal_Int32 nTotalPageCount = aRangeEnum.size();
1100                     if ( bExportPages && bExportNotesPages )
1101                         nTotalPageCount *= 2;
1102                     mxStatusIndicator->start(FilterResId(PDF_PROGRESS_BAR), nTotalPageCount);
1103                 }
1104 
1105                 bRet = nPageCount > 0;
1106 
1107                 if ( bRet && bExportPages )
1108                     bRet = ExportSelection( aPDFWriter, xRenderable, aSelection, aRangeEnum, aRenderOptions, nPageCount );
1109 
1110                 if ( bRet && bExportNotesPages )
1111                 {
1112                     rExportNotesValue <<= true;
1113                     bRet = ExportSelection( aPDFWriter, xRenderable, aSelection, aRangeEnum, aRenderOptions, nPageCount );
1114                 }
1115                 if ( mxStatusIndicator.is() )
1116                     mxStatusIndicator->end();
1117 
1118                 // if during the export the doc locale was set copy it to PDF writer
1119                 const css::lang::Locale& rLoc( aPDFExtOutDevData.GetDocumentLocale() );
1120                 if( !rLoc.Language.isEmpty() )
1121                     aPDFWriter.SetDocumentLocale( rLoc );
1122 
1123                 if( bRet )
1124                 {
1125                     aPDFExtOutDevData.PlayGlobalActions( aPDFWriter );
1126                     bRet = aPDFWriter.Emit();
1127                     aErrors = aPDFWriter.GetErrors();
1128                 }
1129                 pOut->SetExtOutDevData( nullptr );
1130                 if( bReChangeToNormalView )
1131                 {
1132                     try
1133                     {
1134                         xViewProperties->setPropertyValue( sShowOnlineLayout, uno::Any( true ) );
1135                     }
1136                     catch( const uno::Exception& )
1137                     {
1138                     }
1139                 }
1140                 if( bReHideWhitespace )
1141                 {
1142                     try
1143                     {
1144                         xViewProperties->setPropertyValue( sHideWhitespace, uno::Any( true ) );
1145                     }
1146                     catch( const uno::Exception& )
1147                     {
1148                     }
1149                 }
1150             }
1151         }
1152     }
1153 
1154     // show eventual errors during export
1155     showErrors( aErrors );
1156 
1157     return bRet;
1158 }
1159 
1160 
1161 namespace
1162 {
1163 
1164 typedef comphelper::WeakComponentImplHelper< task::XInteractionRequest > PDFErrorRequestBase;
1165 
1166 class PDFErrorRequest : public PDFErrorRequestBase
1167 {
1168     task::PDFExportException maExc;
1169 public:
1170     explicit PDFErrorRequest( task::PDFExportException aExc );
1171 
1172     // XInteractionRequest
1173     virtual uno::Any SAL_CALL getRequest() override;
1174     virtual uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL getContinuations() override;
1175 };
1176 
1177 
PDFErrorRequest(task::PDFExportException aExc)1178 PDFErrorRequest::PDFErrorRequest( task::PDFExportException aExc ) :
1179     maExc(std::move( aExc ))
1180 {
1181 }
1182 
1183 
getRequest()1184 uno::Any SAL_CALL PDFErrorRequest::getRequest()
1185 {
1186     std::unique_lock guard( m_aMutex );
1187 
1188     uno::Any aRet;
1189     aRet <<= maExc;
1190     return aRet;
1191 }
1192 
1193 
getContinuations()1194 uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL PDFErrorRequest::getContinuations()
1195 {
1196     return uno::Sequence< uno::Reference< task::XInteractionContinuation > >();
1197 }
1198 
1199 } // end anonymous namespace
1200 
1201 
showErrors(const std::set<vcl::PDFWriter::ErrorCode> & rErrors)1202 void PDFExport::showErrors( const std::set< vcl::PDFWriter::ErrorCode >& rErrors )
1203 {
1204     if( ! rErrors.empty() && mxIH.is() )
1205     {
1206         task::PDFExportException aExc;
1207         aExc.ErrorCodes = comphelper::containerToSequence<sal_Int32>( rErrors );
1208         Reference< task::XInteractionRequest > xReq( new PDFErrorRequest( std::move(aExc) ) );
1209         mxIH->handle( xReq );
1210     }
1211 }
1212 
1213 
ImplExportPage(vcl::PDFWriter & rWriter,vcl::PDFExtOutDevData & rPDFExtOutDevData,const GDIMetaFile & rMtf)1214 void PDFExport::ImplExportPage( vcl::PDFWriter& rWriter, vcl::PDFExtOutDevData& rPDFExtOutDevData, const GDIMetaFile& rMtf )
1215 {
1216     //Rectangle(Point, Size) creates a rectangle off by 1, use Rectangle(long, long, long, long) instead
1217     basegfx::B2DPolygon aSize(tools::Polygon(tools::Rectangle(0, 0, rMtf.GetPrefSize().Width(), rMtf.GetPrefSize().Height())).getB2DPolygon());
1218     basegfx::B2DPolygon aSizePDF(OutputDevice::LogicToLogic(aSize, rMtf.GetPrefMapMode(), MapMode(MapUnit::MapPoint)));
1219     basegfx::B2DRange aRangePDF(aSizePDF.getB2DRange());
1220     tools::Rectangle       aPageRect( Point(), rMtf.GetPrefSize() );
1221 
1222     rWriter.NewPage( aRangePDF.getWidth(), aRangePDF.getHeight() );
1223     rWriter.SetMapMode( rMtf.GetPrefMapMode() );
1224 
1225     vcl::PDFWriter::PlayMetafileContext aCtx;
1226     GDIMetaFile aMtf;
1227     if( mbRemoveTransparencies )
1228     {
1229         aCtx.m_bTransparenciesWereRemoved = rWriter.GetReferenceDevice()->
1230             RemoveTransparenciesFromMetaFile( rMtf, aMtf, mnMaxImageResolution, mnMaxImageResolution,
1231                                               false, true, mbReduceImageResolution );
1232         // tdf#134736 if the metafile was replaced then rPDFExtOutDevData's PageSyncData mActions
1233         // all still point to MetaAction indexes in the original metafile that are now invalid.
1234         // Throw them all away in the absence of a way to reposition them to new positions of
1235         // their replacements.
1236         if (aCtx.m_bTransparenciesWereRemoved)
1237             rPDFExtOutDevData.ResetSyncData(&rWriter);
1238     }
1239     else
1240     {
1241         aMtf = rMtf;
1242     }
1243     aCtx.m_nMaxImageResolution      = mbReduceImageResolution ? mnMaxImageResolution : 0;
1244     aCtx.m_bOnlyLosslessCompression = mbUseLosslessCompression;
1245     aCtx.m_nJPEGQuality             = mnQuality;
1246 
1247 
1248     rWriter.SetClipRegion( basegfx::B2DPolyPolygon(
1249         basegfx::utils::createPolygonFromRect( vcl::unotools::b2DRectangleFromRectangle(aPageRect) ) ) );
1250 
1251     rWriter.PlayMetafile( aMtf, aCtx, &rPDFExtOutDevData );
1252 
1253     rPDFExtOutDevData.ResetSyncData(nullptr);
1254 
1255     if (!msWatermark.isEmpty())
1256     {
1257         ImplWriteWatermark( rWriter, Size(aRangePDF.getWidth(), aRangePDF.getHeight()) );
1258     }
1259     else if (!msTiledWatermark.isEmpty())
1260     {
1261         ImplWriteTiledWatermark( rWriter, Size(aRangePDF.getWidth(), aRangePDF.getHeight()) );
1262     }
1263 }
1264 
1265 
ImplWriteWatermark(vcl::PDFWriter & rWriter,const Size & rPageSize)1266 void PDFExport::ImplWriteWatermark( vcl::PDFWriter& rWriter, const Size& rPageSize )
1267 {
1268     vcl::Font aFont( maWatermarkFontName, Size( 0, moWatermarkFontHeight ? *moWatermarkFontHeight : 3*rPageSize.Height()/4 ) );
1269     aFont.SetItalic( ITALIC_NONE );
1270     aFont.SetWidthType( WIDTH_NORMAL );
1271     aFont.SetWeight( WEIGHT_NORMAL );
1272     aFont.SetAlignment( ALIGN_BOTTOM );
1273     tools::Long nTextWidth = rPageSize.Width();
1274     if( rPageSize.Width() < rPageSize.Height() )
1275     {
1276         nTextWidth = rPageSize.Height();
1277         aFont.SetOrientation( 2700_deg10 );
1278     }
1279 
1280     if (moWatermarkRotateAngle)
1281     {
1282         aFont.SetOrientation(*moWatermarkRotateAngle);
1283         if (rPageSize.Width() < rPageSize.Height())
1284         {
1285             // Set text width based on the shorter side, so rotation can't push text outside the
1286             // page boundaries.
1287             nTextWidth = rPageSize.Width();
1288         }
1289     }
1290 
1291     // adjust font height for text to fit
1292     OutputDevice* pDev = rWriter.GetReferenceDevice();
1293     pDev->Push();
1294     pDev->SetFont( aFont );
1295     pDev->SetMapMode( MapMode( MapUnit::MapPoint ) );
1296     int w = 0;
1297     if (moWatermarkFontHeight)
1298     {
1299         w = pDev->GetTextWidth(msWatermark);
1300     }
1301     else
1302     {
1303         while( ( w = pDev->GetTextWidth( msWatermark ) ) > nTextWidth )
1304         {
1305             if (w == 0)
1306                 break;
1307             tools::Long nNewHeight = aFont.GetFontHeight() * nTextWidth / w;
1308             if( nNewHeight == aFont.GetFontHeight() )
1309             {
1310                 nNewHeight--;
1311                 if( nNewHeight <= 0 )
1312                     break;
1313             }
1314             aFont.SetFontHeight( nNewHeight );
1315             pDev->SetFont( aFont );
1316         }
1317     }
1318     tools::Long nTextHeight = pDev->GetTextHeight();
1319     // leave some maneuvering room for rounding issues, also
1320     // some fonts go a little outside ascent/descent
1321     nTextHeight += nTextHeight/20;
1322     pDev->Pop();
1323 
1324     rWriter.Push();
1325     // tdf#152235 tag around the reference to the XObject on the page
1326     sal_Int32 const id = rWriter.EnsureStructureElement();
1327     rWriter.InitStructureElement(id, vcl::PDFWriter::NonStructElement, ::std::u16string_view());
1328     rWriter.BeginStructureElement(id);
1329     rWriter.SetStructureAttribute(vcl::PDFWriter::Type, vcl::PDFWriter::Pagination);
1330     rWriter.SetStructureAttribute(vcl::PDFWriter::Subtype, vcl::PDFWriter::Watermark);
1331     // HACK: this should produce *nothing* itself but is necessary to output
1332     // the Artifact tag here, not inside the XObject
1333     rWriter.DrawPolyLine({});
1334     rWriter.SetMapMode( MapMode( MapUnit::MapPoint ) );
1335     rWriter.SetFont( aFont );
1336     rWriter.SetTextColor(maWatermarkColor);
1337     Point aTextPoint;
1338     tools::Rectangle aTextRect;
1339     if( rPageSize.Width() > rPageSize.Height() )
1340     {
1341         aTextPoint = Point( (rPageSize.Width()-w)/2,
1342                             rPageSize.Height()-(rPageSize.Height()-nTextHeight)/2 );
1343         aTextRect = tools::Rectangle( Point( (rPageSize.Width()-w)/2,
1344                                       (rPageSize.Height()-nTextHeight)/2 ),
1345                                Size( w, nTextHeight ) );
1346     }
1347     else
1348     {
1349         aTextPoint = Point( (rPageSize.Width()-nTextHeight)/2,
1350                             (rPageSize.Height()-w)/2 );
1351         aTextRect = tools::Rectangle( aTextPoint, Size( nTextHeight, w ) );
1352     }
1353 
1354     if (moWatermarkRotateAngle)
1355     {
1356         // First set the text's starting point to the center of the page.
1357         tools::Rectangle aPageRectangle(Point(0, 0), rPageSize);
1358         aTextPoint = aPageRectangle.Center();
1359         // Then adjust it so that the text remains centered, based on the rotation angle.
1360         basegfx::B2DPolygon aTextPolygon
1361             = basegfx::utils::createPolygonFromRect(basegfx::B2DRectangle(0, -nTextHeight, w, 0));
1362         basegfx::B2DHomMatrix aMatrix;
1363         aMatrix.rotate(-1 * toRadians(*moWatermarkRotateAngle));
1364         aTextPolygon.transform(aMatrix);
1365         basegfx::B2DPoint aPolygonCenter = aTextPolygon.getB2DRange().getCenter();
1366         aTextPoint.AdjustX(-aPolygonCenter.getX());
1367         aTextPoint.AdjustY(-aPolygonCenter.getY());
1368 
1369         aTextRect = aPageRectangle;
1370     }
1371 
1372     rWriter.SetClipRegion();
1373     rWriter.BeginTransparencyGroup();
1374     rWriter.DrawText( aTextPoint, msWatermark );
1375     rWriter.EndTransparencyGroup( aTextRect, 50 );
1376     rWriter.EndStructureElement();
1377     rWriter.Pop();
1378 }
1379 
ImplWriteTiledWatermark(vcl::PDFWriter & rWriter,const Size & rPageSize)1380 void PDFExport::ImplWriteTiledWatermark( vcl::PDFWriter& rWriter, const Size& rPageSize )
1381 {
1382     OUString watermark = msTiledWatermark;
1383     // Maximum number of characters in one line.
1384     // it is set to 21 to make it look like tiled watermarks as online in secure view
1385     const int lineLength = 21;
1386     vcl::Font aFont( u"Liberation Sans"_ustr, Size( 0, 40 ) );
1387     aFont.SetItalic( ITALIC_NONE );
1388     aFont.SetWidthType( WIDTH_NORMAL );
1389     aFont.SetWeight( WEIGHT_NORMAL );
1390     aFont.SetAlignment( ALIGN_BOTTOM );
1391     aFont.SetFontHeight(40);
1392     aFont.SetOrientation(450_deg10);
1393 
1394     OutputDevice* pDev = rWriter.GetReferenceDevice();
1395     pDev->SetFont(aFont);
1396     pDev->Push();
1397     pDev->SetFont(aFont);
1398     pDev->SetMapMode( MapMode( MapUnit::MapPoint ) );
1399     int w = 0;
1400     int watermarkcount = ((rPageSize.Width()) / 200)+1;
1401     tools::Long nTextWidth = rPageSize.Width() / (watermarkcount*1.5);
1402     OUString oneLineText = watermark;
1403 
1404     if(watermark.getLength() > lineLength)
1405         oneLineText = watermark.copy(0, lineLength);
1406 
1407     while((w = pDev->GetTextWidth(oneLineText)) > nTextWidth)
1408     {
1409         if(w==0)
1410             break;
1411 
1412         tools::Long nNewHeight = aFont.GetFontHeight() * nTextWidth / w;
1413         aFont.SetFontHeight(nNewHeight);
1414         pDev->SetFont( aFont );
1415     }
1416     // maximum number of watermark count for the width
1417     if(watermarkcount > 8)
1418         watermarkcount = 8;
1419 
1420     pDev->Pop();
1421 
1422     rWriter.Push();
1423     // tdf#152235 tag around the reference to the XObject on the page
1424     sal_Int32 const id = rWriter.EnsureStructureElement();
1425     rWriter.InitStructureElement(id, vcl::PDFWriter::NonStructElement, ::std::u16string_view());
1426     rWriter.BeginStructureElement(id);
1427     rWriter.SetStructureAttribute(vcl::PDFWriter::Type, vcl::PDFWriter::Pagination);
1428     rWriter.SetStructureAttribute(vcl::PDFWriter::Subtype, vcl::PDFWriter::Watermark);
1429     // HACK: this should produce *nothing* itself but is necessary to output
1430     // the Artifact tag here, not inside the XObject
1431     rWriter.DrawPolyLine({});
1432     rWriter.SetMapMode( MapMode( MapUnit::MapPoint ) );
1433     rWriter.SetFont(aFont);
1434     rWriter.SetTextColor( Color(19,20,22) );
1435     // center watermarks horizontally
1436     Point aTextPoint( (rPageSize.Width()/2) - (((nTextWidth*watermarkcount)+(watermarkcount-1)*nTextWidth)/2),
1437                       pDev->GetTextHeight());
1438 
1439     for( int i = 0; i < watermarkcount; i ++)
1440     {
1441         while(aTextPoint.getY()+pDev->GetTextHeight()*3 <= rPageSize.Height())
1442         {
1443             tools::Rectangle aTextRect(aTextPoint, Size(nTextWidth*2,pDev->GetTextHeight()*4));
1444 
1445             pDev->Push();
1446             rWriter.SetClipRegion();
1447             rWriter.BeginTransparencyGroup();
1448             rWriter.SetTextColor( Color(19,20,22) );
1449             rWriter.DrawText(aTextRect, watermark, DrawTextFlags::MultiLine|DrawTextFlags::Center|DrawTextFlags::VCenter|DrawTextFlags::WordBreak|DrawTextFlags::Bottom);
1450             rWriter.EndTransparencyGroup( aTextRect, 50 );
1451             pDev->Pop();
1452 
1453             pDev->Push();
1454             rWriter.SetClipRegion();
1455             rWriter.BeginTransparencyGroup();
1456             rWriter.SetTextColor( Color(236,235,233) );
1457             rWriter.DrawText(aTextRect, watermark, DrawTextFlags::MultiLine|DrawTextFlags::Center|DrawTextFlags::VCenter|DrawTextFlags::WordBreak|DrawTextFlags::Bottom);
1458             rWriter.EndTransparencyGroup( aTextRect, 50 );
1459             pDev->Pop();
1460 
1461             aTextPoint.Move(0, pDev->GetTextHeight()*3);
1462         }
1463         aTextPoint=Point( aTextPoint.getX(), pDev->GetTextHeight() );
1464         aTextPoint.Move( nTextWidth*1.5, 0 );
1465     }
1466 
1467     rWriter.EndStructureElement();
1468     rWriter.Pop();
1469 }
1470 
1471 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1472