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