tdf#62728 add PDF/A-2 support, change UI default to use that
[LibreOffice.git] / filter / source / pdf / pdfexport.cxx
blob2ca3829db36a94b54b0e7fbe64b04190ff640d60
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/fract.hxx>
25 #include <tools/poly.hxx>
26 #include <unotools/resmgr.hxx>
27 #include <vcl/mapmod.hxx>
28 #include <vcl/virdev.hxx>
29 #include <vcl/metaact.hxx>
30 #include <vcl/gdimtf.hxx>
31 #include <vcl/jobset.hxx>
32 #include <vcl/bitmapaccess.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/FilterConfigItem.hxx>
35 #include <vcl/graphicfilter.hxx>
36 #include <vcl/settings.hxx>
37 #include <vcl/graphictools.hxx>
38 #include <svl/solar.hrc>
39 #include <comphelper/sequence.hxx>
40 #include <comphelper/string.hxx>
41 #include <comphelper/storagehelper.hxx>
42 #include <basegfx/polygon/b2dpolygon.hxx>
43 #include <basegfx/polygon/b2dpolypolygon.hxx>
44 #include <basegfx/polygon/b2dpolygontools.hxx>
45 #include <toolkit/awt/vclxdevice.hxx>
46 #include <unotools/streamwrap.hxx>
47 #include <unotools/saveopt.hxx>
48 #include <unotools/configmgr.hxx>
49 #include <cppuhelper/exc_hlp.hxx>
50 #include <cppuhelper/compbase.hxx>
51 #include <cppuhelper/basemutex.hxx>
53 #include "pdfexport.hxx"
54 #include "impdialog.hxx"
55 #include <strings.hrc>
57 #include <com/sun/star/beans/XPropertySet.hpp>
58 #include <com/sun/star/configuration/theDefaultProvider.hpp>
59 #include <com/sun/star/awt/Rectangle.hpp>
60 #include <com/sun/star/awt/XDevice.hpp>
61 #include <com/sun/star/util/MeasureUnit.hpp>
62 #include <com/sun/star/frame/XModel.hpp>
63 #include <com/sun/star/frame/ModuleManager.hpp>
64 #include <com/sun/star/frame/XStorable.hpp>
65 #include <com/sun/star/document/XDocumentProperties.hpp>
66 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
67 #include <com/sun/star/container/XNameAccess.hpp>
68 #include <com/sun/star/view/XViewSettingsSupplier.hpp>
69 #include <com/sun/star/task/XInteractionRequest.hpp>
70 #include <com/sun/star/task/PDFExportException.hpp>
71 #include <com/sun/star/io/IOException.hpp>
72 #include <com/sun/star/io/XSeekable.hpp>
73 #include <com/sun/star/lang/XServiceInfo.hpp>
74 #include <com/sun/star/drawing/XShapes.hpp>
75 #include <com/sun/star/graphic/XGraphicProvider.hpp>
76 #include <com/sun/star/security/XCertificate.hpp>
77 #include <com/sun/star/beans/XMaterialHolder.hpp>
79 #include <memory>
81 using namespace ::com::sun::star;
82 using namespace ::com::sun::star::uno;
83 using namespace ::com::sun::star::lang;
84 using namespace ::com::sun::star::beans;
85 using namespace ::com::sun::star::view;
86 using namespace ::com::sun::star::graphic;
89 PDFExport::PDFExport( const Reference< XComponent >& rxSrcDoc,
90 const Reference< task::XStatusIndicator >& rxStatusIndicator,
91 const Reference< task::XInteractionHandler >& rxIH,
92 const Reference< XComponentContext >& xContext ) :
93 mxSrcDoc ( rxSrcDoc ),
94 mxContext ( xContext ),
95 mxStatusIndicator ( rxStatusIndicator ),
96 mxIH ( rxIH ),
97 mbUseTaggedPDF ( false ),
98 mnPDFTypeSelection ( 0 ),
99 mbExportNotes ( true ),
100 mbExportPlaceholders ( false ),
101 mbUseReferenceXObject ( false ),
102 mbExportNotesPages ( false ),
103 mbExportOnlyNotesPages ( false ),
104 mbUseTransitionEffects ( true ),
105 mbExportBookmarks ( true ),
106 mbExportHiddenSlides ( false ),
107 mnOpenBookmarkLevels ( -1 ),
108 mbUseLosslessCompression ( false ),
109 mbReduceImageResolution ( true ),
110 mbSkipEmptyPages ( true ),
111 mbAddStream ( false ),
112 mnMaxImageResolution ( 300 ),
113 mnQuality ( 90 ),
114 mnFormsFormat ( 0 ),
115 mbExportFormFields ( true ),
116 mbAllowDuplicateFieldNames ( false ),
117 mnProgressValue ( 0 ),
118 mbRemoveTransparencies ( false ),
120 mbIsRedactMode ( false ),
122 mbHideViewerToolbar ( false ),
123 mbHideViewerMenubar ( false ),
124 mbHideViewerWindowControls ( false ),
125 mbFitWindow ( false ),
126 mbCenterWindow ( false ),
127 mbOpenInFullScreenMode ( false ),
128 mbDisplayPDFDocumentTitle ( true ),
129 mnPDFDocumentMode ( 0 ),
130 mnPDFDocumentAction ( 0 ),
131 mnZoom ( 100 ),
132 mnInitialPage ( 1 ),
133 mnPDFPageLayout ( 0 ),
135 mbEncrypt ( false ),
136 mbRestrictPermissions ( false ),
137 mnPrintAllowed ( 2 ),
138 mnChangesAllowed ( 4 ),
139 mbCanCopyOrExtract ( true ),
140 mbCanExtractForAccessibility( true ),
142 // #i56629
143 mbExportRelativeFsysLinks ( false ),
144 mnDefaultLinkAction ( 0 ),
145 mbConvertOOoTargetToPDFTarget( false ),
146 mbExportBmkToDest ( false ),
147 mbSignPDF ( false )
152 PDFExport::~PDFExport()
157 bool PDFExport::ExportSelection( vcl::PDFWriter& rPDFWriter,
158 Reference< css::view::XRenderable > const & rRenderable,
159 const Any& rSelection,
160 const StringRangeEnumerator& rRangeEnum,
161 Sequence< PropertyValue >& rRenderOptions,
162 sal_Int32 nPageCount )
164 bool bRet = false;
167 Any* pFirstPage = nullptr;
168 Any* pLastPage = nullptr;
170 bool bExportNotesPages = false;
172 for( sal_Int32 nData = 0, nDataCount = rRenderOptions.getLength(); nData < nDataCount; ++nData )
174 if ( rRenderOptions[ nData ].Name == "IsFirstPage" )
175 pFirstPage = &rRenderOptions[ nData ].Value;
176 else if ( rRenderOptions[ nData ].Name == "IsLastPage" )
177 pLastPage = &rRenderOptions[ nData ].Value;
178 else if ( rRenderOptions[ nData ].Name == "ExportNotesPages" )
179 rRenderOptions[ nData ].Value >>= bExportNotesPages;
182 OutputDevice* pOut = rPDFWriter.GetReferenceDevice();
184 if( pOut )
186 if ( nPageCount )
188 vcl::PDFExtOutDevData& rPDFExtOutDevData = dynamic_cast<vcl::PDFExtOutDevData&>(*pOut->GetExtOutDevData());
189 rPDFExtOutDevData.SetIsExportNotesPages( bExportNotesPages );
191 sal_Int32 nCurrentPage(0);
192 StringRangeEnumerator::Iterator aIter = rRangeEnum.begin();
193 StringRangeEnumerator::Iterator aEnd = rRangeEnum.end();
194 while ( aIter != aEnd )
196 Sequence< PropertyValue > aRenderer( rRenderable->getRenderer( *aIter, rSelection, rRenderOptions ) );
197 awt::Size aPageSize;
199 for( sal_Int32 nProperty = 0, nPropertyCount = aRenderer.getLength(); nProperty < nPropertyCount; ++nProperty )
201 if ( aRenderer[ nProperty ].Name == "PageSize" )
202 aRenderer[ nProperty].Value >>= aPageSize;
205 rPDFExtOutDevData.SetCurrentPageNumber( nCurrentPage );
207 GDIMetaFile aMtf;
208 const MapMode aMapMode( MapUnit::Map100thMM );
209 const Size aMtfSize( aPageSize.Width, aPageSize.Height );
211 pOut->Push();
212 pOut->EnableOutput( false );
213 pOut->SetMapMode( aMapMode );
215 aMtf.SetPrefSize( aMtfSize );
216 aMtf.SetPrefMapMode( aMapMode );
217 aMtf.Record( pOut );
219 // #i35176#
220 // IsLastPage property.
221 const sal_Int32 nCurrentRenderer = *aIter;
222 ++aIter;
223 if ( pLastPage && aIter == aEnd )
224 *pLastPage <<= true;
226 rRenderable->render( nCurrentRenderer, rSelection, rRenderOptions );
228 aMtf.Stop();
229 aMtf.WindStart();
231 if( aMtf.GetActionSize() &&
232 ( !mbSkipEmptyPages || aPageSize.Width || aPageSize.Height ) )
234 // We convert the whole metafile into a bitmap to get rid of the
235 // text covered by redaction shapes
236 if (mbIsRedactMode)
240 Graphic aGraph(aMtf);
241 BitmapEx bmp = aGraph.GetBitmapEx();
242 Graphic bgraph(bmp);
243 aMtf = bgraph.GetGDIMetaFile();
245 catch(const Exception& e)
247 SAL_WARN("filter.pdf", "Something went wrong while converting metafile to bitmap. Exception: "
248 << e.Message);
252 ImplExportPage(rPDFWriter, rPDFExtOutDevData, aMtf);
253 bRet = true;
256 pOut->Pop();
258 if ( mxStatusIndicator.is() )
259 mxStatusIndicator->setValue( mnProgressValue );
260 if ( pFirstPage )
261 *pFirstPage <<= false;
263 ++mnProgressValue;
264 ++nCurrentPage;
267 else
269 bRet = true; // #i18334# nPageCount == 0,
270 rPDFWriter.NewPage( 10000, 10000 ); // creating dummy page
271 rPDFWriter.SetMapMode(MapMode(MapUnit::Map100thMM));
275 catch(const RuntimeException &)
278 return bRet;
281 class PDFExportStreamDoc : public vcl::PDFOutputStream
283 private:
285 Reference< XComponent > m_xSrcDoc;
286 Sequence< beans::NamedValue > m_aPreparedPassword;
288 public:
290 PDFExportStreamDoc( const Reference< XComponent >& xDoc, const Sequence<beans::NamedValue>& rPwd )
291 : m_xSrcDoc( xDoc ),
292 m_aPreparedPassword( rPwd )
295 virtual void write( const Reference< XOutputStream >& xStream ) override;
298 void PDFExportStreamDoc::write( const Reference< XOutputStream >& xStream )
300 Reference< css::frame::XStorable > xStore( m_xSrcDoc, UNO_QUERY );
301 if( xStore.is() )
303 Sequence< beans::PropertyValue > aArgs( 2 + ((m_aPreparedPassword.getLength() > 0) ? 1 : 0) );
304 aArgs.getArray()[0].Name = "FilterName";
305 aArgs.getArray()[1].Name = "OutputStream";
306 aArgs.getArray()[1].Value <<= xStream;
307 if( m_aPreparedPassword.getLength() )
309 aArgs.getArray()[2].Name = "EncryptionData";
310 aArgs.getArray()[2].Value <<= m_aPreparedPassword;
315 xStore->storeToURL( "private:stream", aArgs );
317 catch( const IOException& )
324 static OUString getMimetypeForDocument( const Reference< XComponentContext >& xContext,
325 const Reference< XComponent >& xDoc ) throw()
327 OUString aDocMimetype;
330 // get document service name
331 Reference< css::frame::XStorable > xStore( xDoc, UNO_QUERY );
332 Reference< frame::XModuleManager2 > xModuleManager = frame::ModuleManager::create(xContext);
333 if( xStore.is() )
335 OUString aDocServiceName = xModuleManager->identify( Reference< XInterface >( xStore, uno::UNO_QUERY ) );
336 if ( !aDocServiceName.isEmpty() )
338 // get the actual filter name
339 OUString aFilterName;
340 Reference< lang::XMultiServiceFactory > xConfigProvider =
341 configuration::theDefaultProvider::get( xContext );
342 uno::Sequence< uno::Any > aArgs( 1 );
343 beans::NamedValue aPathProp;
344 aPathProp.Name = "nodepath";
345 aPathProp.Value <<= OUString( "/org.openoffice.Setup/Office/Factories/" );
346 aArgs[0] <<= aPathProp;
348 Reference< container::XNameAccess > xSOFConfig(
349 xConfigProvider->createInstanceWithArguments(
350 "com.sun.star.configuration.ConfigurationAccess", aArgs ),
351 uno::UNO_QUERY );
353 Reference< container::XNameAccess > xApplConfig;
354 xSOFConfig->getByName( aDocServiceName ) >>= xApplConfig;
355 if ( xApplConfig.is() )
357 xApplConfig->getByName( "ooSetupFactoryActualFilter" ) >>= aFilterName;
358 if( !aFilterName.isEmpty() )
360 // find the related type name
361 OUString aTypeName;
362 Reference< container::XNameAccess > xFilterFactory(
363 xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", xContext),
364 uno::UNO_QUERY );
366 Sequence< beans::PropertyValue > aFilterData;
367 xFilterFactory->getByName( aFilterName ) >>= aFilterData;
368 for ( sal_Int32 nInd = 0; nInd < aFilterData.getLength(); nInd++ )
369 if ( aFilterData[nInd].Name == "Type" )
370 aFilterData[nInd].Value >>= aTypeName;
372 if ( !aTypeName.isEmpty() )
374 // find the mediatype
375 Reference< container::XNameAccess > xTypeDetection(
376 xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", xContext),
377 UNO_QUERY );
379 Sequence< beans::PropertyValue > aTypeData;
380 xTypeDetection->getByName( aTypeName ) >>= aTypeData;
381 for ( sal_Int32 nInd = 0; nInd < aTypeData.getLength(); nInd++ )
382 if ( aTypeData[nInd].Name == "MediaType" )
383 aTypeData[nInd].Value >>= aDocMimetype;
390 catch (...)
393 return aDocMimetype;
397 bool PDFExport::Export( const OUString& rFile, const Sequence< PropertyValue >& rFilterData )
399 INetURLObject aURL( rFile );
400 bool bRet = false;
402 std::set< vcl::PDFWriter::ErrorCode > aErrors;
404 if( aURL.GetProtocol() != INetProtocol::File )
406 OUString aTmp;
408 if( osl::FileBase::getFileURLFromSystemPath( rFile, aTmp ) == osl::FileBase::E_None )
409 aURL = INetURLObject(aTmp);
412 if( aURL.GetProtocol() == INetProtocol::File )
414 Reference< XRenderable > xRenderable( mxSrcDoc, UNO_QUERY );
416 if( xRenderable.is() )
418 rtl::Reference<VCLXDevice> xDevice(new VCLXDevice);
419 OUString aPageRange;
420 Any aSelection;
421 vcl::PDFWriter::PDFWriterContext aContext;
422 OUString aOpenPassword, aPermissionPassword;
423 Reference< beans::XMaterialHolder > xEnc;
424 Sequence< beans::NamedValue > aPreparedPermissionPassword;
427 // getting the string for the creator
428 OUString aCreator;
429 Reference< XServiceInfo > xInfo( mxSrcDoc, UNO_QUERY );
430 if ( xInfo.is() )
432 if ( xInfo->supportsService( "com.sun.star.presentation.PresentationDocument" ) )
433 aCreator += "Impress";
434 else if ( xInfo->supportsService( "com.sun.star.drawing.DrawingDocument" ) )
435 aCreator += "Draw";
436 else if ( xInfo->supportsService( "com.sun.star.text.TextDocument" ) )
437 aCreator += "Writer";
438 else if ( xInfo->supportsService( "com.sun.star.sheet.SpreadsheetDocument" ) )
439 aCreator += "Calc";
440 else if ( xInfo->supportsService( "com.sun.star.formula.FormulaProperties" ) )
441 aCreator += "Math";
444 Reference< document::XDocumentPropertiesSupplier > xDocumentPropsSupplier( mxSrcDoc, UNO_QUERY );
445 if ( xDocumentPropsSupplier.is() )
447 Reference< document::XDocumentProperties > xDocumentProps( xDocumentPropsSupplier->getDocumentProperties() );
448 if ( xDocumentProps.is() )
450 aContext.DocumentInfo.Title = xDocumentProps->getTitle();
451 aContext.DocumentInfo.Author = xDocumentProps->getAuthor();
452 aContext.DocumentInfo.Subject = xDocumentProps->getSubject();
453 aContext.DocumentInfo.Keywords = ::comphelper::string::convertCommaSeparated(xDocumentProps->getKeywords());
456 // getting the string for the producer
457 aContext.DocumentInfo.Producer =
458 utl::ConfigManager::getProductName() +
459 " " +
460 utl::ConfigManager::getProductVersion();
461 aContext.DocumentInfo.Creator = aCreator;
463 for( sal_Int32 nData = 0, nDataCount = rFilterData.getLength(); nData < nDataCount; ++nData )
465 if ( rFilterData[ nData ].Name == "PageRange" )
466 rFilterData[ nData ].Value >>= aPageRange;
467 else if ( rFilterData[ nData ].Name == "Selection" )
468 aSelection = rFilterData[ nData ].Value;
469 else if ( rFilterData[ nData ].Name == "UseLosslessCompression" )
470 rFilterData[ nData ].Value >>= mbUseLosslessCompression;
471 else if ( rFilterData[ nData ].Name == "Quality" )
472 rFilterData[ nData ].Value >>= mnQuality;
473 else if ( rFilterData[ nData ].Name == "ReduceImageResolution" )
474 rFilterData[ nData ].Value >>= mbReduceImageResolution;
475 else if ( rFilterData[ nData ].Name == "IsSkipEmptyPages" )
476 rFilterData[ nData ].Value >>= mbSkipEmptyPages;
477 else if ( rFilterData[ nData ].Name == "MaxImageResolution" )
478 rFilterData[ nData ].Value >>= mnMaxImageResolution;
479 else if ( rFilterData[ nData ].Name == "UseTaggedPDF" )
480 rFilterData[ nData ].Value >>= mbUseTaggedPDF;
481 else if ( rFilterData[ nData ].Name == "SelectPdfVersion" )
482 rFilterData[ nData ].Value >>= mnPDFTypeSelection;
483 else if ( rFilterData[ nData ].Name == "ExportNotes" )
484 rFilterData[ nData ].Value >>= mbExportNotes;
485 else if ( rFilterData[ nData ].Name == "ExportNotesPages" )
486 rFilterData[ nData ].Value >>= mbExportNotesPages;
487 else if ( rFilterData[ nData ].Name == "ExportOnlyNotesPages" )
488 rFilterData[ nData ].Value >>= mbExportOnlyNotesPages;
489 else if ( rFilterData[ nData ].Name == "UseTransitionEffects" )
490 rFilterData[ nData ].Value >>= mbUseTransitionEffects;
491 else if ( rFilterData[ nData ].Name == "ExportFormFields" )
492 rFilterData[ nData ].Value >>= mbExportFormFields;
493 else if ( rFilterData[ nData ].Name == "FormsType" )
494 rFilterData[ nData ].Value >>= mnFormsFormat;
495 else if ( rFilterData[ nData ].Name == "AllowDuplicateFieldNames" )
496 rFilterData[ nData ].Value >>= mbAllowDuplicateFieldNames;
497 // viewer properties
498 else if ( rFilterData[ nData ].Name == "HideViewerToolbar" )
499 rFilterData[ nData ].Value >>= mbHideViewerToolbar;
500 else if ( rFilterData[ nData ].Name == "HideViewerMenubar" )
501 rFilterData[ nData ].Value >>= mbHideViewerMenubar;
502 else if ( rFilterData[ nData ].Name == "HideViewerWindowControls" )
503 rFilterData[ nData ].Value >>= mbHideViewerWindowControls;
504 else if ( rFilterData[ nData ].Name == "ResizeWindowToInitialPage" )
505 rFilterData[ nData ].Value >>= mbFitWindow;
506 else if ( rFilterData[ nData ].Name == "CenterWindow" )
507 rFilterData[ nData ].Value >>= mbCenterWindow;
508 else if ( rFilterData[ nData ].Name == "OpenInFullScreenMode" )
509 rFilterData[ nData ].Value >>= mbOpenInFullScreenMode;
510 else if ( rFilterData[ nData ].Name == "DisplayPDFDocumentTitle" )
511 rFilterData[ nData ].Value >>= mbDisplayPDFDocumentTitle;
512 else if ( rFilterData[ nData ].Name == "InitialView" )
513 rFilterData[ nData ].Value >>= mnPDFDocumentMode;
514 else if ( rFilterData[ nData ].Name == "Magnification" )
515 rFilterData[ nData ].Value >>= mnPDFDocumentAction;
516 else if ( rFilterData[ nData ].Name == "Zoom" )
517 rFilterData[ nData ].Value >>= mnZoom;
518 else if ( rFilterData[ nData ].Name == "InitialPage" )
519 rFilterData[ nData ].Value >>= mnInitialPage;
520 else if ( rFilterData[ nData ].Name == "PageLayout" )
521 rFilterData[ nData ].Value >>= mnPDFPageLayout;
522 else if ( rFilterData[ nData ].Name == "FirstPageOnLeft" )
523 rFilterData[ nData ].Value >>= aContext.FirstPageLeft;
524 else if ( rFilterData[ nData ].Name == "IsAddStream" )
525 rFilterData[ nData ].Value >>= mbAddStream;
526 else if ( rFilterData[ nData ].Name == "Watermark" )
527 rFilterData[ nData ].Value >>= msWatermark;
528 // now all the security related properties...
529 else if ( rFilterData[ nData ].Name == "EncryptFile" )
530 rFilterData[ nData ].Value >>= mbEncrypt;
531 else if ( rFilterData[ nData ].Name == "DocumentOpenPassword" )
532 rFilterData[ nData ].Value >>= aOpenPassword;
533 else if ( rFilterData[ nData ].Name == "RestrictPermissions" )
534 rFilterData[ nData ].Value >>= mbRestrictPermissions;
535 else if ( rFilterData[ nData ].Name == "PermissionPassword" )
536 rFilterData[ nData ].Value >>= aPermissionPassword;
537 else if ( rFilterData[ nData ].Name == "PreparedPasswords" )
538 rFilterData[ nData ].Value >>= xEnc;
539 else if ( rFilterData[ nData ].Name == "PreparedPermissionPassword" )
540 rFilterData[ nData ].Value >>= aPreparedPermissionPassword;
541 else if ( rFilterData[ nData ].Name == "Printing" )
542 rFilterData[ nData ].Value >>= mnPrintAllowed;
543 else if ( rFilterData[ nData ].Name == "Changes" )
544 rFilterData[ nData ].Value >>= mnChangesAllowed;
545 else if ( rFilterData[ nData ].Name == "EnableCopyingOfContent" )
546 rFilterData[ nData ].Value >>= mbCanCopyOrExtract;
547 else if ( rFilterData[ nData ].Name == "EnableTextAccessForAccessibilityTools" )
548 rFilterData[ nData ].Value >>= mbCanExtractForAccessibility;
549 // i56629 links extra (relative links and other related stuff)
550 else if ( rFilterData[ nData ].Name == "ExportLinksRelativeFsys" )
551 rFilterData[ nData ].Value >>= mbExportRelativeFsysLinks;
552 else if ( rFilterData[ nData ].Name == "PDFViewSelection" )
553 rFilterData[ nData ].Value >>= mnDefaultLinkAction;
554 else if ( rFilterData[ nData ].Name == "ConvertOOoTargetToPDFTarget" )
555 rFilterData[ nData ].Value >>= mbConvertOOoTargetToPDFTarget;
556 else if ( rFilterData[ nData ].Name == "ExportBookmarksToPDFDestination" )
557 rFilterData[ nData ].Value >>= mbExportBmkToDest;
558 else if ( rFilterData[ nData ].Name == "ExportBookmarks" )
559 rFilterData[ nData ].Value >>= mbExportBookmarks;
560 else if ( rFilterData[ nData ].Name == "ExportHiddenSlides" )
561 rFilterData[ nData ].Value >>= mbExportHiddenSlides;
562 else if ( rFilterData[ nData ].Name == "OpenBookmarkLevels" )
563 rFilterData[ nData ].Value >>= mnOpenBookmarkLevels;
564 else if ( rFilterData[ nData ].Name == "SignPDF" )
565 rFilterData[ nData ].Value >>= mbSignPDF;
566 else if ( rFilterData[ nData ].Name == "SignatureLocation" )
567 rFilterData[ nData ].Value >>= msSignLocation;
568 else if ( rFilterData[ nData ].Name == "SignatureReason" )
569 rFilterData[ nData ].Value >>= msSignReason;
570 else if ( rFilterData[ nData ].Name == "SignatureContactInfo" )
571 rFilterData[ nData ].Value >>= msSignContact;
572 else if ( rFilterData[ nData ].Name == "SignaturePassword" )
573 rFilterData[ nData ].Value >>= msSignPassword;
574 else if ( rFilterData[ nData ].Name == "SignatureCertificate" )
575 rFilterData[ nData ].Value >>= maSignCertificate;
576 else if ( rFilterData[ nData ].Name == "SignatureTSA" )
577 rFilterData[ nData ].Value >>= msSignTSA;
578 else if ( rFilterData[ nData ].Name == "ExportPlaceholders" )
579 rFilterData[ nData ].Value >>= mbExportPlaceholders;
580 else if ( rFilterData[ nData ].Name == "UseReferenceXObject" )
581 rFilterData[ nData ].Value >>= mbUseReferenceXObject;
582 // Redaction & bitmap related stuff
583 else if ( rFilterData[ nData ].Name == "IsRedactMode" )
584 rFilterData[ nData ].Value >>= mbIsRedactMode;
587 aContext.URL = aURL.GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
589 // set the correct version, depending on user request
590 switch( mnPDFTypeSelection )
592 default:
593 case 0:
594 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_5;
595 break;
596 case 1:
597 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_1;
598 mbUseTaggedPDF = true; // force the tagged PDF as well
599 mbExportFormFields = false; // force disabling of form conversion
600 mbRemoveTransparencies = true; // PDF/A does not allow transparencies
601 mbEncrypt = false; // no encryption
602 xEnc.clear();
603 break;
604 case 2:
605 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_A_2;
606 mbUseTaggedPDF = true; // force the tagged PDF as well
607 mbRemoveTransparencies = false; // PDF/A-2 does allow transparencies
608 mbEncrypt = false; // no encryption
609 xEnc.clear();
610 break;
611 case 16:
612 aContext.Version = vcl::PDFWriter::PDFVersion::PDF_1_6;
613 break;
616 // copy in context the values default in the constructor or set by the FilterData sequence of properties
617 aContext.Tagged = mbUseTaggedPDF;
619 // values used in viewer
620 aContext.HideViewerToolbar = mbHideViewerToolbar;
621 aContext.HideViewerMenubar = mbHideViewerMenubar;
622 aContext.HideViewerWindowControls = mbHideViewerWindowControls;
623 aContext.FitWindow = mbFitWindow;
624 aContext.CenterWindow = mbCenterWindow;
625 aContext.OpenInFullScreenMode = mbOpenInFullScreenMode;
626 aContext.DisplayPDFDocumentTitle = mbDisplayPDFDocumentTitle;
627 aContext.InitialPage = mnInitialPage-1;
628 aContext.OpenBookmarkLevels = mnOpenBookmarkLevels;
630 switch( mnPDFDocumentMode )
632 default:
633 case 0:
634 aContext.PDFDocumentMode = vcl::PDFWriter::ModeDefault;
635 break;
636 case 1:
637 aContext.PDFDocumentMode = vcl::PDFWriter::UseOutlines;
638 break;
639 case 2:
640 aContext.PDFDocumentMode = vcl::PDFWriter::UseThumbs;
641 break;
643 switch( mnPDFDocumentAction )
645 default:
646 case 0:
647 aContext.PDFDocumentAction = vcl::PDFWriter::ActionDefault;
648 break;
649 case 1:
650 aContext.PDFDocumentAction = vcl::PDFWriter::FitInWindow;
651 break;
652 case 2:
653 aContext.PDFDocumentAction = vcl::PDFWriter::FitWidth;
654 break;
655 case 3:
656 aContext.PDFDocumentAction = vcl::PDFWriter::FitVisible;
657 break;
658 case 4:
659 aContext.PDFDocumentAction = vcl::PDFWriter::ActionZoom;
660 aContext.Zoom = mnZoom;
661 break;
664 switch( mnPDFPageLayout )
666 default:
667 case 0:
668 aContext.PageLayout = vcl::PDFWriter::DefaultLayout;
669 break;
670 case 1:
671 aContext.PageLayout = vcl::PDFWriter::SinglePage;
672 break;
673 case 2:
674 aContext.PageLayout = vcl::PDFWriter::Continuous;
675 break;
676 case 3:
677 aContext.PageLayout = vcl::PDFWriter::ContinuousFacing;
678 break;
681 aContext.FirstPageLeft = false;
683 // check if PDF/A, which does not allow encryption
684 if( aContext.Version != vcl::PDFWriter::PDFVersion::PDF_A_1 )
686 // set check for permission change password
687 // if not enabled and no permission password, force permissions to default as if PDF where without encryption
688 if( mbRestrictPermissions && (xEnc.is() || !aPermissionPassword.isEmpty()) )
690 mbEncrypt = true; // permission set as desired, done after
692 else
694 // force permission to default
695 mnPrintAllowed = 2 ;
696 mnChangesAllowed = 4 ;
697 mbCanCopyOrExtract = true;
698 mbCanExtractForAccessibility = true ;
701 switch( mnPrintAllowed )
703 case 0: // initialized when aContext is build, means no printing
704 break;
705 default:
706 case 2:
707 aContext.Encryption.CanPrintFull = true;
708 [[fallthrough]];
709 case 1:
710 aContext.Encryption.CanPrintTheDocument = true;
711 break;
714 switch( mnChangesAllowed )
716 case 0: // already in struct PDFSecPermissions CTOR
717 break;
718 case 1:
719 aContext.Encryption.CanAssemble = true;
720 break;
721 case 2:
722 aContext.Encryption.CanFillInteractive = true;
723 break;
724 case 3:
725 aContext.Encryption.CanAddOrModify = true;
726 break;
727 default:
728 case 4:
729 aContext.Encryption.CanModifyTheContent =
730 aContext.Encryption.CanCopyOrExtract =
731 aContext.Encryption.CanAddOrModify =
732 aContext.Encryption.CanFillInteractive = true;
733 break;
736 aContext.Encryption.CanCopyOrExtract = mbCanCopyOrExtract;
737 aContext.Encryption.CanExtractForAccessibility = mbCanExtractForAccessibility;
738 if( mbEncrypt && ! xEnc.is() )
739 xEnc = vcl::PDFWriter::InitEncryption( aPermissionPassword, aOpenPassword );
740 if( mbEncrypt && !aPermissionPassword.isEmpty() && ! aPreparedPermissionPassword.getLength() )
741 aPreparedPermissionPassword = comphelper::OStorageHelper::CreatePackageEncryptionData( aPermissionPassword );
743 // after this point we don't need the legacy clear passwords anymore
744 // however they are still inside the passed filter data sequence
745 // which is sadly out of our control
746 aPermissionPassword.clear();
747 aOpenPassword.clear();
750 * FIXME: the entries are only implicitly defined by the resource file. Should there
751 * ever be an additional form submit format this could get invalid.
753 switch( mnFormsFormat )
755 case 1:
756 aContext.SubmitFormat = vcl::PDFWriter::PDF;
757 break;
758 case 2:
759 aContext.SubmitFormat = vcl::PDFWriter::HTML;
760 break;
761 case 3:
762 aContext.SubmitFormat = vcl::PDFWriter::XML;
763 break;
764 default:
765 case 0:
766 aContext.SubmitFormat = vcl::PDFWriter::FDF;
767 break;
769 aContext.AllowDuplicateFieldNames = mbAllowDuplicateFieldNames;
771 // get model
772 Reference< frame::XModel > xModel( mxSrcDoc, UNO_QUERY );
774 // #i56629: Relative link stuff
775 // set the base URL of the file: then base URL
776 aContext.BaseURL = xModel->getURL();
777 // relative link option is private to PDF Export filter and limited to local filesystem only
778 aContext.RelFsys = mbExportRelativeFsysLinks;
779 // determine the default acton for PDF links
780 switch( mnDefaultLinkAction )
782 default:
783 // default: URI, without fragment conversion (the bookmark in PDF may not work)
784 case 0:
785 aContext.DefaultLinkAction = vcl::PDFWriter::URIAction;
786 break;
787 case 1:
788 // view PDF through the reader application
789 aContext.ForcePDFAction = true;
790 aContext.DefaultLinkAction = vcl::PDFWriter::LaunchAction;
791 break;
792 case 2:
793 // view PDF through an Internet browser
794 aContext.DefaultLinkAction = vcl::PDFWriter::URIActionDestination;
795 break;
797 aContext.ConvertOOoTargetToPDFTarget = mbConvertOOoTargetToPDFTarget;
799 // check for Link Launch action, not allowed on PDF/A-1
800 // this code chunk checks when the filter is called from scripting
801 if( aContext.Version == vcl::PDFWriter::PDFVersion::PDF_A_1 &&
802 aContext.DefaultLinkAction == vcl::PDFWriter::LaunchAction )
804 // force the similar allowed URI action
805 aContext.DefaultLinkAction = vcl::PDFWriter::URIActionDestination;
806 // and remove the remote goto action forced on PDF file
807 aContext.ForcePDFAction = false;
811 aContext.SignPDF = mbSignPDF;
812 aContext.SignLocation = msSignLocation;
813 aContext.SignContact = msSignContact;
814 aContext.SignReason = msSignReason;
815 aContext.SignPassword = msSignPassword;
816 aContext.SignCertificate = maSignCertificate;
817 aContext.SignTSA = msSignTSA;
818 aContext.UseReferenceXObject = mbUseReferenceXObject;
820 // all context data set, time to create the printing device
821 std::unique_ptr<vcl::PDFWriter> pPDFWriter(new vcl::PDFWriter( aContext, xEnc ));
822 OutputDevice* pOut = pPDFWriter->GetReferenceDevice();
824 DBG_ASSERT( pOut, "PDFExport::Export: no reference device" );
825 xDevice->SetOutputDevice(pOut);
827 if( mbAddStream )
829 // export stream
830 // get mimetype
831 OUString aSrcMimetype = getMimetypeForDocument( mxContext, mxSrcDoc );
832 pPDFWriter->AddStream( aSrcMimetype,
833 new PDFExportStreamDoc( mxSrcDoc, aPreparedPermissionPassword )
837 if ( pOut )
839 DBG_ASSERT( pOut->GetExtOutDevData() == nullptr, "PDFExport: ExtOutDevData already set!!!" );
840 std::unique_ptr<vcl::PDFExtOutDevData> pPDFExtOutDevData(new vcl::PDFExtOutDevData( *pOut ));
841 pOut->SetExtOutDevData( pPDFExtOutDevData.get() );
842 pPDFExtOutDevData->SetIsExportNotes( mbExportNotes );
843 pPDFExtOutDevData->SetIsExportTaggedPDF( mbUseTaggedPDF );
844 pPDFExtOutDevData->SetIsExportTransitionEffects( mbUseTransitionEffects );
845 pPDFExtOutDevData->SetIsExportFormFields( mbExportFormFields );
846 pPDFExtOutDevData->SetIsExportBookmarks( mbExportBookmarks );
847 pPDFExtOutDevData->SetIsExportHiddenSlides( mbExportHiddenSlides );
848 pPDFExtOutDevData->SetIsLosslessCompression( mbUseLosslessCompression );
849 pPDFExtOutDevData->SetCompressionQuality( mnQuality );
850 pPDFExtOutDevData->SetIsReduceImageResolution( mbReduceImageResolution );
851 pPDFExtOutDevData->SetIsExportNamedDestinations( mbExportBmkToDest );
853 Sequence< PropertyValue > aRenderOptions( 7 );
854 aRenderOptions[ 0 ].Name = "RenderDevice";
855 aRenderOptions[ 0 ].Value <<= uno::Reference<awt::XDevice>(xDevice.get());
856 aRenderOptions[ 1 ].Name = "ExportNotesPages";
857 aRenderOptions[ 1 ].Value <<= false;
858 Any& rExportNotesValue = aRenderOptions[ 1 ].Value;
859 aRenderOptions[ 2 ].Name = "IsFirstPage";
860 aRenderOptions[ 2 ].Value <<= true;
861 aRenderOptions[ 3 ].Name = "IsLastPage";
862 aRenderOptions[ 3 ].Value <<= false;
863 aRenderOptions[ 4 ].Name = "IsSkipEmptyPages";
864 aRenderOptions[ 4 ].Value <<= mbSkipEmptyPages;
865 aRenderOptions[ 5 ].Name = "PageRange";
866 aRenderOptions[ 5 ].Value <<= aPageRange;
867 aRenderOptions[ 6 ].Name = "ExportPlaceholders";
868 aRenderOptions[ 6 ].Value <<= mbExportPlaceholders;
870 if( !aPageRange.isEmpty() || !aSelection.hasValue() )
872 aSelection = Any();
873 aSelection <<= mxSrcDoc;
875 bool bExportNotesPages = false;
876 bool bReChangeToNormalView = false;
877 const OUString sShowOnlineLayout( "ShowOnlineLayout" );
878 bool bReHideWhitespace = false;
879 const OUString sHideWhitespace("HideWhitespace");
880 uno::Reference< beans::XPropertySet > xViewProperties;
882 if ( aCreator == "Writer" )
884 // #i92835: if Writer is in web layout mode this has to be switched to normal view and back to web view in the end
887 Reference< view::XViewSettingsSupplier > xVSettingsSupplier( xModel->getCurrentController(), uno::UNO_QUERY_THROW );
888 xViewProperties = xVSettingsSupplier->getViewSettings();
889 xViewProperties->getPropertyValue( sShowOnlineLayout ) >>= bReChangeToNormalView;
890 if( bReChangeToNormalView )
892 xViewProperties->setPropertyValue( sShowOnlineLayout, uno::makeAny( false ) );
895 // Also, disable hide-whitespace during export.
896 xViewProperties->getPropertyValue(sHideWhitespace) >>= bReHideWhitespace;
897 if (bReHideWhitespace)
899 xViewProperties->setPropertyValue(sHideWhitespace, uno::makeAny(false));
902 catch( const uno::Exception& )
908 const sal_Int32 nPageCount = xRenderable->getRendererCount( aSelection, aRenderOptions );
910 if ( mbExportNotesPages && aCreator == "Impress" )
912 uno::Reference< drawing::XShapes > xShapes; // do not allow to export notes when exporting a selection
913 if ( ! ( aSelection >>= xShapes ) )
914 bExportNotesPages = true;
916 const bool bExportPages = !bExportNotesPages || !mbExportOnlyNotesPages;
918 if( aPageRange.isEmpty() )
920 aPageRange = OUString::number( 1 ) + "-" + OUString::number(nPageCount );
922 StringRangeEnumerator aRangeEnum( aPageRange, 0, nPageCount-1 );
924 if ( mxStatusIndicator.is() )
926 std::locale loc(Translate::Create("flt"));
927 sal_Int32 nTotalPageCount = aRangeEnum.size();
928 if ( bExportPages && bExportNotesPages )
929 nTotalPageCount *= 2;
930 mxStatusIndicator->start(Translate::get(PDF_PROGRESS_BAR, loc), nTotalPageCount);
933 bRet = nPageCount > 0;
935 if ( bRet && bExportPages )
936 bRet = ExportSelection( *pPDFWriter, xRenderable, aSelection, aRangeEnum, aRenderOptions, nPageCount );
938 if ( bRet && bExportNotesPages )
940 rExportNotesValue <<= true;
941 bRet = ExportSelection( *pPDFWriter, xRenderable, aSelection, aRangeEnum, aRenderOptions, nPageCount );
943 if ( mxStatusIndicator.is() )
944 mxStatusIndicator->end();
946 // if during the export the doc locale was set copy it to PDF writer
947 const css::lang::Locale& rLoc( pPDFExtOutDevData->GetDocumentLocale() );
948 if( !rLoc.Language.isEmpty() )
949 pPDFWriter->SetDocumentLocale( rLoc );
951 if( bRet )
953 pPDFExtOutDevData->PlayGlobalActions( *pPDFWriter );
954 bRet = pPDFWriter->Emit();
955 aErrors = pPDFWriter->GetErrors();
957 pOut->SetExtOutDevData( nullptr );
958 if( bReChangeToNormalView )
962 xViewProperties->setPropertyValue( sShowOnlineLayout, uno::makeAny( true ) );
964 catch( const uno::Exception& )
968 if( bReHideWhitespace )
972 xViewProperties->setPropertyValue( sHideWhitespace, uno::makeAny( true ) );
974 catch( const uno::Exception& )
982 // show eventual errors during export
983 showErrors( aErrors );
985 return bRet;
989 namespace
992 typedef cppu::WeakComponentImplHelper< task::XInteractionRequest > PDFErrorRequestBase;
994 class PDFErrorRequest : private cppu::BaseMutex,
995 public PDFErrorRequestBase
997 task::PDFExportException maExc;
998 public:
999 explicit PDFErrorRequest( const task::PDFExportException& i_rExc );
1001 // XInteractionRequest
1002 virtual uno::Any SAL_CALL getRequest() override;
1003 virtual uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL getContinuations() override;
1007 PDFErrorRequest::PDFErrorRequest( const task::PDFExportException& i_rExc ) :
1008 PDFErrorRequestBase( m_aMutex ),
1009 maExc( i_rExc )
1014 uno::Any SAL_CALL PDFErrorRequest::getRequest()
1016 osl::MutexGuard const guard( m_aMutex );
1018 uno::Any aRet;
1019 aRet <<= maExc;
1020 return aRet;
1024 uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL PDFErrorRequest::getContinuations()
1026 return uno::Sequence< uno::Reference< task::XInteractionContinuation > >();
1029 } // end anonymous namespace
1032 void PDFExport::showErrors( const std::set< vcl::PDFWriter::ErrorCode >& rErrors )
1034 if( ! rErrors.empty() && mxIH.is() )
1036 task::PDFExportException aExc;
1037 aExc.ErrorCodes = comphelper::containerToSequence<sal_Int32>( rErrors );
1038 Reference< task::XInteractionRequest > xReq( new PDFErrorRequest( aExc ) );
1039 mxIH->handle( xReq );
1044 void PDFExport::ImplExportPage( vcl::PDFWriter& rWriter, vcl::PDFExtOutDevData& rPDFExtOutDevData, const GDIMetaFile& rMtf )
1046 //Rectangle(Point, Size) creates a rectangle off by 1, use Rectangle(long, long, long, long) instead
1047 basegfx::B2DPolygon aSize(tools::Polygon(tools::Rectangle(0, 0, rMtf.GetPrefSize().Width(), rMtf.GetPrefSize().Height())).getB2DPolygon());
1048 basegfx::B2DPolygon aSizePDF(OutputDevice::LogicToLogic(aSize, rMtf.GetPrefMapMode(), MapMode(MapUnit::MapPoint)));
1049 basegfx::B2DRange aRangePDF(aSizePDF.getB2DRange());
1050 tools::Rectangle aPageRect( Point(), rMtf.GetPrefSize() );
1052 rWriter.NewPage( aRangePDF.getWidth(), aRangePDF.getHeight() );
1053 rWriter.SetMapMode( rMtf.GetPrefMapMode() );
1055 vcl::PDFWriter::PlayMetafileContext aCtx;
1056 GDIMetaFile aMtf;
1057 if( mbRemoveTransparencies )
1059 aCtx.m_bTransparenciesWereRemoved = rWriter.GetReferenceDevice()->
1060 RemoveTransparenciesFromMetaFile( rMtf, aMtf, mnMaxImageResolution, mnMaxImageResolution,
1061 false, true, mbReduceImageResolution );
1063 else
1065 aMtf = rMtf;
1067 aCtx.m_nMaxImageResolution = mbReduceImageResolution ? mnMaxImageResolution : 0;
1068 aCtx.m_bOnlyLosslessCompression = mbUseLosslessCompression;
1069 aCtx.m_nJPEGQuality = mnQuality;
1072 basegfx::B2DRectangle aB2DRect( aPageRect.Left(), aPageRect.Top(), aPageRect.Right(), aPageRect.Bottom() );
1073 rWriter.SetClipRegion( basegfx::B2DPolyPolygon( basegfx::utils::createPolygonFromRect( aB2DRect ) ) );
1075 rWriter.PlayMetafile( aMtf, aCtx, &rPDFExtOutDevData );
1077 rPDFExtOutDevData.ResetSyncData();
1079 if (!msWatermark.isEmpty())
1080 ImplWriteWatermark( rWriter, Size(aRangePDF.getWidth(), aRangePDF.getHeight()) );
1084 void PDFExport::ImplWriteWatermark( vcl::PDFWriter& rWriter, const Size& rPageSize )
1086 vcl::Font aFont( OUString( "Helvetica" ), Size( 0, 3*rPageSize.Height()/4 ) );
1087 aFont.SetItalic( ITALIC_NONE );
1088 aFont.SetWidthType( WIDTH_NORMAL );
1089 aFont.SetWeight( WEIGHT_NORMAL );
1090 aFont.SetAlignment( ALIGN_BOTTOM );
1091 long nTextWidth = rPageSize.Width();
1092 if( rPageSize.Width() < rPageSize.Height() )
1094 nTextWidth = rPageSize.Height();
1095 aFont.SetOrientation( 2700 );
1098 // adjust font height for text to fit
1099 OutputDevice* pDev = rWriter.GetReferenceDevice();
1100 pDev->Push();
1101 pDev->SetFont( aFont );
1102 pDev->SetMapMode( MapMode( MapUnit::MapPoint ) );
1103 int w = 0;
1104 while( ( w = pDev->GetTextWidth( msWatermark ) ) > nTextWidth )
1106 if (w == 0)
1107 break;
1108 long nNewHeight = aFont.GetFontHeight() * nTextWidth / w;
1109 if( nNewHeight == aFont.GetFontHeight() )
1111 nNewHeight--;
1112 if( nNewHeight <= 0 )
1113 break;
1115 aFont.SetFontHeight( nNewHeight );
1116 pDev->SetFont( aFont );
1118 long nTextHeight = pDev->GetTextHeight();
1119 // leave some maneuvering room for rounding issues, also
1120 // some fonts go a little outside ascent/descent
1121 nTextHeight += nTextHeight/20;
1122 pDev->Pop();
1124 rWriter.Push();
1125 rWriter.SetMapMode( MapMode( MapUnit::MapPoint ) );
1126 rWriter.SetFont( aFont );
1127 rWriter.SetTextColor( COL_LIGHTGREEN );
1128 Point aTextPoint;
1129 tools::Rectangle aTextRect;
1130 if( rPageSize.Width() > rPageSize.Height() )
1132 aTextPoint = Point( (rPageSize.Width()-w)/2,
1133 rPageSize.Height()-(rPageSize.Height()-nTextHeight)/2 );
1134 aTextRect = tools::Rectangle( Point( (rPageSize.Width()-w)/2,
1135 (rPageSize.Height()-nTextHeight)/2 ),
1136 Size( w, nTextHeight ) );
1138 else
1140 aTextPoint = Point( (rPageSize.Width()-nTextHeight)/2,
1141 (rPageSize.Height()-w)/2 );
1142 aTextRect = tools::Rectangle( aTextPoint, Size( nTextHeight, w ) );
1144 rWriter.SetClipRegion();
1145 rWriter.BeginTransparencyGroup();
1146 rWriter.DrawText( aTextPoint, msWatermark );
1147 rWriter.EndTransparencyGroup( aTextRect, 50 );
1148 rWriter.Pop();
1151 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */