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