xref: /core/vcl/source/gdi/pdfwriter_impl.cxx (revision 11d2f3d6)
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 #include <config_features.h>
21 
22 #include <sal/types.h>
23 
24 #include <math.h>
25 #include <algorithm>
26 
27 #if defined __GNUC__ && __cplusplus > 201402L
28 #pragma GCC diagnostic push
29 #pragma GCC diagnostic ignored "-Wregister"
30 #endif
31 #include <lcms2.h>
32 #if defined __GNUC__ && __cplusplus > 201402L
33 #pragma GCC diagnostic pop
34 #endif
35 
36 #include <basegfx/matrix/b2dhommatrix.hxx>
37 #include <basegfx/polygon/b2dpolygon.hxx>
38 #include <basegfx/polygon/b2dpolygontools.hxx>
39 #include <basegfx/polygon/b2dpolypolygon.hxx>
40 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
41 #include <basegfx/polygon/b2dpolypolygontools.hxx>
42 #include <memory>
43 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
44 #include <com/sun/star/util/URL.hpp>
45 #include <com/sun/star/util/URLTransformer.hpp>
46 #include <comphelper/processfactory.hxx>
47 #include <comphelper/random.hxx>
48 #include <comphelper/string.hxx>
49 #include <cppuhelper/implbase.hxx>
50 #include <i18nlangtag/languagetag.hxx>
51 #include <o3tl/numeric.hxx>
52 #include <o3tl/make_unique.hxx>
53 #include <osl/file.hxx>
54 #include <osl/thread.h>
55 #include <rtl/crc.h>
56 #include <rtl/digest.h>
57 #include <rtl/ustrbuf.hxx>
58 #include <svl/urihelper.hxx>
59 #include <tools/debug.hxx>
60 #include <tools/fract.hxx>
61 #include <tools/stream.hxx>
62 #include <tools/urlobj.hxx>
63 #include <tools/zcodec.hxx>
64 #include <svl/cryptosign.hxx>
65 #include <vcl/bitmapex.hxx>
66 #include <vcl/bitmapaccess.hxx>
67 #include <vcl/cvtgrf.hxx>
68 #include <vcl/image.hxx>
69 #include <vcl/lineinfo.hxx>
70 #include <vcl/metric.hxx>
71 #include <vcl/settings.hxx>
72 #include <vcl/strhelper.hxx>
73 #include <vcl/svapp.hxx>
74 #include <vcl/virdev.hxx>
75 #include <vcl/filter/pdfdocument.hxx>
76 #include <comphelper/hash.hxx>
77 
78 #include <fontsubset.hxx>
79 #include <outdev.h>
80 #include <PhysicalFontFace.hxx>
81 #include <salgdi.hxx>
82 #include <sallayout.hxx>
83 #include <textlayout.hxx>
84 #include <textlineinfo.hxx>
85 
86 #include "pdfwriter_impl.hxx"
87 
88 #ifdef _WIN32
89 // WinCrypt headers for PDF signing
90 // Note: this uses Windows 7 APIs and requires the relevant data types
91 #include <prewin.h>
92 #include <wincrypt.h>
93 #include <postwin.h>
94 #include <comphelper/windowserrorstring.hxx>
95 #endif
96 
97 #include <config_eot.h>
98 
99 #if ENABLE_EOT
100 #include <libeot/libeot.h>
101 #endif
102 
103 using namespace vcl;
104 using namespace::com::sun::star;
105 
106 static bool g_bDebugDisableCompression = getenv("VCL_DEBUG_DISABLE_PDFCOMPRESSION");
107 
108 #if HAVE_FEATURE_NSS
109 // Is this length truly the maximum possible, or just a number that
110 // seemed large enough when the author tested this (with some type of
111 // certificates)? I suspect the latter.
112 
113 // Used to be 0x4000 = 16384, but a sample signed PDF (produced by
114 // some other software) provided by the customer has a signature
115 // content that is 30000 bytes. The SampleSignedPDFDocument.pdf from
116 // Adobe has one that is 21942 bytes. So let's be careful. Pity this
117 // can't be dynamic, at least not without restructuring the code. Also
118 // note that the checks in the code for this being too small
119 // apparently are broken, if this overflows you end up with an invalid
120 // PDF. Need to fix that.
121 
122 #define MAX_SIGNATURE_CONTENT_LENGTH 50000
123 #endif
124 
125 #ifdef DO_TEST_PDF
126 class PDFTestOutputStream : public PDFOutputStream
127 {
128     public:
129     virtual ~PDFTestOutputStream();
130     virtual void write( const css::uno::Reference< css::io::XOutputStream >& xStream );
131 };
132 
133 PDFTestOutputStream::~PDFTestOutputStream()
134 {
135 }
136 
137 void PDFTestOutputStream::write( const css::uno::Reference< css::io::XOutputStream >& xStream )
138 {
139     OString aStr( "lalala\ntest\ntest\ntest" );
140     css::uno::Sequence< sal_Int8 > aData( aStr.getLength() );
141     memcpy( aData.getArray(), aStr.getStr(), aStr.getLength() );
142     xStream->writeBytes( aData );
143 }
144 
145 // this test code cannot be used to test PDF/A-1 because it forces
146 // control item (widgets) to bypass the structure controlling
147 // the embedding of such elements in actual run
148 void doTestCode()
149 {
150     static const char* pHome = getenv( "HOME"  );
151     OUString aTestFile( "file://"  );
152     aTestFile += OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 );
153     aTestFile += "/pdf_export_test.pdf";
154 
155     PDFWriter::PDFWriterContext aContext;
156     aContext.URL            = aTestFile;
157     aContext.Version        = PDFWriter::PDF_1_4;
158     aContext.Tagged         = true;
159     aContext.InitialPage    = 2;
160     aContext.DocumentInfo.Title = "PDF export test document";
161     aContext.DocumentInfo.Producer = "VCL";
162 
163     aContext.SignPDF        = true;
164     aContext.SignLocation   = "Burdur";
165     aContext.SignReason     = "Some valid reason to sign";
166     aContext.SignContact    = "signer@example.com";
167 
168     css::uno::Reference< css::beans::XMaterialHolder > xEnc;
169     PDFWriter aWriter( aContext, xEnc );
170     aWriter.NewPage( 595, 842 );
171     aWriter.BeginStructureElement( PDFWriter::Document );
172     // set duration of 3 sec for first page
173     aWriter.SetAutoAdvanceTime( 3 );
174     aWriter.SetMapMode( MapMode( MapUnit::Map100thMM ) );
175 
176     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
177     aWriter.SetLineColor( Color( COL_LIGHTGREEN ) );
178     aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
179 
180     aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
181     aWriter.SetTextColor( Color( COL_BLACK ) );
182     aWriter.SetLineColor( Color( COL_BLACK ) );
183     aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
184 
185     Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
186     aWriter.DrawRect( aRect );
187     aWriter.DrawText( aRect, OUString( "Link annot 1" ) );
188     sal_Int32 nFirstLink = aWriter.CreateLink( aRect );
189     PDFNote aNote;
190     aNote.Title = "A small test note";
191     aNote.Contents = "There is no business like show business like no business i know. Everything about it is appealing.";
192     aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote );
193 
194     Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
195     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
196     aWriter.DrawRect( aTargetRect );
197     aWriter.DrawText( aTargetRect, "Dest second link" );
198     sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect );
199 
200     aWriter.BeginStructureElement( PDFWriter::Section );
201     aWriter.BeginStructureElement( PDFWriter::Heading );
202     aWriter.DrawText( Point(4500, 9000), "A small structure test" );
203     aWriter.EndStructureElement();
204     aWriter.BeginStructureElement( PDFWriter::Paragraph );
205     aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
206     aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline );
207     aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
208                      "It was the best of PDF, it was the worst of PDF ... or so. This is a pretty nonsensical text to denote a paragraph. I suggest you stop reading it. Because if you read on you might get bored. So continue on your on risk. Hey, you're still here ? Why do you continue to read this as it is of no use at all ? OK, it's your time, but still... . Woah, i even get bored writing this, so let's end this here and now.",
209                       DrawTextFlags::MultiLine | DrawTextFlags::WordBreak
210                       );
211     aWriter.SetActualText( "It was the best of PDF, it was the worst of PDF ... or so. This is a pretty nonsensical text to denote a paragraph. I suggest you stop reading it. Because if you read on you might get bored. So continue on your on risk. Hey, you're still here ? Why do you continue to read this as it is of no use at all ? OK, it's your time, but still... . Woah, i even get bored writing this, so let's end this here and now." );
212     aWriter.SetAlternateText( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." );
213     aWriter.EndStructureElement();
214     aWriter.BeginStructureElement( PDFWriter::Paragraph );
215     aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
216     aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
217                       "This paragraph is nothing special either but ends on the next page structurewise",
218                       DrawTextFlags::MultiLine | DrawTextFlags::WordBreak
219                       );
220 
221     aWriter.NewPage( 595, 842 );
222     // test AddStream interface
223     aWriter.AddStream( "text/plain", new PDFTestOutputStream(), true );
224     // set transitional mode
225     aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 );
226     aWriter.SetMapMode( MapMode( MapUnit::Map100thMM ) );
227     aWriter.SetTextColor( Color( COL_BLACK ) );
228     aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
229     aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
230                       "Here's where all things come to an end ... well at least the paragraph from the last page.",
231                       DrawTextFlags::MultiLine | DrawTextFlags::WordBreak
232                       );
233     aWriter.EndStructureElement();
234 
235     aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
236     // disable structure
237     aWriter.BeginStructureElement( PDFWriter::NonStructElement );
238     aWriter.DrawRect( aRect );
239     aWriter.BeginStructureElement( PDFWriter::Paragraph );
240     aWriter.DrawText( aRect, "Link annot 2" );
241     sal_Int32 nSecondLink = aWriter.CreateLink( aRect );
242 
243     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
244     aWriter.BeginStructureElement( PDFWriter::ListItem );
245     aWriter.DrawRect( aTargetRect );
246     aWriter.DrawText( aTargetRect, "Dest first link" );
247     sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect );
248     // enable structure
249     aWriter.EndStructureElement();
250 
251     aWriter.EndStructureElement();
252     aWriter.EndStructureElement();
253     aWriter.BeginStructureElement( PDFWriter::Figure );
254     aWriter.BeginStructureElement( PDFWriter::Caption );
255     aWriter.DrawText( Point( 4500, 9000 ), "Some drawing stuff inside the structure" );
256     aWriter.EndStructureElement();
257 
258     // test clipping
259     basegfx::B2DPolyPolygon aClip;
260     basegfx::B2DPolygon aClipPoly;
261     aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) );
262     aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) );
263     aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) );
264     aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) );
265     aClipPoly.setClosed( true );
266     aClip.append( aClipPoly );
267 
268     aWriter.Push( PushFlags::CLIPREGION | PushFlags::FILLCOLOR );
269     aWriter.SetClipRegion( aClip );
270     aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
271     aWriter.MoveClipRegion( 1000, 500 );
272     aWriter.SetFillColor( Color( COL_RED ) );
273     aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
274     aWriter.Pop();
275     // test transparency
276     // draw background
277     Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
278     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
279     aWriter.DrawRect( aTranspRect );
280     aWriter.BeginTransparencyGroup();
281 
282     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
283     aWriter.DrawEllipse( aTranspRect );
284     aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
285     aWriter.DrawText( aTranspRect,
286                       "Some transparent text",
287                       DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
288 
289     aWriter.EndTransparencyGroup( aTranspRect, 50 );
290 
291     // prepare an alpha mask
292     Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
293     Bitmap::ScopedWriteAccess pAcc(aTransMask);
294     for( int nX = 0; nX < 256; nX++ )
295         for( int nY = 0; nY < 256; nY++ )
296             pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) );
297     pAcc.reset();
298     aTransMask.SetPrefMapMode( MapUnit::MapMM );
299     aTransMask.SetPrefSize( Size( 10, 10 ) );
300 
301     aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask );
302 
303     aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
304     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
305     aWriter.DrawRect( aTranspRect );
306     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
307     aWriter.DrawEllipse( aTranspRect );
308     aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
309     aWriter.DrawText( aTranspRect,
310                       "Some transparent text",
311                       DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
312     aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
313     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
314     aWriter.DrawRect( aTranspRect );
315 
316     Bitmap aImageBmp( Size( 256, 256 ), 24 );
317     pAcc = Bitmap::ScopedWriteAccess(aImageBmp);
318     pAcc->SetFillColor( Color( 0xff, 0, 0xff ) );
319     pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
320     pAcc.reset();
321     BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) );
322     aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx );
323 
324     aWriter.EndStructureElement();
325     aWriter.EndStructureElement();
326 
327     LineInfo aLI( LineStyle::Dash, 3 );
328     aLI.SetDashCount( 2 );
329     aLI.SetDashLen( 50 );
330     aLI.SetDotCount( 2 );
331     aLI.SetDotLen( 25 );
332     aLI.SetDistance( 15 );
333     Point aLIPoints[] = { Point( 4000, 10000 ),
334                           Point( 8000, 12000 ),
335                           Point( 3000, 19000 ) };
336     tools::Polygon aLIPoly( 3, aLIPoints );
337     aWriter.SetLineColor( Color( COL_BLUE ) );
338     aWriter.SetFillColor();
339     aWriter.DrawPolyLine( aLIPoly, aLI );
340 
341     aLI.SetDashCount( 4 );
342     aLIPoly.Move( 1000, 1000 );
343     aWriter.DrawPolyLine( aLIPoly, aLI );
344 
345     aWriter.NewPage( 595, 842 );
346     aWriter.SetMapMode( MapMode( MapUnit::Map100thMM ) );
347     Wallpaper aWall( aTransMask );
348     aWall.SetStyle( WallpaperStyle::Tile );
349     aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall );
350 
351     aWriter.NewPage( 595, 842 );
352     aWriter.SetMapMode( MapMode( MapUnit::Map100thMM ) );
353     aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
354     aWriter.SetTextColor( Color( COL_BLACK ) );
355     aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
356     aWriter.DrawRect( aRect );
357     aWriter.DrawText( aRect, "www.heise.de" );
358     sal_Int32 nURILink = aWriter.CreateLink( aRect );
359     aWriter.SetLinkURL( nURILink, OUString( "http://www.heise.de" ) );
360 
361     aWriter.SetLinkDest( nFirstLink, nFirstDest );
362     aWriter.SetLinkDest( nSecondLink, nSecondDest );
363 
364     // include a button
365     PDFWriter::PushButtonWidget aBtn;
366     aBtn.Name = "testButton";
367     aBtn.Description = "A test button";
368     aBtn.Text = "hit me";
369     aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
370     aBtn.Border = aBtn.Background = true;
371     aWriter.CreateControl( aBtn );
372 
373     // include a uri button
374     PDFWriter::PushButtonWidget aUriBtn;
375     aUriBtn.Name = "wwwButton";
376     aUriBtn.Description = "A URI button";
377     aUriBtn.Text = "to www";
378     aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
379     aUriBtn.Border = aUriBtn.Background = true;
380     aUriBtn.URL = "http://www.heise.de";
381     aWriter.CreateControl( aUriBtn );
382 
383     // include a dest button
384     PDFWriter::PushButtonWidget aDstBtn;
385     aDstBtn.Name = "destButton";
386     aDstBtn.Description = "A Dest button";
387     aDstBtn.Text = "to paragraph";
388     aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
389     aDstBtn.Border = aDstBtn.Background = true;
390     aDstBtn.Dest = nFirstDest;
391     aWriter.CreateControl( aDstBtn );
392 
393     PDFWriter::CheckBoxWidget aCBox;
394     aCBox.Name = "textCheckBox";
395     aCBox.Description = "A test check box";
396     aCBox.Text = "check me";
397     aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
398     aCBox.Checked = true;
399     aCBox.Border = aCBox.Background = false;
400     aWriter.CreateControl( aCBox );
401 
402     PDFWriter::CheckBoxWidget aCBox2;
403     aCBox2.Name = "textCheckBox2";
404     aCBox2.Description = "Another test check box";
405     aCBox2.Text = "check me right";
406     aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
407     aCBox2.Checked = true;
408     aCBox2.Border = aCBox2.Background = false;
409     aCBox2.ButtonIsLeft = false;
410     aWriter.CreateControl( aCBox2 );
411 
412     PDFWriter::RadioButtonWidget aRB1;
413     aRB1.Name = "rb1_1";
414     aRB1.Description = "radio 1 button 1";
415     aRB1.Text = "Despair";
416     aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
417     aRB1.Selected = true;
418     aRB1.RadioGroup = 1;
419     aRB1.Border = aRB1.Background = true;
420     aRB1.ButtonIsLeft = false;
421     aRB1.BorderColor = Color( COL_LIGHTGREEN );
422     aRB1.BackgroundColor = Color( COL_LIGHTBLUE );
423     aRB1.TextColor = Color( COL_LIGHTRED );
424     aRB1.TextFont = Font( OUString( "Courier" ), Size( 0, 800 ) );
425     aWriter.CreateControl( aRB1 );
426 
427     PDFWriter::RadioButtonWidget aRB2;
428     aRB2.Name = "rb2_1";
429     aRB2.Description = "radio 2 button 1";
430     aRB2.Text = "Joy";
431     aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
432     aRB2.Selected = true;
433     aRB2.RadioGroup = 2;
434     aWriter.CreateControl( aRB2 );
435 
436     PDFWriter::RadioButtonWidget aRB3;
437     aRB3.Name = "rb1_2";
438     aRB3.Description = "radio 1 button 2";
439     aRB3.Text = "Desperation";
440     aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
441     aRB3.Selected = true;
442     aRB3.RadioGroup = 1;
443     aWriter.CreateControl( aRB3 );
444 
445     PDFWriter::EditWidget aEditBox;
446     aEditBox.Name = "testEdit";
447     aEditBox.Description = "A test edit field";
448     aEditBox.Text = "A little test text";
449     aEditBox.TextStyle = DrawTextFlags::Left | DrawTextFlags::VCenter;
450     aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
451     aEditBox.MaxLen = 100;
452     aEditBox.Border = aEditBox.Background = true;
453     aEditBox.BorderColor = Color( COL_BLACK );
454     aWriter.CreateControl( aEditBox );
455 
456     // normal list box
457     PDFWriter::ListBoxWidget aLstBox;
458     aLstBox.Name = "testListBox";
459     aLstBox.Text = "One";
460     aLstBox.Description = "select me";
461     aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
462     aLstBox.Sort = true;
463     aLstBox.MultiSelect = true;
464     aLstBox.Border = aLstBox.Background = true;
465     aLstBox.BorderColor = Color( COL_BLACK );
466     aLstBox.Entries.push_back( OUString( "One"  ) );
467     aLstBox.Entries.push_back( OUString( "Two"  ) );
468     aLstBox.Entries.push_back( OUString( "Three"  ) );
469     aLstBox.Entries.push_back( OUString( "Four"  ) );
470     aLstBox.SelectedEntries.push_back( 1 );
471     aLstBox.SelectedEntries.push_back( 2 );
472     aWriter.CreateControl( aLstBox );
473 
474     // dropdown list box
475     aLstBox.Name = "testDropDownListBox";
476     aLstBox.DropDown = true;
477     aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
478     aWriter.CreateControl( aLstBox );
479 
480     // combo box
481     PDFWriter::ComboBoxWidget aComboBox;
482     aComboBox.Name = "testComboBox";
483     aComboBox.Text = "test a combobox";
484     aComboBox.Entries.push_back( OUString( "Larry"  ) );
485     aComboBox.Entries.push_back( OUString( "Curly"  ) );
486     aComboBox.Entries.push_back( OUString( "Moe"  ) );
487     aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
488     aWriter.CreateControl( aComboBox );
489 
490     // test outlines
491     sal_Int32 nPage1OL = aWriter.CreateOutlineItem();
492     aWriter.SetOutlineItemText( nPage1OL, OUString( "Page 1"  ) );
493     aWriter.SetOutlineItemDest( nPage1OL, nSecondDest );
494     aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2"  ), nSecondDest );
495     aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 revisited"  ), nSecondDest );
496     aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 again"  ), nSecondDest );
497     sal_Int32 nPage2OL = aWriter.CreateOutlineItem();
498     aWriter.SetOutlineItemText( nPage2OL, OUString( "Page 2"  ) );
499     aWriter.CreateOutlineItem( nPage2OL, OUString( "Dest 1"  ), nFirstDest );
500 
501     aWriter.EndStructureElement(); // close document
502 
503     aWriter.Emit();
504 }
505 #endif
506 
507 static const sal_Int32 nLog10Divisor = 1;
508 static const double fDivisor = 10.0;
509 
510 static inline double pixelToPoint( double px ) { return px/fDivisor; }
511 static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); }
512 
513 const sal_uInt8 PDFWriterImpl::s_nPadString[32] =
514 {
515     0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
516     0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
517 };
518 
519 static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
520 {
521     static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
522                                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
523     rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
524     rBuffer.append( pHexDigits[ nInt & 15 ] );
525 }
526 
527 static void appendName( const OUString& rStr, OStringBuffer& rBuffer )
528 {
529 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
530 // I guess than when reading the #xx sequence it will count for a single character.
531     OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) );
532     int nLen = aStr.getLength();
533     for( int i = 0; i < nLen; i++ )
534     {
535         /*  #i16920# PDF recommendation: output UTF8, any byte
536          *  outside the interval [33(=ASCII'!');126(=ASCII'~')]
537          *  should be escaped hexadecimal
538          *  for the sake of ghostscript which also reads PDF
539          *  but has a narrower acceptance rate we only pass
540          *  alphanumerics and '-' literally.
541          */
542         if( (aStr[i] >= 'A' && aStr[i] <= 'Z' ) ||
543             (aStr[i] >= 'a' && aStr[i] <= 'z' ) ||
544             (aStr[i] >= '0' && aStr[i] <= '9' ) ||
545             aStr[i] == '-' )
546         {
547             rBuffer.append( aStr[i] );
548         }
549         else
550         {
551             rBuffer.append( '#' );
552             appendHex( (sal_Int8)aStr[i], rBuffer );
553         }
554     }
555 }
556 
557 static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer )
558 {
559     // FIXME i59651 see above
560     while( pStr && *pStr )
561     {
562         if( (*pStr >= 'A' && *pStr <= 'Z' ) ||
563             (*pStr >= 'a' && *pStr <= 'z' ) ||
564             (*pStr >= '0' && *pStr <= '9' ) ||
565             *pStr == '-' )
566         {
567             rBuffer.append( *pStr );
568         }
569         else
570         {
571             rBuffer.append( '#' );
572             appendHex( (sal_Int8)*pStr, rBuffer );
573         }
574         pStr++;
575     }
576 }
577 
578 //used only to emit encoded passwords
579 static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer )
580 {
581     while( nLength )
582     {
583         switch( *pStr )
584         {
585         case '\n' :
586             rBuffer.append( "\\n" );
587             break;
588         case '\r' :
589             rBuffer.append( "\\r" );
590             break;
591         case '\t' :
592             rBuffer.append( "\\t" );
593             break;
594         case '\b' :
595             rBuffer.append( "\\b" );
596             break;
597         case '\f' :
598             rBuffer.append( "\\f" );
599             break;
600         case '(' :
601         case ')' :
602         case '\\' :
603             rBuffer.append( "\\" );
604             rBuffer.append( (sal_Char) *pStr );
605             break;
606         default:
607             rBuffer.append( (sal_Char) *pStr );
608             break;
609         }
610         pStr++;
611         nLength--;
612     }
613 }
614 
615 /*
616  * Convert a string before using it.
617  *
618  * This string conversion function is needed because the destination name
619  * in a PDF file seen through an Internet browser should be
620  * specially crafted, in order to be used directly by the browser.
621  * In this way the fragment part of a hyperlink to a PDF file (e.g. something
622  * as 'test1/test2/a-file.pdf\#thefragment) will be (hopefully) interpreted by the
623  * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
624  * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
625  * and go to named destination thefragment using default zoom'.
626  * The conversion is needed because in case of a fragment in the form: Slide%201
627  * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
628  * using this conversion, in both the generated named destinations, fragment and GoToR
629  * destination.
630  *
631  * The names for destinations are name objects and so they don't need to be encrypted
632  * even though they expose the content of PDF file (e.g. guessing the PDF content from the
633  * destination name).
634  *
635  * Further limitation: it is advisable to use standard ASCII characters for
636  * OOo bookmarks.
637 */
638 static void appendDestinationName( const OUString& rString, OStringBuffer& rBuffer )
639 {
640     const sal_Unicode* pStr = rString.getStr();
641     sal_Int32 nLen = rString.getLength();
642     for( int i = 0; i < nLen; i++ )
643     {
644         sal_Unicode aChar = pStr[i];
645         if( (aChar >= '0' && aChar <= '9' ) ||
646             (aChar >= 'a' && aChar <= 'z' ) ||
647             (aChar >= 'A' && aChar <= 'Z' ) ||
648             aChar == '-' )
649         {
650             rBuffer.append((sal_Char)aChar);
651         }
652         else
653         {
654             sal_Int8 aValueHigh = sal_Int8(aChar >> 8);
655             if(aValueHigh > 0)
656                 appendHex( aValueHigh, rBuffer );
657             appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
658         }
659     }
660 }
661 
662 void PDFWriter::AppendUnicodeTextString(const OUString& rString, OStringBuffer& rBuffer)
663 {
664     rBuffer.append( "FEFF" );
665     const sal_Unicode* pStr = rString.getStr();
666     sal_Int32 nLen = rString.getLength();
667     for( int i = 0; i < nLen; i++ )
668     {
669         sal_Unicode aChar = pStr[i];
670         appendHex( (sal_Int8)(aChar >> 8), rBuffer );
671         appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
672     }
673 }
674 
675 void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl )
676 {
677     /* #i80258# previously we use appendName here
678        however we need a slightly different coding scheme than the normal
679        name encoding for field names
680     */
681     const OUString& rName = (m_aContext.Version > PDFWriter::PDFVersion::PDF_1_2) ? i_rControl.Name : i_rControl.Text;
682     OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) );
683     int nLen = aStr.getLength();
684 
685     OStringBuffer aBuffer( rName.getLength()+64 );
686     for( int i = 0; i < nLen; i++ )
687     {
688         /*  #i16920# PDF recommendation: output UTF8, any byte
689          *  outside the interval [32(=ASCII' ');126(=ASCII'~')]
690          *  should be escaped hexadecimal
691          */
692         if( aStr[i] >= 32 && aStr[i] <= 126 )
693             aBuffer.append( aStr[i] );
694         else
695         {
696             aBuffer.append( '#' );
697             appendHex( (sal_Int8)aStr[i], aBuffer );
698         }
699     }
700 
701     OString aFullName( aBuffer.makeStringAndClear() );
702 
703     /* #i82785# create hierarchical fields down to the for each dot in i_rName */
704     sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0;
705     OString aPartialName;
706     OString aDomain;
707     do
708     {
709         nLastTokenIndex = nTokenIndex;
710         aPartialName = aFullName.getToken( 0, '.', nTokenIndex );
711         if( nTokenIndex != -1 )
712         {
713             // find or create a hierarchical field
714             // first find the fully qualified name up to this field
715             aDomain = aFullName.copy( 0, nTokenIndex-1 );
716             std::unordered_map< OString, sal_Int32 >::const_iterator it = m_aFieldNameMap.find( aDomain );
717             if( it == m_aFieldNameMap.end() )
718             {
719                  // create new hierarchy field
720                 sal_Int32 nNewWidget = m_aWidgets.size();
721                 m_aWidgets.emplace_back( );
722                 m_aWidgets[nNewWidget].m_nObject = createObject();
723                 m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy;
724                 m_aWidgets[nNewWidget].m_aName = aPartialName;
725                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
726                 m_aFieldNameMap[aDomain] = nNewWidget;
727                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
728                 if( nLastTokenIndex > 0 )
729                 {
730                     // this field is not a root field and
731                     // needs to be inserted to its parent
732                     OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) );
733                     it = m_aFieldNameMap.find( aParentDomain );
734                     OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" );
735                     if( it != m_aFieldNameMap.end()  )
736                     {
737                         OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" );
738                         if( it->second < sal_Int32(m_aWidgets.size()) )
739                         {
740                             PDFWidget& rParentField( m_aWidgets[it->second] );
741                             rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject );
742                             rParentField.m_aKidsIndex.push_back( nNewWidget );
743                             m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject;
744                         }
745                     }
746                 }
747             }
748             else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy )
749             {
750                 // this is invalid, someone tries to have a terminal field as parent
751                 // example: a button with the name foo.bar exists and
752                 // another button is named foo.bar.no
753                 // workaround: put the second terminal field as much up in the hierarchy as
754                 // necessary to have a non-terminal field as parent (or none at all)
755                 // since it->second already is terminal, we just need to use its parent
756                 aDomain.clear();
757                 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
758                 if( nLastTokenIndex > 0 )
759                 {
760                     aDomain = aFullName.copy( 0, nLastTokenIndex-1 );
761                     OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() );
762                     aBuf.append( aDomain );
763                     aBuf.append( '.' );
764                     aBuf.append( aPartialName );
765                     aFullName = aBuf.makeStringAndClear();
766                 }
767                 else
768                     aFullName = aPartialName;
769                 break;
770             }
771         }
772     } while( nTokenIndex != -1 );
773 
774     // insert widget into its hierarchy field
775     if( !aDomain.isEmpty() )
776     {
777         std::unordered_map< OString, sal_Int32 >::const_iterator it = m_aFieldNameMap.find( aDomain );
778         if( it != m_aFieldNameMap.end() )
779         {
780             OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" );
781             if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) )
782             {
783                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject;
784                 m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject);
785                 m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex );
786             }
787         }
788     }
789 
790     if( aPartialName.isEmpty() )
791     {
792         // how funny, an empty field name
793         if( i_rControl.getType() == PDFWriter::RadioButton )
794         {
795             aPartialName  = "RadioGroup";
796             aPartialName += OString::number( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup );
797         }
798         else
799             aPartialName = OString( "Widget" );
800     }
801 
802     if( ! m_aContext.AllowDuplicateFieldNames )
803     {
804         std::unordered_map<OString, sal_Int32>::iterator it = m_aFieldNameMap.find( aFullName );
805 
806         if( it != m_aFieldNameMap.end() ) // not unique
807         {
808             std::unordered_map< OString, sal_Int32 >::const_iterator check_it;
809             OString aTry;
810             sal_Int32 nTry = 2;
811             do
812             {
813                 OStringBuffer aUnique( aFullName.getLength() + 16 );
814                 aUnique.append( aFullName );
815                 aUnique.append( '_' );
816                 aUnique.append( nTry++ );
817                 aTry = aUnique.makeStringAndClear();
818                 check_it = m_aFieldNameMap.find( aTry );
819             } while( check_it != m_aFieldNameMap.end() );
820             aFullName = aTry;
821             m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
822             aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
823         }
824         else
825             m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
826     }
827 
828     // finally
829     m_aWidgets[i_nWidgetIndex].m_aName = aPartialName;
830 }
831 
832 static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer )
833 {
834     if( nValue < 0 )
835     {
836         rBuffer.append( '-' );
837         nValue = -nValue;
838     }
839     const sal_Int32 nFactor = 10;
840     const sal_Int32 nInt = nValue / nFactor;
841     rBuffer.append( nInt );
842     sal_Int32 nDecimal  = nValue % nFactor;
843     if (nDecimal)
844     {
845         rBuffer.append('.');
846         rBuffer.append(nDecimal);
847     }
848 }
849 
850 // appends a double. PDF does not accept exponential format, only fixed point
851 static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 )
852 {
853     bool bNeg = false;
854     if( fValue < 0.0 )
855     {
856         bNeg = true;
857         fValue=-fValue;
858     }
859 
860     sal_Int64 nInt = (sal_Int64)fValue;
861     fValue -= (double)nInt;
862     // optimizing hardware may lead to a value of 1.0 after the subtraction
863     if( rtl::math::approxEqual(fValue, 1.0) || log10( 1.0-fValue ) <= -nPrecision )
864     {
865         nInt++;
866         fValue = 0.0;
867     }
868     sal_Int64 nFrac = 0;
869     if( fValue )
870     {
871         fValue *= pow( 10.0, (double)nPrecision );
872         nFrac = (sal_Int64)fValue;
873     }
874     if( bNeg && ( nInt || nFrac ) )
875         rBuffer.append( '-' );
876     rBuffer.append( nInt );
877     if( nFrac )
878     {
879         int i;
880         rBuffer.append( '.' );
881         sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5);
882         for ( i = 0; ( i < nPrecision ) && nFrac; i++ )
883         {
884             sal_Int64 nNumb = nFrac / nBound;
885             nFrac -= nNumb * nBound;
886             rBuffer.append( nNumb );
887             nBound /= 10;
888         }
889     }
890 }
891 
892 static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey )
893 {
894 
895     if( rColor != Color( COL_TRANSPARENT ) )
896     {
897         if( bConvertToGrey )
898         {
899             sal_uInt8 cByte = rColor.GetLuminance();
900             appendDouble( (double)cByte / 255.0, rBuffer );
901         }
902         else
903         {
904             appendDouble( (double)rColor.GetRed() / 255.0, rBuffer );
905             rBuffer.append( ' ' );
906             appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer );
907             rBuffer.append( ' ' );
908             appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer );
909         }
910     }
911 }
912 
913 void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
914 {
915     if( rColor != Color( COL_TRANSPARENT ) )
916     {
917         bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
918         appendColor( rColor, rBuffer, bGrey );
919         rBuffer.append( bGrey ? " G" : " RG" );
920     }
921 }
922 
923 void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
924 {
925     if( rColor != Color( COL_TRANSPARENT ) )
926     {
927         bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
928         appendColor( rColor, rBuffer, bGrey );
929         rBuffer.append( bGrey ? " g" : " rg" );
930     }
931 }
932 
933 // matrix helper class
934 // TODO: use basegfx matrix class instead or derive from it
935 namespace vcl // TODO: use anonymous namespace to keep this class local
936 {
937 /*  for sparse matrices of the form (2D linear transformations)
938  *  f[0] f[1] 0
939  *  f[2] f[3] 0
940  *  f[4] f[5] 1
941  */
942 class Matrix3
943 {
944     double f[6];
945 
946     void set( const double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; }
947 public:
948     Matrix3();
949 
950     void skew( double alpha, double beta );
951     void scale( double sx, double sy );
952     void rotate( double angle );
953     void translate( double tx, double ty );
954     void invert();
955 
956     void append( PDFWriterImpl::PDFPage const & rPage, OStringBuffer& rBuffer );
957 
958     Point transform( const Point& rPoint ) const;
959 };
960 }
961 
962 Matrix3::Matrix3()
963 {
964     // initialize to unity
965     f[0] = 1.0;
966     f[1] = 0.0;
967     f[2] = 0.0;
968     f[3] = 1.0;
969     f[4] = 0.0;
970     f[5] = 0.0;
971 }
972 
973 Point Matrix3::transform( const Point& rOrig ) const
974 {
975     double x = (double)rOrig.X(), y = (double)rOrig.Y();
976     return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) );
977 }
978 
979 void Matrix3::skew( double alpha, double beta )
980 {
981     double fn[6];
982     double tb = tan( beta );
983     fn[0] = f[0] + f[2]*tb;
984     fn[1] = f[1];
985     fn[2] = f[2] + f[3]*tb;
986     fn[3] = f[3];
987     fn[4] = f[4] + f[5]*tb;
988     fn[5] = f[5];
989     if( alpha != 0.0 )
990     {
991         double ta = tan( alpha );
992         fn[1] += f[0]*ta;
993         fn[3] += f[2]*ta;
994         fn[5] += f[4]*ta;
995     }
996     set( fn );
997 }
998 
999 void Matrix3::scale( double sx, double sy )
1000 {
1001     double fn[6];
1002     fn[0] = sx*f[0];
1003     fn[1] = sy*f[1];
1004     fn[2] = sx*f[2];
1005     fn[3] = sy*f[3];
1006     fn[4] = sx*f[4];
1007     fn[5] = sy*f[5];
1008     set( fn );
1009 }
1010 
1011 void Matrix3::rotate( double angle )
1012 {
1013     double fn[6];
1014     double fSin = sin(angle);
1015     double fCos = cos(angle);
1016     fn[0] = f[0]*fCos - f[1]*fSin;
1017     fn[1] = f[0]*fSin + f[1]*fCos;
1018     fn[2] = f[2]*fCos - f[3]*fSin;
1019     fn[3] = f[2]*fSin + f[3]*fCos;
1020     fn[4] = f[4]*fCos - f[5]*fSin;
1021     fn[5] = f[4]*fSin + f[5]*fCos;
1022     set( fn );
1023 }
1024 
1025 void Matrix3::translate( double tx, double ty )
1026 {
1027     f[4] += tx;
1028     f[5] += ty;
1029 }
1030 
1031 void Matrix3::invert()
1032 {
1033     // short circuit trivial cases
1034     if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 )
1035     {
1036         f[4] = -f[4];
1037         f[5] = -f[5];
1038         return;
1039     }
1040 
1041     // check determinant
1042     const double fDet = f[0]*f[3]-f[1]*f[2];
1043     if( fDet == 0.0 )
1044         return;
1045 
1046     // invert the matrix
1047     double fn[6];
1048     fn[0] = +f[3] / fDet;
1049     fn[1] = -f[1] / fDet;
1050     fn[2] = -f[2] / fDet;
1051     fn[3] = +f[0] / fDet;
1052 
1053     // apply inversion to translation
1054     fn[4] = -(f[4]*fn[0] + f[5]*fn[2]);
1055     fn[5] = -(f[4]*fn[1] + f[5]*fn[3]);
1056 
1057     set( fn );
1058 }
1059 
1060 void Matrix3::append( PDFWriterImpl::PDFPage const & rPage, OStringBuffer& rBuffer )
1061 {
1062     appendDouble( f[0], rBuffer );
1063     rBuffer.append( ' ' );
1064     appendDouble( f[1], rBuffer );
1065     rBuffer.append( ' ' );
1066     appendDouble( f[2], rBuffer );
1067     rBuffer.append( ' ' );
1068     appendDouble( f[3], rBuffer );
1069     rBuffer.append( ' ' );
1070     rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer );
1071 }
1072 
1073 static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList )
1074 {
1075     if( rList.empty() )
1076         return;
1077     rBuf.append( '/' );
1078     rBuf.append( pPrefix );
1079     rBuf.append( "<<" );
1080     int ni = 0;
1081     for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it )
1082     {
1083         if( !it->first.isEmpty() && it->second > 0 )
1084         {
1085             rBuf.append( '/' );
1086             rBuf.append( it->first );
1087             rBuf.append( ' ' );
1088             rBuf.append( it->second );
1089             rBuf.append( " 0 R" );
1090             if( ((++ni) & 7) == 0 )
1091                 rBuf.append( '\n' );
1092         }
1093     }
1094     rBuf.append( ">>\n" );
1095 }
1096 
1097 void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject )
1098 {
1099     rBuf.append( "<</Font " );
1100     rBuf.append( nFontDictObject );
1101     rBuf.append( " 0 R\n" );
1102     appendResourceMap( rBuf, "XObject", m_aXObjects );
1103     appendResourceMap( rBuf, "ExtGState", m_aExtGStates );
1104     appendResourceMap( rBuf, "Shading", m_aShadings );
1105     appendResourceMap( rBuf, "Pattern", m_aPatterns );
1106     rBuf.append( "/ProcSet[/PDF/Text" );
1107     if( !m_aXObjects.empty() )
1108         rBuf.append( "/ImageC/ImageI/ImageB" );
1109     rBuf.append( "]\n>>\n" );
1110 };
1111 
1112 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, double nPageWidth, double nPageHeight, PDFWriter::Orientation eOrientation )
1113         :
1114         m_pWriter( pWriter ),
1115         m_nPageWidth( nPageWidth ),
1116         m_nPageHeight( nPageHeight ),
1117         m_eOrientation( eOrientation ),
1118         m_nPageObject( 0 ),  // invalid object number
1119         m_nStreamLengthObject( 0 ),
1120         m_nBeginStreamPos( 0 ),
1121         m_eTransition( PDFWriter::PageTransition::Regular ),
1122         m_nTransTime( 0 ),
1123         m_nDuration( 0 )
1124 {
1125     // object ref must be only ever updated in emit()
1126     m_nPageObject = m_pWriter->createObject();
1127 }
1128 
1129 PDFWriterImpl::PDFPage::~PDFPage()
1130 {
1131 }
1132 
1133 void PDFWriterImpl::PDFPage::beginStream()
1134 {
1135     if (g_bDebugDisableCompression)
1136     {
1137         m_pWriter->emitComment("PDFWriterImpl::PDFPage::beginStream, +");
1138     }
1139     m_aStreamObjects.push_back(m_pWriter->createObject());
1140     if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) )
1141         return;
1142 
1143     m_nStreamLengthObject = m_pWriter->createObject();
1144     // write content stream header
1145     OStringBuffer aLine;
1146     aLine.append( m_aStreamObjects.back() );
1147     aLine.append( " 0 obj\n<</Length " );
1148     aLine.append( m_nStreamLengthObject );
1149     aLine.append( " 0 R" );
1150     if (!g_bDebugDisableCompression)
1151         aLine.append( "/Filter/FlateDecode" );
1152     aLine.append( ">>\nstream\n" );
1153     if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) )
1154         return;
1155     if (osl::File::E_None != m_pWriter->m_aFile.getPos(m_nBeginStreamPos))
1156     {
1157         m_pWriter->m_aFile.close();
1158         m_pWriter->m_bOpen = false;
1159     }
1160     if (!g_bDebugDisableCompression)
1161         m_pWriter->beginCompression();
1162     m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() );
1163 }
1164 
1165 void PDFWriterImpl::PDFPage::endStream()
1166 {
1167     if (!g_bDebugDisableCompression)
1168         m_pWriter->endCompression();
1169     sal_uInt64 nEndStreamPos;
1170     if (osl::File::E_None != m_pWriter->m_aFile.getPos(nEndStreamPos))
1171     {
1172         m_pWriter->m_aFile.close();
1173         m_pWriter->m_bOpen = false;
1174         return;
1175     }
1176     m_pWriter->disableStreamEncryption();
1177     if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1178         return;
1179     // emit stream length object
1180     if( ! m_pWriter->updateObject( m_nStreamLengthObject ) )
1181         return;
1182     OStringBuffer aLine;
1183     aLine.append( m_nStreamLengthObject );
1184     aLine.append( " 0 obj\n" );
1185     aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) );
1186     aLine.append( "\nendobj\n\n" );
1187     m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1188 }
1189 
1190 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject )
1191 {
1192     // emit page object
1193     if( ! m_pWriter->updateObject( m_nPageObject ) )
1194         return false;
1195     OStringBuffer aLine;
1196 
1197     aLine.append( m_nPageObject );
1198     aLine.append( " 0 obj\n"
1199                   "<</Type/Page/Parent " );
1200     aLine.append( nParentObject );
1201     aLine.append( " 0 R" );
1202     aLine.append( "/Resources " );
1203     aLine.append( m_pWriter->getResourceDictObj() );
1204     aLine.append( " 0 R" );
1205     if( m_nPageWidth && m_nPageHeight )
1206     {
1207         aLine.append( "/MediaBox[0 0 " );
1208         aLine.append( m_nPageWidth );
1209         aLine.append( ' ' );
1210         aLine.append( m_nPageHeight );
1211         aLine.append( "]" );
1212     }
1213     switch( m_eOrientation )
1214     {
1215         case PDFWriter::Orientation::Portrait: aLine.append( "/Rotate 0\n" );break;
1216         case PDFWriter::Orientation::Inherit:  break;
1217     }
1218     int nAnnots = m_aAnnotations.size();
1219     if( nAnnots > 0 )
1220     {
1221         aLine.append( "/Annots[\n" );
1222         for( int i = 0; i < nAnnots; i++ )
1223         {
1224             aLine.append( m_aAnnotations[i] );
1225             aLine.append( " 0 R" );
1226             aLine.append( ((i+1)%15) ? " " : "\n" );
1227         }
1228         aLine.append( "]\n" );
1229     }
1230     if( m_aMCIDParents.size() > 0 )
1231     {
1232         OStringBuffer aStructParents( 1024 );
1233         aStructParents.append( "[ " );
1234         int nParents = m_aMCIDParents.size();
1235         for( int i = 0; i < nParents; i++ )
1236         {
1237             aStructParents.append( m_aMCIDParents[i] );
1238             aStructParents.append( " 0 R" );
1239             aStructParents.append( ((i%10) == 9) ? "\n" : " " );
1240         }
1241         aStructParents.append( "]" );
1242         m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() );
1243 
1244         aLine.append( "/StructParents " );
1245         aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) );
1246         aLine.append( "\n" );
1247     }
1248     if( m_nDuration > 0 )
1249     {
1250         aLine.append( "/Dur " );
1251         aLine.append( (sal_Int32)m_nDuration );
1252         aLine.append( "\n" );
1253     }
1254     if( m_eTransition != PDFWriter::PageTransition::Regular && m_nTransTime > 0 )
1255     {
1256         // transition duration
1257         aLine.append( "/Trans<</D " );
1258         appendDouble( (double)m_nTransTime/1000.0, aLine, 3 );
1259         aLine.append( "\n" );
1260         const char *pStyle = nullptr, *pDm = nullptr, *pM = nullptr, *pDi = nullptr;
1261         switch( m_eTransition )
1262         {
1263             case PDFWriter::PageTransition::SplitHorizontalInward:
1264                 pStyle = "Split"; pDm = "H"; pM = "I"; break;
1265             case PDFWriter::PageTransition::SplitHorizontalOutward:
1266                 pStyle = "Split"; pDm = "H"; pM = "O"; break;
1267             case PDFWriter::PageTransition::SplitVerticalInward:
1268                 pStyle = "Split"; pDm = "V"; pM = "I"; break;
1269             case PDFWriter::PageTransition::SplitVerticalOutward:
1270                 pStyle = "Split"; pDm = "V"; pM = "O"; break;
1271             case PDFWriter::PageTransition::BlindsHorizontal:
1272                 pStyle = "Blinds"; pDm = "H"; break;
1273             case PDFWriter::PageTransition::BlindsVertical:
1274                 pStyle = "Blinds"; pDm = "V"; break;
1275             case PDFWriter::PageTransition::BoxInward:
1276                 pStyle = "Box"; pM = "I"; break;
1277             case PDFWriter::PageTransition::BoxOutward:
1278                 pStyle = "Box"; pM = "O"; break;
1279             case PDFWriter::PageTransition::WipeLeftToRight:
1280                 pStyle = "Wipe"; pDi = "0"; break;
1281             case PDFWriter::PageTransition::WipeBottomToTop:
1282                 pStyle = "Wipe"; pDi = "90"; break;
1283             case PDFWriter::PageTransition::WipeRightToLeft:
1284                 pStyle = "Wipe"; pDi = "180"; break;
1285             case PDFWriter::PageTransition::WipeTopToBottom:
1286                 pStyle = "Wipe"; pDi = "270"; break;
1287             case PDFWriter::PageTransition::Dissolve:
1288                 pStyle = "Dissolve"; break;
1289             case PDFWriter::PageTransition::Regular:
1290                 break;
1291         }
1292         // transition style
1293         if( pStyle )
1294         {
1295             aLine.append( "/S/" );
1296             aLine.append( pStyle );
1297             aLine.append( "\n" );
1298         }
1299         if( pDm )
1300         {
1301             aLine.append( "/Dm/" );
1302             aLine.append( pDm );
1303             aLine.append( "\n" );
1304         }
1305         if( pM )
1306         {
1307             aLine.append( "/M/" );
1308             aLine.append( pM );
1309             aLine.append( "\n" );
1310         }
1311         if( pDi  )
1312         {
1313             aLine.append( "/Di " );
1314             aLine.append( pDi );
1315             aLine.append( "\n" );
1316         }
1317         aLine.append( ">>\n" );
1318     }
1319     if( m_pWriter->getVersion() > PDFWriter::PDFVersion::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 )
1320     {
1321         aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1322     }
1323     aLine.append( "/Contents" );
1324     unsigned int nStreamObjects = m_aStreamObjects.size();
1325     if( nStreamObjects > 1 )
1326         aLine.append( '[' );
1327     for(sal_Int32 i : m_aStreamObjects)
1328     {
1329         aLine.append( ' ' );
1330         aLine.append( i );
1331         aLine.append( " 0 R" );
1332     }
1333     if( nStreamObjects > 1 )
1334         aLine.append( ']' );
1335     aLine.append( ">>\nendobj\n\n" );
1336     return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1337 }
1338 
1339 namespace vcl
1340 {
1341 template < class GEOMETRY >
1342 GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
1343 {
1344     GEOMETRY aPoint;
1345     if ( MapUnit::MapPixel == _rSource.GetMapUnit() )
1346     {
1347         aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
1348     }
1349     else
1350     {
1351         aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
1352     }
1353     return aPoint;
1354 }
1355 }
1356 
1357 void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer ) const
1358 {
1359     Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1360                                m_pWriter->m_aMapMode,
1361                                m_pWriter->getReferenceDevice(),
1362                                rPoint ) );
1363 
1364     sal_Int32 nValue    = aPoint.X();
1365 
1366     appendFixedInt( nValue, rBuffer );
1367 
1368     rBuffer.append( ' ' );
1369 
1370     nValue      = pointToPixel(getHeight()) - aPoint.Y();
1371 
1372     appendFixedInt( nValue, rBuffer );
1373 }
1374 
1375 void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const
1376 {
1377     double fValue   = pixelToPoint(rPoint.getX());
1378 
1379     appendDouble( fValue, rBuffer, nLog10Divisor );
1380     rBuffer.append( ' ' );
1381     fValue      = getHeight() - pixelToPoint(rPoint.getY());
1382     appendDouble( fValue, rBuffer, nLog10Divisor );
1383 }
1384 
1385 void PDFWriterImpl::PDFPage::appendRect( const tools::Rectangle& rRect, OStringBuffer& rBuffer ) const
1386 {
1387     appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer );
1388     rBuffer.append( ' ' );
1389     appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false );
1390     rBuffer.append( ' ' );
1391     appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer );
1392     rBuffer.append( " re" );
1393 }
1394 
1395 void PDFWriterImpl::PDFPage::convertRect( tools::Rectangle& rRect ) const
1396 {
1397     Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1398                              m_pWriter->m_aMapMode,
1399                              m_pWriter->getReferenceDevice(),
1400                              rRect.BottomLeft() + Point( 0, 1 )
1401                              );
1402     Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1403                               m_pWriter->m_aMapMode,
1404                               m_pWriter->getReferenceDevice(),
1405                               rRect.GetSize() );
1406     rRect.Left()    = aLL.X();
1407     rRect.Right()   = aLL.X() + aSize.Width();
1408     rRect.Top()     = pointToPixel(getHeight()) - aLL.Y();
1409     rRect.Bottom()  = rRect.Top() + aSize.Height();
1410 }
1411 
1412 void PDFWriterImpl::PDFPage::appendPolygon( const tools::Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1413 {
1414     sal_uInt16 nPoints = rPoly.GetSize();
1415     /*
1416      *  #108582# applications do weird things
1417      */
1418     sal_uInt32 nBufLen = rBuffer.getLength();
1419     if( nPoints > 0 )
1420     {
1421         const PolyFlags* pFlagArray = rPoly.GetConstFlagAry();
1422         appendPoint( rPoly[0], rBuffer );
1423         rBuffer.append( " m\n" );
1424         for( sal_uInt16 i = 1; i < nPoints; i++ )
1425         {
1426             if( pFlagArray && pFlagArray[i] == PolyFlags::Control && nPoints-i > 2 )
1427             {
1428                 // bezier
1429                 SAL_WARN_IF( pFlagArray[i+1] != PolyFlags::Control || pFlagArray[i+2] == PolyFlags::Control, "vcl.pdfwriter", "unexpected sequence of control points" );
1430                 appendPoint( rPoly[i], rBuffer );
1431                 rBuffer.append( " " );
1432                 appendPoint( rPoly[i+1], rBuffer );
1433                 rBuffer.append( " " );
1434                 appendPoint( rPoly[i+2], rBuffer );
1435                 rBuffer.append( " c" );
1436                 i += 2; // add additionally consumed points
1437             }
1438             else
1439             {
1440                 // line
1441                 appendPoint( rPoly[i], rBuffer );
1442                 rBuffer.append( " l" );
1443             }
1444             if( (rBuffer.getLength() - nBufLen) > 65 )
1445             {
1446                 rBuffer.append( "\n" );
1447                 nBufLen = rBuffer.getLength();
1448             }
1449             else
1450                 rBuffer.append( " " );
1451         }
1452         if( bClose )
1453             rBuffer.append( "h\n" );
1454     }
1455 }
1456 
1457 void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer ) const
1458 {
1459     basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1460                                             m_pWriter->m_aMapMode,
1461                                             m_pWriter->getReferenceDevice(),
1462                                             rPoly ) );
1463 
1464     if( basegfx::utils::isRectangle( aPoly ) )
1465     {
1466         basegfx::B2DRange aRange( aPoly.getB2DRange() );
1467         basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() );
1468         appendPixelPoint( aBL, rBuffer );
1469         rBuffer.append( ' ' );
1470         appendMappedLength( aRange.getWidth(), rBuffer, false, nLog10Divisor );
1471         rBuffer.append( ' ' );
1472         appendMappedLength( aRange.getHeight(), rBuffer, true, nLog10Divisor );
1473         rBuffer.append( " re\n" );
1474         return;
1475     }
1476     sal_uInt32 nPoints = aPoly.count();
1477     if( nPoints > 0 )
1478     {
1479         sal_uInt32 nBufLen = rBuffer.getLength();
1480         basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) );
1481         appendPixelPoint( aLastPoint, rBuffer );
1482         rBuffer.append( " m\n" );
1483         for( sal_uInt32 i = 1; i <= nPoints; i++ )
1484         {
1485             if( i != nPoints || aPoly.isClosed() )
1486             {
1487                 sal_uInt32 nCurPoint  = i % nPoints;
1488                 sal_uInt32 nLastPoint = i-1;
1489                 basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) );
1490                 if( aPoly.isNextControlPointUsed( nLastPoint ) &&
1491                     aPoly.isPrevControlPointUsed( nCurPoint ) )
1492                 {
1493                     appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1494                     rBuffer.append( ' ' );
1495                     appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1496                     rBuffer.append( ' ' );
1497                     appendPixelPoint( aPoint, rBuffer );
1498                     rBuffer.append( " c" );
1499                 }
1500                 else if( aPoly.isNextControlPointUsed( nLastPoint ) )
1501                 {
1502                     appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1503                     rBuffer.append( ' ' );
1504                     appendPixelPoint( aPoint, rBuffer );
1505                     rBuffer.append( " y" );
1506                 }
1507                 else if( aPoly.isPrevControlPointUsed( nCurPoint ) )
1508                 {
1509                     appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1510                     rBuffer.append( ' ' );
1511                     appendPixelPoint( aPoint, rBuffer );
1512                     rBuffer.append( " v" );
1513                 }
1514                 else
1515                 {
1516                     appendPixelPoint( aPoint, rBuffer );
1517                     rBuffer.append( " l" );
1518                 }
1519                 if( (rBuffer.getLength() - nBufLen) > 65 )
1520                 {
1521                     rBuffer.append( "\n" );
1522                     nBufLen = rBuffer.getLength();
1523                 }
1524                 else
1525                     rBuffer.append( " " );
1526             }
1527         }
1528         rBuffer.append( "h\n" );
1529     }
1530 }
1531 
1532 void PDFWriterImpl::PDFPage::appendPolyPolygon( const tools::PolyPolygon& rPolyPoly, OStringBuffer& rBuffer ) const
1533 {
1534     sal_uInt16 nPolygons = rPolyPoly.Count();
1535     for( sal_uInt16 n = 0; n < nPolygons; n++ )
1536         appendPolygon( rPolyPoly[n], rBuffer );
1537 }
1538 
1539 void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer ) const
1540 {
1541     sal_uInt32 nPolygons = rPolyPoly.count();
1542     for( sal_uInt32 n = 0; n < nPolygons; n++ )
1543         appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer );
1544 }
1545 
1546 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1547 {
1548     sal_Int32 nValue = nLength;
1549     if ( nLength < 0 )
1550     {
1551         rBuffer.append( '-' );
1552         nValue = -nLength;
1553     }
1554     Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1555                              m_pWriter->m_aMapMode,
1556                              m_pWriter->getReferenceDevice(),
1557                              Size( nValue, nValue ) ) );
1558     nValue = bVertical ? aSize.Height() : aSize.Width();
1559     if( pOutLength )
1560         *pOutLength = ((nLength < 0 ) ? -nValue : nValue);
1561 
1562     appendFixedInt( nValue, rBuffer );
1563 }
1564 
1565 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32 nPrecision ) const
1566 {
1567     Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1568                              m_pWriter->m_aMapMode,
1569                              m_pWriter->getReferenceDevice(),
1570                              Size( 1000, 1000 ) ) );
1571     fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0);
1572     appendDouble( fLength, rBuffer, nPrecision );
1573 }
1574 
1575 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const
1576 {
1577     if(LineStyle::Dash == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen())
1578     {
1579         // dashed and non-degraded case, check for implementation limits of dash array
1580         // in PDF reader apps (e.g. acroread)
1581         if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10)
1582         {
1583             return false;
1584         }
1585     }
1586 
1587     if(basegfx::B2DLineJoin::NONE != rInfo.GetLineJoin())
1588     {
1589         // LineJoin used, ExtLineInfo required
1590         return false;
1591     }
1592 
1593     if(css::drawing::LineCap_BUTT != rInfo.GetLineCap())
1594     {
1595         // LineCap used, ExtLineInfo required
1596         return false;
1597     }
1598 
1599     if( rInfo.GetStyle() == LineStyle::Dash )
1600     {
1601         rBuffer.append( "[ " );
1602         if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case
1603         {
1604             appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1605             rBuffer.append( ' ' );
1606             appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1607             rBuffer.append( ' ' );
1608         }
1609         else
1610         {
1611             for( int n = 0; n < rInfo.GetDashCount(); n++ )
1612             {
1613                 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1614                 rBuffer.append( ' ' );
1615                 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1616                 rBuffer.append( ' ' );
1617             }
1618             for( int m = 0; m < rInfo.GetDotCount(); m++ )
1619             {
1620                 appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer );
1621                 rBuffer.append( ' ' );
1622                 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1623                 rBuffer.append( ' ' );
1624             }
1625         }
1626         rBuffer.append( "] 0 d\n" );
1627     }
1628 
1629     if( rInfo.GetWidth() > 1 )
1630     {
1631         appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer );
1632         rBuffer.append( " w\n" );
1633     }
1634     else if( rInfo.GetWidth() == 0 )
1635     {
1636         // "pixel" line
1637         appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->GetDPIX()), rBuffer );
1638         rBuffer.append( " w\n" );
1639     }
1640 
1641     return true;
1642 }
1643 
1644 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const
1645 {
1646     if( nWidth <= 0 )
1647         return;
1648     if( nDelta < 1 )
1649         nDelta = 1;
1650 
1651     rBuffer.append( "0 " );
1652     appendMappedLength( nY, rBuffer );
1653     rBuffer.append( " m\n" );
1654     for( sal_Int32 n = 0; n < nWidth; )
1655     {
1656         n += nDelta;
1657         appendMappedLength( n, rBuffer, false );
1658         rBuffer.append( ' ' );
1659         appendMappedLength( nDelta+nY, rBuffer );
1660         rBuffer.append( ' ' );
1661         n += nDelta;
1662         appendMappedLength( n, rBuffer, false );
1663         rBuffer.append( ' ' );
1664         appendMappedLength( nY, rBuffer );
1665         rBuffer.append( " v " );
1666         if( n < nWidth )
1667         {
1668             n += nDelta;
1669             appendMappedLength( n, rBuffer, false );
1670             rBuffer.append( ' ' );
1671             appendMappedLength( nY-nDelta, rBuffer );
1672             rBuffer.append( ' ' );
1673             n += nDelta;
1674             appendMappedLength( n, rBuffer, false );
1675             rBuffer.append( ' ' );
1676             appendMappedLength( nY, rBuffer );
1677             rBuffer.append( " v\n" );
1678         }
1679     }
1680     rBuffer.append( "S\n" );
1681 }
1682 
1683  PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext,
1684                                const css::uno::Reference< css::beans::XMaterialHolder >& xEnc,
1685                                PDFWriter& i_rOuterFace)
1686         :
1687         m_pReferenceDevice( nullptr ),
1688         m_aMapMode( MapUnit::MapPoint, Point(), Fraction( 1, pointToPixel(1) ), Fraction( 1, pointToPixel(1) ) ),
1689         m_nCurrentStructElement( 0 ),
1690         m_bEmitStructure( true ),
1691         m_nNextFID( 1 ),
1692         m_nInheritedPageWidth( 595 ),  // default A4
1693         m_nInheritedPageHeight( 842 ), // default A4
1694         m_nCurrentPage( -1 ),
1695         m_nCatalogObject(0),
1696         m_nSignatureObject( -1 ),
1697         m_nSignatureContentOffset( 0 ),
1698         m_nSignatureLastByteRangeNoOffset( 0 ),
1699         m_nResourceDict( -1 ),
1700         m_nFontDictObject( -1 ),
1701         m_aContext(rContext),
1702         m_aFile(m_aContext.URL),
1703         m_bOpen(false),
1704         m_aDocDigest( rtl_digest_createMD5() ),
1705         m_aCipher( nullptr ),
1706         m_aDigest( nullptr ),
1707         m_nKeyLength(0),
1708         m_nRC4KeyLength(0),
1709         m_bEncryptThisStream( false ),
1710         m_nAccessPermissions(0),
1711         m_pEncryptionBuffer( nullptr ),
1712         m_nEncryptionBufferSize( 0 ),
1713         m_bIsPDF_A1( false ),
1714         m_rOuterFace( i_rOuterFace )
1715 {
1716 #ifdef DO_TEST_PDF
1717     static bool bOnce = true;
1718     if( bOnce )
1719     {
1720         bOnce = false;
1721         doTestCode();
1722     }
1723 #endif
1724     m_aStructure.emplace_back( );
1725     m_aStructure[0].m_nOwnElement       = 0;
1726     m_aStructure[0].m_nParentElement    = 0;
1727 
1728     Font aFont;
1729     aFont.SetFamilyName( "Times" );
1730     aFont.SetFontSize( Size( 0, 12 ) );
1731 
1732     GraphicsState aState;
1733     aState.m_aMapMode       = m_aMapMode;
1734     aState.m_aFont          = aFont;
1735     m_aGraphicsStack.push_front( aState );
1736 
1737     osl::File::RC aError = m_aFile.open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
1738     if (aError != osl::File::E_None)
1739     {
1740         if (aError == osl::File::E_EXIST)
1741         {
1742             aError = m_aFile.open(osl_File_OpenFlag_Write);
1743             if (aError == osl::File::E_None)
1744                 aError = m_aFile.setSize(0);
1745         }
1746     }
1747     if (aError != osl::File::E_None)
1748         return;
1749 
1750     m_bOpen = true;
1751 
1752     // setup DocInfo
1753     setupDocInfo();
1754 
1755     /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1756     m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1757     m_aDigest = rtl_digest_createMD5();
1758 
1759     /* the size of the Codec default maximum */
1760     /* is this 0x4000 required to be the same as MAX_SIGNATURE_CONTENT_LENGTH or just coincidentally the same at the moment? */
1761     if (!checkEncryptionBufferSize(0x4000))
1762     {
1763         m_aFile.close();
1764         m_bOpen = false;
1765         return;
1766     }
1767 
1768     if( xEnc.is() )
1769         prepareEncryption( xEnc );
1770 
1771     if( m_aContext.Encryption.Encrypt() )
1772     {
1773         // sanity check
1774         if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE ||
1775             m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE ||
1776             m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH
1777            )
1778         {
1779             // the field lengths are invalid ? This was not setup by initEncryption.
1780             // do not encrypt after all
1781             m_aContext.Encryption.OValue.clear();
1782             m_aContext.Encryption.UValue.clear();
1783             OSL_ENSURE( false, "encryption data failed sanity check, encryption disabled" );
1784         }
1785         else // setup key lengths
1786             m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength );
1787     }
1788 
1789     // write header
1790     OStringBuffer aBuffer( 20 );
1791     aBuffer.append( "%PDF-" );
1792     switch( m_aContext.Version )
1793     {
1794         case PDFWriter::PDFVersion::PDF_1_2: aBuffer.append( "1.2" );break;
1795         case PDFWriter::PDFVersion::PDF_1_3: aBuffer.append( "1.3" );break;
1796         case PDFWriter::PDFVersion::PDF_A_1:
1797         default:
1798         case PDFWriter::PDFVersion::PDF_1_4: aBuffer.append( "1.4" );break;
1799         case PDFWriter::PDFVersion::PDF_1_5: aBuffer.append( "1.5" );break;
1800     }
1801     // append something binary as comment (suggested in PDF Reference)
1802     aBuffer.append( "\n%\303\244\303\274\303\266\303\237\n" );
1803     if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) )
1804     {
1805         m_aFile.close();
1806         m_bOpen = false;
1807         return;
1808     }
1809 
1810     // insert outline root
1811     m_aOutline.emplace_back( );
1812 
1813     m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDFVersion::PDF_A_1);
1814     if( m_bIsPDF_A1 )
1815         m_aContext.Version = PDFWriter::PDFVersion::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour
1816 }
1817 
1818 PDFWriterImpl::~PDFWriterImpl()
1819 {
1820     if( m_aDocDigest )
1821         rtl_digest_destroyMD5( m_aDocDigest );
1822     m_pReferenceDevice.disposeAndClear();
1823 
1824     if( m_aCipher )
1825         rtl_cipher_destroyARCFOUR( m_aCipher );
1826     if( m_aDigest )
1827         rtl_digest_destroyMD5( m_aDigest );
1828 
1829     rtl_freeMemory( m_pEncryptionBuffer );
1830 }
1831 
1832 void PDFWriterImpl::setupDocInfo()
1833 {
1834     std::vector< sal_uInt8 > aId;
1835     m_aCreationDateString = PDFWriter::GetDateTime();
1836     computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString );
1837     if( m_aContext.Encryption.DocumentIdentifier.empty() )
1838         m_aContext.Encryption.DocumentIdentifier = aId;
1839 }
1840 
1841 OString PDFWriter::GetDateTime()
1842 {
1843     OStringBuffer aRet;
1844 
1845     TimeValue aTVal, aGMT;
1846     oslDateTime aDT;
1847     osl_getSystemTime(&aGMT);
1848     osl_getLocalTimeFromSystemTime(&aGMT, &aTVal);
1849     osl_getDateTimeFromTimeValue(&aTVal, &aDT);
1850     aRet.append("D:");
1851     aRet.append((sal_Char)('0' + ((aDT.Year / 1000) % 10)));
1852     aRet.append((sal_Char)('0' + ((aDT.Year / 100) % 10)));
1853     aRet.append((sal_Char)('0' + ((aDT.Year / 10) % 10)));
1854     aRet.append((sal_Char)('0' + (aDT.Year % 10)));
1855     aRet.append((sal_Char)('0' + ((aDT.Month / 10) % 10)));
1856     aRet.append((sal_Char)('0' + (aDT.Month % 10)));
1857     aRet.append((sal_Char)('0' + ((aDT.Day / 10) % 10)));
1858     aRet.append((sal_Char)('0' + (aDT.Day % 10)));
1859     aRet.append((sal_Char)('0' + ((aDT.Hours / 10) % 10)));
1860     aRet.append((sal_Char)('0' + (aDT.Hours % 10)));
1861     aRet.append((sal_Char)('0' + ((aDT.Minutes / 10) % 10)));
1862     aRet.append((sal_Char)('0' + (aDT.Minutes % 10)));
1863     aRet.append((sal_Char)('0' + ((aDT.Seconds / 10) % 10)));
1864     aRet.append((sal_Char)('0' + (aDT.Seconds % 10)));
1865 
1866     sal_uInt32 nDelta = 0;
1867     if (aGMT.Seconds > aTVal.Seconds)
1868     {
1869         aRet.append("-");
1870         nDelta = aGMT.Seconds-aTVal.Seconds;
1871     }
1872     else if (aGMT.Seconds < aTVal.Seconds)
1873     {
1874         aRet.append("+");
1875         nDelta = aTVal.Seconds-aGMT.Seconds;
1876     }
1877     else
1878         aRet.append("Z");
1879 
1880     if (nDelta)
1881     {
1882         aRet.append((sal_Char)('0' + ((nDelta / 36000) % 10)));
1883         aRet.append((sal_Char)('0' + ((nDelta / 3600) % 10)));
1884         aRet.append("'");
1885         aRet.append((sal_Char)('0' + ((nDelta / 600) % 6)));
1886         aRet.append((sal_Char)('0' + ((nDelta / 60) % 10)));
1887     }
1888     aRet.append( "'" );
1889 
1890     return aRet.makeStringAndClear();
1891 }
1892 
1893 void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier,
1894                                                const vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
1895                                                const OString& i_rCString1,
1896                                                OString& o_rCString2
1897                                                )
1898 {
1899     o_rIdentifier.clear();
1900 
1901     //build the document id
1902     OString aInfoValuesOut;
1903     OStringBuffer aID( 1024 );
1904     if( !i_rDocInfo.Title.isEmpty() )
1905         PDFWriter::AppendUnicodeTextString(i_rDocInfo.Title, aID);
1906     if( !i_rDocInfo.Author.isEmpty() )
1907         PDFWriter::AppendUnicodeTextString(i_rDocInfo.Author, aID);
1908     if( !i_rDocInfo.Subject.isEmpty() )
1909         PDFWriter::AppendUnicodeTextString(i_rDocInfo.Subject, aID);
1910     if( !i_rDocInfo.Keywords.isEmpty() )
1911         PDFWriter::AppendUnicodeTextString(i_rDocInfo.Keywords, aID);
1912     if( !i_rDocInfo.Creator.isEmpty() )
1913         PDFWriter::AppendUnicodeTextString(i_rDocInfo.Creator, aID);
1914     if( !i_rDocInfo.Producer.isEmpty() )
1915         PDFWriter::AppendUnicodeTextString(i_rDocInfo.Producer, aID);
1916 
1917     TimeValue aTVal, aGMT;
1918     oslDateTime aDT;
1919     osl_getSystemTime( &aGMT );
1920     osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
1921     osl_getDateTimeFromTimeValue( &aTVal, &aDT );
1922     OStringBuffer aCreationMetaDateString(64);
1923 
1924     // i59651: we fill the Metadata date string as well, if PDF/A is requested
1925     // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1926     // local time zone offset UTC only, whereas Acrobat 8 seems
1927     // to use the localtime notation only
1928     // according to a recommendation in XMP Specification (Jan 2004, page 75)
1929     // the Acrobat way seems the right approach
1930     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1931     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1932     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1933     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1934     aCreationMetaDateString.append( "-" );
1935     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1936     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1937     aCreationMetaDateString.append( "-" );
1938     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1939     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1940     aCreationMetaDateString.append( "T" );
1941     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1942     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1943     aCreationMetaDateString.append( ":" );
1944     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1945     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1946     aCreationMetaDateString.append( ":" );
1947     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1948     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1949 
1950     sal_uInt32 nDelta = 0;
1951     if( aGMT.Seconds > aTVal.Seconds )
1952     {
1953         nDelta = aGMT.Seconds-aTVal.Seconds;
1954         aCreationMetaDateString.append( "-" );
1955     }
1956     else if( aGMT.Seconds < aTVal.Seconds )
1957     {
1958         nDelta = aTVal.Seconds-aGMT.Seconds;
1959         aCreationMetaDateString.append( "+" );
1960     }
1961     else
1962     {
1963         aCreationMetaDateString.append( "Z" );
1964 
1965     }
1966     if( nDelta )
1967     {
1968         aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1969         aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1970         aCreationMetaDateString.append( ":" );
1971         aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1972         aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1973     }
1974     aID.append( i_rCString1.getStr(), i_rCString1.getLength() );
1975 
1976     aInfoValuesOut = aID.makeStringAndClear();
1977     o_rCString2 = aCreationMetaDateString.makeStringAndClear();
1978 
1979     rtlDigest aDigest = rtl_digest_createMD5();
1980     OSL_ENSURE( aDigest != nullptr, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" );
1981     if( aDigest )
1982     {
1983         rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) );
1984         if( nError == rtl_Digest_E_None )
1985             nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() );
1986         if( nError == rtl_Digest_E_None )
1987         {
1988             o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 );
1989             //the binary form of the doc id is needed for encryption stuff
1990             rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 );
1991         }
1992         rtl_digest_destroyMD5(aDigest);
1993     }
1994 }
1995 
1996 /* i12626 methods */
1997 /*
1998 check if the Unicode string must be encrypted or not, perform the requested task,
1999 append the string as unicode hex, encrypted if needed
2000  */
2001 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2002 {
2003     rOutBuffer.append( "<" );
2004     if( m_aContext.Encryption.Encrypt() )
2005     {
2006         const sal_Unicode* pStr = rInString.getStr();
2007         sal_Int32 nLen = rInString.getLength();
2008         //prepare a unicode string, encrypt it
2009         if( checkEncryptionBufferSize( nLen*2 ) )
2010         {
2011             enableStringEncryption( nInObjectNumber );
2012             sal_uInt8 *pCopy = m_pEncryptionBuffer;
2013             sal_Int32 nChars = 2;
2014             *pCopy++ = 0xFE;
2015             *pCopy++ = 0xFF;
2016             // we need to prepare a byte stream from the unicode string buffer
2017             for( int i = 0; i < nLen; i++ )
2018             {
2019                 sal_Unicode aUnChar = pStr[i];
2020                 *pCopy++ = (sal_uInt8)( aUnChar >> 8 );
2021                 *pCopy++ = (sal_uInt8)( aUnChar & 255 );
2022                 nChars += 2;
2023             }
2024             //encrypt in place
2025             rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars );
2026             //now append, hexadecimal (appendHex), the encrypted result
2027             for(int i = 0; i < nChars; i++)
2028                 appendHex( m_pEncryptionBuffer[i], rOutBuffer );
2029         }
2030     }
2031     else
2032         PDFWriter::AppendUnicodeTextString(rInString, rOutBuffer);
2033     rOutBuffer.append( ">" );
2034 }
2035 
2036 inline void PDFWriterImpl::appendLiteralStringEncrypt( OStringBuffer const & rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2037 {
2038     rOutBuffer.append( "(" );
2039     sal_Int32 nChars = rInString.getLength();
2040     //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
2041     if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) )
2042     {
2043         //encrypt the string in a buffer, then append it
2044         enableStringEncryption( nInObjectNumber );
2045         rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars );
2046         appendLiteralString( reinterpret_cast<sal_Char*>(m_pEncryptionBuffer), nChars, rOutBuffer );
2047     }
2048     else
2049         appendLiteralString( rInString.getStr(), nChars , rOutBuffer );
2050     rOutBuffer.append( ")" );
2051 }
2052 
2053 inline void PDFWriterImpl::appendLiteralStringEncrypt( const OString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2054 {
2055     OStringBuffer aBufferString( rInString );
2056     appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2057 }
2058 
2059 void PDFWriterImpl::appendLiteralStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc )
2060 {
2061     OString aBufferString( OUStringToOString( rInString, nEnc ) );
2062     sal_Int32 nLen = aBufferString.getLength();
2063     OStringBuffer aBuf( nLen );
2064     const sal_Char* pT = aBufferString.getStr();
2065 
2066     for( sal_Int32 i = 0; i < nLen; i++, pT++ )
2067     {
2068         if( (*pT & 0x80) == 0 )
2069             aBuf.append( *pT );
2070         else
2071         {
2072             aBuf.append( '<' );
2073             appendHex( *pT, aBuf );
2074             aBuf.append( '>' );
2075         }
2076     }
2077     aBufferString = aBuf.makeStringAndClear();
2078     appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2079 }
2080 
2081 /* end i12626 methods */
2082 
2083 void PDFWriterImpl::emitComment( const char* pComment )
2084 {
2085     OStringBuffer aLine( 64 );
2086     aLine.append( "% " );
2087     aLine.append( pComment );
2088     aLine.append( "\n" );
2089     writeBuffer( aLine.getStr(), aLine.getLength() );
2090 }
2091 
2092 bool PDFWriterImpl::compressStream( SvMemoryStream* pStream )
2093 {
2094     if (!g_bDebugDisableCompression)
2095     {
2096         pStream->Seek( STREAM_SEEK_TO_END );
2097         sal_uLong nEndPos = pStream->Tell();
2098         pStream->Seek( STREAM_SEEK_TO_BEGIN );
2099         ZCodec aCodec( 0x4000, 0x4000 );
2100         SvMemoryStream aStream;
2101         aCodec.BeginCompression();
2102         aCodec.Write( aStream, static_cast<const sal_uInt8*>(pStream->GetData()), nEndPos );
2103         aCodec.EndCompression();
2104         nEndPos = aStream.Tell();
2105         pStream->Seek( STREAM_SEEK_TO_BEGIN );
2106         aStream.Seek( STREAM_SEEK_TO_BEGIN );
2107         pStream->SetStreamSize( nEndPos );
2108         pStream->WriteBytes( aStream.GetData(), nEndPos );
2109         return true;
2110     }
2111     else
2112         return false;
2113 }
2114 
2115 void PDFWriterImpl::beginCompression()
2116 {
2117     if (!g_bDebugDisableCompression)
2118     {
2119         m_pCodec = o3tl::make_unique<ZCodec>( 0x4000, 0x4000 );
2120         m_pMemStream = o3tl::make_unique<SvMemoryStream>();
2121         m_pCodec->BeginCompression();
2122     }
2123 }
2124 
2125 void PDFWriterImpl::endCompression()
2126 {
2127     if (!g_bDebugDisableCompression && m_pCodec)
2128     {
2129         m_pCodec->EndCompression();
2130         m_pCodec.reset();
2131         sal_uInt64 nLen = m_pMemStream->Tell();
2132         m_pMemStream->Seek( 0 );
2133         writeBuffer( m_pMemStream->GetData(), nLen );
2134         m_pMemStream.reset();
2135     }
2136 }
2137 
2138 bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes )
2139 {
2140     if( ! m_bOpen ) // we are already down the drain
2141         return false;
2142 
2143     if( ! nBytes ) // huh ?
2144         return true;
2145 
2146     if( !m_aOutputStreams.empty() )
2147     {
2148         m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END );
2149         m_aOutputStreams.front().m_pStream->WriteBytes(
2150                 pBuffer, sal::static_int_cast<std::size_t>(nBytes));
2151         return true;
2152     }
2153 
2154     sal_uInt64 nWritten;
2155     if( m_pCodec )
2156     {
2157         m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes );
2158         nWritten = nBytes;
2159     }
2160     else
2161     {
2162         bool  buffOK = true;
2163         if( m_bEncryptThisStream )
2164         {
2165             /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
2166             buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) );
2167             if( buffOK )
2168                 rtl_cipher_encodeARCFOUR( m_aCipher,
2169                                           pBuffer, static_cast<sal_Size>(nBytes),
2170                                           m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) );
2171         }
2172 
2173         const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer  : pBuffer;
2174         if( m_aDocDigest )
2175             rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) );
2176 
2177         if (m_aFile.write(pWriteBuffer, nBytes, nWritten) != osl::File::E_None)
2178             nWritten = 0;
2179 
2180         if( nWritten != nBytes )
2181         {
2182             m_aFile.close();
2183             m_bOpen = false;
2184         }
2185     }
2186 
2187     return nWritten == nBytes;
2188 }
2189 
2190 OutputDevice* PDFWriterImpl::getReferenceDevice()
2191 {
2192     if( ! m_pReferenceDevice )
2193     {
2194         VclPtrInstance<VirtualDevice> pVDev(DeviceFormat::DEFAULT);
2195 
2196         m_pReferenceDevice = pVDev;
2197 
2198         if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 )
2199             pVDev->SetReferenceDevice( VirtualDevice::RefDevMode::PDF1 );
2200         else
2201             pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy );
2202 
2203         pVDev->SetOutputSizePixel( Size( 640, 480 ) );
2204         pVDev->SetMapMode(MapMode(MapUnit::MapMM));
2205 
2206         m_pReferenceDevice->mpPDFWriter = this;
2207         m_pReferenceDevice->ImplUpdateFontData();
2208     }
2209     return m_pReferenceDevice;
2210 }
2211 
2212 static FontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin )
2213 {
2214     FontAttributes aDFA;
2215     aDFA.SetFamilyName( OUString::createFromAscii( rBuiltin.m_pName ) );
2216     aDFA.SetStyleName( OUString::createFromAscii( rBuiltin.m_pStyleName ) );
2217     aDFA.SetFamilyType( rBuiltin.m_eFamily );
2218     aDFA.SetSymbolFlag( rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 );
2219     aDFA.SetPitch( rBuiltin.m_ePitch );
2220     aDFA.SetWeight( rBuiltin.m_eWeight );
2221     aDFA.SetItalic( rBuiltin.m_eItalic );
2222     aDFA.SetWidthType( rBuiltin.m_eWidthType );
2223 
2224     aDFA.SetQuality( 50000 );
2225     return aDFA;
2226 }
2227 
2228 PdfBuiltinFontFace::PdfBuiltinFontFace( const PDFWriterImpl::BuiltinFont& rBuiltin )
2229 :   PhysicalFontFace( GetDevFontAttributes(rBuiltin) ),
2230     mrBuiltin( rBuiltin )
2231 {}
2232 
2233 LogicalFontInstance* PdfBuiltinFontFace::CreateFontInstance( FontSelectPattern& rFSD ) const
2234 {
2235     LogicalFontInstance* pEntry = new LogicalFontInstance( rFSD );
2236     return pEntry;
2237 }
2238 
2239 
2240 void PDFWriterImpl::newPage( double nPageWidth, double nPageHeight, PDFWriter::Orientation eOrientation )
2241 {
2242     endPage();
2243     m_nCurrentPage = m_aPages.size();
2244     m_aPages.emplace_back(this, nPageWidth, nPageHeight, eOrientation );
2245     m_aPages.back().beginStream();
2246 
2247     // setup global graphics state
2248     // linewidth is "1 pixel" by default
2249     OStringBuffer aBuf( 16 );
2250     appendDouble( 72.0/double(getReferenceDevice()->GetDPIX()), aBuf );
2251     aBuf.append( " w\n" );
2252     writeBuffer( aBuf.getStr(), aBuf.getLength() );
2253 }
2254 
2255 void PDFWriterImpl::endPage()
2256 {
2257     if( !m_aPages.empty() )
2258     {
2259         // close eventual MC sequence
2260         endStructureElementMCSeq();
2261 
2262         // sanity check
2263         if( !m_aOutputStreams.empty() )
2264         {
2265             OSL_FAIL( "redirection across pages !!!" );
2266             m_aOutputStreams.clear(); // leak !
2267             m_aMapMode.SetOrigin( Point() );
2268         }
2269 
2270         m_aGraphicsStack.clear();
2271         m_aGraphicsStack.emplace_back( );
2272 
2273         // this should pop the PDF graphics stack if necessary
2274         updateGraphicsState();
2275 
2276         m_aPages.back().endStream();
2277 
2278         // reset the default font
2279         Font aFont;
2280         aFont.SetFamilyName( "Times" );
2281         aFont.SetFontSize( Size( 0, 12 ) );
2282 
2283         m_aCurrentPDFState = m_aGraphicsStack.front();
2284         m_aGraphicsStack.front().m_aFont =  aFont;
2285 
2286         for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin();
2287              it != m_aBitmaps.end(); ++it )
2288         {
2289             if( ! it->m_aBitmap.IsEmpty() )
2290             {
2291                 writeBitmapObject( *it );
2292                 it->m_aBitmap = BitmapEx();
2293             }
2294         }
2295         for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg )
2296         {
2297             if( jpeg->m_pStream )
2298             {
2299                 writeJPG( *jpeg );
2300                 jpeg->m_pStream.reset();
2301                 jpeg->m_aMask = Bitmap();
2302             }
2303         }
2304         for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin();
2305              t != m_aTransparentObjects.end(); ++t )
2306         {
2307             if( t->m_pContentStream )
2308             {
2309                 writeTransparentObject( *t );
2310                 delete t->m_pContentStream;
2311                 t->m_pContentStream = nullptr;
2312             }
2313         }
2314     }
2315 }
2316 
2317 sal_Int32 PDFWriterImpl::createObject()
2318 {
2319     m_aObjects.push_back( ~0U );
2320     return m_aObjects.size();
2321 }
2322 
2323 bool PDFWriterImpl::updateObject( sal_Int32 n )
2324 {
2325     if( ! m_bOpen )
2326         return false;
2327 
2328     sal_uInt64 nOffset = ~0U;
2329     osl::File::RC aError = m_aFile.getPos(nOffset);
2330     SAL_WARN_IF( aError != osl::File::E_None, "vcl.pdfwriter", "could not register object" );
2331     if (aError != osl::File::E_None)
2332     {
2333         m_aFile.close();
2334         m_bOpen = false;
2335     }
2336     m_aObjects[ n-1 ] = nOffset;
2337     return aError == osl::File::E_None;
2338 }
2339 
2340 #define CHECK_RETURN( x ) if( !(x) ) return 0
2341 #define CHECK_RETURN2( x ) if( !(x) ) return
2342 
2343 sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject )
2344 {
2345     if( nObject > 0 )
2346     {
2347         OStringBuffer aLine( 1024 );
2348 
2349         aLine.append( nObject );
2350         aLine.append( " 0 obj\n"
2351                       "<</Nums[\n" );
2352         sal_Int32 nTreeItems = m_aStructParentTree.size();
2353         for( sal_Int32 n = 0; n < nTreeItems; n++ )
2354         {
2355             aLine.append( n );
2356             aLine.append( ' ' );
2357             aLine.append( m_aStructParentTree[n] );
2358             aLine.append( "\n" );
2359         }
2360         aLine.append( "]>>\nendobj\n\n" );
2361         CHECK_RETURN( updateObject( nObject ) );
2362         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2363     }
2364     return nObject;
2365 }
2366 
2367 const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr )
2368 {
2369     static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings;
2370     // fill maps once
2371     if( aAttributeStrings.empty() )
2372     {
2373         aAttributeStrings[ PDFWriter::Placement ]           = "Placement";
2374         aAttributeStrings[ PDFWriter::WritingMode ]         = "WritingMode";
2375         aAttributeStrings[ PDFWriter::SpaceBefore ]         = "SpaceBefore";
2376         aAttributeStrings[ PDFWriter::SpaceAfter ]          = "SpaceAfter";
2377         aAttributeStrings[ PDFWriter::StartIndent ]         = "StartIndent";
2378         aAttributeStrings[ PDFWriter::EndIndent ]           = "EndIndent";
2379         aAttributeStrings[ PDFWriter::TextIndent ]          = "TextIndent";
2380         aAttributeStrings[ PDFWriter::TextAlign ]           = "TextAlign";
2381         aAttributeStrings[ PDFWriter::Width ]               = "Width";
2382         aAttributeStrings[ PDFWriter::Height ]              = "Height";
2383         aAttributeStrings[ PDFWriter::BlockAlign ]          = "BlockAlign";
2384         aAttributeStrings[ PDFWriter::InlineAlign ]         = "InlineAlign";
2385         aAttributeStrings[ PDFWriter::LineHeight ]          = "LineHeight";
2386         aAttributeStrings[ PDFWriter::BaselineShift ]       = "BaselineShift";
2387         aAttributeStrings[ PDFWriter::TextDecorationType ]  = "TextDecorationType";
2388         aAttributeStrings[ PDFWriter::ListNumbering ]       = "ListNumbering";
2389         aAttributeStrings[ PDFWriter::RowSpan ]             = "RowSpan";
2390         aAttributeStrings[ PDFWriter::ColSpan ]             = "ColSpan";
2391         aAttributeStrings[ PDFWriter::LinkAnnotation ]      = "LinkAnnotation";
2392     }
2393 
2394     std::map< PDFWriter::StructAttribute, const char* >::const_iterator it =
2395         aAttributeStrings.find( eAttr );
2396 
2397     if( it == aAttributeStrings.end() )
2398         SAL_INFO("vcl.pdfwriter", "invalid PDFWriter::StructAttribute " << eAttr);
2399 
2400     return it != aAttributeStrings.end() ? it->second : "";
2401 }
2402 
2403 const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal )
2404 {
2405     static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings;
2406 
2407     if( aValueStrings.empty() )
2408     {
2409         aValueStrings[ PDFWriter::NONE ]                    = "None";
2410         aValueStrings[ PDFWriter::Block ]                   = "Block";
2411         aValueStrings[ PDFWriter::Inline ]                  = "Inline";
2412         aValueStrings[ PDFWriter::Before ]                  = "Before";
2413         aValueStrings[ PDFWriter::After ]                   = "After";
2414         aValueStrings[ PDFWriter::Start ]                   = "Start";
2415         aValueStrings[ PDFWriter::End ]                     = "End";
2416         aValueStrings[ PDFWriter::LrTb ]                    = "LrTb";
2417         aValueStrings[ PDFWriter::RlTb ]                    = "RlTb";
2418         aValueStrings[ PDFWriter::TbRl ]                    = "TbRl";
2419         aValueStrings[ PDFWriter::Center ]                  = "Center";
2420         aValueStrings[ PDFWriter::Justify ]                 = "Justify";
2421         aValueStrings[ PDFWriter::Auto ]                    = "Auto";
2422         aValueStrings[ PDFWriter::Middle ]                  = "Middle";
2423         aValueStrings[ PDFWriter::Normal ]                  = "Normal";
2424         aValueStrings[ PDFWriter::Underline ]               = "Underline";
2425         aValueStrings[ PDFWriter::Overline ]                = "Overline";
2426         aValueStrings[ PDFWriter::LineThrough ]             = "LineThrough";
2427         aValueStrings[ PDFWriter::Disc ]                    = "Disc";
2428         aValueStrings[ PDFWriter::Circle ]                  = "Circle";
2429         aValueStrings[ PDFWriter::Square ]                  = "Square";
2430         aValueStrings[ PDFWriter::Decimal ]                 = "Decimal";
2431         aValueStrings[ PDFWriter::UpperRoman ]              = "UpperRoman";
2432         aValueStrings[ PDFWriter::LowerRoman ]              = "LowerRoman";
2433         aValueStrings[ PDFWriter::UpperAlpha ]              = "UpperAlpha";
2434         aValueStrings[ PDFWriter::LowerAlpha ]              = "LowerAlpha";
2435     }
2436 
2437     std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it =
2438         aValueStrings.find( eVal );
2439 
2440     if( it == aValueStrings.end() )
2441         SAL_INFO("vcl.pdfwriter", "invalid PDFWriter::StructAttributeValue " << eVal);
2442 
2443     return it != aValueStrings.end() ? it->second : "";
2444 }
2445 
2446 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt )
2447 {
2448     o_rLine.append( "/" );
2449     o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) );
2450 
2451     if( i_rVal.eValue != PDFWriter::Invalid )
2452     {
2453         o_rLine.append( "/" );
2454         o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) );
2455     }
2456     else
2457     {
2458         // numerical value
2459         o_rLine.append( " " );
2460         if( i_bIsFixedInt )
2461             appendFixedInt( i_rVal.nValue, o_rLine );
2462         else
2463             o_rLine.append( i_rVal.nValue );
2464     }
2465     o_rLine.append( "\n" );
2466 }
2467 
2468 OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
2469 {
2470     // create layout, list and table attribute sets
2471     OStringBuffer aLayout(256), aList(64), aTable(64);
2472     for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin();
2473          it != i_rEle.m_aAttributes.end(); ++it )
2474     {
2475         if( it->first == PDFWriter::ListNumbering )
2476             appendStructureAttributeLine( it->first, it->second, aList, true );
2477         else if( it->first == PDFWriter::RowSpan ||
2478                  it->first == PDFWriter::ColSpan )
2479             appendStructureAttributeLine( it->first, it->second, aTable, false );
2480         else if( it->first == PDFWriter::LinkAnnotation )
2481         {
2482             sal_Int32 nLink = it->second.nValue;
2483             std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
2484                 m_aLinkPropertyMap.find( nLink );
2485             if( link_it != m_aLinkPropertyMap.end() )
2486                 nLink = link_it->second;
2487             if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() )
2488             {
2489                 // update struct parent of link
2490                 OStringBuffer aStructParentEntry( 32 );
2491                 aStructParentEntry.append( i_rEle.m_nObject );
2492                 aStructParentEntry.append( " 0 R" );
2493                 m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() );
2494                 m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1;
2495 
2496                 sal_Int32 nRefObject = createObject();
2497                 OStringBuffer aRef( 256 );
2498                 aRef.append( nRefObject );
2499                 aRef.append( " 0 obj\n"
2500                              "<</Type/OBJR/Obj " );
2501                 aRef.append( m_aLinks[ nLink ].m_nObject );
2502                 aRef.append( " 0 R>>\n"
2503                              "endobj\n\n"
2504                              );
2505                 if (updateObject(nRefObject))
2506                 {
2507                     writeBuffer( aRef.getStr(), aRef.getLength() );
2508                 }
2509 
2510                 i_rEle.m_aKids.emplace_back( nRefObject );
2511             }
2512             else
2513             {
2514                 OSL_FAIL( "unresolved link id for Link structure" );
2515                 SAL_INFO("vcl.pdfwriter", "unresolved link id " << nLink << " for Link structure");
2516                 if (g_bDebugDisableCompression)
2517                 {
2518                     OStringBuffer aLine( "unresolved link id " );
2519                     aLine.append( nLink );
2520                     aLine.append( " for Link structure" );
2521                     emitComment( aLine.getStr() );
2522                 }
2523             }
2524         }
2525         else
2526             appendStructureAttributeLine( it->first, it->second, aLayout, true );
2527     }
2528     if( ! i_rEle.m_aBBox.IsEmpty() )
2529     {
2530         aLayout.append( "/BBox[" );
2531         appendFixedInt( i_rEle.m_aBBox.Left(), aLayout );
2532         aLayout.append( " " );
2533         appendFixedInt( i_rEle.m_aBBox.Top(), aLayout );
2534         aLayout.append( " " );
2535         appendFixedInt( i_rEle.m_aBBox.Right(), aLayout );
2536         aLayout.append( " " );
2537         appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout );
2538         aLayout.append( "]\n" );
2539     }
2540 
2541     std::vector< sal_Int32 > aAttribObjects;
2542     if( !aLayout.isEmpty() )
2543     {
2544         aAttribObjects.push_back( createObject() );
2545         if (updateObject( aAttribObjects.back() ))
2546         {
2547             OStringBuffer aObj( 64 );
2548             aObj.append( aAttribObjects.back() );
2549             aObj.append( " 0 obj\n"
2550                          "<</O/Layout\n" );
2551             aLayout.append( ">>\nendobj\n\n" );
2552             writeBuffer( aObj.getStr(), aObj.getLength() );
2553             writeBuffer( aLayout.getStr(), aLayout.getLength() );
2554         }
2555     }
2556     if( !aList.isEmpty() )
2557     {
2558         aAttribObjects.push_back( createObject() );
2559         if (updateObject( aAttribObjects.back() ))
2560         {
2561             OStringBuffer aObj( 64 );
2562             aObj.append( aAttribObjects.back() );
2563             aObj.append( " 0 obj\n"
2564                          "<</O/List\n" );
2565             aList.append( ">>\nendobj\n\n" );
2566             writeBuffer( aObj.getStr(), aObj.getLength() );
2567             writeBuffer( aList.getStr(), aList.getLength() );
2568         }
2569     }
2570     if( !aTable.isEmpty() )
2571     {
2572         aAttribObjects.push_back( createObject() );
2573         if (updateObject( aAttribObjects.back() ))
2574         {
2575             OStringBuffer aObj( 64 );
2576             aObj.append( aAttribObjects.back() );
2577             aObj.append( " 0 obj\n"
2578                          "<</O/Table\n" );
2579             aTable.append( ">>\nendobj\n\n" );
2580             writeBuffer( aObj.getStr(), aObj.getLength() );
2581             writeBuffer( aTable.getStr(), aTable.getLength() );
2582         }
2583     }
2584 
2585     OStringBuffer aRet( 64 );
2586     if( aAttribObjects.size() > 1 )
2587         aRet.append( " [" );
2588     for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin();
2589          at_it != aAttribObjects.end(); ++at_it )
2590     {
2591         aRet.append( " " );
2592         aRet.append( *at_it );
2593         aRet.append( " 0 R" );
2594     }
2595     if( aAttribObjects.size() > 1 )
2596         aRet.append( " ]" );
2597     return aRet.makeStringAndClear();
2598 }
2599 
2600 sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
2601 {
2602     if(
2603        // do not emit NonStruct and its children
2604        rEle.m_eType == PDFWriter::NonStructElement &&
2605        rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root
2606        )
2607         return 0;
2608 
2609     for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
2610     {
2611         if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
2612         {
2613             PDFStructureElement& rChild = m_aStructure[ *it ];
2614             if( rChild.m_eType != PDFWriter::NonStructElement )
2615             {
2616                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
2617                     emitStructure( rChild );
2618                 else
2619                 {
2620                     OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure element" );
2621                     SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::emitStructure: invalid child structure element with id " << *it);
2622                 }
2623             }
2624         }
2625         else
2626         {
2627             OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
2628             SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::emitStructure: invalid child structure id " << *it);
2629         }
2630     }
2631 
2632     OStringBuffer aLine( 512 );
2633     aLine.append( rEle.m_nObject );
2634     aLine.append( " 0 obj\n"
2635                   "<</Type" );
2636     sal_Int32 nParentTree = -1;
2637     if( rEle.m_nOwnElement == rEle.m_nParentElement )
2638     {
2639         nParentTree = createObject();
2640         CHECK_RETURN( nParentTree );
2641         aLine.append( "/StructTreeRoot\n" );
2642         aLine.append( "/ParentTree " );
2643         aLine.append( nParentTree );
2644         aLine.append( " 0 R\n" );
2645         if( ! m_aRoleMap.empty() )
2646         {
2647             aLine.append( "/RoleMap<<" );
2648             for( std::unordered_map<OString,OString>::const_iterator
2649                  it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it )
2650             {
2651                 aLine.append( '/' );
2652                 aLine.append(it->first);
2653                 aLine.append( '/' );
2654                 aLine.append( it->second );
2655                 aLine.append( '\n' );
2656             }
2657             aLine.append( ">>\n" );
2658         }
2659     }
2660     else
2661     {
2662         aLine.append( "/StructElem\n"
2663                       "/S/" );
2664         if( !rEle.m_aAlias.isEmpty() )
2665             aLine.append( rEle.m_aAlias );
2666         else
2667             aLine.append( getStructureTag( rEle.m_eType ) );
2668         aLine.append( "\n"
2669                       "/P " );
2670         aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject );
2671         aLine.append( " 0 R\n"
2672                       "/Pg " );
2673         aLine.append( rEle.m_nFirstPageObject );
2674         aLine.append( " 0 R\n" );
2675         if( !rEle.m_aActualText.isEmpty() )
2676         {
2677             aLine.append( "/ActualText" );
2678             appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine );
2679             aLine.append( "\n" );
2680         }
2681         if( !rEle.m_aAltText.isEmpty() )
2682         {
2683             aLine.append( "/Alt" );
2684             appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine );
2685             aLine.append( "\n" );
2686         }
2687     }
2688     if( (! rEle.m_aBBox.IsEmpty()) || (! rEle.m_aAttributes.empty()) )
2689     {
2690         OString aAttribs =  emitStructureAttributes( rEle );
2691         if( !aAttribs.isEmpty() )
2692         {
2693             aLine.append( "/A" );
2694             aLine.append( aAttribs );
2695             aLine.append( "\n" );
2696         }
2697     }
2698     if( !rEle.m_aLocale.Language.isEmpty() )
2699     {
2700         /* PDF allows only RFC 3066, which is only partly BCP 47 and does not
2701          * include script tags and others.
2702          * http://pdf.editme.com/pdfua-naturalLanguageSpecification
2703          * http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf#page=886
2704          * https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf#M13.9.19332.1Heading.97.Natural.Language.Specification
2705          * */
2706         LanguageTag aLanguageTag( rEle.m_aLocale);
2707         OUString aLanguage, aScript, aCountry;
2708         aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
2709         if (!aLanguage.isEmpty())
2710         {
2711             OUStringBuffer aLocBuf( 16 );
2712             aLocBuf.append( aLanguage );
2713             if( !aCountry.isEmpty() )
2714             {
2715                 aLocBuf.append( '-' );
2716                 aLocBuf.append( aCountry );
2717             }
2718             aLine.append( "/Lang" );
2719             appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine );
2720             aLine.append( "\n" );
2721         }
2722     }
2723     if( ! rEle.m_aKids.empty() )
2724     {
2725         unsigned int i = 0;
2726         aLine.append( "/K[" );
2727         for( std::list< PDFStructureElementKid >::const_iterator it =
2728                  rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ )
2729         {
2730             if( it->nMCID == -1 )
2731             {
2732                 aLine.append( it->nObject );
2733                 aLine.append( " 0 R" );
2734                 aLine.append( ( (i & 15) == 15 ) ? "\n" : " " );
2735             }
2736             else
2737             {
2738                 if( it->nObject == rEle.m_nFirstPageObject )
2739                 {
2740                     aLine.append( it->nMCID );
2741                     aLine.append( " " );
2742                 }
2743                 else
2744                 {
2745                     aLine.append( "<</Type/MCR/Pg " );
2746                     aLine.append( it->nObject );
2747                     aLine.append( " 0 R /MCID " );
2748                     aLine.append( it->nMCID );
2749                     aLine.append( ">>\n" );
2750                 }
2751             }
2752         }
2753         aLine.append( "]\n" );
2754     }
2755     aLine.append( ">>\nendobj\n\n" );
2756 
2757     CHECK_RETURN( updateObject( rEle.m_nObject ) );
2758     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2759 
2760     CHECK_RETURN( emitStructParentTree( nParentTree ) );
2761 
2762     return rEle.m_nObject;
2763 }
2764 
2765 bool PDFWriterImpl::emitGradients()
2766 {
2767     for( std::list<GradientEmit>::iterator it = m_aGradients.begin();
2768          it != m_aGradients.end(); ++it )
2769     {
2770         if ( !writeGradientFunction( *it ) ) return false;
2771     }
2772     return true;
2773 }
2774 
2775 bool PDFWriterImpl::emitTilings()
2776 {
2777     OStringBuffer aTilingObj( 1024 );
2778 
2779     for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it )
2780     {
2781         SAL_WARN_IF( !it->m_pTilingStream, "vcl.pdfwriter", "tiling without stream" );
2782         if( ! it->m_pTilingStream )
2783             continue;
2784 
2785         aTilingObj.setLength( 0 );
2786 
2787         if (g_bDebugDisableCompression)
2788         {
2789             emitComment( "PDFWriterImpl::emitTilings" );
2790         }
2791 
2792         sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left();
2793         sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top();
2794         sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth();
2795         sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight();
2796         if( it->m_aCellSize.Width() == 0 )
2797             it->m_aCellSize.Width() = nW;
2798         if( it->m_aCellSize.Height() == 0 )
2799             it->m_aCellSize.Height() = nH;
2800 
2801         bool bDeflate = compressStream( it->m_pTilingStream );
2802         it->m_pTilingStream->Seek( STREAM_SEEK_TO_END );
2803         sal_uInt64 const nTilingStreamSize = it->m_pTilingStream->Tell();
2804         it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN );
2805 
2806         // write pattern object
2807         aTilingObj.append( it->m_nObject );
2808         aTilingObj.append( " 0 obj\n" );
2809         aTilingObj.append( "<</Type/Pattern/PatternType 1\n"
2810                            "/PaintType 1\n"
2811                            "/TilingType 2\n"
2812                            "/BBox[" );
2813         appendFixedInt( nX, aTilingObj );
2814         aTilingObj.append( ' ' );
2815         appendFixedInt( nY, aTilingObj );
2816         aTilingObj.append( ' ' );
2817         appendFixedInt( nX+nW, aTilingObj );
2818         aTilingObj.append( ' ' );
2819         appendFixedInt( nY+nH, aTilingObj );
2820         aTilingObj.append( "]\n"
2821                            "/XStep " );
2822         appendFixedInt( it->m_aCellSize.Width(), aTilingObj );
2823         aTilingObj.append( "\n"
2824                            "/YStep " );
2825         appendFixedInt( it->m_aCellSize.Height(), aTilingObj );
2826         aTilingObj.append( "\n" );
2827         if( it->m_aTransform.matrix[0] != 1.0 ||
2828             it->m_aTransform.matrix[1] != 0.0 ||
2829             it->m_aTransform.matrix[3] != 0.0 ||
2830             it->m_aTransform.matrix[4] != 1.0 ||
2831             it->m_aTransform.matrix[2] != 0.0 ||
2832             it->m_aTransform.matrix[5] != 0.0 )
2833         {
2834             aTilingObj.append( "/Matrix [" );
2835             // TODO: scaling, mirroring on y, etc
2836             appendDouble( it->m_aTransform.matrix[0], aTilingObj );
2837             aTilingObj.append( ' ' );
2838             appendDouble( it->m_aTransform.matrix[1], aTilingObj );
2839             aTilingObj.append( ' ' );
2840             appendDouble( it->m_aTransform.matrix[3], aTilingObj );
2841             aTilingObj.append( ' ' );
2842             appendDouble( it->m_aTransform.matrix[4], aTilingObj );
2843             aTilingObj.append( ' ' );
2844             appendDouble( it->m_aTransform.matrix[2], aTilingObj );
2845             aTilingObj.append( ' ' );
2846             appendDouble( it->m_aTransform.matrix[5], aTilingObj );
2847             aTilingObj.append( "]\n" );
2848         }
2849         aTilingObj.append( "/Resources" );
2850         it->m_aResources.append( aTilingObj, getFontDictObject() );
2851         if( bDeflate )
2852             aTilingObj.append( "/Filter/FlateDecode" );
2853         aTilingObj.append( "/Length " );
2854         aTilingObj.append( (sal_Int32)nTilingStreamSize );
2855         aTilingObj.append( ">>\nstream\n" );
2856         if ( !updateObject( it->m_nObject ) ) return false;
2857         if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false;
2858         checkAndEnableStreamEncryption( it->m_nObject );
2859         bool written = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize );
2860         delete it->m_pTilingStream;
2861         it->m_pTilingStream = nullptr;
2862         if( !written )
2863             return false;
2864         disableStreamEncryption();
2865         aTilingObj.setLength( 0 );
2866         aTilingObj.append( "\nendstream\nendobj\n\n" );
2867         if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false;
2868     }
2869     return true;
2870 }
2871 
2872 sal_Int32 PDFWriterImpl::emitBuiltinFont( const PdfBuiltinFontFace* pFD, sal_Int32 nFontObject )
2873 {
2874     if( !pFD )
2875         return 0;
2876     const BuiltinFont& rBuiltinFont = pFD->GetBuiltinFont();
2877 
2878     OStringBuffer aLine( 1024 );
2879 
2880     if( nFontObject <= 0 )
2881         nFontObject = createObject();
2882     CHECK_RETURN( updateObject( nFontObject ) );
2883     aLine.append( nFontObject );
2884     aLine.append( " 0 obj\n"
2885                   "<</Type/Font/Subtype/Type1/BaseFont/" );
2886     appendName( rBuiltinFont.m_pPSName, aLine );
2887     aLine.append( "\n" );
2888     if( rBuiltinFont.m_eCharSet == RTL_TEXTENCODING_MS_1252 )
2889          aLine.append( "/Encoding/WinAnsiEncoding\n" );
2890     aLine.append( ">>\nendobj\n\n" );
2891     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2892     return nFontObject;
2893 }
2894 
2895 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const PhysicalFontFace* pFont, EmbedFont const & rEmbed )
2896 {
2897     std::map< sal_Int32, sal_Int32 > aRet;
2898 
2899     sal_Int32 nFontDescriptor = 0;
2900     OString aSubType( "/Type1" );
2901     FontSubsetInfo aInfo;
2902     // fill in dummy values
2903     aInfo.m_nAscent = 1000;
2904     aInfo.m_nDescent = 200;
2905     aInfo.m_nCapHeight = 1000;
2906     aInfo.m_aFontBBox = tools::Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) );
2907     aInfo.m_aPSName = pFont->GetFamilyName();
2908     sal_Int32 pWidths[256];
2909     memset( pWidths, 0, sizeof(pWidths) );
2910 
2911     SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics();
2912 
2913     assert(pGraphics);
2914 
2915     aSubType = OString( "/TrueType" );
2916     std::vector< sal_Int32 > aGlyphWidths;
2917     Ucs2UIntMap aUnicodeMap;
2918     pGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap );
2919 
2920     OUString aTmpName;
2921     osl_createTempFile( nullptr, nullptr, &aTmpName.pData );
2922     sal_GlyphId aGlyphIds[ 256 ];
2923     sal_uInt8 pEncoding[ 256 ];
2924     sal_Int32 pDuWidths[ 256 ];
2925 
2926     memset( aGlyphIds, 0, sizeof( aGlyphIds ) );
2927     memset( pEncoding, 0, sizeof( pEncoding ) );
2928     memset( pDuWidths, 0, sizeof( pDuWidths ) );
2929 
2930     for( sal_Ucs c = 32; c < 256; c++ )
2931     {
2932         pEncoding[c] = c;
2933         aGlyphIds[c] = 0;
2934         if( aUnicodeMap.find( c ) != aUnicodeMap.end() )
2935             pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ];
2936     }
2937     //TODO: surely this is utterly broken because aGlyphIds is just all zeros, if we
2938     //had the right glyphids here then I imagine we could replace pDuWidths with
2939     //pWidths and remove pWidths assignment above. i.e. start with the glyph ids
2940     //and map those to unicode rather than try and reverse map them ?
2941     pGraphics->CreateFontSubset( aTmpName, pFont, aGlyphIds, pEncoding, pDuWidths, 256, aInfo );
2942     osl_removeFile( aTmpName.pData );
2943 
2944     // write font descriptor
2945     nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 );
2946     if( nFontDescriptor )
2947     {
2948         // write font object
2949         sal_Int32 nObject = createObject();
2950         if( updateObject( nObject ) )
2951         {
2952             OStringBuffer aLine( 1024 );
2953             aLine.append( nObject );
2954             aLine.append( " 0 obj\n"
2955                           "<</Type/Font/Subtype" );
2956             aLine.append( aSubType );
2957             aLine.append( "/BaseFont/" );
2958             appendName( aInfo.m_aPSName, aLine );
2959             aLine.append( "\n" );
2960             if( !pFont->IsSymbolFont() )
2961                 aLine.append( "/Encoding/WinAnsiEncoding\n" );
2962             aLine.append( "/FirstChar 32 /LastChar 255\n"
2963                           "/Widths[" );
2964             for( int i = 32; i < 256; i++ )
2965             {
2966                 aLine.append( pWidths[i] );
2967                 aLine.append( ((i&15) == 15) ? "\n" : " " );
2968             }
2969             aLine.append( "]\n"
2970                           "/FontDescriptor " );
2971             aLine.append( nFontDescriptor );
2972             aLine.append( " 0 R>>\n"
2973                           "endobj\n\n" );
2974             writeBuffer( aLine.getStr(), aLine.getLength() );
2975 
2976             aRet[ rEmbed.m_nNormalFontID ] = nObject;
2977         }
2978     }
2979 
2980     return aRet;
2981 }
2982 
2983 typedef int ThreeInts[3];
2984 static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen,
2985     ThreeInts& rSegmentLengths )
2986 {
2987     if( !pFontBytes || (nByteLen < 0) )
2988         return false;
2989     const unsigned char* pPtr = pFontBytes;
2990     const unsigned char* pEnd = pFontBytes + nByteLen;
2991 
2992     for(int & rSegmentLength : rSegmentLengths) {
2993         // read segment1 header
2994         if( pPtr+6 >= pEnd )
2995             return false;
2996         if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) )
2997             return false;
2998         const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2];
2999         if( nLen <= 0)
3000             return false;
3001         rSegmentLength = nLen;
3002         pPtr += nLen + 6;
3003     }
3004 
3005     // read segment-end header
3006     if( pPtr+2 >= pEnd )
3007         return false;
3008     if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) )
3009         return false;
3010 
3011     return true;
3012 }
3013 
3014 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
3015 {
3016     if( nSubsetID )
3017     {
3018         for( int i = 0; i < 6; i++ )
3019         {
3020             int nOffset = (nSubsetID % 26);
3021             nSubsetID /= 26;
3022             rBuffer.append( (sal_Char)('A'+nOffset) );
3023         }
3024         rBuffer.append( '+' );
3025     }
3026     appendName( rPSName, rBuffer );
3027 }
3028 
3029 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding,
3030                                               const sal_Ucs* pCodeUnits,
3031                                               const sal_Int32* pCodeUnitsPerGlyph,
3032                                               const sal_Int32* pEncToUnicodeIndex,
3033                                               int nGlyphs )
3034 {
3035     int nMapped = 0;
3036     for (int n = 0; n < nGlyphs; ++n)
3037         if( pCodeUnits[pEncToUnicodeIndex[n]] && pCodeUnitsPerGlyph[n] )
3038             nMapped++;
3039 
3040     if( nMapped == 0 )
3041         return 0;
3042 
3043     sal_Int32 nStream = createObject();
3044     CHECK_RETURN( updateObject( nStream ) );
3045 
3046     OStringBuffer aContents( 1024 );
3047     aContents.append(
3048                      "/CIDInit/ProcSet findresource begin\n"
3049                      "12 dict begin\n"
3050                      "begincmap\n"
3051                      "/CIDSystemInfo<<\n"
3052                      "/Registry (Adobe)\n"
3053                      "/Ordering (UCS)\n"
3054                      "/Supplement 0\n"
3055                      ">> def\n"
3056                      "/CMapName/Adobe-Identity-UCS def\n"
3057                      "/CMapType 2 def\n"
3058                      "1 begincodespacerange\n"
3059                      "<00> <FF>\n"
3060                      "endcodespacerange\n"
3061                      );
3062     int nCount = 0;
3063     for (int n = 0; n < nGlyphs; ++n)
3064     {
3065         if( pCodeUnits[pEncToUnicodeIndex[n]] && pCodeUnitsPerGlyph[n] )
3066         {
3067             if( (nCount % 100) == 0 )
3068             {
3069                 if( nCount )
3070                     aContents.append( "endbfchar\n" );
3071                 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) );
3072                 aContents.append( " beginbfchar\n" );
3073             }
3074             aContents.append( '<' );
3075             appendHex( (sal_Int8)pEncoding[n], aContents );
3076             aContents.append( "> <" );
3077             // TODO: handle code points>U+FFFF
3078             sal_Int32 nIndex = pEncToUnicodeIndex[n];
3079             for( sal_Int32 j = 0; j < pCodeUnitsPerGlyph[n]; j++ )
3080             {
3081                 appendHex( (sal_Int8)(pCodeUnits[nIndex + j] / 256), aContents );
3082                 appendHex( (sal_Int8)(pCodeUnits[nIndex + j] & 255), aContents );
3083             }
3084             aContents.append( ">\n" );
3085             nCount++;
3086         }
3087     }
3088     aContents.append( "endbfchar\n"
3089                       "endcmap\n"
3090                       "CMapName currentdict /CMap define resource pop\n"
3091                       "end\n"
3092                       "end\n" );
3093     SvMemoryStream aStream;
3094     if (!g_bDebugDisableCompression)
3095     {
3096         ZCodec aCodec( 0x4000, 0x4000 );
3097         aCodec.BeginCompression();
3098         aCodec.Write( aStream, reinterpret_cast<const sal_uInt8*>(aContents.getStr()), aContents.getLength() );
3099         aCodec.EndCompression();
3100     }
3101 
3102     if (g_bDebugDisableCompression)
3103     {
3104         emitComment( "PDFWriterImpl::createToUnicodeCMap" );
3105     }
3106     OStringBuffer aLine( 40 );
3107 
3108     aLine.append( nStream );
3109     aLine.append( " 0 obj\n<</Length " );
3110     sal_Int32 nLen = 0;
3111     if (!g_bDebugDisableCompression)
3112     {
3113         nLen = (sal_Int32)aStream.Tell();
3114         aStream.Seek( 0 );
3115         aLine.append( nLen );
3116         aLine.append( "/Filter/FlateDecode" );
3117     }
3118     else
3119         aLine.append( aContents.getLength() );
3120     aLine.append( ">>\nstream\n" );
3121     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3122     checkAndEnableStreamEncryption( nStream );
3123     if (!g_bDebugDisableCompression)
3124     {
3125         CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
3126     }
3127     else
3128     {
3129         CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
3130     }
3131     disableStreamEncryption();
3132     aLine.setLength( 0 );
3133     aLine.append( "\nendstream\n"
3134                   "endobj\n\n" );
3135     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3136     return nStream;
3137 }
3138 
3139 sal_Int32 PDFWriterImpl::emitFontDescriptor( const PhysicalFontFace* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
3140 {
3141     OStringBuffer aLine( 1024 );
3142     // get font flags, see PDF reference 1.4 p. 358
3143     // possibly characters outside Adobe standard encoding
3144     // so set Symbolic flag
3145     sal_Int32 nFontFlags = (1<<2);
3146     if( pFont->GetItalic() == ITALIC_NORMAL || pFont->GetItalic() == ITALIC_OBLIQUE )
3147         nFontFlags |= (1 << 6);
3148     if( pFont->GetPitch() == PITCH_FIXED )
3149         nFontFlags |= 1;
3150     if( pFont->GetFamilyType() == FAMILY_SCRIPT )
3151         nFontFlags |= (1 << 3);
3152     else if( pFont->GetFamilyType() == FAMILY_ROMAN )
3153         nFontFlags |= (1 << 1);
3154 
3155     sal_Int32 nFontDescriptor = createObject();
3156     CHECK_RETURN( updateObject( nFontDescriptor ) );
3157     aLine.setLength( 0 );
3158     aLine.append( nFontDescriptor );
3159     aLine.append( " 0 obj\n"
3160                   "<</Type/FontDescriptor/FontName/" );
3161     appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
3162     aLine.append( "\n"
3163                   "/Flags " );
3164     aLine.append( nFontFlags );
3165     aLine.append( "\n"
3166                   "/FontBBox[" );
3167     // note: Top and Bottom are reversed in VCL and PDF rectangles
3168     aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() );
3169     aLine.append( ' ' );
3170     aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() );
3171     aLine.append( ' ' );
3172     aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() );
3173     aLine.append( ' ' );
3174     aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) );
3175     aLine.append( "]/ItalicAngle " );
3176     if( pFont->GetItalic() == ITALIC_OBLIQUE || pFont->GetItalic() == ITALIC_NORMAL )
3177         aLine.append( "-30" );
3178     else
3179         aLine.append( "0" );
3180     aLine.append( "\n"
3181                   "/Ascent " );
3182     aLine.append( (sal_Int32)rInfo.m_nAscent );
3183     aLine.append( "\n"
3184                   "/Descent " );
3185     aLine.append( (sal_Int32)-rInfo.m_nDescent );
3186     aLine.append( "\n"
3187                   "/CapHeight " );
3188     aLine.append( (sal_Int32)rInfo.m_nCapHeight );
3189     // According to PDF reference 1.4 StemV is required
3190     // seems a tad strange to me, but well ...
3191     aLine.append( "\n"
3192                   "/StemV 80\n" );
3193     if( nFontStream )
3194     {
3195         aLine.append( "/FontFile" );
3196         switch( rInfo.m_nFontType )
3197         {
3198             case FontType::SFNT_TTF:
3199                 aLine.append( '2' );
3200                 break;
3201             case FontType::TYPE1_PFA:
3202             case FontType::TYPE1_PFB:
3203             case FontType::ANY_TYPE1:
3204                 break;
3205             default:
3206                 OSL_FAIL( "unknown fonttype in PDF font descriptor" );
3207                 return 0;
3208         }
3209         aLine.append( ' ' );
3210         aLine.append( nFontStream );
3211         aLine.append( " 0 R\n" );
3212     }
3213     aLine.append( ">>\n"
3214                   "endobj\n\n" );
3215     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3216 
3217     return nFontDescriptor;
3218 }
3219 
3220 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const
3221 {
3222     for( std::map< sal_Int32, sal_Int32 >::const_iterator it =
3223          m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it )
3224     {
3225         rDict.append( m_aBuiltinFonts[it->first].getNameObject() );
3226         rDict.append( ' ' );
3227         rDict.append( it->second );
3228         rDict.append( " 0 R" );
3229     }
3230 }
3231 
3232 bool PDFWriterImpl::emitFonts()
3233 {
3234     SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics();
3235 
3236     if (!pGraphics)
3237         return false;
3238 
3239     OStringBuffer aLine( 1024 );
3240 
3241     std::map< sal_Int32, sal_Int32 > aFontIDToObject;
3242 
3243     OUString aTmpName;
3244     osl_createTempFile( nullptr, nullptr, &aTmpName.pData );
3245     for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it )
3246     {
3247         for( std::list< FontEmit >::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit )
3248         {
3249             sal_GlyphId aGlyphIds[ 256 ];
3250             sal_Int32 pWidths[ 256 ];
3251             sal_uInt8 pEncoding[ 256 ];
3252             sal_Int32 pEncToUnicodeIndex[ 256 ];
3253             sal_Int32 pCodeUnitsPerGlyph[ 256 ];
3254             std::vector<sal_Ucs> aCodeUnits;
3255             aCodeUnits.reserve( 256 );
3256             int nGlyphs = 1;
3257             // fill arrays and prepare encoding index map
3258             sal_Int32 nToUnicodeStream = 0;
3259 
3260             memset( aGlyphIds, 0, sizeof( aGlyphIds ) );
3261             memset( pEncoding, 0, sizeof( pEncoding ) );
3262             memset( pCodeUnitsPerGlyph, 0, sizeof( pCodeUnitsPerGlyph ) );
3263             memset( pEncToUnicodeIndex, 0, sizeof( pEncToUnicodeIndex ) );
3264             for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit )
3265             {
3266                 sal_uInt8 nEnc = fit->second.getGlyphId();
3267 
3268                 SAL_WARN_IF( aGlyphIds[nEnc] != 0 || pEncoding[nEnc] != 0, "vcl.pdfwriter", "duplicate glyph" );
3269                 SAL_WARN_IF( nEnc > lit->m_aMapping.size(), "vcl.pdfwriter", "invalid glyph encoding" );
3270 
3271                 aGlyphIds[ nEnc ] = fit->first;
3272                 pEncoding[ nEnc ] = nEnc;
3273                 pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aCodeUnits.size());
3274                 pCodeUnitsPerGlyph[ nEnc ] = fit->second.countCodes();
3275                 for( sal_Int32 n = 0; n < pCodeUnitsPerGlyph[ nEnc ]; n++ )
3276                     aCodeUnits.push_back( fit->second.getCode( n ) );
3277                 if( fit->second.getCode(0) )
3278                     nToUnicodeStream = 1;
3279                 if( nGlyphs < 256 )
3280                     nGlyphs++;
3281                 else
3282                 {
3283                     OSL_FAIL( "too many glyphs for subset" );
3284                 }
3285             }
3286             FontSubsetInfo aSubsetInfo;
3287             if( pGraphics->CreateFontSubset( aTmpName, it->first, aGlyphIds, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
3288             {
3289                 // create font stream
3290                 osl::File aFontFile(aTmpName);
3291                 if (osl::File::E_None != aFontFile.open(osl_File_OpenFlag_Read)) return false;
3292                 // get file size
3293                 sal_uInt64 nLength1;
3294                 if ( osl::File::E_None != aFontFile.setPos(osl_Pos_End, 0) ) return false;
3295                 if ( osl::File::E_None != aFontFile.getPos(nLength1) ) return false;
3296                 if ( osl::File::E_None != aFontFile.setPos(osl_Pos_Absolut, 0) ) return false;
3297 
3298                 if (g_bDebugDisableCompression)
3299                 {
3300                     emitComment( "PDFWriterImpl::emitFonts" );
3301                 }
3302                 sal_Int32 nFontStream = createObject();
3303                 sal_Int32 nStreamLengthObject = createObject();
3304                 if ( !updateObject( nFontStream ) ) return false;
3305                 aLine.setLength( 0 );
3306                 aLine.append( nFontStream );
3307                 aLine.append( " 0 obj\n"
3308                              "<</Length " );
3309                 aLine.append( nStreamLengthObject );
3310                 if (!g_bDebugDisableCompression)
3311                     aLine.append( " 0 R"
3312                                  "/Filter/FlateDecode"
3313                                  "/Length1 " );
3314                 else
3315                     aLine.append( " 0 R"
3316                                  "/Length1 " );
3317 
3318                 sal_uInt64 nStartPos = 0;
3319                 if( aSubsetInfo.m_nFontType == FontType::SFNT_TTF )
3320                 {
3321                     aLine.append( (sal_Int32)nLength1 );
3322 
3323                     aLine.append( ">>\n"
3324                                  "stream\n" );
3325                     if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
3326                     if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false;
3327 
3328                     // copy font file
3329                     beginCompression();
3330                     checkAndEnableStreamEncryption( nFontStream );
3331                     sal_Bool bEOF = false;
3332                     do
3333                     {
3334                         char buf[8192];
3335                         sal_uInt64 nRead;
3336                         if ( osl::File::E_None != aFontFile.read(buf, sizeof(buf), nRead) ) return false;
3337                         if ( !writeBuffer( buf, nRead ) ) return false;
3338                         if ( osl::File::E_None != aFontFile.isEndOfFile(&bEOF) ) return false;
3339                     } while( ! bEOF );
3340                 }
3341                 else if( aSubsetInfo.m_nFontType & FontType::CFF_FONT)
3342                 {
3343                     // TODO: implement
3344                     OSL_FAIL( "PDFWriterImpl does not support CFF-font subsets yet!" );
3345                 }
3346                 else if( aSubsetInfo.m_nFontType & FontType::TYPE1_PFB) // TODO: also support PFA?
3347                 {
3348                     std::unique_ptr<unsigned char[]> xBuffer(new unsigned char[nLength1]);
3349 
3350                     sal_uInt64 nBytesRead = 0;
3351                     if ( osl::File::E_None != aFontFile.read(xBuffer.get(), nLength1, nBytesRead) ) return false;
3352                     SAL_WARN_IF( nBytesRead!=nLength1, "vcl.pdfwriter", "PDF-FontSubset read incomplete!" );
3353                     if ( osl::File::E_None != aFontFile.setPos(osl_Pos_Absolut, 0) ) return false;
3354                     // get the PFB-segment lengths
3355                     ThreeInts aSegmentLengths = {0,0,0};
3356                     getPfbSegmentLengths(xBuffer.get(), (int)nBytesRead, aSegmentLengths);
3357                     // the lengths below are mandatory for PDF-exported Type1 fonts
3358                     // because the PFB segment headers get stripped! WhyOhWhy.
3359                     aLine.append( (sal_Int32)aSegmentLengths[0] );
3360                     aLine.append( "/Length2 " );
3361                     aLine.append( (sal_Int32)aSegmentLengths[1] );
3362                     aLine.append( "/Length3 " );
3363                     aLine.append( (sal_Int32)aSegmentLengths[2] );
3364 
3365                     aLine.append( ">>\n"
3366                                  "stream\n" );
3367                     if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
3368                     if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false;
3369 
3370                     // emit PFB-sections without section headers
3371                     beginCompression();
3372                     checkAndEnableStreamEncryption( nFontStream );
3373                     if ( !writeBuffer( &xBuffer[6], aSegmentLengths[0] ) ) return false;
3374                     if ( !writeBuffer( &xBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) ) return false;
3375                     if ( !writeBuffer( &xBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ) return false;
3376                 }
3377                 else
3378                 {
3379                     SAL_INFO("vcl.pdfwriter", "PDF: CreateFontSubset result in not yet supported format=" << (int)aSubsetInfo.m_nFontType);
3380                     aLine.append( "0 >>\nstream\n" );
3381                 }
3382 
3383                 endCompression();
3384                 disableStreamEncryption();
3385                 // close the file
3386                 aFontFile.close();
3387 
3388                 sal_uInt64 nEndPos = 0;
3389                 if ( osl::File::E_None != m_aFile.getPos(nEndPos) ) return false;
3390                 // end the stream
3391                 aLine.setLength( 0 );
3392                 aLine.append( "\nendstream\nendobj\n\n" );
3393                 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
3394 
3395                 // emit stream length object
3396                 if ( !updateObject( nStreamLengthObject ) ) return false;
3397                 aLine.setLength( 0 );
3398                 aLine.append( nStreamLengthObject );
3399                 aLine.append( " 0 obj\n" );
3400                 aLine.append( (sal_Int64)(nEndPos-nStartPos) );
3401                 aLine.append( "\nendobj\n\n" );
3402                 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
3403 
3404                 // write font descriptor
3405                 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream );
3406 
3407                 if( nToUnicodeStream )
3408                     nToUnicodeStream = createToUnicodeCMap( pEncoding, &aCodeUnits[0], pCodeUnitsPerGlyph, pEncToUnicodeIndex, nGlyphs );
3409 
3410                 sal_Int32 nFontObject = createObject();
3411                 if ( !updateObject( nFontObject ) ) return false;
3412                 aLine.setLength( 0 );
3413                 aLine.append( nFontObject );
3414 
3415                 aLine.append( " 0 obj\n" );
3416                 aLine.append( (aSubsetInfo.m_nFontType & FontType::ANY_TYPE1) ?
3417                              "<</Type/Font/Subtype/Type1/BaseFont/" :
3418                              "<</Type/Font/Subtype/TrueType/BaseFont/" );
3419                 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine );
3420                 aLine.append( "\n"
3421                              "/FirstChar 0\n"
3422                              "/LastChar " );
3423                 aLine.append( (sal_Int32)(nGlyphs-1) );
3424                 aLine.append( "\n"
3425                              "/Widths[" );
3426                 for( int i = 0; i < nGlyphs; i++ )
3427                 {
3428                     aLine.append( pWidths[ i ] );
3429                     aLine.append( ((i & 15) == 15) ? "\n" : " " );
3430                 }
3431                 aLine.append( "]\n"
3432                              "/FontDescriptor " );
3433                 aLine.append( nFontDescriptor );
3434                 aLine.append( " 0 R\n" );
3435                 if( nToUnicodeStream )
3436                 {
3437                     aLine.append( "/ToUnicode " );
3438                     aLine.append( nToUnicodeStream );
3439                     aLine.append( " 0 R\n" );
3440                 }
3441                 aLine.append( ">>\n"
3442                              "endobj\n\n" );
3443                 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
3444 
3445                 aFontIDToObject[ lit->m_nFontID ] = nFontObject;
3446             }
3447             else
3448             {
3449                 const PhysicalFontFace* pFont = it->first;
3450                 OStringBuffer aErrorComment( 256 );
3451                 aErrorComment.append( "CreateFontSubset failed for font \"" );
3452                 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3453                 aErrorComment.append( '\"' );
3454                 if( pFont->GetItalic() == ITALIC_NORMAL )
3455                     aErrorComment.append( " italic" );
3456                 else if( pFont->GetItalic() == ITALIC_OBLIQUE )
3457                     aErrorComment.append( " oblique" );
3458                 aErrorComment.append( " weight=" );
3459                 aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3460                 emitComment( aErrorComment.getStr() );
3461             }
3462         }
3463     }
3464     osl_removeFile( aTmpName.pData );
3465 
3466     // emit system fonts
3467     for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit )
3468     {
3469         std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second );
3470         for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
3471         {
3472             if ( !fit->second ) return false;
3473             aFontIDToObject[ fit->first ] = fit->second;
3474         }
3475     }
3476 
3477     OStringBuffer aFontDict( 1024 );
3478     aFontDict.append( getFontDictObject() );
3479     aFontDict.append( " 0 obj\n"
3480                      "<<" );
3481     int ni = 0;
3482     for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit )
3483     {
3484         aFontDict.append( "/F" );
3485         aFontDict.append( mit->first );
3486         aFontDict.append( ' ' );
3487         aFontDict.append( mit->second );
3488         aFontDict.append( " 0 R" );
3489         if( ((++ni) & 7) == 0 )
3490             aFontDict.append( '\n' );
3491     }
3492     // emit builtin font for widget appearances / variable text
3493     for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin();
3494         it != m_aBuiltinFontToObjectMap.end(); ++it )
3495     {
3496         PdfBuiltinFontFace aData(m_aBuiltinFonts[it->first]);
3497         it->second = emitBuiltinFont( &aData, it->second );
3498     }
3499     appendBuiltinFontsToDict( aFontDict );
3500     aFontDict.append( "\n>>\nendobj\n\n" );
3501 
3502     if ( !updateObject( getFontDictObject() ) ) return false;
3503     if ( !writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) ) return false;
3504     return true;
3505 }
3506 
3507 sal_Int32 PDFWriterImpl::emitResources()
3508 {
3509     // emit shadings
3510     if( ! m_aGradients.empty() )
3511         CHECK_RETURN( emitGradients() );
3512     // emit tilings
3513     if( ! m_aTilings.empty() )
3514         CHECK_RETURN( emitTilings() );
3515 
3516     // emit font dict
3517     CHECK_RETURN( emitFonts() );
3518 
3519     // emit Resource dict
3520     OStringBuffer aLine( 512 );
3521     sal_Int32 nResourceDict = getResourceDictObj();
3522     CHECK_RETURN( updateObject( nResourceDict ) );
3523     aLine.setLength( 0 );
3524     aLine.append( nResourceDict );
3525     aLine.append( " 0 obj\n" );
3526     m_aGlobalResourceDict.append( aLine, getFontDictObject() );
3527     aLine.append( "endobj\n\n" );
3528     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3529     return nResourceDict;
3530 }
3531 
3532 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
3533                                                  sal_Int32 nItemLevel,
3534                                                  sal_Int32 nCurrentItemId )
3535 {
3536     /* The /Count number of an item is
3537        positive: the number of visible subitems
3538        negative: the negative number of subitems that will become visible if
3539                  the item gets opened
3540        see PDF ref 1.4 p 478
3541     */
3542 
3543     sal_Int32 nCount = 0;
3544 
3545     if( m_aContext.OpenBookmarkLevels < 0           || // all levels are visible
3546         m_aContext.OpenBookmarkLevels >= nItemLevel    // this level is visible
3547       )
3548     {
3549         PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
3550         sal_Int32 nChildren = rItem.m_aChildren.size();
3551         for( sal_Int32 i = 0; i < nChildren; i++ )
3552             nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
3553         rCounts[nCurrentItemId] = nCount;
3554         // return 1 (this item) + visible sub items
3555         if( nCount < 0 )
3556             nCount = 0;
3557         nCount++;
3558     }
3559     else
3560     {
3561         // this bookmark level is invisible
3562         PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
3563         sal_Int32 nChildren = rItem.m_aChildren.size();
3564         rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
3565         for( sal_Int32 i = 0; i < nChildren; i++ )
3566             updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
3567         nCount = -1;
3568     }
3569 
3570     return nCount;
3571 }
3572 
3573 sal_Int32 PDFWriterImpl::emitOutline()
3574 {
3575     int i, nItems = m_aOutline.size();
3576 
3577     // do we have an outline at all ?
3578     if( nItems < 2 )
3579         return 0;
3580 
3581     // reserve object numbers for all outline items
3582     for( i = 0; i < nItems; ++i )
3583         m_aOutline[i].m_nObject = createObject();
3584 
3585     // update all parent, next and prev object ids
3586     for( i = 0; i < nItems; ++i )
3587     {
3588         PDFOutlineEntry& rItem = m_aOutline[i];
3589         int nChildren = rItem.m_aChildren.size();
3590 
3591         if( nChildren )
3592         {
3593             for( int n = 0; n < nChildren; ++n )
3594             {
3595                 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
3596 
3597                 rChild.m_nParentObject = rItem.m_nObject;
3598                 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
3599                 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
3600             }
3601 
3602         }
3603     }
3604 
3605     // calculate Count entries for all items
3606     std::vector< sal_Int32 > aCounts( nItems );
3607     updateOutlineItemCount( aCounts, 0, 0 );
3608 
3609     // emit hierarchy
3610     for( i = 0; i < nItems; ++i )
3611     {
3612         PDFOutlineEntry& rItem = m_aOutline[i];
3613         OStringBuffer aLine( 1024 );
3614 
3615         CHECK_RETURN( updateObject( rItem.m_nObject ) );
3616         aLine.append( rItem.m_nObject );
3617         aLine.append( " 0 obj\n" );
3618         aLine.append( "<<" );
3619         // number of visible children (all levels)
3620         if( i > 0 || aCounts[0] > 0 )
3621         {
3622             aLine.append( "/Count " );
3623             aLine.append( aCounts[i] );
3624         }
3625         if( ! rItem.m_aChildren.empty() )
3626         {
3627             // children list: First, Last
3628             aLine.append( "/First " );
3629             aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
3630             aLine.append( " 0 R/Last " );
3631             aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
3632             aLine.append( " 0 R\n" );
3633         }
3634         if( i > 0 )
3635         {
3636             // Title, Dest, Parent, Prev, Next
3637             aLine.append( "/Title" );
3638             appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
3639             aLine.append( "\n" );
3640             // Dest is not required
3641             if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() )
3642             {
3643                 aLine.append( "/Dest" );
3644                 appendDest( rItem.m_nDestID, aLine );
3645             }
3646             aLine.append( "/Parent " );
3647             aLine.append( rItem.m_nParentObject );
3648             aLine.append( " 0 R" );
3649             if( rItem.m_nPrevObject )
3650             {
3651                 aLine.append( "/Prev " );
3652                 aLine.append( rItem.m_nPrevObject );
3653                 aLine.append( " 0 R" );
3654             }
3655             if( rItem.m_nNextObject )
3656             {
3657                 aLine.append( "/Next " );
3658                 aLine.append( rItem.m_nNextObject );
3659                 aLine.append( " 0 R" );
3660             }
3661         }
3662         aLine.append( ">>\nendobj\n\n" );
3663         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3664     }
3665 
3666     return m_aOutline[0].m_nObject;
3667 }
3668 
3669 #undef CHECK_RETURN
3670 #define CHECK_RETURN( x ) if( !x ) return false
3671 
3672 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
3673 {
3674     if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() )
3675     {
3676         SAL_INFO("vcl.pdfwriter", "ERROR: invalid dest " << (int)nDestID << " requested");
3677         return false;
3678     }
3679 
3680     const PDFDest& rDest        = m_aDests[ nDestID ];
3681     const PDFPage& rDestPage    = m_aPages[ rDest.m_nPage ];
3682 
3683     rBuffer.append( '[' );
3684     rBuffer.append( rDestPage.m_nPageObject );
3685     rBuffer.append( " 0 R" );
3686 
3687     switch( rDest.m_eType )
3688     {
3689         case PDFWriter::DestAreaType::XYZ:
3690         default:
3691             rBuffer.append( "/XYZ " );
3692             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
3693             rBuffer.append( ' ' );
3694             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
3695             rBuffer.append( " 0" );
3696             break;
3697         case PDFWriter::DestAreaType::FitRectangle:
3698             rBuffer.append( "/FitR " );
3699             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
3700             rBuffer.append( ' ' );
3701             appendFixedInt( rDest.m_aRect.Top(), rBuffer );
3702             rBuffer.append( ' ' );
3703             appendFixedInt( rDest.m_aRect.Right(), rBuffer );
3704             rBuffer.append( ' ' );
3705             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
3706             break;
3707     }
3708     rBuffer.append( ']' );
3709 
3710     return true;
3711 }
3712 
3713 bool PDFWriterImpl::emitScreenAnnotations()
3714 {
3715     int nAnnots = m_aScreens.size();
3716     for (int i = 0; i < nAnnots; i++)
3717     {
3718         const PDFScreen& rScreen = m_aScreens[i];
3719 
3720         OStringBuffer aLine;
3721         bool bEmbed = false;
3722         if (!rScreen.m_aTempFileURL.isEmpty())
3723         {
3724             bEmbed = true;
3725             if (!updateObject(rScreen.m_nTempFileObject))
3726                 continue;
3727 
3728             SvFileStream aFileStream(rScreen.m_aTempFileURL, StreamMode::READ);
3729             SvMemoryStream aMemoryStream;
3730             aMemoryStream.WriteStream(aFileStream);
3731 
3732             aLine.append(rScreen.m_nTempFileObject);
3733             aLine.append(" 0 obj\n");
3734             aLine.append("<< /Type /EmbeddedFile /Length ");
3735             aLine.append(static_cast<sal_Int64>(aMemoryStream.GetSize()));
3736             aLine.append(" >>\nstream\n");
3737             CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength()));
3738             aLine.setLength(0);
3739 
3740             CHECK_RETURN(writeBuffer(aMemoryStream.GetData(), aMemoryStream.GetSize()));
3741 
3742             aLine.append("\nendstream\nendobj\n\n");
3743             CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength()));
3744             aLine.setLength(0);
3745         }
3746 
3747         if (!updateObject(rScreen.m_nObject))
3748             continue;
3749 
3750         // Annot dictionary.
3751         aLine.append(rScreen.m_nObject);
3752         aLine.append(" 0 obj\n");
3753         aLine.append("<</Type/Annot");
3754         aLine.append("/Subtype/Screen/Rect[");
3755         appendFixedInt(rScreen.m_aRect.Left(), aLine);
3756         aLine.append(' ');
3757         appendFixedInt(rScreen.m_aRect.Top(), aLine);
3758         aLine.append(' ');
3759         appendFixedInt(rScreen.m_aRect.Right(), aLine);
3760         aLine.append(' ');
3761         appendFixedInt(rScreen.m_aRect.Bottom(), aLine);
3762         aLine.append("]");
3763 
3764         // Action dictionary.
3765         aLine.append("/A<</Type/Action /S/Rendition /AN ");
3766         aLine.append(rScreen.m_nObject);
3767         aLine.append(" 0 R ");
3768 
3769         // Rendition dictionary.
3770         aLine.append("/R<</Type/Rendition /S/MR ");
3771 
3772         // MediaClip dictionary.
3773         aLine.append("/C<</Type/MediaClip /S/MCD ");
3774         if (bEmbed)
3775         {
3776             aLine.append("/D << /Type /Filespec /F (<embedded file>) /EF << /F ");
3777             aLine.append(rScreen.m_nTempFileObject);
3778             aLine.append(" 0 R >> >>");
3779         }
3780         else
3781         {
3782             // Linked.
3783             aLine.append("/D << /Type /Filespec /FS /URL /F ");
3784             appendLiteralStringEncrypt(rScreen.m_aURL, rScreen.m_nObject, aLine, osl_getThreadTextEncoding());
3785             aLine.append(" >>");
3786         }
3787         // Allow playing the video via a tempfile.
3788         aLine.append("/P <</TF (TEMPACCESS)>>");
3789         // Until the real MIME type (instead of application/vnd.sun.star.media) is available here.
3790         aLine.append("/CT (video/mp4)");
3791         aLine.append(">>");
3792 
3793         // End Rendition dictionary by requesting play/pause/stop controls.
3794         aLine.append("/P<</BE<</C true >>>>");
3795         aLine.append(">>");
3796 
3797         // End Action dictionary.
3798         aLine.append("/OP 0 >>");
3799 
3800         // End Annot dictionary.
3801         aLine.append("/P ");
3802         aLine.append(m_aPages[rScreen.m_nPage].m_nPageObject);
3803         aLine.append(" 0 R\n>>\nendobj\n\n");
3804         CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength()));
3805     }
3806 
3807     return true;
3808 }
3809 
3810 bool PDFWriterImpl::emitLinkAnnotations()
3811 {
3812     int nAnnots = m_aLinks.size();
3813     for( int i = 0; i < nAnnots; i++ )
3814     {
3815         const PDFLink& rLink            = m_aLinks[i];
3816         if( ! updateObject( rLink.m_nObject ) )
3817             continue;
3818 
3819         OStringBuffer aLine( 1024 );
3820         aLine.append( rLink.m_nObject );
3821         aLine.append( " 0 obj\n" );
3822 // i59651: key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
3823 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
3824         aLine.append( "<</Type/Annot" );
3825         if( m_bIsPDF_A1 )
3826             aLine.append( "/F 4" );
3827         aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
3828 
3829         appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
3830         aLine.append( ' ' );
3831         appendFixedInt( rLink.m_aRect.Top(), aLine );
3832         aLine.append( ' ' );
3833         appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
3834         aLine.append( ' ' );
3835         appendFixedInt( rLink.m_aRect.Bottom(), aLine );
3836         aLine.append( "]" );
3837         if( rLink.m_nDest >= 0 )
3838         {
3839             aLine.append( "/Dest" );
3840             appendDest( rLink.m_nDest, aLine );
3841         }
3842         else
3843         {
3844 /*
3845 destination is external to the document, so
3846 we check in the following sequence:
3847 
3848  if target type is neither .pdf, nor .od[tpgs], then
3849           check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
3850                              end processing
3851  else if target is .od[tpgs]: then
3852       if conversion of type from od[tpgs]  to pdf is requested, convert it and this becomes the new target file
3853       processing continue
3854 
3855  if (new)target is .pdf : then
3856      if GotToR is requested, then
3857            convert the target in GoToR where the fragment of the URI is
3858            considered the named destination in the target file, set relative or absolute as requested
3859      else strip the fragment from URL and then set URI or 'launch application' as requested
3860 */
3861 
3862 // FIXME: check if the decode mechanisms for URL processing throughout this implementation
3863 // are the correct one!!
3864 
3865 // extract target file type
3866             auto url(URIHelper::resolveIdnaHost(rLink.m_aURL));
3867 
3868             INetURLObject aDocumentURL( m_aContext.BaseURL );
3869             INetURLObject aTargetURL( url );
3870             bool bSetGoToRMode = false;
3871             bool    bTargetHasPDFExtension = false;
3872             INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
3873             bool    bIsUNCPath = false;
3874 
3875             // check if the protocol is a known one, or if there is no protocol at all (on target only)
3876             // if there is no protocol, make the target relative to the current document directory
3877             // getting the needed URL information from the current document path
3878             if( eTargetProtocol == INetProtocol::NotValid )
3879             {
3880                 if( url.getLength() > 4 && url.startsWith("\\\\\\\\"))
3881                 {
3882                     bIsUNCPath = true;
3883                 }
3884                 else
3885                 {
3886                     INetURLObject aNewBase( aDocumentURL );//duplicate document URL
3887                     aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
3888                                               //target document
3889                     aNewBase.insertName( url );
3890                     aTargetURL = aNewBase;//reassign the new target URL
3891                     //recompute the target protocol, with the new URL
3892                     //normal URL processing resumes
3893                     eTargetProtocol = aTargetURL.GetProtocol();
3894                 }
3895             }
3896 
3897             OUString aFileExtension = aTargetURL.GetFileExtension();
3898 
3899             // Check if the URL ends in '/': if yes it's a directory,
3900             // it will be forced to a URI link.
3901             // possibly a malformed URI, leave it as it is, force as URI
3902             if( aTargetURL.hasFinalSlash() )
3903                 m_aContext.DefaultLinkAction = PDFWriter::URIAction;
3904 
3905             if( !aFileExtension.isEmpty() )
3906             {
3907                 if( m_aContext.ConvertOOoTargetToPDFTarget )
3908                 {
3909                     bool bChangeFileExtensionToPDF = false;
3910                     //examine the file type (.odm .odt. .odp, odg, ods)
3911                     if( aFileExtension.equalsIgnoreAsciiCase( "odm" ) )
3912                         bChangeFileExtensionToPDF = true;
3913                     if( aFileExtension.equalsIgnoreAsciiCase( "odt" ) )
3914                         bChangeFileExtensionToPDF = true;
3915                     else if( aFileExtension.equalsIgnoreAsciiCase( "odp" ) )
3916                         bChangeFileExtensionToPDF = true;
3917                     else if( aFileExtension.equalsIgnoreAsciiCase( "odg" ) )
3918                         bChangeFileExtensionToPDF = true;
3919                     else if( aFileExtension.equalsIgnoreAsciiCase( "ods" ) )
3920                         bChangeFileExtensionToPDF = true;
3921                     if( bChangeFileExtensionToPDF )
3922                         aTargetURL.setExtension("pdf" );
3923                 }
3924                 //check if extension is pdf, see if GoToR should be forced
3925                 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase( "pdf" );
3926                 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
3927                     bSetGoToRMode = true;
3928             }
3929             //prepare the URL, if relative or not
3930             INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
3931             //queue the string common to all types of actions
3932             aLine.append( "/A<</Type/Action/S");
3933             if( bIsUNCPath ) // handle Win UNC paths
3934             {
3935                 aLine.append( "/Launch/Win<</F" );
3936                 // INetURLObject is not good with UNC paths, use original path
3937                 appendLiteralStringEncrypt( url, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
3938                 aLine.append( ">>" );
3939             }
3940             else
3941             {
3942                 bool bSetRelative = false;
3943                 bool bFileSpec = false;
3944                 //check if relative file link is requested and if the protocol is 'file://'
3945                 if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INetProtocol::File )
3946                     bSetRelative = true;
3947 
3948                 OUString aFragment = aTargetURL.GetMark( INetURLObject::DecodeMechanism::NONE /*DecodeMechanism::WithCharset*/ ); //fragment as is,
3949                 if( !bSetGoToRMode )
3950                 {
3951                     switch( m_aContext.DefaultLinkAction )
3952                     {
3953                     default:
3954                     case PDFWriter::URIAction :
3955                     case PDFWriter::URIActionDestination :
3956                         aLine.append( "/URI/URI" );
3957                         break;
3958                     case PDFWriter::LaunchAction:
3959                         // now:
3960                         // if a launch action is requested and the hyperlink target has a fragment
3961                         // and the target file does not have a pdf extension, or it's not a 'file:://'
3962                         // protocol then force the uri action on it
3963                         // This code will permit the correct opening of application on web pages,
3964                         // the one that normally have fragments (but I may be wrong...)
3965                         // and will force the use of URI when the protocol is not file:
3966                         if( (!aFragment.isEmpty() && !bTargetHasPDFExtension) ||
3967                                         eTargetProtocol != INetProtocol::File )
3968                         {
3969                             aLine.append( "/URI/URI" );
3970                         }
3971                         else
3972                         {
3973                             aLine.append( "/Launch/F" );
3974                             bFileSpec = true;
3975                         }
3976                         break;
3977                     }
3978                 }
3979 
3980                 //fragment are encoded in the same way as in the named destination processing
3981                 if( bSetGoToRMode )
3982                 {
3983                     //add the fragment
3984                     OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DecodeMechanism::WithCharset );
3985                     aLine.append("/GoToR");
3986                     aLine.append("/F");
3987                     appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark,
3988                                                                                          INetURLObject::EncodeMechanism::WasEncoded,
3989                                                                                          INetURLObject::DecodeMechanism::WithCharset ) :
3990                                                                    aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
3991                     if( !aFragment.isEmpty() )
3992                     {
3993                         aLine.append("/D/");
3994                         appendDestinationName( aFragment , aLine );
3995                     }
3996                 }
3997                 else
3998                 {
3999                     // change the fragment to accommodate the bookmark (only if the file extension
4000                     // is PDF and the requested action is of the correct type)
4001                     if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination &&
4002                                bTargetHasPDFExtension && !aFragment.isEmpty() )
4003                     {
4004                         OStringBuffer aLineLoc( 1024 );
4005                         appendDestinationName( aFragment , aLineLoc );
4006                         //substitute the fragment
4007                         aTargetURL.SetMark( OStringToOUString(aLineLoc.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US) );
4008                     }
4009                     OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DecodeMechanism::WithCharset : INetURLObject::DecodeMechanism::NONE );
4010                     appendLiteralStringEncrypt(bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL,
4011                                                                                         INetURLObject::EncodeMechanism::WasEncoded,
4012                                                                                             bFileSpec ? INetURLObject::DecodeMechanism::WithCharset : INetURLObject::DecodeMechanism::NONE
4013                                                                                             ) :
4014                                                                                aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4015                 }
4016             }
4017             aLine.append( ">>\n" );
4018         }
4019         if( rLink.m_nStructParent > 0 )
4020         {
4021             aLine.append( "/StructParent " );
4022             aLine.append( rLink.m_nStructParent );
4023         }
4024         aLine.append( ">>\nendobj\n\n" );
4025         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4026     }
4027 
4028     return true;
4029 }
4030 
4031 bool PDFWriterImpl::emitNoteAnnotations()
4032 {
4033     // emit note annotations
4034     int nAnnots = m_aNotes.size();
4035     for( int i = 0; i < nAnnots; i++ )
4036     {
4037         const PDFNoteEntry& rNote       = m_aNotes[i];
4038         if( ! updateObject( rNote.m_nObject ) )
4039             return false;
4040 
4041         OStringBuffer aLine( 1024 );
4042         aLine.append( rNote.m_nObject );
4043         aLine.append( " 0 obj\n" );
4044 // i59651: key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4045 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4046         aLine.append( "<</Type/Annot" );
4047         if( m_bIsPDF_A1 )
4048             aLine.append( "/F 4" );
4049         aLine.append( "/Subtype/Text/Rect[" );
4050 
4051         appendFixedInt( rNote.m_aRect.Left(), aLine );
4052         aLine.append( ' ' );
4053         appendFixedInt( rNote.m_aRect.Top(), aLine );
4054         aLine.append( ' ' );
4055         appendFixedInt( rNote.m_aRect.Right(), aLine );
4056         aLine.append( ' ' );
4057         appendFixedInt( rNote.m_aRect.Bottom(), aLine );
4058         aLine.append( "]" );
4059 
4060         // contents of the note (type text string)
4061         aLine.append( "/Contents\n" );
4062         appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
4063         aLine.append( "\n" );
4064 
4065         // optional title
4066         if( !rNote.m_aContents.Title.isEmpty() )
4067         {
4068             aLine.append( "/T" );
4069             appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
4070             aLine.append( "\n" );
4071         }
4072 
4073         aLine.append( ">>\nendobj\n\n" );
4074         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4075     }
4076     return true;
4077 }
4078 
4079 Font PDFWriterImpl::replaceFont( const vcl::Font& rControlFont, const vcl::Font&  rAppSetFont )
4080 {
4081     bool bAdjustSize = false;
4082 
4083     Font aFont( rControlFont );
4084     if( aFont.GetFamilyName().isEmpty() )
4085     {
4086         aFont = rAppSetFont;
4087         if( rControlFont.GetFontHeight() )
4088             aFont.SetFontSize( Size( 0, rControlFont.GetFontHeight() ) );
4089         else
4090             bAdjustSize = true;
4091         if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
4092             aFont.SetItalic( rControlFont.GetItalic() );
4093         if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
4094             aFont.SetWeight( rControlFont.GetWeight() );
4095     }
4096     else if( ! aFont.GetFontHeight() )
4097     {
4098         aFont.SetFontSize( rAppSetFont.GetFontSize() );
4099         bAdjustSize = true;
4100     }
4101     if( bAdjustSize )
4102     {
4103         Size aFontSize = aFont.GetFontSize();
4104         OutputDevice* pDefDev = Application::GetDefaultDevice();
4105         aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
4106         aFont.SetFontSize( aFontSize );
4107     }
4108     return aFont;
4109 }
4110 
4111 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const vcl::Font& rFont )
4112 {
4113     sal_Int32 nBest = 4; // default to Helvetica
4114     OUString aFontName( rFont.GetFamilyName() );
4115     aFontName = aFontName.toAsciiLowerCase();
4116 
4117     if( aFontName.indexOf( "times" ) != -1 )
4118         nBest = 8;
4119     else if( aFontName.indexOf( "courier" ) != -1 )
4120         nBest = 0;
4121     else if( aFontName.indexOf( "dingbats" ) != -1 )
4122         nBest = 13;
4123     else if( aFontName.indexOf( "symbol" ) != -1 )
4124         nBest = 12;
4125     if( nBest < 12 )
4126     {
4127         if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
4128             nBest += 1;
4129         if( rFont.GetWeight() > WEIGHT_MEDIUM )
4130             nBest += 2;
4131     }
4132 
4133     if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() )
4134         m_aBuiltinFontToObjectMap[ nBest ] = createObject();
4135 
4136     return nBest;
4137 }
4138 
4139 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
4140 {
4141     return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1;
4142 }
4143 
4144 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget )
4145 {
4146     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4147 
4148     // save graphics state
4149     push( PushFlags::ALL );
4150 
4151     // transform relative to control's coordinates since an
4152     // appearance stream is a form XObject
4153     // this relies on the m_aRect member of rButton NOT already being transformed
4154     // to default user space
4155     if( rWidget.Background || rWidget.Border )
4156     {
4157         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) );
4158         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) );
4159         drawRectangle( rWidget.Location );
4160     }
4161     // prepare font to use
4162     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
4163     setFont( aFont );
4164     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
4165 
4166     drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
4167 
4168     // create DA string while local mapmode is still in place
4169     // (that is before endRedirect())
4170     OStringBuffer aDA( 256 );
4171     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
4172     Font aDummyFont( "Helvetica", aFont.GetFontSize() );
4173     sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont );
4174     aDA.append( ' ' );
4175     aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() );
4176     aDA.append( ' ' );
4177     m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetFontHeight() ), aDA );
4178     aDA.append( " Tf" );
4179     rButton.m_aDAString = aDA.makeStringAndClear();
4180 
4181     pop();
4182 
4183     rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
4184 
4185     /* seems like a bad hack but at least works in both AR5 and 6:
4186        we draw the button ourselves and tell AR
4187        the button would be totally transparent with no text
4188 
4189        One would expect that simply setting a normal appearance
4190        should suffice, but no, as soon as the user actually presses
4191        the button and an action is tied to it (gasp! a button that
4192        does something) the appearance gets replaced by some crap that AR
4193        creates on the fly even if no DA or MK is given. On AR6 at least
4194        the DA and MK work as expected, but on AR5 this creates a region
4195        filled with the background color but nor text. Urgh.
4196     */
4197     rButton.m_aMKDict = "/BC [] /BG [] /CA";
4198     rButton.m_aMKDictCAString = "";
4199 }
4200 
4201 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern,
4202                                      const PDFWriter::AnyWidget& rWidget,
4203                                      const StyleSettings& rSettings )
4204 {
4205     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
4206 
4207     if( rWidget.Background || rWidget.Border )
4208     {
4209         if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) )
4210         {
4211             sal_Int32 nDelta = getReferenceDevice()->GetDPIX() / 500;
4212             if( nDelta < 1 )
4213                 nDelta = 1;
4214             setLineColor( Color( COL_TRANSPARENT ) );
4215             tools::Rectangle aRect = rIntern.m_aRect;
4216             setFillColor( rSettings.GetLightBorderColor() );
4217             drawRectangle( aRect );
4218             aRect.Left()  += nDelta; aRect.Top()     += nDelta;
4219             aRect.Right() -= nDelta; aRect.Bottom()  -= nDelta;
4220             setFillColor( rSettings.GetFieldColor() );
4221             drawRectangle( aRect );
4222             setFillColor( rSettings.GetLightColor() );
4223             drawRectangle( tools::Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
4224             drawRectangle( tools::Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
4225             setFillColor( rSettings.GetDarkShadowColor() );
4226             drawRectangle( tools::Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
4227             drawRectangle( tools::Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
4228         }
4229         else
4230         {
4231             setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) );
4232             setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4233             drawRectangle( rIntern.m_aRect );
4234         }
4235 
4236         if( rWidget.Border )
4237         {
4238             // adjust edit area accounting for border
4239             sal_Int32 nDelta = aFont.GetFontHeight()/4;
4240             if( nDelta < 1 )
4241                 nDelta = 1;
4242             rIntern.m_aRect.Left()  += nDelta;
4243             rIntern.m_aRect.Top()   += nDelta;
4244             rIntern.m_aRect.Right() -= nDelta;
4245             rIntern.m_aRect.Bottom()-= nDelta;
4246         }
4247     }
4248     return aFont;
4249 }
4250 
4251 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget )
4252 {
4253     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4254     SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
4255 
4256     push( PushFlags::ALL );
4257 
4258     // prepare font to use, draw field border
4259     Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
4260     sal_Int32 nBest = getSystemFont( aFont );
4261 
4262     // prepare DA string
4263     OStringBuffer aDA( 32 );
4264     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4265     aDA.append( ' ' );
4266     aDA.append( "/F" );
4267     aDA.append( nBest );
4268 
4269     OStringBuffer aDR( 32 );
4270     aDR.append( "/Font " );
4271     aDR.append( getFontDictObject() );
4272     aDR.append( " 0 R" );
4273     rEdit.m_aDRDict = aDR.makeStringAndClear();
4274     aDA.append( ' ' );
4275     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetFontHeight() ), aDA );
4276     aDA.append( " Tf" );
4277 
4278     /*  create an empty appearance stream, let the viewer create
4279         the appearance at runtime. This is because AR5 seems to
4280         paint the widget appearance always, and a dynamically created
4281         appearance on top of it. AR6 is well behaved in that regard, so
4282         that behaviour seems to be a bug. Anyway this empty appearance
4283         relies on /NeedAppearances in the AcroForm dictionary set to "true"
4284      */
4285     beginRedirect( pEditStream, rEdit.m_aRect );
4286     OStringBuffer aAppearance( 32 );
4287     aAppearance.append( "/Tx BMC\nEMC\n" );
4288     writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4289 
4290     endRedirect();
4291     pop();
4292 
4293     rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
4294 
4295     rEdit.m_aDAString = aDA.makeStringAndClear();
4296 }
4297 
4298 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget )
4299 {
4300     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4301     SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
4302 
4303     push( PushFlags::ALL );
4304 
4305     // prepare font to use, draw field border
4306     Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
4307     sal_Int32 nBest = getSystemFont( aFont );
4308 
4309     beginRedirect( pListBoxStream, rBox.m_aRect );
4310     OStringBuffer aAppearance( 64 );
4311 
4312     setLineColor( Color( COL_TRANSPARENT ) );
4313     setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
4314     drawRectangle( rBox.m_aRect );
4315 
4316     // empty appearance, see createDefaultEditAppearance for reference
4317     aAppearance.append( "/Tx BMC\nEMC\n" );
4318     writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4319 
4320     endRedirect();
4321     pop();
4322 
4323     rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
4324 
4325     // prepare DA string
4326     OStringBuffer aDA( 256 );
4327     // prepare DA string
4328     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4329     aDA.append( ' ' );
4330     aDA.append( "/F" );
4331     aDA.append( nBest );
4332 
4333     OStringBuffer aDR( 32 );
4334     aDR.append( "/Font " );
4335     aDR.append( getFontDictObject() );
4336     aDR.append( " 0 R" );
4337     rBox.m_aDRDict = aDR.makeStringAndClear();
4338     aDA.append( ' ' );
4339     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetFontHeight() ), aDA );
4340     aDA.append( " Tf" );
4341     rBox.m_aDAString = aDA.makeStringAndClear();
4342 }
4343 
4344 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget )
4345 {
4346     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4347 
4348     // save graphics state
4349     push( PushFlags::ALL );
4350 
4351     if( rWidget.Background || rWidget.Border )
4352     {
4353         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
4354         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4355         drawRectangle( rBox.m_aRect );
4356     }
4357 
4358     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
4359     setFont( aFont );
4360     Size aFontSize = aFont.GetFontSize();
4361     if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
4362         aFontSize.Height() = rBox.m_aRect.GetHeight();
4363     sal_Int32 nDelta = aFontSize.Height()/10;
4364     if( nDelta < 1 )
4365         nDelta = 1;
4366 
4367     tools::Rectangle aCheckRect, aTextRect;
4368     {
4369         aCheckRect.Left()   = rBox.m_aRect.Left() + nDelta;
4370         aCheckRect.Top()    = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4371         aCheckRect.Right()  = aCheckRect.Left() + aFontSize.Height();
4372         aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4373 
4374         // #i74206# handle small controls without text area
4375         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4376         {
4377             aCheckRect.Right()  -= nDelta;
4378             aCheckRect.Top()    += nDelta/2;
4379             aCheckRect.Bottom() -= nDelta - (nDelta/2);
4380         }
4381 
4382         aTextRect.Left()    = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
4383         aTextRect.Top()     = rBox.m_aRect.Top();
4384         aTextRect.Right()   = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4385         aTextRect.Bottom()  = rBox.m_aRect.Bottom();
4386     }
4387     setLineColor( Color( COL_BLACK ) );
4388     setFillColor( Color( COL_TRANSPARENT ) );
4389     OStringBuffer aLW( 32 );
4390     aLW.append( "q " );
4391     m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
4392     aLW.append( " w " );
4393     writeBuffer( aLW.getStr(), aLW.getLength() );
4394     drawRectangle( aCheckRect );
4395     writeBuffer( " Q\n", 3 );
4396     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4397     drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
4398 
4399     pop();
4400 
4401     OStringBuffer aDA( 256 );
4402     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4403     sal_Int32 nBest = getBestBuiltinFont( Font( "ZapfDingbats", aFont.GetFontSize() ) );
4404     aDA.append( ' ' );
4405     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4406     aDA.append( " 0 Tf" );
4407     rBox.m_aDAString = aDA.makeStringAndClear();
4408     rBox.m_aMKDict = "/CA";
4409     rBox.m_aMKDictCAString = "8";
4410     rBox.m_aRect = aCheckRect;
4411 
4412     // create appearance streams
4413     sal_Char cMark = '8';
4414     sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)];
4415     nCharXOffset *= aCheckRect.GetHeight();
4416     nCharXOffset /= 2000;
4417     sal_Int32 nCharYOffset = 1000-
4418         (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative
4419     nCharYOffset *= aCheckRect.GetHeight();
4420     nCharYOffset /= 2000;
4421 
4422     SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
4423     beginRedirect( pCheckStream, aCheckRect );
4424     aDA.append( "/Tx BMC\nq BT\n" );
4425     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4426     aDA.append( ' ' );
4427     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4428     aDA.append( ' ' );
4429     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
4430     aDA.append( " Tf\n" );
4431     m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
4432     aDA.append( " " );
4433     m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
4434     aDA.append( " Td (" );
4435     aDA.append( cMark );
4436     aDA.append( ") Tj\nET\nQ\nEMC\n" );
4437     writeBuffer( aDA.getStr(), aDA.getLength() );
4438     endRedirect();
4439     rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
4440 
4441     SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
4442     beginRedirect( pUncheckStream, aCheckRect );
4443     writeBuffer( "/Tx BMC\nEMC\n", 12 );
4444     endRedirect();
4445     rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
4446 }
4447 
4448 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget )
4449 {
4450     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4451 
4452     // save graphics state
4453     push( PushFlags::ALL );
4454 
4455     if( rWidget.Background || rWidget.Border )
4456     {
4457         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
4458         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4459         drawRectangle( rBox.m_aRect );
4460     }
4461 
4462     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
4463     setFont( aFont );
4464     Size aFontSize = aFont.GetFontSize();
4465     if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
4466         aFontSize.Height() = rBox.m_aRect.GetHeight();
4467     sal_Int32 nDelta = aFontSize.Height()/10;
4468     if( nDelta < 1 )
4469         nDelta = 1;
4470 
4471     tools::Rectangle aCheckRect, aTextRect;
4472     {
4473         aCheckRect.Left()   = rBox.m_aRect.Left() + nDelta;
4474         aCheckRect.Top()    = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4475         aCheckRect.Right()  = aCheckRect.Left() + aFontSize.Height();
4476         aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4477 
4478         // #i74206# handle small controls without text area
4479         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4480         {
4481             aCheckRect.Right()  -= nDelta;
4482             aCheckRect.Top()    += nDelta/2;
4483             aCheckRect.Bottom() -= nDelta - (nDelta/2);
4484         }
4485 
4486         aTextRect.Left()    = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
4487         aTextRect.Top()     = rBox.m_aRect.Top();
4488         aTextRect.Right()   = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4489         aTextRect.Bottom()  = rBox.m_aRect.Bottom();
4490     }
4491     setLineColor( Color( COL_BLACK ) );
4492     setFillColor( Color( COL_TRANSPARENT ) );
4493     OStringBuffer aLW( 32 );
4494     aLW.append( "q " );
4495     m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
4496     aLW.append( " w " );
4497     writeBuffer( aLW.getStr(), aLW.getLength() );
4498     drawEllipse( aCheckRect );
4499     writeBuffer( " Q\n", 3 );
4500     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4501     drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
4502 
4503     pop();
4504 
4505     OStringBuffer aDA( 256 );
4506     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4507     sal_Int32 nBest = getBestBuiltinFont( Font( "ZapfDingbats", aFont.GetFontSize() ) );
4508     aDA.append( ' ' );
4509     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4510     aDA.append( " 0 Tf" );
4511     rBox.m_aDAString = aDA.makeStringAndClear();
4512     //to encrypt this (el)
4513     rBox.m_aMKDict = "/CA";
4514     //after this assignement, to m_aMKDic cannot be added anything
4515     rBox.m_aMKDictCAString = "l";
4516 
4517     rBox.m_aRect = aCheckRect;
4518 
4519     // create appearance streams
4520     push( PushFlags::ALL);
4521     SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
4522 
4523     beginRedirect( pCheckStream, aCheckRect );
4524     aDA.append( "/Tx BMC\nq BT\n" );
4525     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4526     aDA.append( ' ' );
4527     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4528     aDA.append( ' ' );
4529     m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
4530     aDA.append( " Tf\n0 0 Td\nET\nQ\n" );
4531     writeBuffer( aDA.getStr(), aDA.getLength() );
4532     setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4533     setLineColor( Color( COL_TRANSPARENT ) );
4534     aCheckRect.Left()   += 3*nDelta;
4535     aCheckRect.Top()    += 3*nDelta;
4536     aCheckRect.Bottom() -= 3*nDelta;
4537     aCheckRect.Right()  -= 3*nDelta;
4538     drawEllipse( aCheckRect );
4539     writeBuffer( "\nEMC\n", 5 );
4540     endRedirect();
4541 
4542     pop();
4543     rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
4544 
4545     SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
4546     beginRedirect( pUncheckStream, aCheckRect );
4547     writeBuffer( "/Tx BMC\nEMC\n", 12 );
4548     endRedirect();
4549     rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
4550 }
4551 
4552 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
4553 {
4554     // TODO: check and insert default streams
4555     OString aStandardAppearance;
4556     switch( rWidget.m_eType )
4557     {
4558         case PDFWriter::CheckBox:
4559             aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
4560             break;
4561         default:
4562             break;
4563     }
4564 
4565     if( !rWidget.m_aAppearances.empty() )
4566     {
4567         rAnnotDict.append( "/AP<<\n" );
4568         for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it )
4569         {
4570             rAnnotDict.append( "/" );
4571             rAnnotDict.append( dict_it->first );
4572             bool bUseSubDict = (dict_it->second.size() > 1);
4573             rAnnotDict.append( bUseSubDict ? "<<" : " " );
4574 
4575             for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin();
4576                  stream_it != dict_it->second.end(); ++stream_it )
4577             {
4578                 SvMemoryStream* pApppearanceStream = stream_it->second;
4579                 dict_it->second[ stream_it->first ] = nullptr;
4580 
4581                 bool bDeflate = compressStream( pApppearanceStream );
4582 
4583                 pApppearanceStream->Seek( STREAM_SEEK_TO_END );
4584                 sal_Int64 nStreamLen = pApppearanceStream->Tell();
4585                 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
4586                 sal_Int32 nObject = createObject();
4587                 CHECK_RETURN( updateObject( nObject ) );
4588                 if (g_bDebugDisableCompression)
4589                 {
4590                     emitComment( "PDFWriterImpl::emitAppearances" );
4591                 }
4592                 OStringBuffer aLine;
4593                 aLine.append( nObject );
4594 
4595                 aLine.append( " 0 obj\n"
4596                               "<</Type/XObject\n"
4597                               "/Subtype/Form\n"
4598                               "/BBox[0 0 " );
4599                 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
4600                 aLine.append( " " );
4601                 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
4602                 aLine.append( "]\n"
4603                               "/Resources " );
4604                 aLine.append( getResourceDictObj() );
4605                 aLine.append( " 0 R\n"
4606                               "/Length " );
4607                 aLine.append( nStreamLen );
4608                 aLine.append( "\n" );
4609                 if( bDeflate )
4610                     aLine.append( "/Filter/FlateDecode\n" );
4611                 aLine.append( ">>\nstream\n" );
4612                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4613                 checkAndEnableStreamEncryption( nObject );
4614                 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
4615                 disableStreamEncryption();
4616                 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
4617 
4618                 if( bUseSubDict )
4619                 {
4620                     rAnnotDict.append( " /" );
4621                     rAnnotDict.append( stream_it->first );
4622                     rAnnotDict.append( " " );
4623                 }
4624                 rAnnotDict.append( nObject );
4625                 rAnnotDict.append( " 0 R" );
4626 
4627                 delete pApppearanceStream;
4628             }
4629 
4630             rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
4631         }
4632         rAnnotDict.append( ">>\n" );
4633         if( !aStandardAppearance.isEmpty() )
4634         {
4635             rAnnotDict.append( "/AS /" );
4636             rAnnotDict.append( aStandardAppearance );
4637             rAnnotDict.append( "\n" );
4638         }
4639     }
4640 
4641     return true;
4642 }
4643 
4644 bool PDFWriterImpl::emitWidgetAnnotations()
4645 {
4646     ensureUniqueRadioOnValues();
4647 
4648     int nAnnots = m_aWidgets.size();
4649     for( int a = 0; a < nAnnots; a++ )
4650     {
4651         PDFWidget& rWidget = m_aWidgets[a];
4652 
4653         OStringBuffer aLine( 1024 );
4654         OStringBuffer aValue( 256 );
4655         aLine.append( rWidget.m_nObject );
4656         aLine.append( " 0 obj\n"
4657                       "<<" );
4658         if( rWidget.m_eType != PDFWriter::Hierarchy )
4659         {
4660             // emit widget annotation only for terminal fields
4661             if( rWidget.m_aKids.empty() )
4662             {
4663                 int iRectMargin;
4664 
4665                 aLine.append( "/Type/Annot/Subtype/Widget/F " );
4666 
4667                 if (rWidget.m_eType == PDFWriter::Signature)
4668                 {
4669                     aLine.append( "132\n" ); // Print & Locked
4670                     iRectMargin = 0;
4671                 }
4672                 else
4673                 {
4674                     aLine.append( "4\n" );
4675                     iRectMargin = 1;
4676                 }
4677 
4678                 aLine.append("/Rect[" );
4679                 appendFixedInt( rWidget.m_aRect.Left()-iRectMargin, aLine );
4680                 aLine.append( ' ' );
4681                 appendFixedInt( rWidget.m_aRect.Top()+iRectMargin, aLine );
4682                 aLine.append( ' ' );
4683                 appendFixedInt( rWidget.m_aRect.Right()+iRectMargin, aLine );
4684                 aLine.append( ' ' );
4685                 appendFixedInt( rWidget.m_aRect.Bottom()-iRectMargin, aLine );
4686                 aLine.append( "]\n" );
4687             }
4688             aLine.append( "/FT/" );
4689             switch( rWidget.m_eType )
4690             {
4691                 case PDFWriter::RadioButton:
4692                 case PDFWriter::CheckBox:
4693                     // for radio buttons only the RadioButton field, not the
4694                     // CheckBox children should have a value, else acrobat reader
4695                     // does not always check the right button
4696                     // of course real check boxes (not belonging to a radio group)
4697                     // need their values, too
4698                     if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
4699                     {
4700                         aValue.append( "/" );
4701                         // check for radio group with all buttons unpressed
4702                         if( rWidget.m_aValue.isEmpty() )
4703                             aValue.append( "Off" );
4704                         else
4705                             appendName( rWidget.m_aValue, aValue );
4706                     }
4707                     SAL_FALLTHROUGH;
4708                 case PDFWriter::PushButton:
4709                     aLine.append( "Btn" );
4710                     break;
4711                 case PDFWriter::ListBox:
4712                     if( rWidget.m_nFlags & 0x200000 ) // multiselect
4713                     {
4714                         aValue.append( "[" );
4715                         for( size_t i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
4716                         {
4717                             sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
4718                             if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
4719                                 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
4720                         }
4721                         aValue.append( "]" );
4722                     }
4723                     else if( rWidget.m_aSelectedEntries.size() > 0 &&
4724                              rWidget.m_aSelectedEntries[0] >= 0 &&
4725                              rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
4726                     {
4727                         appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
4728                     }
4729                     else
4730                         appendUnicodeTextStringEncrypt( OUString(), rWidget.m_nObject, aValue );
4731                     aLine.append( "Ch" );
4732                     break;
4733                 case PDFWriter::ComboBox:
4734                     appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
4735                     aLine.append( "Ch" );
4736                     break;
4737                 case PDFWriter::Edit:
4738                     aLine.append( "Tx" );
4739                     appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
4740                     break;
4741                 case PDFWriter::Signature:
4742                     aLine.append( "Sig" );
4743                     aValue.append(OUStringToOString(rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US));
4744                     break;
4745                 case PDFWriter::Hierarchy: // make the compiler happy
4746                     break;
4747             }
4748             aLine.append( "\n" );
4749             aLine.append( "/P " );
4750             aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
4751             aLine.append( " 0 R\n" );
4752         }
4753         if( rWidget.m_nParent )
4754         {
4755             aLine.append( "/Parent " );
4756             aLine.append( rWidget.m_nParent );
4757             aLine.append( " 0 R\n" );
4758         }
4759         if( rWidget.m_aKids.size() )
4760         {
4761             aLine.append( "/Kids[" );
4762             for( size_t i = 0; i < rWidget.m_aKids.size(); i++ )
4763             {
4764                 aLine.append( rWidget.m_aKids[i] );
4765                 aLine.append( " 0 R" );
4766                 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
4767             }
4768             aLine.append( "]\n" );
4769         }
4770         if( !rWidget.m_aName.isEmpty() )
4771         {
4772             aLine.append( "/T" );
4773             appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
4774             aLine.append( "\n" );
4775         }
4776         if( m_aContext.Version > PDFWriter::PDFVersion::PDF_1_2 && !rWidget.m_aDescription.isEmpty() )
4777         {
4778             // the alternate field name should be unicode able since it is
4779             // supposed to be used in UI
4780             aLine.append( "/TU" );
4781             appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
4782             aLine.append( "\n" );
4783         }
4784 
4785         if( rWidget.m_nFlags )
4786         {
4787             aLine.append( "/Ff " );
4788             aLine.append( rWidget.m_nFlags );
4789             aLine.append( "\n" );
4790         }
4791         if( !aValue.isEmpty() )
4792         {
4793             OString aVal = aValue.makeStringAndClear();
4794             aLine.append( "/V " );
4795             aLine.append( aVal );
4796             aLine.append( "\n"
4797                           "/DV " );
4798             aLine.append( aVal );
4799             aLine.append( "\n" );
4800         }
4801         if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
4802         {
4803             sal_Int32 nTI = -1;
4804             aLine.append( "/Opt[\n" );
4805             sal_Int32 i = 0;
4806             for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i )
4807             {
4808                 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine );
4809                 aLine.append( "\n" );
4810                 if( *it == rWidget.m_aValue )
4811                     nTI = i;
4812             }
4813             aLine.append( "]\n" );
4814             if( nTI > 0 )
4815             {
4816                 aLine.append( "/TI " );
4817                 aLine.append( nTI );
4818                 aLine.append( "\n" );
4819                 if( rWidget.m_nFlags & 0x200000 ) // Multiselect
4820                 {
4821                     aLine.append( "/I [" );
4822                     aLine.append( nTI );
4823                     aLine.append( "]\n" );
4824                 }
4825             }
4826         }
4827         if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
4828         {
4829             aLine.append( "/MaxLen " );
4830             aLine.append( rWidget.m_nMaxLen );
4831             aLine.append( "\n" );
4832         }
4833         if( rWidget.m_eType == PDFWriter::PushButton )
4834         {
4835             if(!m_bIsPDF_A1)
4836             {
4837                 OStringBuffer aDest;
4838                 if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) )
4839                 {
4840                     aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
4841                     aLine.append( aDest.makeStringAndClear() );
4842                     aLine.append( ">>>>\n" );
4843                 }
4844                 else if( rWidget.m_aListEntries.empty() )
4845                 {
4846                     // create a reset form action
4847                     aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
4848                 }
4849                 else if( rWidget.m_bSubmit )
4850                 {
4851                     // create a submit form action
4852                     aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
4853                     appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() );
4854                     aLine.append( "/Flags " );
4855 
4856                     sal_Int32 nFlags = 0;
4857                     switch( m_aContext.SubmitFormat )
4858                     {
4859                     case PDFWriter::HTML:
4860                         nFlags |= 4;
4861                         break;
4862                     case PDFWriter::XML:
4863                         if( m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 )
4864                             nFlags |= 32;
4865                         break;
4866                     case PDFWriter::PDF:
4867                         if( m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 )
4868                             nFlags |= 256;
4869                         break;
4870                     case PDFWriter::FDF:
4871                     default:
4872                         break;
4873                     }
4874                     if( rWidget.m_bSubmitGet )
4875                         nFlags |= 8;
4876                     aLine.append( nFlags );
4877                     aLine.append( ">>>>\n" );
4878                 }
4879                 else
4880                 {
4881                     // create a URI action
4882                     aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
4883                     aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
4884                     aLine.append( ")>>>>\n" );
4885                 }
4886             }
4887             else
4888                 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA );
4889         }
4890         if( !rWidget.m_aDAString.isEmpty() )
4891         {
4892             if( !rWidget.m_aDRDict.isEmpty() )
4893             {
4894                 aLine.append( "/DR<<" );
4895                 aLine.append( rWidget.m_aDRDict );
4896                 aLine.append( ">>\n" );
4897             }
4898             else
4899             {
4900                 aLine.append( "/DR<</Font<<" );
4901                 appendBuiltinFontsToDict( aLine );
4902                 aLine.append( ">>>>\n" );
4903             }
4904             aLine.append( "/DA" );
4905             appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
4906             aLine.append( "\n" );
4907             if( rWidget.m_nTextStyle & DrawTextFlags::Center )
4908                 aLine.append( "/Q 1\n" );
4909             else if( rWidget.m_nTextStyle & DrawTextFlags::Right )
4910                 aLine.append( "/Q 2\n" );
4911         }
4912         // appearance characteristics for terminal fields
4913         // which are supposed to have an appearance constructed
4914         // by the viewer application
4915         if( !rWidget.m_aMKDict.isEmpty() )
4916         {
4917             aLine.append( "/MK<<" );
4918             aLine.append( rWidget.m_aMKDict );
4919             //add the CA string, encrypting it
4920             appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
4921             aLine.append( ">>\n" );
4922         }
4923 
4924         CHECK_RETURN( emitAppearances( rWidget, aLine ) );
4925 
4926         aLine.append( ">>\n"
4927                       "endobj\n\n" );
4928         CHECK_RETURN( updateObject( rWidget.m_nObject ) );
4929         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4930     }
4931     return true;
4932 }
4933 
4934 bool PDFWriterImpl::emitAnnotations()
4935 {
4936     if( m_aPages.size() < 1 )
4937         return false;
4938 
4939     CHECK_RETURN( emitLinkAnnotations() );
4940     CHECK_RETURN(emitScreenAnnotations());
4941     CHECK_RETURN( emitNoteAnnotations() );
4942     CHECK_RETURN( emitWidgetAnnotations() );
4943 
4944     return true;
4945 }
4946 
4947 bool PDFWriterImpl::emitEmbeddedFiles()
4948 {
4949     for (auto& rEmbeddedFile : m_aEmbeddedFiles)
4950     {
4951         if (!updateObject(rEmbeddedFile.m_nObject))
4952             continue;
4953 
4954         OStringBuffer aLine;
4955         aLine.append(rEmbeddedFile.m_nObject);
4956         aLine.append(" 0 obj\n");
4957         aLine.append("<< /Type /EmbeddedFile /Length ");
4958         aLine.append(static_cast<sal_Int64>(rEmbeddedFile.m_aData.getLength()));
4959         aLine.append(" >>\nstream\n");
4960         CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength()));
4961         aLine.setLength(0);
4962 
4963         CHECK_RETURN(writeBuffer(rEmbeddedFile.m_aData.getArray(), rEmbeddedFile.m_aData.getLength()));
4964 
4965         aLine.append("\nendstream\nendobj\n\n");
4966         CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength()));
4967     }
4968     return true;
4969 }
4970 
4971 #undef CHECK_RETURN
4972 #define CHECK_RETURN( x ) if( !x ) return false
4973 
4974 bool PDFWriterImpl::emitCatalog()
4975 {
4976     // build page tree
4977     // currently there is only one node that contains all leaves
4978 
4979     // first create a page tree node id
4980     sal_Int32 nTreeNode = createObject();
4981 
4982     // emit global resource dictionary (page emit needs it)
4983     CHECK_RETURN( emitResources() );
4984 
4985     // emit all pages
4986     for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it )
4987         if( ! it->emit( nTreeNode ) )
4988             return false;
4989 
4990     sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
4991 
4992     sal_Int32 nOutlineDict = emitOutline();
4993 
4994     // emit Output intent
4995     sal_Int32 nOutputIntentObject = emitOutputIntent();
4996 
4997     // emit metadata
4998     sal_Int32 nMetadataObject = emitDocumentMetadata();
4999 
5000     sal_Int32 nStructureDict = 0;
5001     if(m_aStructure.size() > 1)
5002     {
5003         // check if dummy structure containers are needed
5004         addInternalStructureContainer(m_aStructure[0]);
5005         nStructureDict = m_aStructure[0].m_nObject = createObject();
5006         emitStructure( m_aStructure[ 0 ] );
5007     }
5008 
5009     // adjust tree node file offset
5010     if( ! updateObject( nTreeNode ) )
5011         return false;
5012 
5013     // emit tree node
5014     OStringBuffer aLine( 2048 );
5015     aLine.append( nTreeNode );
5016     aLine.append( " 0 obj\n" );
5017     aLine.append( "<</Type/Pages\n" );
5018     aLine.append( "/Resources " );
5019     aLine.append( getResourceDictObj() );
5020     aLine.append( " 0 R\n" );
5021 
5022     sal_Int32 nMediaBoxWidth = 0;
5023     sal_Int32 nMediaBoxHeight = 0;
5024     if( m_aPages.empty() ) // sanity check, this should not happen
5025     {
5026         nMediaBoxWidth = m_nInheritedPageWidth;
5027         nMediaBoxHeight = m_nInheritedPageHeight;
5028     }
5029     else
5030     {
5031         for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter )
5032         {
5033             if( iter->m_nPageWidth > nMediaBoxWidth )
5034                 nMediaBoxWidth = iter->m_nPageWidth;
5035             if( iter->m_nPageHeight > nMediaBoxHeight )
5036                 nMediaBoxHeight = iter->m_nPageHeight;
5037         }
5038     }
5039     aLine.append( "/MediaBox[ 0 0 " );
5040     aLine.append( nMediaBoxWidth );
5041     aLine.append( ' ' );
5042     aLine.append( nMediaBoxHeight );
5043     aLine.append( " ]\n"
5044                   "/Kids[ " );
5045     unsigned int i = 0;
5046     for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ )
5047     {
5048         aLine.append( iter->m_nPageObject );
5049         aLine.append( " 0 R" );
5050         aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5051     }
5052     aLine.append( "]\n"
5053                   "/Count " );
5054     aLine.append( (sal_Int32)m_aPages.size() );
5055     aLine.append( ">>\n"
5056                   "endobj\n\n" );
5057     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5058 
5059     // emit annotation objects
5060     CHECK_RETURN( emitAnnotations() );
5061     CHECK_RETURN( emitEmbeddedFiles() );
5062 
5063     // emit Catalog
5064     m_nCatalogObject = createObject();
5065     if( ! updateObject( m_nCatalogObject ) )
5066         return false;
5067     aLine.setLength( 0 );
5068     aLine.append( m_nCatalogObject );
5069     aLine.append( " 0 obj\n"
5070                   "<</Type/Catalog/Pages " );
5071     aLine.append( nTreeNode );
5072     aLine.append( " 0 R\n" );
5073 
5074     // check if there are named destinations to emit (root must be inside the catalog)
5075     if( nNamedDestinationsDictionary )
5076     {
5077         aLine.append("/Dests ");
5078         aLine.append( nNamedDestinationsDictionary );
5079         aLine.append( " 0 R\n" );
5080     }
5081 
5082     if( m_aContext.PageLayout != PDFWriter::DefaultLayout )
5083         switch(  m_aContext.PageLayout )
5084         {
5085         default :
5086         case  PDFWriter::SinglePage :
5087             aLine.append( "/PageLayout/SinglePage\n" );
5088             break;
5089         case  PDFWriter::Continuous :
5090             aLine.append( "/PageLayout/OneColumn\n" );
5091             break;
5092         case  PDFWriter::ContinuousFacing :
5093             // the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5094             aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5095             break;
5096         }
5097     if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode )
5098         switch(  m_aContext.PDFDocumentMode )
5099         {
5100         default :
5101             aLine.append( "/PageMode/UseNone\n" );
5102             break;
5103         case PDFWriter::UseOutlines :
5104             aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5105             break;
5106         case PDFWriter::UseThumbs :
5107             aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5108             break;
5109         }
5110     else if( m_aContext.OpenInFullScreenMode )
5111         aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5112 
5113     OStringBuffer aInitPageRef;
5114     if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() )
5115     {
5116         aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5117         aInitPageRef.append( " 0 R" );
5118     }
5119     else
5120         aInitPageRef.append( "0" );
5121 
5122     switch( m_aContext.PDFDocumentAction )
5123     {
5124     case PDFWriter::ActionDefault :     //do nothing, this is the Acrobat default
5125     default:
5126         if( aInitPageRef.getLength() > 1 )
5127         {
5128             aLine.append( "/OpenAction[" );
5129             aLine.append( aInitPageRef.makeStringAndClear() );
5130             aLine.append( " /XYZ null null 0]\n" );
5131         }
5132         break;
5133     case PDFWriter::FitInWindow :
5134         aLine.append( "/OpenAction[" );
5135         aLine.append( aInitPageRef.makeStringAndClear() );
5136         aLine.append( " /Fit]\n" ); //Open fit page
5137         break;
5138     case PDFWriter::FitWidth :
5139         aLine.append( "/OpenAction[" );
5140         aLine.append( aInitPageRef.makeStringAndClear() );
5141         aLine.append( " /FitH " );
5142         aLine.append( m_nInheritedPageHeight );//Open fit width
5143         aLine.append( "]\n" );
5144         break;
5145     case PDFWriter::FitVisible :
5146         aLine.append( "/OpenAction[" );
5147         aLine.append( aInitPageRef.makeStringAndClear() );
5148         aLine.append( " /FitBH " );
5149         aLine.append( m_nInheritedPageHeight );//Open fit visible
5150         aLine.append( "]\n" );
5151         break;
5152     case PDFWriter::ActionZoom :
5153         aLine.append( "/OpenAction[" );
5154         aLine.append( aInitPageRef.makeStringAndClear() );
5155         aLine.append( " /XYZ null null " );
5156         if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5157             aLine.append( (double)m_aContext.Zoom/100.0 );
5158         else
5159             aLine.append( "0" );
5160         aLine.append( "]\n" );
5161         break;
5162     }
5163 
5164     // viewer preferences, if we had some, then emit
5165     if( m_aContext.HideViewerToolbar ||
5166         ( m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 && !m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle ) ||
5167         m_aContext.HideViewerMenubar ||
5168         m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
5169         m_aContext.CenterWindow || (m_aContext.FirstPageLeft  &&  m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
5170         m_aContext.OpenInFullScreenMode )
5171     {
5172         aLine.append( "/ViewerPreferences<<" );
5173         if( m_aContext.HideViewerToolbar )
5174             aLine.append( "/HideToolbar true\n" );
5175         if( m_aContext.HideViewerMenubar )
5176             aLine.append( "/HideMenubar true\n" );
5177         if( m_aContext.HideViewerWindowControls )
5178             aLine.append( "/HideWindowUI true\n" );
5179         if( m_aContext.FitWindow )
5180             aLine.append( "/FitWindow true\n" );
5181         if( m_aContext.CenterWindow )
5182             aLine.append( "/CenterWindow true\n" );
5183         if( m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 && !m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle )
5184             aLine.append( "/DisplayDocTitle true\n" );
5185         if( m_aContext.FirstPageLeft &&  m_aContext.PageLayout == PDFWriter::ContinuousFacing )
5186             aLine.append( "/Direction/R2L\n" );
5187         if( m_aContext.OpenInFullScreenMode )
5188             switch( m_aContext.PDFDocumentMode )
5189             {
5190             default :
5191             case PDFWriter::ModeDefault :
5192                 aLine.append( "/NonFullScreenPageMode/UseNone\n" );
5193                 break;
5194             case PDFWriter::UseOutlines :
5195                 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
5196                 break;
5197             case PDFWriter::UseThumbs :
5198                 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
5199                 break;
5200             }
5201         aLine.append( ">>\n" );
5202     }
5203 
5204     if( nOutlineDict )
5205     {
5206         aLine.append( "/Outlines " );
5207         aLine.append( nOutlineDict );
5208         aLine.append( " 0 R\n" );
5209     }
5210     if( nStructureDict )
5211     {
5212         aLine.append( "/StructTreeRoot " );
5213         aLine.append( nStructureDict );
5214         aLine.append( " 0 R\n" );
5215     }
5216     if( !m_aContext.DocumentLocale.Language.isEmpty() )
5217     {
5218         /* PDF allows only RFC 3066, see above in emitStructure(). */
5219         LanguageTag aLanguageTag( m_aContext.DocumentLocale);
5220         OUString aLanguage, aScript, aCountry;
5221         aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
5222         if (!aLanguage.isEmpty())
5223         {
5224             OUStringBuffer aLocBuf( 16 );
5225             aLocBuf.append( aLanguage );
5226             if( !aCountry.isEmpty() )
5227             {
5228                 aLocBuf.append( '-' );
5229                 aLocBuf.append( aCountry );
5230             }
5231             aLine.append( "/Lang" );
5232             appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
5233             aLine.append( "\n" );
5234         }
5235     }
5236     if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 )
5237     {
5238         aLine.append( "/MarkInfo<</Marked true>>\n" );
5239     }
5240     if( m_aWidgets.size() > 0 )
5241     {
5242         aLine.append( "/AcroForm<</Fields[\n" );
5243         int nWidgets = m_aWidgets.size();
5244         int nOut = 0;
5245         for( int j = 0; j < nWidgets; j++ )
5246         {
5247             // output only root fields
5248             if( m_aWidgets[j].m_nParent < 1 )
5249             {
5250                 aLine.append( m_aWidgets[j].m_nObject );
5251                 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
5252             }
5253         }
5254         aLine.append( "\n]" );
5255 
5256 #if HAVE_FEATURE_NSS
5257         if (m_nSignatureObject != -1)
5258             aLine.append( "/SigFlags 3");
5259 #endif
5260 
5261         aLine.append( "/DR " );
5262         aLine.append( getResourceDictObj() );
5263         aLine.append( " 0 R" );
5264         // NeedAppearances must not be used if PDF is signed
5265         if( m_bIsPDF_A1
5266 #if HAVE_FEATURE_NSS
5267             || ( m_nSignatureObject != -1 )
5268 #endif
5269             )
5270             aLine.append( ">>\n" );
5271         else
5272             aLine.append( "/NeedAppearances true>>\n" );
5273     }
5274 
5275     //check if there is a Metadata object
5276     if( nOutputIntentObject )
5277     {
5278         aLine.append("/OutputIntents[");
5279         aLine.append( nOutputIntentObject );
5280         aLine.append( " 0 R]" );
5281     }
5282 
5283     if( nMetadataObject )
5284     {
5285         aLine.append("/Metadata ");
5286         aLine.append( nMetadataObject );
5287         aLine.append( " 0 R" );
5288     }
5289 
5290     aLine.append( ">>\n"
5291                   "endobj\n\n" );
5292     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5293 
5294     return true;
5295 }
5296 
5297 #if HAVE_FEATURE_NSS
5298 
5299 bool PDFWriterImpl::emitSignature()
5300 {
5301     if( !updateObject( m_nSignatureObject ) )
5302         return false;
5303 
5304     OStringBuffer aLine( 0x5000 );
5305     aLine.append( m_nSignatureObject );
5306     aLine.append( " 0 obj\n" );
5307     aLine.append("<</Contents <" );
5308 
5309     sal_uInt64 nOffset = ~0U;
5310     CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) );
5311 
5312     m_nSignatureContentOffset = nOffset + aLine.getLength();
5313 
5314     // reserve some space for the PKCS#7 object
5315     OStringBuffer aContentFiller( MAX_SIGNATURE_CONTENT_LENGTH );
5316     comphelper::string::padToLength(aContentFiller, MAX_SIGNATURE_CONTENT_LENGTH, '0');
5317     aLine.append( aContentFiller.makeStringAndClear() );
5318     aLine.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached");
5319 
5320     if( !m_aContext.DocumentInfo.Author.isEmpty() )
5321     {
5322         aLine.append( "/Name" );
5323         appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, m_nSignatureObject, aLine );
5324     }
5325 
5326     aLine.append( " /M ");
5327     appendLiteralStringEncrypt( m_aCreationDateString, m_nSignatureObject, aLine );
5328 
5329     aLine.append( " /ByteRange [ 0 ");
5330     aLine.append( m_nSignatureContentOffset - 1 );
5331     aLine.append( " " );
5332     aLine.append( m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1 );
5333     aLine.append( " " );
5334 
5335     m_nSignatureLastByteRangeNoOffset = nOffset + aLine.getLength();
5336 
5337     // mark the last ByteRange no and add some space. Now, we don't know
5338     // how many bytes we need for this ByteRange value
5339     // The real value will be overwritten in the finalizeSignature method
5340     OStringBuffer aByteRangeFiller( 100  );
5341     comphelper::string::padToLength(aByteRangeFiller, 100, ' ');
5342     aLine.append( aByteRangeFiller.makeStringAndClear() );
5343     aLine.append("  /Filter/Adobe.PPKMS");
5344 
5345     //emit reason, location and contactinfo
5346     if ( !m_aContext.SignReason.isEmpty() )
5347     {
5348         aLine.append("/Reason");
5349         appendUnicodeTextStringEncrypt( m_aContext.SignReason, m_nSignatureObject, aLine );
5350     }
5351 
5352     if ( !m_aContext.SignLocation.isEmpty() )
5353     {
5354         aLine.append("/Location");
5355         appendUnicodeTextStringEncrypt( m_aContext.SignLocation, m_nSignatureObject, aLine );
5356     }
5357 
5358     if ( !m_aContext.SignContact.isEmpty() )
5359     {
5360         aLine.append("/ContactInfo");
5361         appendUnicodeTextStringEncrypt( m_aContext.SignContact, m_nSignatureObject, aLine );
5362     }
5363 
5364     aLine.append(" >>\nendobj\n\n" );
5365 
5366     if (!writeBuffer( aLine.getStr(), aLine.getLength() ))
5367         return false;
5368 
5369     return true;
5370 }
5371 
5372 bool PDFWriterImpl::finalizeSignature()
5373 {
5374     if (!m_aContext.SignCertificate.is())
5375         return false;
5376 
5377     // 1- calculate last ByteRange value
5378     sal_uInt64 nOffset = ~0U;
5379     CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) );
5380 
5381     sal_Int64 nLastByteRangeNo = nOffset - (m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1);
5382 
5383     // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position
5384     sal_uInt64 nWritten = 0;
5385     CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureLastByteRangeNoOffset) ) );
5386     OStringBuffer aByteRangeNo( 256 );
5387     aByteRangeNo.append( nLastByteRangeNo );
5388     aByteRangeNo.append( " ]" );
5389 
5390     if (m_aFile.write(aByteRangeNo.getStr(), aByteRangeNo.getLength(), nWritten) != osl::File::E_None)
5391     {
5392         CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
5393         return false;
5394     }
5395 
5396     // 3- create the PKCS#7 object using NSS
5397 
5398     // Prepare buffer and calculate PDF file digest
5399     CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, 0)) );
5400 
5401     std::unique_ptr<char[]> buffer1(new char[m_nSignatureContentOffset + 1]);
5402     sal_uInt64 bytesRead1;
5403 
5404     //FIXME: Check if hash is calculated from the correct byterange
5405     if (osl::File::E_None != m_aFile.read(buffer1.get(), m_nSignatureContentOffset - 1 , bytesRead1) ||
5406         bytesRead1 != (sal_uInt64)m_nSignatureContentOffset - 1)
5407     {
5408         SAL_WARN("vcl.pdfwriter", "First buffer read failed");
5409         return false;
5410     }
5411 
5412     std::unique_ptr<char[]> buffer2(new char[nLastByteRangeNo + 1]);
5413     sal_uInt64 bytesRead2;
5414 
5415     if (osl::File::E_None != m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1) ||
5416         osl::File::E_None != m_aFile.read(buffer2.get(), nLastByteRangeNo, bytesRead2) ||
5417         bytesRead2 != (sal_uInt64) nLastByteRangeNo)
5418     {
5419         SAL_WARN("vcl.pdfwriter", "Second buffer read failed");
5420         return false;
5421     }
5422 
5423     OStringBuffer aCMSHexBuffer;
5424     svl::crypto::Signing aSigning(m_aContext.SignCertificate);
5425     aSigning.AddDataRange(buffer1.get(), bytesRead1);
5426     aSigning.AddDataRange(buffer2.get(), bytesRead2);
5427     aSigning.SetSignTSA(m_aContext.SignTSA);
5428     aSigning.SetSignPassword(m_aContext.SignPassword);
5429     if (!aSigning.Sign(aCMSHexBuffer))
5430     {
5431         SAL_WARN("vcl.pdfwriter", "PDFWriter::Sign() failed");
5432         return false;
5433     }
5434 
5435     assert(aCMSHexBuffer.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH);
5436 
5437     // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
5438     nWritten = 0;
5439     CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset)) );
5440     m_aFile.write(aCMSHexBuffer.getStr(), aCMSHexBuffer.getLength(), nWritten);
5441 
5442     CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
5443     return true;
5444 }
5445 
5446 #endif //HAVE_FEATURE_NSS
5447 
5448 sal_Int32 PDFWriterImpl::emitInfoDict( )
5449 {
5450     sal_Int32 nObject = createObject();
5451 
5452     if( updateObject( nObject ) )
5453     {
5454         OStringBuffer aLine( 1024 );
5455         aLine.append( nObject );
5456         aLine.append( " 0 obj\n"
5457                       "<<" );
5458         if( !m_aContext.DocumentInfo.Title.isEmpty() )
5459         {
5460             aLine.append( "/Title" );
5461             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine );
5462             aLine.append( "\n" );
5463         }
5464         if( !m_aContext.DocumentInfo.Author.isEmpty() )
5465         {
5466             aLine.append( "/Author" );
5467             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine );
5468             aLine.append( "\n" );
5469         }
5470         if( !m_aContext.DocumentInfo.Subject.isEmpty() )
5471         {
5472             aLine.append( "/Subject" );
5473             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine );
5474             aLine.append( "\n" );
5475         }
5476         if( !m_aContext.DocumentInfo.Keywords.isEmpty() )
5477         {
5478             aLine.append( "/Keywords" );
5479             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine );
5480             aLine.append( "\n" );
5481         }
5482         if( !m_aContext.DocumentInfo.Creator.isEmpty() )
5483         {
5484             aLine.append( "/Creator" );
5485             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine );
5486             aLine.append( "\n" );
5487         }
5488         if( !m_aContext.DocumentInfo.Producer.isEmpty() )
5489         {
5490             aLine.append( "/Producer" );
5491             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine );
5492             aLine.append( "\n" );
5493         }
5494 
5495          aLine.append( "/CreationDate" );
5496          appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine );
5497         aLine.append( ">>\nendobj\n\n" );
5498         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
5499             nObject = 0;
5500     }
5501     else
5502         nObject = 0;
5503 
5504     return nObject;
5505 }
5506 
5507 // Part of this function may be shared with method appendDest.
5508 sal_Int32 PDFWriterImpl::emitNamedDestinations()
5509 {
5510     sal_Int32  nCount = m_aNamedDests.size();
5511     if( nCount <= 0 )
5512         return 0;//define internal error
5513 
5514     //get the object number for all the destinations
5515     sal_Int32 nObject = createObject();
5516 
5517     if( updateObject( nObject ) )
5518     {
5519         //emit the dictionary
5520         OStringBuffer aLine( 1024 );
5521         aLine.append( nObject );
5522         aLine.append( " 0 obj\n"
5523                       "<<" );
5524 
5525         sal_Int32  nDestID;
5526         for( nDestID = 0; nDestID < nCount; nDestID++ )
5527         {
5528             const PDFNamedDest& rDest   = m_aNamedDests[ nDestID ];
5529             // In order to correctly function both under an Internet browser and
5530             // directly with a reader (provided the reader has the feature) we
5531             // need to set the name of the destination the same way it will be encoded
5532             // in an Internet link
5533             INetURLObject aLocalURL( "http://ahost.ax" ); //dummy location, won't be used
5534             aLocalURL.SetMark( rDest.m_aDestName );
5535 
5536             const OUString aName   = aLocalURL.GetMark( INetURLObject::DecodeMechanism::NONE ); //same coding as
5537             // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
5538             const PDFPage& rDestPage    = m_aPages[ rDest.m_nPage ];
5539 
5540             aLine.append( '/' );
5541             appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
5542             aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
5543                                  //maps the preceding character properly
5544             aLine.append( rDestPage.m_nPageObject );
5545             aLine.append( " 0 R" );
5546 
5547             switch( rDest.m_eType )
5548             {
5549             case PDFWriter::DestAreaType::XYZ:
5550             default:
5551                 aLine.append( "/XYZ " );
5552                 appendFixedInt( rDest.m_aRect.Left(), aLine );
5553                 aLine.append( ' ' );
5554                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5555                 aLine.append( " 0" );
5556                 break;
5557             case PDFWriter::DestAreaType::FitRectangle:
5558                 aLine.append( "/FitR " );
5559                 appendFixedInt( rDest.m_aRect.Left(), aLine );
5560                 aLine.append( ' ' );
5561                 appendFixedInt( rDest.m_aRect.Top(), aLine );
5562                 aLine.append( ' ' );
5563                 appendFixedInt( rDest.m_aRect.Right(), aLine );
5564                 aLine.append( ' ' );
5565                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5566                 break;
5567             }
5568             aLine.append( "]\n" );
5569         }
5570 
5571         //close
5572         aLine.append( ">>\nendobj\n\n" );
5573         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
5574             nObject = 0;
5575     }
5576     else
5577         nObject = 0;
5578 
5579     return nObject;
5580 }
5581 
5582 // emits the output intent dictionary
5583 sal_Int32 PDFWriterImpl::emitOutputIntent()
5584 {
5585     if( !m_bIsPDF_A1 )
5586         return 0;
5587 
5588     //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
5589 
5590     OStringBuffer aLine( 1024 );
5591     sal_Int32 nICCObject = createObject();
5592     sal_Int32 nStreamLengthObject = createObject();
5593 
5594     aLine.append( nICCObject );
5595 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
5596     aLine.append( " 0 obj\n<</N 3/Length " );
5597     aLine.append( nStreamLengthObject );
5598     aLine.append( " 0 R" );
5599     if (!g_bDebugDisableCompression)
5600         aLine.append( "/Filter/FlateDecode" );
5601     aLine.append( ">>\nstream\n" );
5602     if ( !updateObject( nICCObject ) ) return 0;
5603     if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
5604     //get file position
5605     sal_uInt64 nBeginStreamPos = 0;
5606     m_aFile.getPos(nBeginStreamPos);
5607     beginCompression();
5608     checkAndEnableStreamEncryption( nICCObject );
5609     cmsHPROFILE hProfile = cmsCreate_sRGBProfile();
5610     //force ICC profile version 2.1
5611     cmsSetProfileVersion(hProfile, 2.1);
5612     cmsUInt32Number nBytesNeeded = 0;
5613     cmsSaveProfileToMem(hProfile, nullptr, &nBytesNeeded);
5614     if (!nBytesNeeded)
5615       return 0;
5616     std::vector<unsigned char> aBuffer(nBytesNeeded);
5617     cmsSaveProfileToMem(hProfile, &aBuffer[0], &nBytesNeeded);
5618     cmsCloseProfile(hProfile);
5619     bool written = writeBuffer( &aBuffer[0], (sal_Int32) aBuffer.size() );
5620     disableStreamEncryption();
5621     endCompression();
5622     sal_uInt64 nEndStreamPos = 0;
5623     m_aFile.getPos(nEndStreamPos);
5624 
5625     if( !written )
5626         return 0;
5627     if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
5628         return 0 ;
5629     aLine.setLength( 0 );
5630 
5631     //emit the stream length   object
5632     if ( !updateObject( nStreamLengthObject ) ) return 0;
5633     aLine.setLength( 0 );
5634     aLine.append( nStreamLengthObject );
5635     aLine.append( " 0 obj\n" );
5636     aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
5637     aLine.append( "\nendobj\n\n" );
5638     if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
5639     aLine.setLength( 0 );
5640 
5641     //emit the OutputIntent dictionary
5642     sal_Int32 nOIObject = createObject();
5643     if ( !updateObject( nOIObject ) ) return 0;
5644     aLine.append( nOIObject );
5645     aLine.append( " 0 obj\n"
5646                   "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
5647 
5648     OUString const aComment( "sRGB IEC61966-2.1"  );
5649     appendLiteralStringEncrypt( aComment ,nOIObject, aLine );
5650     aLine.append("/DestOutputProfile ");
5651     aLine.append( nICCObject );
5652     aLine.append( " 0 R>>\nendobj\n\n" );
5653     if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
5654 
5655     return nOIObject;
5656 }
5657 
5658 // formats the string for the XML stream
5659 static void escapeStringXML( const OUString& rStr, OUString &rValue)
5660 {
5661     const sal_Unicode* pUni = rStr.getStr();
5662     int nLen = rStr.getLength();
5663     for( ; nLen; nLen--, pUni++ )
5664     {
5665         switch( *pUni )
5666         {
5667         case u'&':
5668             rValue += "&amp;";
5669         break;
5670         case u'<':
5671             rValue += "&lt;";
5672         break;
5673         case u'>':
5674             rValue += "&gt;";
5675         break;
5676         case u'\'':
5677             rValue += "&apos;";
5678         break;
5679         case u'"':
5680             rValue += "&quot;";
5681         break;
5682         default:
5683             rValue += OUStringLiteral1( *pUni );
5684             break;
5685         }
5686     }
5687 }
5688 
5689 // emits the document metadata
5690 sal_Int32 PDFWriterImpl::emitDocumentMetadata()
5691 {
5692     if( !m_bIsPDF_A1 )
5693         return 0;
5694 
5695     //get the object number for all the destinations
5696     sal_Int32 nObject = createObject();
5697 
5698     if( updateObject( nObject ) )
5699     {
5700         // the following string are written in UTF-8 unicode
5701         OStringBuffer aMetadataStream( 8192 );
5702 
5703         aMetadataStream.append( "<?xpacket begin=\"" );
5704         // these lines write Unicode "zero width non-breaking space character" (U+FEFF)
5705         // (aka byte-order mark ) used as a byte-order marker.
5706         aMetadataStream.append( OUStringToOString( OUString( u'\xFEFF' ), RTL_TEXTENCODING_UTF8 ) );
5707         aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
5708         aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
5709         aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
5710         //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
5711         aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
5712         aMetadataStream.append( "      xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
5713         aMetadataStream.append( "   <pdfaid:part>1</pdfaid:part>\n" );
5714         aMetadataStream.append( "   <pdfaid:conformance>A</pdfaid:conformance>\n" );
5715         aMetadataStream.append( "  </rdf:Description>\n" );
5716         //... Dublin Core properties go here
5717         if( !m_aContext.DocumentInfo.Title.isEmpty() ||
5718             !m_aContext.DocumentInfo.Author.isEmpty() ||
5719             !m_aContext.DocumentInfo.Subject.isEmpty() )
5720         {
5721             aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
5722             aMetadataStream.append( "      xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
5723             if( !m_aContext.DocumentInfo.Title.isEmpty() )
5724             {
5725                 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
5726                 aMetadataStream.append( "   <dc:title>\n" );
5727                 aMetadataStream.append( "    <rdf:Alt>\n" );
5728                 aMetadataStream.append( "     <rdf:li xml:lang=\"x-default\">" );
5729                 OUString aTitle;
5730                 escapeStringXML( m_aContext.DocumentInfo.Title, aTitle );
5731                 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 )  );
5732                 aMetadataStream.append( "</rdf:li>\n" );
5733                 aMetadataStream.append( "    </rdf:Alt>\n" );
5734                 aMetadataStream.append( "   </dc:title>\n" );
5735             }
5736             if( !m_aContext.DocumentInfo.Author.isEmpty() )
5737             {
5738                 aMetadataStream.append( "   <dc:creator>\n" );
5739                 aMetadataStream.append( "    <rdf:Seq>\n" );
5740                 aMetadataStream.append( "     <rdf:li>" );
5741                 OUString aAuthor;
5742                 escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor );
5743                 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 )  );
5744                 aMetadataStream.append( "</rdf:li>\n" );
5745                 aMetadataStream.append( "    </rdf:Seq>\n" );
5746                 aMetadataStream.append( "   </dc:creator>\n" );
5747             }
5748             if( !m_aContext.DocumentInfo.Subject.isEmpty() )
5749             {
5750                 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
5751                 aMetadataStream.append( "   <dc:description>\n" );
5752                 aMetadataStream.append( "    <rdf:Alt>\n" );
5753                 aMetadataStream.append( "     <rdf:li xml:lang=\"x-default\">" );
5754                 OUString aSubject;
5755                 escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject );
5756                 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 )  );
5757                 aMetadataStream.append( "</rdf:li>\n" );
5758                 aMetadataStream.append( "    </rdf:Alt>\n" );
5759                 aMetadataStream.append( "   </dc:description>\n" );
5760             }
5761             aMetadataStream.append( "  </rdf:Description>\n" );
5762         }
5763 
5764         //... PDF properties go here
5765         if( !m_aContext.DocumentInfo.Producer.isEmpty() ||
5766             !m_aContext.DocumentInfo.Keywords.isEmpty() )
5767         {
5768             aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
5769             aMetadataStream.append( "     xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
5770             if( !m_aContext.DocumentInfo.Producer.isEmpty() )
5771             {
5772                 aMetadataStream.append( "   <pdf:Producer>" );
5773                 OUString aProducer;
5774                 escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer );
5775                 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 )  );
5776                 aMetadataStream.append( "</pdf:Producer>\n" );
5777             }
5778             if( !m_aContext.DocumentInfo.Keywords.isEmpty() )
5779             {
5780                 aMetadataStream.append( "   <pdf:Keywords>" );
5781                 OUString aKeywords;
5782                 escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords );
5783                 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 )  );
5784                 aMetadataStream.append( "</pdf:Keywords>\n" );
5785             }
5786             aMetadataStream.append( "  </rdf:Description>\n" );
5787         }
5788 
5789         aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
5790         aMetadataStream.append( "    xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
5791         if( !m_aContext.DocumentInfo.Creator.isEmpty() )
5792         {
5793             aMetadataStream.append( "   <xmp:CreatorTool>" );
5794             OUString aCreator;
5795             escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator );
5796             aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 )  );
5797             aMetadataStream.append( "</xmp:CreatorTool>\n" );
5798         }
5799         //creation date
5800         aMetadataStream.append( "   <xmp:CreateDate>" );
5801         aMetadataStream.append( m_aCreationMetaDateString );
5802         aMetadataStream.append( "</xmp:CreateDate>\n" );
5803 
5804         aMetadataStream.append( "  </rdf:Description>\n" );
5805         aMetadataStream.append( " </rdf:RDF>\n" );
5806         aMetadataStream.append( "</x:xmpmeta>\n" );
5807 
5808         //add the padding
5809         for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ )
5810         {
5811             aMetadataStream.append( " " );
5812             if( nSpaces % 100 == 0 )
5813                 aMetadataStream.append( "\n" );
5814         }
5815 
5816         aMetadataStream.append( "<?xpacket end=\"w\"?>\n" );
5817 
5818         OStringBuffer aMetadataObj( 1024 );
5819 
5820         aMetadataObj.append( nObject );
5821         aMetadataObj.append( " 0 obj\n" );
5822 
5823         aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
5824 
5825         aMetadataObj.append( aMetadataStream.getLength() );
5826         aMetadataObj.append( ">>\nstream\n" );
5827         if ( !writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
5828             return 0;
5829         //emit the stream
5830         if ( !writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) )
5831             return 0;
5832 
5833         aMetadataObj.setLength( 0 );
5834         aMetadataObj.append( "\nendstream\nendobj\n\n" );
5835         if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
5836             nObject = 0;
5837     }
5838     else
5839         nObject = 0;
5840 
5841     return nObject;
5842 }
5843 
5844 bool PDFWriterImpl::emitTrailer()
5845 {
5846     // emit doc info
5847     sal_Int32 nDocInfoObject = emitInfoDict( );
5848 
5849     sal_Int32 nSecObject = 0;
5850 
5851     if( m_aContext.Encryption.Encrypt() )
5852     {
5853         //emit the security information
5854         //must be emitted as indirect dictionary object, since
5855         //Acrobat Reader 5 works only with this kind of implementation
5856         nSecObject = createObject();
5857 
5858         if( updateObject( nSecObject ) )
5859         {
5860             OStringBuffer aLineS( 1024 );
5861             aLineS.append( nSecObject );
5862             aLineS.append( " 0 obj\n"
5863                            "<</Filter/Standard/V " );
5864             // check the version
5865             aLineS.append( "2/Length 128/R 3" );
5866 
5867             // emit the owner password, must not be encrypted
5868             aLineS.append( "/O(" );
5869             appendLiteralString( reinterpret_cast<char*>(&m_aContext.Encryption.OValue[0]), sal_Int32(m_aContext.Encryption.OValue.size()), aLineS );
5870             aLineS.append( ")/U(" );
5871             appendLiteralString( reinterpret_cast<char*>(&m_aContext.Encryption.UValue[0]), sal_Int32(m_aContext.Encryption.UValue.size()), aLineS );
5872             aLineS.append( ")/P " );// the permission set
5873             aLineS.append( m_nAccessPermissions );
5874             aLineS.append( ">>\nendobj\n\n" );
5875             if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) )
5876                 nSecObject = 0;
5877         }
5878         else
5879             nSecObject = 0;
5880     }
5881     // emit xref table
5882     // remember start
5883     sal_uInt64 nXRefOffset = 0;
5884     CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nXRefOffset )) );
5885     CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
5886 
5887     sal_Int32 nObjects = m_aObjects.size();
5888     OStringBuffer aLine;
5889     aLine.append( "0 " );
5890     aLine.append( (sal_Int32)(nObjects+1) );
5891     aLine.append( "\n" );
5892     aLine.append( "0000000000 65535 f \n" );
5893     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5894 
5895     for( sal_Int32 i = 0; i < nObjects; i++ )
5896     {
5897         aLine.setLength( 0 );
5898         OString aOffset = OString::number( m_aObjects[i] );
5899         for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
5900             aLine.append( '0' );
5901         aLine.append( aOffset );
5902         aLine.append( " 00000 n \n" );
5903         SAL_WARN_IF( aLine.getLength() != 20, "vcl.pdfwriter", "invalid xref entry" );
5904         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5905     }
5906 
5907     // prepare document checksum
5908     OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
5909     if( m_aDocDigest )
5910     {
5911         sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
5912         rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) );
5913         for(sal_uInt8 i : nMD5Sum)
5914             appendHex( i, aDocChecksum );
5915     }
5916     // document id set in setDocInfo method
5917     // emit trailer
5918     aLine.setLength( 0 );
5919     aLine.append( "trailer\n"
5920                   "<</Size " );
5921     aLine.append( (sal_Int32)(nObjects+1) );
5922     aLine.append( "/Root " );
5923     aLine.append( m_nCatalogObject );
5924     aLine.append( " 0 R\n" );
5925     if( nSecObject )
5926     {
5927         aLine.append( "/Encrypt ");
5928         aLine.append( nSecObject );
5929         aLine.append( " 0 R\n" );
5930     }
5931     if( nDocInfoObject )
5932     {
5933         aLine.append( "/Info " );
5934         aLine.append( nDocInfoObject );
5935         aLine.append( " 0 R\n" );
5936     }
5937     if( ! m_aContext.Encryption.DocumentIdentifier.empty() )
5938     {
5939         aLine.append( "/ID [ <" );
5940         for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
5941              it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
5942         {
5943             appendHex( sal_Int8(*it), aLine );
5944         }
5945         aLine.append( ">\n"
5946                       "<" );
5947         for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
5948              it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
5949         {
5950             appendHex( sal_Int8(*it), aLine );
5951         }
5952         aLine.append( "> ]\n" );
5953     }
5954     if( !aDocChecksum.isEmpty() )
5955     {
5956         aLine.append( "/DocChecksum /" );
5957         aLine.append( aDocChecksum.makeStringAndClear() );
5958         aLine.append( "\n" );
5959     }
5960     if( m_aAdditionalStreams.size() > 0 )
5961     {
5962         aLine.append( "/AdditionalStreams [" );
5963         for(const PDFAddStream & rAdditionalStream : m_aAdditionalStreams)
5964         {
5965             aLine.append( "/" );
5966             appendName( rAdditionalStream.m_aMimeType, aLine );
5967             aLine.append( " " );
5968             aLine.append( rAdditionalStream.m_nStreamObject );
5969             aLine.append( " 0 R\n" );
5970         }
5971         aLine.append( "]\n" );
5972     }
5973     aLine.append( ">>\n"
5974                   "startxref\n" );
5975     aLine.append( (sal_Int64)nXRefOffset );
5976     aLine.append( "\n"
5977                   "%%EOF\n" );
5978     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5979 
5980     return true;
5981 }
5982 
5983 struct AnnotationSortEntry
5984 {
5985     sal_Int32 nTabOrder;
5986     sal_Int32 nObject;
5987     sal_Int32 nWidgetIndex;
5988 
5989     AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
5990         nTabOrder( nTab ),
5991         nObject( nObj ),
5992         nWidgetIndex( nI )
5993     {}
5994 };
5995 
5996 struct AnnotSortContainer
5997 {
5998     std::set< sal_Int32 >               aObjects;
5999     std::vector< AnnotationSortEntry >    aSortedAnnots;
6000 };
6001 
6002 struct AnnotSorterLess
6003 {
6004     std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets;
6005 
6006     explicit AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {}
6007 
6008     bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
6009     {
6010         if( rLeft.nTabOrder < rRight.nTabOrder )
6011             return true;
6012         if( rRight.nTabOrder < rLeft.nTabOrder )
6013             return false;
6014         if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
6015             return false;
6016         if( rRight.nWidgetIndex < 0 )
6017             return true;
6018         if( rLeft.nWidgetIndex < 0 )
6019             return false;
6020         // remember: widget rects are in PDF coordinates, so they are ordered down up
6021         if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
6022             m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
6023             return true;
6024         if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
6025             m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
6026             return false;
6027         if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
6028             m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
6029             return true;
6030         return false;
6031     }
6032 };
6033 
6034 void PDFWriterImpl::sortWidgets()
6035 {
6036     // sort widget annotations on each page as per their
6037     // TabOrder attribute
6038     std::unordered_map< sal_Int32, AnnotSortContainer > sorted;
6039     int nWidgets = m_aWidgets.size();
6040     for( int nW = 0; nW < nWidgets; nW++ )
6041     {
6042         const PDFWidget& rWidget = m_aWidgets[nW];
6043         if( rWidget.m_nPage >= 0 )
6044         {
6045             AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
6046             // optimize vector allocation
6047             if( rCont.aSortedAnnots.empty() )
6048                 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
6049             // insert widget to tab sorter
6050             // RadioButtons are not page annotations, only their individual check boxes are
6051             if( rWidget.m_eType != PDFWriter::RadioButton )
6052             {
6053                 rCont.aObjects.insert( rWidget.m_nObject );
6054                 rCont.aSortedAnnots.emplace_back( rWidget.m_nTabOrder, rWidget.m_nObject, nW );
6055             }
6056         }
6057     }
6058     for( std::unordered_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it )
6059     {
6060         // append entries for non widget annotations
6061         PDFPage& rPage = m_aPages[ it->first ];
6062         unsigned int nAnnots = rPage.m_aAnnotations.size();
6063         for( unsigned int nA = 0; nA < nAnnots; nA++ )
6064             if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end())
6065                 it->second.aSortedAnnots.emplace_back( 10000, rPage.m_aAnnotations[nA], -1 );
6066 
6067         AnnotSorterLess aLess( m_aWidgets );
6068         std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess );
6069         // sanity check
6070         if( it->second.aSortedAnnots.size() == nAnnots)
6071         {
6072             for( unsigned int nA = 0; nA < nAnnots; nA++ )
6073                 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject;
6074         }
6075         else
6076         {
6077             SAL_WARN( "vcl.pdfwriter", "wrong number of sorted annotations" );
6078             SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions "
6079                      "on page nr " << (long int)it->first << ", " <<
6080                      (long int)it->second.aSortedAnnots.size() << " sorted and " <<
6081                      (long int)nAnnots << " unsorted");
6082         }
6083     }
6084 
6085     // FIXME: implement tab order in structure tree for PDF 1.5
6086 }
6087 
6088 namespace vcl {
6089 class PDFStreamIf :
6090         public cppu::WeakImplHelper< css::io::XOutputStream >
6091 {
6092     PDFWriterImpl*  m_pWriter;
6093     bool            m_bWrite;
6094     public:
6095     explicit PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
6096 
6097     virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) override;
6098     virtual void SAL_CALL flush() override;
6099     virtual void SAL_CALL closeOutput() override;
6100 };
6101 }
6102 
6103 void SAL_CALL  PDFStreamIf::writeBytes( const css::uno::Sequence< sal_Int8 >& aData )
6104 {
6105     if( m_bWrite && aData.getLength() )
6106     {
6107         sal_Int32 nBytes = aData.getLength();
6108         m_pWriter->writeBuffer( aData.getConstArray(), nBytes );
6109     }
6110 }
6111 
6112 void SAL_CALL PDFStreamIf::flush()
6113 {
6114 }
6115 
6116 void SAL_CALL PDFStreamIf::closeOutput()
6117 {
6118     m_bWrite = false;
6119 }
6120 
6121 bool PDFWriterImpl::emitAdditionalStreams()
6122 {
6123     unsigned int nStreams = m_aAdditionalStreams.size();
6124     for( unsigned int i = 0; i < nStreams; i++ )
6125     {
6126         PDFAddStream& rStream = m_aAdditionalStreams[i];
6127         rStream.m_nStreamObject = createObject();
6128         sal_Int32 nSizeObject = createObject();
6129 
6130         if( ! updateObject( rStream.m_nStreamObject ) )
6131             return false;
6132 
6133         OStringBuffer aLine;
6134         aLine.append( rStream.m_nStreamObject );
6135         aLine.append( " 0 obj\n<</Length " );
6136         aLine.append( nSizeObject );
6137         aLine.append( " 0 R" );
6138         if( rStream.m_bCompress )
6139             aLine.append( "/Filter/FlateDecode" );
6140         aLine.append( ">>\nstream\n" );
6141         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6142             return false;
6143         sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0;
6144         if( osl::File::E_None != m_aFile.getPos(nBeginStreamPos) )
6145         {
6146             m_aFile.close();
6147             m_bOpen = false;
6148         }
6149         if( rStream.m_bCompress )
6150             beginCompression();
6151 
6152         checkAndEnableStreamEncryption( rStream.m_nStreamObject );
6153         css::uno::Reference< css::io::XOutputStream > xStream( new PDFStreamIf( this ) );
6154         assert(rStream.m_pStream);
6155         if (!rStream.m_pStream)
6156             return false;
6157         rStream.m_pStream->write( xStream );
6158         xStream.clear();
6159         delete rStream.m_pStream;
6160         rStream.m_pStream = nullptr;
6161         disableStreamEncryption();
6162 
6163         if( rStream.m_bCompress )
6164             endCompression();
6165 
6166         if (osl::File::E_None != m_aFile.getPos(nEndStreamPos))
6167         {
6168             m_aFile.close();
6169             m_bOpen = false;
6170             return false;
6171         }
6172         if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6173             return false ;
6174         // emit stream length object
6175         if( ! updateObject( nSizeObject ) )
6176             return false;
6177         aLine.setLength( 0 );
6178         aLine.append( nSizeObject );
6179         aLine.append( " 0 obj\n" );
6180         aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6181         aLine.append( "\nendobj\n\n" );
6182         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6183             return false;
6184     }
6185     return true;
6186 }
6187 
6188 bool PDFWriterImpl::emit()
6189 {
6190     endPage();
6191 
6192     // resort structure tree and annotations if necessary
6193     // needed for widget tab order
6194     sortWidgets();
6195 
6196 #if HAVE_FEATURE_NSS
6197     if( m_aContext.SignPDF )
6198     {
6199         // sign the document
6200         PDFWriter::SignatureWidget aSignature;
6201         aSignature.Name = "Signature1";
6202         createControl( aSignature, 0 );
6203     }
6204 #endif
6205 
6206     // emit additional streams
6207     CHECK_RETURN( emitAdditionalStreams() );
6208 
6209     // emit catalog
6210     CHECK_RETURN( emitCatalog() );
6211 
6212 #if HAVE_FEATURE_NSS
6213     if (m_nSignatureObject != -1) // if document is signed, emit sigdict
6214     {
6215         if( !emitSignature() )
6216         {
6217             m_aErrors.insert( PDFWriter::Error_Signature_Failed );
6218             return false;
6219         }
6220     }
6221 #endif
6222 
6223     // emit trailer
6224     CHECK_RETURN( emitTrailer() );
6225 
6226 #if HAVE_FEATURE_NSS
6227     if (m_nSignatureObject != -1) // finalize the signature
6228     {
6229         if( !finalizeSignature() )
6230         {
6231             m_aErrors.insert( PDFWriter::Error_Signature_Failed );
6232             return false;
6233         }
6234     }
6235 #endif
6236 
6237     m_aFile.close();
6238     m_bOpen = false;
6239 
6240     return true;
6241 }
6242 
6243 
6244 sal_Int32 PDFWriterImpl::getSystemFont( const vcl::Font& i_rFont )
6245 {
6246     getReferenceDevice()->Push();
6247     getReferenceDevice()->SetFont( i_rFont );
6248     getReferenceDevice()->ImplNewFont();
6249 
6250     const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontInstance->maFontSelData.mpFontData;
6251     sal_Int32 nFontID = 0;
6252     FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont );
6253     if( it != m_aSystemFonts.end() )
6254         nFontID = it->second.m_nNormalFontID;
6255     else
6256     {
6257         nFontID = m_nNextFID++;
6258         m_aSystemFonts[ pDevFont ] = EmbedFont();
6259         m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID;
6260     }
6261 
6262     getReferenceDevice()->Pop();
6263     getReferenceDevice()->ImplNewFont();
6264 
6265     return nFontID;
6266 }
6267 
6268 void PDFWriterImpl::registerGlyphs( int nGlyphs,
6269                                     const GlyphItem** pGlyphs,
6270                                     sal_Int32* pGlyphWidths,
6271                                     sal_Ucs* pCodeUnits,
6272                                     sal_Int32 const * pCodeUnitsPerGlyph,
6273                                     sal_uInt8* pMappedGlyphs,
6274                                     sal_Int32* pMappedFontObjects,
6275                                     const PhysicalFontFace* pFallbackFonts[] )
6276 {
6277     SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics();
6278 
6279     if (!pGraphics)
6280         return;
6281 
6282     const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontInstance->maFontSelData.mpFontData;
6283     sal_Ucs* pCurUnicode = pCodeUnits;
6284     for( int i = 0; i < nGlyphs; pCurUnicode += pCodeUnitsPerGlyph[i] , i++ )
6285     {
6286         const int nFontGlyphId = pGlyphs[i]->maGlyphId;
6287         const PhysicalFontFace* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont;
6288 
6289         FontSubset& rSubset = m_aSubsets[ pCurrentFont ];
6290         // search for font specific glyphID
6291         FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId );
6292         if( it != rSubset.m_aMapping.end() )
6293         {
6294             pMappedFontObjects[i] = it->second.m_nFontID;
6295             pMappedGlyphs[i] = it->second.m_nSubsetGlyphID;
6296         }
6297         else
6298         {
6299             // create new subset if necessary
6300             if( rSubset.m_aSubsets.empty()
6301             || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
6302             {
6303                 rSubset.m_aSubsets.emplace_back( m_nNextFID++ );
6304             }
6305 
6306             // copy font id
6307             pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID;
6308             // create new glyph in subset
6309             sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
6310             pMappedGlyphs[i] = nNewId;
6311 
6312             // add new glyph to emitted font subset
6313             GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
6314             rNewGlyphEmit.setGlyphId( nNewId );
6315             for( sal_Int32 n = 0; n < pCodeUnitsPerGlyph[i]; n++ )
6316                 rNewGlyphEmit.addCode( pCurUnicode[n] );
6317 
6318             // add new glyph to font mapping
6319             Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
6320             rNewGlyph.m_nFontID = pMappedFontObjects[i];
6321             rNewGlyph.m_nSubsetGlyphID = nNewId;
6322         }
6323         if (!getReferenceDevice()->AcquireGraphics())
6324             return;
6325         pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont,
6326                                                       nFontGlyphId,
6327                                                       pGlyphs[i]->IsVertical(),
6328                                                       pGraphics );
6329     }
6330 }
6331 
6332 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const OUString& rText, bool bTextLines )
6333 {
6334     push( PushFlags::ALL );
6335 
6336     FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief();
6337 
6338     Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor();
6339     Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
6340     Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
6341     Color aReliefColor( COL_LIGHTGRAY );
6342     if( aTextColor == COL_BLACK )
6343         aTextColor = Color( COL_WHITE );
6344     if( aTextLineColor == COL_BLACK )
6345         aTextLineColor = Color( COL_WHITE );
6346     if( aOverlineColor == COL_BLACK )
6347         aOverlineColor = Color( COL_WHITE );
6348     if( aTextColor == COL_WHITE )
6349         aReliefColor = Color( COL_BLACK );
6350 
6351     Font aSetFont = m_aCurrentPDFState.m_aFont;
6352     aSetFont.SetRelief( FontRelief::NONE );
6353     aSetFont.SetShadow( false );
6354 
6355     aSetFont.SetColor( aReliefColor );
6356     setTextLineColor( aReliefColor );
6357     setOverlineColor( aReliefColor );
6358     setFont( aSetFont );
6359     long nOff = 1 + getReferenceDevice()->mnDPIX/300;
6360     if( eRelief == FontRelief::Engraved )
6361         nOff = -nOff;
6362 
6363     rLayout.DrawOffset() += Point( nOff, nOff );
6364     updateGraphicsState();
6365     drawLayout( rLayout, rText, bTextLines );
6366 
6367     rLayout.DrawOffset() -= Point( nOff, nOff );
6368     setTextLineColor( aTextLineColor );
6369     setOverlineColor( aOverlineColor );
6370     aSetFont.SetColor( aTextColor );
6371     setFont( aSetFont );
6372     updateGraphicsState();
6373     drawLayout( rLayout, rText, bTextLines );
6374 
6375     // clean up the mess
6376     pop();
6377 }
6378 
6379 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const OUString& rText, bool bTextLines )
6380 {
6381     Font aSaveFont = m_aCurrentPDFState.m_aFont;
6382     Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
6383     Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
6384 
6385     Font& rFont = m_aCurrentPDFState.m_aFont;
6386     if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 )
6387         rFont.SetColor( Color( COL_LIGHTGRAY ) );
6388     else
6389         rFont.SetColor( Color( COL_BLACK ) );
6390     rFont.SetShadow( false );
6391     rFont.SetOutline( false );
6392     setFont( rFont );
6393     setTextLineColor( rFont.GetColor() );
6394     setOverlineColor( rFont.GetColor() );
6395     updateGraphicsState();
6396 
6397     long nOff = 1 + ((m_pReferenceDevice->mpFontInstance->mnLineHeight-24)/24);
6398     if( rFont.IsOutline() )
6399         nOff++;
6400     rLayout.DrawBase() += Point( nOff, nOff );
6401     drawLayout( rLayout, rText, bTextLines );
6402     rLayout.DrawBase() -= Point( nOff, nOff );
6403 
6404     setFont( aSaveFont );
6405     setTextLineColor( aSaveTextLineColor );
6406     setOverlineColor( aSaveOverlineColor );
6407     updateGraphicsState();
6408 }
6409 
6410 void PDFWriterImpl::drawVerticalGlyphs(
6411         const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
6412         OStringBuffer& rLine,
6413         const Point& rAlignOffset,
6414         const Matrix3& rRotScale,
6415         double fAngle,
6416         double fXScale,
6417         double fSkew,
6418         sal_Int32 nFontHeight )
6419 {
6420     long nXOffset = 0;
6421     Point aCurPos( rGlyphs[0].m_aPos );
6422     aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
6423     aCurPos += rAlignOffset;
6424     for( size_t i = 0; i < rGlyphs.size(); i++ )
6425     {
6426         // have to emit each glyph on its own
6427         double fDeltaAngle = 0.0;
6428         double fYScale = 1.0;
6429         double fTempXScale = fXScale;
6430         double fSkewB = fSkew;
6431         double fSkewA = 0.0;
6432 
6433         Point aDeltaPos;
6434         if (rGlyphs[i].m_bVertical)
6435         {
6436             fDeltaAngle = M_PI/2.0;
6437             aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent();
6438             aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale);
6439             fYScale = fXScale;
6440             fTempXScale = 1.0;
6441             fSkewA = -fSkewB;
6442             fSkewB = 0.0;
6443         }
6444         aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) );
6445         if( i < rGlyphs.size()-1 )
6446         // #i120627# the text on the Y axis is reversed when export ppt file to PDF format
6447         {
6448             long nOffsetX = rGlyphs[i+1].m_aPos.X() - rGlyphs[i].m_aPos.X();
6449             long nOffsetY = rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
6450             nXOffset += (int)sqrt(double(nOffsetX*nOffsetX + nOffsetY*nOffsetY));
6451         }
6452         if( ! rGlyphs[i].m_nGlyphId )
6453             continue;
6454 
6455         aDeltaPos = rRotScale.transform( aDeltaPos );
6456 
6457         Matrix3 aMat;
6458         if( fSkewB != 0.0 || fSkewA != 0.0 )
6459             aMat.skew( fSkewA, fSkewB );
6460         aMat.scale( fTempXScale, fYScale );
6461         aMat.rotate( fAngle+fDeltaAngle );
6462         aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
6463         aMat.append( m_aPages.back(), rLine );
6464         rLine.append( " Tm" );
6465         if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
6466         {
6467             rLine.append( " /F" );
6468             rLine.append( rGlyphs[i].m_nMappedFontId );
6469             rLine.append( ' ' );
6470             m_aPages.back().appendMappedLength( nFontHeight, rLine );
6471             rLine.append( " Tf" );
6472         }
6473         rLine.append( "<" );
6474         appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
6475         rLine.append( ">Tj\n" );
6476     }
6477 }
6478 
6479 void PDFWriterImpl::drawHorizontalGlyphs(
6480         const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
6481         OStringBuffer& rLine,
6482         const Point& rAlignOffset,
6483         double fAngle,
6484         double fXScale,
6485         double fSkew,
6486         sal_Int32 nFontHeight,
6487         sal_Int32 nPixelFontHeight
6488         )
6489 {
6490     // horizontal (= normal) case
6491 
6492     // fill in  run end indices
6493     // end is marked by index of the first glyph of the next run
6494     // a run is marked by same mapped font id and same Y position
6495     std::vector< sal_uInt32 > aRunEnds;
6496     aRunEnds.reserve( rGlyphs.size() );
6497     for( size_t i = 1; i < rGlyphs.size(); i++ )
6498     {
6499         if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
6500             rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
6501         {
6502             aRunEnds.push_back(i);
6503         }
6504     }
6505     // last run ends at last glyph
6506     aRunEnds.push_back( rGlyphs.size() );
6507 
6508     // loop over runs of the same font
6509     sal_uInt32 nBeginRun = 0;
6510     for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
6511     {
6512         // setup text matrix
6513         Point aCurPos = rGlyphs[nBeginRun].m_aPos;
6514         // back transformation to current coordinate system
6515         aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
6516         aCurPos += rAlignOffset;
6517         // the first run can be set with "Td" operator
6518         // subsequent use of that operator would move
6519         // the textline matrix relative to what was set before
6520         // making use of that would drive us into rounding issues
6521         Matrix3 aMat;
6522         if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
6523         {
6524             m_aPages.back().appendPoint( aCurPos, rLine );
6525             rLine.append( " Td " );
6526         }
6527         else
6528         {
6529             if( fSkew != 0.0 )
6530                 aMat.skew( 0.0, fSkew );
6531             aMat.scale( fXScale, 1.0 );
6532             aMat.rotate( fAngle );
6533             aMat.translate( aCurPos.X(), aCurPos.Y() );
6534             aMat.append( m_aPages.back(), rLine );
6535             rLine.append( " Tm\n" );
6536         }
6537         // set up correct font
6538         rLine.append( "/F" );
6539         rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
6540         rLine.append( ' ' );
6541         m_aPages.back().appendMappedLength( nFontHeight, rLine );
6542         rLine.append( " Tf" );
6543 
6544         // output glyphs using Tj or TJ
6545         OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
6546         aKernedLine.append( "[<" );
6547         aUnkernedLine.append( '<' );
6548         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
6549         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
6550 
6551         aMat.invert();
6552         bool bNeedKern = false;
6553         for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
6554         {
6555             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
6556             // check if default glyph positioning is sufficient
6557             const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
6558             const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
6559             double fAdvance = aThisPos.X() - aPrevPos.X();
6560             fAdvance *= 1000.0 / nPixelFontHeight;
6561             const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5);
6562             if( nAdjustment != 0 )
6563             {
6564                 // apply individual glyph positioning
6565                 bNeedKern = true;
6566                 aKernedLine.append( ">" );
6567                 aKernedLine.append( nAdjustment );
6568                 aKernedLine.append( "<" );
6569             }
6570             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
6571         }
6572         aKernedLine.append( ">]TJ\n" );
6573         aUnkernedLine.append( ">Tj\n" );
6574         rLine.append(
6575             (bNeedKern ? aKernedLine : aUnkernedLine).makeStringAndClear() );
6576 
6577         // set beginning of next run
6578         nBeginRun = aRunEnds[nRun];
6579     }
6580 }
6581 
6582 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool bTextLines )
6583 {
6584     // relief takes precedence over shadow (see outdev3.cxx)
6585     if(  m_aCurrentPDFState.m_aFont.GetRelief() != FontRelief::NONE )
6586     {
6587         drawRelief( rLayout, rText, bTextLines );
6588         return;
6589     }
6590     else if( m_aCurrentPDFState.m_aFont.IsShadow() )
6591         drawShadow( rLayout, rText, bTextLines );
6592 
6593     OStringBuffer aLine( 512 );
6594 
6595     const int nMaxGlyphs = 256;
6596 
6597     const GlyphItem* pGlyphs[nMaxGlyphs] = { nullptr };
6598     const PhysicalFontFace* pFallbackFonts[nMaxGlyphs] = { nullptr };
6599     sal_Int32 pGlyphWidths[nMaxGlyphs];
6600     sal_uInt8 pMappedGlyphs[nMaxGlyphs];
6601     sal_Int32 pMappedFontObjects[nMaxGlyphs];
6602     std::vector<sal_Ucs> aCodeUnits;
6603     aCodeUnits.reserve(nMaxGlyphs);
6604     std::vector<sal_Int32> aCodeUnitsPerGlyph;
6605     aCodeUnits.reserve(nMaxGlyphs);
6606     bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
6607     int nGlyphs;
6608     int nIndex = 0;
6609     int nMaxCharPos = rText.getLength()-1;
6610     double fXScale = 1.0;
6611     double fSkew = 0.0;
6612     sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontInstance->maFontSelData.mnHeight;
6613     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlignment();
6614 
6615     // transform font height back to current units
6616     // note: the layout calculates in outdevs device pixel !!
6617     sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
6618     if( m_aCurrentPDFState.m_aFont.GetAverageFontWidth() )
6619     {
6620         Font aFont( m_aCurrentPDFState.m_aFont );
6621         aFont.SetAverageFontWidth( 0 );
6622         FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
6623         if( aMetric.GetAverageFontWidth() != m_aCurrentPDFState.m_aFont.GetAverageFontWidth() )
6624         {
6625             fXScale =
6626                 (double)m_aCurrentPDFState.m_aFont.GetAverageFontWidth() /
6627                 (double)aMetric.GetAverageFontWidth();
6628         }
6629         // force state before GetFontMetric
6630         m_pReferenceDevice->ImplNewFont();
6631     }
6632 
6633     // perform artificial italics if necessary
6634     if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
6635           m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
6636         !( m_pReferenceDevice->mpFontInstance->maFontSelData.mpFontData->GetItalic() == ITALIC_NORMAL ||
6637            m_pReferenceDevice->mpFontInstance->maFontSelData.mpFontData->GetItalic() == ITALIC_OBLIQUE )
6638         )
6639     {
6640         fSkew = M_PI/12.0;
6641     }
6642 
6643     // if the mapmode is distorted we need to adjust for that also
6644     if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
6645     {
6646         fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
6647     }
6648 
6649     int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
6650     // normalize angles
6651     while( nAngle < 0 )
6652         nAngle += 3600;
6653     nAngle = nAngle % 3600;
6654     double fAngle = (double)nAngle * M_PI / 1800.0;
6655 
6656     Matrix3 aRotScale;
6657     aRotScale.scale( fXScale, 1.0 );
6658     if( fAngle != 0.0 )
6659         aRotScale.rotate( -fAngle );
6660 
6661     bool bPop = false;
6662     bool bABold = false;
6663     // artificial bold necessary ?
6664     if( m_pReferenceDevice->mpFontInstance->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
6665         m_pReferenceDevice->mpFontInstance->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
6666     {
6667         if( ! bPop )
6668             aLine.append( "q " );
6669         bPop = true;
6670         bABold = true;
6671     }
6672     // setup text colors (if necessary)
6673     Color aStrokeColor( COL_TRANSPARENT );
6674     Color aNonStrokeColor( COL_TRANSPARENT );
6675 
6676     if( m_aCurrentPDFState.m_aFont.IsOutline() )
6677     {
6678         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6679         aNonStrokeColor = Color( COL_WHITE );
6680     }
6681     else
6682         aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6683     if( bABold )
6684         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6685 
6686     if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
6687     {
6688         if( ! bPop )
6689             aLine.append( "q " );
6690         bPop = true;
6691         appendStrokingColor( aStrokeColor, aLine );
6692         aLine.append( "\n" );
6693     }
6694     if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
6695     {
6696         if( ! bPop )
6697             aLine.append( "q " );
6698         bPop = true;
6699         appendNonStrokingColor( aNonStrokeColor, aLine );
6700         aLine.append( "\n" );
6701     }
6702 
6703     // begin text object
6704     aLine.append( "BT\n" );
6705     // outline attribute ?
6706     if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
6707     {
6708         // set correct text mode, set stroke width
6709         aLine.append( "2 Tr " ); // fill, then stroke
6710 
6711         if( m_aCurrentPDFState.m_aFont.IsOutline() )
6712         {
6713             // unclear what to do in case of outline and artificial bold
6714             // for the time being outline wins
6715             aLine.append( "0.25 w \n" );
6716         }
6717         else
6718         {
6719             double fW = (double)m_aCurrentPDFState.m_aFont.GetFontHeight() / 30.0;
6720             m_aPages.back().appendMappedLength( fW, aLine );
6721             aLine.append ( " w\n" );
6722         }
6723     }
6724 
6725     FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
6726 
6727     // collect the glyphs into a single array
6728     const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
6729     std::vector< PDFGlyph > aGlyphs;
6730     aGlyphs.reserve( nTmpMaxGlyphs );
6731     // first get all the glyphs and register them; coordinates still in Pixel
6732     Point aGNGlyphPos;
6733     while ((nGlyphs = rLayout.GetNextGlyphs(nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, pFallbackFonts)) != 0)
6734     {
6735         aCodeUnits.clear();
6736         for( int i = 0; i < nGlyphs; i++ )
6737         {
6738             // default case: 1 glyph is one unicode
6739             aCodeUnitsPerGlyph.push_back(1);
6740             if (pGlyphs[i]->mnCharPos >= 0 && pGlyphs[i]->mnCharPos <= nMaxCharPos)
6741             {
6742                 int nChars = 1;
6743                 // try to handle ligatures and such
6744                 if( i < nGlyphs-1 )
6745                 {
6746                     nChars = pGlyphs[i+1]->mnCharPos - pGlyphs[i]->mnCharPos;
6747                     int start = pGlyphs[i]->mnCharPos;
6748                     // #i115618# fix for simple RTL+CTL cases
6749                     // supports RTL ligatures. TODO: more complex CTL, etc.
6750                     if( nChars < 0 )
6751                     {
6752                         nChars = -nChars;
6753                         start = pGlyphs[i+1]->mnCharPos + 1;
6754                     }
6755                     else if (nChars == 0)
6756                         nChars = 1;
6757                     aCodeUnitsPerGlyph.back() = nChars;
6758                     for( int n = 0; n < nChars; n++ )
6759                         aCodeUnits.push_back( rText[ start + n ] );
6760                 }
6761                 else
6762                     aCodeUnits.push_back(rText[pGlyphs[i]->mnCharPos]);
6763             }
6764             else
6765                 aCodeUnits.push_back( 0 );
6766             // note: in case of ctl one character may result
6767             // in multiple glyphs. The current SalLayout
6768             // implementations set -1 then to indicate that no direct
6769             // mapping is possible
6770         }
6771 
6772         registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, aCodeUnits.data(), aCodeUnitsPerGlyph.data(), pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
6773 
6774         for( int i = 0; i < nGlyphs; i++ )
6775         {
6776             aGlyphs.emplace_back( aGNGlyphPos,
6777                                          pGlyphWidths[i],
6778                                          pGlyphs[i]->maGlyphId,
6779                                          pMappedFontObjects[i],
6780                                          pMappedGlyphs[i],
6781                                          pGlyphs[i]->IsVertical() );
6782             if( bVertical )
6783                 aGNGlyphPos.Y() += pGlyphs[i]->mnNewWidth/rLayout.GetUnitsPerPixel();
6784             else
6785                 aGNGlyphPos.X() += pGlyphs[i]->mnNewWidth/rLayout.GetUnitsPerPixel();
6786         }
6787     }
6788 
6789     // Avoid fill color when map mode is in pixels, the below code assumes
6790     // logic map mode.
6791     bool bPixel = m_aCurrentPDFState.m_aMapMode.GetMapUnit() == MapUnit::MapPixel;
6792     if (m_aCurrentPDFState.m_aFont.GetFillColor() != Color(COL_TRANSPARENT) && !bPixel)
6793     {
6794         // PDF doesn't have a text fill color, so draw a rectangle before
6795         // drawing the actual text.
6796         push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
6797         setFillColor(m_aCurrentPDFState.m_aFont.GetFillColor());
6798         // Avoid border around the rectangle for Writer shape text.
6799         setLineColor(Color(COL_TRANSPARENT));
6800 
6801         // The rectangle is the bounding box of the text, but also includes
6802         // ascent / descent to match the on-screen rendering.
6803         tools::Rectangle aRectangle;
6804         // This is the top left of the text without ascent / descent.
6805         aRectangle.SetPos(m_pReferenceDevice->PixelToLogic(rLayout.GetDrawPosition()));
6806         aRectangle.setY(aRectangle.getY() - aRefDevFontMetric.GetAscent());
6807         aRectangle.SetSize(m_pReferenceDevice->PixelToLogic(Size(rLayout.GetTextWidth(), 0)));
6808         // This includes ascent / descent.
6809         aRectangle.setHeight(aRefDevFontMetric.GetLineHeight());
6810 
6811         LogicalFontInstance* pFontInstance = m_pReferenceDevice->mpFontInstance;
6812         if (pFontInstance->mnOrientation)
6813         {
6814             // Adapt rectangle for rotated text.
6815             tools::Polygon aPolygon(aRectangle);
6816             aPolygon.Rotate(m_pReferenceDevice->PixelToLogic(rLayout.GetDrawPosition()), pFontInstance->mnOrientation);
6817             drawPolygon(aPolygon);
6818         }
6819         else
6820             drawRectangle(aRectangle);
6821 
6822         pop();
6823     }
6824 
6825     Point aAlignOffset;
6826     if ( eAlign == ALIGN_BOTTOM )
6827         aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
6828     else if ( eAlign == ALIGN_TOP )
6829         aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
6830     if( aAlignOffset.X() || aAlignOffset.Y() )
6831         aAlignOffset = aRotScale.transform( aAlignOffset );
6832 
6833     /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
6834        string contained only on of the UTF16 BOMs
6835     */
6836     if( ! aGlyphs.empty() )
6837     {
6838         if( bVertical )
6839             drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
6840         else
6841             drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
6842     }
6843 
6844     // end textobject
6845     aLine.append( "ET\n" );
6846     if( bPop )
6847         aLine.append( "Q\n" );
6848 
6849     writeBuffer( aLine.getStr(), aLine.getLength() );
6850 
6851     // draw eventual textlines
6852     FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
6853     FontLineStyle eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
6854     FontLineStyle eOverline  = m_aCurrentPDFState.m_aFont.GetOverline();
6855     if( bTextLines &&
6856         (
6857          ( eUnderline != LINESTYLE_NONE && eUnderline != LINESTYLE_DONTKNOW ) ||
6858          ( eOverline  != LINESTYLE_NONE && eOverline  != LINESTYLE_DONTKNOW ) ||
6859          ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
6860          )
6861         )
6862     {
6863         bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
6864         if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
6865         {
6866             Point aPos, aStartPt;
6867             sal_Int32 nWidth = 0;
6868             const GlyphItem* pGlyph;
6869             int nStart = 0;
6870             while (rLayout.GetNextGlyphs(1, &pGlyph, aPos, nStart))
6871             {
6872                 if (!pGlyph->IsSpacing())
6873                 {
6874                     if( !nWidth )
6875                         aStartPt = aPos;
6876 
6877                     nWidth += pGlyph->mnNewWidth;
6878                 }
6879                 else if( nWidth > 0 )
6880                 {
6881                     drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
6882                                   m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
6883                                   eStrikeout, eUnderline, eOverline, bUnderlineAbove );
6884                     nWidth = 0;
6885                 }
6886             }
6887 
6888             if( nWidth > 0 )
6889             {
6890                 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
6891                               m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
6892                               eStrikeout, eUnderline, eOverline, bUnderlineAbove );
6893             }
6894         }
6895         else
6896         {
6897             Point aStartPt = rLayout.GetDrawPosition();
6898             int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
6899             drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
6900                           m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
6901                           eStrikeout, eUnderline, eOverline, bUnderlineAbove );
6902         }
6903     }
6904 
6905     // write eventual emphasis marks
6906     if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & FontEmphasisMark::Style )
6907     {
6908         tools::PolyPolygon             aEmphPoly;
6909         tools::Rectangle               aEmphRect1;
6910         tools::Rectangle               aEmphRect2;
6911         long                    nEmphYOff;
6912         long                    nEmphWidth;
6913         long                    nEmphHeight;
6914         bool                    bEmphPolyLine;
6915         FontEmphasisMark        nEmphMark;
6916 
6917         push( PushFlags::ALL );
6918 
6919         aLine.setLength( 0 );
6920         aLine.append( "q\n" );
6921 
6922         nEmphMark = OutputDevice::ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
6923         if ( nEmphMark & FontEmphasisMark::PosBelow )
6924             nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
6925         else
6926             nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
6927         m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
6928                                                  bEmphPolyLine,
6929                                                  aEmphRect1,
6930                                                  aEmphRect2,
6931                                                  nEmphYOff,
6932                                                  nEmphWidth,
6933                                                  nEmphMark,
6934                                                  m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight) );
6935         if ( bEmphPolyLine )
6936         {
6937             setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
6938             setFillColor( Color( COL_TRANSPARENT ) );
6939         }
6940         else
6941         {
6942             setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
6943             setLineColor( Color( COL_TRANSPARENT ) );
6944         }
6945         writeBuffer( aLine.getStr(), aLine.getLength() );
6946 
6947         Point aOffset = Point(0,0);
6948 
6949         if ( nEmphMark & FontEmphasisMark::PosBelow )
6950             aOffset.Y() += m_pReferenceDevice->mpFontInstance->mxFontMetric->GetDescent() + nEmphYOff;
6951         else
6952             aOffset.Y() -= m_pReferenceDevice->mpFontInstance->mxFontMetric->GetAscent() + nEmphYOff;
6953 
6954         long nEmphWidth2     = nEmphWidth / 2;
6955         long nEmphHeight2    = nEmphHeight / 2;
6956         aOffset += Point( nEmphWidth2, nEmphHeight2 );
6957 
6958         if ( eAlign == ALIGN_BOTTOM )
6959             aOffset.Y() -= m_pReferenceDevice->mpFontInstance->mxFontMetric->GetDescent();
6960         else if ( eAlign == ALIGN_TOP )
6961             aOffset.Y() += m_pReferenceDevice->mpFontInstance->mxFontMetric->GetAscent();
6962 
6963         Point aPos;
6964         const GlyphItem* pGlyph;
6965         int nStart = 0;
6966         while (rLayout.GetNextGlyphs(1, &pGlyph, aPos, nStart))
6967         {
6968             if (pGlyph->IsSpacing())
6969             {
6970                 Point aAdjOffset = aOffset;
6971                 aAdjOffset.X() += (pGlyph->mnNewWidth - nEmphWidth) / 2;
6972                 aAdjOffset = aRotScale.transform( aAdjOffset );
6973 
6974                 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
6975 
6976                 aPos += aAdjOffset;
6977                 aPos = m_pReferenceDevice->PixelToLogic( aPos );
6978                 drawEmphasisMark( aPos.X(), aPos.Y(),
6979                                   aEmphPoly, bEmphPolyLine,
6980                                   aEmphRect1, aEmphRect2 );
6981             }
6982         }
6983 
6984         writeBuffer( "Q\n", 2 );
6985         pop();
6986     }
6987 }
6988 
6989 void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
6990                                       const tools::PolyPolygon& rPolyPoly, bool bPolyLine,
6991                                       const tools::Rectangle& rRect1, const tools::Rectangle& rRect2 )
6992 {
6993     // TODO: pass nWidth as width of this mark
6994     // long nWidth = 0;
6995 
6996     if ( rPolyPoly.Count() )
6997     {
6998         if ( bPolyLine )
6999         {
7000             tools::Polygon aPoly = rPolyPoly.GetObject( 0 );
7001             aPoly.Move( nX, nY );
7002             drawPolyLine( aPoly );
7003         }
7004         else
7005         {
7006             tools::PolyPolygon aPolyPoly = rPolyPoly;
7007             aPolyPoly.Move( nX, nY );
7008             drawPolyPolygon( aPolyPoly );
7009         }
7010     }
7011 
7012     if ( !rRect1.IsEmpty() )
7013     {
7014         tools::Rectangle aRect( Point( nX+rRect1.Left(),
7015                                 nY+rRect1.Top() ), rRect1.GetSize() );
7016         drawRectangle( aRect );
7017     }
7018 
7019     if ( !rRect2.IsEmpty() )
7020     {
7021         tools::Rectangle aRect( Point( nX+rRect2.Left(),
7022                                 nY+rRect2.Top() ), rRect2.GetSize() );
7023 
7024         drawRectangle( aRect );
7025     }
7026 }
7027 
7028 void PDFWriterImpl::drawText( const Point& rPos, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines )
7029 {
7030     MARK( "drawText" );
7031 
7032     updateGraphicsState();
7033 
7034     // get a layout from the OutputDevice's SalGraphics
7035     // this also enforces font substitution and sets the font on SalGraphics
7036     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
7037     if( pLayout )
7038     {
7039         drawLayout( *pLayout, rText, bTextLines );
7040         delete pLayout;
7041     }
7042 }
7043 
7044 void PDFWriterImpl::drawTextArray( const Point& rPos, const OUString& rText, const long* pDXArray, sal_Int32 nIndex, sal_Int32 nLen )
7045 {
7046     MARK( "drawText with array" );
7047 
7048     updateGraphicsState();
7049 
7050     // get a layout from the OutputDevice's SalGraphics
7051     // this also enforces font substitution and sets the font on SalGraphics
7052     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
7053     if( pLayout )
7054     {
7055         drawLayout( *pLayout, rText, true );
7056         delete pLayout;
7057     }
7058 }
7059 
7060 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen )
7061 {
7062     MARK( "drawStretchText" );
7063 
7064     updateGraphicsState();
7065 
7066     // get a layout from the OutputDevice's SalGraphics
7067     // this also enforces font substitution and sets the font on SalGraphics
7068     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
7069     if( pLayout )
7070     {
7071         drawLayout( *pLayout, rText, true );
7072         delete pLayout;
7073     }
7074 }
7075 
7076 void PDFWriterImpl::drawText( const tools::Rectangle& rRect, const OUString& rOrigStr, DrawTextFlags nStyle )
7077 {
7078     long        nWidth          = rRect.GetWidth();
7079     long        nHeight         = rRect.GetHeight();
7080 
7081     if ( nWidth <= 0 || nHeight <= 0 )
7082         return;
7083 
7084     MARK( "drawText with rectangle" );
7085 
7086     updateGraphicsState();
7087 
7088     // clip with rectangle
7089     OStringBuffer aLine;
7090     aLine.append( "q " );
7091     m_aPages.back().appendRect( rRect, aLine );
7092     aLine.append( " W* n\n" );
7093     writeBuffer( aLine.getStr(), aLine.getLength() );
7094 
7095     // if disabled text is needed, put in here
7096 
7097     Point       aPos            = rRect.TopLeft();
7098 
7099     long        nTextHeight     = m_pReferenceDevice->GetTextHeight();
7100     sal_Int32   nMnemonicPos    = -1;
7101 
7102     OUString aStr = rOrigStr;
7103     if ( nStyle & DrawTextFlags::Mnemonic )
7104         aStr = OutputDevice::GetNonMnemonicString( aStr, nMnemonicPos );
7105 
7106     // multiline text
7107     if ( nStyle & DrawTextFlags::MultiLine )
7108     {
7109         OUString           aLastLine;
7110         ImplMultiTextLineInfo   aMultiLineInfo;
7111         ImplTextLineInfo*       pLineInfo;
7112         sal_Int32               i;
7113         sal_Int32               nLines;
7114         sal_Int32               nFormatLines;
7115 
7116         if ( nTextHeight )
7117         {
7118             vcl::DefaultTextLayout aLayout( *m_pReferenceDevice );
7119             OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout );
7120             nLines = nHeight/nTextHeight;
7121             nFormatLines = aMultiLineInfo.Count();
7122             if ( !nLines )
7123                 nLines = 1;
7124             if ( nFormatLines > nLines )
7125             {
7126                 if ( nStyle & DrawTextFlags::EndEllipsis )
7127                 {
7128                     // handle last line
7129                     nFormatLines = nLines-1;
7130 
7131                     pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
7132                     aLastLine = convertLineEnd(aStr.copy(pLineInfo->GetIndex()), LINEEND_LF);
7133                     // replace line feed by space
7134                     aLastLine = aLastLine.replace('\n', ' ');
7135                     aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
7136                     nStyle &= ~DrawTextFlags(DrawTextFlags::VCenter | DrawTextFlags::Bottom);
7137                     nStyle |= DrawTextFlags::Top;
7138                 }
7139             }
7140 
7141             // vertical alignment
7142             if ( nStyle & DrawTextFlags::Bottom )
7143                 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
7144             else if ( nStyle & DrawTextFlags::VCenter )
7145                 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
7146 
7147             // draw all lines excluding the last
7148             for ( i = 0; i < nFormatLines; i++ )
7149             {
7150                 pLineInfo = aMultiLineInfo.GetLine( i );
7151                 if ( nStyle & DrawTextFlags::Right )
7152                     aPos.X() += nWidth-pLineInfo->GetWidth();
7153                 else if ( nStyle & DrawTextFlags::Center )
7154                     aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
7155                 sal_Int32 nIndex = pLineInfo->GetIndex();
7156                 sal_Int32 nLineLen = pLineInfo->GetLen();
7157                 drawText( aPos, aStr, nIndex, nLineLen );
7158                 // mnemonics should not appear in documents,
7159                 // if the need arises, put them in here
7160                 aPos.Y() += nTextHeight;
7161                 aPos.X() = rRect.Left();
7162             }
7163 
7164             // output last line left adjusted since it was shortened
7165             if (!aLastLine.isEmpty())
7166                 drawText( aPos, aLastLine, 0, aLastLine.getLength() );
7167         }
7168     }
7169     else
7170     {
7171         long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7172 
7173         // Evt. Text kuerzen
7174         if ( nTextWidth > nWidth )
7175         {
7176             if ( nStyle & (DrawTextFlags::EndEllipsis | DrawTextFlags::PathEllipsis | DrawTextFlags::NewsEllipsis) )
7177             {
7178                 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
7179                 nStyle &= ~DrawTextFlags(DrawTextFlags::Center | DrawTextFlags::Right);
7180                 nStyle |= DrawTextFlags::Left;
7181                 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7182             }
7183         }
7184 
7185         // vertical alignment
7186         if ( nStyle & DrawTextFlags::Right )
7187             aPos.X() += nWidth-nTextWidth;
7188         else if ( nStyle & DrawTextFlags::Center )
7189             aPos.X() += (nWidth-nTextWidth)/2;
7190 
7191         if ( nStyle & DrawTextFlags::Bottom )
7192             aPos.Y() += nHeight-nTextHeight;
7193         else if ( nStyle & DrawTextFlags::VCenter )
7194             aPos.Y() += (nHeight-nTextHeight)/2;
7195 
7196         // mnemonics should be inserted here if the need arises
7197 
7198         // draw the actual text
7199         drawText( aPos, aStr, 0, aStr.getLength() );
7200     }
7201 
7202     // reset clip region to original value
7203     aLine.setLength( 0 );
7204     aLine.append( "Q\n" );
7205     writeBuffer( aLine.getStr(), aLine.getLength() );
7206 }
7207 
7208 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
7209 {
7210     MARK( "drawLine" );
7211 
7212     updateGraphicsState();
7213 
7214     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7215         return;
7216 
7217     OStringBuffer aLine;
7218     m_aPages.back().appendPoint( rStart, aLine );
7219     aLine.append( " m " );
7220     m_aPages.back().appendPoint( rStop, aLine );
7221     aLine.append( " l S\n" );
7222 
7223     writeBuffer( aLine.getStr(), aLine.getLength() );
7224 }
7225 
7226 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
7227 {
7228     MARK( "drawLine with LineInfo" );
7229     updateGraphicsState();
7230 
7231     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7232         return;
7233 
7234     if( rInfo.GetStyle() == LineStyle::Solid && rInfo.GetWidth() < 2 )
7235     {
7236         drawLine( rStart, rStop );
7237         return;
7238     }
7239 
7240     OStringBuffer aLine;
7241 
7242     aLine.append( "q " );
7243     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
7244     {
7245         m_aPages.back().appendPoint( rStart, aLine );
7246         aLine.append( " m " );
7247         m_aPages.back().appendPoint( rStop, aLine );
7248         aLine.append( " l S Q\n" );
7249 
7250         writeBuffer( aLine.getStr(), aLine.getLength() );
7251     }
7252     else
7253     {
7254         PDFWriter::ExtLineInfo aInfo;
7255         convertLineInfoToExtLineInfo( rInfo, aInfo );
7256         Point aPolyPoints[2] = { rStart, rStop };
7257         tools::Polygon aPoly( 2, aPolyPoints );
7258         drawPolyLine( aPoly, aInfo );
7259     }
7260 }
7261 
7262 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
7263 
7264 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontLineStyle eTextLine, Color aColor, bool bIsAbove )
7265 {
7266     // note: units in pFontInstance are ref device pixel
7267     LogicalFontInstance*  pFontInstance = m_pReferenceDevice->mpFontInstance;
7268     long            nLineHeight = 0;
7269     long            nLinePos = 0;
7270 
7271     appendStrokingColor( aColor, aLine );
7272     aLine.append( "\n" );
7273 
7274     if ( bIsAbove )
7275     {
7276         if ( !pFontInstance->mxFontMetric->GetAboveWavelineUnderlineSize() )
7277             m_pReferenceDevice->ImplInitAboveTextLineSize();
7278         nLineHeight = HCONV( pFontInstance->mxFontMetric->GetAboveWavelineUnderlineSize() );
7279         nLinePos = HCONV( pFontInstance->mxFontMetric->GetAboveWavelineUnderlineOffset() );
7280     }
7281     else
7282     {
7283         if ( !pFontInstance->mxFontMetric->GetWavelineUnderlineSize() )
7284             m_pReferenceDevice->ImplInitTextLineSize();
7285         nLineHeight = HCONV( pFontInstance->mxFontMetric->GetWavelineUnderlineSize() );
7286         nLinePos = HCONV( pFontInstance->mxFontMetric->GetWavelineUnderlineOffset() );
7287     }
7288     if ( (eTextLine == LINESTYLE_SMALLWAVE) && (nLineHeight > 3) )
7289         nLineHeight = 3;
7290 
7291     long nLineWidth = getReferenceDevice()->mnDPIX/450;
7292     if ( ! nLineWidth )
7293         nLineWidth = 1;
7294 
7295     if ( eTextLine == LINESTYLE_BOLDWAVE )
7296         nLineWidth = 3*nLineWidth;
7297 
7298     m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
7299     aLine.append( " w " );
7300 
7301     if ( eTextLine == LINESTYLE_DOUBLEWAVE )
7302     {
7303         long nOrgLineHeight = nLineHeight;
7304         nLineHeight /= 3;
7305         if ( nLineHeight < 2 )
7306         {
7307             if ( nOrgLineHeight > 1 )
7308                 nLineHeight = 2;
7309             else
7310                 nLineHeight = 1;
7311         }
7312         long nLineDY = nOrgLineHeight-(nLineHeight*2);
7313         if ( nLineDY < nLineWidth )
7314             nLineDY = nLineWidth;
7315         long nLineDY2 = nLineDY/2;
7316         if ( !nLineDY2 )
7317             nLineDY2 = 1;
7318 
7319         nLinePos -= nLineWidth-nLineDY2;
7320 
7321         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
7322 
7323         nLinePos += nLineWidth+nLineDY;
7324         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
7325     }
7326     else
7327     {
7328         if ( eTextLine != LINESTYLE_BOLDWAVE )
7329             nLinePos -= nLineWidth/2;
7330         m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
7331     }
7332 }
7333 
7334 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontLineStyle eTextLine, Color aColor, bool bIsAbove )
7335 {
7336     // note: units in pFontInstance are ref device pixel
7337     LogicalFontInstance*  pFontInstance = m_pReferenceDevice->mpFontInstance;
7338     long            nLineHeight = 0;
7339     long            nLinePos  = 0;
7340     long            nLinePos2 = 0;
7341 
7342     if ( eTextLine > LINESTYLE_BOLDWAVE )
7343         eTextLine = LINESTYLE_SINGLE;
7344 
7345     switch ( eTextLine )
7346     {
7347         case LINESTYLE_SINGLE:
7348         case LINESTYLE_DOTTED:
7349         case LINESTYLE_DASH:
7350         case LINESTYLE_LONGDASH:
7351         case LINESTYLE_DASHDOT:
7352         case LINESTYLE_DASHDOTDOT:
7353             if ( bIsAbove )
7354             {
7355                 if ( !pFontInstance->mxFontMetric->GetAboveUnderlineSize() )
7356                     m_pReferenceDevice->ImplInitAboveTextLineSize();
7357                 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetAboveUnderlineSize() );
7358                 nLinePos    = HCONV( pFontInstance->mxFontMetric->GetAboveUnderlineOffset() );
7359             }
7360             else
7361             {
7362                 if ( !pFontInstance->mxFontMetric->GetUnderlineSize() )
7363                     m_pReferenceDevice->ImplInitTextLineSize();
7364                 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetUnderlineSize() );
7365                 nLinePos    = HCONV( pFontInstance->mxFontMetric->GetUnderlineOffset() );
7366             }
7367             break;
7368         case LINESTYLE_BOLD:
7369         case LINESTYLE_BOLDDOTTED:
7370         case LINESTYLE_BOLDDASH:
7371         case LINESTYLE_BOLDLONGDASH:
7372         case LINESTYLE_BOLDDASHDOT:
7373         case LINESTYLE_BOLDDASHDOTDOT:
7374             if ( bIsAbove )
7375             {
7376                 if ( !pFontInstance->mxFontMetric->GetAboveBoldUnderlineSize() )
7377                     m_pReferenceDevice->ImplInitAboveTextLineSize();
7378                 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetAboveBoldUnderlineSize() );
7379                 nLinePos    = HCONV( pFontInstance->mxFontMetric->GetAboveBoldUnderlineOffset() );
7380             }
7381             else
7382             {
7383                 if ( !pFontInstance->mxFontMetric->GetBoldUnderlineSize() )
7384                     m_pReferenceDevice->ImplInitTextLineSize();
7385                 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetBoldUnderlineSize() );
7386                 nLinePos    = HCONV( pFontInstance->mxFontMetric->GetBoldUnderlineOffset() );
7387                 nLinePos += nLineHeight/2;
7388             }
7389             break;
7390         case LINESTYLE_DOUBLE:
7391             if ( bIsAbove )
7392             {
7393                 if ( !pFontInstance->mxFontMetric->GetAboveDoubleUnderlineSize() )
7394                     m_pReferenceDevice->ImplInitAboveTextLineSize();
7395                 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetAboveDoubleUnderlineSize() );
7396                 nLinePos    = HCONV( pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset1() );
7397                 nLinePos2   = HCONV( pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset2() );
7398             }
7399             else
7400             {
7401                 if ( !pFontInstance->mxFontMetric->GetDoubleUnderlineSize() )
7402                     m_pReferenceDevice->ImplInitTextLineSize();
7403                 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetDoubleUnderlineSize() );
7404                 nLinePos    = HCONV( pFontInstance->mxFontMetric->GetDoubleUnderlineOffset1() );
7405                 nLinePos2   = HCONV( pFontInstance->mxFontMetric->GetDoubleUnderlineOffset2() );
7406             }
7407             break;
7408         default:
7409             break;
7410     }
7411 
7412     if ( nLineHeight )
7413     {
7414         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine );
7415         aLine.append( " w " );
7416         appendStrokingColor( aColor, aLine );
7417         aLine.append( "\n" );
7418 
7419         switch ( eTextLine )
7420         {
7421             case LINESTYLE_DOTTED:
7422             case LINESTYLE_BOLDDOTTED:
7423                 aLine.append( "[ " );
7424                 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7425                 aLine.append( " ] 0 d\n" );
7426                 break;
7427             case LINESTYLE_DASH:
7428             case LINESTYLE_LONGDASH:
7429             case LINESTYLE_BOLDDASH:
7430             case LINESTYLE_BOLDLONGDASH:
7431                 {
7432                     sal_Int32 nDashLength = 4*nLineHeight;
7433                     sal_Int32 nVoidLength = 2*nLineHeight;
7434                     if ( ( eTextLine == LINESTYLE_LONGDASH ) || ( eTextLine == LINESTYLE_BOLDLONGDASH ) )
7435                         nDashLength = 8*nLineHeight;
7436 
7437                     aLine.append( "[ " );
7438                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7439                     aLine.append( ' ' );
7440                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7441                     aLine.append( " ] 0 d\n" );
7442                 }
7443                 break;
7444             case LINESTYLE_DASHDOT:
7445             case LINESTYLE_BOLDDASHDOT:
7446                 {
7447                     sal_Int32 nDashLength = 4*nLineHeight;
7448                     sal_Int32 nVoidLength = 2*nLineHeight;
7449                     aLine.append( "[ " );
7450                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7451                     aLine.append( ' ' );
7452                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7453                     aLine.append( ' ' );
7454                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7455                     aLine.append( ' ' );
7456                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7457                     aLine.append( " ] 0 d\n" );
7458                 }
7459                 break;
7460             case LINESTYLE_DASHDOTDOT:
7461             case LINESTYLE_BOLDDASHDOTDOT:
7462                 {
7463                     sal_Int32 nDashLength = 4*nLineHeight;
7464                     sal_Int32 nVoidLength = 2*nLineHeight;
7465                     aLine.append( "[ " );
7466                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7467                     aLine.append( ' ' );
7468                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7469                     aLine.append( ' ' );
7470                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7471                     aLine.append( ' ' );
7472                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7473                     aLine.append( ' ' );
7474                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7475                     aLine.append( ' ' );
7476                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7477                     aLine.append( " ] 0 d\n" );
7478                 }
7479                 break;
7480             default:
7481                 break;
7482         }
7483 
7484         aLine.append( "0 " );
7485         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine );
7486         aLine.append( " m " );
7487         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
7488         aLine.append( ' ' );
7489         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine );
7490         aLine.append( " l S\n" );
7491         if ( eTextLine == LINESTYLE_DOUBLE )
7492         {
7493             aLine.append( "0 " );
7494             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine );
7495             aLine.append( " m " );
7496             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
7497             aLine.append( ' ' );
7498             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine );
7499             aLine.append( " l S\n" );
7500         }
7501     }
7502 }
7503 
7504 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
7505 {
7506     // note: units in pFontInstance are ref device pixel
7507     LogicalFontInstance*  pFontInstance = m_pReferenceDevice->mpFontInstance;
7508     long            nLineHeight = 0;
7509     long            nLinePos  = 0;
7510     long            nLinePos2 = 0;
7511 
7512     if ( eStrikeout > STRIKEOUT_X )
7513         eStrikeout = STRIKEOUT_SINGLE;
7514 
7515     switch ( eStrikeout )
7516     {
7517         case STRIKEOUT_SINGLE:
7518             if ( !pFontInstance->mxFontMetric->GetStrikeoutSize() )
7519                 m_pReferenceDevice->ImplInitTextLineSize();
7520             nLineHeight = HCONV( pFontInstance->mxFontMetric->GetStrikeoutSize() );
7521             nLinePos    = HCONV( pFontInstance->mxFontMetric->GetStrikeoutOffset() );
7522             break;
7523         case STRIKEOUT_BOLD:
7524             if ( !pFontInstance->mxFontMetric->GetBoldStrikeoutSize() )
7525                 m_pReferenceDevice->ImplInitTextLineSize();
7526             nLineHeight = HCONV( pFontInstance->mxFontMetric->GetBoldStrikeoutSize() );
7527             nLinePos    = HCONV( pFontInstance->mxFontMetric->GetBoldStrikeoutOffset() );
7528             break;
7529         case STRIKEOUT_DOUBLE:
7530             if ( !pFontInstance->mxFontMetric->GetDoubleStrikeoutSize() )
7531                 m_pReferenceDevice->ImplInitTextLineSize();
7532             nLineHeight = HCONV( pFontInstance->mxFontMetric->GetDoubleStrikeoutSize() );
7533             nLinePos    = HCONV( pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset1() );
7534             nLinePos2   = HCONV( pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset2() );
7535             break;
7536         default:
7537             break;
7538     }
7539 
7540     if ( nLineHeight )
7541     {
7542         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine );
7543         aLine.append( " w " );
7544         appendStrokingColor( aColor, aLine );
7545         aLine.append( "\n" );
7546 
7547         aLine.append( "0 " );
7548         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine );
7549         aLine.append( " m " );
7550         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine );
7551         aLine.append( ' ' );
7552         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine );
7553         aLine.append( " l S\n" );
7554 
7555         if ( eStrikeout == STRIKEOUT_DOUBLE )
7556         {
7557             aLine.append( "0 " );
7558             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine );
7559             aLine.append( " m " );
7560             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine );
7561             aLine.append( ' ' );
7562             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine );
7563             aLine.append( " l S\n" );
7564         }
7565     }
7566 }
7567 
7568 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
7569 {
7570     //See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
7571     //to tweak this
7572 
7573     OUString aStrikeoutChar = eStrikeout == STRIKEOUT_SLASH ? OUString( "/" ) : OUString( "X" );
7574     OUString aStrikeout = aStrikeoutChar;
7575     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
7576         aStrikeout += aStrikeout;
7577 
7578     // do not get broader than nWidth modulo 1 character
7579     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
7580         aStrikeout = aStrikeout.replaceAt( 0, 1, "" );
7581     aStrikeout += aStrikeoutChar;
7582     bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
7583     if ( bShadow )
7584     {
7585         Font aFont = m_aCurrentPDFState.m_aFont;
7586         aFont.SetShadow( false );
7587         setFont( aFont );
7588         updateGraphicsState();
7589     }
7590 
7591     // strikeout string is left aligned non-CTL text
7592     ComplexTextLayoutFlags nOrigTLM = m_pReferenceDevice->GetLayoutMode();
7593     m_pReferenceDevice->SetLayoutMode(ComplexTextLayoutFlags::BiDiStrong);
7594 
7595     push( PushFlags::CLIPREGION );
7596     FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
7597     tools::Rectangle aRect;
7598     aRect.Left() = rPos.X();
7599     aRect.Right() = aRect.Left()+nWidth;
7600     aRect.Bottom() = rPos.Y()+aRefDevFontMetric.GetDescent();
7601     aRect.Top() = rPos.Y()-aRefDevFontMetric.GetAscent();
7602 
7603     LogicalFontInstance* pFontInstance = m_pReferenceDevice->mpFontInstance;
7604     if (pFontInstance->mnOrientation)
7605     {
7606         tools::Polygon aPoly( aRect );
7607         aPoly.Rotate( rPos, pFontInstance->mnOrientation);
7608         aRect = aPoly.GetBoundRect();
7609     }
7610 
7611     intersectClipRegion( aRect );
7612     drawText( rPos, aStrikeout, 0, aStrikeout.getLength(), false );
7613     pop();
7614 
7615     m_pReferenceDevice->SetLayoutMode( nOrigTLM );
7616 
7617     if ( bShadow )
7618     {
7619         Font aFont = m_aCurrentPDFState.m_aFont;
7620         aFont.SetShadow( true );
7621         setFont( aFont );
7622         updateGraphicsState();
7623     }
7624 }
7625 
7626 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontLineStyle eUnderline, FontLineStyle eOverline, bool bUnderlineAbove )
7627 {
7628     if ( !nWidth ||
7629          ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
7630            ((eUnderline == LINESTYLE_NONE)||(eUnderline == LINESTYLE_DONTKNOW)) &&
7631            ((eOverline  == LINESTYLE_NONE)||(eOverline  == LINESTYLE_DONTKNOW)) ) )
7632         return;
7633 
7634     MARK( "drawTextLine" );
7635     updateGraphicsState();
7636 
7637     // note: units in pFontInstance are ref device pixel
7638     LogicalFontInstance* pFontInstance = m_pReferenceDevice->mpFontInstance;
7639     Color           aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
7640     Color           aOverlineColor  = m_aCurrentPDFState.m_aOverlineColor;
7641     Color           aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
7642     bool            bStrikeoutDone = false;
7643     bool            bUnderlineDone = false;
7644     bool            bOverlineDone  = false;
7645 
7646     if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
7647     {
7648         drawStrikeoutChar( rPos, nWidth, eStrikeout );
7649         bStrikeoutDone = true;
7650     }
7651 
7652     Point aPos( rPos );
7653     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlignment();
7654     if( eAlign == ALIGN_TOP )
7655         aPos.Y() += HCONV( pFontInstance->mxFontMetric->GetAscent() );
7656     else if( eAlign == ALIGN_BOTTOM )
7657         aPos.Y() -= HCONV( pFontInstance->mxFontMetric->GetDescent() );
7658 
7659     OStringBuffer aLine( 512 );
7660     // save GS
7661     aLine.append( "q " );
7662 
7663     // rotate and translate matrix
7664     double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
7665     Matrix3 aMat;
7666     aMat.rotate( fAngle );
7667     aMat.translate( aPos.X(), aPos.Y() );
7668     aMat.append( m_aPages.back(), aLine );
7669     aLine.append( " cm\n" );
7670 
7671     if ( aUnderlineColor.GetTransparency() != 0 )
7672         aUnderlineColor = aStrikeoutColor;
7673 
7674     if ( (eUnderline == LINESTYLE_SMALLWAVE) ||
7675          (eUnderline == LINESTYLE_WAVE) ||
7676          (eUnderline == LINESTYLE_DOUBLEWAVE) ||
7677          (eUnderline == LINESTYLE_BOLDWAVE) )
7678     {
7679         drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
7680         bUnderlineDone = true;
7681     }
7682 
7683     if ( (eOverline == LINESTYLE_SMALLWAVE) ||
7684          (eOverline == LINESTYLE_WAVE) ||
7685          (eOverline == LINESTYLE_DOUBLEWAVE) ||
7686          (eOverline == LINESTYLE_BOLDWAVE) )
7687     {
7688         drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
7689         bOverlineDone = true;
7690     }
7691 
7692     if ( !bUnderlineDone )
7693     {
7694         drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
7695     }
7696 
7697     if ( !bOverlineDone )
7698     {
7699         drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
7700     }
7701 
7702     if ( !bStrikeoutDone )
7703     {
7704         drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
7705     }
7706 
7707     aLine.append( "Q\n" );
7708     writeBuffer( aLine.getStr(), aLine.getLength() );
7709 }
7710 
7711 void PDFWriterImpl::drawPolygon( const tools::Polygon& rPoly )
7712 {
7713     MARK( "drawPolygon" );
7714 
7715     updateGraphicsState();
7716 
7717     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
7718         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
7719         return;
7720 
7721     int nPoints = rPoly.GetSize();
7722     OStringBuffer aLine( 20 * nPoints );
7723     m_aPages.back().appendPolygon( rPoly, aLine );
7724     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
7725         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
7726         aLine.append( "B*\n" );
7727     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
7728         aLine.append( "S\n" );
7729     else
7730         aLine.append( "f*\n" );
7731 
7732     writeBuffer( aLine.getStr(), aLine.getLength() );
7733 }
7734 
7735 void PDFWriterImpl::drawPolyPolygon( const tools::PolyPolygon& rPolyPoly )
7736 {
7737     MARK( "drawPolyPolygon" );
7738 
7739     updateGraphicsState();
7740 
7741     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
7742         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
7743         return;
7744 
7745     int nPolygons = rPolyPoly.Count();
7746 
7747     OStringBuffer aLine( 40 * nPolygons );
7748     m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
7749     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
7750         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
7751         aLine.append( "B*\n" );
7752     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
7753         aLine.append( "S\n" );
7754     else
7755         aLine.append( "f*\n" );
7756 
7757     writeBuffer( aLine.getStr(), aLine.getLength() );
7758 }
7759 
7760 void PDFWriterImpl::drawTransparent( const tools::PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
7761 {
7762     SAL_WARN_IF( nTransparentPercent > 100, "vcl.pdfwriter", "invalid alpha value" );
7763     nTransparentPercent = nTransparentPercent % 100;
7764 
7765     MARK( "drawTransparent" );
7766 
7767     updateGraphicsState();
7768 
7769     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
7770         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
7771         return;
7772 
7773     if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDFVersion::PDF_1_4 )
7774     {
7775         m_aErrors.insert( m_bIsPDF_A1 ?
7776                           PDFWriter::Warning_Transparency_Omitted_PDFA :
7777                           PDFWriter::Warning_Transparency_Omitted_PDF13 );
7778 
7779         drawPolyPolygon( rPolyPoly );
7780         return;
7781     }
7782 
7783     // create XObject
7784     m_aTransparentObjects.emplace_back( );
7785     // FIXME: polygons with beziers may yield incorrect bound rect
7786     m_aTransparentObjects.back().m_aBoundRect     = rPolyPoly.GetBoundRect();
7787     // convert rectangle to default user space
7788     m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
7789     m_aTransparentObjects.back().m_nObject          = createObject();
7790     m_aTransparentObjects.back().m_nExtGStateObject = createObject();
7791     m_aTransparentObjects.back().m_fAlpha           = (double)(100-nTransparentPercent) / 100.0;
7792     m_aTransparentObjects.back().m_pContentStream   = new SvMemoryStream( 256, 256 );
7793     // create XObject's content stream
7794     OStringBuffer aContent( 256 );
7795     m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
7796     if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
7797         m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
7798         aContent.append( " B*\n" );
7799     else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
7800         aContent.append( " S\n" );
7801     else
7802         aContent.append( " f*\n" );
7803     m_aTransparentObjects.back().m_pContentStream->WriteBytes(
7804         aContent.getStr(), aContent.getLength() );
7805 
7806     OStringBuffer aObjName( 16 );
7807     aObjName.append( "Tr" );
7808     aObjName.append( m_aTransparentObjects.back().m_nObject );
7809     OString aTrName( aObjName.makeStringAndClear() );
7810     aObjName.append( "EGS" );
7811     aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
7812     OString aExtName( aObjName.makeStringAndClear() );
7813 
7814     OStringBuffer aLine( 80 );
7815     // insert XObject
7816     aLine.append( "q /" );
7817     aLine.append( aExtName );
7818     aLine.append( " gs /" );
7819     aLine.append( aTrName );
7820     aLine.append( " Do Q\n" );
7821     writeBuffer( aLine.getStr(), aLine.getLength() );
7822 
7823     pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
7824     pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
7825 }
7826 
7827 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
7828 {
7829     if( nObject >= 0 )
7830     {
7831         switch( eKind )
7832         {
7833             case ResXObject:
7834                 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
7835                 if( ! m_aOutputStreams.empty() )
7836                     m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
7837                 break;
7838             case ResExtGState:
7839                 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
7840                 if( ! m_aOutputStreams.empty() )
7841                     m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
7842                 break;
7843             case ResShading:
7844                 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
7845                 if( ! m_aOutputStreams.empty() )
7846                     m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
7847                 break;
7848             case ResPattern:
7849                 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
7850                 if( ! m_aOutputStreams.empty() )
7851                     m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
7852                 break;
7853         }
7854     }
7855 }
7856 
7857 void PDFWriterImpl::beginRedirect( SvStream* pStream, const tools::Rectangle& rTargetRect )
7858 {
7859     push( PushFlags::ALL );
7860 
7861     // force reemitting clip region inside the new stream, and
7862     // prevent emitting an unbalanced "Q" at the start
7863     clearClipRegion();
7864     // this is needed to point m_aCurrentPDFState at the pushed state
7865     // ... but it's pointless to actually write into the "outer" stream here!
7866     updateGraphicsState(NOWRITE);
7867 
7868     m_aOutputStreams.push_front( StreamRedirect() );
7869     m_aOutputStreams.front().m_pStream = pStream;
7870     m_aOutputStreams.front().m_aMapMode = m_aMapMode;
7871 
7872     if( !rTargetRect.IsEmpty() )
7873     {
7874         m_aOutputStreams.front().m_aTargetRect =
7875             lcl_convert( m_aGraphicsStack.front().m_aMapMode,
7876                          m_aMapMode,
7877                          getReferenceDevice(),
7878                          rTargetRect );
7879         Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
7880         long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
7881         aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
7882         m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
7883     }
7884 
7885     // setup graphics state for independent object stream
7886 
7887     // force reemitting colors
7888     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
7889     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
7890 }
7891 
7892 SvStream* PDFWriterImpl::endRedirect()
7893 {
7894     SvStream* pStream = nullptr;
7895     if( ! m_aOutputStreams.empty() )
7896     {
7897         pStream     = m_aOutputStreams.front().m_pStream;
7898         m_aMapMode  = m_aOutputStreams.front().m_aMapMode;
7899         m_aOutputStreams.pop_front();
7900     }
7901 
7902     pop();
7903 
7904     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
7905     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
7906 
7907     // needed after pop() to set m_aCurrentPDFState
7908     updateGraphicsState(NOWRITE);
7909 
7910     return pStream;
7911 }
7912 
7913 void PDFWriterImpl::beginTransparencyGroup()
7914 {
7915     updateGraphicsState();
7916     if( m_aContext.Version >= PDFWriter::PDFVersion::PDF_1_4 )
7917         beginRedirect( new SvMemoryStream( 1024, 1024 ), tools::Rectangle() );
7918 }
7919 
7920 void PDFWriterImpl::endTransparencyGroup( const tools::Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
7921 {
7922     SAL_WARN_IF( nTransparentPercent > 100, "vcl.pdfwriter", "invalid alpha value" );
7923     nTransparentPercent = nTransparentPercent % 100;
7924 
7925     if( m_aContext.Version >= PDFWriter::PDFVersion::PDF_1_4 )
7926     {
7927         // create XObject
7928         m_aTransparentObjects.emplace_back( );
7929         m_aTransparentObjects.back().m_aBoundRect   = rBoundingBox;
7930         // convert rectangle to default user space
7931         m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
7932         m_aTransparentObjects.back().m_nObject      = createObject();
7933         m_aTransparentObjects.back().m_fAlpha       = (double)(100-nTransparentPercent) / 100.0;
7934         // get XObject's content stream
7935         m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
7936         m_aTransparentObjects.back().m_nExtGStateObject = createObject();
7937 
7938         OStringBuffer aObjName( 16 );
7939         aObjName.append( "Tr" );
7940         aObjName.append( m_aTransparentObjects.back().m_nObject );
7941         OString aTrName( aObjName.makeStringAndClear() );
7942         aObjName.append( "EGS" );
7943         aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
7944         OString aExtName( aObjName.makeStringAndClear() );
7945 
7946         OStringBuffer aLine( 80 );
7947         // insert XObject
7948         aLine.append( "q /" );
7949         aLine.append( aExtName );
7950         aLine.append( " gs /" );
7951         aLine.append( aTrName );
7952         aLine.append( " Do Q\n" );
7953         writeBuffer( aLine.getStr(), aLine.getLength() );
7954 
7955         pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
7956         pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
7957     }
7958 }
7959 
7960 void PDFWriterImpl::drawRectangle( const tools::Rectangle& rRect )
7961 {
7962     MARK( "drawRectangle" );
7963 
7964     updateGraphicsState();
7965 
7966     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
7967         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
7968         return;
7969 
7970     OStringBuffer aLine( 40 );
7971     m_aPages.back().appendRect( rRect, aLine );
7972 
7973     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
7974         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
7975         aLine.append( " B*\n" );
7976     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
7977         aLine.append( " S\n" );
7978     else
7979         aLine.append( " f*\n" );
7980 
7981     writeBuffer( aLine.getStr(), aLine.getLength() );
7982 }
7983 
7984 void PDFWriterImpl::drawRectangle( const tools::Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
7985 {
7986     MARK( "drawRectangle with rounded edges" );
7987 
7988     if( !nHorzRound && !nVertRound )
7989         drawRectangle( rRect );
7990 
7991     updateGraphicsState();
7992 
7993     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
7994         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
7995         return;
7996 
7997     if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
7998         nHorzRound = rRect.GetWidth()/2;
7999     if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
8000         nVertRound = rRect.GetHeight()/2;
8001 
8002     Point aPoints[16];
8003     const double kappa = 0.5522847498;
8004     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
8005     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
8006 
8007     aPoints[1]  = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
8008     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8009     aPoints[2]  = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
8010     aPoints[3]  = Point( aPoints[2].X()+kx, aPoints[2].Y() );
8011 
8012     aPoints[5]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
8013     aPoints[4]  = Point( aPoints[5].X(), aPoints[5].Y()-ky );
8014     aPoints[6]  = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
8015     aPoints[7]  = Point( aPoints[6].X(), aPoints[6].Y()+ky );
8016 
8017     aPoints[9]  = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
8018     aPoints[8]  = Point( aPoints[9].X()+kx, aPoints[9].Y() );
8019     aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
8020     aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
8021 
8022     aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
8023     aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
8024     aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
8025     aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
8026 
8027     OStringBuffer aLine( 80 );
8028     m_aPages.back().appendPoint( aPoints[1], aLine );
8029     aLine.append( " m " );
8030     m_aPages.back().appendPoint( aPoints[2], aLine );
8031     aLine.append( " l " );
8032     m_aPages.back().appendPoint( aPoints[3], aLine );
8033     aLine.append( ' ' );
8034     m_aPages.back().appendPoint( aPoints[4], aLine );
8035     aLine.append( ' ' );
8036     m_aPages.back().appendPoint( aPoints[5], aLine );
8037     aLine.append( " c\n" );
8038     m_aPages.back().appendPoint( aPoints[6], aLine );
8039     aLine.append( " l " );
8040     m_aPages.back().appendPoint( aPoints[7], aLine );
8041     aLine.append( ' ' );
8042     m_aPages.back().appendPoint( aPoints[8], aLine );
8043     aLine.append( ' ' );
8044     m_aPages.back().appendPoint( aPoints[9], aLine );
8045     aLine.append( " c\n" );
8046     m_aPages.back().appendPoint( aPoints[10], aLine );
8047     aLine.append( " l " );
8048     m_aPages.back().appendPoint( aPoints[11], aLine );
8049     aLine.append( ' ' );
8050     m_aPages.back().appendPoint( aPoints[12], aLine );
8051     aLine.append( ' ' );
8052     m_aPages.back().appendPoint( aPoints[13], aLine );
8053     aLine.append( " c\n" );
8054     m_aPages.back().appendPoint( aPoints[14], aLine );
8055     aLine.append( " l " );
8056     m_aPages.back().appendPoint( aPoints[15], aLine );
8057     aLine.append( ' ' );
8058     m_aPages.back().appendPoint( aPoints[0], aLine );
8059     aLine.append( ' ' );
8060     m_aPages.back().appendPoint( aPoints[1], aLine );
8061     aLine.append( " c " );
8062 
8063     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8064         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8065         aLine.append( "b*\n" );
8066     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8067         aLine.append( "s\n" );
8068     else
8069         aLine.append( "f*\n" );
8070 
8071     writeBuffer( aLine.getStr(), aLine.getLength() );
8072 }
8073 
8074 void PDFWriterImpl::drawEllipse( const tools::Rectangle& rRect )
8075 {
8076     MARK( "drawEllipse" );
8077 
8078     updateGraphicsState();
8079 
8080     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8081         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8082         return;
8083 
8084     Point aPoints[12];
8085     const double kappa = 0.5522847498;
8086     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
8087     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
8088 
8089     aPoints[1]  = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
8090     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8091     aPoints[2]  = Point( aPoints[1].X() + kx, aPoints[1].Y() );
8092 
8093     aPoints[4]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
8094     aPoints[3]  = Point( aPoints[4].X(), aPoints[4].Y() - ky );
8095     aPoints[5]  = Point( aPoints[4].X(), aPoints[4].Y() + ky );
8096 
8097     aPoints[7]  = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
8098     aPoints[6]  = Point( aPoints[7].X() + kx, aPoints[7].Y() );
8099     aPoints[8]  = Point( aPoints[7].X() - kx, aPoints[7].Y() );
8100 
8101     aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
8102     aPoints[9]  = Point( aPoints[10].X(), aPoints[10].Y() + ky );
8103     aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
8104 
8105     OStringBuffer aLine( 80 );
8106     m_aPages.back().appendPoint( aPoints[1], aLine );
8107     aLine.append( " m " );
8108     m_aPages.back().appendPoint( aPoints[2], aLine );
8109     aLine.append( ' ' );
8110     m_aPages.back().appendPoint( aPoints[3], aLine );
8111     aLine.append( ' ' );
8112     m_aPages.back().appendPoint( aPoints[4], aLine );
8113     aLine.append( " c\n" );
8114     m_aPages.back().appendPoint( aPoints[5], aLine );
8115     aLine.append( ' ' );
8116     m_aPages.back().appendPoint( aPoints[6], aLine );
8117     aLine.append( ' ' );
8118     m_aPages.back().appendPoint( aPoints[7], aLine );
8119     aLine.append( " c\n" );
8120     m_aPages.back().appendPoint( aPoints[8], aLine );
8121     aLine.append( ' ' );
8122     m_aPages.back().appendPoint( aPoints[9], aLine );
8123     aLine.append( ' ' );
8124     m_aPages.back().appendPoint( aPoints[10], aLine );
8125     aLine.append( " c\n" );
8126     m_aPages.back().appendPoint( aPoints[11], aLine );
8127     aLine.append( ' ' );
8128     m_aPages.back().appendPoint( aPoints[0], aLine );
8129     aLine.append( ' ' );
8130     m_aPages.back().appendPoint( aPoints[1], aLine );
8131     aLine.append( " c " );
8132 
8133     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8134         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8135         aLine.append( "b*\n" );
8136     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8137         aLine.append( "s\n" );
8138     else
8139         aLine.append( "f*\n" );
8140 
8141     writeBuffer( aLine.getStr(), aLine.getLength() );
8142 }
8143 
8144 static double calcAngle( const tools::Rectangle& rRect, const Point& rPoint )
8145 {
8146     Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
8147                   (rRect.Top()+rRect.Bottom()+1)/2);
8148     Point aPoint = rPoint - aOrigin;
8149 
8150     double fX = (double)aPoint.X();
8151     double fY = (double)-aPoint.Y();
8152 
8153     if ((rRect.GetHeight() == 0) || (rRect.GetWidth() == 0))
8154         throw o3tl::divide_by_zero();
8155 
8156     if( rRect.GetWidth() > rRect.GetHeight() )
8157         fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
8158     else if( rRect.GetHeight() > rRect.GetWidth() )
8159         fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
8160     return atan2( fY, fX );
8161 }
8162 
8163 void PDFWriterImpl::drawArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
8164 {
8165     MARK( "drawArc" );
8166 
8167     updateGraphicsState();
8168 
8169     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8170         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8171         return;
8172 
8173     // calculate start and stop angles
8174     const double fStartAngle = calcAngle( rRect, rStart );
8175     double fStopAngle  = calcAngle( rRect, rStop );
8176     while( fStopAngle < fStartAngle )
8177         fStopAngle += 2.0*M_PI;
8178     const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
8179     const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
8180     const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
8181     const double halfWidth = (double)rRect.GetWidth()/2.0;
8182     const double halfHeight = (double)rRect.GetHeight()/2.0;
8183 
8184     const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
8185                          (rRect.Top()+rRect.Bottom()+1)/2 );
8186 
8187     OStringBuffer aLine( 30*nFragments );
8188     Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
8189                   -(int)(halfHeight * sin(fStartAngle) ) );
8190     aPoint += aCenter;
8191     m_aPages.back().appendPoint( aPoint, aLine );
8192     aLine.append( " m " );
8193     if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
8194     {
8195         for( int i = 0; i < nFragments; i++ )
8196         {
8197             const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
8198             const double fStopFragment = fStartFragment + fFragmentDelta;
8199             aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
8200                             -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
8201             aPoint += aCenter;
8202             m_aPages.back().appendPoint( aPoint, aLine );
8203             aLine.append( ' ' );
8204 
8205             aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
8206                             -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
8207             aPoint += aCenter;
8208             m_aPages.back().appendPoint( aPoint, aLine );
8209             aLine.append( ' ' );
8210 
8211             aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
8212                             -(int)(halfHeight * sin(fStopFragment) ) );
8213             aPoint += aCenter;
8214             m_aPages.back().appendPoint( aPoint, aLine );
8215             aLine.append( " c\n" );
8216         }
8217     }
8218     if( bWithChord || bWithPie )
8219     {
8220         if( bWithPie )
8221         {
8222             m_aPages.back().appendPoint( aCenter, aLine );
8223             aLine.append( " l " );
8224         }
8225         aLine.append( "h " );
8226     }
8227     if( ! bWithChord && ! bWithPie )
8228         aLine.append( "S\n" );
8229     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8230         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8231         aLine.append( "B*\n" );
8232     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8233         aLine.append( "S\n" );
8234     else
8235         aLine.append( "f*\n" );
8236 
8237     writeBuffer( aLine.getStr(), aLine.getLength() );
8238 }
8239 
8240 void PDFWriterImpl::drawPolyLine( const tools::Polygon& rPoly )
8241 {
8242     MARK( "drawPolyLine" );
8243 
8244     sal_uInt16 nPoints = rPoly.GetSize();
8245     if( nPoints < 2 )
8246         return;
8247 
8248     updateGraphicsState();
8249 
8250     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8251         return;
8252 
8253     OStringBuffer aLine( 20 * nPoints );
8254     m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
8255     aLine.append( "S\n" );
8256 
8257     writeBuffer( aLine.getStr(), aLine.getLength() );
8258 }
8259 
8260 void PDFWriterImpl::drawPolyLine( const tools::Polygon& rPoly, const LineInfo& rInfo )
8261 {
8262     MARK( "drawPolyLine with LineInfo" );
8263 
8264     updateGraphicsState();
8265 
8266     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8267         return;
8268 
8269     OStringBuffer aLine;
8270     aLine.append( "q " );
8271     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
8272     {
8273         writeBuffer( aLine.getStr(), aLine.getLength() );
8274         drawPolyLine( rPoly );
8275         writeBuffer( "Q\n", 2 );
8276     }
8277     else
8278     {
8279         PDFWriter::ExtLineInfo aInfo;
8280         convertLineInfoToExtLineInfo( rInfo, aInfo );
8281         drawPolyLine( rPoly, aInfo );
8282     }
8283 }
8284 
8285 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
8286 {
8287     SAL_WARN_IF( rIn.GetStyle() != LineStyle::Dash, "vcl.pdfwriter", "invalid conversion" );
8288     rOut.m_fLineWidth           = rIn.GetWidth();
8289     rOut.m_fTransparency        = 0.0;
8290     rOut.m_eCap                 = PDFWriter::capButt;
8291     rOut.m_eJoin                = PDFWriter::joinMiter;
8292     rOut.m_fMiterLimit          = 10;
8293     rOut.m_aDashArray.clear();
8294 
8295     // add DashDot to DashArray
8296     const int nDashes   = rIn.GetDashCount();
8297     const int nDashLen  = rIn.GetDashLen();
8298     const int nDistance = rIn.GetDistance();
8299 
8300     for( int n  = 0; n < nDashes; n++ )
8301     {
8302         rOut.m_aDashArray.push_back( nDashLen );
8303         rOut.m_aDashArray.push_back( nDistance );
8304     }
8305     const int nDots   = rIn.GetDotCount();
8306     const int nDotLen = rIn.GetDotLen();
8307 
8308     for( int n  = 0; n < nDots; n++ )
8309     {
8310         rOut.m_aDashArray.push_back( nDotLen );
8311         rOut.m_aDashArray.push_back( nDistance );
8312     }
8313 
8314     // add LineJoin
8315     switch(rIn.GetLineJoin())
8316     {
8317         case basegfx::B2DLineJoin::Bevel :
8318         {
8319             rOut.m_eJoin = PDFWriter::joinBevel;
8320             break;
8321         }
8322         // Pdf has no 'none' lineJoin, default is miter
8323         case basegfx::B2DLineJoin::NONE :
8324         case basegfx::B2DLineJoin::Miter :
8325         {
8326             rOut.m_eJoin = PDFWriter::joinMiter;
8327             break;
8328         }
8329         case basegfx::B2DLineJoin::Round :
8330         {
8331             rOut.m_eJoin = PDFWriter::joinRound;
8332             break;
8333         }
8334     }
8335 
8336     // add LineCap
8337     switch(rIn.GetLineCap())
8338     {
8339         default: /* css::drawing::LineCap_BUTT */
8340         {
8341             rOut.m_eCap = PDFWriter::capButt;
8342             break;
8343         }
8344         case css::drawing::LineCap_ROUND:
8345         {
8346             rOut.m_eCap = PDFWriter::capRound;
8347             break;
8348         }
8349         case css::drawing::LineCap_SQUARE:
8350         {
8351             rOut.m_eCap = PDFWriter::capSquare;
8352             break;
8353         }
8354     }
8355 }
8356 
8357 void PDFWriterImpl::drawPolyLine( const tools::Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
8358 {
8359     MARK( "drawPolyLine with ExtLineInfo" );
8360 
8361     updateGraphicsState();
8362 
8363     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8364         return;
8365 
8366     if( rInfo.m_fTransparency >= 1.0 )
8367         return;
8368 
8369     if( rInfo.m_fTransparency != 0.0 )
8370         beginTransparencyGroup();
8371 
8372     OStringBuffer aLine;
8373     aLine.append( "q " );
8374     m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
8375     aLine.append( " w" );
8376     if( rInfo.m_aDashArray.size() < 10 ) // implementation limit of acrobat reader
8377     {
8378         switch( rInfo.m_eCap )
8379         {
8380             default:
8381             case PDFWriter::capButt:   aLine.append( " 0 J" );break;
8382             case PDFWriter::capRound:  aLine.append( " 1 J" );break;
8383             case PDFWriter::capSquare: aLine.append( " 2 J" );break;
8384         }
8385         switch( rInfo.m_eJoin )
8386         {
8387             default:
8388             case PDFWriter::joinMiter:
8389             {
8390                 double fLimit = rInfo.m_fMiterLimit;
8391                 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
8392                     fLimit = fLimit / rInfo.m_fLineWidth;
8393                 if( fLimit < 1.0 )
8394                     fLimit = 1.0;
8395                 aLine.append( " 0 j " );
8396                 appendDouble( fLimit, aLine );
8397                 aLine.append( " M" );
8398             }
8399             break;
8400             case PDFWriter::joinRound:  aLine.append( " 1 j" );break;
8401             case PDFWriter::joinBevel:  aLine.append( " 2 j" );break;
8402         }
8403         if( rInfo.m_aDashArray.size() > 0 )
8404         {
8405             aLine.append( " [ " );
8406             for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
8407                  it != rInfo.m_aDashArray.end(); ++it )
8408             {
8409                 m_aPages.back().appendMappedLength( *it, aLine );
8410                 aLine.append( ' ' );
8411             }
8412             aLine.append( "] 0 d" );
8413         }
8414         aLine.append( "\n" );
8415         writeBuffer( aLine.getStr(), aLine.getLength() );
8416         drawPolyLine( rPoly );
8417     }
8418     else
8419     {
8420         basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
8421         basegfx::B2DPolyPolygon aPolyPoly;
8422 
8423         basegfx::utils::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
8424 
8425         // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
8426         // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
8427         // this line needs to be removed and the loop below adapted accordingly
8428         aPolyPoly = basegfx::utils::adaptiveSubdivideByAngle(aPolyPoly);
8429 
8430         const sal_uInt32 nPolygonCount(aPolyPoly.count());
8431 
8432         for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
8433         {
8434             aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
8435             aPoly = aPolyPoly.getB2DPolygon( nPoly );
8436             const sal_uInt32 nPointCount(aPoly.count());
8437 
8438             if(nPointCount)
8439             {
8440                 const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
8441                 basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
8442 
8443                 for(sal_uInt32 a(0); a < nEdgeCount; a++)
8444                 {
8445                     if( a > 0 )
8446                         aLine.append( " " );
8447                     const sal_uInt32 nNextIndex((a + 1) % nPointCount);
8448                     const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
8449 
8450                     m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
8451                                                         FRound(aCurrent.getY()) ),
8452                                                  aLine );
8453                     aLine.append( " m " );
8454                     m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
8455                                                         FRound(aNext.getY()) ),
8456                                                  aLine );
8457                     aLine.append( " l" );
8458 
8459                     // prepare next edge
8460                     aCurrent = aNext;
8461                 }
8462             }
8463         }
8464         aLine.append( " S " );
8465         writeBuffer( aLine.getStr(), aLine.getLength() );
8466     }
8467     writeBuffer( "Q\n", 2 );
8468 
8469     if( rInfo.m_fTransparency != 0.0 )
8470     {
8471         // FIXME: actually this may be incorrect with bezier polygons
8472         tools::Rectangle aBoundRect( rPoly.GetBoundRect() );
8473         // avoid clipping with thick lines
8474         if( rInfo.m_fLineWidth > 0.0 )
8475         {
8476             sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
8477             aBoundRect.Top()    -= nLW;
8478             aBoundRect.Left()   -= nLW;
8479             aBoundRect.Right()  += nLW;
8480             aBoundRect.Bottom() += nLW;
8481         }
8482         endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) );
8483     }
8484 }
8485 
8486 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
8487 {
8488     MARK( "drawPixel" );
8489 
8490     Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
8491 
8492     if( aColor == Color( COL_TRANSPARENT ) )
8493         return;
8494 
8495     // pixels are drawn in line color, so have to set
8496     // the nonstroking color to line color
8497     Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
8498     setFillColor( aColor );
8499 
8500     updateGraphicsState();
8501 
8502     OStringBuffer aLine( 20 );
8503     m_aPages.back().appendPoint( rPoint, aLine );
8504     aLine.append( ' ' );
8505     appendDouble( 1.0/double(getReferenceDevice()->GetDPIX()), aLine );
8506     aLine.append( ' ' );
8507     appendDouble( 1.0/double(getReferenceDevice()->GetDPIY()), aLine );
8508     aLine.append( " re f\n" );
8509     writeBuffer( aLine.getStr(), aLine.getLength() );
8510 
8511     setFillColor( aOldFillColor );
8512 }
8513 
8514 void PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
8515 {
8516     CHECK_RETURN2( updateObject( rObject.m_nObject ) );
8517 
8518     bool bFlateFilter = compressStream( rObject.m_pContentStream );
8519     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
8520     sal_uLong nSize = rObject.m_pContentStream->Tell();
8521     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
8522     if (g_bDebugDisableCompression)
8523     {
8524         emitComment( "PDFWriterImpl::writeTransparentObject" );
8525     }
8526     OStringBuffer aLine( 512 );
8527     CHECK_RETURN2( updateObject( rObject.m_nObject ) );
8528     aLine.append( rObject.m_nObject );
8529     aLine.append( " 0 obj\n"
8530                   "<</Type/XObject\n"
8531                   "/Subtype/Form\n"
8532                   "/BBox[ " );
8533     appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
8534     aLine.append( ' ' );
8535     appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
8536     aLine.append( ' ' );
8537     appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
8538     aLine.append( ' ' );
8539     appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
8540     aLine.append( " ]\n" );
8541     if( ! rObject.m_pSoftMaskStream )
8542     {
8543         if( ! m_bIsPDF_A1 )
8544         {
8545             aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
8546         }
8547     }
8548     /* #i42884# the PDF reference recommends that each Form XObject
8549     *  should have a resource dict; alas if that is the same object
8550     *  as the one of the page it triggers an endless recursion in
8551     *  acroread 5 (6 and up have that fixed). Since we have only one
8552     *  resource dict anyway, let's use the one from the page by NOT
8553     *  emitting a Resources entry.
8554     */
8555 
8556     aLine.append( "/Length " );
8557     aLine.append( (sal_Int32)nSize );
8558     aLine.append( "\n" );
8559     if( bFlateFilter )
8560         aLine.append( "/Filter/FlateDecode\n" );
8561     aLine.append( ">>\n"
8562                   "stream\n" );
8563     CHECK_RETURN2( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8564     checkAndEnableStreamEncryption( rObject.m_nObject );
8565     CHECK_RETURN2( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
8566     disableStreamEncryption();
8567     aLine.setLength( 0 );
8568     aLine.append( "\n"
8569                   "endstream\n"
8570                   "endobj\n\n" );
8571     CHECK_RETURN2( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8572 
8573     // write ExtGState dict for this XObject
8574     aLine.setLength( 0 );
8575     aLine.append( rObject.m_nExtGStateObject );
8576     aLine.append( " 0 obj\n"
8577                   "<<" );
8578     if( ! rObject.m_pSoftMaskStream )
8579     {
8580         if( m_bIsPDF_A1 )
8581         {
8582             aLine.append( "/CA 1.0/ca 1.0" );
8583             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
8584         }
8585         else
8586         {
8587             aLine.append(  "/CA " );
8588             appendDouble( rObject.m_fAlpha, aLine );
8589             aLine.append( "\n"
8590                           "   /ca " );
8591             appendDouble( rObject.m_fAlpha, aLine );
8592         }
8593         aLine.append( "\n" );
8594     }
8595     else
8596     {
8597         if( m_bIsPDF_A1 )
8598         {
8599             aLine.append( "/SMask/None" );
8600             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
8601         }
8602         else
8603         {
8604             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
8605             sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
8606             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
8607             sal_Int32 nMaskObject = createObject();
8608             aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
8609             aLine.append( nMaskObject );
8610             aLine.append( " 0 R>>\n" );
8611 
8612             OStringBuffer aMask;
8613             aMask.append( nMaskObject );
8614             aMask.append( " 0 obj\n"
8615                           "<</Type/XObject\n"
8616                           "/Subtype/Form\n"
8617                           "/BBox[" );
8618             appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
8619             aMask.append( ' ' );
8620             appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
8621             aMask.append( ' ' );
8622             appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
8623             aMask.append( ' ' );
8624             appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
8625             aMask.append( "]\n" );
8626 
8627             /* #i42884# see above */
8628             aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
8629             aMask.append( "/Length " );
8630             aMask.append( nMaskSize );
8631             aMask.append( ">>\n"
8632                           "stream\n" );
8633             CHECK_RETURN2( updateObject( nMaskObject ) );
8634             checkAndEnableStreamEncryption(  nMaskObject );
8635             CHECK_RETURN2( writeBuffer( aMask.getStr(), aMask.getLength() ) );
8636             CHECK_RETURN2( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
8637             disableStreamEncryption();
8638             aMask.setLength( 0 );
8639             aMask.append( "\nendstream\n"
8640                           "endobj\n\n" );
8641             CHECK_RETURN2( writeBuffer( aMask.getStr(), aMask.getLength() ) );
8642         }
8643     }
8644     aLine.append( ">>\n"
8645                   "endobj\n\n" );
8646     CHECK_RETURN2( updateObject( rObject.m_nExtGStateObject ) );
8647     CHECK_RETURN2( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8648 }
8649 
8650 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
8651 {
8652     // LO internal gradient -> PDF shading type:
8653     //  * GradientStyle::Linear: axial shading, using sampled-function with 2 samples
8654     //                          [t=0:colorStart, t=1:colorEnd]
8655     //  * GradientStyle::Axial: axial shading, using sampled-function with 3 samples
8656     //                          [t=0:colorEnd, t=0.5:colorStart, t=1:colorEnd]
8657     //  * other styles: function shading with aSize.Width() * aSize.Height() samples
8658     sal_Int32 nFunctionObject = createObject();
8659     CHECK_RETURN( updateObject( nFunctionObject ) );
8660 
8661     ScopedVclPtrInstance< VirtualDevice > aDev;
8662     aDev->SetOutputSizePixel( rObject.m_aSize );
8663     aDev->SetMapMode( MapMode( MapUnit::MapPixel ) );
8664     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
8665         aDev->SetDrawMode( aDev->GetDrawMode() |
8666                           ( DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText |
8667                             DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient ) );
8668     aDev->DrawGradient( tools::Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
8669 
8670     Bitmap aSample = aDev->GetBitmap( Point( 0, 0 ), rObject.m_aSize );
8671     Bitmap::ScopedReadAccess pAccess(aSample);
8672 
8673     Size aSize = aSample.GetSizePixel();
8674 
8675     sal_Int32 nStreamLengthObject = createObject();
8676     if (g_bDebugDisableCompression)
8677     {
8678         emitComment( "PDFWriterImpl::writeGradientFunction" );
8679     }
8680     OStringBuffer aLine( 120 );
8681     aLine.append( nFunctionObject );
8682     aLine.append( " 0 obj\n"
8683                   "<</FunctionType 0\n");
8684     switch (rObject.m_aGradient.GetStyle())
8685     {
8686         case GradientStyle::Linear:
8687         case GradientStyle::Axial:
8688             aLine.append("/Domain[ 0 1]\n");
8689             break;
8690         default:
8691             aLine.append("/Domain[ 0 1 0 1]\n");
8692     }
8693     aLine.append("/Size[ " );
8694     switch (rObject.m_aGradient.GetStyle())
8695     {
8696         case GradientStyle::Linear:
8697             aLine.append('2');
8698             break;
8699         case GradientStyle::Axial:
8700             aLine.append('3');
8701             break;
8702         default:
8703             aLine.append( (sal_Int32)aSize.Width() );
8704             aLine.append( ' ' );
8705             aLine.append( (sal_Int32)aSize.Height() );
8706     }
8707     aLine.append( " ]\n"
8708                   "/BitsPerSample 8\n"
8709                   "/Range[ 0 1 0 1 0 1 ]\n"
8710                   "/Order 3\n"
8711                   "/Length " );
8712     aLine.append( nStreamLengthObject );
8713     if (!g_bDebugDisableCompression)
8714         aLine.append( " 0 R\n"
8715                       "/Filter/FlateDecode"
8716                       ">>\n"
8717                       "stream\n" );
8718     else
8719         aLine.append( " 0 R\n"
8720                       ">>\n"
8721                       "stream\n" );
8722     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8723 
8724     sal_uInt64 nStartStreamPos = 0;
8725     CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nStartStreamPos)) );
8726 
8727     checkAndEnableStreamEncryption( nFunctionObject );
8728     beginCompression();
8729     sal_uInt8 aCol[3];
8730     switch (rObject.m_aGradient.GetStyle())
8731     {
8732         case GradientStyle::Axial:
8733             aCol[0] = rObject.m_aGradient.GetEndColor().GetRed();
8734             aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen();
8735             aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue();
8736             CHECK_RETURN( writeBuffer( aCol, 3 ) );
8737             SAL_FALLTHROUGH;
8738         case GradientStyle::Linear:
8739         {
8740             aCol[0] = rObject.m_aGradient.GetStartColor().GetRed();
8741             aCol[1] = rObject.m_aGradient.GetStartColor().GetGreen();
8742             aCol[2] = rObject.m_aGradient.GetStartColor().GetBlue();
8743             CHECK_RETURN( writeBuffer( aCol, 3 ) );
8744 
8745             aCol[0] = rObject.m_aGradient.GetEndColor().GetRed();
8746             aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen();
8747             aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue();
8748             CHECK_RETURN( writeBuffer( aCol, 3 ) );
8749             break;
8750         }
8751         default:
8752             for( int y = aSize.Height()-1; y >= 0; y-- )
8753             {
8754                 for( long x = 0; x < aSize.Width(); x++ )
8755                 {
8756                     BitmapColor aColor = pAccess->GetColor( y, x );
8757                     aCol[0] = aColor.GetRed();
8758                     aCol[1] = aColor.GetGreen();
8759                     aCol[2] = aColor.GetBlue();
8760                     CHECK_RETURN( writeBuffer( aCol, 3 ) );
8761                 }
8762             }
8763     }
8764     endCompression();
8765     disableStreamEncryption();
8766 
8767     sal_uInt64 nEndStreamPos = 0;
8768     CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nEndStreamPos)) );
8769 
8770     aLine.setLength( 0 );
8771     aLine.append( "\nendstream\nendobj\n\n" );
8772     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8773 
8774     // write stream length
8775     CHECK_RETURN( updateObject( nStreamLengthObject ) );
8776     aLine.setLength( 0 );
8777     aLine.append( nStreamLengthObject );
8778     aLine.append( " 0 obj\n" );
8779     aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
8780     aLine.append( "\nendobj\n\n" );
8781     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8782 
8783     CHECK_RETURN( updateObject( rObject.m_nObject ) );
8784     aLine.setLength( 0 );
8785     aLine.append( rObject.m_nObject );
8786     aLine.append( " 0 obj\n");
8787     switch (rObject.m_aGradient.GetStyle())
8788     {
8789         case GradientStyle::Linear:
8790         case GradientStyle::Axial:
8791             aLine.append("<</ShadingType 2\n");
8792             break;
8793         default:
8794             aLine.append("<</ShadingType 1\n");
8795     }
8796     aLine.append("/ColorSpace/DeviceRGB\n"
8797                   "/AntiAlias true\n");
8798 
8799     // Determination of shading axis
8800     // See: OutputDevice::ImplDrawLinearGradient for reference
8801     tools::Rectangle aRect;
8802     aRect.Left() = aRect.Top() = 0;
8803     aRect.Right() = aSize.Width();
8804     aRect.Bottom() = aSize.Height();
8805 
8806     tools::Rectangle aBoundRect;
8807     Point     aCenter;
8808     sal_uInt16    nAngle = rObject.m_aGradient.GetAngle() % 3600;
8809     rObject.m_aGradient.GetBoundRect( aRect, aBoundRect, aCenter );
8810 
8811     const bool bLinear = (rObject.m_aGradient.GetStyle() == GradientStyle::Linear);
8812     double fBorder = aBoundRect.GetHeight() * rObject.m_aGradient.GetBorder() / 100.0;
8813     if ( !bLinear )
8814     {
8815         fBorder /= 2.0;
8816     }
8817 
8818     aBoundRect.Bottom() -= fBorder;
8819     if (!bLinear)
8820     {
8821         aBoundRect.Top() += fBorder;
8822     }
8823 
8824     switch (rObject.m_aGradient.GetStyle())
8825     {
8826         case GradientStyle::Linear:
8827         case GradientStyle::Axial:
8828         {
8829             aLine.append("/Domain[ 0 1 ]\n"
8830                     "/Coords[ " );
8831             tools::Polygon aPoly( 2 );
8832             aPoly[0] = aBoundRect.BottomCenter();
8833             aPoly[1] = aBoundRect.TopCenter();
8834             aPoly.Rotate( aCenter, 3600 - nAngle );
8835 
8836             aLine.append( (sal_Int32) aPoly[0].X() );
8837             aLine.append( " " );
8838             aLine.append( (sal_Int32) aPoly[0].Y() );
8839             aLine.append( " " );
8840             aLine.append( (sal_Int32) aPoly[1].X());
8841             aLine.append( " ");
8842             aLine.append( (sal_Int32) aPoly[1].Y());
8843             aLine.append( " ]\n");
8844             aLine.append("/Extend [true true]\n");
8845             break;
8846         }
8847         default:
8848             aLine.append("/Domain[ 0 1 0 1 ]\n"
8849                     "/Matrix[ " );
8850             aLine.append( (sal_Int32)aSize.Width() );
8851             aLine.append( " 0 0 " );
8852             aLine.append( (sal_Int32)aSize.Height() );
8853             aLine.append( " 0 0 ]\n");
8854     }
8855     aLine.append("/Function " );
8856     aLine.append( nFunctionObject );
8857     aLine.append( " 0 R\n"
8858                   ">>\n"
8859                   "endobj\n\n" );
8860     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8861 
8862     return true;
8863 }
8864 
8865 void PDFWriterImpl::writeJPG( JPGEmit& rObject )
8866 {
8867     if (rObject.m_aReferenceXObject.m_aPDFData.hasElements() && !m_aContext.UseReferenceXObject)
8868     {
8869         writeReferenceXObject(rObject.m_aReferenceXObject);
8870         return;
8871     }
8872 
8873     CHECK_RETURN2( rObject.m_pStream );
8874     CHECK_RETURN2( updateObject( rObject.m_nObject ) );
8875 
8876     sal_Int32 nLength = 0;
8877     rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
8878     nLength = rObject.m_pStream->Tell();
8879     rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
8880 
8881     sal_Int32 nMaskObject = 0;
8882     if( !!rObject.m_aMask )
8883     {
8884         if( rObject.m_aMask.GetBitCount() == 1 ||
8885             ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDFVersion::PDF_1_4 && !m_bIsPDF_A1 )
8886             )
8887         {
8888             nMaskObject = createObject();
8889         }
8890         else if( m_bIsPDF_A1 )
8891             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
8892         else if( m_aContext.Version < PDFWriter::PDFVersion::PDF_1_4 )
8893             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
8894 
8895     }
8896     if (g_bDebugDisableCompression)
8897     {
8898         emitComment( "PDFWriterImpl::writeJPG" );
8899     }
8900 
8901     OStringBuffer aLine(200);
8902     aLine.append( rObject.m_nObject );
8903     aLine.append( " 0 obj\n"
8904                   "<</Type/XObject/Subtype/Image/Width " );
8905     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
8906     aLine.append( " /Height " );
8907     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
8908     aLine.append( " /BitsPerComponent 8 " );
8909     if( rObject.m_bTrueColor )
8910         aLine.append( "/ColorSpace/DeviceRGB" );
8911     else
8912         aLine.append( "/ColorSpace/DeviceGray" );
8913     aLine.append( "/Filter/DCTDecode/Length " );
8914     aLine.append( nLength );
8915     if( nMaskObject )
8916     {
8917         aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
8918         aLine.append( nMaskObject );
8919         aLine.append( " 0 R " );
8920     }
8921     aLine.append( ">>\nstream\n" );
8922     CHECK_RETURN2( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8923 
8924     checkAndEnableStreamEncryption( rObject.m_nObject );
8925     CHECK_RETURN2( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
8926     disableStreamEncryption();
8927 
8928     aLine.setLength( 0 );
8929     aLine.append( "\nendstream\nendobj\n\n" );
8930     CHECK_RETURN2( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8931 
8932     if( nMaskObject )
8933     {
8934         BitmapEmit aEmit;
8935         aEmit.m_nObject = nMaskObject;
8936         if( rObject.m_aMask.GetBitCount() == 1 )
8937             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
8938         else if( rObject.m_aMask.GetBitCount() == 8 )
8939             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
8940         writeBitmapObject( aEmit, true );
8941     }
8942 
8943     writeReferenceXObject(rObject.m_aReferenceXObject);
8944 }
8945 
8946 sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter::PDFObjectElement& rObject, std::map<sal_Int32, sal_Int32>& rCopiedResources)
8947 {
8948     auto it = rCopiedResources.find(rObject.GetObjectValue());
8949     if (it != rCopiedResources.end())
8950         // This resource was already copied once, nothing to do.
8951         return it->second;
8952 
8953     sal_Int32 nObject = createObject();
8954     // Remember what is the ID of this object in our output.
8955     rCopiedResources[rObject.GetObjectValue()] = nObject;
8956     SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::copyExternalResource: " << rObject.GetObjectValue() << " -> " << nObject);
8957 
8958     OStringBuffer aLine;
8959     aLine.append(nObject);
8960     aLine.append(" 0 obj\n");
8961     if (rObject.GetDictionary())
8962     {
8963         aLine.append("<<");
8964 
8965         // Complex case: can't copy the dictionary byte array as is, as it may contain references.
8966         bool bDone = false;
8967         sal_uInt64 nCopyStart = 0;
8968         for (auto pReference : rObject.GetDictionaryReferences())
8969         {
8970             if (pReference)
8971             {
8972                 filter::PDFObjectElement* pReferenced = pReference->LookupObject();
8973                 if (pReferenced)
8974                 {
8975                     // Copy the referenced object.
8976                     sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced, rCopiedResources);
8977 
8978                     sal_uInt64 nReferenceStart = pReference->GetObjectElement().GetLocation();
8979                     sal_uInt64 nReferenceEnd = pReference->GetOffset();
8980                     sal_uInt64 nOffset = 0;
8981                     if (nCopyStart == 0)
8982                         // Dict start -> reference start.
8983                         nOffset = rObject.GetDictionaryOffset();
8984                     else
8985                         // Previous reference end -> reference start.
8986                         nOffset = nCopyStart;
8987                     aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nOffset, nReferenceStart - nOffset);
8988                     // Write the updated reference.
8989                     aLine.append(" ");
8990                     aLine.append(nRef);
8991                     aLine.append(" 0 R");
8992                     // Start copying here next time.
8993                     nCopyStart = nReferenceEnd;
8994 
8995                     bDone = true;
8996                 }
8997             }
8998         }
8999 
9000         if (bDone)
9001         {
9002             // Copy the last part here, in the complex case.
9003             sal_uInt64 nDictEnd = rObject.GetDictionaryOffset() + rObject.GetDictionaryLength();
9004             aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nCopyStart, nDictEnd - nCopyStart);
9005         }
9006         else
9007             // Can copy it as-is.
9008             aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + rObject.GetDictionaryOffset(), rObject.GetDictionaryLength());
9009 
9010         aLine.append(">>\n");
9011     }
9012 
9013     if (filter::PDFStreamElement* pStream = rObject.GetStream())
9014     {
9015         aLine.append("stream\n");
9016         SvMemoryStream& rStream = pStream->GetMemory();
9017         aLine.append(static_cast<const sal_Char*>(rStream.GetData()), rStream.GetSize());
9018         aLine.append("\nendstream\n");
9019     }
9020 
9021     if (filter::PDFArrayElement* pArray = rObject.GetArray())
9022     {
9023         aLine.append("[");
9024 
9025         const std::vector<filter::PDFElement*>& rElements = pArray->GetElements();
9026         bool bDone = false;
9027         // Complex case: can't copy the array byte array as is, as it may contain references.
9028         sal_uInt64 nCopyStart = 0;
9029         for (const auto pElement : rElements)
9030         {
9031             auto pReference = dynamic_cast<filter::PDFReferenceElement*>(pElement);
9032             if (pReference)
9033             {
9034                 filter::PDFObjectElement* pReferenced = pReference->LookupObject();
9035                 if (pReferenced)
9036                 {
9037                     // Copy the referenced object.
9038                     sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced, rCopiedResources);
9039 
9040                     sal_uInt64 nReferenceStart = pReference->GetObjectElement().GetLocation();
9041                     sal_uInt64 nReferenceEnd = pReference->GetOffset();
9042                     sal_uInt64 nOffset = 0;
9043                     if (nCopyStart == 0)
9044                         // Array start -> reference start.
9045                         nOffset = rObject.GetArrayOffset();
9046                     else
9047                         // Previous reference end -> reference start.
9048                         nOffset = nCopyStart;
9049                     aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nOffset, nReferenceStart - nOffset);
9050 
9051                     // Write the updated reference.
9052                     aLine.append(" ");
9053                     aLine.append(nRef);
9054                     aLine.append(" 0 R");
9055                     // Start copying here next time.
9056                     nCopyStart = nReferenceEnd;
9057 
9058                     bDone = true;
9059                 }
9060             }
9061         }
9062 
9063         if (bDone)
9064         {
9065             // Copy the last part here, in the complex case.
9066             sal_uInt64 nArrEnd = rObject.GetArrayOffset() + rObject.GetArrayLength();
9067             aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nCopyStart, nArrEnd - nCopyStart);
9068         }
9069         else
9070             // Can copy it as-is.
9071             aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + rObject.GetArrayOffset(), rObject.GetArrayLength());
9072 
9073         aLine.append("]\n");
9074     }
9075 
9076     // If the object has a number element outside a dictionary or array, copy that.
9077     if (filter::PDFNumberElement* pNumber = rObject.GetNumberElement())
9078     {
9079         aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + pNumber->GetLocation(), pNumber->GetLength());
9080         aLine.append("\n");
9081     }
9082 
9083 
9084     aLine.append("endobj\n\n");
9085 
9086     // We have the whole object, now write it to the output.
9087     if (!updateObject(nObject))
9088         return -1;
9089     if (!writeBuffer(aLine.getStr(), aLine.getLength()))
9090         return -1;
9091 
9092     return nObject;
9093 }
9094 
9095 OString PDFWriterImpl::copyExternalResources(filter::PDFObjectElement& rPage, const OString& rKind, std::map<sal_Int32, sal_Int32>& rCopiedResources)
9096 {
9097     // A name - object ID map, IDs as they appear in our output, not the
9098     // original ones.
9099     std::map<OString, sal_Int32> aRet;
9100 
9101     // Get the rKind subset of the resource dictionary.
9102     std::map<OString, filter::PDFElement*> aItems;
9103     if (auto pResources = dynamic_cast<filter::PDFDictionaryElement*>(rPage.Lookup("Resources")))
9104     {
9105         // Resources is a direct dictionary.
9106         if (auto pDictionary = dynamic_cast<filter::PDFDictionaryElement*>(pResources->LookupElement(rKind)))
9107             aItems = pDictionary->GetItems();
9108     }
9109     else if (filter::PDFObjectElement* pPageResources = rPage.LookupObject("Resources"))
9110     {
9111         // Resources is an indirect object.
9112         filter::PDFElement* pValue = pPageResources->Lookup(rKind);
9113         if (auto pDictionary = dynamic_cast<filter::PDFDictionaryElement*>(pValue))
9114             // Kind is a direct dictionary.
9115             aItems = pDictionary->GetItems();
9116         else if (filter::PDFObjectElement* pObject = pPageResources->LookupObject(rKind))
9117             // Kind is an indirect object.
9118             aItems = pObject->GetDictionaryItems();
9119     }
9120     if (aItems.empty())
9121         return OString();
9122 
9123     SvMemoryStream& rDocBuffer = rPage.GetDocument().GetEditBuffer();
9124 
9125     for (const auto& rItem : aItems)
9126     {
9127         // For each item copy it over to our output then insert it into aRet.
9128         auto pReference = dynamic_cast<filter::PDFReferenceElement*>(rItem.second);
9129         if (!pReference)
9130             continue;
9131 
9132         filter::PDFObjectElement* pValue = pReference->LookupObject();
9133         if (!pValue)
9134             continue;
9135 
9136         // Then copying over an object copy its dictionary and its stream.
9137         sal_Int32 nObject = copyExternalResource(rDocBuffer, *pValue, rCopiedResources);
9138         aRet[rItem.first] = nObject;
9139     }
9140 
9141     // Build the dictionary entry string.
9142     OString sRet = "/" + rKind + "<<";
9143     for (const auto& rPair : aRet)
9144     {
9145         sRet += "/" + rPair.first + " " + OString::number(rPair.second) + " 0 R";
9146     }
9147     sRet += ">>";
9148 
9149     return sRet;
9150 }
9151 
9152 void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit)
9153 {
9154     if (rEmit.m_nFormObject <= 0)
9155         return;
9156 
9157     // Count /Matrix and /BBox.
9158     // vcl::ImportPDF() works with 96 DPI so use the same values here, too.
9159     sal_Int32 nOldDPIX = getReferenceDevice()->GetDPIX();
9160     getReferenceDevice()->SetDPIX(96);
9161     sal_Int32 nOldDPIY = getReferenceDevice()->GetDPIY();
9162     getReferenceDevice()->SetDPIY(96);
9163     Size aSize = getReferenceDevice()->PixelToLogic(rEmit.m_aPixelSize, MapMode(m_aMapMode.GetMapUnit()));
9164     getReferenceDevice()->SetDPIX(nOldDPIX);
9165     getReferenceDevice()->SetDPIY(nOldDPIY);
9166     double fScaleX = 1.0 / aSize.Width();
9167     double fScaleY = 1.0 / aSize.Height();
9168 
9169     sal_Int32 nWrappedFormObject = 0;
9170     if (!m_aContext.UseReferenceXObject)
9171     {
9172         // Parse the PDF data, we need that to write the PDF dictionary of our
9173         // object.
9174         SvMemoryStream aPDFStream;
9175         aPDFStream.WriteBytes(rEmit.m_aPDFData.getArray(), rEmit.m_aPDFData.getLength());
9176         aPDFStream.Seek(0);
9177         filter::PDFDocument aPDFDocument;
9178         if (!aPDFDocument.Read(aPDFStream))
9179         {
9180             SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: reading the PDF document failed");
9181             return;
9182         }
9183         std::vector<filter::PDFObjectElement*> aPages = aPDFDocument.GetPages();
9184         if (aPages.empty())
9185         {
9186             SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no pages");
9187             return;
9188         }
9189 
9190         filter::PDFObjectElement* pPage = aPages[0];
9191         if (!pPage)
9192         {
9193             SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no page");
9194             return;
9195         }
9196 
9197         std::vector<filter::PDFObjectElement*> aContentStreams;
9198         if (filter::PDFObjectElement* pContentStream = pPage->LookupObject("Contents"))
9199             aContentStreams.push_back(pContentStream);
9200         else if (auto pArray = dynamic_cast<filter::PDFArrayElement*>(pPage->Lookup("Contents")))
9201         {
9202             for (const auto pElement : pArray->GetElements())
9203             {
9204                 auto pReference = dynamic_cast<filter::PDFReferenceElement*>(pElement);
9205                 if (!pReference)
9206                     continue;
9207 
9208                 filter::PDFObjectElement* pObject = pReference->LookupObject();
9209                 if (!pObject)
9210                     continue;
9211 
9212                 aContentStreams.push_back(pObject);
9213             }
9214         }
9215 
9216         if (aContentStreams.empty())
9217         {
9218             SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no content stream");
9219             return;
9220         }
9221 
9222         // Maps from source object id (PDF image) to target object id (export result).
9223         std::map<sal_Int32, sal_Int32> aCopiedResources;
9224 
9225         nWrappedFormObject = createObject();
9226         // Write the form XObject wrapped below. This is a separate object from
9227         // the wrapper, this way there is no need to alter the stream contents.
9228 
9229         OStringBuffer aLine;
9230         aLine.append(nWrappedFormObject);
9231         aLine.append(" 0 obj\n");
9232         aLine.append("<< /Type /XObject");
9233         aLine.append(" /Subtype /Form");
9234         aLine.append(" /Resources <<");
9235         static const std::initializer_list<OString> aKeys =
9236         {
9237             "ColorSpace",
9238             "ExtGState",
9239             "Font",
9240             "XObject",
9241             "Shading"
9242         };
9243         for (const auto& rKey : aKeys)
9244             aLine.append(copyExternalResources(*pPage, rKey, aCopiedResources));
9245         aLine.append(">>");
9246         aLine.append(" /BBox [ 0 0 ");
9247         aLine.append(aSize.Width());
9248         aLine.append(" ");
9249         aLine.append(aSize.Height());
9250         aLine.append(" ]");
9251 
9252         if (!g_bDebugDisableCompression)
9253             aLine.append(" /Filter/FlateDecode");
9254         aLine.append(" /Length ");
9255 
9256         SvMemoryStream aStream;
9257         for (auto pContent : aContentStreams)
9258         {
9259             filter::PDFStreamElement* pPageStream = pContent->GetStream();
9260             if (!pPageStream)
9261             {
9262                 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: contents has no stream");
9263                 continue;
9264             }
9265 
9266             SvMemoryStream& rPageStream = pPageStream->GetMemory();
9267 
9268             auto pFilter = dynamic_cast<filter::PDFNameElement*>(pContent->Lookup("Filter"));
9269             if (pFilter)
9270             {
9271                 if (pFilter->GetValue() != "FlateDecode")
9272                     continue;
9273 
9274                 SvMemoryStream aMemoryStream;
9275                 ZCodec aZCodec;
9276                 rPageStream.Seek(0);
9277                 aZCodec.BeginCompression();
9278                 aZCodec.Decompress(rPageStream, aMemoryStream);
9279                 if (!aZCodec.EndCompression())
9280                 {
9281                     SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: decompression failed");
9282                     continue;
9283                 }
9284 
9285                 aStream.WriteBytes(aMemoryStream.GetData(), aMemoryStream.GetSize());
9286             }
9287             else
9288                 aStream.WriteBytes(rPageStream.GetData(), rPageStream.GetSize());
9289         }
9290 
9291         compressStream(&aStream);
9292         sal_Int32 nLength = aStream.Tell();
9293         aLine.append(nLength);
9294 
9295         aLine.append(">>\nstream\n");
9296         // Copy the original page streams to the form XObject stream.
9297         aLine.append(static_cast<const sal_Char*>(aStream.GetData()), aStream.GetSize());
9298         aLine.append("\nendstream\nendobj\n\n");
9299         if (!updateObject(nWrappedFormObject))
9300             return;
9301         if (!writeBuffer(aLine.getStr(), aLine.getLength()))
9302             return;
9303     }
9304 
9305     OStringBuffer aLine;
9306     if (!updateObject(rEmit.m_nFormObject))
9307         return;
9308 
9309     // Now have all the info to write the form XObject.
9310     aLine.append(rEmit.m_nFormObject);
9311     aLine.append(" 0 obj\n");
9312     aLine.append("<< /Type /XObject");
9313     aLine.append(" /Subtype /Form");
9314     aLine.append(" /Resources << /XObject<<");
9315 
9316     sal_Int32 nObject = m_aContext.UseReferenceXObject ? rEmit.m_nBitmapObject : nWrappedFormObject;
9317     aLine.append(" /Im");
9318     aLine.append(nObject);
9319     aLine.append(" ");
9320     aLine.append(nObject);
9321     aLine.append(" 0 R");
9322 
9323     aLine.append(">> >>");
9324     aLine.append(" /Matrix [ ");
9325     appendDouble(fScaleX, aLine);
9326     aLine.append(" 0 0 ");
9327     appendDouble(fScaleY, aLine);
9328     aLine.append(" 0 0 ]");
9329     aLine.append(" /BBox [ 0 0 ");
9330     aLine.append(aSize.Width());
9331     aLine.append(" ");
9332     aLine.append(aSize.Height());
9333     aLine.append(" ]\n");
9334 
9335     if (m_aContext.UseReferenceXObject && rEmit.m_nEmbeddedObject > 0)
9336     {
9337         // Write the reference dictionary.
9338         aLine.append("/Ref<< /F << /Type /Filespec /F (<embedded file>) /EF << /F ");
9339         aLine.append(rEmit.m_nEmbeddedObject);
9340         aLine.append(" 0 R >> >> /Page 0 >>\n");
9341     }
9342 
9343     aLine.append("/Length ");
9344 
9345     OStringBuffer aStream;
9346     aStream.append("q ");
9347     if (m_aContext.UseReferenceXObject)
9348     {
9349         // Reference XObject markup is used, just refer to the fallback bitmap
9350         // here.
9351         aStream.append(aSize.Width());
9352         aStream.append(" 0 0 ");
9353         aStream.append(aSize.Height());
9354         aStream.append(" 0 0 cm\n");
9355         aStream.append("/Im");
9356         aStream.append(rEmit.m_nBitmapObject);
9357         aStream.append(" Do\n");
9358     }
9359     else
9360     {
9361         // Reset line width to the default.
9362         aStream.append(" 1 w\n");
9363 
9364         // No reference XObject, draw the form XObject containing the original
9365         // page streams.
9366         aStream.append("/Im");
9367         aStream.append(nWrappedFormObject);
9368         aStream.append(" Do\n");
9369     }
9370     aStream.append("Q");
9371     aLine.append(aStream.getLength());
9372 
9373     aLine.append(">>\nstream\n");
9374     aLine.append(aStream.getStr());
9375     aLine.append("\nendstream\nendobj\n\n");
9376     CHECK_RETURN2(writeBuffer(aLine.getStr(), aLine.getLength()));
9377 }
9378 
9379 namespace
9380 {
9381     unsigned char reverseByte(unsigned char b)
9382     {
9383         b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
9384         b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
9385         b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
9386         return b;
9387     }
9388 
9389     //tdf#103051 convert any N1BitLsbPal to N1BitMsbPal
9390     Bitmap getExportBitmap(const Bitmap &rBitmap)
9391     {
9392         Bitmap::ScopedReadAccess pAccess(const_cast<Bitmap&>(rBitmap));
9393         const ScanlineFormat eFormat = pAccess->GetScanlineFormat();
9394         if (eFormat != ScanlineFormat::N1BitLsbPal)
9395             return rBitmap;
9396         Bitmap aNewBmp(rBitmap);
9397         Bitmap::ScopedWriteAccess xWriteAcc(aNewBmp);
9398         const int nScanLineBytes = (pAccess->Width() + 7U) / 8U;
9399         for (long nY = 0L; nY < xWriteAcc->Height(); ++nY)
9400         {
9401             Scanline pBitSwap = xWriteAcc->GetScanline(nY);
9402             for (int x = 0; x < nScanLineBytes; ++x)
9403                 pBitSwap[x] = reverseByte(pBitSwap[x]);
9404         }
9405         return aNewBmp;
9406     }
9407 }
9408 
9409 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
9410 {
9411     if (rObject.m_aReferenceXObject.m_aPDFData.hasElements() && !m_aContext.UseReferenceXObject)
9412     {
9413         writeReferenceXObject(rObject.m_aReferenceXObject);
9414         return true;
9415     }
9416 
9417     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9418 
9419     Bitmap  aBitmap;
9420     Color   aTransparentColor( COL_TRANSPARENT );
9421     bool    bWriteMask = false;
9422     if( ! bMask )
9423     {
9424         aBitmap = getExportBitmap(rObject.m_aBitmap.GetBitmap());
9425         if( rObject.m_aBitmap.IsAlpha() )
9426         {
9427             if( m_aContext.Version >= PDFWriter::PDFVersion::PDF_1_4 )
9428                 bWriteMask = true;
9429             // else draw without alpha channel
9430         }
9431         else
9432         {
9433             switch( rObject.m_aBitmap.GetTransparentType() )
9434             {
9435                 case TransparentType::NONE:
9436                     break;
9437                 case TransparentType::Color:
9438                     aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
9439                     break;
9440                 case TransparentType::Bitmap:
9441                     bWriteMask = true;
9442                     break;
9443             }
9444         }
9445     }
9446     else
9447     {
9448         if( m_aContext.Version < PDFWriter::PDFVersion::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
9449         {
9450             aBitmap = getExportBitmap(rObject.m_aBitmap.GetMask());
9451             aBitmap.Convert( BmpConversion::N1BitThreshold );
9452             SAL_WARN_IF( aBitmap.GetBitCount() != 1, "vcl.pdfwriter", "mask conversion failed" );
9453         }
9454         else if( aBitmap.GetBitCount() != 8 )
9455         {
9456             aBitmap = getExportBitmap(rObject.m_aBitmap.GetAlpha().GetBitmap());
9457             aBitmap.Convert( BmpConversion::N8BitGreys );
9458             SAL_WARN_IF( aBitmap.GetBitCount() != 8, "vcl.pdfwriter", "alpha mask conversion failed" );
9459         }
9460     }
9461 
9462     Bitmap::ScopedReadAccess pAccess(aBitmap);
9463 
9464     bool bTrueColor;
9465     sal_Int32 nBitsPerComponent;
9466     switch( aBitmap.GetBitCount() )
9467     {
9468         case 1:
9469         case 2:
9470         case 4:
9471         case 8:
9472             bTrueColor = false;
9473             nBitsPerComponent = aBitmap.GetBitCount();
9474             break;
9475         default:
9476             bTrueColor = true;
9477             nBitsPerComponent = 8;
9478             break;
9479     }
9480 
9481     sal_Int32 nStreamLengthObject   = createObject();
9482     sal_Int32 nMaskObject           = 0;
9483 
9484     if (g_bDebugDisableCompression)
9485     {
9486         emitComment( "PDFWriterImpl::writeBitmapObject" );
9487     }
9488     OStringBuffer aLine(1024);
9489     aLine.append( rObject.m_nObject );
9490     aLine.append( " 0 obj\n"
9491                   "<</Type/XObject/Subtype/Image/Width " );
9492     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9493     aLine.append( "/Height " );
9494     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
9495     aLine.append( "/BitsPerComponent " );
9496     aLine.append( nBitsPerComponent );
9497     aLine.append( "/Length " );
9498     aLine.append( nStreamLengthObject );
9499     aLine.append( " 0 R\n" );
9500     if (!g_bDebugDisableCompression)
9501     {
9502         if( nBitsPerComponent != 1 )
9503         {
9504             aLine.append( "/Filter/FlateDecode" );
9505         }
9506         else
9507         {
9508             aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
9509             aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9510             aLine.append( ">>\n" );
9511         }
9512     }
9513     if( ! bMask )
9514     {
9515         aLine.append( "/ColorSpace" );
9516         if( bTrueColor )
9517             aLine.append( "/DeviceRGB\n" );
9518         else if( aBitmap.HasGreyPalette() )
9519         {
9520             aLine.append( "/DeviceGray\n" );
9521             if( aBitmap.GetBitCount() == 1 )
9522             {
9523                 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
9524                 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9525                 SAL_WARN_IF( nBlackIndex != 0 && nBlackIndex != 1, "vcl.pdfwriter", "wrong black index" );
9526                 if( nBlackIndex == 1 )
9527                     aLine.append( "/Decode[1 0]\n" );
9528             }
9529         }
9530         else
9531         {
9532             aLine.append( "[ /Indexed/DeviceRGB " );
9533             aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
9534             aLine.append( "\n<" );
9535             if( m_aContext.Encryption.Encrypt() )
9536             {
9537                 enableStringEncryption( rObject.m_nObject );
9538                 //check encryption buffer size
9539                 if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
9540                 {
9541                     int nChar = 0;
9542                     //fill the encryption buffer
9543                     for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9544                     {
9545                         const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9546                         m_pEncryptionBuffer[nChar++] = rColor.GetRed();
9547                         m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
9548                         m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
9549                     }
9550                     //encrypt the colorspace lookup table
9551                     rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
9552                     //now queue the data for output
9553                     nChar = 0;
9554                     for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9555                     {
9556                         appendHex(m_pEncryptionBuffer[nChar++], aLine );
9557                         appendHex(m_pEncryptionBuffer[nChar++], aLine );
9558                         appendHex(m_pEncryptionBuffer[nChar++], aLine );
9559                     }
9560                 }
9561             }
9562             else //no encryption requested (PDF/A-1a program flow drops here)
9563             {
9564                 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9565                 {
9566                     const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9567                     appendHex( rColor.GetRed(), aLine );
9568                     appendHex( rColor.GetGreen(), aLine );
9569                     appendHex( rColor.GetBlue(), aLine );
9570                 }
9571             }
9572             aLine.append( ">\n]\n" );
9573         }
9574     }
9575     else
9576     {
9577         if( aBitmap.GetBitCount() == 1 )
9578         {
9579             aLine.append( "/ImageMask true\n" );
9580             sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9581             SAL_WARN_IF( nBlackIndex != 0 && nBlackIndex != 1, "vcl.pdfwriter", "wrong black index" );
9582             if( nBlackIndex )
9583                 aLine.append( "/Decode[ 1 0 ]\n" );
9584             else
9585                 aLine.append( "/Decode[ 0 1 ]\n" );
9586         }
9587         else if( aBitmap.GetBitCount() == 8 )
9588         {
9589             aLine.append( "/ColorSpace/DeviceGray\n"
9590                           "/Decode [ 1 0 ]\n" );
9591         }
9592     }
9593 
9594     if( ! bMask && m_aContext.Version > PDFWriter::PDFVersion::PDF_1_2 && !m_bIsPDF_A1 )
9595     {
9596         if( bWriteMask )
9597         {
9598             nMaskObject = createObject();
9599             if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 )
9600                 aLine.append( "/SMask " );
9601             else
9602                 aLine.append( "/Mask " );
9603             aLine.append( nMaskObject );
9604             aLine.append( " 0 R\n" );
9605         }
9606         else if( aTransparentColor != Color( COL_TRANSPARENT ) )
9607         {
9608             aLine.append( "/Mask[ " );
9609             if( bTrueColor )
9610             {
9611                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9612                 aLine.append( ' ' );
9613                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9614                 aLine.append( ' ' );
9615                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9616                 aLine.append( ' ' );
9617                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9618                 aLine.append( ' ' );
9619                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9620                 aLine.append( ' ' );
9621                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9622             }
9623             else
9624             {
9625                 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
9626                 aLine.append( nIndex );
9627             }
9628             aLine.append( " ]\n" );
9629         }
9630     }
9631     else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) )
9632         m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9633 
9634     aLine.append( ">>\n"
9635                   "stream\n" );
9636     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9637     sal_uInt64 nStartPos = 0;
9638     CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nStartPos)) );
9639 
9640     checkAndEnableStreamEncryption( rObject.m_nObject );
9641     if (!g_bDebugDisableCompression && nBitsPerComponent == 1)
9642     {
9643         writeG4Stream(pAccess.get());
9644     }
9645     else
9646     {
9647         beginCompression();
9648         if( ! bTrueColor || pAccess->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb )
9649         {
9650             //With PDF bitmaps, each row is padded to a BYTE boundary (multiple of 8 bits).
9651             const int nScanLineBytes = ((pAccess->GetBitCount() * pAccess->Width()) + 7U) / 8U;
9652 
9653             for( long i = 0; i < pAccess->Height(); i++ )
9654             {
9655                 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
9656             }
9657         }
9658         else
9659         {
9660             const int nScanLineBytes = pAccess->Width()*3;
9661             std::unique_ptr<sal_uInt8[]> xCol(new sal_uInt8[nScanLineBytes]);
9662             for( long y = 0; y < pAccess->Height(); y++ )
9663             {
9664                 for( long x = 0; x < pAccess->Width(); x++ )
9665                 {
9666                     BitmapColor aColor = pAccess->GetColor( y, x );
9667                     xCol[3*x+0] = aColor.GetRed();
9668                     xCol[3*x+1] = aColor.GetGreen();
9669                     xCol[3*x+2] = aColor.GetBlue();
9670                 }
9671                 CHECK_RETURN(writeBuffer(xCol.get(), nScanLineBytes));
9672             }
9673         }
9674         endCompression();
9675     }
9676     disableStreamEncryption();
9677 
9678     sal_uInt64 nEndPos = 0;
9679     CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nEndPos)) );
9680     aLine.setLength( 0 );
9681     aLine.append( "\nendstream\nendobj\n\n" );
9682     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9683     CHECK_RETURN( updateObject( nStreamLengthObject ) );
9684     aLine.setLength( 0 );
9685     aLine.append( nStreamLengthObject );
9686     aLine.append( " 0 obj\n" );
9687     aLine.append( (sal_Int64)(nEndPos-nStartPos) );
9688     aLine.append( "\nendobj\n\n" );
9689     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9690 
9691     if( nMaskObject )
9692     {
9693         BitmapEmit aEmit;
9694         aEmit.m_nObject             = nMaskObject;
9695         aEmit.m_aBitmap             = rObject.m_aBitmap;
9696         return writeBitmapObject( aEmit, true );
9697     }
9698 
9699     writeReferenceXObject(rObject.m_aReferenceXObject);
9700 
9701     return true;
9702 }
9703 
9704 void PDFWriterImpl::createEmbeddedFile(const Graphic& rGraphic, ReferenceXObjectEmit& rEmit, sal_Int32 nBitmapObject)
9705 {
9706     // The bitmap object is always a valid identifier, even if the graphic has
9707     // no pdf data.
9708     rEmit.m_nBitmapObject = nBitmapObject;
9709 
9710     if (!rGraphic.getPdfData().hasElements())
9711         return;
9712 
9713     if (m_aContext.UseReferenceXObject)
9714     {
9715         // Store the original PDF data as an embedded file.
9716         m_aEmbeddedFiles.emplace_back();
9717         m_aEmbeddedFiles.back().m_nObject = createObject();
9718         m_aEmbeddedFiles.back().m_aData = rGraphic.getPdfData();
9719 
9720         rEmit.m_nEmbeddedObject = m_aEmbeddedFiles.back().m_nObject;
9721     }
9722     else
9723         rEmit.m_aPDFData = rGraphic.getPdfData();
9724 
9725     rEmit.m_nFormObject = createObject();
9726     rEmit.m_aPixelSize = rGraphic.GetBitmap().GetPrefSize();
9727 }
9728 
9729 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const tools::Rectangle& rTargetArea, const Bitmap& rMask, const Graphic& rGraphic )
9730 {
9731     MARK( "drawJPGBitmap" );
9732 
9733     OStringBuffer aLine( 80 );
9734     updateGraphicsState();
9735 
9736     // #i40055# sanity check
9737     if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
9738         return;
9739     if( ! (rSizePixel.Width() && rSizePixel.Height()) )
9740         return;
9741 
9742     rDCTData.Seek( 0 );
9743     if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9744     {
9745         // need to convert to grayscale;
9746         // load stream to bitmap and draw the bitmap instead
9747         Graphic aGraphic;
9748         GraphicConverter::Import( rDCTData, aGraphic, ConvertDataFormat::JPG );
9749         Bitmap aBmp( aGraphic.GetBitmap() );
9750         if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() )
9751         {
9752             BitmapEx aBmpEx( aBmp, rMask );
9753             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx );
9754         }
9755         else
9756             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp );
9757         return;
9758     }
9759 
9760     SvMemoryStream* pStream = new SvMemoryStream;
9761     pStream->WriteStream( rDCTData );
9762     pStream->Seek( STREAM_SEEK_TO_END );
9763 
9764     BitmapID aID;
9765     aID.m_aPixelSize    = rSizePixel;
9766     aID.m_nSize         = pStream->Tell();
9767     pStream->Seek( STREAM_SEEK_TO_BEGIN );
9768     aID.m_nChecksum     = vcl_get_checksum( 0, pStream->GetData(), aID.m_nSize );
9769     if( ! rMask.IsEmpty() )
9770         aID.m_nMaskChecksum = rMask.GetChecksum();
9771 
9772     std::list< JPGEmit >::const_iterator it;
9773     for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
9774         ;
9775     if( it == m_aJPGs.end() )
9776     {
9777         m_aJPGs.emplace( m_aJPGs.begin() );
9778         JPGEmit& rEmit = m_aJPGs.front();
9779         if (!rGraphic.getPdfData().hasElements() || m_aContext.UseReferenceXObject)
9780             rEmit.m_nObject = createObject();
9781         rEmit.m_aID         = aID;
9782         rEmit.m_pStream.reset( pStream );
9783         rEmit.m_bTrueColor  = bIsTrueColor;
9784         if( !! rMask && rMask.GetSizePixel() == rSizePixel )
9785             rEmit.m_aMask   = rMask;
9786         createEmbeddedFile(rGraphic, rEmit.m_aReferenceXObject, rEmit.m_nObject);
9787 
9788         it = m_aJPGs.begin();
9789     }
9790     else
9791         delete pStream;
9792 
9793     aLine.append( "q " );
9794     sal_Int32 nCheckWidth = 0;
9795     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth );
9796     aLine.append( " 0 0 " );
9797     sal_Int32 nCheckHeight = 0;
9798     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight );
9799     aLine.append( ' ' );
9800     m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
9801     aLine.append( " cm\n/Im" );
9802     sal_Int32 nObject = it->m_aReferenceXObject.getObject();
9803     aLine.append(nObject);
9804     aLine.append( " Do Q\n" );
9805     if( nCheckWidth == 0 || nCheckHeight == 0 )
9806     {
9807         // #i97512# avoid invalid current matrix
9808         aLine.setLength( 0 );
9809         aLine.append( "\n%jpeg image /Im" );
9810         aLine.append( it->m_nObject );
9811         aLine.append( " scaled to zero size, omitted\n" );
9812     }
9813     writeBuffer( aLine.getStr(), aLine.getLength() );
9814 
9815     OStringBuffer aObjName( 16 );
9816     aObjName.append( "Im" );
9817     aObjName.append(nObject);
9818     pushResource( ResXObject, aObjName.makeStringAndClear(), nObject );
9819 
9820 }
9821 
9822 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
9823 {
9824     OStringBuffer aLine( 80 );
9825     updateGraphicsState();
9826 
9827     aLine.append( "q " );
9828     if( rFillColor != Color( COL_TRANSPARENT ) )
9829     {
9830         appendNonStrokingColor( rFillColor, aLine );
9831         aLine.append( ' ' );
9832     }
9833     sal_Int32 nCheckWidth = 0;
9834     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth );
9835     aLine.append( " 0 0 " );
9836     sal_Int32 nCheckHeight = 0;
9837     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight );
9838     aLine.append( ' ' );
9839     m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
9840     aLine.append( " cm\n/Im" );
9841     sal_Int32 nObject = rBitmap.m_aReferenceXObject.getObject();
9842     aLine.append(nObject);
9843     aLine.append( " Do Q\n" );
9844     if( nCheckWidth == 0 || nCheckHeight == 0 )
9845     {
9846         // #i97512# avoid invalid current matrix
9847         aLine.setLength( 0 );
9848         aLine.append( "\n%bitmap image /Im" );
9849         aLine.append( rBitmap.m_nObject );
9850         aLine.append( " scaled to zero size, omitted\n" );
9851     }
9852     writeBuffer( aLine.getStr(), aLine.getLength() );
9853 }
9854 
9855 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, const Graphic& rGraphic )
9856 {
9857     BitmapEx aBitmap( i_rBitmap );
9858     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9859     {
9860         BmpConversion eConv = BmpConversion::N8BitGreys;
9861         int nDepth = aBitmap.GetBitmap().GetBitCount();
9862         if( nDepth <= 4 )
9863             eConv = BmpConversion::N4BitGreys;
9864         if( nDepth > 1 )
9865             aBitmap.Convert( eConv );
9866     }
9867     BitmapID aID;
9868     aID.m_aPixelSize        = aBitmap.GetSizePixel();
9869     aID.m_nSize             = aBitmap.GetBitCount();
9870     aID.m_nChecksum         = aBitmap.GetBitmap().GetChecksum();
9871     aID.m_nMaskChecksum     = 0;
9872     if( aBitmap.IsAlpha() )
9873         aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum();
9874     else
9875     {
9876         Bitmap aMask = aBitmap.GetMask();
9877         if( ! aMask.IsEmpty() )
9878             aID.m_nMaskChecksum = aMask.GetChecksum();
9879     }
9880     std::list< BitmapEmit >::const_iterator it;
9881     for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
9882     {
9883         if( aID == it->m_aID )
9884             break;
9885     }
9886     if( it == m_aBitmaps.end() )
9887     {
9888         m_aBitmaps.push_front( BitmapEmit() );
9889         m_aBitmaps.front().m_aID        = aID;
9890         m_aBitmaps.front().m_aBitmap    = aBitmap;
9891         if (!rGraphic.getPdfData().hasElements() || m_aContext.UseReferenceXObject)
9892             m_aBitmaps.front().m_nObject = createObject();
9893         createEmbeddedFile(rGraphic, m_aBitmaps.front().m_aReferenceXObject, m_aBitmaps.front().m_nObject);
9894         it = m_aBitmaps.begin();
9895     }
9896 
9897     OStringBuffer aObjName( 16 );
9898     aObjName.append( "Im" );
9899     sal_Int32 nObject = it->m_aReferenceXObject.getObject();
9900     aObjName.append(nObject);
9901     pushResource( ResXObject, aObjName.makeStringAndClear(), nObject );
9902 
9903     return *it;
9904 }
9905 
9906 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Graphic& rGraphic )
9907 {
9908     MARK( "drawBitmap (Bitmap)" );
9909 
9910     // #i40055# sanity check
9911     if( ! (rDestSize.Width() && rDestSize.Height()) )
9912         return;
9913 
9914     const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ), rGraphic );
9915     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
9916 }
9917 
9918 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
9919 {
9920     MARK( "drawBitmap (BitmapEx)" );
9921 
9922     // #i40055# sanity check
9923     if( ! (rDestSize.Width() && rDestSize.Height()) )
9924         return;
9925 
9926     const BitmapEmit& rEmit = createBitmapEmit( rBitmap, Graphic() );
9927     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
9928 }
9929 
9930 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
9931 {
9932     Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
9933                                MapMode( MapUnit::MapPoint ),
9934                                getReferenceDevice(),
9935                                rSize ) );
9936     // check if we already have this gradient
9937     std::list<GradientEmit>::iterator it;
9938     // rounding to point will generally lose some pixels
9939     // round up to point boundary
9940     aPtSize.Width()++;
9941     aPtSize.Height()++;
9942     for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
9943     {
9944         if( it->m_aGradient == rGradient )
9945         {
9946             if( it->m_aSize == aPtSize )
9947                 break;
9948         }
9949     }
9950     if( it == m_aGradients.end() )
9951     {
9952         m_aGradients.push_front( GradientEmit() );
9953         m_aGradients.front().m_aGradient    = rGradient;
9954         m_aGradients.front().m_nObject      = createObject();
9955         m_aGradients.front().m_aSize        = aPtSize;
9956         it = m_aGradients.begin();
9957     }
9958 
9959     OStringBuffer aObjName( 16 );
9960     aObjName.append( 'P' );
9961     aObjName.append( it->m_nObject );
9962     pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
9963 
9964     return it->m_nObject;
9965 }
9966 
9967 void PDFWriterImpl::drawGradient( const tools::Rectangle& rRect, const Gradient& rGradient )
9968 {
9969     MARK( "drawGradient (Rectangle)" );
9970 
9971     if( m_aContext.Version == PDFWriter::PDFVersion::PDF_1_2 )
9972     {
9973         drawRectangle( rRect );
9974         return;
9975     }
9976 
9977     sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
9978 
9979     Point aTranslate( rRect.BottomLeft() );
9980     aTranslate += Point( 0, 1 );
9981 
9982     updateGraphicsState();
9983 
9984     OStringBuffer aLine( 80 );
9985     aLine.append( "q 1 0 0 1 " );
9986     m_aPages.back().appendPoint( aTranslate, aLine );
9987     aLine.append( " cm " );
9988     // if a stroke is appended reset the clip region before stroke
9989     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9990         aLine.append( "q " );
9991     aLine.append( "0 0 " );
9992     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
9993     aLine.append( ' ' );
9994     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine );
9995     aLine.append( " re W n\n" );
9996 
9997     aLine.append( "/P" );
9998     aLine.append( nGradient );
9999     aLine.append( " sh " );
10000     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10001     {
10002         aLine.append( "Q 0 0 " );
10003         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10004         aLine.append( ' ' );
10005         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine );
10006         aLine.append( " re S " );
10007     }
10008     aLine.append( "Q\n" );
10009     writeBuffer( aLine.getStr(), aLine.getLength() );
10010 }
10011 
10012 void PDFWriterImpl::drawHatch( const tools::PolyPolygon& rPolyPoly, const Hatch& rHatch )
10013 {
10014     MARK( "drawHatch" );
10015 
10016     updateGraphicsState();
10017 
10018     if( rPolyPoly.Count() )
10019     {
10020         tools::PolyPolygon     aPolyPoly( rPolyPoly );
10021 
10022         aPolyPoly.Optimize( PolyOptimizeFlags::NO_SAME );
10023         push( PushFlags::LINECOLOR );
10024         setLineColor( rHatch.GetColor() );
10025         getReferenceDevice()->DrawHatch( aPolyPoly, rHatch, false );
10026         pop();
10027     }
10028 }
10029 
10030 void PDFWriterImpl::drawWallpaper( const tools::Rectangle& rRect, const Wallpaper& rWall )
10031 {
10032     MARK( "drawWallpaper" );
10033 
10034     bool bDrawColor         = false;
10035     bool bDrawGradient      = false;
10036     bool bDrawBitmap        = false;
10037 
10038     BitmapEx aBitmap;
10039     Point aBmpPos = rRect.TopLeft();
10040     Size aBmpSize;
10041     if( rWall.IsBitmap() )
10042     {
10043         aBitmap = rWall.GetBitmap();
10044         aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
10045                                 getMapMode(),
10046                                 getReferenceDevice(),
10047                                 aBitmap.GetPrefSize() );
10048         tools::Rectangle aRect( rRect );
10049         if( rWall.IsRect() )
10050         {
10051             aRect = rWall.GetRect();
10052             aBmpPos = aRect.TopLeft();
10053             aBmpSize = aRect.GetSize();
10054         }
10055         if( rWall.GetStyle() != WallpaperStyle::Scale )
10056         {
10057             if( rWall.GetStyle() != WallpaperStyle::Tile )
10058             {
10059                 bDrawBitmap     = true;
10060                 if( rWall.IsGradient() )
10061                     bDrawGradient = true;
10062                 else
10063                     bDrawColor = true;
10064                 switch( rWall.GetStyle() )
10065                 {
10066                     case WallpaperStyle::TopLeft:
10067                         break;
10068                     case WallpaperStyle::Top:
10069                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10070                         break;
10071                     case WallpaperStyle::Left:
10072                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10073                         break;
10074                     case WallpaperStyle::TopRight:
10075                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10076                         break;
10077                     case WallpaperStyle::Center:
10078                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10079                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10080                         break;
10081                     case WallpaperStyle::Right:
10082                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10083                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10084                         break;
10085                     case WallpaperStyle::BottomLeft:
10086                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10087                         break;
10088                     case WallpaperStyle::Bottom:
10089                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10090                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10091                         break;
10092                     case WallpaperStyle::BottomRight:
10093                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10094                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10095                         break;
10096                     default: ;
10097                 }
10098             }
10099             else
10100             {
10101                 // push the bitmap
10102                 const BitmapEmit& rEmit = createBitmapEmit( aBitmap, Graphic() );
10103 
10104                 // convert to page coordinates; this needs to be done here
10105                 // since the emit does not know the page anymore
10106                 tools::Rectangle aConvertRect( aBmpPos, aBmpSize );
10107                 m_aPages.back().convertRect( aConvertRect );
10108 
10109                 OStringBuffer aNameBuf(16);
10110                 aNameBuf.append( "Im" );
10111                 aNameBuf.append( rEmit.m_nObject );
10112                 OString aImageName( aNameBuf.makeStringAndClear() );
10113 
10114                 // push the pattern
10115                 OStringBuffer aTilingStream( 32 );
10116                 appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
10117                 aTilingStream.append( " 0 0 " );
10118                 appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
10119                 aTilingStream.append( " 0 0 cm\n/" );
10120                 aTilingStream.append( aImageName );
10121                 aTilingStream.append( " Do\n" );
10122 
10123                 m_aTilings.emplace_back( );
10124                 m_aTilings.back().m_nObject         = createObject();
10125                 m_aTilings.back().m_aRectangle      = tools::Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
10126                 m_aTilings.back().m_pTilingStream   = new SvMemoryStream();
10127                 m_aTilings.back().m_pTilingStream->WriteBytes(
10128                     aTilingStream.getStr(), aTilingStream.getLength() );
10129                 // phase the tiling so wallpaper begins on upper left
10130                 if ((aConvertRect.GetWidth() == 0) || (aConvertRect.GetHeight() == 0))
10131                     throw o3tl::divide_by_zero();
10132                 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
10133                 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
10134                 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
10135 
10136                 updateGraphicsState();
10137 
10138                 OStringBuffer aObjName( 16 );
10139                 aObjName.append( 'P' );
10140                 aObjName.append( m_aTilings.back().m_nObject );
10141                 OString aPatternName( aObjName.makeStringAndClear() );
10142                 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
10143 
10144                 // fill a rRect with the pattern
10145                 OStringBuffer aLine( 100 );
10146                 aLine.append( "q /Pattern cs /" );
10147                 aLine.append( aPatternName );
10148                 aLine.append( " scn " );
10149                 m_aPages.back().appendRect( rRect, aLine );
10150                 aLine.append( " f Q\n" );
10151                 writeBuffer( aLine.getStr(), aLine.getLength() );
10152             }
10153         }
10154         else
10155         {
10156             aBmpPos     = aRect.TopLeft();
10157             aBmpSize    = aRect.GetSize();
10158             bDrawBitmap = true;
10159         }
10160 
10161         if( aBitmap.IsTransparent() )
10162         {
10163             if( rWall.IsGradient() )
10164                 bDrawGradient = true;
10165             else
10166                 bDrawColor = true;
10167         }
10168     }
10169     else if( rWall.IsGradient() )
10170         bDrawGradient = true;
10171     else
10172         bDrawColor = true;
10173 
10174     if( bDrawGradient )
10175     {
10176         drawGradient( rRect, rWall.GetGradient() );
10177     }
10178     if( bDrawColor )
10179     {
10180         Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
10181         Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
10182         setLineColor( Color( COL_TRANSPARENT ) );
10183         setFillColor( rWall.GetColor() );
10184         drawRectangle( rRect );
10185         setLineColor( aOldLineColor );
10186         setFillColor( aOldFillColor );
10187     }
10188     if( bDrawBitmap )
10189     {
10190         // set temporary clip region since aBmpPos and aBmpSize
10191         // may be outside rRect
10192         OStringBuffer aLine( 20 );
10193         aLine.append( "q " );
10194         m_aPages.back().appendRect( rRect, aLine );
10195         aLine.append( " W n\n" );
10196         writeBuffer( aLine.getStr(), aLine.getLength() );
10197         drawBitmap( aBmpPos, aBmpSize, aBitmap );
10198         writeBuffer( "Q\n", 2 );
10199     }
10200 }
10201 
10202 void PDFWriterImpl::updateGraphicsState(Mode const mode)
10203 {
10204     OStringBuffer aLine( 256 );
10205     GraphicsState& rNewState = m_aGraphicsStack.front();
10206     // first set clip region since it might invalidate everything else
10207 
10208     if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::ClipRegion) )
10209     {
10210         rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::ClipRegion;
10211 
10212         if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion ||
10213             ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) )
10214         {
10215             if( m_aCurrentPDFState.m_bClipRegion )
10216             {
10217                 aLine.append( "Q " );
10218                 // invalidate everything but the clip region
10219                 m_aCurrentPDFState = GraphicsState();
10220                 rNewState.m_nUpdateFlags = ~GraphicsStateUpdateFlags::ClipRegion;
10221             }
10222             if( rNewState.m_bClipRegion )
10223             {
10224                 // clip region is always stored in private PDF mapmode
10225                 MapMode aNewMapMode = rNewState.m_aMapMode;
10226                 rNewState.m_aMapMode = m_aMapMode;
10227                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10228                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10229 
10230                 aLine.append("q ");
10231                 if ( rNewState.m_aClipRegion.count() )
10232                 {
10233                     m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine );
10234                     aLine.append( "W* n\n" );
10235                 }
10236 
10237                 rNewState.m_aMapMode = aNewMapMode;
10238                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10239                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10240             }
10241         }
10242     }
10243 
10244     if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::MapMode) )
10245     {
10246         rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::MapMode;
10247         getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10248     }
10249 
10250     if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::Font) )
10251     {
10252         rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::Font;
10253         getReferenceDevice()->SetFont( rNewState.m_aFont );
10254         getReferenceDevice()->ImplNewFont();
10255     }
10256 
10257     if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::LayoutMode) )
10258     {
10259         rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::LayoutMode;
10260         getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
10261     }
10262 
10263     if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::DigitLanguage) )
10264     {
10265         rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::DigitLanguage;
10266         getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
10267     }
10268 
10269     if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::LineColor) )
10270     {
10271         rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::LineColor;
10272         if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
10273             rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
10274         {
10275             appendStrokingColor( rNewState.m_aLineColor, aLine );
10276             aLine.append( "\n" );
10277         }
10278     }
10279 
10280     if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::FillColor) )
10281     {
10282         rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::FillColor;
10283         if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
10284             rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
10285         {
10286             appendNonStrokingColor( rNewState.m_aFillColor, aLine );
10287             aLine.append( "\n" );
10288         }
10289     }
10290 
10291     if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::TransparentPercent) )
10292     {
10293         rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::TransparentPercent;
10294         if( m_aContext.Version >= PDFWriter::PDFVersion::PDF_1_4 )
10295         {
10296             // TODO: switch extended graphicsstate
10297         }
10298     }
10299 
10300     // everything is up to date now
10301     m_aCurrentPDFState = m_aGraphicsStack.front();
10302     if ((mode != NOWRITE) &&  !aLine.isEmpty())
10303         writeBuffer( aLine.getStr(), aLine.getLength() );
10304 }
10305 
10306 /* #i47544# imitate OutputDevice behaviour:
10307 *  if a font with a nontransparent color is set, it overwrites the current
10308 *  text color. OTOH setting the text color will overwrite the color of the font.
10309 */
10310 void PDFWriterImpl::setFont( const vcl::Font& rFont )
10311 {
10312     Color aColor = rFont.GetColor();
10313     if( aColor == Color( COL_TRANSPARENT ) )
10314         aColor = m_aGraphicsStack.front().m_aFont.GetColor();
10315     m_aGraphicsStack.front().m_aFont = rFont;
10316     m_aGraphicsStack.front().m_aFont.SetColor( aColor );
10317     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::Font;
10318 }
10319 
10320 void PDFWriterImpl::push( PushFlags nFlags )
10321 {
10322     OSL_ENSURE( !m_aGraphicsStack.empty(), "invalid graphics stack" );
10323     m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
10324     m_aGraphicsStack.front().m_nFlags = nFlags;
10325 }
10326 
10327 void PDFWriterImpl::pop()
10328 {
10329     OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" );
10330     if( m_aGraphicsStack.size() < 2 )
10331         return;
10332 
10333     GraphicsState aState = m_aGraphicsStack.front();
10334     m_aGraphicsStack.pop_front();
10335     GraphicsState& rOld = m_aGraphicsStack.front();
10336 
10337     // move those parameters back that were not pushed
10338     // in the first place
10339     if( ! (aState.m_nFlags & PushFlags::LINECOLOR) )
10340         setLineColor( aState.m_aLineColor );
10341     if( ! (aState.m_nFlags & PushFlags::FILLCOLOR) )
10342         setFillColor( aState.m_aFillColor );
10343     if( ! (aState.m_nFlags & PushFlags::FONT) )
10344         setFont( aState.m_aFont );
10345     if( ! (aState.m_nFlags & PushFlags::TEXTCOLOR) )
10346         setTextColor( aState.m_aFont.GetColor() );
10347     if( ! (aState.m_nFlags & PushFlags::MAPMODE) )
10348         setMapMode( aState.m_aMapMode );
10349     if( ! (aState.m_nFlags & PushFlags::CLIPREGION) )
10350     {
10351         // do not use setClipRegion here
10352         // it would convert again assuming the current mapmode
10353         rOld.m_aClipRegion = aState.m_aClipRegion;
10354         rOld.m_bClipRegion = aState.m_bClipRegion;
10355     }
10356     if( ! (aState.m_nFlags & PushFlags::TEXTLINECOLOR ) )
10357         setTextLineColor( aState.m_aTextLineColor );
10358     if( ! (aState.m_nFlags & PushFlags::OVERLINECOLOR ) )
10359         setOverlineColor( aState.m_aOverlineColor );
10360     if( ! (aState.m_nFlags & PushFlags::TEXTALIGN ) )
10361         setTextAlign( aState.m_aFont.GetAlignment() );
10362     if( ! (aState.m_nFlags & PushFlags::TEXTFILLCOLOR) )
10363         setTextFillColor( aState.m_aFont.GetFillColor() );
10364     if( ! (aState.m_nFlags & PushFlags::REFPOINT) )
10365     {
10366         // what ?
10367     }
10368     // invalidate graphics state
10369     m_aGraphicsStack.front().m_nUpdateFlags = GraphicsStateUpdateFlags::All;
10370 }
10371 
10372 void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
10373 {
10374     m_aGraphicsStack.front().m_aMapMode = rMapMode;
10375     getReferenceDevice()->SetMapMode( rMapMode );
10376     m_aCurrentPDFState.m_aMapMode = rMapMode;
10377 }
10378 
10379 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10380 {
10381     basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10382     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10383     m_aGraphicsStack.front().m_aClipRegion = aRegion;
10384     m_aGraphicsStack.front().m_bClipRegion = true;
10385     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion;
10386 }
10387 
10388 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
10389 {
10390     if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() )
10391     {
10392         Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10393                                    m_aMapMode,
10394                                    getReferenceDevice(),
10395                                    Point( nX, nY ) ) );
10396         aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10397                                m_aMapMode,
10398                                getReferenceDevice(),
10399                                Point() );
10400         basegfx::B2DHomMatrix aMat;
10401         aMat.translate( aPoint.X(), aPoint.Y() );
10402         m_aGraphicsStack.front().m_aClipRegion.transform( aMat );
10403         m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion;
10404     }
10405 }
10406 
10407 void PDFWriterImpl::intersectClipRegion( const tools::Rectangle& rRect )
10408 {
10409     basegfx::B2DPolyPolygon aRect( basegfx::utils::createPolygonFromRect(
10410         basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) );
10411     intersectClipRegion( aRect );
10412 }
10413 
10414 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10415 {
10416     basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) );
10417     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10418     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion;
10419     if( m_aGraphicsStack.front().m_bClipRegion )
10420     {
10421         basegfx::B2DPolyPolygon aOld( basegfx::utils::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) );
10422         aRegion = basegfx::utils::prepareForPolygonOperation( aRegion );
10423         m_aGraphicsStack.front().m_aClipRegion = basegfx::utils::solvePolygonOperationAnd( aOld, aRegion );
10424     }
10425     else
10426     {
10427         m_aGraphicsStack.front().m_aClipRegion = aRegion;
10428         m_aGraphicsStack.front().m_bClipRegion = true;
10429     }
10430     return true;
10431 }
10432 
10433 void PDFWriterImpl::createNote( const tools::Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
10434 {
10435     if( nPageNr < 0 )
10436         nPageNr = m_nCurrentPage;
10437 
10438     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10439         return;
10440 
10441     m_aNotes.emplace_back( );
10442     m_aNotes.back().m_nObject       = createObject();
10443     m_aNotes.back().m_aContents     = rNote;
10444     m_aNotes.back().m_aRect         = rRect;
10445     // convert to default user space now, since the mapmode may change
10446     m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
10447 
10448     // insert note to page's annotation list
10449     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
10450 }
10451 
10452 sal_Int32 PDFWriterImpl::createLink( const tools::Rectangle& rRect, sal_Int32 nPageNr )
10453 {
10454     if( nPageNr < 0 )
10455         nPageNr = m_nCurrentPage;
10456 
10457     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10458         return -1;
10459 
10460     sal_Int32 nRet = m_aLinks.size();
10461 
10462     m_aLinks.emplace_back( );
10463     m_aLinks.back().m_nObject   = createObject();
10464     m_aLinks.back().m_nPage     = nPageNr;
10465     m_aLinks.back().m_aRect     = rRect;
10466     // convert to default user space now, since the mapmode may change
10467     m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
10468 
10469     // insert link to page's annotation list
10470     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
10471 
10472     return nRet;
10473 }
10474 
10475 sal_Int32 PDFWriterImpl::createScreen(const tools::Rectangle& rRect, sal_Int32 nPageNr)
10476 {
10477     if (nPageNr < 0)
10478         nPageNr = m_nCurrentPage;
10479 
10480     if (nPageNr < 0 || nPageNr >= static_cast<sal_Int32>(m_aPages.size()))
10481         return -1;
10482 
10483     sal_Int32 nRet = m_aScreens.size();
10484 
10485     m_aScreens.emplace_back();
10486     m_aScreens.back().m_nObject = createObject();
10487     m_aScreens.back().m_nPage = nPageNr;
10488     m_aScreens.back().m_aRect = rRect;
10489     // Convert to default user space now, since the mapmode may change.
10490     m_aPages[nPageNr].convertRect(m_aScreens.back().m_aRect);
10491 
10492     // Insert link to page's annotation list.
10493     m_aPages[nPageNr].m_aAnnotations.push_back(m_aScreens.back().m_nObject);
10494 
10495     return nRet;
10496 }
10497 
10498 sal_Int32 PDFWriterImpl::createNamedDest( const OUString& sDestName, const tools::Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10499 {
10500     if( nPageNr < 0 )
10501         nPageNr = m_nCurrentPage;
10502 
10503     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10504         return -1;
10505 
10506     sal_Int32 nRet = m_aNamedDests.size();
10507 
10508     m_aNamedDests.emplace_back( );
10509     m_aNamedDests.back().m_aDestName = sDestName;
10510     m_aNamedDests.back().m_nPage = nPageNr;
10511     m_aNamedDests.back().m_eType = eType;
10512     m_aNamedDests.back().m_aRect = rRect;
10513     // convert to default user space now, since the mapmode may change
10514     m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
10515 
10516     return nRet;
10517 }
10518 
10519 sal_Int32 PDFWriterImpl::createDest( const tools::Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10520 {
10521     if( nPageNr < 0 )
10522         nPageNr = m_nCurrentPage;
10523 
10524     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10525         return -1;
10526 
10527     sal_Int32 nRet = m_aDests.size();
10528 
10529     m_aDests.emplace_back( );
10530     m_aDests.back().m_nPage = nPageNr;
10531     m_aDests.back().m_eType = eType;
10532     m_aDests.back().m_aRect = rRect;
10533     // convert to default user space now, since the mapmode may change
10534     m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
10535 
10536     return nRet;
10537 }
10538 
10539 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const tools::Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10540 {
10541     return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType );
10542 }
10543 
10544 void PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
10545 {
10546     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10547         return;
10548     if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
10549         return;
10550 
10551     m_aLinks[ nLinkId ].m_nDest = nDestId;
10552 }
10553 
10554 void PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
10555 {
10556     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10557         return;
10558 
10559     m_aLinks[ nLinkId ].m_nDest = -1;
10560 
10561     using namespace ::com::sun::star;
10562 
10563     if (!m_xTrans.is())
10564     {
10565         uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
10566         m_xTrans = util::URLTransformer::create(xContext);
10567     }
10568 
10569     util::URL aURL;
10570     aURL.Complete = rURL;
10571 
10572     m_xTrans->parseStrict( aURL );
10573 
10574     m_aLinks[ nLinkId ].m_aURL  = aURL.Complete;
10575 }
10576 
10577 void PDFWriterImpl::setScreenURL(sal_Int32 nScreenId, const OUString& rURL)
10578 {
10579     if (nScreenId < 0 || nScreenId >= static_cast<sal_Int32>(m_aScreens.size()))
10580         return;
10581 
10582     m_aScreens[nScreenId].m_aURL = rURL;
10583 }
10584 
10585 void PDFWriterImpl::setScreenStream(sal_Int32 nScreenId, const OUString& rURL)
10586 {
10587     if (nScreenId < 0 || nScreenId >= static_cast<sal_Int32>(m_aScreens.size()))
10588         return;
10589 
10590     m_aScreens[nScreenId].m_aTempFileURL = rURL;
10591     m_aScreens[nScreenId].m_nTempFileObject = createObject();
10592 }
10593 
10594 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
10595 {
10596     m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
10597 }
10598 
10599 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
10600 {
10601     // create new item
10602     sal_Int32 nNewItem = m_aOutline.size();
10603     m_aOutline.emplace_back( );
10604 
10605     // set item attributes
10606     setOutlineItemParent( nNewItem, nParent );
10607     setOutlineItemText( nNewItem, rText );
10608     setOutlineItemDest( nNewItem, nDestID );
10609 
10610     return nNewItem;
10611 }
10612 
10613 void PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
10614 {
10615     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10616         return;
10617 
10618     if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
10619     {
10620         nNewParent = 0;
10621     }
10622     // insert item to new parent's list of children
10623     m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
10624 }
10625 
10626 void PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
10627 {
10628     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10629         return;
10630 
10631     m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText );
10632 }
10633 
10634 void PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
10635 {
10636     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
10637         return;
10638     if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
10639         return;
10640     m_aOutline[nItem].m_nDestID = nDestID;
10641 }
10642 
10643 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
10644 {
10645     static std::map< PDFWriter::StructElement, const char* > aTagStrings;
10646     if( aTagStrings.empty() )
10647     {
10648         aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
10649         aTagStrings[ PDFWriter::Document ]      = "Document";
10650         aTagStrings[ PDFWriter::Part ]          = "Part";
10651         aTagStrings[ PDFWriter::Article ]       = "Art";
10652         aTagStrings[ PDFWriter::Section ]       = "Sect";
10653         aTagStrings[ PDFWriter::Division ]      = "Div";
10654         aTagStrings[ PDFWriter::BlockQuote ]    = "BlockQuote";
10655         aTagStrings[ PDFWriter::Caption ]       = "Caption";
10656         aTagStrings[ PDFWriter::TOC ]           = "TOC";
10657         aTagStrings[ PDFWriter::TOCI ]          = "TOCI";
10658         aTagStrings[ PDFWriter::Index ]         = "Index";
10659         aTagStrings[ PDFWriter::Paragraph ]     = "P";
10660         aTagStrings[ PDFWriter::Heading ]       = "H";
10661         aTagStrings[ PDFWriter::H1 ]            = "H1";
10662         aTagStrings[ PDFWriter::H2 ]            = "H2";
10663         aTagStrings[ PDFWriter::H3 ]            = "H3";
10664         aTagStrings[ PDFWriter::H4 ]            = "H4";
10665         aTagStrings[ PDFWriter::H5 ]            = "H5";
10666         aTagStrings[ PDFWriter::H6 ]            = "H6";
10667         aTagStrings[ PDFWriter::List ]          = "L";
10668         aTagStrings[ PDFWriter::ListItem ]      = "LI";
10669         aTagStrings[ PDFWriter::LILabel ]       = "Lbl";
10670         aTagStrings[ PDFWriter::LIBody ]        = "LBody";
10671         aTagStrings[ PDFWriter::Table ]         = "Table";
10672         aTagStrings[ PDFWriter::TableRow ]      = "TR";
10673         aTagStrings[ PDFWriter::TableHeader ]   = "TH";
10674         aTagStrings[ PDFWriter::TableData ]     = "TD";
10675         aTagStrings[ PDFWriter::Span ]          = "Span";
10676         aTagStrings[ PDFWriter::Quote ]         = "Quote";
10677         aTagStrings[ PDFWriter::Note ]          = "Note";
10678         aTagStrings[ PDFWriter::Reference ]     = "Reference";
10679         aTagStrings[ PDFWriter::BibEntry ]      = "BibEntry";
10680         aTagStrings[ PDFWriter::Code ]          = "Code";
10681         aTagStrings[ PDFWriter::Link ]          = "Link";
10682         aTagStrings[ PDFWriter::Figure ]        = "Figure";
10683         aTagStrings[ PDFWriter::Formula ]       = "Formula";
10684         aTagStrings[ PDFWriter::Form ]          = "Form";
10685     }
10686 
10687     std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
10688 
10689     return it != aTagStrings.end() ? it->second : "Div";
10690 }
10691 
10692 void PDFWriterImpl::beginStructureElementMCSeq()
10693 {
10694     if( m_bEmitStructure &&
10695         m_nCurrentStructElement > 0 && // StructTreeRoot
10696         ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
10697         )
10698     {
10699         PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
10700         OStringBuffer aLine( 128 );
10701         sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
10702         aLine.append( "/" );
10703         if( !rEle.m_aAlias.isEmpty() )
10704             aLine.append( rEle.m_aAlias );
10705         else
10706             aLine.append( getStructureTag( rEle.m_eType ) );
10707         aLine.append( "<</MCID " );
10708         aLine.append( nMCID );
10709         aLine.append( ">>BDC\n" );
10710         writeBuffer( aLine.getStr(), aLine.getLength() );
10711 
10712         // update the element's content list
10713         SAL_INFO("vcl.pdfwriter", "beginning marked content id " << nMCID << " on page object "
10714                  << m_aPages[ m_nCurrentPage ].m_nPageObject << ", structure first page = "
10715                  << rEle.m_nFirstPageObject);
10716         rEle.m_aKids.emplace_back( nMCID, m_aPages[m_nCurrentPage].m_nPageObject );
10717         // update the page's mcid parent list
10718         m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
10719         // mark element MC sequence as open
10720         rEle.m_bOpenMCSeq = true;
10721     }
10722     // handle artifacts
10723     else if( ! m_bEmitStructure && m_aContext.Tagged &&
10724                m_nCurrentStructElement > 0 &&
10725                m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
10726              ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
10727              )
10728     {
10729         OStringBuffer aLine( 128 );
10730         aLine.append( "/Artifact BMC\n" );
10731         writeBuffer( aLine.getStr(), aLine.getLength() );
10732         // mark element MC sequence as open
10733         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
10734     }
10735 }
10736 
10737 void PDFWriterImpl::endStructureElementMCSeq()
10738 {
10739     if( m_nCurrentStructElement > 0 && // StructTreeRoot
10740         ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
10741         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
10742         )
10743     {
10744         writeBuffer( "EMC\n", 4 );
10745         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
10746     }
10747 }
10748 
10749 bool PDFWriterImpl::checkEmitStructure()
10750 {
10751     bool bEmit = false;
10752     if( m_aContext.Tagged )
10753     {
10754         bEmit = true;
10755         sal_Int32 nEle = m_nCurrentStructElement;
10756         while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
10757         {
10758             if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
10759             {
10760                 bEmit = false;
10761                 break;
10762             }
10763             nEle = m_aStructure[ nEle ].m_nParentElement;
10764         }
10765     }
10766     return bEmit;
10767 }
10768 
10769 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const OUString& rAlias )
10770 {
10771     if( m_nCurrentPage < 0 )
10772         return -1;
10773 
10774     if( ! m_aContext.Tagged )
10775         return -1;
10776 
10777     // close eventual current MC sequence
10778     endStructureElementMCSeq();
10779 
10780     if( m_nCurrentStructElement == 0 &&
10781         eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
10782     {
10783         // struct tree root hit, but not beginning document
10784         // this might happen with setCurrentStructureElement
10785         // silently insert structure into document again if one properly exists
10786         if( ! m_aStructure[ 0 ].m_aChildren.empty() )
10787         {
10788             PDFWriter::StructElement childType = PDFWriter::NonStructElement;
10789             sal_Int32 nNewCurElement = 0;
10790             const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
10791             for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
10792                  childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
10793             {
10794                 nNewCurElement = *it;
10795                 childType = m_aStructure[ nNewCurElement ].m_eType;
10796             }
10797             if( childType == PDFWriter::Document )
10798             {
10799                 m_nCurrentStructElement = nNewCurElement;
10800                 SAL_WARN( "vcl.pdfwriter", "Structure element inserted to StructTreeRoot that is not a document" );
10801             }
10802             else {
10803                 OSL_FAIL( "document structure in disorder !" );
10804             }
10805         }
10806         else {
10807             OSL_FAIL( "PDF document structure MUST be contained in a Document element" );
10808         }
10809     }
10810 
10811     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
10812     m_aStructure.emplace_back( );
10813     PDFStructureElement& rEle = m_aStructure.back();
10814     rEle.m_eType            = eType;
10815     rEle.m_nOwnElement      = nNewId;
10816     rEle.m_nParentElement   = m_nCurrentStructElement;
10817     rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject;
10818     m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
10819     m_nCurrentStructElement = nNewId;
10820 
10821     // handle alias names
10822     if( !rAlias.isEmpty() && eType != PDFWriter::NonStructElement )
10823     {
10824         OStringBuffer aNameBuf( rAlias.getLength() );
10825         appendName( rAlias, aNameBuf );
10826         OString aAliasName( aNameBuf.makeStringAndClear() );
10827         rEle.m_aAlias = aAliasName;
10828         m_aRoleMap[ aAliasName ] = getStructureTag( eType );
10829     }
10830 
10831     if (g_bDebugDisableCompression)
10832     {
10833         OStringBuffer aLine( "beginStructureElement " );
10834         aLine.append( m_nCurrentStructElement );
10835         aLine.append( ": " );
10836         aLine.append( getStructureTag( eType ) );
10837         if( !rEle.m_aAlias.isEmpty() )
10838         {
10839             aLine.append( " aliased as \"" );
10840             aLine.append( rEle.m_aAlias );
10841             aLine.append( '\"' );
10842         }
10843         emitComment( aLine.getStr() );
10844     }
10845 
10846     // check whether to emit structure henceforth
10847     m_bEmitStructure = checkEmitStructure();
10848 
10849     if( m_bEmitStructure ) // don't create nonexistent objects
10850     {
10851         rEle.m_nObject      = createObject();
10852         // update parent's kids list
10853         m_aStructure[ rEle.m_nParentElement ].m_aKids.emplace_back(rEle.m_nObject);
10854     }
10855     return nNewId;
10856 }
10857 
10858 void PDFWriterImpl::endStructureElement()
10859 {
10860     if( m_nCurrentPage < 0 )
10861         return;
10862 
10863     if( ! m_aContext.Tagged )
10864         return;
10865 
10866     if( m_nCurrentStructElement == 0 )
10867     {
10868         // hit the struct tree root, that means there is an endStructureElement
10869         // without corresponding beginStructureElement
10870         return;
10871     }
10872 
10873     // end the marked content sequence
10874     endStructureElementMCSeq();
10875 
10876     OStringBuffer aLine;
10877     if (g_bDebugDisableCompression)
10878     {
10879         aLine.append( "endStructureElement " );
10880         aLine.append( m_nCurrentStructElement );
10881         aLine.append( ": " );
10882         aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
10883         if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
10884         {
10885             aLine.append( " aliased as \"" );
10886             aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
10887             aLine.append( '\"' );
10888         }
10889     }
10890 
10891     // "end" the structure element, the parent becomes current element
10892     m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
10893 
10894     // check whether to emit structure henceforth
10895     m_bEmitStructure = checkEmitStructure();
10896 
10897     if (g_bDebugDisableCompression && m_bEmitStructure)
10898     {
10899         emitComment( aLine.getStr() );
10900     }
10901 }
10902 
10903 /*
10904  * This function adds an internal structure list container to overcome the 8191 elements array limitation
10905  * in kids element emission.
10906  * Recursive function
10907  *
10908  */
10909 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
10910 {
10911     if( rEle.m_eType == PDFWriter::NonStructElement &&
10912         rEle.m_nOwnElement != rEle.m_nParentElement )
10913         return;
10914 
10915     for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
10916     {
10917         if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
10918         {
10919             PDFStructureElement& rChild = m_aStructure[ *it ];
10920             if( rChild.m_eType != PDFWriter::NonStructElement )
10921             {
10922                 //triggered when a child of the rEle element is found
10923                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
10924                     addInternalStructureContainer( rChild );//examine the child
10925                 else
10926                 {
10927                     OSL_FAIL( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
10928                     SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::addInternalStructureContainer: invalid child structure element with id " << *it );
10929                 }
10930             }
10931         }
10932         else
10933         {
10934             OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
10935             SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::addInternalStructureContainer: invalid child structure id " << *it );
10936         }
10937     }
10938 
10939     if( rEle.m_nOwnElement != rEle.m_nParentElement )
10940     {
10941         if( !rEle.m_aKids.empty() )
10942         {
10943             if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
10944                 //then we need to add the containers for the kids elements
10945                 // a list to be used for the new kid element
10946                 std::list< PDFStructureElementKid > aNewKids;
10947                 std::list< sal_Int32 > aNewChildren;
10948 
10949                 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
10950                 OString aAliasName( "Div" );
10951                 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
10952 
10953                 while( rEle.m_aKids.size() > ncMaxPDFArraySize )
10954                 {
10955                     sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
10956                     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
10957                     m_aStructure.emplace_back( );
10958                     PDFStructureElement& rEleNew = m_aStructure.back();
10959                     rEleNew.m_aAlias            = aAliasName;
10960                     rEleNew.m_eType             = PDFWriter::Division; // a new Div type container
10961                     rEleNew.m_nOwnElement       = nNewId;
10962                     rEleNew.m_nParentElement    = nCurrentStructElement;
10963                     //inherit the same page as the first child to be reparented
10964                     rEleNew.m_nFirstPageObject  = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
10965                     rEleNew.m_nObject           = createObject();//assign a PDF object number
10966                     //add the object to the kid list of the parent
10967                     aNewKids.emplace_back( rEleNew.m_nObject );
10968                     aNewChildren.push_back( nNewId );
10969 
10970                     std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
10971                     std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
10972                     advance( aChildEndIt, ncMaxPDFArraySize );
10973                     advance( aKidEndIt, ncMaxPDFArraySize );
10974 
10975                     rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
10976                                             rEle.m_aKids,
10977                                             rEle.m_aKids.begin(),
10978                                             aKidEndIt );
10979                     rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
10980                                                 rEle.m_aChildren,
10981                                                 rEle.m_aChildren.begin(),
10982                                                 aChildEndIt );
10983                     // set the kid's new parent
10984                     for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
10985                          it != rEleNew.m_aChildren.end(); ++it )
10986                     {
10987                         m_aStructure[ *it ].m_nParentElement = nNewId;
10988                     }
10989                 }
10990                 //finally add the new kids resulting from the container added
10991                 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
10992                 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
10993             }
10994         }
10995     }
10996 }
10997 
10998 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
10999 {
11000     bool bSuccess = false;
11001 
11002     if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
11003     {
11004         // end eventual previous marked content sequence
11005         endStructureElementMCSeq();
11006 
11007         m_nCurrentStructElement = nEle;
11008         m_bEmitStructure = checkEmitStructure();
11009         if (g_bDebugDisableCompression)
11010         {
11011             OStringBuffer aLine( "setCurrentStructureElement " );
11012             aLine.append( m_nCurrentStructElement );
11013             aLine.append( ": " );
11014             aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11015             if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
11016             {
11017                 aLine.append( " aliased as \"" );
11018                 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11019                 aLine.append( '\"' );
11020             }
11021             if( ! m_bEmitStructure )
11022                 aLine.append( " (inside NonStruct)" );
11023             emitComment( aLine.getStr() );
11024         }
11025         bSuccess = true;
11026     }
11027 
11028     return bSuccess;
11029 }
11030 
11031 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
11032 {
11033     if( !m_aContext.Tagged )
11034         return false;
11035 
11036     bool bInsert = false;
11037     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11038     {
11039         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11040         switch( eAttr )
11041         {
11042             case PDFWriter::Placement:
11043                 if( eVal == PDFWriter::Block        ||
11044                     eVal == PDFWriter::Inline       ||
11045                     eVal == PDFWriter::Before       ||
11046                     eVal == PDFWriter::Start        ||
11047                     eVal == PDFWriter::End )
11048                     bInsert = true;
11049                 break;
11050             case PDFWriter::WritingMode:
11051                 if( eVal == PDFWriter::LrTb         ||
11052                     eVal == PDFWriter::RlTb         ||
11053                     eVal == PDFWriter::TbRl )
11054                 {
11055                     bInsert = true;
11056                 }
11057                 break;
11058             case PDFWriter::TextAlign:
11059                 if( eVal == PDFWriter::Start        ||
11060                     eVal == PDFWriter::Center       ||
11061                     eVal == PDFWriter::End          ||
11062                     eVal == PDFWriter::Justify )
11063                 {
11064                     if( eType == PDFWriter::Paragraph   ||
11065                         eType == PDFWriter::Heading     ||
11066                         eType == PDFWriter::H1          ||
11067                         eType == PDFWriter::H2          ||
11068                         eType == PDFWriter::H3          ||
11069                         eType == PDFWriter::H4          ||
11070                         eType == PDFWriter::H5          ||
11071                         eType == PDFWriter::H6          ||
11072                         eType == PDFWriter::List        ||
11073                         eType == PDFWriter::ListItem    ||
11074                         eType == PDFWriter::LILabel     ||
11075                         eType == PDFWriter::LIBody      ||
11076                         eType == PDFWriter::Table       ||
11077                         eType == PDFWriter::TableRow    ||
11078                         eType == PDFWriter::TableHeader ||
11079                         eType == PDFWriter::TableData )
11080                     {
11081                         bInsert = true;
11082                     }
11083                 }
11084                 break;
11085             case PDFWriter::Width:
11086             case PDFWriter::Height:
11087                 if( eVal == PDFWriter::Auto )
11088                 {
11089                     if( eType == PDFWriter::Figure      ||
11090                         eType == PDFWriter::Formula     ||
11091                         eType == PDFWriter::Form        ||
11092                         eType == PDFWriter::Table       ||
11093                         eType == PDFWriter::TableHeader ||
11094                         eType == PDFWriter::TableData )
11095                     {
11096                         bInsert = true;
11097                     }
11098                 }
11099                 break;
11100             case PDFWriter::BlockAlign:
11101                 if( eVal == PDFWriter::Before       ||
11102                     eVal == PDFWriter::Middle       ||
11103                     eVal == PDFWriter::After        ||
11104                     eVal == PDFWriter::Justify )
11105                 {
11106                     if( eType == PDFWriter::TableHeader ||
11107                         eType == PDFWriter::TableData )
11108                     {
11109                         bInsert = true;
11110                     }
11111                 }
11112                 break;
11113             case PDFWriter::InlineAlign:
11114                 if( eVal == PDFWriter::Start        ||
11115                     eVal == PDFWriter::Center       ||
11116                     eVal == PDFWriter::End )
11117                 {
11118                     if( eType == PDFWriter::TableHeader ||
11119                         eType == PDFWriter::TableData )
11120                     {
11121                         bInsert = true;
11122                     }
11123                 }
11124                 break;
11125             case PDFWriter::LineHeight:
11126                 if( eVal == PDFWriter::Normal       ||
11127                     eVal == PDFWriter::Auto )
11128                 {
11129                     // only for ILSE and BLSE
11130                     if( eType == PDFWriter::Paragraph   ||
11131                         eType == PDFWriter::Heading     ||
11132                         eType == PDFWriter::H1          ||
11133                         eType == PDFWriter::H2          ||
11134                         eType == PDFWriter::H3          ||
11135                         eType == PDFWriter::H4          ||
11136                         eType == PDFWriter::H5          ||
11137                         eType == PDFWriter::H6          ||
11138                         eType == PDFWriter::List        ||
11139                         eType == PDFWriter::ListItem    ||
11140                         eType == PDFWriter::LILabel     ||
11141                         eType == PDFWriter::LIBody      ||
11142                         eType == PDFWriter::Table       ||
11143                         eType == PDFWriter::TableRow    ||
11144                         eType == PDFWriter::TableHeader ||
11145                         eType == PDFWriter::TableData   ||
11146                         eType == PDFWriter::Span        ||
11147                         eType == PDFWriter::Quote       ||
11148                         eType == PDFWriter::Note        ||
11149                         eType == PDFWriter::Reference   ||
11150                         eType == PDFWriter::BibEntry    ||
11151                         eType == PDFWriter::Code        ||
11152                         eType == PDFWriter::Link )
11153                     {
11154                         bInsert = true;
11155                     }
11156                 }
11157                 break;
11158             case PDFWriter::TextDecorationType:
11159                 if( eVal == PDFWriter::NONE         ||
11160                     eVal == PDFWriter::Underline    ||
11161                     eVal == PDFWriter::Overline     ||
11162                     eVal == PDFWriter::LineThrough )
11163                 {
11164                     // only for ILSE and BLSE
11165                     if( eType == PDFWriter::Paragraph   ||
11166                         eType == PDFWriter::Heading     ||
11167                         eType == PDFWriter::H1          ||
11168                         eType == PDFWriter::H2          ||
11169                         eType == PDFWriter::H3          ||
11170                         eType == PDFWriter::H4          ||
11171                         eType == PDFWriter::H5          ||
11172                         eType == PDFWriter::H6          ||
11173                         eType == PDFWriter::List        ||
11174                         eType == PDFWriter::ListItem    ||
11175                         eType == PDFWriter::LILabel     ||
11176                         eType == PDFWriter::LIBody      ||
11177                         eType == PDFWriter::Table       ||
11178                         eType == PDFWriter::TableRow    ||
11179                         eType == PDFWriter::TableHeader ||
11180                         eType == PDFWriter::TableData   ||
11181                         eType == PDFWriter::Span        ||
11182                         eType == PDFWriter::Quote       ||
11183                         eType == PDFWriter::Note        ||
11184                         eType == PDFWriter::Reference   ||
11185                         eType == PDFWriter::BibEntry    ||
11186                         eType == PDFWriter::Code        ||
11187                         eType == PDFWriter::Link )
11188                     {
11189                         bInsert = true;
11190                     }
11191                 }
11192                 break;
11193             case PDFWriter::ListNumbering:
11194                 if( eVal == PDFWriter::NONE         ||
11195                     eVal == PDFWriter::Disc         ||
11196                     eVal == PDFWriter::Circle       ||
11197                     eVal == PDFWriter::Square       ||
11198                     eVal == PDFWriter::Decimal      ||
11199                     eVal == PDFWriter::UpperRoman   ||
11200                     eVal == PDFWriter::LowerRoman   ||
11201                     eVal == PDFWriter::UpperAlpha   ||
11202                     eVal == PDFWriter::LowerAlpha )
11203                 {
11204                     if( eType == PDFWriter::List )
11205                         bInsert = true;
11206                 }
11207                 break;
11208             default: break;
11209         }
11210     }
11211 
11212     if( bInsert )
11213         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
11214     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11215         SAL_INFO("vcl.pdfwriter",
11216                  "rejecting setStructureAttribute( " << getAttributeTag( eAttr )
11217                  << ", " << getAttributeValueTag( eVal )
11218                  << " ) on " << getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType )
11219                  << " (" << m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
11220                  << ") element");
11221 
11222     return bInsert;
11223 }
11224 
11225 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
11226 {
11227     if( ! m_aContext.Tagged )
11228         return false;
11229 
11230     bool bInsert = false;
11231     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11232     {
11233         if( eAttr == PDFWriter::Language )
11234         {
11235             m_aStructure[ m_nCurrentStructElement ].m_aLocale = LanguageTag( LanguageType(nValue) ).getLocale();
11236             return true;
11237         }
11238 
11239         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11240         switch( eAttr )
11241         {
11242             case PDFWriter::SpaceBefore:
11243             case PDFWriter::SpaceAfter:
11244             case PDFWriter::StartIndent:
11245             case PDFWriter::EndIndent:
11246                 // just for BLSE
11247                 if( eType == PDFWriter::Paragraph   ||
11248                     eType == PDFWriter::Heading     ||
11249                     eType == PDFWriter::H1          ||
11250                     eType == PDFWriter::H2          ||
11251                     eType == PDFWriter::H3          ||
11252                     eType == PDFWriter::H4          ||
11253                     eType == PDFWriter::H5          ||
11254                     eType == PDFWriter::H6          ||
11255                     eType == PDFWriter::List        ||
11256                     eType == PDFWriter::ListItem    ||
11257                     eType == PDFWriter::LILabel     ||
11258                     eType == PDFWriter::LIBody      ||
11259                     eType == PDFWriter::Table       ||
11260                     eType == PDFWriter::TableRow    ||
11261                     eType == PDFWriter::TableHeader ||
11262                     eType == PDFWriter::TableData )
11263                 {
11264                     bInsert = true;
11265                 }
11266                 break;
11267             case PDFWriter::TextIndent:
11268                 // paragraph like BLSE and additional elements
11269                 if( eType == PDFWriter::Paragraph   ||
11270                     eType == PDFWriter::Heading     ||
11271                     eType == PDFWriter::H1          ||
11272                     eType == PDFWriter::H2          ||
11273                     eType == PDFWriter::H3          ||
11274                     eType == PDFWriter::H4          ||
11275                     eType == PDFWriter::H5          ||
11276                     eType == PDFWriter::H6          ||
11277                     eType == PDFWriter::LILabel     ||
11278                     eType == PDFWriter::LIBody      ||
11279                     eType == PDFWriter::TableHeader ||
11280                     eType == PDFWriter::TableData )
11281                 {
11282                     bInsert = true;
11283                 }
11284                 break;
11285             case PDFWriter::Width:
11286             case PDFWriter::Height:
11287                 if( eType == PDFWriter::Figure      ||
11288                     eType == PDFWriter::Formula     ||
11289                     eType == PDFWriter::Form        ||
11290                     eType == PDFWriter::Table       ||
11291                     eType == PDFWriter::TableHeader ||
11292                     eType == PDFWriter::TableData )
11293                 {
11294                     bInsert = true;
11295                 }
11296                 break;
11297             case PDFWriter::LineHeight:
11298             case PDFWriter::BaselineShift:
11299                 // only for ILSE and BLSE
11300                 if( eType == PDFWriter::Paragraph   ||
11301                     eType == PDFWriter::Heading     ||
11302                     eType == PDFWriter::H1          ||
11303                     eType == PDFWriter::H2          ||
11304                     eType == PDFWriter::H3          ||
11305                     eType == PDFWriter::H4          ||
11306                     eType == PDFWriter::H5          ||
11307                     eType == PDFWriter::H6          ||
11308                     eType == PDFWriter::List        ||
11309                     eType == PDFWriter::ListItem    ||
11310                     eType == PDFWriter::LILabel     ||
11311                     eType == PDFWriter::LIBody      ||
11312                     eType == PDFWriter::Table       ||
11313                     eType == PDFWriter::TableRow    ||
11314                     eType == PDFWriter::TableHeader ||
11315                     eType == PDFWriter::TableData   ||
11316                     eType == PDFWriter::Span        ||
11317                     eType == PDFWriter::Quote       ||
11318                     eType == PDFWriter::Note        ||
11319                     eType == PDFWriter::Reference   ||
11320                     eType == PDFWriter::BibEntry    ||
11321                     eType == PDFWriter::Code        ||
11322                     eType == PDFWriter::Link )
11323                 {
11324                         bInsert = true;
11325                 }
11326                 break;
11327             case PDFWriter::RowSpan:
11328             case PDFWriter::ColSpan:
11329                 // only for table cells
11330                 if( eType == PDFWriter::TableHeader ||
11331                     eType == PDFWriter::TableData )
11332                 {
11333                     bInsert = true;
11334                 }
11335                 break;
11336             case PDFWriter::LinkAnnotation:
11337                 if( eType == PDFWriter::Link )
11338                     bInsert = true;
11339                 break;
11340             default: break;
11341         }
11342     }
11343 
11344     if( bInsert )
11345         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
11346     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11347         SAL_INFO("vcl.pdfwriter",
11348                  "rejecting setStructureAttributeNumerical( " << getAttributeTag( eAttr )
11349                  << ", " << (int)nValue
11350                  << " ) on " << getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType )
11351                  << " (" << m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
11352                  << ") element");
11353 
11354     return bInsert;
11355 }
11356 
11357 void PDFWriterImpl::setStructureBoundingBox( const tools::Rectangle& rRect )
11358 {
11359     sal_Int32 nPageNr = m_nCurrentPage;
11360     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
11361         return;
11362 
11363     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11364     {
11365         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11366         if( eType == PDFWriter::Figure      ||
11367             eType == PDFWriter::Formula     ||
11368             eType == PDFWriter::Form        ||
11369             eType == PDFWriter::Table )
11370         {
11371             m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
11372             // convert to default user space now, since the mapmode may change
11373             m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
11374         }
11375     }
11376 }
11377 
11378 void PDFWriterImpl::setActualText( const OUString& rText )
11379 {
11380     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11381     {
11382         m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
11383     }
11384 }
11385 
11386 void PDFWriterImpl::setAlternateText( const OUString& rText )
11387 {
11388     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11389     {
11390         m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
11391     }
11392 }
11393 
11394 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
11395 {
11396     if( nPageNr < 0 )
11397         nPageNr = m_nCurrentPage;
11398 
11399     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11400         return;
11401 
11402     m_aPages[ nPageNr ].m_nDuration = nSeconds;
11403 }
11404 
11405 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
11406 {
11407     if( nPageNr < 0 )
11408         nPageNr = m_nCurrentPage;
11409 
11410     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11411         return;
11412 
11413     m_aPages[ nPageNr ].m_eTransition   = eType;
11414     m_aPages[ nPageNr ].m_nTransTime    = nMilliSec;
11415 }
11416 
11417 void PDFWriterImpl::ensureUniqueRadioOnValues()
11418 {
11419     // loop over radio groups
11420     for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
11421          group != m_aRadioGroupWidgets.end(); ++group )
11422     {
11423         PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
11424         // check whether all kids have a unique OnValue
11425         std::unordered_map< OUString, sal_Int32 > aOnValues;
11426         int nChildren = rGroupWidget.m_aKidsIndex.size();
11427         bool bIsUnique = true;
11428         for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
11429         {
11430             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11431             const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
11432             SAL_INFO("vcl.pdfwriter", "OnValue: " << rVal);
11433             if( aOnValues.find( rVal ) == aOnValues.end() )
11434             {
11435                 aOnValues[ rVal ] = 1;
11436             }
11437             else
11438             {
11439                 bIsUnique = false;
11440             }
11441         }
11442         if( ! bIsUnique )
11443         {
11444             SAL_INFO("vcl.pdfwriter", "enforcing unique OnValues" );
11445             // make unique by using ascending OnValues
11446             for( int nKid = 0; nKid < nChildren; nKid++ )
11447             {
11448                 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11449                 PDFWidget& rKid = m_aWidgets[nKidIndex];
11450                 rKid.m_aOnValue = OUString::number( nKid+1 );
11451                 if( rKid.m_aValue != "Off" )
11452                     rKid.m_aValue = rKid.m_aOnValue;
11453             }
11454         }
11455         // finally move the "Yes" appearance to the OnValue appearance
11456         for( int nKid = 0; nKid < nChildren; nKid++ )
11457         {
11458             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11459             PDFWidget& rKid = m_aWidgets[nKidIndex];
11460             PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
11461             if( app_it != rKid.m_aAppearances.end() )
11462             {
11463                 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
11464                 if( stream_it != app_it->second.end() )
11465                 {
11466                     SvMemoryStream* pStream = stream_it->second;
11467                     app_it->second.erase( stream_it );
11468                     OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
11469                     appendName( rKid.m_aOnValue, aBuf );
11470                     (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11471                 }
11472                 else
11473                     SAL_INFO("vcl.pdfwriter", "error: RadioButton without \"Yes\" stream" );
11474             }
11475             // update selected radio button
11476             if( rKid.m_aValue != "Off" )
11477             {
11478                 rGroupWidget.m_aValue = rKid.m_aValue;
11479             }
11480         }
11481     }
11482 }
11483 
11484 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
11485 {
11486     sal_Int32 nRadioGroupWidget = -1;
11487 
11488     std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
11489 
11490     if( it == m_aRadioGroupWidgets.end() )
11491     {
11492         m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
11493             sal_Int32(m_aWidgets.size());
11494 
11495         // new group, insert the radiobutton
11496         m_aWidgets.emplace_back( );
11497         m_aWidgets.back().m_nObject     = createObject();
11498         m_aWidgets.back().m_nPage       = m_nCurrentPage;
11499         m_aWidgets.back().m_eType       = PDFWriter::RadioButton;
11500         m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
11501         m_aWidgets.back().m_nFlags |= 0x0000C000;   // NoToggleToOff and Radio bits
11502 
11503         createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn );
11504     }
11505     else
11506         nRadioGroupWidget = it->second;
11507 
11508     return nRadioGroupWidget;
11509 }
11510 
11511 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
11512 {
11513     if( nPageNr < 0 )
11514         nPageNr = m_nCurrentPage;
11515 
11516     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11517         return -1;
11518 
11519     bool sigHidden(true);
11520     sal_Int32 nNewWidget = m_aWidgets.size();
11521     m_aWidgets.emplace_back( );
11522 
11523     m_aWidgets.back().m_nObject         = createObject();
11524     m_aWidgets.back().m_aRect           = rControl.Location;
11525     m_aWidgets.back().m_nPage           = nPageNr;
11526     m_aWidgets.back().m_eType           = rControl.getType();
11527 
11528     sal_Int32 nRadioGroupWidget = -1;
11529     // for unknown reasons the radio buttons of a radio group must not have a
11530     // field name, else the buttons are in fact check boxes -
11531     // that is multiple buttons of the radio group can be selected
11532     if( rControl.getType() == PDFWriter::RadioButton )
11533         nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
11534     else
11535     {
11536         createWidgetFieldName( nNewWidget, rControl );
11537     }
11538 
11539     // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
11540     PDFWidget& rNewWidget           = m_aWidgets[nNewWidget];
11541     rNewWidget.m_aDescription       = rControl.Description;
11542     rNewWidget.m_aText              = rControl.Text;
11543     rNewWidget.m_nTextStyle         = rControl.TextStyle &
11544         (  DrawTextFlags::Left | DrawTextFlags::Center | DrawTextFlags::Right | DrawTextFlags::Top |
11545            DrawTextFlags::VCenter | DrawTextFlags::Bottom |
11546            DrawTextFlags::MultiLine | DrawTextFlags::WordBreak  );
11547     rNewWidget.m_nTabOrder          = rControl.TabOrder;
11548 
11549     // various properties are set via the flags (/Ff) property of the field dict
11550     if( rControl.ReadOnly )
11551         rNewWidget.m_nFlags |= 1;
11552     if( rControl.getType() == PDFWriter::PushButton )
11553     {
11554         const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
11555         if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11556             rNewWidget.m_nTextStyle =
11557                 DrawTextFlags::Center | DrawTextFlags::VCenter |
11558                 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
11559 
11560         rNewWidget.m_nFlags |= 0x00010000;
11561         if( !rBtn.URL.isEmpty() )
11562             rNewWidget.m_aListEntries.push_back( rBtn.URL );
11563         rNewWidget.m_bSubmit    = rBtn.Submit;
11564         rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
11565         rNewWidget.m_nDest      = rBtn.Dest;
11566         createDefaultPushButtonAppearance( rNewWidget, rBtn );
11567     }
11568     else if( rControl.getType() == PDFWriter::RadioButton )
11569     {
11570         const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
11571         if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11572             rNewWidget.m_nTextStyle =
11573                 DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
11574         /*  PDF sees a RadioButton group as one radio button with
11575          *  children which are in turn check boxes
11576          *
11577          *  so we need to create a radio button on demand for a new group
11578          *  and insert a checkbox for each RadioButtonWidget as its child
11579          */
11580         rNewWidget.m_eType          = PDFWriter::CheckBox;
11581         rNewWidget.m_nRadioGroup    = rBtn.RadioGroup;
11582 
11583         SAL_WARN_IF( nRadioGroupWidget < 0 || nRadioGroupWidget >= (sal_Int32)m_aWidgets.size(), "vcl.pdfwriter", "no radio group parent" );
11584 
11585         PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
11586         rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
11587         rRadioButton.m_aKidsIndex.push_back( nNewWidget );
11588         rNewWidget.m_nParent = rRadioButton.m_nObject;
11589 
11590         rNewWidget.m_aValue     = "Off";
11591         rNewWidget.m_aOnValue   = rBtn.OnValue;
11592         if( rRadioButton.m_aValue.isEmpty() && rBtn.Selected )
11593         {
11594             rNewWidget.m_aValue     = rNewWidget.m_aOnValue;
11595             rRadioButton.m_aValue   = rNewWidget.m_aOnValue;
11596         }
11597         createDefaultRadioButtonAppearance( rNewWidget, rBtn );
11598 
11599         // union rect of radio group
11600         tools::Rectangle aRect = rNewWidget.m_aRect;
11601         m_aPages[ nPageNr ].convertRect( aRect );
11602         rRadioButton.m_aRect.Union( aRect );
11603     }
11604     else if( rControl.getType() == PDFWriter::CheckBox )
11605     {
11606         const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
11607         if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11608             rNewWidget.m_nTextStyle =
11609                 DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
11610 
11611         rNewWidget.m_aValue = rBox.Checked ? OUString("Yes") : OUString("Off" );
11612         // create default appearance before m_aRect gets transformed
11613         createDefaultCheckBoxAppearance( rNewWidget, rBox );
11614     }
11615     else if( rControl.getType() == PDFWriter::ListBox )
11616     {
11617         if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11618             rNewWidget.m_nTextStyle = DrawTextFlags::VCenter;
11619 
11620         const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
11621         rNewWidget.m_aListEntries     = rLstBox.Entries;
11622         rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
11623         rNewWidget.m_aValue           = rLstBox.Text;
11624         if( rLstBox.DropDown )
11625             rNewWidget.m_nFlags |= 0x00020000;
11626         if( rLstBox.MultiSelect && !rLstBox.DropDown && m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 )
11627             rNewWidget.m_nFlags |= 0x00200000;
11628 
11629         createDefaultListBoxAppearance( rNewWidget, rLstBox );
11630     }
11631     else if( rControl.getType() == PDFWriter::ComboBox )
11632     {
11633         if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11634             rNewWidget.m_nTextStyle = DrawTextFlags::VCenter;
11635 
11636         const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
11637         rNewWidget.m_aValue         = rBox.Text;
11638         rNewWidget.m_aListEntries   = rBox.Entries;
11639         rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
11640 
11641         PDFWriter::ListBoxWidget aLBox;
11642         aLBox.Name              = rBox.Name;
11643         aLBox.Description       = rBox.Description;
11644         aLBox.Text              = rBox.Text;
11645         aLBox.TextStyle         = rBox.TextStyle;
11646         aLBox.ReadOnly          = rBox.ReadOnly;
11647         aLBox.Border            = rBox.Border;
11648         aLBox.BorderColor       = rBox.BorderColor;
11649         aLBox.Background        = rBox.Background;
11650         aLBox.BackgroundColor   = rBox.BackgroundColor;
11651         aLBox.TextFont          = rBox.TextFont;
11652         aLBox.TextColor         = rBox.TextColor;
11653         aLBox.DropDown          = true;
11654         aLBox.MultiSelect       = false;
11655         aLBox.Entries           = rBox.Entries;
11656 
11657         createDefaultListBoxAppearance( rNewWidget, aLBox );
11658     }
11659     else if( rControl.getType() == PDFWriter::Edit )
11660     {
11661         if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
11662             rNewWidget.m_nTextStyle = DrawTextFlags::Left | DrawTextFlags::VCenter;
11663 
11664         const PDFWriter::EditWidget& rEdit = static_cast<const  PDFWriter::EditWidget&>(rControl);
11665         if( rEdit.MultiLine )
11666         {
11667             rNewWidget.m_nFlags |= 0x00001000;
11668             rNewWidget.m_nTextStyle |= DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
11669         }
11670         if( rEdit.Password )
11671             rNewWidget.m_nFlags |= 0x00002000;
11672         if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 )
11673             rNewWidget.m_nFlags |= 0x00100000;
11674         rNewWidget.m_nMaxLen = rEdit.MaxLen;
11675         rNewWidget.m_aValue = rEdit.Text;
11676 
11677         createDefaultEditAppearance( rNewWidget, rEdit );
11678     }
11679 #if HAVE_FEATURE_NSS
11680     else if( rControl.getType() == PDFWriter::Signature)
11681     {
11682         sigHidden = true;
11683 
11684         rNewWidget.m_aRect = tools::Rectangle(0, 0, 0, 0);
11685 
11686         m_nSignatureObject = createObject();
11687         rNewWidget.m_aValue = OUString::number( m_nSignatureObject );
11688         rNewWidget.m_aValue += " 0 R";
11689         // let's add a fake appearance
11690         rNewWidget.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
11691     }
11692 #endif
11693 
11694     // if control is a hidden signature, do not convert coordinates since we
11695     // need /Rect [ 0 0 0 0 ]
11696     if ( ! ( ( rControl.getType() == PDFWriter::Signature ) && sigHidden ) )
11697     {
11698         // convert to default user space now, since the mapmode may change
11699         // note: create default appearances before m_aRect gets transformed
11700         m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
11701     }
11702 
11703     // insert widget to page's annotation list
11704     m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
11705 
11706     return nNewWidget;
11707 }
11708 
11709 void PDFWriterImpl::addStream( const OUString& rMimeType, PDFOutputStream* pStream )
11710 {
11711     if( pStream )
11712     {
11713         m_aAdditionalStreams.emplace_back( );
11714         PDFAddStream& rStream = m_aAdditionalStreams.back();
11715         rStream.m_aMimeType = !rMimeType.isEmpty()
11716                               ? rMimeType
11717                               : OUString( "application/octet-stream"  );
11718         rStream.m_pStream = pStream;
11719         rStream.m_bCompress = false;
11720     }
11721 }
11722 
11723 void PDFWriterImpl::MARK( const char* pString )
11724 {
11725     beginStructureElementMCSeq();
11726     if (g_bDebugDisableCompression)
11727         emitComment( pString );
11728 }
11729 
11730 sal_Int32 PDFWriterImpl::ReferenceXObjectEmit::getObject() const
11731 {
11732     if (m_nFormObject > 0)
11733         return m_nFormObject;
11734     else
11735         return m_nBitmapObject;
11736 }
11737 
11738 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
11739