1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <sal/types.h>
27 #if defined __GNUC__ && __cplusplus > 201402L
28 #pragma GCC diagnostic push
29 #pragma GCC diagnostic ignored "-Wregister"
32 #if defined __GNUC__ && __cplusplus > 201402L
33 #pragma GCC diagnostic pop
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>
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>
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 <vcl/bitmapex.hxx>
65 #include <vcl/bitmapaccess.hxx>
66 #include <vcl/cvtgrf.hxx>
67 #include <vcl/image.hxx>
68 #include <vcl/lineinfo.hxx>
69 #include <vcl/metric.hxx>
70 #include <vcl/settings.hxx>
71 #include <vcl/strhelper.hxx>
72 #include <vcl/svapp.hxx>
73 #include <vcl/virdev.hxx>
74 #include <vcl/filter/pdfdocument.hxx>
76 #include "fontsubset.hxx"
78 #include "PhysicalFontFace.hxx"
80 #include "sallayout.hxx"
81 #include "textlayout.hxx"
82 #include "textlineinfo.hxx"
84 #include "pdfwriter_impl.hxx"
86 #if HAVE_FEATURE_NSS && !defined(_WIN32)
87 // NSS headers for PDF signing
96 // We use curl for RFC3161 time stamp requests
97 #include <curl/curl.h>
101 // WinCrypt headers for PDF signing
102 // Note: this uses Windows 7 APIs and requires the relevant data types;
103 // the functions that don't exist in WinXP must be looked up at runtime!
105 #define _WIN32_WINNT _WIN32_WINNT_WIN7
107 #include <wincrypt.h>
109 #include <comphelper/windowserrorstring.hxx>
112 #include <config_eot.h>
115 #include <libeot/libeot.h>
119 using namespace::com::sun::star
;
121 static bool g_bDebugDisableCompression
= getenv("VCL_DEBUG_DISABLE_PDFCOMPRESSION");
124 // Is this length truly the maximum possible, or just a number that
125 // seemed large enough when the author tested this (with some type of
126 // certificates)? I suspect the latter.
128 // Used to be 0x4000 = 16384, but a sample signed PDF (produced by
129 // some other software) provided by the customer has a signature
130 // content that is 30000 bytes. The SampleSignedPDFDocument.pdf from
131 // Adobe has one that is 21942 bytes. So let's be careful. Pity this
132 // can't be dynamic, at least not without restructuring the code. Also
133 // note that the checks in the code for this being too small
134 // apparently are broken, if this overflows you end up with an invalid
135 // PDF. Need to fix that.
137 #define MAX_SIGNATURE_CONTENT_LENGTH 50000
141 class PDFTestOutputStream
: public PDFOutputStream
144 virtual ~PDFTestOutputStream();
145 virtual void write( const css::uno::Reference
< css::io::XOutputStream
>& xStream
);
148 PDFTestOutputStream::~PDFTestOutputStream()
152 void PDFTestOutputStream::write( const css::uno::Reference
< css::io::XOutputStream
>& xStream
)
154 OString
aStr( "lalala\ntest\ntest\ntest" );
155 css::uno::Sequence
< sal_Int8
> aData( aStr
.getLength() );
156 memcpy( aData
.getArray(), aStr
.getStr(), aStr
.getLength() );
157 xStream
->writeBytes( aData
);
160 // this test code cannot be used to test PDF/A-1 because it forces
161 // control item (widgets) to bypass the structure controlling
162 // the embedding of such elements in actual run
165 static const char* pHome
= getenv( "HOME" );
166 OUString
aTestFile( "file://" );
167 aTestFile
+= OUString( pHome
, strlen( pHome
), RTL_TEXTENCODING_MS_1252
);
168 aTestFile
+= "/pdf_export_test.pdf";
170 PDFWriter::PDFWriterContext aContext
;
171 aContext
.URL
= aTestFile
;
172 aContext
.Version
= PDFWriter::PDF_1_4
;
173 aContext
.Tagged
= true;
174 aContext
.InitialPage
= 2;
175 aContext
.DocumentInfo
.Title
= "PDF export test document";
176 aContext
.DocumentInfo
.Producer
= "VCL";
178 aContext
.SignPDF
= true;
179 aContext
.SignLocation
= "Burdur";
180 aContext
.SignReason
= "Some valid reason to sign";
181 aContext
.SignContact
= "signer@example.com";
183 css::uno::Reference
< css::beans::XMaterialHolder
> xEnc
;
184 PDFWriter
aWriter( aContext
, xEnc
);
185 aWriter
.NewPage( 595, 842 );
186 aWriter
.BeginStructureElement( PDFWriter::Document
);
187 // set duration of 3 sec for first page
188 aWriter
.SetAutoAdvanceTime( 3 );
189 aWriter
.SetMapMode( MapMode( MapUnit::Map100thMM
) );
191 aWriter
.SetFillColor( Color( COL_LIGHTRED
) );
192 aWriter
.SetLineColor( Color( COL_LIGHTGREEN
) );
193 aWriter
.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
195 aWriter
.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
196 aWriter
.SetTextColor( Color( COL_BLACK
) );
197 aWriter
.SetLineColor( Color( COL_BLACK
) );
198 aWriter
.SetFillColor( Color( COL_LIGHTBLUE
) );
200 Rectangle
aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
201 aWriter
.DrawRect( aRect
);
202 aWriter
.DrawText( aRect
, OUString( "Link annot 1" ) );
203 sal_Int32 nFirstLink
= aWriter
.CreateLink( aRect
);
205 aNote
.Title
= "A small test note";
206 aNote
.Contents
= "There is no business like show business like no business i know. Everything about it is appealing.";
207 aWriter
.CreateNote( Rectangle( Point( aRect
.Right(), aRect
.Top() ), Size( 6000, 3000 ) ), aNote
);
209 Rectangle
aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
210 aWriter
.SetFillColor( Color( COL_LIGHTGREEN
) );
211 aWriter
.DrawRect( aTargetRect
);
212 aWriter
.DrawText( aTargetRect
, "Dest second link" );
213 sal_Int32 nSecondDest
= aWriter
.CreateDest( aTargetRect
);
215 aWriter
.BeginStructureElement( PDFWriter::Section
);
216 aWriter
.BeginStructureElement( PDFWriter::Heading
);
217 aWriter
.DrawText( Point(4500, 9000), "A small structure test" );
218 aWriter
.EndStructureElement();
219 aWriter
.BeginStructureElement( PDFWriter::Paragraph
);
220 aWriter
.SetStructureAttribute( PDFWriter::WritingMode
, PDFWriter::LrTb
);
221 aWriter
.SetStructureAttribute( PDFWriter::TextDecorationType
, PDFWriter::Underline
);
222 aWriter
.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
223 "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.",
224 DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
226 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." );
227 aWriter
.SetAlternateText( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." );
228 aWriter
.EndStructureElement();
229 aWriter
.BeginStructureElement( PDFWriter::Paragraph
);
230 aWriter
.SetStructureAttribute( PDFWriter::WritingMode
, PDFWriter::LrTb
);
231 aWriter
.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
232 "This paragraph is nothing special either but ends on the next page structurewise",
233 DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
236 aWriter
.NewPage( 595, 842 );
237 // test AddStream interface
238 aWriter
.AddStream( "text/plain", new PDFTestOutputStream(), true );
239 // set transitional mode
240 aWriter
.SetPageTransition( PDFWriter::WipeRightToLeft
, 1500 );
241 aWriter
.SetMapMode( MapMode( MapUnit::Map100thMM
) );
242 aWriter
.SetTextColor( Color( COL_BLACK
) );
243 aWriter
.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
244 aWriter
.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
245 "Here's where all things come to an end ... well at least the paragraph from the last page.",
246 DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
248 aWriter
.EndStructureElement();
250 aWriter
.SetFillColor( Color( COL_LIGHTBLUE
) );
252 aWriter
.BeginStructureElement( PDFWriter::NonStructElement
);
253 aWriter
.DrawRect( aRect
);
254 aWriter
.BeginStructureElement( PDFWriter::Paragraph
);
255 aWriter
.DrawText( aRect
, "Link annot 2" );
256 sal_Int32 nSecondLink
= aWriter
.CreateLink( aRect
);
258 aWriter
.SetFillColor( Color( COL_LIGHTGREEN
) );
259 aWriter
.BeginStructureElement( PDFWriter::ListItem
);
260 aWriter
.DrawRect( aTargetRect
);
261 aWriter
.DrawText( aTargetRect
, "Dest first link" );
262 sal_Int32 nFirstDest
= aWriter
.CreateDest( aTargetRect
);
264 aWriter
.EndStructureElement();
266 aWriter
.EndStructureElement();
267 aWriter
.EndStructureElement();
268 aWriter
.BeginStructureElement( PDFWriter::Figure
);
269 aWriter
.BeginStructureElement( PDFWriter::Caption
);
270 aWriter
.DrawText( Point( 4500, 9000 ), "Some drawing stuff inside the structure" );
271 aWriter
.EndStructureElement();
274 basegfx::B2DPolyPolygon aClip
;
275 basegfx::B2DPolygon aClipPoly
;
276 aClipPoly
.append( basegfx::B2DPoint( 8250, 9600 ) );
277 aClipPoly
.append( basegfx::B2DPoint( 16500, 11100 ) );
278 aClipPoly
.append( basegfx::B2DPoint( 8250, 12600 ) );
279 aClipPoly
.append( basegfx::B2DPoint( 4500, 11100 ) );
280 aClipPoly
.setClosed( true );
281 aClip
.append( aClipPoly
);
283 aWriter
.Push( PushFlags::CLIPREGION
| PushFlags::FILLCOLOR
);
284 aWriter
.SetClipRegion( aClip
);
285 aWriter
.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
286 aWriter
.MoveClipRegion( 1000, 500 );
287 aWriter
.SetFillColor( Color( COL_RED
) );
288 aWriter
.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
292 Rectangle
aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
293 aWriter
.SetFillColor( Color( COL_LIGHTRED
) );
294 aWriter
.DrawRect( aTranspRect
);
295 aWriter
.BeginTransparencyGroup();
297 aWriter
.SetFillColor( Color( COL_LIGHTGREEN
) );
298 aWriter
.DrawEllipse( aTranspRect
);
299 aWriter
.SetTextColor( Color( COL_LIGHTBLUE
) );
300 aWriter
.DrawText( aTranspRect
,
301 "Some transparent text",
302 DrawTextFlags::Center
| DrawTextFlags::VCenter
| DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
);
304 aWriter
.EndTransparencyGroup( aTranspRect
, 50 );
306 // prepare an alpha mask
307 Bitmap
aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
308 Bitmap::ScopedWriteAccess
pAcc(aTransMask
);
309 for( int nX
= 0; nX
< 256; nX
++ )
310 for( int nY
= 0; nY
< 256; nY
++ )
311 pAcc
->SetPixel( nX
, nY
, BitmapColor( (sal_uInt8
)((nX
+nY
)/2) ) );
313 aTransMask
.SetPrefMapMode( MapUnit::MapMM
);
314 aTransMask
.SetPrefSize( Size( 10, 10 ) );
316 aWriter
.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask
);
318 aTranspRect
= Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
319 aWriter
.SetFillColor( Color( COL_LIGHTRED
) );
320 aWriter
.DrawRect( aTranspRect
);
321 aWriter
.SetFillColor( Color( COL_LIGHTGREEN
) );
322 aWriter
.DrawEllipse( aTranspRect
);
323 aWriter
.SetTextColor( Color( COL_LIGHTBLUE
) );
324 aWriter
.DrawText( aTranspRect
,
325 "Some transparent text",
326 DrawTextFlags::Center
| DrawTextFlags::VCenter
| DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
);
327 aTranspRect
= Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
328 aWriter
.SetFillColor( Color( COL_LIGHTRED
) );
329 aWriter
.DrawRect( aTranspRect
);
331 Bitmap
aImageBmp( Size( 256, 256 ), 24 );
332 pAcc
= Bitmap::ScopedWriteAccess(aImageBmp
);
333 pAcc
->SetFillColor( Color( 0xff, 0, 0xff ) );
334 pAcc
->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
336 BitmapEx
aBmpEx( aImageBmp
, AlphaMask( aTransMask
) );
337 aWriter
.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx
);
339 aWriter
.EndStructureElement();
340 aWriter
.EndStructureElement();
342 LineInfo
aLI( LineStyle::Dash
, 3 );
343 aLI
.SetDashCount( 2 );
344 aLI
.SetDashLen( 50 );
345 aLI
.SetDotCount( 2 );
347 aLI
.SetDistance( 15 );
348 Point aLIPoints
[] = { Point( 4000, 10000 ),
349 Point( 8000, 12000 ),
350 Point( 3000, 19000 ) };
351 tools::Polygon
aLIPoly( 3, aLIPoints
);
352 aWriter
.SetLineColor( Color( COL_BLUE
) );
353 aWriter
.SetFillColor();
354 aWriter
.DrawPolyLine( aLIPoly
, aLI
);
356 aLI
.SetDashCount( 4 );
357 aLIPoly
.Move( 1000, 1000 );
358 aWriter
.DrawPolyLine( aLIPoly
, aLI
);
360 aWriter
.NewPage( 595, 842 );
361 aWriter
.SetMapMode( MapMode( MapUnit::Map100thMM
) );
362 Wallpaper
aWall( aTransMask
);
363 aWall
.SetStyle( WallpaperStyle::Tile
);
364 aWriter
.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall
);
366 aWriter
.NewPage( 595, 842 );
367 aWriter
.SetMapMode( MapMode( MapUnit::Map100thMM
) );
368 aWriter
.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
369 aWriter
.SetTextColor( Color( COL_BLACK
) );
370 aRect
= Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
371 aWriter
.DrawRect( aRect
);
372 aWriter
.DrawText( aRect
, "www.heise.de" );
373 sal_Int32 nURILink
= aWriter
.CreateLink( aRect
);
374 aWriter
.SetLinkURL( nURILink
, OUString( "http://www.heise.de" ) );
376 aWriter
.SetLinkDest( nFirstLink
, nFirstDest
);
377 aWriter
.SetLinkDest( nSecondLink
, nSecondDest
);
380 PDFWriter::PushButtonWidget aBtn
;
381 aBtn
.Name
= "testButton";
382 aBtn
.Description
= "A test button";
383 aBtn
.Text
= "hit me";
384 aBtn
.Location
= Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
385 aBtn
.Border
= aBtn
.Background
= true;
386 aWriter
.CreateControl( aBtn
);
388 // include a uri button
389 PDFWriter::PushButtonWidget aUriBtn
;
390 aUriBtn
.Name
= "wwwButton";
391 aUriBtn
.Description
= "A URI button";
392 aUriBtn
.Text
= "to www";
393 aUriBtn
.Location
= Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
394 aUriBtn
.Border
= aUriBtn
.Background
= true;
395 aUriBtn
.URL
= "http://www.heise.de";
396 aWriter
.CreateControl( aUriBtn
);
398 // include a dest button
399 PDFWriter::PushButtonWidget aDstBtn
;
400 aDstBtn
.Name
= "destButton";
401 aDstBtn
.Description
= "A Dest button";
402 aDstBtn
.Text
= "to paragraph";
403 aDstBtn
.Location
= Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
404 aDstBtn
.Border
= aDstBtn
.Background
= true;
405 aDstBtn
.Dest
= nFirstDest
;
406 aWriter
.CreateControl( aDstBtn
);
408 PDFWriter::CheckBoxWidget aCBox
;
409 aCBox
.Name
= "textCheckBox";
410 aCBox
.Description
= "A test check box";
411 aCBox
.Text
= "check me";
412 aCBox
.Location
= Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
413 aCBox
.Checked
= true;
414 aCBox
.Border
= aCBox
.Background
= false;
415 aWriter
.CreateControl( aCBox
);
417 PDFWriter::CheckBoxWidget aCBox2
;
418 aCBox2
.Name
= "textCheckBox2";
419 aCBox2
.Description
= "Another test check box";
420 aCBox2
.Text
= "check me right";
421 aCBox2
.Location
= Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
422 aCBox2
.Checked
= true;
423 aCBox2
.Border
= aCBox2
.Background
= false;
424 aCBox2
.ButtonIsLeft
= false;
425 aWriter
.CreateControl( aCBox2
);
427 PDFWriter::RadioButtonWidget aRB1
;
429 aRB1
.Description
= "radio 1 button 1";
430 aRB1
.Text
= "Despair";
431 aRB1
.Location
= Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
432 aRB1
.Selected
= true;
434 aRB1
.Border
= aRB1
.Background
= true;
435 aRB1
.ButtonIsLeft
= false;
436 aRB1
.BorderColor
= Color( COL_LIGHTGREEN
);
437 aRB1
.BackgroundColor
= Color( COL_LIGHTBLUE
);
438 aRB1
.TextColor
= Color( COL_LIGHTRED
);
439 aRB1
.TextFont
= Font( OUString( "Courier" ), Size( 0, 800 ) );
440 aWriter
.CreateControl( aRB1
);
442 PDFWriter::RadioButtonWidget aRB2
;
444 aRB2
.Description
= "radio 2 button 1";
446 aRB2
.Location
= Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
447 aRB2
.Selected
= true;
449 aWriter
.CreateControl( aRB2
);
451 PDFWriter::RadioButtonWidget aRB3
;
453 aRB3
.Description
= "radio 1 button 2";
454 aRB3
.Text
= "Desperation";
455 aRB3
.Location
= Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
456 aRB3
.Selected
= true;
458 aWriter
.CreateControl( aRB3
);
460 PDFWriter::EditWidget aEditBox
;
461 aEditBox
.Name
= "testEdit";
462 aEditBox
.Description
= "A test edit field";
463 aEditBox
.Text
= "A little test text";
464 aEditBox
.TextStyle
= DrawTextFlags::Left
| DrawTextFlags::VCenter
;
465 aEditBox
.Location
= Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
466 aEditBox
.MaxLen
= 100;
467 aEditBox
.Border
= aEditBox
.Background
= true;
468 aEditBox
.BorderColor
= Color( COL_BLACK
);
469 aWriter
.CreateControl( aEditBox
);
472 PDFWriter::ListBoxWidget aLstBox
;
473 aLstBox
.Name
= "testListBox";
474 aLstBox
.Text
= "One";
475 aLstBox
.Description
= "select me";
476 aLstBox
.Location
= Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
478 aLstBox
.MultiSelect
= true;
479 aLstBox
.Border
= aLstBox
.Background
= true;
480 aLstBox
.BorderColor
= Color( COL_BLACK
);
481 aLstBox
.Entries
.push_back( OUString( "One" ) );
482 aLstBox
.Entries
.push_back( OUString( "Two" ) );
483 aLstBox
.Entries
.push_back( OUString( "Three" ) );
484 aLstBox
.Entries
.push_back( OUString( "Four" ) );
485 aLstBox
.SelectedEntries
.push_back( 1 );
486 aLstBox
.SelectedEntries
.push_back( 2 );
487 aWriter
.CreateControl( aLstBox
);
490 aLstBox
.Name
= "testDropDownListBox";
491 aLstBox
.DropDown
= true;
492 aLstBox
.Location
= Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
493 aWriter
.CreateControl( aLstBox
);
496 PDFWriter::ComboBoxWidget aComboBox
;
497 aComboBox
.Name
= "testComboBox";
498 aComboBox
.Text
= "test a combobox";
499 aComboBox
.Entries
.push_back( OUString( "Larry" ) );
500 aComboBox
.Entries
.push_back( OUString( "Curly" ) );
501 aComboBox
.Entries
.push_back( OUString( "Moe" ) );
502 aComboBox
.Location
= Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
503 aWriter
.CreateControl( aComboBox
);
506 sal_Int32 nPage1OL
= aWriter
.CreateOutlineItem();
507 aWriter
.SetOutlineItemText( nPage1OL
, OUString( "Page 1" ) );
508 aWriter
.SetOutlineItemDest( nPage1OL
, nSecondDest
);
509 aWriter
.CreateOutlineItem( nPage1OL
, OUString( "Dest 2" ), nSecondDest
);
510 aWriter
.CreateOutlineItem( nPage1OL
, OUString( "Dest 2 revisited" ), nSecondDest
);
511 aWriter
.CreateOutlineItem( nPage1OL
, OUString( "Dest 2 again" ), nSecondDest
);
512 sal_Int32 nPage2OL
= aWriter
.CreateOutlineItem();
513 aWriter
.SetOutlineItemText( nPage2OL
, OUString( "Page 2" ) );
514 aWriter
.CreateOutlineItem( nPage2OL
, OUString( "Dest 1" ), nFirstDest
);
516 aWriter
.EndStructureElement(); // close document
522 static const sal_Int32 nLog10Divisor
= 1;
523 static const double fDivisor
= 10.0;
525 static inline double pixelToPoint( double px
) { return px
/fDivisor
; }
526 static inline sal_Int32
pointToPixel( double pt
) { return sal_Int32(pt
*fDivisor
); }
528 const sal_uInt8
PDFWriterImpl::s_nPadString
[32] =
530 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
531 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
534 static void appendHex( sal_Int8 nInt
, OStringBuffer
& rBuffer
)
536 static const sal_Char pHexDigits
[] = { '0', '1', '2', '3', '4', '5', '6', '7',
537 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
538 rBuffer
.append( pHexDigits
[ (nInt
>> 4) & 15 ] );
539 rBuffer
.append( pHexDigits
[ nInt
& 15 ] );
542 static void appendName( const OUString
& rStr
, OStringBuffer
& rBuffer
)
544 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
545 // I guess than when reading the #xx sequence it will count for a single character.
546 OString
aStr( OUStringToOString( rStr
, RTL_TEXTENCODING_UTF8
) );
547 const sal_Char
* pStr
= aStr
.getStr();
548 int nLen
= aStr
.getLength();
549 for( int i
= 0; i
< nLen
; i
++ )
551 /* #i16920# PDF recommendation: output UTF8, any byte
552 * outside the interval [33(=ASCII'!');126(=ASCII'~')]
553 * should be escaped hexadecimal
554 * for the sake of ghostscript which also reads PDF
555 * but has a narrower acceptance rate we only pass
556 * alphanumerics and '-' literally.
558 if( (pStr
[i
] >= 'A' && pStr
[i
] <= 'Z' ) ||
559 (pStr
[i
] >= 'a' && pStr
[i
] <= 'z' ) ||
560 (pStr
[i
] >= '0' && pStr
[i
] <= '9' ) ||
563 rBuffer
.append( pStr
[i
] );
567 rBuffer
.append( '#' );
568 appendHex( (sal_Int8
)pStr
[i
], rBuffer
);
573 static void appendName( const sal_Char
* pStr
, OStringBuffer
& rBuffer
)
575 // FIXME i59651 see above
576 while( pStr
&& *pStr
)
578 if( (*pStr
>= 'A' && *pStr
<= 'Z' ) ||
579 (*pStr
>= 'a' && *pStr
<= 'z' ) ||
580 (*pStr
>= '0' && *pStr
<= '9' ) ||
583 rBuffer
.append( *pStr
);
587 rBuffer
.append( '#' );
588 appendHex( (sal_Int8
)*pStr
, rBuffer
);
594 //used only to emit encoded passwords
595 static void appendLiteralString( const sal_Char
* pStr
, sal_Int32 nLength
, OStringBuffer
& rBuffer
)
602 rBuffer
.append( "\\n" );
605 rBuffer
.append( "\\r" );
608 rBuffer
.append( "\\t" );
611 rBuffer
.append( "\\b" );
614 rBuffer
.append( "\\f" );
619 rBuffer
.append( "\\" );
620 rBuffer
.append( (sal_Char
) *pStr
);
623 rBuffer
.append( (sal_Char
) *pStr
);
632 * Convert a string before using it.
634 * This string conversion function is needed because the destination name
635 * in a PDF file seen through an Internet browser should be
636 * specially crafted, in order to be used directly by the browser.
637 * In this way the fragment part of a hyperlink to a PDF file (e.g. something
638 * as 'test1/test2/a-file.pdf\#thefragment) will be (hopefully) interpreted by the
639 * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
640 * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
641 * and go to named destination thefragment using default zoom'.
642 * The conversion is needed because in case of a fragment in the form: Slide%201
643 * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
644 * using this conversion, in both the generated named destinations, fragment and GoToR
647 * The names for destinations are name objects and so they don't need to be encrypted
648 * even though they expose the content of PDF file (e.g. guessing the PDF content from the
651 * Further limitation: it is advisable to use standard ASCII characters for
654 static void appendDestinationName( const OUString
& rString
, OStringBuffer
& rBuffer
)
656 const sal_Unicode
* pStr
= rString
.getStr();
657 sal_Int32 nLen
= rString
.getLength();
658 for( int i
= 0; i
< nLen
; i
++ )
660 sal_Unicode aChar
= pStr
[i
];
661 if( (aChar
>= '0' && aChar
<= '9' ) ||
662 (aChar
>= 'a' && aChar
<= 'z' ) ||
663 (aChar
>= 'A' && aChar
<= 'Z' ) ||
666 rBuffer
.append((sal_Char
)aChar
);
670 sal_Int8 aValueHigh
= sal_Int8(aChar
>> 8);
672 appendHex( aValueHigh
, rBuffer
);
673 appendHex( (sal_Int8
)(aChar
& 255 ), rBuffer
);
678 void PDFWriter::AppendUnicodeTextString(const OUString
& rString
, OStringBuffer
& rBuffer
)
680 rBuffer
.append( "FEFF" );
681 const sal_Unicode
* pStr
= rString
.getStr();
682 sal_Int32 nLen
= rString
.getLength();
683 for( int i
= 0; i
< nLen
; i
++ )
685 sal_Unicode aChar
= pStr
[i
];
686 appendHex( (sal_Int8
)(aChar
>> 8), rBuffer
);
687 appendHex( (sal_Int8
)(aChar
& 255 ), rBuffer
);
691 void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex
, const PDFWriter::AnyWidget
& i_rControl
)
693 /* #i80258# previously we use appendName here
694 however we need a slightly different coding scheme than the normal
695 name encoding for field names
697 const OUString
& rName
= (m_aContext
.Version
> PDFWriter::PDFVersion::PDF_1_2
) ? i_rControl
.Name
: i_rControl
.Text
;
698 OString
aStr( OUStringToOString( rName
, RTL_TEXTENCODING_UTF8
) );
699 const sal_Char
* pStr
= aStr
.getStr();
700 int nLen
= aStr
.getLength();
702 OStringBuffer
aBuffer( rName
.getLength()+64 );
703 for( int i
= 0; i
< nLen
; i
++ )
705 /* #i16920# PDF recommendation: output UTF8, any byte
706 * outside the interval [32(=ASCII' ');126(=ASCII'~')]
707 * should be escaped hexadecimal
709 if( (pStr
[i
] >= 32 && pStr
[i
] <= 126 ) )
710 aBuffer
.append( pStr
[i
] );
713 aBuffer
.append( '#' );
714 appendHex( (sal_Int8
)pStr
[i
], aBuffer
);
718 OString
aFullName( aBuffer
.makeStringAndClear() );
720 /* #i82785# create hierarchical fields down to the for each dot in i_rName */
721 sal_Int32 nTokenIndex
= 0, nLastTokenIndex
= 0;
722 OString aPartialName
;
726 nLastTokenIndex
= nTokenIndex
;
727 aPartialName
= aFullName
.getToken( 0, '.', nTokenIndex
);
728 if( nTokenIndex
!= -1 )
730 // find or create a hierarchical field
731 // first find the fully qualified name up to this field
732 aDomain
= aFullName
.copy( 0, nTokenIndex
-1 );
733 std::unordered_map
< OString
, sal_Int32
, OStringHash
>::const_iterator it
= m_aFieldNameMap
.find( aDomain
);
734 if( it
== m_aFieldNameMap
.end() )
736 // create new hierarchy field
737 sal_Int32 nNewWidget
= m_aWidgets
.size();
738 m_aWidgets
.push_back( PDFWidget() );
739 m_aWidgets
[nNewWidget
].m_nObject
= createObject();
740 m_aWidgets
[nNewWidget
].m_eType
= PDFWriter::Hierarchy
;
741 m_aWidgets
[nNewWidget
].m_aName
= aPartialName
;
742 m_aWidgets
[i_nWidgetIndex
].m_nParent
= m_aWidgets
[nNewWidget
].m_nObject
;
743 m_aFieldNameMap
[aDomain
] = nNewWidget
;
744 m_aWidgets
[i_nWidgetIndex
].m_nParent
= m_aWidgets
[nNewWidget
].m_nObject
;
745 if( nLastTokenIndex
> 0 )
747 // this field is not a root field and
748 // needs to be inserted to its parent
749 OString
aParentDomain( aDomain
.copy( 0, nLastTokenIndex
-1 ) );
750 it
= m_aFieldNameMap
.find( aParentDomain
);
751 OSL_ENSURE( it
!= m_aFieldNameMap
.end(), "field name not found" );
752 if( it
!= m_aFieldNameMap
.end() )
754 OSL_ENSURE( it
->second
< sal_Int32(m_aWidgets
.size()), "invalid field number entry" );
755 if( it
->second
< sal_Int32(m_aWidgets
.size()) )
757 PDFWidget
& rParentField( m_aWidgets
[it
->second
] );
758 rParentField
.m_aKids
.push_back( m_aWidgets
[nNewWidget
].m_nObject
);
759 rParentField
.m_aKidsIndex
.push_back( nNewWidget
);
760 m_aWidgets
[nNewWidget
].m_nParent
= rParentField
.m_nObject
;
765 else if( m_aWidgets
[it
->second
].m_eType
!= PDFWriter::Hierarchy
)
767 // this is invalid, someone tries to have a terminal field as parent
768 // example: a button with the name foo.bar exists and
769 // another button is named foo.bar.no
770 // workaround: put the second terminal field as much up in the hierarchy as
771 // necessary to have a non-terminal field as parent (or none at all)
772 // since it->second already is terminal, we just need to use its parent
774 aPartialName
= aFullName
.copy( aFullName
.lastIndexOf( '.' )+1 );
775 if( nLastTokenIndex
> 0 )
777 aDomain
= aFullName
.copy( 0, nLastTokenIndex
-1 );
778 OStringBuffer
aBuf( aDomain
.getLength() + 1 + aPartialName
.getLength() );
779 aBuf
.append( aDomain
);
781 aBuf
.append( aPartialName
);
782 aFullName
= aBuf
.makeStringAndClear();
785 aFullName
= aPartialName
;
789 } while( nTokenIndex
!= -1 );
791 // insert widget into its hierarchy field
792 if( !aDomain
.isEmpty() )
794 std::unordered_map
< OString
, sal_Int32
, OStringHash
>::const_iterator it
= m_aFieldNameMap
.find( aDomain
);
795 if( it
!= m_aFieldNameMap
.end() )
797 OSL_ENSURE( it
->second
>= 0 && it
->second
< sal_Int32( m_aWidgets
.size() ), "invalid field index" );
798 if( it
->second
>= 0 && it
->second
< sal_Int32(m_aWidgets
.size()) )
800 m_aWidgets
[i_nWidgetIndex
].m_nParent
= m_aWidgets
[it
->second
].m_nObject
;
801 m_aWidgets
[it
->second
].m_aKids
.push_back( m_aWidgets
[i_nWidgetIndex
].m_nObject
);
802 m_aWidgets
[it
->second
].m_aKidsIndex
.push_back( i_nWidgetIndex
);
807 if( aPartialName
.isEmpty() )
809 // how funny, an empty field name
810 if( i_rControl
.getType() == PDFWriter::RadioButton
)
812 aPartialName
= "RadioGroup";
813 aPartialName
+= OString::number( static_cast<const PDFWriter::RadioButtonWidget
&>(i_rControl
).RadioGroup
);
816 aPartialName
= OString( "Widget" );
819 if( ! m_aContext
.AllowDuplicateFieldNames
)
821 std::unordered_map
<OString
, sal_Int32
, OStringHash
>::iterator it
= m_aFieldNameMap
.find( aFullName
);
823 if( it
!= m_aFieldNameMap
.end() ) // not unique
825 std::unordered_map
< OString
, sal_Int32
, OStringHash
>::const_iterator check_it
;
830 OStringBuffer
aUnique( aFullName
.getLength() + 16 );
831 aUnique
.append( aFullName
);
832 aUnique
.append( '_' );
833 aUnique
.append( nTry
++ );
834 aTry
= aUnique
.makeStringAndClear();
835 check_it
= m_aFieldNameMap
.find( aTry
);
836 } while( check_it
!= m_aFieldNameMap
.end() );
838 m_aFieldNameMap
[ aFullName
] = i_nWidgetIndex
;
839 aPartialName
= aFullName
.copy( aFullName
.lastIndexOf( '.' )+1 );
842 m_aFieldNameMap
[ aFullName
] = i_nWidgetIndex
;
846 m_aWidgets
[i_nWidgetIndex
].m_aName
= aPartialName
;
849 static void appendFixedInt( sal_Int32 nValue
, OStringBuffer
& rBuffer
)
853 rBuffer
.append( '-' );
856 const sal_Int32 nFactor
= 10;
857 const sal_Int32 nInt
= nValue
/ nFactor
;
858 rBuffer
.append( nInt
);
859 sal_Int32 nDecimal
= nValue
% nFactor
;
863 rBuffer
.append(nDecimal
);
867 // appends a double. PDF does not accept exponential format, only fixed point
868 static void appendDouble( double fValue
, OStringBuffer
& rBuffer
, sal_Int32 nPrecision
= 5 )
877 sal_Int64 nInt
= (sal_Int64
)fValue
;
878 fValue
-= (double)nInt
;
879 // optimizing hardware may lead to a value of 1.0 after the subtraction
880 if( rtl::math::approxEqual(fValue
, 1.0) || log10( 1.0-fValue
) <= -nPrecision
)
888 fValue
*= pow( 10.0, (double)nPrecision
);
889 nFrac
= (sal_Int64
)fValue
;
891 if( bNeg
&& ( nInt
|| nFrac
) )
892 rBuffer
.append( '-' );
893 rBuffer
.append( nInt
);
897 rBuffer
.append( '.' );
898 sal_Int64 nBound
= (sal_Int64
)(pow( 10.0, nPrecision
- 1.0 )+0.5);
899 for ( i
= 0; ( i
< nPrecision
) && nFrac
; i
++ )
901 sal_Int64 nNumb
= nFrac
/ nBound
;
902 nFrac
-= nNumb
* nBound
;
903 rBuffer
.append( nNumb
);
909 static void appendColor( const Color
& rColor
, OStringBuffer
& rBuffer
, bool bConvertToGrey
)
912 if( rColor
!= Color( COL_TRANSPARENT
) )
916 sal_uInt8 cByte
= rColor
.GetLuminance();
917 appendDouble( (double)cByte
/ 255.0, rBuffer
);
921 appendDouble( (double)rColor
.GetRed() / 255.0, rBuffer
);
922 rBuffer
.append( ' ' );
923 appendDouble( (double)rColor
.GetGreen() / 255.0, rBuffer
);
924 rBuffer
.append( ' ' );
925 appendDouble( (double)rColor
.GetBlue() / 255.0, rBuffer
);
930 void PDFWriterImpl::appendStrokingColor( const Color
& rColor
, OStringBuffer
& rBuffer
)
932 if( rColor
!= Color( COL_TRANSPARENT
) )
934 bool bGrey
= m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
;
935 appendColor( rColor
, rBuffer
, bGrey
);
936 rBuffer
.append( bGrey
? " G" : " RG" );
940 void PDFWriterImpl::appendNonStrokingColor( const Color
& rColor
, OStringBuffer
& rBuffer
)
942 if( rColor
!= Color( COL_TRANSPARENT
) )
944 bool bGrey
= m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
;
945 appendColor( rColor
, rBuffer
, bGrey
);
946 rBuffer
.append( bGrey
? " g" : " rg" );
950 // matrix helper class
951 // TODO: use basegfx matrix class instead or derive from it
952 namespace vcl
// TODO: use anonymous namespace to keep this class local
954 /* for sparse matrices of the form (2D linear transformations)
963 void set( double *pn
) { for( int i
= 0 ; i
< 6; i
++ ) f
[i
] = pn
[i
]; }
967 void skew( double alpha
, double beta
);
968 void scale( double sx
, double sy
);
969 void rotate( double angle
);
970 void translate( double tx
, double ty
);
973 void append( PDFWriterImpl::PDFPage
& rPage
, OStringBuffer
& rBuffer
);
975 Point
transform( const Point
& rPoint
) const;
981 // initialize to unity
990 Point
Matrix3::transform( const Point
& rOrig
) const
992 double x
= (double)rOrig
.X(), y
= (double)rOrig
.Y();
993 return Point( (int)(x
*f
[0] + y
*f
[2] + f
[4]), (int)(x
*f
[1] + y
*f
[3] + f
[5]) );
996 void Matrix3::skew( double alpha
, double beta
)
999 double tb
= tan( beta
);
1000 fn
[0] = f
[0] + f
[2]*tb
;
1002 fn
[2] = f
[2] + f
[3]*tb
;
1004 fn
[4] = f
[4] + f
[5]*tb
;
1008 double ta
= tan( alpha
);
1016 void Matrix3::scale( double sx
, double sy
)
1028 void Matrix3::rotate( double angle
)
1031 double fSin
= sin(angle
);
1032 double fCos
= cos(angle
);
1033 fn
[0] = f
[0]*fCos
- f
[1]*fSin
;
1034 fn
[1] = f
[0]*fSin
+ f
[1]*fCos
;
1035 fn
[2] = f
[2]*fCos
- f
[3]*fSin
;
1036 fn
[3] = f
[2]*fSin
+ f
[3]*fCos
;
1037 fn
[4] = f
[4]*fCos
- f
[5]*fSin
;
1038 fn
[5] = f
[4]*fSin
+ f
[5]*fCos
;
1042 void Matrix3::translate( double tx
, double ty
)
1048 void Matrix3::invert()
1050 // short circuit trivial cases
1051 if( f
[1]==f
[2] && f
[1]==0.0 && f
[0]==f
[3] && f
[0]==1.0 )
1058 // check determinant
1059 const double fDet
= f
[0]*f
[3]-f
[1]*f
[2];
1063 // invert the matrix
1065 fn
[0] = +f
[3] / fDet
;
1066 fn
[1] = -f
[1] / fDet
;
1067 fn
[2] = -f
[2] / fDet
;
1068 fn
[3] = +f
[0] / fDet
;
1070 // apply inversion to translation
1071 fn
[4] = -(f
[4]*fn
[0] + f
[5]*fn
[2]);
1072 fn
[5] = -(f
[4]*fn
[1] + f
[5]*fn
[3]);
1077 void Matrix3::append( PDFWriterImpl::PDFPage
& rPage
, OStringBuffer
& rBuffer
)
1079 appendDouble( f
[0], rBuffer
);
1080 rBuffer
.append( ' ' );
1081 appendDouble( f
[1], rBuffer
);
1082 rBuffer
.append( ' ' );
1083 appendDouble( f
[2], rBuffer
);
1084 rBuffer
.append( ' ' );
1085 appendDouble( f
[3], rBuffer
);
1086 rBuffer
.append( ' ' );
1087 rPage
.appendPoint( Point( (long)f
[4], (long)f
[5] ), rBuffer
);
1090 static void appendResourceMap( OStringBuffer
& rBuf
, const char* pPrefix
, const PDFWriterImpl::ResourceMap
& rList
)
1095 rBuf
.append( pPrefix
);
1096 rBuf
.append( "<<" );
1098 for( PDFWriterImpl::ResourceMap::const_iterator it
= rList
.begin(); it
!= rList
.end(); ++it
)
1100 if( !it
->first
.isEmpty() && it
->second
> 0 )
1103 rBuf
.append( it
->first
);
1105 rBuf
.append( it
->second
);
1106 rBuf
.append( " 0 R" );
1107 if( ((++ni
) & 7) == 0 )
1108 rBuf
.append( '\n' );
1111 rBuf
.append( ">>\n" );
1114 void PDFWriterImpl::ResourceDict::append( OStringBuffer
& rBuf
, sal_Int32 nFontDictObject
)
1116 rBuf
.append( "<</Font " );
1117 rBuf
.append( nFontDictObject
);
1118 rBuf
.append( " 0 R\n" );
1119 appendResourceMap( rBuf
, "XObject", m_aXObjects
);
1120 appendResourceMap( rBuf
, "ExtGState", m_aExtGStates
);
1121 appendResourceMap( rBuf
, "Shading", m_aShadings
);
1122 appendResourceMap( rBuf
, "Pattern", m_aPatterns
);
1123 rBuf
.append( "/ProcSet[/PDF/Text" );
1124 if( !m_aXObjects
.empty() )
1125 rBuf
.append( "/ImageC/ImageI/ImageB" );
1126 rBuf
.append( "]\n>>\n" );
1129 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl
* pWriter
, double nPageWidth
, double nPageHeight
, PDFWriter::Orientation eOrientation
)
1131 m_pWriter( pWriter
),
1132 m_nPageWidth( nPageWidth
),
1133 m_nPageHeight( nPageHeight
),
1134 m_eOrientation( eOrientation
),
1135 m_nPageObject( 0 ), // invalid object number
1136 m_nPageIndex( -1 ), // invalid index
1137 m_nStreamLengthObject( 0 ),
1138 m_nBeginStreamPos( 0 ),
1139 m_eTransition( PDFWriter::PageTransition::Regular
),
1142 m_bHasWidgets( false )
1144 // object ref must be only ever updated in emit()
1145 m_nPageObject
= m_pWriter
->createObject();
1148 PDFWriterImpl::PDFPage::~PDFPage()
1152 void PDFWriterImpl::PDFPage::beginStream()
1154 #if OSL_DEBUG_LEVEL > 1
1156 OStringBuffer
aLine( "PDFWriterImpl::PDFPage::beginStream, +" );
1157 m_pWriter
->emitComment( aLine
.getStr() );
1160 m_aStreamObjects
.push_back(m_pWriter
->createObject());
1161 if( ! m_pWriter
->updateObject( m_aStreamObjects
.back() ) )
1164 m_nStreamLengthObject
= m_pWriter
->createObject();
1165 // write content stream header
1166 OStringBuffer aLine
;
1167 aLine
.append( m_aStreamObjects
.back() );
1168 aLine
.append( " 0 obj\n<</Length " );
1169 aLine
.append( m_nStreamLengthObject
);
1170 aLine
.append( " 0 R" );
1171 if (!g_bDebugDisableCompression
)
1172 aLine
.append( "/Filter/FlateDecode" );
1173 aLine
.append( ">>\nstream\n" );
1174 if( ! m_pWriter
->writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
1176 if (osl::File::E_None
!= m_pWriter
->m_aFile
.getPos(m_nBeginStreamPos
))
1178 m_pWriter
->m_aFile
.close();
1179 m_pWriter
->m_bOpen
= false;
1181 if (!g_bDebugDisableCompression
)
1182 m_pWriter
->beginCompression();
1183 m_pWriter
->checkAndEnableStreamEncryption( m_aStreamObjects
.back() );
1186 void PDFWriterImpl::PDFPage::endStream()
1188 if (!g_bDebugDisableCompression
)
1189 m_pWriter
->endCompression();
1190 sal_uInt64 nEndStreamPos
;
1191 if (osl::File::E_None
!= m_pWriter
->m_aFile
.getPos(nEndStreamPos
))
1193 m_pWriter
->m_aFile
.close();
1194 m_pWriter
->m_bOpen
= false;
1197 m_pWriter
->disableStreamEncryption();
1198 if( ! m_pWriter
->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1200 // emit stream length object
1201 if( ! m_pWriter
->updateObject( m_nStreamLengthObject
) )
1203 OStringBuffer aLine
;
1204 aLine
.append( m_nStreamLengthObject
);
1205 aLine
.append( " 0 obj\n" );
1206 aLine
.append( (sal_Int64
)(nEndStreamPos
-m_nBeginStreamPos
) );
1207 aLine
.append( "\nendobj\n\n" );
1208 m_pWriter
->writeBuffer( aLine
.getStr(), aLine
.getLength() );
1211 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject
)
1214 if( ! m_pWriter
->updateObject( m_nPageObject
) )
1216 OStringBuffer aLine
;
1218 aLine
.append( m_nPageObject
);
1219 aLine
.append( " 0 obj\n"
1220 "<</Type/Page/Parent " );
1221 aLine
.append( nParentObject
);
1222 aLine
.append( " 0 R" );
1223 aLine
.append( "/Resources " );
1224 aLine
.append( m_pWriter
->getResourceDictObj() );
1225 aLine
.append( " 0 R" );
1226 if( m_nPageWidth
&& m_nPageHeight
)
1228 aLine
.append( "/MediaBox[0 0 " );
1229 aLine
.append( m_nPageWidth
);
1230 aLine
.append( ' ' );
1231 aLine
.append( m_nPageHeight
);
1232 aLine
.append( "]" );
1234 switch( m_eOrientation
)
1236 case PDFWriter::Orientation::Portrait
: aLine
.append( "/Rotate 0\n" );break;
1237 case PDFWriter::Orientation::Inherit
: break;
1239 int nAnnots
= m_aAnnotations
.size();
1242 aLine
.append( "/Annots[\n" );
1243 for( int i
= 0; i
< nAnnots
; i
++ )
1245 aLine
.append( m_aAnnotations
[i
] );
1246 aLine
.append( " 0 R" );
1247 aLine
.append( ((i
+1)%15) ? " " : "\n" );
1249 aLine
.append( "]\n" );
1251 if( m_aMCIDParents
.size() > 0 )
1253 OStringBuffer
aStructParents( 1024 );
1254 aStructParents
.append( "[ " );
1255 int nParents
= m_aMCIDParents
.size();
1256 for( int i
= 0; i
< nParents
; i
++ )
1258 aStructParents
.append( m_aMCIDParents
[i
] );
1259 aStructParents
.append( " 0 R" );
1260 aStructParents
.append( ((i
%10) == 9) ? "\n" : " " );
1262 aStructParents
.append( "]" );
1263 m_pWriter
->m_aStructParentTree
.push_back( aStructParents
.makeStringAndClear() );
1265 aLine
.append( "/StructParents " );
1266 aLine
.append( sal_Int32(m_pWriter
->m_aStructParentTree
.size()-1) );
1267 aLine
.append( "\n" );
1269 if( m_nDuration
> 0 )
1271 aLine
.append( "/Dur " );
1272 aLine
.append( (sal_Int32
)m_nDuration
);
1273 aLine
.append( "\n" );
1275 if( m_eTransition
!= PDFWriter::PageTransition::Regular
&& m_nTransTime
> 0 )
1277 // transition duration
1278 aLine
.append( "/Trans<</D " );
1279 appendDouble( (double)m_nTransTime
/1000.0, aLine
, 3 );
1280 aLine
.append( "\n" );
1281 const char *pStyle
= nullptr, *pDm
= nullptr, *pM
= nullptr, *pDi
= nullptr;
1282 switch( m_eTransition
)
1284 case PDFWriter::PageTransition::SplitHorizontalInward
:
1285 pStyle
= "Split"; pDm
= "H"; pM
= "I"; break;
1286 case PDFWriter::PageTransition::SplitHorizontalOutward
:
1287 pStyle
= "Split"; pDm
= "H"; pM
= "O"; break;
1288 case PDFWriter::PageTransition::SplitVerticalInward
:
1289 pStyle
= "Split"; pDm
= "V"; pM
= "I"; break;
1290 case PDFWriter::PageTransition::SplitVerticalOutward
:
1291 pStyle
= "Split"; pDm
= "V"; pM
= "O"; break;
1292 case PDFWriter::PageTransition::BlindsHorizontal
:
1293 pStyle
= "Blinds"; pDm
= "H"; break;
1294 case PDFWriter::PageTransition::BlindsVertical
:
1295 pStyle
= "Blinds"; pDm
= "V"; break;
1296 case PDFWriter::PageTransition::BoxInward
:
1297 pStyle
= "Box"; pM
= "I"; break;
1298 case PDFWriter::PageTransition::BoxOutward
:
1299 pStyle
= "Box"; pM
= "O"; break;
1300 case PDFWriter::PageTransition::WipeLeftToRight
:
1301 pStyle
= "Wipe"; pDi
= "0"; break;
1302 case PDFWriter::PageTransition::WipeBottomToTop
:
1303 pStyle
= "Wipe"; pDi
= "90"; break;
1304 case PDFWriter::PageTransition::WipeRightToLeft
:
1305 pStyle
= "Wipe"; pDi
= "180"; break;
1306 case PDFWriter::PageTransition::WipeTopToBottom
:
1307 pStyle
= "Wipe"; pDi
= "270"; break;
1308 case PDFWriter::PageTransition::Dissolve
:
1309 pStyle
= "Dissolve"; break;
1310 case PDFWriter::PageTransition::Regular
:
1316 aLine
.append( "/S/" );
1317 aLine
.append( pStyle
);
1318 aLine
.append( "\n" );
1322 aLine
.append( "/Dm/" );
1323 aLine
.append( pDm
);
1324 aLine
.append( "\n" );
1328 aLine
.append( "/M/" );
1330 aLine
.append( "\n" );
1334 aLine
.append( "/Di " );
1335 aLine
.append( pDi
);
1336 aLine
.append( "\n" );
1338 aLine
.append( ">>\n" );
1340 if( m_pWriter
->getVersion() > PDFWriter::PDFVersion::PDF_1_3
&& ! m_pWriter
->m_bIsPDF_A1
)
1342 aLine
.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1344 aLine
.append( "/Contents" );
1345 unsigned int nStreamObjects
= m_aStreamObjects
.size();
1346 if( nStreamObjects
> 1 )
1347 aLine
.append( '[' );
1348 for(sal_Int32 i
: m_aStreamObjects
)
1350 aLine
.append( ' ' );
1352 aLine
.append( " 0 R" );
1354 if( nStreamObjects
> 1 )
1355 aLine
.append( ']' );
1356 aLine
.append( ">>\nendobj\n\n" );
1357 return m_pWriter
->writeBuffer( aLine
.getStr(), aLine
.getLength() );
1362 template < class GEOMETRY
>
1363 GEOMETRY
lcl_convert( const MapMode
& _rSource
, const MapMode
& _rDest
, OutputDevice
* _pPixelConversion
, const GEOMETRY
& _rObject
)
1366 if ( MapUnit::MapPixel
== _rSource
.GetMapUnit() )
1368 aPoint
= _pPixelConversion
->PixelToLogic( _rObject
, _rDest
);
1372 aPoint
= OutputDevice::LogicToLogic( _rObject
, _rSource
, _rDest
);
1378 void PDFWriterImpl::PDFPage::appendPoint( const Point
& rPoint
, OStringBuffer
& rBuffer
) const
1380 Point
aPoint( lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1381 m_pWriter
->m_aMapMode
,
1382 m_pWriter
->getReferenceDevice(),
1385 sal_Int32 nValue
= aPoint
.X();
1387 appendFixedInt( nValue
, rBuffer
);
1389 rBuffer
.append( ' ' );
1391 nValue
= pointToPixel(getHeight()) - aPoint
.Y();
1393 appendFixedInt( nValue
, rBuffer
);
1396 void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint
& rPoint
, OStringBuffer
& rBuffer
) const
1398 double fValue
= pixelToPoint(rPoint
.getX());
1400 appendDouble( fValue
, rBuffer
, nLog10Divisor
);
1401 rBuffer
.append( ' ' );
1402 fValue
= double(getHeight()) - pixelToPoint(rPoint
.getY());
1403 appendDouble( fValue
, rBuffer
, nLog10Divisor
);
1406 void PDFWriterImpl::PDFPage::appendRect( const tools::Rectangle
& rRect
, OStringBuffer
& rBuffer
) const
1408 appendPoint( rRect
.BottomLeft() + Point( 0, 1 ), rBuffer
);
1409 rBuffer
.append( ' ' );
1410 appendMappedLength( (sal_Int32
)rRect
.GetWidth(), rBuffer
, false );
1411 rBuffer
.append( ' ' );
1412 appendMappedLength( (sal_Int32
)rRect
.GetHeight(), rBuffer
);
1413 rBuffer
.append( " re" );
1416 void PDFWriterImpl::PDFPage::convertRect( tools::Rectangle
& rRect
) const
1418 Point aLL
= lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1419 m_pWriter
->m_aMapMode
,
1420 m_pWriter
->getReferenceDevice(),
1421 rRect
.BottomLeft() + Point( 0, 1 )
1423 Size aSize
= lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1424 m_pWriter
->m_aMapMode
,
1425 m_pWriter
->getReferenceDevice(),
1427 rRect
.Left() = aLL
.X();
1428 rRect
.Right() = aLL
.X() + aSize
.Width();
1429 rRect
.Top() = pointToPixel(getHeight()) - aLL
.Y();
1430 rRect
.Bottom() = rRect
.Top() + aSize
.Height();
1433 void PDFWriterImpl::PDFPage::appendPolygon( const tools::Polygon
& rPoly
, OStringBuffer
& rBuffer
, bool bClose
) const
1435 sal_uInt16 nPoints
= rPoly
.GetSize();
1437 * #108582# applications do weird things
1439 sal_uInt32 nBufLen
= rBuffer
.getLength();
1442 const PolyFlags
* pFlagArray
= rPoly
.GetConstFlagAry();
1443 appendPoint( rPoly
[0], rBuffer
);
1444 rBuffer
.append( " m\n" );
1445 for( sal_uInt16 i
= 1; i
< nPoints
; i
++ )
1447 if( pFlagArray
&& pFlagArray
[i
] == PolyFlags::Control
&& nPoints
-i
> 2 )
1450 SAL_WARN_IF( pFlagArray
[i
+1] != PolyFlags::Control
|| pFlagArray
[i
+2] == PolyFlags::Control
, "vcl.pdfwriter", "unexpected sequence of control points" );
1451 appendPoint( rPoly
[i
], rBuffer
);
1452 rBuffer
.append( " " );
1453 appendPoint( rPoly
[i
+1], rBuffer
);
1454 rBuffer
.append( " " );
1455 appendPoint( rPoly
[i
+2], rBuffer
);
1456 rBuffer
.append( " c" );
1457 i
+= 2; // add additionally consumed points
1462 appendPoint( rPoly
[i
], rBuffer
);
1463 rBuffer
.append( " l" );
1465 if( (rBuffer
.getLength() - nBufLen
) > 65 )
1467 rBuffer
.append( "\n" );
1468 nBufLen
= rBuffer
.getLength();
1471 rBuffer
.append( " " );
1474 rBuffer
.append( "h\n" );
1478 void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon
& rPoly
, OStringBuffer
& rBuffer
) const
1480 basegfx::B2DPolygon
aPoly( lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1481 m_pWriter
->m_aMapMode
,
1482 m_pWriter
->getReferenceDevice(),
1485 if( basegfx::tools::isRectangle( aPoly
) )
1487 basegfx::B2DRange
aRange( aPoly
.getB2DRange() );
1488 basegfx::B2DPoint
aBL( aRange
.getMinX(), aRange
.getMaxY() );
1489 appendPixelPoint( aBL
, rBuffer
);
1490 rBuffer
.append( ' ' );
1491 appendMappedLength( aRange
.getWidth(), rBuffer
, false, nLog10Divisor
);
1492 rBuffer
.append( ' ' );
1493 appendMappedLength( aRange
.getHeight(), rBuffer
, true, nLog10Divisor
);
1494 rBuffer
.append( " re\n" );
1497 sal_uInt32 nPoints
= aPoly
.count();
1500 sal_uInt32 nBufLen
= rBuffer
.getLength();
1501 basegfx::B2DPoint
aLastPoint( aPoly
.getB2DPoint( 0 ) );
1502 appendPixelPoint( aLastPoint
, rBuffer
);
1503 rBuffer
.append( " m\n" );
1504 for( sal_uInt32 i
= 1; i
<= nPoints
; i
++ )
1506 if( i
!= nPoints
|| aPoly
.isClosed() )
1508 sal_uInt32 nCurPoint
= i
% nPoints
;
1509 sal_uInt32 nLastPoint
= i
-1;
1510 basegfx::B2DPoint
aPoint( aPoly
.getB2DPoint( nCurPoint
) );
1511 if( aPoly
.isNextControlPointUsed( nLastPoint
) &&
1512 aPoly
.isPrevControlPointUsed( nCurPoint
) )
1514 appendPixelPoint( aPoly
.getNextControlPoint( nLastPoint
), rBuffer
);
1515 rBuffer
.append( ' ' );
1516 appendPixelPoint( aPoly
.getPrevControlPoint( nCurPoint
), rBuffer
);
1517 rBuffer
.append( ' ' );
1518 appendPixelPoint( aPoint
, rBuffer
);
1519 rBuffer
.append( " c" );
1521 else if( aPoly
.isNextControlPointUsed( nLastPoint
) )
1523 appendPixelPoint( aPoly
.getNextControlPoint( nLastPoint
), rBuffer
);
1524 rBuffer
.append( ' ' );
1525 appendPixelPoint( aPoint
, rBuffer
);
1526 rBuffer
.append( " y" );
1528 else if( aPoly
.isPrevControlPointUsed( nCurPoint
) )
1530 appendPixelPoint( aPoly
.getPrevControlPoint( nCurPoint
), rBuffer
);
1531 rBuffer
.append( ' ' );
1532 appendPixelPoint( aPoint
, rBuffer
);
1533 rBuffer
.append( " v" );
1537 appendPixelPoint( aPoint
, rBuffer
);
1538 rBuffer
.append( " l" );
1540 if( (rBuffer
.getLength() - nBufLen
) > 65 )
1542 rBuffer
.append( "\n" );
1543 nBufLen
= rBuffer
.getLength();
1546 rBuffer
.append( " " );
1549 rBuffer
.append( "h\n" );
1553 void PDFWriterImpl::PDFPage::appendPolyPolygon( const tools::PolyPolygon
& rPolyPoly
, OStringBuffer
& rBuffer
) const
1555 sal_uInt16 nPolygons
= rPolyPoly
.Count();
1556 for( sal_uInt16 n
= 0; n
< nPolygons
; n
++ )
1557 appendPolygon( rPolyPoly
[n
], rBuffer
);
1560 void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon
& rPolyPoly
, OStringBuffer
& rBuffer
) const
1562 sal_uInt32 nPolygons
= rPolyPoly
.count();
1563 for( sal_uInt32 n
= 0; n
< nPolygons
; n
++ )
1564 appendPolygon( rPolyPoly
.getB2DPolygon( n
), rBuffer
);
1567 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength
, OStringBuffer
& rBuffer
, bool bVertical
, sal_Int32
* pOutLength
) const
1569 sal_Int32 nValue
= nLength
;
1572 rBuffer
.append( '-' );
1575 Size
aSize( lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1576 m_pWriter
->m_aMapMode
,
1577 m_pWriter
->getReferenceDevice(),
1578 Size( nValue
, nValue
) ) );
1579 nValue
= bVertical
? aSize
.Height() : aSize
.Width();
1581 *pOutLength
= ((nLength
< 0 ) ? -nValue
: nValue
);
1583 appendFixedInt( nValue
, rBuffer
);
1586 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength
, OStringBuffer
& rBuffer
, bool bVertical
, sal_Int32 nPrecision
) const
1588 Size
aSize( lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1589 m_pWriter
->m_aMapMode
,
1590 m_pWriter
->getReferenceDevice(),
1591 Size( 1000, 1000 ) ) );
1592 fLength
*= pixelToPoint((double)(bVertical
? aSize
.Height() : aSize
.Width()) / 1000.0);
1593 appendDouble( fLength
, rBuffer
, nPrecision
);
1596 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo
& rInfo
, OStringBuffer
& rBuffer
) const
1598 if(LineStyle::Dash
== rInfo
.GetStyle() && rInfo
.GetDashLen() != rInfo
.GetDotLen())
1600 // dashed and non-degraded case, check for implementation limits of dash array
1601 // in PDF reader apps (e.g. acroread)
1602 if(2 * (rInfo
.GetDashCount() + rInfo
.GetDotCount()) > 10)
1608 if(basegfx::B2DLineJoin::NONE
!= rInfo
.GetLineJoin())
1610 // LineJoin used, ExtLineInfo required
1614 if(css::drawing::LineCap_BUTT
!= rInfo
.GetLineCap())
1616 // LineCap used, ExtLineInfo required
1620 if( rInfo
.GetStyle() == LineStyle::Dash
)
1622 rBuffer
.append( "[ " );
1623 if( rInfo
.GetDashLen() == rInfo
.GetDotLen() ) // degraded case
1625 appendMappedLength( (sal_Int32
)rInfo
.GetDashLen(), rBuffer
);
1626 rBuffer
.append( ' ' );
1627 appendMappedLength( (sal_Int32
)rInfo
.GetDistance(), rBuffer
);
1628 rBuffer
.append( ' ' );
1632 for( int n
= 0; n
< rInfo
.GetDashCount(); n
++ )
1634 appendMappedLength( (sal_Int32
)rInfo
.GetDashLen(), rBuffer
);
1635 rBuffer
.append( ' ' );
1636 appendMappedLength( (sal_Int32
)rInfo
.GetDistance(), rBuffer
);
1637 rBuffer
.append( ' ' );
1639 for( int m
= 0; m
< rInfo
.GetDotCount(); m
++ )
1641 appendMappedLength( (sal_Int32
)rInfo
.GetDotLen(), rBuffer
);
1642 rBuffer
.append( ' ' );
1643 appendMappedLength( (sal_Int32
)rInfo
.GetDistance(), rBuffer
);
1644 rBuffer
.append( ' ' );
1647 rBuffer
.append( "] 0 d\n" );
1650 if( rInfo
.GetWidth() > 1 )
1652 appendMappedLength( (sal_Int32
)rInfo
.GetWidth(), rBuffer
);
1653 rBuffer
.append( " w\n" );
1655 else if( rInfo
.GetWidth() == 0 )
1658 appendDouble( 72.0/double(m_pWriter
->getReferenceDevice()->GetDPIX()), rBuffer
);
1659 rBuffer
.append( " w\n" );
1665 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth
, sal_Int32 nY
, sal_Int32 nDelta
, OStringBuffer
& rBuffer
) const
1672 rBuffer
.append( "0 " );
1673 appendMappedLength( nY
, rBuffer
);
1674 rBuffer
.append( " m\n" );
1675 for( sal_Int32 n
= 0; n
< nWidth
; )
1678 appendMappedLength( n
, rBuffer
, false );
1679 rBuffer
.append( ' ' );
1680 appendMappedLength( nDelta
+nY
, rBuffer
);
1681 rBuffer
.append( ' ' );
1683 appendMappedLength( n
, rBuffer
, false );
1684 rBuffer
.append( ' ' );
1685 appendMappedLength( nY
, rBuffer
);
1686 rBuffer
.append( " v " );
1690 appendMappedLength( n
, rBuffer
, false );
1691 rBuffer
.append( ' ' );
1692 appendMappedLength( nY
-nDelta
, rBuffer
);
1693 rBuffer
.append( ' ' );
1695 appendMappedLength( n
, rBuffer
, false );
1696 rBuffer
.append( ' ' );
1697 appendMappedLength( nY
, rBuffer
);
1698 rBuffer
.append( " v\n" );
1701 rBuffer
.append( "S\n" );
1704 PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext
& rContext
,
1705 const css::uno::Reference
< css::beans::XMaterialHolder
>& xEnc
,
1706 PDFWriter
& i_rOuterFace
)
1708 m_pReferenceDevice( nullptr ),
1709 m_aMapMode( MapUnit::MapPoint
, Point(), Fraction( 1, pointToPixel(1) ), Fraction( 1, pointToPixel(1) ) ),
1710 m_nCurrentStructElement( 0 ),
1711 m_bEmitStructure( true ),
1713 m_nInheritedPageWidth( 595 ), // default A4
1714 m_nInheritedPageHeight( 842 ), // default A4
1715 m_nCurrentPage( -1 ),
1716 m_nCatalogObject(0),
1717 m_nSignatureObject( -1 ),
1718 m_nSignatureContentOffset( 0 ),
1719 m_nSignatureLastByteRangeNoOffset( 0 ),
1720 m_nResourceDict( -1 ),
1721 m_nFontDictObject( -1 ),
1722 m_aContext(rContext
),
1723 m_aFile(m_aContext
.URL
),
1725 m_aDocDigest( rtl_digest_createMD5() ),
1726 m_aCipher( nullptr ),
1727 m_aDigest( nullptr ),
1730 m_bEncryptThisStream( false ),
1731 m_nAccessPermissions(0),
1732 m_pEncryptionBuffer( nullptr ),
1733 m_nEncryptionBufferSize( 0 ),
1734 m_bIsPDF_A1( false ),
1735 m_rOuterFace( i_rOuterFace
)
1738 static bool bOnce
= true;
1745 m_aStructure
.push_back( PDFStructureElement() );
1746 m_aStructure
[0].m_nOwnElement
= 0;
1747 m_aStructure
[0].m_nParentElement
= 0;
1750 aFont
.SetFamilyName( "Times" );
1751 aFont
.SetFontSize( Size( 0, 12 ) );
1753 GraphicsState aState
;
1754 aState
.m_aMapMode
= m_aMapMode
;
1755 aState
.m_aFont
= aFont
;
1756 m_aGraphicsStack
.push_front( aState
);
1758 osl::File::RC aError
= m_aFile
.open(osl_File_OpenFlag_Write
| osl_File_OpenFlag_Create
);
1759 if (aError
!= osl::File::E_None
)
1761 if (aError
== osl::File::E_EXIST
)
1763 aError
= m_aFile
.open(osl_File_OpenFlag_Write
);
1764 if (aError
== osl::File::E_None
)
1765 aError
= m_aFile
.setSize(0);
1768 if (aError
!= osl::File::E_None
)
1776 /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1777 m_aCipher
= rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream
);
1778 m_aDigest
= rtl_digest_createMD5();
1780 /* the size of the Codec default maximum */
1781 /* is this 0x4000 required to be the same as MAX_SIGNATURE_CONTENT_LENGTH or just coincidentally the same at the moment? */
1782 if (!checkEncryptionBufferSize(0x4000))
1790 prepareEncryption( xEnc
);
1792 if( m_aContext
.Encryption
.Encrypt() )
1795 if( m_aContext
.Encryption
.OValue
.size() != ENCRYPTED_PWD_SIZE
||
1796 m_aContext
.Encryption
.UValue
.size() != ENCRYPTED_PWD_SIZE
||
1797 m_aContext
.Encryption
.EncryptionKey
.size() != MAXIMUM_RC4_KEY_LENGTH
1800 // the field lengths are invalid ? This was not setup by initEncryption.
1801 // do not encrypt after all
1802 m_aContext
.Encryption
.OValue
.clear();
1803 m_aContext
.Encryption
.UValue
.clear();
1804 OSL_ENSURE( false, "encryption data failed sanity check, encryption disabled" );
1806 else // setup key lengths
1807 m_nAccessPermissions
= computeAccessPermissions( m_aContext
.Encryption
, m_nKeyLength
, m_nRC4KeyLength
);
1811 OStringBuffer
aBuffer( 20 );
1812 aBuffer
.append( "%PDF-" );
1813 switch( m_aContext
.Version
)
1815 case PDFWriter::PDFVersion::PDF_1_2
: aBuffer
.append( "1.2" );break;
1816 case PDFWriter::PDFVersion::PDF_1_3
: aBuffer
.append( "1.3" );break;
1817 case PDFWriter::PDFVersion::PDF_A_1
:
1819 case PDFWriter::PDFVersion::PDF_1_4
: aBuffer
.append( "1.4" );break;
1820 case PDFWriter::PDFVersion::PDF_1_5
: aBuffer
.append( "1.5" );break;
1822 // append something binary as comment (suggested in PDF Reference)
1823 aBuffer
.append( "\n%\303\244\303\274\303\266\303\237\n" );
1824 if( !writeBuffer( aBuffer
.getStr(), aBuffer
.getLength() ) )
1831 // insert outline root
1832 m_aOutline
.push_back( PDFOutlineEntry() );
1834 m_bIsPDF_A1
= (m_aContext
.Version
== PDFWriter::PDFVersion::PDF_A_1
);
1836 m_aContext
.Version
= PDFWriter::PDFVersion::PDF_1_4
; //meaning we need PDF 1.4, PDF/A flavour
1839 PDFWriterImpl::~PDFWriterImpl()
1842 rtl_digest_destroyMD5( m_aDocDigest
);
1843 m_pReferenceDevice
.disposeAndClear();
1846 rtl_cipher_destroyARCFOUR( m_aCipher
);
1848 rtl_digest_destroyMD5( m_aDigest
);
1850 rtl_freeMemory( m_pEncryptionBuffer
);
1853 void PDFWriterImpl::setupDocInfo()
1855 std::vector
< sal_uInt8
> aId
;
1856 m_aCreationDateString
= PDFWriter::GetDateTime();
1857 computeDocumentIdentifier( aId
, m_aContext
.DocumentInfo
, m_aCreationDateString
, m_aCreationMetaDateString
);
1858 if( m_aContext
.Encryption
.DocumentIdentifier
.empty() )
1859 m_aContext
.Encryption
.DocumentIdentifier
= aId
;
1862 OString
PDFWriter::GetDateTime()
1866 TimeValue aTVal
, aGMT
;
1868 osl_getSystemTime(&aGMT
);
1869 osl_getLocalTimeFromSystemTime(&aGMT
, &aTVal
);
1870 osl_getDateTimeFromTimeValue(&aTVal
, &aDT
);
1872 aRet
.append((sal_Char
)('0' + ((aDT
.Year
/ 1000) % 10)));
1873 aRet
.append((sal_Char
)('0' + ((aDT
.Year
/ 100) % 10)));
1874 aRet
.append((sal_Char
)('0' + ((aDT
.Year
/ 10) % 10)));
1875 aRet
.append((sal_Char
)('0' + (aDT
.Year
% 10)));
1876 aRet
.append((sal_Char
)('0' + ((aDT
.Month
/ 10) % 10)));
1877 aRet
.append((sal_Char
)('0' + (aDT
.Month
% 10)));
1878 aRet
.append((sal_Char
)('0' + ((aDT
.Day
/ 10) % 10)));
1879 aRet
.append((sal_Char
)('0' + (aDT
.Day
% 10)));
1880 aRet
.append((sal_Char
)('0' + ((aDT
.Hours
/ 10) % 10)));
1881 aRet
.append((sal_Char
)('0' + (aDT
.Hours
% 10)));
1882 aRet
.append((sal_Char
)('0' + ((aDT
.Minutes
/ 10) % 10)));
1883 aRet
.append((sal_Char
)('0' + (aDT
.Minutes
% 10)));
1884 aRet
.append((sal_Char
)('0' + ((aDT
.Seconds
/ 10) % 10)));
1885 aRet
.append((sal_Char
)('0' + (aDT
.Seconds
% 10)));
1887 sal_uInt32 nDelta
= 0;
1888 if (aGMT
.Seconds
> aTVal
.Seconds
)
1891 nDelta
= aGMT
.Seconds
-aTVal
.Seconds
;
1893 else if (aGMT
.Seconds
< aTVal
.Seconds
)
1896 nDelta
= aTVal
.Seconds
-aGMT
.Seconds
;
1903 aRet
.append((sal_Char
)('0' + ((nDelta
/ 36000) % 10)));
1904 aRet
.append((sal_Char
)('0' + ((nDelta
/ 3600) % 10)));
1906 aRet
.append((sal_Char
)('0' + ((nDelta
/ 600) % 6)));
1907 aRet
.append((sal_Char
)('0' + ((nDelta
/ 60) % 10)));
1911 return aRet
.makeStringAndClear();
1914 void PDFWriterImpl::computeDocumentIdentifier( std::vector
< sal_uInt8
>& o_rIdentifier
,
1915 const vcl::PDFWriter::PDFDocInfo
& i_rDocInfo
,
1916 const OString
& i_rCString1
,
1917 OString
& o_rCString2
1920 o_rIdentifier
.clear();
1922 //build the document id
1923 OString aInfoValuesOut
;
1924 OStringBuffer
aID( 1024 );
1925 if( !i_rDocInfo
.Title
.isEmpty() )
1926 PDFWriter::AppendUnicodeTextString(i_rDocInfo
.Title
, aID
);
1927 if( !i_rDocInfo
.Author
.isEmpty() )
1928 PDFWriter::AppendUnicodeTextString(i_rDocInfo
.Author
, aID
);
1929 if( !i_rDocInfo
.Subject
.isEmpty() )
1930 PDFWriter::AppendUnicodeTextString(i_rDocInfo
.Subject
, aID
);
1931 if( !i_rDocInfo
.Keywords
.isEmpty() )
1932 PDFWriter::AppendUnicodeTextString(i_rDocInfo
.Keywords
, aID
);
1933 if( !i_rDocInfo
.Creator
.isEmpty() )
1934 PDFWriter::AppendUnicodeTextString(i_rDocInfo
.Creator
, aID
);
1935 if( !i_rDocInfo
.Producer
.isEmpty() )
1936 PDFWriter::AppendUnicodeTextString(i_rDocInfo
.Producer
, aID
);
1938 TimeValue aTVal
, aGMT
;
1940 osl_getSystemTime( &aGMT
);
1941 osl_getLocalTimeFromSystemTime( &aGMT
, &aTVal
);
1942 osl_getDateTimeFromTimeValue( &aTVal
, &aDT
);
1943 OStringBuffer
aCreationMetaDateString(64);
1945 // i59651: we fill the Metadata date string as well, if PDF/A is requested
1946 // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1947 // local time zone offset UTC only, whereas Acrobat 8 seems
1948 // to use the localtime notation only
1949 // according to a recommendation in XMP Specification (Jan 2004, page 75)
1950 // the Acrobat way seems the right approach
1951 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Year
/1000)%10)) );
1952 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Year
/100)%10)) );
1953 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Year
/10)%10)) );
1954 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Year
)%10)) );
1955 aCreationMetaDateString
.append( "-" );
1956 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Month
/10)%10)) );
1957 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Month
)%10)) );
1958 aCreationMetaDateString
.append( "-" );
1959 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Day
/10)%10)) );
1960 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Day
)%10)) );
1961 aCreationMetaDateString
.append( "T" );
1962 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Hours
/10)%10)) );
1963 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Hours
)%10)) );
1964 aCreationMetaDateString
.append( ":" );
1965 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Minutes
/10)%10)) );
1966 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Minutes
)%10)) );
1967 aCreationMetaDateString
.append( ":" );
1968 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Seconds
/10)%10)) );
1969 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Seconds
)%10)) );
1971 sal_uInt32 nDelta
= 0;
1972 if( aGMT
.Seconds
> aTVal
.Seconds
)
1974 nDelta
= aGMT
.Seconds
-aTVal
.Seconds
;
1975 aCreationMetaDateString
.append( "-" );
1977 else if( aGMT
.Seconds
< aTVal
.Seconds
)
1979 nDelta
= aTVal
.Seconds
-aGMT
.Seconds
;
1980 aCreationMetaDateString
.append( "+" );
1984 aCreationMetaDateString
.append( "Z" );
1989 aCreationMetaDateString
.append( (sal_Char
)('0' + ((nDelta
/36000)%10)) );
1990 aCreationMetaDateString
.append( (sal_Char
)('0' + ((nDelta
/3600)%10)) );
1991 aCreationMetaDateString
.append( ":" );
1992 aCreationMetaDateString
.append( (sal_Char
)('0' + ((nDelta
/600)%6)) );
1993 aCreationMetaDateString
.append( (sal_Char
)('0' + ((nDelta
/60)%10)) );
1995 aID
.append( i_rCString1
.getStr(), i_rCString1
.getLength() );
1997 aInfoValuesOut
= aID
.makeStringAndClear();
1998 o_rCString2
= aCreationMetaDateString
.makeStringAndClear();
2000 rtlDigest aDigest
= rtl_digest_createMD5();
2001 OSL_ENSURE( aDigest
!= nullptr, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" );
2004 rtlDigestError nError
= rtl_digest_updateMD5( aDigest
, &aGMT
, sizeof( aGMT
) );
2005 if( nError
== rtl_Digest_E_None
)
2006 nError
= rtl_digest_updateMD5( aDigest
, aInfoValuesOut
.getStr(), aInfoValuesOut
.getLength() );
2007 if( nError
== rtl_Digest_E_None
)
2009 o_rIdentifier
= std::vector
< sal_uInt8
>( 16, 0 );
2010 //the binary form of the doc id is needed for encryption stuff
2011 rtl_digest_getMD5( aDigest
, &o_rIdentifier
[0], 16 );
2013 rtl_digest_destroyMD5(aDigest
);
2017 /* i12626 methods */
2019 check if the Unicode string must be encrypted or not, perform the requested task,
2020 append the string as unicode hex, encrypted if needed
2022 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const OUString
& rInString
, const sal_Int32 nInObjectNumber
, OStringBuffer
& rOutBuffer
)
2024 rOutBuffer
.append( "<" );
2025 if( m_aContext
.Encryption
.Encrypt() )
2027 const sal_Unicode
* pStr
= rInString
.getStr();
2028 sal_Int32 nLen
= rInString
.getLength();
2029 //prepare a unicode string, encrypt it
2030 if( checkEncryptionBufferSize( nLen
*2 ) )
2032 enableStringEncryption( nInObjectNumber
);
2033 sal_uInt8
*pCopy
= m_pEncryptionBuffer
;
2034 sal_Int32 nChars
= 2;
2037 // we need to prepare a byte stream from the unicode string buffer
2038 for( int i
= 0; i
< nLen
; i
++ )
2040 sal_Unicode aUnChar
= pStr
[i
];
2041 *pCopy
++ = (sal_uInt8
)( aUnChar
>> 8 );
2042 *pCopy
++ = (sal_uInt8
)( aUnChar
& 255 );
2046 rtl_cipher_encodeARCFOUR( m_aCipher
, m_pEncryptionBuffer
, nChars
, m_pEncryptionBuffer
, nChars
);
2047 //now append, hexadecimal (appendHex), the encrypted result
2048 for(int i
= 0; i
< nChars
; i
++)
2049 appendHex( m_pEncryptionBuffer
[i
], rOutBuffer
);
2053 PDFWriter::AppendUnicodeTextString(rInString
, rOutBuffer
);
2054 rOutBuffer
.append( ">" );
2057 inline void PDFWriterImpl::appendLiteralStringEncrypt( OStringBuffer
& rInString
, const sal_Int32 nInObjectNumber
, OStringBuffer
& rOutBuffer
)
2059 rOutBuffer
.append( "(" );
2060 sal_Int32 nChars
= rInString
.getLength();
2061 //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
2062 if( m_aContext
.Encryption
.Encrypt() && checkEncryptionBufferSize( nChars
) )
2064 //encrypt the string in a buffer, then append it
2065 enableStringEncryption( nInObjectNumber
);
2066 rtl_cipher_encodeARCFOUR( m_aCipher
, rInString
.getStr(), nChars
, m_pEncryptionBuffer
, nChars
);
2067 appendLiteralString( reinterpret_cast<sal_Char
*>(m_pEncryptionBuffer
), nChars
, rOutBuffer
);
2070 appendLiteralString( rInString
.getStr(), nChars
, rOutBuffer
);
2071 rOutBuffer
.append( ")" );
2074 inline void PDFWriterImpl::appendLiteralStringEncrypt( const OString
& rInString
, const sal_Int32 nInObjectNumber
, OStringBuffer
& rOutBuffer
)
2076 OStringBuffer
aBufferString( rInString
);
2077 appendLiteralStringEncrypt( aBufferString
, nInObjectNumber
, rOutBuffer
);
2080 void PDFWriterImpl::appendLiteralStringEncrypt( const OUString
& rInString
, const sal_Int32 nInObjectNumber
, OStringBuffer
& rOutBuffer
, rtl_TextEncoding nEnc
)
2082 OString
aBufferString( OUStringToOString( rInString
, nEnc
) );
2083 sal_Int32 nLen
= aBufferString
.getLength();
2084 OStringBuffer
aBuf( nLen
);
2085 const sal_Char
* pT
= aBufferString
.getStr();
2087 for( sal_Int32 i
= 0; i
< nLen
; i
++, pT
++ )
2089 if( (*pT
& 0x80) == 0 )
2094 appendHex( *pT
, aBuf
);
2098 aBufferString
= aBuf
.makeStringAndClear();
2099 appendLiteralStringEncrypt( aBufferString
, nInObjectNumber
, rOutBuffer
);
2102 /* end i12626 methods */
2104 void PDFWriterImpl::emitComment( const char* pComment
)
2106 OStringBuffer
aLine( 64 );
2107 aLine
.append( "% " );
2108 aLine
.append( pComment
);
2109 aLine
.append( "\n" );
2110 writeBuffer( aLine
.getStr(), aLine
.getLength() );
2113 bool PDFWriterImpl::compressStream( SvMemoryStream
* pStream
)
2115 if (!g_bDebugDisableCompression
)
2117 pStream
->Seek( STREAM_SEEK_TO_END
);
2118 sal_uLong nEndPos
= pStream
->Tell();
2119 pStream
->Seek( STREAM_SEEK_TO_BEGIN
);
2120 ZCodec
aCodec( 0x4000, 0x4000 );
2121 SvMemoryStream aStream
;
2122 aCodec
.BeginCompression();
2123 aCodec
.Write( aStream
, static_cast<const sal_uInt8
*>(pStream
->GetData()), nEndPos
);
2124 aCodec
.EndCompression();
2125 nEndPos
= aStream
.Tell();
2126 pStream
->Seek( STREAM_SEEK_TO_BEGIN
);
2127 aStream
.Seek( STREAM_SEEK_TO_BEGIN
);
2128 pStream
->SetStreamSize( nEndPos
);
2129 pStream
->WriteBytes( aStream
.GetData(), nEndPos
);
2136 void PDFWriterImpl::beginCompression()
2138 if (!g_bDebugDisableCompression
)
2140 m_pCodec
= o3tl::make_unique
<ZCodec
>( 0x4000, 0x4000 );
2141 m_pMemStream
= o3tl::make_unique
<SvMemoryStream
>();
2142 m_pCodec
->BeginCompression();
2146 void PDFWriterImpl::endCompression()
2148 if (!g_bDebugDisableCompression
&& m_pCodec
)
2150 m_pCodec
->EndCompression();
2152 sal_uInt64 nLen
= m_pMemStream
->Tell();
2153 m_pMemStream
->Seek( 0 );
2154 writeBuffer( m_pMemStream
->GetData(), nLen
);
2155 m_pMemStream
.reset();
2159 bool PDFWriterImpl::writeBuffer( const void* pBuffer
, sal_uInt64 nBytes
)
2161 if( ! m_bOpen
) // we are already down the drain
2164 if( ! nBytes
) // huh ?
2167 if( !m_aOutputStreams
.empty() )
2169 m_aOutputStreams
.front().m_pStream
->Seek( STREAM_SEEK_TO_END
);
2170 m_aOutputStreams
.front().m_pStream
->WriteBytes(
2171 pBuffer
, sal::static_int_cast
<std::size_t>(nBytes
));
2175 sal_uInt64 nWritten
;
2178 m_pCodec
->Write( *m_pMemStream
, static_cast<const sal_uInt8
*>(pBuffer
), (sal_uLong
)nBytes
);
2184 if( m_bEncryptThisStream
)
2186 /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
2187 buffOK
= checkEncryptionBufferSize( static_cast<sal_Int32
>(nBytes
) );
2189 rtl_cipher_encodeARCFOUR( m_aCipher
,
2190 pBuffer
, static_cast<sal_Size
>(nBytes
),
2191 m_pEncryptionBuffer
, static_cast<sal_Size
>(nBytes
) );
2194 const void* pWriteBuffer
= ( m_bEncryptThisStream
&& buffOK
) ? m_pEncryptionBuffer
: pBuffer
;
2196 rtl_digest_updateMD5( m_aDocDigest
, pWriteBuffer
, static_cast<sal_uInt32
>(nBytes
) );
2198 if (m_aFile
.write(pWriteBuffer
, nBytes
, nWritten
) != osl::File::E_None
)
2201 if( nWritten
!= nBytes
)
2208 return nWritten
== nBytes
;
2211 OutputDevice
* PDFWriterImpl::getReferenceDevice()
2213 if( ! m_pReferenceDevice
)
2215 VclPtrInstance
<VirtualDevice
> pVDev(DeviceFormat::DEFAULT
);
2217 m_pReferenceDevice
= pVDev
;
2219 if( m_aContext
.DPIx
== 0 || m_aContext
.DPIy
== 0 )
2220 pVDev
->SetReferenceDevice( VirtualDevice::RefDevMode::PDF1
);
2222 pVDev
->SetReferenceDevice( m_aContext
.DPIx
, m_aContext
.DPIy
);
2224 pVDev
->SetOutputSizePixel( Size( 640, 480 ) );
2225 pVDev
->SetMapMode( MapUnit::MapMM
);
2227 m_pReferenceDevice
->mpPDFWriter
= this;
2228 m_pReferenceDevice
->ImplUpdateFontData();
2230 return m_pReferenceDevice
;
2233 static FontAttributes
GetDevFontAttributes( const PDFWriterImpl::BuiltinFont
& rBuiltin
)
2235 FontAttributes aDFA
;
2236 aDFA
.SetFamilyName( OUString::createFromAscii( rBuiltin
.m_pName
) );
2237 aDFA
.SetStyleName( OUString::createFromAscii( rBuiltin
.m_pStyleName
) );
2238 aDFA
.SetFamilyType( rBuiltin
.m_eFamily
);
2239 aDFA
.SetSymbolFlag( rBuiltin
.m_eCharSet
!= RTL_TEXTENCODING_MS_1252
);
2240 aDFA
.SetPitch( rBuiltin
.m_ePitch
);
2241 aDFA
.SetWeight( rBuiltin
.m_eWeight
);
2242 aDFA
.SetItalic( rBuiltin
.m_eItalic
);
2243 aDFA
.SetWidthType( rBuiltin
.m_eWidthType
);
2245 aDFA
.SetQuality( 50000 );
2249 PdfBuiltinFontFace::PdfBuiltinFontFace( const PDFWriterImpl::BuiltinFont
& rBuiltin
)
2250 : PhysicalFontFace( GetDevFontAttributes(rBuiltin
) ),
2251 mrBuiltin( rBuiltin
)
2254 LogicalFontInstance
* PdfBuiltinFontFace::CreateFontInstance( FontSelectPattern
& rFSD
) const
2256 LogicalFontInstance
* pEntry
= new LogicalFontInstance( rFSD
);
2261 void PDFWriterImpl::newPage( double nPageWidth
, double nPageHeight
, PDFWriter::Orientation eOrientation
)
2264 m_nCurrentPage
= m_aPages
.size();
2265 m_aPages
.push_back( PDFPage(this, nPageWidth
, nPageHeight
, eOrientation
) );
2266 m_aPages
.back().m_nPageIndex
= m_nCurrentPage
;
2267 m_aPages
.back().beginStream();
2269 // setup global graphics state
2270 // linewidth is "1 pixel" by default
2271 OStringBuffer
aBuf( 16 );
2272 appendDouble( 72.0/double(getReferenceDevice()->GetDPIX()), aBuf
);
2273 aBuf
.append( " w\n" );
2274 writeBuffer( aBuf
.getStr(), aBuf
.getLength() );
2277 void PDFWriterImpl::endPage()
2279 if( !m_aPages
.empty() )
2281 // close eventual MC sequence
2282 endStructureElementMCSeq();
2285 if( !m_aOutputStreams
.empty() )
2287 OSL_FAIL( "redirection across pages !!!" );
2288 m_aOutputStreams
.clear(); // leak !
2289 m_aMapMode
.SetOrigin( Point() );
2292 m_aGraphicsStack
.clear();
2293 m_aGraphicsStack
.push_back( GraphicsState() );
2295 // this should pop the PDF graphics stack if necessary
2296 updateGraphicsState();
2298 m_aPages
.back().endStream();
2300 // reset the default font
2302 aFont
.SetFamilyName( "Times" );
2303 aFont
.SetFontSize( Size( 0, 12 ) );
2305 m_aCurrentPDFState
= m_aGraphicsStack
.front();
2306 m_aGraphicsStack
.front().m_aFont
= aFont
;
2308 for( std::list
<BitmapEmit
>::iterator it
= m_aBitmaps
.begin();
2309 it
!= m_aBitmaps
.end(); ++it
)
2311 if( ! it
->m_aBitmap
.IsEmpty() )
2313 writeBitmapObject( *it
);
2314 it
->m_aBitmap
= BitmapEx();
2317 for( std::list
<JPGEmit
>::iterator jpeg
= m_aJPGs
.begin(); jpeg
!= m_aJPGs
.end(); ++jpeg
)
2319 if( jpeg
->m_pStream
)
2322 jpeg
->m_pStream
.reset();
2323 jpeg
->m_aMask
= Bitmap();
2326 for( std::list
<TransparencyEmit
>::iterator t
= m_aTransparentObjects
.begin();
2327 t
!= m_aTransparentObjects
.end(); ++t
)
2329 if( t
->m_pContentStream
)
2331 writeTransparentObject( *t
);
2332 delete t
->m_pContentStream
;
2333 t
->m_pContentStream
= nullptr;
2339 sal_Int32
PDFWriterImpl::createObject()
2341 m_aObjects
.push_back( ~0U );
2342 return m_aObjects
.size();
2345 bool PDFWriterImpl::updateObject( sal_Int32 n
)
2350 sal_uInt64 nOffset
= ~0U;
2351 osl::File::RC aError
= m_aFile
.getPos(nOffset
);
2352 SAL_WARN_IF( aError
!= osl::File::E_None
, "vcl.pdfwriter", "could not register object" );
2353 if (aError
!= osl::File::E_None
)
2358 m_aObjects
[ n
-1 ] = nOffset
;
2359 return aError
== osl::File::E_None
;
2362 #define CHECK_RETURN( x ) if( !(x) ) return 0
2363 #define CHECK_RETURN2( x ) if( !(x) ) return
2365 sal_Int32
PDFWriterImpl::emitStructParentTree( sal_Int32 nObject
)
2369 OStringBuffer
aLine( 1024 );
2371 aLine
.append( nObject
);
2372 aLine
.append( " 0 obj\n"
2374 sal_Int32 nTreeItems
= m_aStructParentTree
.size();
2375 for( sal_Int32 n
= 0; n
< nTreeItems
; n
++ )
2378 aLine
.append( ' ' );
2379 aLine
.append( m_aStructParentTree
[n
] );
2380 aLine
.append( "\n" );
2382 aLine
.append( "]>>\nendobj\n\n" );
2383 CHECK_RETURN( updateObject( nObject
) );
2384 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
2389 const sal_Char
* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr
)
2391 static std::map
< PDFWriter::StructAttribute
, const char* > aAttributeStrings
;
2393 if( aAttributeStrings
.empty() )
2395 aAttributeStrings
[ PDFWriter::Placement
] = "Placement";
2396 aAttributeStrings
[ PDFWriter::WritingMode
] = "WritingMode";
2397 aAttributeStrings
[ PDFWriter::SpaceBefore
] = "SpaceBefore";
2398 aAttributeStrings
[ PDFWriter::SpaceAfter
] = "SpaceAfter";
2399 aAttributeStrings
[ PDFWriter::StartIndent
] = "StartIndent";
2400 aAttributeStrings
[ PDFWriter::EndIndent
] = "EndIndent";
2401 aAttributeStrings
[ PDFWriter::TextIndent
] = "TextIndent";
2402 aAttributeStrings
[ PDFWriter::TextAlign
] = "TextAlign";
2403 aAttributeStrings
[ PDFWriter::Width
] = "Width";
2404 aAttributeStrings
[ PDFWriter::Height
] = "Height";
2405 aAttributeStrings
[ PDFWriter::BlockAlign
] = "BlockAlign";
2406 aAttributeStrings
[ PDFWriter::InlineAlign
] = "InlineAlign";
2407 aAttributeStrings
[ PDFWriter::LineHeight
] = "LineHeight";
2408 aAttributeStrings
[ PDFWriter::BaselineShift
] = "BaselineShift";
2409 aAttributeStrings
[ PDFWriter::TextDecorationType
] = "TextDecorationType";
2410 aAttributeStrings
[ PDFWriter::ListNumbering
] = "ListNumbering";
2411 aAttributeStrings
[ PDFWriter::RowSpan
] = "RowSpan";
2412 aAttributeStrings
[ PDFWriter::ColSpan
] = "ColSpan";
2413 aAttributeStrings
[ PDFWriter::LinkAnnotation
] = "LinkAnnotation";
2416 std::map
< PDFWriter::StructAttribute
, const char* >::const_iterator it
=
2417 aAttributeStrings
.find( eAttr
);
2419 #if OSL_DEBUG_LEVEL > 1
2420 if( it
== aAttributeStrings
.end() )
2421 SAL_INFO("vcl.pdfwriter", "invalid PDFWriter::StructAttribute " << eAttr
);
2424 return it
!= aAttributeStrings
.end() ? it
->second
: "";
2427 const sal_Char
* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal
)
2429 static std::map
< PDFWriter::StructAttributeValue
, const char* > aValueStrings
;
2431 if( aValueStrings
.empty() )
2433 aValueStrings
[ PDFWriter::NONE
] = "None";
2434 aValueStrings
[ PDFWriter::Block
] = "Block";
2435 aValueStrings
[ PDFWriter::Inline
] = "Inline";
2436 aValueStrings
[ PDFWriter::Before
] = "Before";
2437 aValueStrings
[ PDFWriter::After
] = "After";
2438 aValueStrings
[ PDFWriter::Start
] = "Start";
2439 aValueStrings
[ PDFWriter::End
] = "End";
2440 aValueStrings
[ PDFWriter::LrTb
] = "LrTb";
2441 aValueStrings
[ PDFWriter::RlTb
] = "RlTb";
2442 aValueStrings
[ PDFWriter::TbRl
] = "TbRl";
2443 aValueStrings
[ PDFWriter::Center
] = "Center";
2444 aValueStrings
[ PDFWriter::Justify
] = "Justify";
2445 aValueStrings
[ PDFWriter::Auto
] = "Auto";
2446 aValueStrings
[ PDFWriter::Middle
] = "Middle";
2447 aValueStrings
[ PDFWriter::Normal
] = "Normal";
2448 aValueStrings
[ PDFWriter::Underline
] = "Underline";
2449 aValueStrings
[ PDFWriter::Overline
] = "Overline";
2450 aValueStrings
[ PDFWriter::LineThrough
] = "LineThrough";
2451 aValueStrings
[ PDFWriter::Disc
] = "Disc";
2452 aValueStrings
[ PDFWriter::Circle
] = "Circle";
2453 aValueStrings
[ PDFWriter::Square
] = "Square";
2454 aValueStrings
[ PDFWriter::Decimal
] = "Decimal";
2455 aValueStrings
[ PDFWriter::UpperRoman
] = "UpperRoman";
2456 aValueStrings
[ PDFWriter::LowerRoman
] = "LowerRoman";
2457 aValueStrings
[ PDFWriter::UpperAlpha
] = "UpperAlpha";
2458 aValueStrings
[ PDFWriter::LowerAlpha
] = "LowerAlpha";
2461 std::map
< PDFWriter::StructAttributeValue
, const char* >::const_iterator it
=
2462 aValueStrings
.find( eVal
);
2464 #if OSL_DEBUG_LEVEL > 1
2465 if( it
== aValueStrings
.end() )
2466 SAL_INFO("vcl.pdfwriter", "invalid PDFWriter::StructAttributeValue " << eVal
);
2469 return it
!= aValueStrings
.end() ? it
->second
: "";
2472 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr
, const PDFWriterImpl::PDFStructureAttribute
& i_rVal
, OStringBuffer
& o_rLine
, bool i_bIsFixedInt
)
2474 o_rLine
.append( "/" );
2475 o_rLine
.append( PDFWriterImpl::getAttributeTag( i_eAttr
) );
2477 if( i_rVal
.eValue
!= PDFWriter::Invalid
)
2479 o_rLine
.append( "/" );
2480 o_rLine
.append( PDFWriterImpl::getAttributeValueTag( i_rVal
.eValue
) );
2485 o_rLine
.append( " " );
2487 appendFixedInt( i_rVal
.nValue
, o_rLine
);
2489 o_rLine
.append( i_rVal
.nValue
);
2491 o_rLine
.append( "\n" );
2494 OString
PDFWriterImpl::emitStructureAttributes( PDFStructureElement
& i_rEle
)
2496 // create layout, list and table attribute sets
2497 OStringBuffer
aLayout(256), aList(64), aTable(64);
2498 for( PDFStructAttributes::const_iterator it
= i_rEle
.m_aAttributes
.begin();
2499 it
!= i_rEle
.m_aAttributes
.end(); ++it
)
2501 if( it
->first
== PDFWriter::ListNumbering
)
2502 appendStructureAttributeLine( it
->first
, it
->second
, aList
, true );
2503 else if( it
->first
== PDFWriter::RowSpan
||
2504 it
->first
== PDFWriter::ColSpan
)
2505 appendStructureAttributeLine( it
->first
, it
->second
, aTable
, false );
2506 else if( it
->first
== PDFWriter::LinkAnnotation
)
2508 sal_Int32 nLink
= it
->second
.nValue
;
2509 std::map
< sal_Int32
, sal_Int32
>::const_iterator link_it
=
2510 m_aLinkPropertyMap
.find( nLink
);
2511 if( link_it
!= m_aLinkPropertyMap
.end() )
2512 nLink
= link_it
->second
;
2513 if( nLink
>= 0 && nLink
< (sal_Int32
)m_aLinks
.size() )
2515 // update struct parent of link
2516 OStringBuffer
aStructParentEntry( 32 );
2517 aStructParentEntry
.append( i_rEle
.m_nObject
);
2518 aStructParentEntry
.append( " 0 R" );
2519 m_aStructParentTree
.push_back( aStructParentEntry
.makeStringAndClear() );
2520 m_aLinks
[ nLink
].m_nStructParent
= m_aStructParentTree
.size()-1;
2522 sal_Int32 nRefObject
= createObject();
2523 OStringBuffer
aRef( 256 );
2524 aRef
.append( nRefObject
);
2525 aRef
.append( " 0 obj\n"
2526 "<</Type/OBJR/Obj " );
2527 aRef
.append( m_aLinks
[ nLink
].m_nObject
);
2528 aRef
.append( " 0 R>>\n"
2531 if (updateObject(nRefObject
))
2533 writeBuffer( aRef
.getStr(), aRef
.getLength() );
2536 i_rEle
.m_aKids
.push_back( PDFStructureElementKid( nRefObject
) );
2540 OSL_FAIL( "unresolved link id for Link structure" );
2541 #if OSL_DEBUG_LEVEL > 1
2542 SAL_INFO("vcl.pdfwriter", "unresolved link id " << nLink
<< " for Link structure");
2544 OStringBuffer
aLine( "unresolved link id " );
2545 aLine
.append( nLink
);
2546 aLine
.append( " for Link structure" );
2547 emitComment( aLine
.getStr() );
2553 appendStructureAttributeLine( it
->first
, it
->second
, aLayout
, true );
2555 if( ! i_rEle
.m_aBBox
.IsEmpty() )
2557 aLayout
.append( "/BBox[" );
2558 appendFixedInt( i_rEle
.m_aBBox
.Left(), aLayout
);
2559 aLayout
.append( " " );
2560 appendFixedInt( i_rEle
.m_aBBox
.Top(), aLayout
);
2561 aLayout
.append( " " );
2562 appendFixedInt( i_rEle
.m_aBBox
.Right(), aLayout
);
2563 aLayout
.append( " " );
2564 appendFixedInt( i_rEle
.m_aBBox
.Bottom(), aLayout
);
2565 aLayout
.append( "]\n" );
2568 std::vector
< sal_Int32
> aAttribObjects
;
2569 if( !aLayout
.isEmpty() )
2571 aAttribObjects
.push_back( createObject() );
2572 if (updateObject( aAttribObjects
.back() ))
2574 OStringBuffer
aObj( 64 );
2575 aObj
.append( aAttribObjects
.back() );
2576 aObj
.append( " 0 obj\n"
2578 aLayout
.append( ">>\nendobj\n\n" );
2579 writeBuffer( aObj
.getStr(), aObj
.getLength() );
2580 writeBuffer( aLayout
.getStr(), aLayout
.getLength() );
2583 if( !aList
.isEmpty() )
2585 aAttribObjects
.push_back( createObject() );
2586 if (updateObject( aAttribObjects
.back() ))
2588 OStringBuffer
aObj( 64 );
2589 aObj
.append( aAttribObjects
.back() );
2590 aObj
.append( " 0 obj\n"
2592 aList
.append( ">>\nendobj\n\n" );
2593 writeBuffer( aObj
.getStr(), aObj
.getLength() );
2594 writeBuffer( aList
.getStr(), aList
.getLength() );
2597 if( !aTable
.isEmpty() )
2599 aAttribObjects
.push_back( createObject() );
2600 if (updateObject( aAttribObjects
.back() ))
2602 OStringBuffer
aObj( 64 );
2603 aObj
.append( aAttribObjects
.back() );
2604 aObj
.append( " 0 obj\n"
2606 aTable
.append( ">>\nendobj\n\n" );
2607 writeBuffer( aObj
.getStr(), aObj
.getLength() );
2608 writeBuffer( aTable
.getStr(), aTable
.getLength() );
2612 OStringBuffer
aRet( 64 );
2613 if( aAttribObjects
.size() > 1 )
2614 aRet
.append( " [" );
2615 for( std::vector
< sal_Int32
>::const_iterator at_it
= aAttribObjects
.begin();
2616 at_it
!= aAttribObjects
.end(); ++at_it
)
2619 aRet
.append( *at_it
);
2620 aRet
.append( " 0 R" );
2622 if( aAttribObjects
.size() > 1 )
2623 aRet
.append( " ]" );
2624 return aRet
.makeStringAndClear();
2627 sal_Int32
PDFWriterImpl::emitStructure( PDFStructureElement
& rEle
)
2630 // do not emit NonStruct and its children
2631 rEle
.m_eType
== PDFWriter::NonStructElement
&&
2632 rEle
.m_nOwnElement
!= rEle
.m_nParentElement
// but of course emit the struct tree root
2636 for( std::list
< sal_Int32
>::const_iterator it
= rEle
.m_aChildren
.begin(); it
!= rEle
.m_aChildren
.end(); ++it
)
2638 if( *it
> 0 && *it
< sal_Int32(m_aStructure
.size()) )
2640 PDFStructureElement
& rChild
= m_aStructure
[ *it
];
2641 if( rChild
.m_eType
!= PDFWriter::NonStructElement
)
2643 if( rChild
.m_nParentElement
== rEle
.m_nOwnElement
)
2644 emitStructure( rChild
);
2647 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure element" );
2648 #if OSL_DEBUG_LEVEL > 1
2649 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::emitStructure: invalid child structure elemnt with id " << *it
);
2656 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
2657 #if OSL_DEBUG_LEVEL > 1
2658 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::emitStructure: invalid child structure id " << *it
);
2663 OStringBuffer
aLine( 512 );
2664 aLine
.append( rEle
.m_nObject
);
2665 aLine
.append( " 0 obj\n"
2667 sal_Int32 nParentTree
= -1;
2668 if( rEle
.m_nOwnElement
== rEle
.m_nParentElement
)
2670 nParentTree
= createObject();
2671 CHECK_RETURN( nParentTree
);
2672 aLine
.append( "/StructTreeRoot\n" );
2673 aLine
.append( "/ParentTree " );
2674 aLine
.append( nParentTree
);
2675 aLine
.append( " 0 R\n" );
2676 if( ! m_aRoleMap
.empty() )
2678 aLine
.append( "/RoleMap<<" );
2679 for( std::unordered_map
<OString
,OString
,OStringHash
>::const_iterator
2680 it
= m_aRoleMap
.begin(); it
!= m_aRoleMap
.end(); ++it
)
2682 aLine
.append( '/' );
2683 aLine
.append(it
->first
);
2684 aLine
.append( '/' );
2685 aLine
.append( it
->second
);
2686 aLine
.append( '\n' );
2688 aLine
.append( ">>\n" );
2693 aLine
.append( "/StructElem\n"
2695 if( !rEle
.m_aAlias
.isEmpty() )
2696 aLine
.append( rEle
.m_aAlias
);
2698 aLine
.append( getStructureTag( rEle
.m_eType
) );
2701 aLine
.append( m_aStructure
[ rEle
.m_nParentElement
].m_nObject
);
2702 aLine
.append( " 0 R\n"
2704 aLine
.append( rEle
.m_nFirstPageObject
);
2705 aLine
.append( " 0 R\n" );
2706 if( !rEle
.m_aActualText
.isEmpty() )
2708 aLine
.append( "/ActualText" );
2709 appendUnicodeTextStringEncrypt( rEle
.m_aActualText
, rEle
.m_nObject
, aLine
);
2710 aLine
.append( "\n" );
2712 if( !rEle
.m_aAltText
.isEmpty() )
2714 aLine
.append( "/Alt" );
2715 appendUnicodeTextStringEncrypt( rEle
.m_aAltText
, rEle
.m_nObject
, aLine
);
2716 aLine
.append( "\n" );
2719 if( (! rEle
.m_aBBox
.IsEmpty()) || (! rEle
.m_aAttributes
.empty()) )
2721 OString aAttribs
= emitStructureAttributes( rEle
);
2722 if( !aAttribs
.isEmpty() )
2724 aLine
.append( "/A" );
2725 aLine
.append( aAttribs
);
2726 aLine
.append( "\n" );
2729 if( !rEle
.m_aLocale
.Language
.isEmpty() )
2731 /* PDF allows only RFC 3066, which is only partly BCP 47 and does not
2732 * include script tags and others.
2733 * http://pdf.editme.com/pdfua-naturalLanguageSpecification
2734 * http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf#page=886
2735 * https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf#M13.9.19332.1Heading.97.Natural.Language.Specification
2737 LanguageTag
aLanguageTag( rEle
.m_aLocale
);
2738 OUString aLanguage
, aScript
, aCountry
;
2739 aLanguageTag
.getIsoLanguageScriptCountry( aLanguage
, aScript
, aCountry
);
2740 if (!aLanguage
.isEmpty())
2742 OUStringBuffer
aLocBuf( 16 );
2743 aLocBuf
.append( aLanguage
);
2744 if( !aCountry
.isEmpty() )
2746 aLocBuf
.append( '-' );
2747 aLocBuf
.append( aCountry
);
2749 aLine
.append( "/Lang" );
2750 appendLiteralStringEncrypt( aLocBuf
.makeStringAndClear(), rEle
.m_nObject
, aLine
);
2751 aLine
.append( "\n" );
2754 if( ! rEle
.m_aKids
.empty() )
2757 aLine
.append( "/K[" );
2758 for( std::list
< PDFStructureElementKid
>::const_iterator it
=
2759 rEle
.m_aKids
.begin(); it
!= rEle
.m_aKids
.end(); ++it
, i
++ )
2761 if( it
->nMCID
== -1 )
2763 aLine
.append( it
->nObject
);
2764 aLine
.append( " 0 R" );
2765 aLine
.append( ( (i
& 15) == 15 ) ? "\n" : " " );
2769 if( it
->nObject
== rEle
.m_nFirstPageObject
)
2771 aLine
.append( it
->nMCID
);
2772 aLine
.append( " " );
2776 aLine
.append( "<</Type/MCR/Pg " );
2777 aLine
.append( it
->nObject
);
2778 aLine
.append( " 0 R /MCID " );
2779 aLine
.append( it
->nMCID
);
2780 aLine
.append( ">>\n" );
2784 aLine
.append( "]\n" );
2786 aLine
.append( ">>\nendobj\n\n" );
2788 CHECK_RETURN( updateObject( rEle
.m_nObject
) );
2789 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
2791 CHECK_RETURN( emitStructParentTree( nParentTree
) );
2793 return rEle
.m_nObject
;
2796 bool PDFWriterImpl::emitGradients()
2798 for( std::list
<GradientEmit
>::iterator it
= m_aGradients
.begin();
2799 it
!= m_aGradients
.end(); ++it
)
2801 if ( !writeGradientFunction( *it
) ) return false;
2806 bool PDFWriterImpl::emitTilings()
2808 OStringBuffer
aTilingObj( 1024 );
2810 for( std::vector
<TilingEmit
>::iterator it
= m_aTilings
.begin(); it
!= m_aTilings
.end(); ++it
)
2812 SAL_WARN_IF( !it
->m_pTilingStream
, "vcl.pdfwriter", "tiling without stream" );
2813 if( ! it
->m_pTilingStream
)
2816 aTilingObj
.setLength( 0 );
2818 #if OSL_DEBUG_LEVEL > 1
2819 emitComment( "PDFWriterImpl::emitTilings" );
2822 sal_Int32 nX
= (sal_Int32
)it
->m_aRectangle
.Left();
2823 sal_Int32 nY
= (sal_Int32
)it
->m_aRectangle
.Top();
2824 sal_Int32 nW
= (sal_Int32
)it
->m_aRectangle
.GetWidth();
2825 sal_Int32 nH
= (sal_Int32
)it
->m_aRectangle
.GetHeight();
2826 if( it
->m_aCellSize
.Width() == 0 )
2827 it
->m_aCellSize
.Width() = nW
;
2828 if( it
->m_aCellSize
.Height() == 0 )
2829 it
->m_aCellSize
.Height() = nH
;
2831 bool bDeflate
= compressStream( it
->m_pTilingStream
);
2832 it
->m_pTilingStream
->Seek( STREAM_SEEK_TO_END
);
2833 sal_uInt64
const nTilingStreamSize
= it
->m_pTilingStream
->Tell();
2834 it
->m_pTilingStream
->Seek( STREAM_SEEK_TO_BEGIN
);
2836 // write pattern object
2837 aTilingObj
.append( it
->m_nObject
);
2838 aTilingObj
.append( " 0 obj\n" );
2839 aTilingObj
.append( "<</Type/Pattern/PatternType 1\n"
2843 appendFixedInt( nX
, aTilingObj
);
2844 aTilingObj
.append( ' ' );
2845 appendFixedInt( nY
, aTilingObj
);
2846 aTilingObj
.append( ' ' );
2847 appendFixedInt( nX
+nW
, aTilingObj
);
2848 aTilingObj
.append( ' ' );
2849 appendFixedInt( nY
+nH
, aTilingObj
);
2850 aTilingObj
.append( "]\n"
2852 appendFixedInt( it
->m_aCellSize
.Width(), aTilingObj
);
2853 aTilingObj
.append( "\n"
2855 appendFixedInt( it
->m_aCellSize
.Height(), aTilingObj
);
2856 aTilingObj
.append( "\n" );
2857 if( it
->m_aTransform
.matrix
[0] != 1.0 ||
2858 it
->m_aTransform
.matrix
[1] != 0.0 ||
2859 it
->m_aTransform
.matrix
[3] != 0.0 ||
2860 it
->m_aTransform
.matrix
[4] != 1.0 ||
2861 it
->m_aTransform
.matrix
[2] != 0.0 ||
2862 it
->m_aTransform
.matrix
[5] != 0.0 )
2864 aTilingObj
.append( "/Matrix [" );
2865 // TODO: scaling, mirroring on y, etc
2866 appendDouble( it
->m_aTransform
.matrix
[0], aTilingObj
);
2867 aTilingObj
.append( ' ' );
2868 appendDouble( it
->m_aTransform
.matrix
[1], aTilingObj
);
2869 aTilingObj
.append( ' ' );
2870 appendDouble( it
->m_aTransform
.matrix
[3], aTilingObj
);
2871 aTilingObj
.append( ' ' );
2872 appendDouble( it
->m_aTransform
.matrix
[4], aTilingObj
);
2873 aTilingObj
.append( ' ' );
2874 appendDouble( it
->m_aTransform
.matrix
[2], aTilingObj
);
2875 aTilingObj
.append( ' ' );
2876 appendDouble( it
->m_aTransform
.matrix
[5], aTilingObj
);
2877 aTilingObj
.append( "]\n" );
2879 aTilingObj
.append( "/Resources" );
2880 it
->m_aResources
.append( aTilingObj
, getFontDictObject() );
2882 aTilingObj
.append( "/Filter/FlateDecode" );
2883 aTilingObj
.append( "/Length " );
2884 aTilingObj
.append( (sal_Int32
)nTilingStreamSize
);
2885 aTilingObj
.append( ">>\nstream\n" );
2886 if ( !updateObject( it
->m_nObject
) ) return false;
2887 if ( !writeBuffer( aTilingObj
.getStr(), aTilingObj
.getLength() ) ) return false;
2888 checkAndEnableStreamEncryption( it
->m_nObject
);
2889 bool written
= writeBuffer( it
->m_pTilingStream
->GetData(), nTilingStreamSize
);
2890 delete it
->m_pTilingStream
;
2891 it
->m_pTilingStream
= nullptr;
2894 disableStreamEncryption();
2895 aTilingObj
.setLength( 0 );
2896 aTilingObj
.append( "\nendstream\nendobj\n\n" );
2897 if ( !writeBuffer( aTilingObj
.getStr(), aTilingObj
.getLength() ) ) return false;
2902 sal_Int32
PDFWriterImpl::emitBuiltinFont( const PdfBuiltinFontFace
* pFD
, sal_Int32 nFontObject
)
2906 const BuiltinFont
& rBuiltinFont
= pFD
->GetBuiltinFont();
2908 OStringBuffer
aLine( 1024 );
2910 if( nFontObject
<= 0 )
2911 nFontObject
= createObject();
2912 CHECK_RETURN( updateObject( nFontObject
) );
2913 aLine
.append( nFontObject
);
2914 aLine
.append( " 0 obj\n"
2915 "<</Type/Font/Subtype/Type1/BaseFont/" );
2916 appendName( rBuiltinFont
.m_pPSName
, aLine
);
2917 aLine
.append( "\n" );
2918 if( rBuiltinFont
.m_eCharSet
== RTL_TEXTENCODING_MS_1252
)
2919 aLine
.append( "/Encoding/WinAnsiEncoding\n" );
2920 aLine
.append( ">>\nendobj\n\n" );
2921 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
2925 std::map
< sal_Int32
, sal_Int32
> PDFWriterImpl::emitSystemFont( const PhysicalFontFace
* pFont
, EmbedFont
& rEmbed
)
2927 std::map
< sal_Int32
, sal_Int32
> aRet
;
2929 sal_Int32 nFontDescriptor
= 0;
2930 OString
aSubType( "/Type1" );
2931 FontSubsetInfo aInfo
;
2932 // fill in dummy values
2933 aInfo
.m_nAscent
= 1000;
2934 aInfo
.m_nDescent
= 200;
2935 aInfo
.m_nCapHeight
= 1000;
2936 aInfo
.m_aFontBBox
= tools::Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) );
2937 aInfo
.m_aPSName
= pFont
->GetFamilyName();
2938 sal_Int32 pWidths
[256];
2939 memset( pWidths
, 0, sizeof(pWidths
) );
2941 SalGraphics
*pGraphics
= m_pReferenceDevice
->GetGraphics();
2945 aSubType
= OString( "/TrueType" );
2946 std::vector
< sal_Int32
> aGlyphWidths
;
2947 Ucs2UIntMap aUnicodeMap
;
2948 pGraphics
->GetGlyphWidths( pFont
, false, aGlyphWidths
, aUnicodeMap
);
2951 osl_createTempFile( nullptr, nullptr, &aTmpName
.pData
);
2952 sal_GlyphId aGlyphIds
[ 256 ];
2953 sal_uInt8 pEncoding
[ 256 ];
2954 sal_Int32 pDuWidths
[ 256 ];
2956 memset( aGlyphIds
, 0, sizeof( aGlyphIds
) );
2957 memset( pEncoding
, 0, sizeof( pEncoding
) );
2958 memset( pDuWidths
, 0, sizeof( pDuWidths
) );
2960 for( sal_Ucs c
= 32; c
< 256; c
++ )
2964 if( aUnicodeMap
.find( c
) != aUnicodeMap
.end() )
2965 pWidths
[ c
] = aGlyphWidths
[ aUnicodeMap
[ c
] ];
2967 //TODO: surely this is utterly broken because aGlyphIds is just all zeros, if we
2968 //had the right glyphids here then I imagine we could replace pDuWidths with
2969 //pWidths and remove pWidths assignment above. i.e. start with the glyph ids
2970 //and map those to unicode rather than try and reverse map them ?
2971 pGraphics
->CreateFontSubset( aTmpName
, pFont
, aGlyphIds
, pEncoding
, pDuWidths
, 256, aInfo
);
2972 osl_removeFile( aTmpName
.pData
);
2974 // write font descriptor
2975 nFontDescriptor
= emitFontDescriptor( pFont
, aInfo
, 0, 0 );
2976 if( nFontDescriptor
)
2978 // write font object
2979 sal_Int32 nObject
= createObject();
2980 if( updateObject( nObject
) )
2982 OStringBuffer
aLine( 1024 );
2983 aLine
.append( nObject
);
2984 aLine
.append( " 0 obj\n"
2985 "<</Type/Font/Subtype" );
2986 aLine
.append( aSubType
);
2987 aLine
.append( "/BaseFont/" );
2988 appendName( aInfo
.m_aPSName
, aLine
);
2989 aLine
.append( "\n" );
2990 if( !pFont
->IsSymbolFont() )
2991 aLine
.append( "/Encoding/WinAnsiEncoding\n" );
2992 aLine
.append( "/FirstChar 32 /LastChar 255\n"
2994 for( int i
= 32; i
< 256; i
++ )
2996 aLine
.append( pWidths
[i
] );
2997 aLine
.append( ((i
&15) == 15) ? "\n" : " " );
3000 "/FontDescriptor " );
3001 aLine
.append( nFontDescriptor
);
3002 aLine
.append( " 0 R>>\n"
3004 writeBuffer( aLine
.getStr(), aLine
.getLength() );
3006 aRet
[ rEmbed
.m_nNormalFontID
] = nObject
;
3013 typedef int ThreeInts
[3];
3014 static bool getPfbSegmentLengths( const unsigned char* pFontBytes
, int nByteLen
,
3015 ThreeInts
& rSegmentLengths
)
3017 if( !pFontBytes
|| (nByteLen
< 0) )
3019 const unsigned char* pPtr
= pFontBytes
;
3020 const unsigned char* pEnd
= pFontBytes
+ nByteLen
;
3022 for(int & rSegmentLength
: rSegmentLengths
) {
3023 // read segment1 header
3024 if( pPtr
+6 >= pEnd
)
3026 if( (pPtr
[0] != 0x80) || (pPtr
[1] >= 0x03) )
3028 const int nLen
= (pPtr
[5]<<24) + (pPtr
[4]<<16) + (pPtr
[3]<<8) + pPtr
[2];
3031 rSegmentLength
= nLen
;
3035 // read segment-end header
3036 if( pPtr
+2 >= pEnd
)
3038 if( (pPtr
[0] != 0x80) || (pPtr
[1] != 0x03) )
3044 static void appendSubsetName( int nSubsetID
, const OUString
& rPSName
, OStringBuffer
& rBuffer
)
3048 for( int i
= 0; i
< 6; i
++ )
3050 int nOffset
= (nSubsetID
% 26);
3052 rBuffer
.append( (sal_Char
)('A'+nOffset
) );
3054 rBuffer
.append( '+' );
3056 appendName( rPSName
, rBuffer
);
3059 sal_Int32
PDFWriterImpl::createToUnicodeCMap( sal_uInt8
* pEncoding
,
3060 sal_Ucs
* pCodeUnits
,
3061 sal_Int32
* pCodeUnitsPerGlyph
,
3062 sal_Int32
* pEncToUnicodeIndex
,
3066 for (int n
= 0; n
< nGlyphs
; ++n
)
3067 if( pCodeUnits
[pEncToUnicodeIndex
[n
]] && pCodeUnitsPerGlyph
[n
] )
3073 sal_Int32 nStream
= createObject();
3074 CHECK_RETURN( updateObject( nStream
) );
3076 OStringBuffer
aContents( 1024 );
3078 "/CIDInit/ProcSet findresource begin\n"
3081 "/CIDSystemInfo<<\n"
3082 "/Registry (Adobe)\n"
3086 "/CMapName/Adobe-Identity-UCS def\n"
3088 "1 begincodespacerange\n"
3090 "endcodespacerange\n"
3093 for (int n
= 0; n
< nGlyphs
; ++n
)
3095 if( pCodeUnits
[pEncToUnicodeIndex
[n
]] && pCodeUnitsPerGlyph
[n
] )
3097 if( (nCount
% 100) == 0 )
3100 aContents
.append( "endbfchar\n" );
3101 aContents
.append( (sal_Int32
)((nMapped
-nCount
> 100) ? 100 : nMapped
-nCount
) );
3102 aContents
.append( " beginbfchar\n" );
3104 aContents
.append( '<' );
3105 appendHex( (sal_Int8
)pEncoding
[n
], aContents
);
3106 aContents
.append( "> <" );
3107 // TODO: handle code points>U+FFFF
3108 sal_Int32 nIndex
= pEncToUnicodeIndex
[n
];
3109 for( sal_Int32 j
= 0; j
< pCodeUnitsPerGlyph
[n
]; j
++ )
3111 appendHex( (sal_Int8
)(pCodeUnits
[nIndex
+ j
] / 256), aContents
);
3112 appendHex( (sal_Int8
)(pCodeUnits
[nIndex
+ j
] & 255), aContents
);
3114 aContents
.append( ">\n" );
3118 aContents
.append( "endbfchar\n"
3120 "CMapName currentdict /CMap define resource pop\n"
3123 SvMemoryStream aStream
;
3124 if (!g_bDebugDisableCompression
)
3126 ZCodec
aCodec( 0x4000, 0x4000 );
3127 aCodec
.BeginCompression();
3128 aCodec
.Write( aStream
, reinterpret_cast<const sal_uInt8
*>(aContents
.getStr()), aContents
.getLength() );
3129 aCodec
.EndCompression();
3132 #if OSL_DEBUG_LEVEL > 1
3133 emitComment( "PDFWriterImpl::createToUnicodeCMap" );
3135 OStringBuffer
aLine( 40 );
3137 aLine
.append( nStream
);
3138 aLine
.append( " 0 obj\n<</Length " );
3140 if (!g_bDebugDisableCompression
)
3142 nLen
= (sal_Int32
)aStream
.Tell();
3144 aLine
.append( nLen
);
3145 aLine
.append( "/Filter/FlateDecode" );
3148 aLine
.append( aContents
.getLength() );
3149 aLine
.append( ">>\nstream\n" );
3150 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
3151 checkAndEnableStreamEncryption( nStream
);
3152 if (!g_bDebugDisableCompression
)
3154 CHECK_RETURN( writeBuffer( aStream
.GetData(), nLen
) );
3158 CHECK_RETURN( writeBuffer( aContents
.getStr(), aContents
.getLength() ) );
3160 disableStreamEncryption();
3161 aLine
.setLength( 0 );
3162 aLine
.append( "\nendstream\n"
3164 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
3168 sal_Int32
PDFWriterImpl::emitFontDescriptor( const PhysicalFontFace
* pFont
, FontSubsetInfo
& rInfo
, sal_Int32 nSubsetID
, sal_Int32 nFontStream
)
3170 OStringBuffer
aLine( 1024 );
3171 // get font flags, see PDF reference 1.4 p. 358
3172 // possibly characters outside Adobe standard encoding
3173 // so set Symbolic flag
3174 sal_Int32 nFontFlags
= (1<<2);
3175 if( pFont
->GetItalic() == ITALIC_NORMAL
|| pFont
->GetItalic() == ITALIC_OBLIQUE
)
3176 nFontFlags
|= (1 << 6);
3177 if( pFont
->GetPitch() == PITCH_FIXED
)
3179 if( pFont
->GetFamilyType() == FAMILY_SCRIPT
)
3180 nFontFlags
|= (1 << 3);
3181 else if( pFont
->GetFamilyType() == FAMILY_ROMAN
)
3182 nFontFlags
|= (1 << 1);
3184 sal_Int32 nFontDescriptor
= createObject();
3185 CHECK_RETURN( updateObject( nFontDescriptor
) );
3186 aLine
.setLength( 0 );
3187 aLine
.append( nFontDescriptor
);
3188 aLine
.append( " 0 obj\n"
3189 "<</Type/FontDescriptor/FontName/" );
3190 appendSubsetName( nSubsetID
, rInfo
.m_aPSName
, aLine
);
3193 aLine
.append( nFontFlags
);
3196 // note: Top and Bottom are reversed in VCL and PDF rectangles
3197 aLine
.append( (sal_Int32
)rInfo
.m_aFontBBox
.TopLeft().X() );
3198 aLine
.append( ' ' );
3199 aLine
.append( (sal_Int32
)rInfo
.m_aFontBBox
.TopLeft().Y() );
3200 aLine
.append( ' ' );
3201 aLine
.append( (sal_Int32
)rInfo
.m_aFontBBox
.BottomRight().X() );
3202 aLine
.append( ' ' );
3203 aLine
.append( (sal_Int32
)(rInfo
.m_aFontBBox
.BottomRight().Y()+1) );
3204 aLine
.append( "]/ItalicAngle " );
3205 if( pFont
->GetItalic() == ITALIC_OBLIQUE
|| pFont
->GetItalic() == ITALIC_NORMAL
)
3206 aLine
.append( "-30" );
3208 aLine
.append( "0" );
3211 aLine
.append( (sal_Int32
)rInfo
.m_nAscent
);
3214 aLine
.append( (sal_Int32
)-rInfo
.m_nDescent
);
3217 aLine
.append( (sal_Int32
)rInfo
.m_nCapHeight
);
3218 // According to PDF reference 1.4 StemV is required
3219 // seems a tad strange to me, but well ...
3224 aLine
.append( "/FontFile" );
3225 switch( rInfo
.m_nFontType
)
3227 case FontType::SFNT_TTF
:
3228 aLine
.append( '2' );
3230 case FontType::TYPE1_PFA
:
3231 case FontType::TYPE1_PFB
:
3232 case FontType::ANY_TYPE1
:
3235 OSL_FAIL( "unknown fonttype in PDF font descriptor" );
3238 aLine
.append( ' ' );
3239 aLine
.append( nFontStream
);
3240 aLine
.append( " 0 R\n" );
3242 aLine
.append( ">>\n"
3244 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
3246 return nFontDescriptor
;
3249 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer
& rDict
) const
3251 for( std::map
< sal_Int32
, sal_Int32
>::const_iterator it
=
3252 m_aBuiltinFontToObjectMap
.begin(); it
!= m_aBuiltinFontToObjectMap
.end(); ++it
)
3254 rDict
.append( m_aBuiltinFonts
[it
->first
].getNameObject() );
3255 rDict
.append( ' ' );
3256 rDict
.append( it
->second
);
3257 rDict
.append( " 0 R" );
3261 bool PDFWriterImpl::emitFonts()
3263 SalGraphics
*pGraphics
= m_pReferenceDevice
->GetGraphics();
3268 OStringBuffer
aLine( 1024 );
3270 std::map
< sal_Int32
, sal_Int32
> aFontIDToObject
;
3273 osl_createTempFile( nullptr, nullptr, &aTmpName
.pData
);
3274 for( FontSubsetData::iterator it
= m_aSubsets
.begin(); it
!= m_aSubsets
.end(); ++it
)
3276 for( std::list
< FontEmit
>::iterator lit
= it
->second
.m_aSubsets
.begin(); lit
!= it
->second
.m_aSubsets
.end(); ++lit
)
3278 sal_GlyphId aGlyphIds
[ 256 ];
3279 sal_Int32 pWidths
[ 256 ];
3280 sal_uInt8 pEncoding
[ 256 ];
3281 sal_Int32 pEncToUnicodeIndex
[ 256 ];
3282 sal_Int32 pCodeUnitsPerGlyph
[ 256 ];
3283 std::vector
<sal_Ucs
> aCodeUnits
;
3284 aCodeUnits
.reserve( 256 );
3286 // fill arrays and prepare encoding index map
3287 sal_Int32 nToUnicodeStream
= 0;
3289 memset( aGlyphIds
, 0, sizeof( aGlyphIds
) );
3290 memset( pEncoding
, 0, sizeof( pEncoding
) );
3291 memset( pCodeUnitsPerGlyph
, 0, sizeof( pCodeUnitsPerGlyph
) );
3292 memset( pEncToUnicodeIndex
, 0, sizeof( pEncToUnicodeIndex
) );
3293 for( FontEmitMapping::iterator fit
= lit
->m_aMapping
.begin(); fit
!= lit
->m_aMapping
.end();++fit
)
3295 sal_uInt8 nEnc
= fit
->second
.getGlyphId();
3297 SAL_WARN_IF( aGlyphIds
[nEnc
] != 0 || pEncoding
[nEnc
] != 0, "vcl.pdfwriter", "duplicate glyph" );
3298 SAL_WARN_IF( nEnc
> lit
->m_aMapping
.size(), "vcl.pdfwriter", "invalid glyph encoding" );
3300 aGlyphIds
[ nEnc
] = fit
->first
;
3301 pEncoding
[ nEnc
] = nEnc
;
3302 pEncToUnicodeIndex
[ nEnc
] = static_cast<sal_Int32
>(aCodeUnits
.size());
3303 pCodeUnitsPerGlyph
[ nEnc
] = fit
->second
.countCodes();
3304 for( sal_Int32 n
= 0; n
< pCodeUnitsPerGlyph
[ nEnc
]; n
++ )
3305 aCodeUnits
.push_back( fit
->second
.getCode( n
) );
3306 if( fit
->second
.getCode(0) )
3307 nToUnicodeStream
= 1;
3312 OSL_FAIL( "too many glyphs for subset" );
3315 FontSubsetInfo aSubsetInfo
;
3316 if( pGraphics
->CreateFontSubset( aTmpName
, it
->first
, aGlyphIds
, pEncoding
, pWidths
, nGlyphs
, aSubsetInfo
) )
3318 // create font stream
3319 osl::File
aFontFile(aTmpName
);
3320 if (osl::File::E_None
!= aFontFile
.open(osl_File_OpenFlag_Read
)) return false;
3322 sal_uInt64 nLength1
;
3323 if ( osl::File::E_None
!= aFontFile
.setPos(osl_Pos_End
, 0) ) return false;
3324 if ( osl::File::E_None
!= aFontFile
.getPos(nLength1
) ) return false;
3325 if ( osl::File::E_None
!= aFontFile
.setPos(osl_Pos_Absolut
, 0) ) return false;
3327 #if OSL_DEBUG_LEVEL > 1
3328 emitComment( "PDFWriterImpl::emitFonts" );
3330 sal_Int32 nFontStream
= createObject();
3331 sal_Int32 nStreamLengthObject
= createObject();
3332 if ( !updateObject( nFontStream
) ) return false;
3333 aLine
.setLength( 0 );
3334 aLine
.append( nFontStream
);
3335 aLine
.append( " 0 obj\n"
3337 aLine
.append( nStreamLengthObject
);
3338 if (!g_bDebugDisableCompression
)
3339 aLine
.append( " 0 R"
3340 "/Filter/FlateDecode"
3343 aLine
.append( " 0 R"
3346 sal_uInt64 nStartPos
= 0;
3347 if( aSubsetInfo
.m_nFontType
== FontType::SFNT_TTF
)
3349 aLine
.append( (sal_Int32
)nLength1
);
3351 aLine
.append( ">>\n"
3353 if ( !writeBuffer( aLine
.getStr(), aLine
.getLength() ) ) return false;
3354 if ( osl::File::E_None
!= m_aFile
.getPos(nStartPos
) ) return false;
3358 checkAndEnableStreamEncryption( nFontStream
);
3359 sal_Bool bEOF
= false;
3364 if ( osl::File::E_None
!= aFontFile
.read(buf
, sizeof(buf
), nRead
) ) return false;
3365 if ( !writeBuffer( buf
, nRead
) ) return false;
3366 if ( osl::File::E_None
!= aFontFile
.isEndOfFile(&bEOF
) ) return false;
3369 else if( aSubsetInfo
.m_nFontType
& FontType::CFF_FONT
)
3372 OSL_FAIL( "PDFWriterImpl does not support CFF-font subsets yet!" );
3374 else if( aSubsetInfo
.m_nFontType
& FontType::TYPE1_PFB
) // TODO: also support PFA?
3376 std::unique_ptr
<unsigned char[]> xBuffer(new unsigned char[nLength1
]);
3378 sal_uInt64 nBytesRead
= 0;
3379 if ( osl::File::E_None
!= aFontFile
.read(xBuffer
.get(), nLength1
, nBytesRead
) ) return false;
3380 SAL_WARN_IF( nBytesRead
!=nLength1
, "vcl.pdfwriter", "PDF-FontSubset read incomplete!" );
3381 if ( osl::File::E_None
!= aFontFile
.setPos(osl_Pos_Absolut
, 0) ) return false;
3382 // get the PFB-segment lengths
3383 ThreeInts aSegmentLengths
= {0,0,0};
3384 getPfbSegmentLengths(xBuffer
.get(), (int)nBytesRead
, aSegmentLengths
);
3385 // the lengths below are mandatory for PDF-exported Type1 fonts
3386 // because the PFB segment headers get stripped! WhyOhWhy.
3387 aLine
.append( (sal_Int32
)aSegmentLengths
[0] );
3388 aLine
.append( "/Length2 " );
3389 aLine
.append( (sal_Int32
)aSegmentLengths
[1] );
3390 aLine
.append( "/Length3 " );
3391 aLine
.append( (sal_Int32
)aSegmentLengths
[2] );
3393 aLine
.append( ">>\n"
3395 if ( !writeBuffer( aLine
.getStr(), aLine
.getLength() ) ) return false;
3396 if ( osl::File::E_None
!= m_aFile
.getPos(nStartPos
) ) return false;
3398 // emit PFB-sections without section headers
3400 checkAndEnableStreamEncryption( nFontStream
);
3401 if ( !writeBuffer( &xBuffer
[6], aSegmentLengths
[0] ) ) return false;
3402 if ( !writeBuffer( &xBuffer
[12] + aSegmentLengths
[0], aSegmentLengths
[1] ) ) return false;
3403 if ( !writeBuffer( &xBuffer
[18] + aSegmentLengths
[0] + aSegmentLengths
[1], aSegmentLengths
[2] ) ) return false;
3407 SAL_INFO("vcl.pdfwriter", "PDF: CreateFontSubset result in not yet supported format=" << (int)aSubsetInfo
.m_nFontType
);
3408 aLine
.append( "0 >>\nstream\n" );
3412 disableStreamEncryption();
3416 sal_uInt64 nEndPos
= 0;
3417 if ( osl::File::E_None
!= m_aFile
.getPos(nEndPos
) ) return false;
3419 aLine
.setLength( 0 );
3420 aLine
.append( "\nendstream\nendobj\n\n" );
3421 if ( !writeBuffer( aLine
.getStr(), aLine
.getLength() ) ) return false;
3423 // emit stream length object
3424 if ( !updateObject( nStreamLengthObject
) ) return false;
3425 aLine
.setLength( 0 );
3426 aLine
.append( nStreamLengthObject
);
3427 aLine
.append( " 0 obj\n" );
3428 aLine
.append( (sal_Int64
)(nEndPos
-nStartPos
) );
3429 aLine
.append( "\nendobj\n\n" );
3430 if ( !writeBuffer( aLine
.getStr(), aLine
.getLength() ) ) return false;
3432 // write font descriptor
3433 sal_Int32 nFontDescriptor
= emitFontDescriptor( it
->first
, aSubsetInfo
, lit
->m_nFontID
, nFontStream
);
3435 if( nToUnicodeStream
)
3436 nToUnicodeStream
= createToUnicodeCMap( pEncoding
, &aCodeUnits
[0], pCodeUnitsPerGlyph
, pEncToUnicodeIndex
, nGlyphs
);
3438 sal_Int32 nFontObject
= createObject();
3439 if ( !updateObject( nFontObject
) ) return false;
3440 aLine
.setLength( 0 );
3441 aLine
.append( nFontObject
);
3443 aLine
.append( " 0 obj\n" );
3444 aLine
.append( (aSubsetInfo
.m_nFontType
& FontType::ANY_TYPE1
) ?
3445 "<</Type/Font/Subtype/Type1/BaseFont/" :
3446 "<</Type/Font/Subtype/TrueType/BaseFont/" );
3447 appendSubsetName( lit
->m_nFontID
, aSubsetInfo
.m_aPSName
, aLine
);
3451 aLine
.append( (sal_Int32
)(nGlyphs
-1) );
3454 for( int i
= 0; i
< nGlyphs
; i
++ )
3456 aLine
.append( pWidths
[ i
] );
3457 aLine
.append( ((i
& 15) == 15) ? "\n" : " " );
3460 "/FontDescriptor " );
3461 aLine
.append( nFontDescriptor
);
3462 aLine
.append( " 0 R\n" );
3463 if( nToUnicodeStream
)
3465 aLine
.append( "/ToUnicode " );
3466 aLine
.append( nToUnicodeStream
);
3467 aLine
.append( " 0 R\n" );
3469 aLine
.append( ">>\n"
3471 if ( !writeBuffer( aLine
.getStr(), aLine
.getLength() ) ) return false;
3473 aFontIDToObject
[ lit
->m_nFontID
] = nFontObject
;
3477 const PhysicalFontFace
* pFont
= it
->first
;
3478 OStringBuffer
aErrorComment( 256 );
3479 aErrorComment
.append( "CreateFontSubset failed for font \"" );
3480 aErrorComment
.append( OUStringToOString( pFont
->GetFamilyName(), RTL_TEXTENCODING_UTF8
) );
3481 aErrorComment
.append( '\"' );
3482 if( pFont
->GetItalic() == ITALIC_NORMAL
)
3483 aErrorComment
.append( " italic" );
3484 else if( pFont
->GetItalic() == ITALIC_OBLIQUE
)
3485 aErrorComment
.append( " oblique" );
3486 aErrorComment
.append( " weight=" );
3487 aErrorComment
.append( sal_Int32(pFont
->GetWeight()) );
3488 emitComment( aErrorComment
.getStr() );
3492 osl_removeFile( aTmpName
.pData
);
3494 // emit system fonts
3495 for( FontEmbedData::iterator sit
= m_aSystemFonts
.begin(); sit
!= m_aSystemFonts
.end(); ++sit
)
3497 std::map
< sal_Int32
, sal_Int32
> aObjects
= emitSystemFont( sit
->first
, sit
->second
);
3498 for( std::map
< sal_Int32
, sal_Int32
>::iterator fit
= aObjects
.begin(); fit
!= aObjects
.end(); ++fit
)
3500 if ( !fit
->second
) return false;
3501 aFontIDToObject
[ fit
->first
] = fit
->second
;
3505 OStringBuffer
aFontDict( 1024 );
3506 aFontDict
.append( getFontDictObject() );
3507 aFontDict
.append( " 0 obj\n"
3510 for( std::map
< sal_Int32
, sal_Int32
>::iterator mit
= aFontIDToObject
.begin(); mit
!= aFontIDToObject
.end(); ++mit
)
3512 aFontDict
.append( "/F" );
3513 aFontDict
.append( mit
->first
);
3514 aFontDict
.append( ' ' );
3515 aFontDict
.append( mit
->second
);
3516 aFontDict
.append( " 0 R" );
3517 if( ((++ni
) & 7) == 0 )
3518 aFontDict
.append( '\n' );
3520 // emit builtin font for widget appearances / variable text
3521 for( std::map
< sal_Int32
, sal_Int32
>::iterator it
= m_aBuiltinFontToObjectMap
.begin();
3522 it
!= m_aBuiltinFontToObjectMap
.end(); ++it
)
3524 PdfBuiltinFontFace
aData(m_aBuiltinFonts
[it
->first
]);
3525 it
->second
= emitBuiltinFont( &aData
, it
->second
);
3527 appendBuiltinFontsToDict( aFontDict
);
3528 aFontDict
.append( "\n>>\nendobj\n\n" );
3530 if ( !updateObject( getFontDictObject() ) ) return false;
3531 if ( !writeBuffer( aFontDict
.getStr(), aFontDict
.getLength() ) ) return false;
3535 sal_Int32
PDFWriterImpl::emitResources()
3538 if( ! m_aGradients
.empty() )
3539 CHECK_RETURN( emitGradients() );
3541 if( ! m_aTilings
.empty() )
3542 CHECK_RETURN( emitTilings() );
3545 CHECK_RETURN( emitFonts() );
3547 // emit Resource dict
3548 OStringBuffer
aLine( 512 );
3549 sal_Int32 nResourceDict
= getResourceDictObj();
3550 CHECK_RETURN( updateObject( nResourceDict
) );
3551 aLine
.setLength( 0 );
3552 aLine
.append( nResourceDict
);
3553 aLine
.append( " 0 obj\n" );
3554 m_aGlobalResourceDict
.append( aLine
, getFontDictObject() );
3555 aLine
.append( "endobj\n\n" );
3556 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
3557 return nResourceDict
;
3560 sal_Int32
PDFWriterImpl::updateOutlineItemCount( std::vector
< sal_Int32
>& rCounts
,
3561 sal_Int32 nItemLevel
,
3562 sal_Int32 nCurrentItemId
)
3564 /* The /Count number of an item is
3565 positive: the number of visible subitems
3566 negative: the negative number of subitems that will become visible if
3567 the item gets opened
3568 see PDF ref 1.4 p 478
3571 sal_Int32 nCount
= 0;
3573 if( m_aContext
.OpenBookmarkLevels
< 0 || // all levels are visible
3574 m_aContext
.OpenBookmarkLevels
>= nItemLevel
// this level is visible
3577 PDFOutlineEntry
& rItem
= m_aOutline
[ nCurrentItemId
];
3578 sal_Int32 nChildren
= rItem
.m_aChildren
.size();
3579 for( sal_Int32 i
= 0; i
< nChildren
; i
++ )
3580 nCount
+= updateOutlineItemCount( rCounts
, nItemLevel
+1, rItem
.m_aChildren
[i
] );
3581 rCounts
[nCurrentItemId
] = nCount
;
3582 // return 1 (this item) + visible sub items
3589 // this bookmark level is invisible
3590 PDFOutlineEntry
& rItem
= m_aOutline
[ nCurrentItemId
];
3591 sal_Int32 nChildren
= rItem
.m_aChildren
.size();
3592 rCounts
[ nCurrentItemId
] = -sal_Int32(rItem
.m_aChildren
.size());
3593 for( sal_Int32 i
= 0; i
< nChildren
; i
++ )
3594 updateOutlineItemCount( rCounts
, nItemLevel
+1, rItem
.m_aChildren
[i
] );
3601 sal_Int32
PDFWriterImpl::emitOutline()
3603 int i
, nItems
= m_aOutline
.size();
3605 // do we have an outline at all ?
3609 // reserve object numbers for all outline items
3610 for( i
= 0; i
< nItems
; ++i
)
3611 m_aOutline
[i
].m_nObject
= createObject();
3613 // update all parent, next and prev object ids
3614 for( i
= 0; i
< nItems
; ++i
)
3616 PDFOutlineEntry
& rItem
= m_aOutline
[i
];
3617 int nChildren
= rItem
.m_aChildren
.size();
3621 for( int n
= 0; n
< nChildren
; ++n
)
3623 PDFOutlineEntry
& rChild
= m_aOutline
[ rItem
.m_aChildren
[n
] ];
3625 rChild
.m_nParentObject
= rItem
.m_nObject
;
3626 rChild
.m_nPrevObject
= (n
> 0) ? m_aOutline
[ rItem
.m_aChildren
[n
-1] ].m_nObject
: 0;
3627 rChild
.m_nNextObject
= (n
< nChildren
-1) ? m_aOutline
[ rItem
.m_aChildren
[n
+1] ].m_nObject
: 0;
3633 // calculate Count entries for all items
3634 std::vector
< sal_Int32
> aCounts( nItems
);
3635 updateOutlineItemCount( aCounts
, 0, 0 );
3638 for( i
= 0; i
< nItems
; ++i
)
3640 PDFOutlineEntry
& rItem
= m_aOutline
[i
];
3641 OStringBuffer
aLine( 1024 );
3643 CHECK_RETURN( updateObject( rItem
.m_nObject
) );
3644 aLine
.append( rItem
.m_nObject
);
3645 aLine
.append( " 0 obj\n" );
3646 aLine
.append( "<<" );
3647 // number of visible children (all levels)
3648 if( i
> 0 || aCounts
[0] > 0 )
3650 aLine
.append( "/Count " );
3651 aLine
.append( aCounts
[i
] );
3653 if( ! rItem
.m_aChildren
.empty() )
3655 // children list: First, Last
3656 aLine
.append( "/First " );
3657 aLine
.append( m_aOutline
[rItem
.m_aChildren
.front()].m_nObject
);
3658 aLine
.append( " 0 R/Last " );
3659 aLine
.append( m_aOutline
[rItem
.m_aChildren
.back()].m_nObject
);
3660 aLine
.append( " 0 R\n" );
3664 // Title, Dest, Parent, Prev, Next
3665 aLine
.append( "/Title" );
3666 appendUnicodeTextStringEncrypt( rItem
.m_aTitle
, rItem
.m_nObject
, aLine
);
3667 aLine
.append( "\n" );
3668 // Dest is not required
3669 if( rItem
.m_nDestID
>= 0 && rItem
.m_nDestID
< (sal_Int32
)m_aDests
.size() )
3671 aLine
.append( "/Dest" );
3672 appendDest( rItem
.m_nDestID
, aLine
);
3674 aLine
.append( "/Parent " );
3675 aLine
.append( rItem
.m_nParentObject
);
3676 aLine
.append( " 0 R" );
3677 if( rItem
.m_nPrevObject
)
3679 aLine
.append( "/Prev " );
3680 aLine
.append( rItem
.m_nPrevObject
);
3681 aLine
.append( " 0 R" );
3683 if( rItem
.m_nNextObject
)
3685 aLine
.append( "/Next " );
3686 aLine
.append( rItem
.m_nNextObject
);
3687 aLine
.append( " 0 R" );
3690 aLine
.append( ">>\nendobj\n\n" );
3691 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
3694 return m_aOutline
[0].m_nObject
;
3698 #define CHECK_RETURN( x ) if( !x ) return false
3700 bool PDFWriterImpl::appendDest( sal_Int32 nDestID
, OStringBuffer
& rBuffer
)
3702 if( nDestID
< 0 || nDestID
>= (sal_Int32
)m_aDests
.size() )
3704 #if OSL_DEBUG_LEVEL > 1
3705 SAL_INFO("vcl.pdfwriter", "ERROR: invalid dest " << (int)nDestID
<< " requested");
3710 const PDFDest
& rDest
= m_aDests
[ nDestID
];
3711 const PDFPage
& rDestPage
= m_aPages
[ rDest
.m_nPage
];
3713 rBuffer
.append( '[' );
3714 rBuffer
.append( rDestPage
.m_nPageObject
);
3715 rBuffer
.append( " 0 R" );
3717 switch( rDest
.m_eType
)
3719 case PDFWriter::DestAreaType::XYZ
:
3721 rBuffer
.append( "/XYZ " );
3722 appendFixedInt( rDest
.m_aRect
.Left(), rBuffer
);
3723 rBuffer
.append( ' ' );
3724 appendFixedInt( rDest
.m_aRect
.Bottom(), rBuffer
);
3725 rBuffer
.append( " 0" );
3727 case PDFWriter::DestAreaType::FitRectangle
:
3728 rBuffer
.append( "/FitR " );
3729 appendFixedInt( rDest
.m_aRect
.Left(), rBuffer
);
3730 rBuffer
.append( ' ' );
3731 appendFixedInt( rDest
.m_aRect
.Top(), rBuffer
);
3732 rBuffer
.append( ' ' );
3733 appendFixedInt( rDest
.m_aRect
.Right(), rBuffer
);
3734 rBuffer
.append( ' ' );
3735 appendFixedInt( rDest
.m_aRect
.Bottom(), rBuffer
);
3738 rBuffer
.append( ']' );
3743 bool PDFWriterImpl::emitScreenAnnotations()
3745 int nAnnots
= m_aScreens
.size();
3746 for (int i
= 0; i
< nAnnots
; i
++)
3748 const PDFScreen
& rScreen
= m_aScreens
[i
];
3750 OStringBuffer aLine
;
3751 bool bEmbed
= false;
3752 if (!rScreen
.m_aTempFileURL
.isEmpty())
3755 if (!updateObject(rScreen
.m_nTempFileObject
))
3758 SvFileStream
aFileStream(rScreen
.m_aTempFileURL
, StreamMode::READ
);
3759 SvMemoryStream aMemoryStream
;
3760 aMemoryStream
.WriteStream(aFileStream
);
3762 aLine
.append(rScreen
.m_nTempFileObject
);
3763 aLine
.append(" 0 obj\n");
3764 aLine
.append("<< /Type /EmbeddedFile /Length ");
3765 aLine
.append(static_cast<sal_Int64
>(aMemoryStream
.GetSize()));
3766 aLine
.append(" >>\nstream\n");
3767 CHECK_RETURN(writeBuffer(aLine
.getStr(), aLine
.getLength()));
3770 CHECK_RETURN(writeBuffer(aMemoryStream
.GetData(), aMemoryStream
.GetSize()));
3772 aLine
.append("\nendstream\nendobj\n\n");
3773 CHECK_RETURN(writeBuffer(aLine
.getStr(), aLine
.getLength()));
3777 if (!updateObject(rScreen
.m_nObject
))
3780 // Annot dictionary.
3781 aLine
.append(rScreen
.m_nObject
);
3782 aLine
.append(" 0 obj\n");
3783 aLine
.append("<</Type/Annot");
3784 aLine
.append("/Subtype/Screen/Rect[");
3785 appendFixedInt(rScreen
.m_aRect
.Left(), aLine
);
3787 appendFixedInt(rScreen
.m_aRect
.Top(), aLine
);
3789 appendFixedInt(rScreen
.m_aRect
.Right(), aLine
);
3791 appendFixedInt(rScreen
.m_aRect
.Bottom(), aLine
);
3794 // Action dictionary.
3795 aLine
.append("/A<</Type/Action /S/Rendition /AN ");
3796 aLine
.append(rScreen
.m_nObject
);
3797 aLine
.append(" 0 R ");
3799 // Rendition dictionary.
3800 aLine
.append("/R<</Type/Rendition /S/MR ");
3802 // MediaClip dictionary.
3803 aLine
.append("/C<</Type/MediaClip /S/MCD ");
3806 aLine
.append("/D << /Type /Filespec /F (<embedded file>) /EF << /F ");
3807 aLine
.append(rScreen
.m_nTempFileObject
);
3808 aLine
.append(" 0 R >> >>");
3813 aLine
.append("/D << /Type /Filespec /FS /URL /F ");
3814 appendLiteralStringEncrypt(rScreen
.m_aURL
, rScreen
.m_nObject
, aLine
, osl_getThreadTextEncoding());
3815 aLine
.append(" >>");
3817 // Allow playing the video via a tempfile.
3818 aLine
.append("/P <</TF (TEMPACCESS)>>");
3819 // Until the real MIME type (instead of application/vnd.sun.star.media) is available here.
3820 aLine
.append("/CT (video/mp4)");
3823 // End Rendition dictionary by requesting play/pause/stop controls.
3824 aLine
.append("/P<</BE<</C true >>>>");
3827 // End Action dictionary.
3828 aLine
.append("/OP 0 >>");
3830 // End Annot dictionary.
3831 aLine
.append("/P ");
3832 aLine
.append(m_aPages
[rScreen
.m_nPage
].m_nPageObject
);
3833 aLine
.append(" 0 R\n>>\nendobj\n\n");
3834 CHECK_RETURN(writeBuffer(aLine
.getStr(), aLine
.getLength()));
3840 bool PDFWriterImpl::emitLinkAnnotations()
3842 int nAnnots
= m_aLinks
.size();
3843 for( int i
= 0; i
< nAnnots
; i
++ )
3845 const PDFLink
& rLink
= m_aLinks
[i
];
3846 if( ! updateObject( rLink
.m_nObject
) )
3849 OStringBuffer
aLine( 1024 );
3850 aLine
.append( rLink
.m_nObject
);
3851 aLine
.append( " 0 obj\n" );
3852 // i59651: key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
3853 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
3854 aLine
.append( "<</Type/Annot" );
3856 aLine
.append( "/F 4" );
3857 aLine
.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
3859 appendFixedInt( rLink
.m_aRect
.Left()-7, aLine
);//the +7 to have a better shape of the border rectangle
3860 aLine
.append( ' ' );
3861 appendFixedInt( rLink
.m_aRect
.Top(), aLine
);
3862 aLine
.append( ' ' );
3863 appendFixedInt( rLink
.m_aRect
.Right()+7, aLine
);//the +7 to have a better shape of the border rectangle
3864 aLine
.append( ' ' );
3865 appendFixedInt( rLink
.m_aRect
.Bottom(), aLine
);
3866 aLine
.append( "]" );
3867 if( rLink
.m_nDest
>= 0 )
3869 aLine
.append( "/Dest" );
3870 appendDest( rLink
.m_nDest
, aLine
);
3875 destination is external to the document, so
3876 we check in the following sequence:
3878 if target type is neither .pdf, nor .od[tpgs], then
3879 check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
3881 else if target is .od[tpgs]: then
3882 if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file
3885 if (new)target is .pdf : then
3886 if GotToR is requested, then
3887 convert the target in GoToR where the fragment of the URI is
3888 considered the named destination in the target file, set relative or absolute as requested
3889 else strip the fragment from URL and then set URI or 'launch application' as requested
3892 // FIXME: check if the decode mechanisms for URL processing throughout this implementation
3893 // are the correct one!!
3895 // extract target file type
3896 auto url(URIHelper::resolveIdnaHost(rLink
.m_aURL
));
3898 INetURLObject
aDocumentURL( m_aContext
.BaseURL
);
3899 INetURLObject
aTargetURL( url
);
3900 bool bSetGoToRMode
= false;
3901 bool bTargetHasPDFExtension
= false;
3902 INetProtocol eTargetProtocol
= aTargetURL
.GetProtocol();
3903 bool bIsUNCPath
= false;
3905 // check if the protocol is a known one, or if there is no protocol at all (on target only)
3906 // if there is no protocol, make the target relative to the current document directory
3907 // getting the needed URL information from the current document path
3908 if( eTargetProtocol
== INetProtocol::NotValid
)
3910 if( url
.getLength() > 4 && url
.startsWith("\\\\\\\\"))
3916 INetURLObject
aNewBase( aDocumentURL
);//duplicate document URL
3917 aNewBase
.removeSegment(); //remove last segment from it, obtaining the base URL of the
3919 aNewBase
.insertName( url
);
3920 aTargetURL
= aNewBase
;//reassign the new target URL
3921 //recompute the target protocol, with the new URL
3922 //normal URL processing resumes
3923 eTargetProtocol
= aTargetURL
.GetProtocol();
3927 OUString aFileExtension
= aTargetURL
.GetFileExtension();
3929 // Check if the URL ends in '/': if yes it's a directory,
3930 // it will be forced to a URI link.
3931 // possibly a malformed URI, leave it as it is, force as URI
3932 if( aTargetURL
.hasFinalSlash() )
3933 m_aContext
.DefaultLinkAction
= PDFWriter::URIAction
;
3935 if( !aFileExtension
.isEmpty() )
3937 if( m_aContext
.ConvertOOoTargetToPDFTarget
)
3939 bool bChangeFileExtensionToPDF
= false;
3940 //examine the file type (.odm .odt. .odp, odg, ods)
3941 if( aFileExtension
.equalsIgnoreAsciiCase( "odm" ) )
3942 bChangeFileExtensionToPDF
= true;
3943 if( aFileExtension
.equalsIgnoreAsciiCase( "odt" ) )
3944 bChangeFileExtensionToPDF
= true;
3945 else if( aFileExtension
.equalsIgnoreAsciiCase( "odp" ) )
3946 bChangeFileExtensionToPDF
= true;
3947 else if( aFileExtension
.equalsIgnoreAsciiCase( "odg" ) )
3948 bChangeFileExtensionToPDF
= true;
3949 else if( aFileExtension
.equalsIgnoreAsciiCase( "ods" ) )
3950 bChangeFileExtensionToPDF
= true;
3951 if( bChangeFileExtensionToPDF
)
3952 aTargetURL
.setExtension("pdf" );
3954 //check if extension is pdf, see if GoToR should be forced
3955 bTargetHasPDFExtension
= aTargetURL
.GetFileExtension().equalsIgnoreAsciiCase( "pdf" );
3956 if( m_aContext
.ForcePDFAction
&& bTargetHasPDFExtension
)
3957 bSetGoToRMode
= true;
3959 //prepare the URL, if relative or not
3960 INetProtocol eBaseProtocol
= aDocumentURL
.GetProtocol();
3961 //queue the string common to all types of actions
3962 aLine
.append( "/A<</Type/Action/S");
3963 if( bIsUNCPath
) // handle Win UNC paths
3965 aLine
.append( "/Launch/Win<</F" );
3966 // INetURLObject is not good with UNC paths, use original path
3967 appendLiteralStringEncrypt( url
, rLink
.m_nObject
, aLine
, osl_getThreadTextEncoding() );
3968 aLine
.append( ">>" );
3972 bool bSetRelative
= false;
3973 bool bFileSpec
= false;
3974 //check if relative file link is requested and if the protocol is 'file://'
3975 if( m_aContext
.RelFsys
&& eBaseProtocol
== eTargetProtocol
&& eTargetProtocol
== INetProtocol::File
)
3976 bSetRelative
= true;
3978 OUString aFragment
= aTargetURL
.GetMark( INetURLObject::DecodeMechanism::NONE
/*DecodeMechanism::WithCharset*/ ); //fragment as is,
3979 if( !bSetGoToRMode
)
3981 switch( m_aContext
.DefaultLinkAction
)
3984 case PDFWriter::URIAction
:
3985 case PDFWriter::URIActionDestination
:
3986 aLine
.append( "/URI/URI" );
3988 case PDFWriter::LaunchAction
:
3990 // if a launch action is requested and the hyperlink target has a fragment
3991 // and the target file does not have a pdf extension, or it's not a 'file:://'
3992 // protocol then force the uri action on it
3993 // This code will permit the correct opening of application on web pages,
3994 // the one that normally have fragments (but I may be wrong...)
3995 // and will force the use of URI when the protocol is not file:
3996 if( (!aFragment
.isEmpty() && !bTargetHasPDFExtension
) ||
3997 eTargetProtocol
!= INetProtocol::File
)
3999 aLine
.append( "/URI/URI" );
4003 aLine
.append( "/Launch/F" );
4010 //fragment are encoded in the same way as in the named destination processing
4014 OUString aURLNoMark
= aTargetURL
.GetURLNoMark( INetURLObject::DecodeMechanism::WithCharset
);
4015 aLine
.append("/GoToR");
4017 appendLiteralStringEncrypt( bSetRelative
? INetURLObject::GetRelURL( m_aContext
.BaseURL
, aURLNoMark
,
4018 INetURLObject::EncodeMechanism::WasEncoded
,
4019 INetURLObject::DecodeMechanism::WithCharset
) :
4020 aURLNoMark
, rLink
.m_nObject
, aLine
, osl_getThreadTextEncoding() );
4021 if( !aFragment
.isEmpty() )
4023 aLine
.append("/D/");
4024 appendDestinationName( aFragment
, aLine
);
4029 // change the fragment to accommodate the bookmark (only if the file extension
4030 // is PDF and the requested action is of the correct type)
4031 if(m_aContext
.DefaultLinkAction
== PDFWriter::URIActionDestination
&&
4032 bTargetHasPDFExtension
&& !aFragment
.isEmpty() )
4034 OStringBuffer
aLineLoc( 1024 );
4035 appendDestinationName( aFragment
, aLineLoc
);
4036 //substitute the fragment
4037 aTargetURL
.SetMark( OStringToOUString(aLineLoc
.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US
) );
4039 OUString aURL
= aTargetURL
.GetMainURL( bFileSpec
? INetURLObject::DecodeMechanism::WithCharset
: INetURLObject::DecodeMechanism::NONE
);
4040 appendLiteralStringEncrypt(bSetRelative
? INetURLObject::GetRelURL( m_aContext
.BaseURL
, aURL
,
4041 INetURLObject::EncodeMechanism::WasEncoded
,
4042 bFileSpec
? INetURLObject::DecodeMechanism::WithCharset
: INetURLObject::DecodeMechanism::NONE
4044 aURL
, rLink
.m_nObject
, aLine
, osl_getThreadTextEncoding() );
4047 aLine
.append( ">>\n" );
4049 if( rLink
.m_nStructParent
> 0 )
4051 aLine
.append( "/StructParent " );
4052 aLine
.append( rLink
.m_nStructParent
);
4054 aLine
.append( ">>\nendobj\n\n" );
4055 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4061 bool PDFWriterImpl::emitNoteAnnotations()
4063 // emit note annotations
4064 int nAnnots
= m_aNotes
.size();
4065 for( int i
= 0; i
< nAnnots
; i
++ )
4067 const PDFNoteEntry
& rNote
= m_aNotes
[i
];
4068 if( ! updateObject( rNote
.m_nObject
) )
4071 OStringBuffer
aLine( 1024 );
4072 aLine
.append( rNote
.m_nObject
);
4073 aLine
.append( " 0 obj\n" );
4074 // i59651: key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4075 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4076 aLine
.append( "<</Type/Annot" );
4078 aLine
.append( "/F 4" );
4079 aLine
.append( "/Subtype/Text/Rect[" );
4081 appendFixedInt( rNote
.m_aRect
.Left(), aLine
);
4082 aLine
.append( ' ' );
4083 appendFixedInt( rNote
.m_aRect
.Top(), aLine
);
4084 aLine
.append( ' ' );
4085 appendFixedInt( rNote
.m_aRect
.Right(), aLine
);
4086 aLine
.append( ' ' );
4087 appendFixedInt( rNote
.m_aRect
.Bottom(), aLine
);
4088 aLine
.append( "]" );
4090 // contents of the note (type text string)
4091 aLine
.append( "/Contents\n" );
4092 appendUnicodeTextStringEncrypt( rNote
.m_aContents
.Contents
, rNote
.m_nObject
, aLine
);
4093 aLine
.append( "\n" );
4096 if( !rNote
.m_aContents
.Title
.isEmpty() )
4098 aLine
.append( "/T" );
4099 appendUnicodeTextStringEncrypt( rNote
.m_aContents
.Title
, rNote
.m_nObject
, aLine
);
4100 aLine
.append( "\n" );
4103 aLine
.append( ">>\nendobj\n\n" );
4104 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4109 Font
PDFWriterImpl::replaceFont( const vcl::Font
& rControlFont
, const vcl::Font
& rAppSetFont
)
4111 bool bAdjustSize
= false;
4113 Font
aFont( rControlFont
);
4114 if( aFont
.GetFamilyName().isEmpty() )
4116 aFont
= rAppSetFont
;
4117 if( rControlFont
.GetFontHeight() )
4118 aFont
.SetFontSize( Size( 0, rControlFont
.GetFontHeight() ) );
4121 if( rControlFont
.GetItalic() != ITALIC_DONTKNOW
)
4122 aFont
.SetItalic( rControlFont
.GetItalic() );
4123 if( rControlFont
.GetWeight() != WEIGHT_DONTKNOW
)
4124 aFont
.SetWeight( rControlFont
.GetWeight() );
4126 else if( ! aFont
.GetFontHeight() )
4128 aFont
.SetFontSize( rAppSetFont
.GetFontSize() );
4133 Size aFontSize
= aFont
.GetFontSize();
4134 OutputDevice
* pDefDev
= Application::GetDefaultDevice();
4135 aFontSize
= OutputDevice::LogicToLogic( aFontSize
, pDefDev
->GetMapMode(), getMapMode() );
4136 aFont
.SetFontSize( aFontSize
);
4141 sal_Int32
PDFWriterImpl::getBestBuiltinFont( const vcl::Font
& rFont
)
4143 sal_Int32 nBest
= 4; // default to Helvetica
4144 OUString
aFontName( rFont
.GetFamilyName() );
4145 aFontName
= aFontName
.toAsciiLowerCase();
4147 if( aFontName
.indexOf( "times" ) != -1 )
4149 else if( aFontName
.indexOf( "courier" ) != -1 )
4151 else if( aFontName
.indexOf( "dingbats" ) != -1 )
4153 else if( aFontName
.indexOf( "symbol" ) != -1 )
4157 if( rFont
.GetItalic() == ITALIC_OBLIQUE
|| rFont
.GetItalic() == ITALIC_NORMAL
)
4159 if( rFont
.GetWeight() > WEIGHT_MEDIUM
)
4163 if( m_aBuiltinFontToObjectMap
.find( nBest
) == m_aBuiltinFontToObjectMap
.end() )
4164 m_aBuiltinFontToObjectMap
[ nBest
] = createObject();
4169 static inline const Color
& replaceColor( const Color
& rCol1
, const Color
& rCol2
)
4171 return (rCol1
== Color( COL_TRANSPARENT
)) ? rCol2
: rCol1
;
4174 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget
& rButton
, const PDFWriter::PushButtonWidget
& rWidget
)
4176 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
4178 // save graphics state
4179 push( PushFlags::ALL
);
4181 // transform relative to control's coordinates since an
4182 // appearance stream is a form XObject
4183 // this relies on the m_aRect member of rButton NOT already being transformed
4184 // to default user space
4185 if( rWidget
.Background
|| rWidget
.Border
)
4187 setLineColor( rWidget
.Border
? replaceColor( rWidget
.BorderColor
, rSettings
.GetLightColor() ) : Color( COL_TRANSPARENT
) );
4188 setFillColor( rWidget
.Background
? replaceColor( rWidget
.BackgroundColor
, rSettings
.GetDialogColor() ) : Color( COL_TRANSPARENT
) );
4189 drawRectangle( rWidget
.Location
);
4191 // prepare font to use
4192 Font aFont
= replaceFont( rWidget
.TextFont
, rSettings
.GetPushButtonFont() );
4194 setTextColor( replaceColor( rWidget
.TextColor
, rSettings
.GetButtonTextColor() ) );
4196 drawText( rButton
.m_aRect
, rButton
.m_aText
, rButton
.m_nTextStyle
);
4198 // create DA string while local mapmode is still in place
4199 // (that is before endRedirect())
4200 OStringBuffer
aDA( 256 );
4201 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetButtonTextColor() ), aDA
);
4202 Font
aDummyFont( "Helvetica", aFont
.GetFontSize() );
4203 sal_Int32 nDummyBuiltin
= getBestBuiltinFont( aDummyFont
);
4205 aDA
.append( m_aBuiltinFonts
[nDummyBuiltin
].getNameObject() );
4207 m_aPages
[m_nCurrentPage
].appendMappedLength( sal_Int32( aFont
.GetFontHeight() ), aDA
);
4208 aDA
.append( " Tf" );
4209 rButton
.m_aDAString
= aDA
.makeStringAndClear();
4213 rButton
.m_aAppearances
[ "N" ][ "Standard" ] = new SvMemoryStream();
4215 /* seems like a bad hack but at least works in both AR5 and 6:
4216 we draw the button ourselves and tell AR
4217 the button would be totally transparent with no text
4219 One would expect that simply setting a normal appearance
4220 should suffice, but no, as soon as the user actually presses
4221 the button and an action is tied to it (gasp! a button that
4222 does something) the appearance gets replaced by some crap that AR
4223 creates on the fly even if no DA or MK is given. On AR6 at least
4224 the DA and MK work as expected, but on AR5 this creates a region
4225 filled with the background color but nor text. Urgh.
4227 rButton
.m_aMKDict
= "/BC [] /BG [] /CA";
4228 rButton
.m_aMKDictCAString
= "";
4231 Font
PDFWriterImpl::drawFieldBorder( PDFWidget
& rIntern
,
4232 const PDFWriter::AnyWidget
& rWidget
,
4233 const StyleSettings
& rSettings
)
4235 Font aFont
= replaceFont( rWidget
.TextFont
, rSettings
.GetFieldFont() );
4237 if( rWidget
.Background
|| rWidget
.Border
)
4239 if( rWidget
.Border
&& rWidget
.BorderColor
== Color( COL_TRANSPARENT
) )
4241 sal_Int32 nDelta
= getReferenceDevice()->GetDPIX() / 500;
4244 setLineColor( Color( COL_TRANSPARENT
) );
4245 tools::Rectangle aRect
= rIntern
.m_aRect
;
4246 setFillColor( rSettings
.GetLightBorderColor() );
4247 drawRectangle( aRect
);
4248 aRect
.Left() += nDelta
; aRect
.Top() += nDelta
;
4249 aRect
.Right() -= nDelta
; aRect
.Bottom() -= nDelta
;
4250 setFillColor( rSettings
.GetFieldColor() );
4251 drawRectangle( aRect
);
4252 setFillColor( rSettings
.GetLightColor() );
4253 drawRectangle( tools::Rectangle( Point( aRect
.Left(), aRect
.Bottom()-nDelta
), aRect
.BottomRight() ) );
4254 drawRectangle( tools::Rectangle( Point( aRect
.Right()-nDelta
, aRect
.Top() ), aRect
.BottomRight() ) );
4255 setFillColor( rSettings
.GetDarkShadowColor() );
4256 drawRectangle( tools::Rectangle( aRect
.TopLeft(), Point( aRect
.Left()+nDelta
, aRect
.Bottom() ) ) );
4257 drawRectangle( tools::Rectangle( aRect
.TopLeft(), Point( aRect
.Right(), aRect
.Top()+nDelta
) ) );
4261 setLineColor( rWidget
.Border
? replaceColor( rWidget
.BorderColor
, rSettings
.GetShadowColor() ) : Color( COL_TRANSPARENT
) );
4262 setFillColor( rWidget
.Background
? replaceColor( rWidget
.BackgroundColor
, rSettings
.GetFieldColor() ) : Color( COL_TRANSPARENT
) );
4263 drawRectangle( rIntern
.m_aRect
);
4266 if( rWidget
.Border
)
4268 // adjust edit area accounting for border
4269 sal_Int32 nDelta
= aFont
.GetFontHeight()/4;
4272 rIntern
.m_aRect
.Left() += nDelta
;
4273 rIntern
.m_aRect
.Top() += nDelta
;
4274 rIntern
.m_aRect
.Right() -= nDelta
;
4275 rIntern
.m_aRect
.Bottom()-= nDelta
;
4281 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget
& rEdit
, const PDFWriter::EditWidget
& rWidget
)
4283 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
4284 SvMemoryStream
* pEditStream
= new SvMemoryStream( 1024, 1024 );
4286 push( PushFlags::ALL
);
4288 // prepare font to use, draw field border
4289 Font aFont
= drawFieldBorder( rEdit
, rWidget
, rSettings
);
4290 sal_Int32 nBest
= getSystemFont( aFont
);
4292 // prepare DA string
4293 OStringBuffer
aDA( 32 );
4294 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetFieldTextColor() ), aDA
);
4297 aDA
.append( nBest
);
4299 OStringBuffer
aDR( 32 );
4300 aDR
.append( "/Font " );
4301 aDR
.append( getFontDictObject() );
4302 aDR
.append( " 0 R" );
4303 rEdit
.m_aDRDict
= aDR
.makeStringAndClear();
4305 m_aPages
[ m_nCurrentPage
].appendMappedLength( sal_Int32( aFont
.GetFontHeight() ), aDA
);
4306 aDA
.append( " Tf" );
4308 /* create an empty appearance stream, let the viewer create
4309 the appearance at runtime. This is because AR5 seems to
4310 paint the widget appearance always, and a dynamically created
4311 appearance on top of it. AR6 is well behaved in that regard, so
4312 that behaviour seems to be a bug. Anyway this empty appearance
4313 relies on /NeedAppearances in the AcroForm dictionary set to "true"
4315 beginRedirect( pEditStream
, rEdit
.m_aRect
);
4316 OStringBuffer
aAppearance( 32 );
4317 aAppearance
.append( "/Tx BMC\nEMC\n" );
4318 writeBuffer( aAppearance
.getStr(), aAppearance
.getLength() );
4323 rEdit
.m_aAppearances
[ "N" ][ "Standard" ] = pEditStream
;
4325 rEdit
.m_aDAString
= aDA
.makeStringAndClear();
4328 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget
& rBox
, const PDFWriter::ListBoxWidget
& rWidget
)
4330 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
4331 SvMemoryStream
* pListBoxStream
= new SvMemoryStream( 1024, 1024 );
4333 push( PushFlags::ALL
);
4335 // prepare font to use, draw field border
4336 Font aFont
= drawFieldBorder( rBox
, rWidget
, rSettings
);
4337 sal_Int32 nBest
= getSystemFont( aFont
);
4339 beginRedirect( pListBoxStream
, rBox
.m_aRect
);
4340 OStringBuffer
aAppearance( 64 );
4342 setLineColor( Color( COL_TRANSPARENT
) );
4343 setFillColor( replaceColor( rWidget
.BackgroundColor
, rSettings
.GetFieldColor() ) );
4344 drawRectangle( rBox
.m_aRect
);
4346 // empty appearance, see createDefaultEditAppearance for reference
4347 aAppearance
.append( "/Tx BMC\nEMC\n" );
4348 writeBuffer( aAppearance
.getStr(), aAppearance
.getLength() );
4353 rBox
.m_aAppearances
[ "N" ][ "Standard" ] = pListBoxStream
;
4355 // prepare DA string
4356 OStringBuffer
aDA( 256 );
4357 // prepare DA string
4358 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetFieldTextColor() ), aDA
);
4361 aDA
.append( nBest
);
4363 OStringBuffer
aDR( 32 );
4364 aDR
.append( "/Font " );
4365 aDR
.append( getFontDictObject() );
4366 aDR
.append( " 0 R" );
4367 rBox
.m_aDRDict
= aDR
.makeStringAndClear();
4369 m_aPages
[ m_nCurrentPage
].appendMappedLength( sal_Int32( aFont
.GetFontHeight() ), aDA
);
4370 aDA
.append( " Tf" );
4371 rBox
.m_aDAString
= aDA
.makeStringAndClear();
4374 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget
& rBox
, const PDFWriter::CheckBoxWidget
& rWidget
)
4376 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
4378 // save graphics state
4379 push( PushFlags::ALL
);
4381 if( rWidget
.Background
|| rWidget
.Border
)
4383 setLineColor( rWidget
.Border
? replaceColor( rWidget
.BorderColor
, rSettings
.GetCheckedColor() ) : Color( COL_TRANSPARENT
) );
4384 setFillColor( rWidget
.Background
? replaceColor( rWidget
.BackgroundColor
, rSettings
.GetFieldColor() ) : Color( COL_TRANSPARENT
) );
4385 drawRectangle( rBox
.m_aRect
);
4388 Font aFont
= replaceFont( rWidget
.TextFont
, rSettings
.GetRadioCheckFont() );
4390 Size aFontSize
= aFont
.GetFontSize();
4391 if( aFontSize
.Height() > rBox
.m_aRect
.GetHeight() )
4392 aFontSize
.Height() = rBox
.m_aRect
.GetHeight();
4393 sal_Int32 nDelta
= aFontSize
.Height()/10;
4397 tools::Rectangle aCheckRect
, aTextRect
;
4399 aCheckRect
.Left() = rBox
.m_aRect
.Left() + nDelta
;
4400 aCheckRect
.Top() = rBox
.m_aRect
.Top() + (rBox
.m_aRect
.GetHeight()-aFontSize
.Height())/2;
4401 aCheckRect
.Right() = aCheckRect
.Left() + aFontSize
.Height();
4402 aCheckRect
.Bottom() = aCheckRect
.Top() + aFontSize
.Height();
4404 // #i74206# handle small controls without text area
4405 while( aCheckRect
.GetWidth() > rBox
.m_aRect
.GetWidth() && aCheckRect
.GetWidth() > nDelta
)
4407 aCheckRect
.Right() -= nDelta
;
4408 aCheckRect
.Top() += nDelta
/2;
4409 aCheckRect
.Bottom() -= nDelta
- (nDelta
/2);
4412 aTextRect
.Left() = rBox
.m_aRect
.Left() + aCheckRect
.GetWidth()+5*nDelta
;
4413 aTextRect
.Top() = rBox
.m_aRect
.Top();
4414 aTextRect
.Right() = aTextRect
.Left() + rBox
.m_aRect
.GetWidth() - aCheckRect
.GetWidth()-6*nDelta
;
4415 aTextRect
.Bottom() = rBox
.m_aRect
.Bottom();
4417 setLineColor( Color( COL_BLACK
) );
4418 setFillColor( Color( COL_TRANSPARENT
) );
4419 OStringBuffer
aLW( 32 );
4421 m_aPages
[m_nCurrentPage
].appendMappedLength( nDelta
, aLW
);
4422 aLW
.append( " w " );
4423 writeBuffer( aLW
.getStr(), aLW
.getLength() );
4424 drawRectangle( aCheckRect
);
4425 writeBuffer( " Q\n", 3 );
4426 setTextColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ) );
4427 drawText( aTextRect
, rBox
.m_aText
, rBox
.m_nTextStyle
);
4431 OStringBuffer
aDA( 256 );
4432 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ), aDA
);
4433 sal_Int32 nBest
= getBestBuiltinFont( Font( "ZapfDingbats", aFont
.GetFontSize() ) );
4435 aDA
.append( m_aBuiltinFonts
[nBest
].getNameObject() );
4436 aDA
.append( " 0 Tf" );
4437 rBox
.m_aDAString
= aDA
.makeStringAndClear();
4438 rBox
.m_aMKDict
= "/CA";
4439 rBox
.m_aMKDictCAString
= "8";
4440 rBox
.m_aRect
= aCheckRect
;
4442 // create appearance streams
4443 sal_Char cMark
= '8';
4444 sal_Int32 nCharXOffset
= 1000-m_aBuiltinFonts
[13].m_aWidths
[sal_Int32(cMark
)];
4445 nCharXOffset
*= aCheckRect
.GetHeight();
4446 nCharXOffset
/= 2000;
4447 sal_Int32 nCharYOffset
= 1000-
4448 (m_aBuiltinFonts
[13].m_nAscent
+m_aBuiltinFonts
[13].m_nDescent
); // descent is negative
4449 nCharYOffset
*= aCheckRect
.GetHeight();
4450 nCharYOffset
/= 2000;
4452 SvMemoryStream
* pCheckStream
= new SvMemoryStream( 256, 256 );
4453 beginRedirect( pCheckStream
, aCheckRect
);
4454 aDA
.append( "/Tx BMC\nq BT\n" );
4455 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ), aDA
);
4457 aDA
.append( m_aBuiltinFonts
[nBest
].getNameObject() );
4459 m_aPages
[ m_nCurrentPage
].appendMappedLength( sal_Int32( aCheckRect
.GetHeight() ), aDA
);
4460 aDA
.append( " Tf\n" );
4461 m_aPages
[ m_nCurrentPage
].appendMappedLength( nCharXOffset
, aDA
);
4463 m_aPages
[ m_nCurrentPage
].appendMappedLength( nCharYOffset
, aDA
);
4464 aDA
.append( " Td (" );
4465 aDA
.append( cMark
);
4466 aDA
.append( ") Tj\nET\nQ\nEMC\n" );
4467 writeBuffer( aDA
.getStr(), aDA
.getLength() );
4469 rBox
.m_aAppearances
[ "N" ][ "Yes" ] = pCheckStream
;
4471 SvMemoryStream
* pUncheckStream
= new SvMemoryStream( 256, 256 );
4472 beginRedirect( pUncheckStream
, aCheckRect
);
4473 writeBuffer( "/Tx BMC\nEMC\n", 12 );
4475 rBox
.m_aAppearances
[ "N" ][ "Off" ] = pUncheckStream
;
4478 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget
& rBox
, const PDFWriter::RadioButtonWidget
& rWidget
)
4480 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
4482 // save graphics state
4483 push( PushFlags::ALL
);
4485 if( rWidget
.Background
|| rWidget
.Border
)
4487 setLineColor( rWidget
.Border
? replaceColor( rWidget
.BorderColor
, rSettings
.GetCheckedColor() ) : Color( COL_TRANSPARENT
) );
4488 setFillColor( rWidget
.Background
? replaceColor( rWidget
.BackgroundColor
, rSettings
.GetFieldColor() ) : Color( COL_TRANSPARENT
) );
4489 drawRectangle( rBox
.m_aRect
);
4492 Font aFont
= replaceFont( rWidget
.TextFont
, rSettings
.GetRadioCheckFont() );
4494 Size aFontSize
= aFont
.GetFontSize();
4495 if( aFontSize
.Height() > rBox
.m_aRect
.GetHeight() )
4496 aFontSize
.Height() = rBox
.m_aRect
.GetHeight();
4497 sal_Int32 nDelta
= aFontSize
.Height()/10;
4501 tools::Rectangle aCheckRect
, aTextRect
;
4503 aCheckRect
.Left() = rBox
.m_aRect
.Left() + nDelta
;
4504 aCheckRect
.Top() = rBox
.m_aRect
.Top() + (rBox
.m_aRect
.GetHeight()-aFontSize
.Height())/2;
4505 aCheckRect
.Right() = aCheckRect
.Left() + aFontSize
.Height();
4506 aCheckRect
.Bottom() = aCheckRect
.Top() + aFontSize
.Height();
4508 // #i74206# handle small controls without text area
4509 while( aCheckRect
.GetWidth() > rBox
.m_aRect
.GetWidth() && aCheckRect
.GetWidth() > nDelta
)
4511 aCheckRect
.Right() -= nDelta
;
4512 aCheckRect
.Top() += nDelta
/2;
4513 aCheckRect
.Bottom() -= nDelta
- (nDelta
/2);
4516 aTextRect
.Left() = rBox
.m_aRect
.Left() + aCheckRect
.GetWidth()+5*nDelta
;
4517 aTextRect
.Top() = rBox
.m_aRect
.Top();
4518 aTextRect
.Right() = aTextRect
.Left() + rBox
.m_aRect
.GetWidth() - aCheckRect
.GetWidth()-6*nDelta
;
4519 aTextRect
.Bottom() = rBox
.m_aRect
.Bottom();
4521 setLineColor( Color( COL_BLACK
) );
4522 setFillColor( Color( COL_TRANSPARENT
) );
4523 OStringBuffer
aLW( 32 );
4525 m_aPages
[ m_nCurrentPage
].appendMappedLength( nDelta
, aLW
);
4526 aLW
.append( " w " );
4527 writeBuffer( aLW
.getStr(), aLW
.getLength() );
4528 drawEllipse( aCheckRect
);
4529 writeBuffer( " Q\n", 3 );
4530 setTextColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ) );
4531 drawText( aTextRect
, rBox
.m_aText
, rBox
.m_nTextStyle
);
4535 OStringBuffer
aDA( 256 );
4536 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ), aDA
);
4537 sal_Int32 nBest
= getBestBuiltinFont( Font( "ZapfDingbats", aFont
.GetFontSize() ) );
4539 aDA
.append( m_aBuiltinFonts
[nBest
].getNameObject() );
4540 aDA
.append( " 0 Tf" );
4541 rBox
.m_aDAString
= aDA
.makeStringAndClear();
4542 //to encrypt this (el)
4543 rBox
.m_aMKDict
= "/CA";
4544 //after this assignement, to m_aMKDic cannot be added anything
4545 rBox
.m_aMKDictCAString
= "l";
4547 rBox
.m_aRect
= aCheckRect
;
4549 // create appearance streams
4550 push( PushFlags::ALL
);
4551 SvMemoryStream
* pCheckStream
= new SvMemoryStream( 256, 256 );
4553 beginRedirect( pCheckStream
, aCheckRect
);
4554 aDA
.append( "/Tx BMC\nq BT\n" );
4555 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ), aDA
);
4557 aDA
.append( m_aBuiltinFonts
[nBest
].getNameObject() );
4559 m_aPages
[m_nCurrentPage
].appendMappedLength( sal_Int32( aCheckRect
.GetHeight() ), aDA
);
4560 aDA
.append( " Tf\n0 0 Td\nET\nQ\n" );
4561 writeBuffer( aDA
.getStr(), aDA
.getLength() );
4562 setFillColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ) );
4563 setLineColor( Color( COL_TRANSPARENT
) );
4564 aCheckRect
.Left() += 3*nDelta
;
4565 aCheckRect
.Top() += 3*nDelta
;
4566 aCheckRect
.Bottom() -= 3*nDelta
;
4567 aCheckRect
.Right() -= 3*nDelta
;
4568 drawEllipse( aCheckRect
);
4569 writeBuffer( "\nEMC\n", 5 );
4573 rBox
.m_aAppearances
[ "N" ][ "Yes" ] = pCheckStream
;
4575 SvMemoryStream
* pUncheckStream
= new SvMemoryStream( 256, 256 );
4576 beginRedirect( pUncheckStream
, aCheckRect
);
4577 writeBuffer( "/Tx BMC\nEMC\n", 12 );
4579 rBox
.m_aAppearances
[ "N" ][ "Off" ] = pUncheckStream
;
4582 bool PDFWriterImpl::emitAppearances( PDFWidget
& rWidget
, OStringBuffer
& rAnnotDict
)
4584 // TODO: check and insert default streams
4585 OString aStandardAppearance
;
4586 switch( rWidget
.m_eType
)
4588 case PDFWriter::CheckBox
:
4589 aStandardAppearance
= OUStringToOString( rWidget
.m_aValue
, RTL_TEXTENCODING_ASCII_US
);
4595 if( !rWidget
.m_aAppearances
.empty() )
4597 rAnnotDict
.append( "/AP<<\n" );
4598 for( PDFAppearanceMap::iterator dict_it
= rWidget
.m_aAppearances
.begin(); dict_it
!= rWidget
.m_aAppearances
.end(); ++dict_it
)
4600 rAnnotDict
.append( "/" );
4601 rAnnotDict
.append( dict_it
->first
);
4602 bool bUseSubDict
= (dict_it
->second
.size() > 1);
4603 rAnnotDict
.append( bUseSubDict
? "<<" : " " );
4605 for( PDFAppearanceStreams::const_iterator stream_it
= dict_it
->second
.begin();
4606 stream_it
!= dict_it
->second
.end(); ++stream_it
)
4608 SvMemoryStream
* pApppearanceStream
= stream_it
->second
;
4609 dict_it
->second
[ stream_it
->first
] = nullptr;
4611 bool bDeflate
= compressStream( pApppearanceStream
);
4613 pApppearanceStream
->Seek( STREAM_SEEK_TO_END
);
4614 sal_Int64 nStreamLen
= pApppearanceStream
->Tell();
4615 pApppearanceStream
->Seek( STREAM_SEEK_TO_BEGIN
);
4616 sal_Int32 nObject
= createObject();
4617 CHECK_RETURN( updateObject( nObject
) );
4618 #if OSL_DEBUG_LEVEL > 1
4619 emitComment( "PDFWriterImpl::emitAppearances" );
4621 OStringBuffer aLine
;
4622 aLine
.append( nObject
);
4624 aLine
.append( " 0 obj\n"
4628 appendFixedInt( rWidget
.m_aRect
.GetWidth()-1, aLine
);
4629 aLine
.append( " " );
4630 appendFixedInt( rWidget
.m_aRect
.GetHeight()-1, aLine
);
4633 aLine
.append( getResourceDictObj() );
4634 aLine
.append( " 0 R\n"
4636 aLine
.append( nStreamLen
);
4637 aLine
.append( "\n" );
4639 aLine
.append( "/Filter/FlateDecode\n" );
4640 aLine
.append( ">>\nstream\n" );
4641 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4642 checkAndEnableStreamEncryption( nObject
);
4643 CHECK_RETURN( writeBuffer( pApppearanceStream
->GetData(), nStreamLen
) );
4644 disableStreamEncryption();
4645 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
4649 rAnnotDict
.append( " /" );
4650 rAnnotDict
.append( stream_it
->first
);
4651 rAnnotDict
.append( " " );
4653 rAnnotDict
.append( nObject
);
4654 rAnnotDict
.append( " 0 R" );
4656 delete pApppearanceStream
;
4659 rAnnotDict
.append( bUseSubDict
? ">>\n" : "\n" );
4661 rAnnotDict
.append( ">>\n" );
4662 if( !aStandardAppearance
.isEmpty() )
4664 rAnnotDict
.append( "/AS /" );
4665 rAnnotDict
.append( aStandardAppearance
);
4666 rAnnotDict
.append( "\n" );
4673 bool PDFWriterImpl::emitWidgetAnnotations()
4675 ensureUniqueRadioOnValues();
4677 int nAnnots
= m_aWidgets
.size();
4678 for( int a
= 0; a
< nAnnots
; a
++ )
4680 PDFWidget
& rWidget
= m_aWidgets
[a
];
4682 OStringBuffer
aLine( 1024 );
4683 OStringBuffer
aValue( 256 );
4684 aLine
.append( rWidget
.m_nObject
);
4685 aLine
.append( " 0 obj\n"
4687 if( rWidget
.m_eType
!= PDFWriter::Hierarchy
)
4689 // emit widget annotation only for terminal fields
4690 if( rWidget
.m_aKids
.empty() )
4694 aLine
.append( "/Type/Annot/Subtype/Widget/F " );
4696 if (rWidget
.m_eType
== PDFWriter::Signature
)
4698 aLine
.append( "132\n" ); // Print & Locked
4703 aLine
.append( "4\n" );
4707 aLine
.append("/Rect[" );
4708 appendFixedInt( rWidget
.m_aRect
.Left()-iRectMargin
, aLine
);
4709 aLine
.append( ' ' );
4710 appendFixedInt( rWidget
.m_aRect
.Top()+iRectMargin
, aLine
);
4711 aLine
.append( ' ' );
4712 appendFixedInt( rWidget
.m_aRect
.Right()+iRectMargin
, aLine
);
4713 aLine
.append( ' ' );
4714 appendFixedInt( rWidget
.m_aRect
.Bottom()-iRectMargin
, aLine
);
4715 aLine
.append( "]\n" );
4717 aLine
.append( "/FT/" );
4718 switch( rWidget
.m_eType
)
4720 case PDFWriter::RadioButton
:
4721 case PDFWriter::CheckBox
:
4722 // for radio buttons only the RadioButton field, not the
4723 // CheckBox children should have a value, else acrobat reader
4724 // does not always check the right button
4725 // of course real check boxes (not belonging to a radio group)
4726 // need their values, too
4727 if( rWidget
.m_eType
== PDFWriter::RadioButton
|| rWidget
.m_nRadioGroup
< 0 )
4729 aValue
.append( "/" );
4730 // check for radio group with all buttons unpressed
4731 if( rWidget
.m_aValue
.isEmpty() )
4732 aValue
.append( "Off" );
4734 appendName( rWidget
.m_aValue
, aValue
);
4737 case PDFWriter::PushButton
:
4738 aLine
.append( "Btn" );
4740 case PDFWriter::ListBox
:
4741 if( rWidget
.m_nFlags
& 0x200000 ) // multiselect
4743 aValue
.append( "[" );
4744 for( size_t i
= 0; i
< rWidget
.m_aSelectedEntries
.size(); i
++ )
4746 sal_Int32 nEntry
= rWidget
.m_aSelectedEntries
[i
];
4747 if( nEntry
>= 0 && nEntry
< sal_Int32(rWidget
.m_aListEntries
.size()) )
4748 appendUnicodeTextStringEncrypt( rWidget
.m_aListEntries
[ nEntry
], rWidget
.m_nObject
, aValue
);
4750 aValue
.append( "]" );
4752 else if( rWidget
.m_aSelectedEntries
.size() > 0 &&
4753 rWidget
.m_aSelectedEntries
[0] >= 0 &&
4754 rWidget
.m_aSelectedEntries
[0] < sal_Int32(rWidget
.m_aListEntries
.size()) )
4756 appendUnicodeTextStringEncrypt( rWidget
.m_aListEntries
[ rWidget
.m_aSelectedEntries
[0] ], rWidget
.m_nObject
, aValue
);
4759 appendUnicodeTextStringEncrypt( OUString(), rWidget
.m_nObject
, aValue
);
4760 aLine
.append( "Ch" );
4762 case PDFWriter::ComboBox
:
4763 appendUnicodeTextStringEncrypt( rWidget
.m_aValue
, rWidget
.m_nObject
, aValue
);
4764 aLine
.append( "Ch" );
4766 case PDFWriter::Edit
:
4767 aLine
.append( "Tx" );
4768 appendUnicodeTextStringEncrypt( rWidget
.m_aValue
, rWidget
.m_nObject
, aValue
);
4770 case PDFWriter::Signature
:
4771 aLine
.append( "Sig" );
4772 aValue
.append(OUStringToOString(rWidget
.m_aValue
, RTL_TEXTENCODING_ASCII_US
));
4774 case PDFWriter::Hierarchy
: // make the compiler happy
4777 aLine
.append( "\n" );
4778 aLine
.append( "/P " );
4779 aLine
.append( m_aPages
[ rWidget
.m_nPage
].m_nPageObject
);
4780 aLine
.append( " 0 R\n" );
4782 if( rWidget
.m_nParent
)
4784 aLine
.append( "/Parent " );
4785 aLine
.append( rWidget
.m_nParent
);
4786 aLine
.append( " 0 R\n" );
4788 if( rWidget
.m_aKids
.size() )
4790 aLine
.append( "/Kids[" );
4791 for( size_t i
= 0; i
< rWidget
.m_aKids
.size(); i
++ )
4793 aLine
.append( rWidget
.m_aKids
[i
] );
4794 aLine
.append( " 0 R" );
4795 aLine
.append( ( (i
&15) == 15 ) ? "\n" : " " );
4797 aLine
.append( "]\n" );
4799 if( !rWidget
.m_aName
.isEmpty() )
4801 aLine
.append( "/T" );
4802 appendLiteralStringEncrypt( rWidget
.m_aName
, rWidget
.m_nObject
, aLine
);
4803 aLine
.append( "\n" );
4805 if( m_aContext
.Version
> PDFWriter::PDFVersion::PDF_1_2
&& !rWidget
.m_aDescription
.isEmpty() )
4807 // the alternate field name should be unicode able since it is
4808 // supposed to be used in UI
4809 aLine
.append( "/TU" );
4810 appendUnicodeTextStringEncrypt( rWidget
.m_aDescription
, rWidget
.m_nObject
, aLine
);
4811 aLine
.append( "\n" );
4814 if( rWidget
.m_nFlags
)
4816 aLine
.append( "/Ff " );
4817 aLine
.append( rWidget
.m_nFlags
);
4818 aLine
.append( "\n" );
4820 if( !aValue
.isEmpty() )
4822 OString aVal
= aValue
.makeStringAndClear();
4823 aLine
.append( "/V " );
4824 aLine
.append( aVal
);
4827 aLine
.append( aVal
);
4828 aLine
.append( "\n" );
4830 if( rWidget
.m_eType
== PDFWriter::ListBox
|| rWidget
.m_eType
== PDFWriter::ComboBox
)
4833 aLine
.append( "/Opt[\n" );
4835 for( std::vector
< OUString
>::const_iterator it
= rWidget
.m_aListEntries
.begin(); it
!= rWidget
.m_aListEntries
.end(); ++it
, ++i
)
4837 appendUnicodeTextStringEncrypt( *it
, rWidget
.m_nObject
, aLine
);
4838 aLine
.append( "\n" );
4839 if( *it
== rWidget
.m_aValue
)
4842 aLine
.append( "]\n" );
4845 aLine
.append( "/TI " );
4846 aLine
.append( nTI
);
4847 aLine
.append( "\n" );
4848 if( rWidget
.m_nFlags
& 0x200000 ) // Multiselect
4850 aLine
.append( "/I [" );
4851 aLine
.append( nTI
);
4852 aLine
.append( "]\n" );
4856 if( rWidget
.m_eType
== PDFWriter::Edit
&& rWidget
.m_nMaxLen
> 0 )
4858 aLine
.append( "/MaxLen " );
4859 aLine
.append( rWidget
.m_nMaxLen
);
4860 aLine
.append( "\n" );
4862 if( rWidget
.m_eType
== PDFWriter::PushButton
)
4866 OStringBuffer aDest
;
4867 if( rWidget
.m_nDest
!= -1 && appendDest( m_aDestinationIdTranslation
[ rWidget
.m_nDest
], aDest
) )
4869 aLine
.append( "/AA<</D<</Type/Action/S/GoTo/D " );
4870 aLine
.append( aDest
.makeStringAndClear() );
4871 aLine
.append( ">>>>\n" );
4873 else if( rWidget
.m_aListEntries
.empty() )
4875 // create a reset form action
4876 aLine
.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
4878 else if( rWidget
.m_bSubmit
)
4880 // create a submit form action
4881 aLine
.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
4882 appendLiteralStringEncrypt( rWidget
.m_aListEntries
.front(), rWidget
.m_nObject
, aLine
, osl_getThreadTextEncoding() );
4883 aLine
.append( "/Flags " );
4885 sal_Int32 nFlags
= 0;
4886 switch( m_aContext
.SubmitFormat
)
4888 case PDFWriter::HTML
:
4891 case PDFWriter::XML
:
4892 if( m_aContext
.Version
> PDFWriter::PDFVersion::PDF_1_3
)
4895 case PDFWriter::PDF
:
4896 if( m_aContext
.Version
> PDFWriter::PDFVersion::PDF_1_3
)
4899 case PDFWriter::FDF
:
4903 if( rWidget
.m_bSubmitGet
)
4905 aLine
.append( nFlags
);
4906 aLine
.append( ">>>>\n" );
4910 // create a URI action
4911 aLine
.append( "/AA<</D<</Type/Action/S/URI/URI(" );
4912 aLine
.append( OUStringToOString( rWidget
.m_aListEntries
.front(), RTL_TEXTENCODING_ASCII_US
) );
4913 aLine
.append( ")>>>>\n" );
4917 m_aErrors
.insert( PDFWriter::Warning_FormAction_Omitted_PDFA
);
4919 if( !rWidget
.m_aDAString
.isEmpty() )
4921 if( !rWidget
.m_aDRDict
.isEmpty() )
4923 aLine
.append( "/DR<<" );
4924 aLine
.append( rWidget
.m_aDRDict
);
4925 aLine
.append( ">>\n" );
4929 aLine
.append( "/DR<</Font<<" );
4930 appendBuiltinFontsToDict( aLine
);
4931 aLine
.append( ">>>>\n" );
4933 aLine
.append( "/DA" );
4934 appendLiteralStringEncrypt( rWidget
.m_aDAString
, rWidget
.m_nObject
, aLine
);
4935 aLine
.append( "\n" );
4936 if( rWidget
.m_nTextStyle
& DrawTextFlags::Center
)
4937 aLine
.append( "/Q 1\n" );
4938 else if( rWidget
.m_nTextStyle
& DrawTextFlags::Right
)
4939 aLine
.append( "/Q 2\n" );
4941 // appearance characteristics for terminal fields
4942 // which are supposed to have an appearance constructed
4943 // by the viewer application
4944 if( !rWidget
.m_aMKDict
.isEmpty() )
4946 aLine
.append( "/MK<<" );
4947 aLine
.append( rWidget
.m_aMKDict
);
4948 //add the CA string, encrypting it
4949 appendLiteralStringEncrypt(rWidget
.m_aMKDictCAString
, rWidget
.m_nObject
, aLine
);
4950 aLine
.append( ">>\n" );
4953 CHECK_RETURN( emitAppearances( rWidget
, aLine
) );
4955 aLine
.append( ">>\n"
4957 CHECK_RETURN( updateObject( rWidget
.m_nObject
) );
4958 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4963 bool PDFWriterImpl::emitAnnotations()
4965 if( m_aPages
.size() < 1 )
4968 CHECK_RETURN( emitLinkAnnotations() );
4969 CHECK_RETURN(emitScreenAnnotations());
4970 CHECK_RETURN( emitNoteAnnotations() );
4971 CHECK_RETURN( emitWidgetAnnotations() );
4976 bool PDFWriterImpl::emitEmbeddedFiles()
4978 for (auto& rEmbeddedFile
: m_aEmbeddedFiles
)
4980 if (!updateObject(rEmbeddedFile
.m_nObject
))
4983 OStringBuffer aLine
;
4984 aLine
.append(rEmbeddedFile
.m_nObject
);
4985 aLine
.append(" 0 obj\n");
4986 aLine
.append("<< /Type /EmbeddedFile /Length ");
4987 aLine
.append(static_cast<sal_Int64
>(rEmbeddedFile
.m_aData
.getLength()));
4988 aLine
.append(" >>\nstream\n");
4989 CHECK_RETURN(writeBuffer(aLine
.getStr(), aLine
.getLength()));
4992 CHECK_RETURN(writeBuffer(rEmbeddedFile
.m_aData
.getArray(), rEmbeddedFile
.m_aData
.getLength()));
4994 aLine
.append("\nendstream\nendobj\n\n");
4995 CHECK_RETURN(writeBuffer(aLine
.getStr(), aLine
.getLength()));
5001 #define CHECK_RETURN( x ) if( !x ) return false
5003 bool PDFWriterImpl::emitCatalog()
5006 // currently there is only one node that contains all leaves
5008 // first create a page tree node id
5009 sal_Int32 nTreeNode
= createObject();
5011 // emit global resource dictionary (page emit needs it)
5012 CHECK_RETURN( emitResources() );
5015 for( std::vector
<PDFPage
>::iterator it
= m_aPages
.begin(); it
!= m_aPages
.end(); ++it
)
5016 if( ! it
->emit( nTreeNode
) )
5019 sal_Int32 nNamedDestinationsDictionary
= emitNamedDestinations();
5021 sal_Int32 nOutlineDict
= emitOutline();
5023 // emit Output intent
5024 sal_Int32 nOutputIntentObject
= emitOutputIntent();
5027 sal_Int32 nMetadataObject
= emitDocumentMetadata();
5029 sal_Int32 nStructureDict
= 0;
5030 if(m_aStructure
.size() > 1)
5032 // check if dummy structure containers are needed
5033 addInternalStructureContainer(m_aStructure
[0]);
5034 nStructureDict
= m_aStructure
[0].m_nObject
= createObject();
5035 emitStructure( m_aStructure
[ 0 ] );
5038 // adjust tree node file offset
5039 if( ! updateObject( nTreeNode
) )
5043 OStringBuffer
aLine( 2048 );
5044 aLine
.append( nTreeNode
);
5045 aLine
.append( " 0 obj\n" );
5046 aLine
.append( "<</Type/Pages\n" );
5047 aLine
.append( "/Resources " );
5048 aLine
.append( getResourceDictObj() );
5049 aLine
.append( " 0 R\n" );
5051 sal_Int32 nMediaBoxWidth
= 0;
5052 sal_Int32 nMediaBoxHeight
= 0;
5053 if( m_aPages
.empty() ) // sanity check, this should not happen
5055 nMediaBoxWidth
= m_nInheritedPageWidth
;
5056 nMediaBoxHeight
= m_nInheritedPageHeight
;
5060 for( std::vector
<PDFPage
>::const_iterator iter
= m_aPages
.begin(); iter
!= m_aPages
.end(); ++iter
)
5062 if( iter
->m_nPageWidth
> nMediaBoxWidth
)
5063 nMediaBoxWidth
= iter
->m_nPageWidth
;
5064 if( iter
->m_nPageHeight
> nMediaBoxHeight
)
5065 nMediaBoxHeight
= iter
->m_nPageHeight
;
5068 aLine
.append( "/MediaBox[ 0 0 " );
5069 aLine
.append( nMediaBoxWidth
);
5070 aLine
.append( ' ' );
5071 aLine
.append( nMediaBoxHeight
);
5072 aLine
.append( " ]\n"
5075 for( std::vector
<PDFPage
>::const_iterator iter
= m_aPages
.begin(); iter
!= m_aPages
.end(); ++iter
, i
++ )
5077 aLine
.append( iter
->m_nPageObject
);
5078 aLine
.append( " 0 R" );
5079 aLine
.append( ( (i
&15) == 15 ) ? "\n" : " " );
5083 aLine
.append( (sal_Int32
)m_aPages
.size() );
5084 aLine
.append( ">>\n"
5086 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
5088 // emit annotation objects
5089 CHECK_RETURN( emitAnnotations() );
5090 CHECK_RETURN( emitEmbeddedFiles() );
5093 m_nCatalogObject
= createObject();
5094 if( ! updateObject( m_nCatalogObject
) )
5096 aLine
.setLength( 0 );
5097 aLine
.append( m_nCatalogObject
);
5098 aLine
.append( " 0 obj\n"
5099 "<</Type/Catalog/Pages " );
5100 aLine
.append( nTreeNode
);
5101 aLine
.append( " 0 R\n" );
5103 // check if there are named destinations to emit (root must be inside the catalog)
5104 if( nNamedDestinationsDictionary
)
5106 aLine
.append("/Dests ");
5107 aLine
.append( nNamedDestinationsDictionary
);
5108 aLine
.append( " 0 R\n" );
5111 if( m_aContext
.PageLayout
!= PDFWriter::DefaultLayout
)
5112 switch( m_aContext
.PageLayout
)
5115 case PDFWriter::SinglePage
:
5116 aLine
.append( "/PageLayout/SinglePage\n" );
5118 case PDFWriter::Continuous
:
5119 aLine
.append( "/PageLayout/OneColumn\n" );
5121 case PDFWriter::ContinuousFacing
:
5122 // the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5123 aLine
.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5126 if( m_aContext
.PDFDocumentMode
!= PDFWriter::ModeDefault
&& !m_aContext
.OpenInFullScreenMode
)
5127 switch( m_aContext
.PDFDocumentMode
)
5130 aLine
.append( "/PageMode/UseNone\n" );
5132 case PDFWriter::UseOutlines
:
5133 aLine
.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5135 case PDFWriter::UseThumbs
:
5136 aLine
.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5139 else if( m_aContext
.OpenInFullScreenMode
)
5140 aLine
.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5142 OStringBuffer aInitPageRef
;
5143 if( m_aContext
.InitialPage
>= 0 && m_aContext
.InitialPage
< (sal_Int32
)m_aPages
.size() )
5145 aInitPageRef
.append( m_aPages
[m_aContext
.InitialPage
].m_nPageObject
);
5146 aInitPageRef
.append( " 0 R" );
5149 aInitPageRef
.append( "0" );
5151 switch( m_aContext
.PDFDocumentAction
)
5153 case PDFWriter::ActionDefault
: //do nothing, this is the Acrobat default
5155 if( aInitPageRef
.getLength() > 1 )
5157 aLine
.append( "/OpenAction[" );
5158 aLine
.append( aInitPageRef
.makeStringAndClear() );
5159 aLine
.append( " /XYZ null null 0]\n" );
5162 case PDFWriter::FitInWindow
:
5163 aLine
.append( "/OpenAction[" );
5164 aLine
.append( aInitPageRef
.makeStringAndClear() );
5165 aLine
.append( " /Fit]\n" ); //Open fit page
5167 case PDFWriter::FitWidth
:
5168 aLine
.append( "/OpenAction[" );
5169 aLine
.append( aInitPageRef
.makeStringAndClear() );
5170 aLine
.append( " /FitH " );
5171 aLine
.append( m_nInheritedPageHeight
);//Open fit width
5172 aLine
.append( "]\n" );
5174 case PDFWriter::FitVisible
:
5175 aLine
.append( "/OpenAction[" );
5176 aLine
.append( aInitPageRef
.makeStringAndClear() );
5177 aLine
.append( " /FitBH " );
5178 aLine
.append( m_nInheritedPageHeight
);//Open fit visible
5179 aLine
.append( "]\n" );
5181 case PDFWriter::ActionZoom
:
5182 aLine
.append( "/OpenAction[" );
5183 aLine
.append( aInitPageRef
.makeStringAndClear() );
5184 aLine
.append( " /XYZ null null " );
5185 if( m_aContext
.Zoom
>= 50 && m_aContext
.Zoom
<= 1600 )
5186 aLine
.append( (double)m_aContext
.Zoom
/100.0 );
5188 aLine
.append( "0" );
5189 aLine
.append( "]\n" );
5193 // viewer preferences, if we had some, then emit
5194 if( m_aContext
.HideViewerToolbar
||
5195 ( m_aContext
.Version
> PDFWriter::PDFVersion::PDF_1_3
&& !m_aContext
.DocumentInfo
.Title
.isEmpty() && m_aContext
.DisplayPDFDocumentTitle
) ||
5196 m_aContext
.HideViewerMenubar
||
5197 m_aContext
.HideViewerWindowControls
|| m_aContext
.FitWindow
||
5198 m_aContext
.CenterWindow
|| (m_aContext
.FirstPageLeft
&& m_aContext
.PageLayout
== PDFWriter::ContinuousFacing
) ||
5199 m_aContext
.OpenInFullScreenMode
)
5201 aLine
.append( "/ViewerPreferences<<" );
5202 if( m_aContext
.HideViewerToolbar
)
5203 aLine
.append( "/HideToolbar true\n" );
5204 if( m_aContext
.HideViewerMenubar
)
5205 aLine
.append( "/HideMenubar true\n" );
5206 if( m_aContext
.HideViewerWindowControls
)
5207 aLine
.append( "/HideWindowUI true\n" );
5208 if( m_aContext
.FitWindow
)
5209 aLine
.append( "/FitWindow true\n" );
5210 if( m_aContext
.CenterWindow
)
5211 aLine
.append( "/CenterWindow true\n" );
5212 if( m_aContext
.Version
> PDFWriter::PDFVersion::PDF_1_3
&& !m_aContext
.DocumentInfo
.Title
.isEmpty() && m_aContext
.DisplayPDFDocumentTitle
)
5213 aLine
.append( "/DisplayDocTitle true\n" );
5214 if( m_aContext
.FirstPageLeft
&& m_aContext
.PageLayout
== PDFWriter::ContinuousFacing
)
5215 aLine
.append( "/Direction/R2L\n" );
5216 if( m_aContext
.OpenInFullScreenMode
)
5217 switch( m_aContext
.PDFDocumentMode
)
5220 case PDFWriter::ModeDefault
:
5221 aLine
.append( "/NonFullScreenPageMode/UseNone\n" );
5223 case PDFWriter::UseOutlines
:
5224 aLine
.append( "/NonFullScreenPageMode/UseOutlines\n" );
5226 case PDFWriter::UseThumbs
:
5227 aLine
.append( "/NonFullScreenPageMode/UseThumbs\n" );
5230 aLine
.append( ">>\n" );
5235 aLine
.append( "/Outlines " );
5236 aLine
.append( nOutlineDict
);
5237 aLine
.append( " 0 R\n" );
5239 if( nStructureDict
)
5241 aLine
.append( "/StructTreeRoot " );
5242 aLine
.append( nStructureDict
);
5243 aLine
.append( " 0 R\n" );
5245 if( !m_aContext
.DocumentLocale
.Language
.isEmpty() )
5247 /* PDF allows only RFC 3066, see above in emitStructure(). */
5248 LanguageTag
aLanguageTag( m_aContext
.DocumentLocale
);
5249 OUString aLanguage
, aScript
, aCountry
;
5250 aLanguageTag
.getIsoLanguageScriptCountry( aLanguage
, aScript
, aCountry
);
5251 if (!aLanguage
.isEmpty())
5253 OUStringBuffer
aLocBuf( 16 );
5254 aLocBuf
.append( aLanguage
);
5255 if( !aCountry
.isEmpty() )
5257 aLocBuf
.append( '-' );
5258 aLocBuf
.append( aCountry
);
5260 aLine
.append( "/Lang" );
5261 appendLiteralStringEncrypt( aLocBuf
.makeStringAndClear(), m_nCatalogObject
, aLine
);
5262 aLine
.append( "\n" );
5265 if( m_aContext
.Tagged
&& m_aContext
.Version
> PDFWriter::PDFVersion::PDF_1_3
)
5267 aLine
.append( "/MarkInfo<</Marked true>>\n" );
5269 if( m_aWidgets
.size() > 0 )
5271 aLine
.append( "/AcroForm<</Fields[\n" );
5272 int nWidgets
= m_aWidgets
.size();
5274 for( int j
= 0; j
< nWidgets
; j
++ )
5276 // output only root fields
5277 if( m_aWidgets
[j
].m_nParent
< 1 )
5279 aLine
.append( m_aWidgets
[j
].m_nObject
);
5280 aLine
.append( (nOut
++ % 5)==4 ? " 0 R\n" : " 0 R " );
5283 aLine
.append( "\n]" );
5285 #if HAVE_FEATURE_NSS
5286 if (m_nSignatureObject
!= -1)
5287 aLine
.append( "/SigFlags 3");
5290 aLine
.append( "/DR " );
5291 aLine
.append( getResourceDictObj() );
5292 aLine
.append( " 0 R" );
5293 // NeedAppearances must not be used if PDF is signed
5295 #if HAVE_FEATURE_NSS
5296 || ( m_nSignatureObject
!= -1 )
5299 aLine
.append( ">>\n" );
5301 aLine
.append( "/NeedAppearances true>>\n" );
5304 //check if there is a Metadata object
5305 if( nOutputIntentObject
)
5307 aLine
.append("/OutputIntents[");
5308 aLine
.append( nOutputIntentObject
);
5309 aLine
.append( " 0 R]" );
5312 if( nMetadataObject
)
5314 aLine
.append("/Metadata ");
5315 aLine
.append( nMetadataObject
);
5316 aLine
.append( " 0 R" );
5319 aLine
.append( ">>\n"
5321 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
5326 #if HAVE_FEATURE_NSS
5328 bool PDFWriterImpl::emitSignature()
5330 if( !updateObject( m_nSignatureObject
) )
5333 OStringBuffer
aLine( 0x5000 );
5334 aLine
.append( m_nSignatureObject
);
5335 aLine
.append( " 0 obj\n" );
5336 aLine
.append("<</Contents <" );
5338 sal_uInt64 nOffset
= ~0U;
5339 CHECK_RETURN( (osl::File::E_None
== m_aFile
.getPos(nOffset
) ) );
5341 m_nSignatureContentOffset
= nOffset
+ aLine
.getLength();
5343 // reserve some space for the PKCS#7 object
5344 OStringBuffer
aContentFiller( MAX_SIGNATURE_CONTENT_LENGTH
);
5345 comphelper::string::padToLength(aContentFiller
, MAX_SIGNATURE_CONTENT_LENGTH
, '0');
5346 aLine
.append( aContentFiller
.makeStringAndClear() );
5347 aLine
.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached");
5349 if( !m_aContext
.DocumentInfo
.Author
.isEmpty() )
5351 aLine
.append( "/Name" );
5352 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Author
, m_nSignatureObject
, aLine
);
5355 aLine
.append( " /M ");
5356 appendLiteralStringEncrypt( m_aCreationDateString
, m_nSignatureObject
, aLine
);
5358 aLine
.append( " /ByteRange [ 0 ");
5359 aLine
.append( m_nSignatureContentOffset
- 1 );
5360 aLine
.append( " " );
5361 aLine
.append( m_nSignatureContentOffset
+ MAX_SIGNATURE_CONTENT_LENGTH
+ 1 );
5362 aLine
.append( " " );
5364 m_nSignatureLastByteRangeNoOffset
= nOffset
+ aLine
.getLength();
5366 // mark the last ByteRange no and add some space. Now, we don't know
5367 // how many bytes we need for this ByteRange value
5368 // The real value will be overwritten in the finalizeSignature method
5369 OStringBuffer
aByteRangeFiller( 100 );
5370 comphelper::string::padToLength(aByteRangeFiller
, 100, ' ');
5371 aLine
.append( aByteRangeFiller
.makeStringAndClear() );
5372 aLine
.append(" /Filter/Adobe.PPKMS");
5374 //emit reason, location and contactinfo
5375 if ( !m_aContext
.SignReason
.isEmpty() )
5377 aLine
.append("/Reason");
5378 appendUnicodeTextStringEncrypt( m_aContext
.SignReason
, m_nSignatureObject
, aLine
);
5381 if ( !m_aContext
.SignLocation
.isEmpty() )
5383 aLine
.append("/Location");
5384 appendUnicodeTextStringEncrypt( m_aContext
.SignLocation
, m_nSignatureObject
, aLine
);
5387 if ( !m_aContext
.SignContact
.isEmpty() )
5389 aLine
.append("/ContactInfo");
5390 appendUnicodeTextStringEncrypt( m_aContext
.SignContact
, m_nSignatureObject
, aLine
);
5393 aLine
.append(" >>\nendobj\n\n" );
5395 if (!writeBuffer( aLine
.getStr(), aLine
.getLength() ))
5401 #if HAVE_FEATURE_NSS && !defined(_WIN32)
5405 char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo
* /*slot*/, PRBool
/*retry*/, void *arg
)
5407 return PL_strdup(static_cast<char *>(arg
));
5410 class HashContextScope
{
5413 explicit HashContextScope(HASHContext
*pPtr
) : mpPtr(pPtr
) {}
5414 ~HashContextScope() { clear(); }
5415 void clear() { if (mpPtr
) { HASH_Destroy(mpPtr
); } mpPtr
= nullptr; }
5416 HASHContext
*get() { return mpPtr
; }
5419 // ASN.1 used in the (much simpler) time stamp request. From RFC3161
5420 // and other sources.
5423 AlgorithmIdentifier ::= SEQUENCE {
5424 algorithm OBJECT IDENTIFIER,
5425 parameters ANY DEFINED BY algorithm OPTIONAL }
5426 -- contains a value of the type
5427 -- registered for use with the
5428 -- algorithm object identifier value
5430 MessageImprint ::= SEQUENCE {
5431 hashAlgorithm AlgorithmIdentifier,
5432 hashedMessage OCTET STRING }
5436 SECAlgorithmID hashAlgorithm
;
5437 SECItem hashedMessage
;
5441 Extension ::= SEQUENCE {
5442 extnID OBJECT IDENTIFIER,
5443 critical BOOLEAN DEFAULT FALSE,
5444 extnValue OCTET STRING }
5454 Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
5458 TSAPolicyId ::= OBJECT IDENTIFIER
5460 TimeStampReq ::= SEQUENCE {
5461 version INTEGER { v1(1) },
5462 messageImprint MessageImprint,
5463 --a hash algorithm OID and the hash value of the data to be
5465 reqPolicy TSAPolicyId OPTIONAL,
5466 nonce INTEGER OPTIONAL,
5467 certReq BOOLEAN DEFAULT FALSE,
5468 extensions [0] IMPLICIT Extensions OPTIONAL }
5473 MessageImprint messageImprint
;
5477 Extension
*extensions
;
5481 * General name, defined by RFC 3280.
5489 * List of general names (only one for now), defined by RFC 3280.
5497 * Supplies different fields to identify a certificate, defined by RFC 5035.
5501 GeneralNames issuer
;
5502 SECItem serialNumber
;
5506 * Supplies different fields that are used to identify certificates, defined by
5511 SECAlgorithmID hashAlgorithm
;
5513 IssuerSerial issuerSerial
;
5517 * This attribute uses the ESSCertIDv2 structure, defined by RFC 5035.
5519 struct SigningCertificateV2
5521 ESSCertIDv2
** certs
;
5523 SigningCertificateV2()
5529 // (Partial) ASN.1 for the time stamp response. Very complicated. Pulled
5530 // together from various RFCs.
5533 Accuracy ::= SEQUENCE {
5534 seconds INTEGER OPTIONAL,
5535 millis [0] INTEGER (1..999) OPTIONAL,
5536 micros [1] INTEGER (1..999) OPTIONAL }
5538 PKIStatus ::= INTEGER {
5540 -- when the PKIStatus contains the value zero a TimeStampToken, as requested, is present.
5541 grantedWithMods (1),
5542 -- when the PKIStatus contains the value one a TimeStampToken, with modifications, is present.
5545 revocationWarning (4),
5546 -- this message contains a warning that a revocation is
5548 revocationNotification (5)
5549 -- notification that a revocation has occurred
5552 PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String
5553 -- text encoded as UTF-8 String [RFC3629] (note: each
5554 -- UTF8String MAY include an [RFC3066] language tag
5555 -- to indicate the language of the contained text
5556 -- see [RFC2482] for details)
5558 PKIFailureInfo ::= BIT STRING {
5560 -- unrecognized or unsupported Algorithm Identifier
5562 -- transaction not permitted or supported
5564 -- the data submitted has the wrong format
5565 timeNotAvailable (14),
5566 -- the TSA's time source is not available
5567 unacceptedPolicy (15),
5568 -- the requested TSA policy is not supported by the TSA.
5569 unacceptedExtension (16),
5570 -- the requested extension is not supported by the TSA.
5571 addInfoNotAvailable (17),
5572 -- the additional information requested could not be understood
5573 -- or is not available
5575 -- the request cannot be handled due to system failure
5578 PKIStatusInfo ::= SEQUENCE {
5580 statusString PKIFreeText OPTIONAL,
5581 failInfo PKIFailureInfo OPTIONAL }
5583 ContentType ::= OBJECT IDENTIFIER
5585 ContentInfo ::= SEQUENCE {
5586 contentType ContentType,
5587 content [0] EXPLICIT ANY DEFINED BY contentType }
5589 CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) }
5591 DigestAlgorithmIdentifier ::= AlgorithmIdentifier
5593 DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
5595 ContentType ::= OBJECT IDENTIFIER
5597 EncapsulatedContentInfo ::= SEQUENCE {
5598 eContentType ContentType,
5599 eContent [0] EXPLICIT OCTET STRING OPTIONAL }
5601 OtherCertificateFormat ::= SEQUENCE {
5602 otherCertFormat OBJECT IDENTIFIER,
5603 otherCert ANY DEFINED BY otherCertFormat }
5605 CertificateChoices ::= CHOICE {
5606 certificate Certificate,
5607 extendedCertificate [0] IMPLICIT ExtendedCertificate, -- Obsolete
5608 v1AttrCert [1] IMPLICIT AttributeCertificateV1, -- Obsolete
5609 v2AttrCert [2] IMPLICIT AttributeCertificateV2,
5610 other [3] IMPLICIT OtherCertificateFormat }
5612 CertificateSet ::= SET OF CertificateChoices
5614 CertificateList ::= SEQUENCE {
5615 tbsCertList TBSCertList,
5616 signatureAlgorithm AlgorithmIdentifier,
5617 signatureValue BIT STRING }
5619 TBSCertList ::= SEQUENCE {
5620 version Version OPTIONAL,
5621 -- if present, MUST be v2
5622 signature AlgorithmIdentifier,
5625 nextUpdate Time OPTIONAL,
5626 revokedCertificates SEQUENCE OF SEQUENCE {
5627 userCertificate CertificateSerialNumber,
5628 revocationDate Time,
5629 crlEntryExtensions Extensions OPTIONAL
5630 -- if present, version MUST be v2
5632 crlExtensions [0] EXPLICIT Extensions OPTIONAL
5633 -- if present, version MUST be v2
5636 OtherRevocationInfoFormat ::= SEQUENCE {
5637 otherRevInfoFormat OBJECT IDENTIFIER,
5638 otherRevInfo ANY DEFINED BY otherRevInfoFormat }
5640 RevocationInfoChoice ::= CHOICE {
5641 crl CertificateList,
5642 other [1] IMPLICIT OtherRevocationInfoFormat }
5644 RevocationInfoChoices ::= SET OF RevocationInfoChoice
5646 SignerIdentifier ::= CHOICE {
5647 issuerAndSerialNumber IssuerAndSerialNumber,
5648 subjectKeyIdentifier [0] SubjectKeyIdentifier }
5650 AttributeValue ::= ANY
5652 Attribute ::= SEQUENCE {
5653 attrType OBJECT IDENTIFIER,
5654 attrValues SET OF AttributeValue }
5656 SignedAttributes ::= SET SIZE (1..MAX) OF Attribute
5658 SignatureValue ::= OCTET STRING
5660 UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute
5662 SignerInfo ::= SEQUENCE {
5664 sid SignerIdentifier,
5665 digestAlgorithm DigestAlgorithmIdentifier,
5666 signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
5667 signatureAlgorithm SignatureAlgorithmIdentifier,
5668 signature SignatureValue,
5669 unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
5671 SignerInfos ::= SET OF SignerInfo
5673 SignedData ::= SEQUENCE {
5675 digestAlgorithms DigestAlgorithmIdentifiers,
5676 encapContentInfo EncapsulatedContentInfo,
5677 certificates [0] IMPLICIT CertificateSet OPTIONAL,
5678 crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
5679 signerInfos SignerInfos }
5681 TimeStampToken ::= ContentInfo
5682 -- contentType is id-signedData as defined in [CMS]
5683 -- content is SignedData as defined in([CMS])
5684 -- eContentType within SignedData is id-ct-TSTInfo
5685 -- eContent within SignedData is TSTInfo
5687 TSTInfo ::= SEQUENCE {
5688 version INTEGER { v1(1) },
5690 messageImprint MessageImprint,
5691 -- MUST have the same value as the similar field in
5693 serialNumber INTEGER,
5694 -- Time-Stamping users MUST be ready to accommodate integers
5696 genTime GeneralizedTime,
5697 accuracy Accuracy OPTIONAL,
5698 ordering BOOLEAN DEFAULT FALSE,
5699 nonce INTEGER OPTIONAL,
5700 -- MUST be present if the similar field was present
5701 -- in TimeStampReq. In that case it MUST have the same value.
5702 tsa [0] GeneralName OPTIONAL,
5703 extensions [1] IMPLICIT Extensions OPTIONAL }
5705 TimeStampResp ::= SEQUENCE {
5706 status PKIStatusInfo,
5707 timeStampToken TimeStampToken OPTIONAL }
5710 const SEC_ASN1Template MessageImprint_Template
[] =
5712 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(MessageImprint
) },
5713 { SEC_ASN1_INLINE
, offsetof(MessageImprint
, hashAlgorithm
), SECOID_AlgorithmIDTemplate
, 0 },
5714 { SEC_ASN1_OCTET_STRING
, offsetof(MessageImprint
, hashedMessage
), nullptr, 0 },
5715 { 0, 0, nullptr, 0 }
5718 const SEC_ASN1Template Extension_Template
[] =
5720 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(Extension
) },
5721 { SEC_ASN1_OBJECT_ID
, offsetof(Extension
, extnID
), nullptr, 0 },
5722 { SEC_ASN1_BOOLEAN
, offsetof(Extension
, critical
), nullptr, 0 },
5723 { SEC_ASN1_OCTET_STRING
, offsetof(Extension
, extnValue
), nullptr, 0 },
5724 { 0, 0, nullptr, 0 }
5727 const SEC_ASN1Template Extensions_Template
[] =
5729 { SEC_ASN1_SEQUENCE_OF
, 0, Extension_Template
, 0 }
5732 const SEC_ASN1Template TimeStampReq_Template
[] =
5734 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(TimeStampReq
) },
5735 { SEC_ASN1_INTEGER
, offsetof(TimeStampReq
, version
), nullptr, 0 },
5736 { SEC_ASN1_INLINE
, offsetof(TimeStampReq
, messageImprint
), MessageImprint_Template
, 0 },
5737 { SEC_ASN1_OBJECT_ID
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, reqPolicy
), nullptr, 0 },
5738 { SEC_ASN1_INTEGER
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, nonce
), nullptr, 0 },
5739 { SEC_ASN1_BOOLEAN
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampReq
, certReq
), nullptr, 0 },
5740 { SEC_ASN1_OPTIONAL
| SEC_ASN1_CONTEXT_SPECIFIC
| 0, offsetof(TimeStampReq
, extensions
), Extensions_Template
, 0 },
5741 { 0, 0, nullptr, 0 }
5745 * GeneralName ::= CHOICE {
5746 * otherName [0] OtherName,
5747 * rfc822Name [1] IA5String,
5748 * dNSName [2] IA5String,
5749 * x400Address [3] ORAddress,
5750 * directoryName [4] Name,
5751 * ediPartyName [5] EDIPartyName,
5752 * uniformResourceIdentifier [6] IA5String,
5753 * iPAddress [7] OCTET STRING,
5754 * registeredID [8] OBJECT IDENTIFIER
5757 const SEC_ASN1Template GeneralNameTemplate
[] =
5759 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(GeneralName
)},
5760 {SEC_ASN1_INLINE
, offsetof(GeneralName
, name
), CERT_NameTemplate
, 0},
5765 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
5767 const SEC_ASN1Template GeneralNamesTemplate
[] =
5769 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(GeneralNames
)},
5770 {SEC_ASN1_INLINE
| SEC_ASN1_CONTEXT_SPECIFIC
| 4, offsetof(GeneralNames
, names
), GeneralNameTemplate
, 0},
5775 * IssuerSerial ::= SEQUENCE {
5776 * issuer GeneralNames,
5777 * serialNumber CertificateSerialNumber
5780 const SEC_ASN1Template IssuerSerialTemplate
[] =
5782 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(IssuerSerial
)},
5783 {SEC_ASN1_INLINE
, offsetof(IssuerSerial
, issuer
), GeneralNamesTemplate
, 0},
5784 {SEC_ASN1_INTEGER
, offsetof(IssuerSerial
, serialNumber
), nullptr, 0},
5789 * Hash ::= OCTET STRING
5791 * ESSCertIDv2 ::= SEQUENCE {
5792 * hashAlgorithm AlgorithmIdentifier DEFAULT {algorithm id-sha256},
5794 * issuerSerial IssuerSerial OPTIONAL
5797 const SEC_ASN1Template ESSCertIDv2Template
[] =
5799 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(ESSCertIDv2
)},
5800 {SEC_ASN1_INLINE
| SEC_ASN1_XTRN
, offsetof(ESSCertIDv2
, hashAlgorithm
), SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate
), 0},
5801 {SEC_ASN1_OCTET_STRING
, offsetof(ESSCertIDv2
, certHash
), nullptr, 0},
5802 {SEC_ASN1_INLINE
| SEC_ASN1_XTRN
, offsetof(ESSCertIDv2
, issuerSerial
), IssuerSerialTemplate
, 0},
5807 * SigningCertificateV2 ::= SEQUENCE {
5810 const SEC_ASN1Template SigningCertificateV2Template
[] =
5812 {SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(SigningCertificateV2
)},
5813 {SEC_ASN1_SEQUENCE_OF
, offsetof(SigningCertificateV2
, certs
), ESSCertIDv2Template
, 0},
5819 SECItem statusString
;
5823 const SEC_ASN1Template PKIStatusInfo_Template
[] =
5825 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(PKIStatusInfo
) },
5826 { SEC_ASN1_INTEGER
, offsetof(PKIStatusInfo
, status
), nullptr, 0 },
5827 { SEC_ASN1_CONSTRUCTED
| SEC_ASN1_SEQUENCE
| SEC_ASN1_OPTIONAL
, offsetof(PKIStatusInfo
, statusString
), nullptr, 0 },
5828 { SEC_ASN1_BIT_STRING
| SEC_ASN1_OPTIONAL
, offsetof(PKIStatusInfo
, failInfo
), nullptr, 0 },
5829 { 0, 0, nullptr, 0 }
5832 const SEC_ASN1Template Any_Template
[] =
5834 { SEC_ASN1_ANY
, 0, nullptr, sizeof(SECItem
) }
5838 PKIStatusInfo status
;
5839 SECItem timeStampToken
;
5842 const SEC_ASN1Template TimeStampResp_Template
[] =
5844 { SEC_ASN1_SEQUENCE
, 0, nullptr, sizeof(TimeStampResp
) },
5845 { SEC_ASN1_INLINE
, offsetof(TimeStampResp
, status
), PKIStatusInfo_Template
, 0 },
5846 { SEC_ASN1_ANY
| SEC_ASN1_OPTIONAL
, offsetof(TimeStampResp
, timeStampToken
), Any_Template
, 0 },
5847 { 0, 0, nullptr, 0 }
5850 /* Will see if these are needed or not
5857 const SEC_ASN1Template Integer_Template[] =
5859 { SEC_ASN1_INTEGER, 0, NULL, sizeof(SECItem) }
5862 const SEC_ASN1Template Accuracy_Template[] =
5864 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(Accuracy) },
5865 { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(Accuracy, seconds), 0, 0 },
5866 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(Accuracy, millis), Integer_Template, 0 },
5867 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 1, offsetof(Accuracy, micros), Integer_Template, 0 },
5872 size_t AppendToBuffer(char *ptr
, size_t size
, size_t nmemb
, void *userdata
)
5874 OStringBuffer
*pBuffer
= static_cast<OStringBuffer
*>(userdata
);
5875 pBuffer
->append(ptr
, size
*nmemb
);
5880 OUString
PKIStatusToString(int n
)
5884 case 0: return OUString("granted");
5885 case 1: return OUString("grantedWithMods");
5886 case 2: return OUString("rejection");
5887 case 3: return OUString("waiting");
5888 case 4: return OUString("revocationWarning");
5889 case 5: return OUString("revocationNotification");
5890 default: return "unknown (" + OUString::number(n
) + ")";
5894 OUString
PKIStatusInfoToString(const PKIStatusInfo
& rStatusInfo
)
5898 result
+= "{status=";
5899 if (rStatusInfo
.status
.len
== 1)
5900 result
+= PKIStatusToString(rStatusInfo
.status
.data
[0]);
5902 result
+= "unknown (len=" + OUString::number(rStatusInfo
.status
.len
);
5904 // FIXME: Perhaps look at rStatusInfo.statusString.data but note
5905 // that we of course can't assume it contains proper UTF-8. After
5906 // all, it is data from an external source. Also, RFC3161 claims
5907 // it should be a SEQUENCE (1..MAX) OF UTF8String, but another
5908 // source claimed it would be a single UTF8String, hmm?
5910 // FIXME: Worth it to decode failInfo to cleartext, probably not at least as long as this is only for a SAL_INFO
5917 // SEC_StringToOID() and NSS_CMSSignerInfo_AddUnauthAttr() are
5918 // not exported from libsmime, so copy them here. Sigh.
5921 my_SEC_StringToOID(SECItem
*to
, const char *from
, PRUint32 len
)
5923 PRUint32 decimal_numbers
= 0;
5924 PRUint32 result_bytes
= 0;
5926 PRUint8 result
[1024];
5928 static const PRUint32 max_decimal
= (0xffffffff / 10);
5929 static const char OIDstring
[] = {"OID."};
5932 PORT_SetError(SEC_ERROR_INVALID_ARGS
);
5936 len
= PL_strlen(from
);
5938 if (len
>= 4 && !PL_strncasecmp(from
, OIDstring
, 4)) {
5939 from
+= 4; /* skip leading "OID." if present */
5944 PORT_SetError(SEC_ERROR_BAD_DATA
);
5948 PRUint32 decimal
= 0;
5949 while (len
> 0 && rtl::isAsciiDigit(static_cast<unsigned char>(*from
))) {
5950 PRUint32 addend
= (*from
++ - '0');
5952 if (decimal
> max_decimal
) /* overflow */
5954 decimal
= (decimal
* 10) + addend
;
5955 if (decimal
< addend
) /* overflow */
5958 if (len
!= 0 && *from
!= '.') {
5961 if (decimal_numbers
== 0) {
5964 result
[0] = decimal
* 40;
5966 } else if (decimal_numbers
== 1) {
5969 result
[0] += decimal
;
5971 /* encode the decimal number, */
5973 PRUint32 num_bytes
= 0;
5974 PRUint32 tmp
= decimal
;
5980 ++num_bytes
; /* use one byte for a zero value */
5981 if (num_bytes
+ result_bytes
> sizeof result
)
5984 rp
= result
+ result_bytes
- 1;
5985 rp
[tmp
] = (PRUint8
)(decimal
& 0x7f);
5988 rp
[tmp
] = (PRUint8
)(decimal
| 0x80);
5991 result_bytes
+= num_bytes
;
5994 if (len
> 0) { /* skip trailing '.' */
5999 /* now result contains result_bytes of data */
6000 if (to
->data
&& to
->len
>= result_bytes
) {
6001 PORT_Memcpy(to
->data
, result
, to
->len
= result_bytes
);
6004 SECItem result_item
= {siBuffer
, nullptr, 0 };
6005 result_item
.data
= result
;
6006 result_item
.len
= result_bytes
;
6007 rv
= SECITEM_CopyItem(nullptr, to
, &result_item
);
6013 my_NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute
**attrs
, SECOidTag oidtag
, PRBool only
)
6016 NSSCMSAttribute
*attr1
, *attr2
;
6018 if (attrs
== nullptr)
6021 oid
= SECOID_FindOIDByTag(oidtag
);
6025 while ((attr1
= *attrs
++) != nullptr) {
6026 if (attr1
->type
.len
== oid
->oid
.len
&& PORT_Memcmp (attr1
->type
.data
,
6032 if (attr1
== nullptr)
6038 while ((attr2
= *attrs
++) != nullptr) {
6039 if (attr2
->type
.len
== oid
->oid
.len
&& PORT_Memcmp (attr2
->type
.data
,
6045 if (attr2
!= nullptr)
6052 my_NSS_CMSArray_Add(PLArenaPool
*poolp
, void ***array
, void *obj
)
6057 PORT_Assert(array
!= NULL
);
6058 if (array
== nullptr)
6061 if (*array
== nullptr) {
6062 dest
= static_cast<void **>(PORT_ArenaAlloc(poolp
, 2 * sizeof(void *)));
6067 dest
= static_cast<void **>(PORT_ArenaGrow (poolp
,
6069 (n
+ 1) * sizeof(void *),
6070 (n
+ 2) * sizeof(void *)));
6073 if (dest
== nullptr)
6077 dest
[n
+1] = nullptr;
6083 my_NSS_CMSAttribute_GetType(NSSCMSAttribute
*attr
)
6085 SECOidData
*typetag
;
6087 typetag
= SECOID_FindOID(&(attr
->type
));
6088 if (typetag
== nullptr)
6089 return SEC_OID_UNKNOWN
;
6091 return typetag
->offset
;
6095 my_NSS_CMSAttributeArray_AddAttr(PLArenaPool
*poolp
, NSSCMSAttribute
***attrs
, NSSCMSAttribute
*attr
)
6097 NSSCMSAttribute
*oattr
;
6101 mark
= PORT_ArenaMark(poolp
);
6103 /* find oidtag of attr */
6104 type
= my_NSS_CMSAttribute_GetType(attr
);
6106 /* see if we have one already */
6107 oattr
= my_NSS_CMSAttributeArray_FindAttrByOidTag(*attrs
, type
, PR_FALSE
);
6108 PORT_Assert (oattr
== NULL
);
6109 if (oattr
!= nullptr)
6110 goto loser
; /* XXX or would it be better to replace it? */
6112 /* no, shove it in */
6113 if (my_NSS_CMSArray_Add(poolp
, reinterpret_cast<void ***>(attrs
), static_cast<void *>(attr
)) != SECSuccess
)
6116 PORT_ArenaUnmark(poolp
, mark
);
6120 PORT_ArenaRelease(poolp
, mark
);
6125 my_NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo
*signerinfo
, NSSCMSAttribute
*attr
)
6127 return my_NSS_CMSAttributeArray_AddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->unAuthAttr
), attr
);
6131 my_NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo
*signerinfo
, NSSCMSAttribute
*attr
)
6133 return my_NSS_CMSAttributeArray_AddAttr(signerinfo
->cmsg
->poolp
, &(signerinfo
->authAttr
), attr
);
6136 NSSCMSMessage
*CreateCMSMessage(PRTime
* time
,
6137 NSSCMSSignedData
**cms_sd
,
6138 NSSCMSSignerInfo
**cms_signer
,
6139 CERTCertificate
*cert
,
6142 NSSCMSMessage
*result
= NSS_CMSMessage_Create(nullptr);
6145 SAL_WARN("vcl.pdfwriter", "NSS_CMSMessage_Create failed");
6149 *cms_sd
= NSS_CMSSignedData_Create(result
);
6152 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_Create failed");
6153 NSS_CMSMessage_Destroy(result
);
6157 NSSCMSContentInfo
*cms_cinfo
= NSS_CMSMessage_GetContentInfo(result
);
6158 if (NSS_CMSContentInfo_SetContent_SignedData(result
, cms_cinfo
, *cms_sd
) != SECSuccess
)
6160 SAL_WARN("vcl.pdfwriter", "NSS_CMSContentInfo_SetContent_SignedData failed");
6161 NSS_CMSSignedData_Destroy(*cms_sd
);
6162 NSS_CMSMessage_Destroy(result
);
6166 cms_cinfo
= NSS_CMSSignedData_GetContentInfo(*cms_sd
);
6168 // Attach NULL data as detached data
6169 if (NSS_CMSContentInfo_SetContent_Data(result
, cms_cinfo
, nullptr, PR_TRUE
) != SECSuccess
)
6171 SAL_WARN("vcl.pdfwriter", "NSS_CMSContentInfo_SetContent_Data failed");
6172 NSS_CMSSignedData_Destroy(*cms_sd
);
6173 NSS_CMSMessage_Destroy(result
);
6177 *cms_signer
= NSS_CMSSignerInfo_Create(result
, cert
, SEC_OID_SHA256
);
6180 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_Create failed");
6181 NSS_CMSSignedData_Destroy(*cms_sd
);
6182 NSS_CMSMessage_Destroy(result
);
6186 if (time
&& NSS_CMSSignerInfo_AddSigningTime(*cms_signer
, *time
) != SECSuccess
)
6188 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_AddSigningTime failed");
6189 NSS_CMSSignedData_Destroy(*cms_sd
);
6190 NSS_CMSMessage_Destroy(result
);
6194 if (NSS_CMSSignerInfo_IncludeCerts(*cms_signer
, NSSCMSCM_CertChain
, certUsageEmailSigner
) != SECSuccess
)
6196 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_IncludeCerts failed");
6197 NSS_CMSSignedData_Destroy(*cms_sd
);
6198 NSS_CMSMessage_Destroy(result
);
6202 if (NSS_CMSSignedData_AddCertificate(*cms_sd
, cert
) != SECSuccess
)
6204 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_AddCertificate failed");
6205 NSS_CMSSignedData_Destroy(*cms_sd
);
6206 NSS_CMSMessage_Destroy(result
);
6210 if (NSS_CMSSignedData_AddSignerInfo(*cms_sd
, *cms_signer
) != SECSuccess
)
6212 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_AddSignerInfo failed");
6213 NSS_CMSSignedData_Destroy(*cms_sd
);
6214 NSS_CMSMessage_Destroy(result
);
6218 if (NSS_CMSSignedData_SetDigestValue(*cms_sd
, SEC_OID_SHA256
, digest
) != SECSuccess
)
6220 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_SetDigestValue failed");
6221 NSS_CMSSignedData_Destroy(*cms_sd
);
6222 NSS_CMSMessage_Destroy(result
);
6229 } // anonymous namespace
6231 #endif // HAVE_FEATURE_NSS && !defined(_WIN32)
6235 typedef BOOL (WINAPI
*PointerTo_CryptRetrieveTimeStamp
)(LPCWSTR wszUrl
,
6236 DWORD dwRetrievalFlags
,
6239 const CRYPT_TIMESTAMP_PARA
*pPara
,
6242 PCRYPT_TIMESTAMP_CONTEXT
*ppTsContext
,
6243 PCCERT_CONTEXT
*ppTsSigner
,
6244 HCERTSTORE phStore
);
6249 /// Counts how many bytes are needed to encode a given length.
6250 size_t GetDERLengthOfLength(size_t nLength
)
6256 while (nLength
>> (nRet
* 8))
6258 // Long form means one additional byte: the length of the length and
6259 // the length itself.
6265 /// Writes the length part of the header.
6266 void WriteDERLength(SvStream
& rStream
, size_t nLength
)
6268 size_t nLengthOfLength
= GetDERLengthOfLength(nLength
);
6269 if (nLengthOfLength
== 1)
6271 // We can use the short form.
6272 rStream
.WriteUInt8(nLength
);
6276 // 0x80 means that the we use the long form: the first byte is the length
6277 // of length with the highest bit set to 1, not the actual length.
6278 rStream
.WriteUInt8(0x80 | (nLengthOfLength
- 1));
6279 for (size_t i
= 1; i
< nLengthOfLength
; ++i
)
6280 rStream
.WriteUInt8(nLength
>> ((nLengthOfLength
- i
- 1) * 8));
6283 const unsigned nASN1_INTEGER
= 0x02;
6284 const unsigned nASN1_OCTET_STRING
= 0x04;
6285 const unsigned nASN1_NULL
= 0x05;
6286 const unsigned nASN1_OBJECT_IDENTIFIER
= 0x06;
6287 const unsigned nASN1_SEQUENCE
= 0x10;
6288 /// An explicit tag on a constructed value.
6289 const unsigned nASN1_TAGGED_CONSTRUCTED
= 0xa0;
6290 const unsigned nASN1_CONSTRUCTED
= 0x20;
6292 /// Create payload for the 'signing-certificate' signed attribute.
6293 bool CreateSigningCertificateAttribute(vcl::PDFWriter::PDFSignContext
& rContext
, PCCERT_CONTEXT pCertContext
, SvStream
& rEncodedCertificate
)
6295 // CryptEncodeObjectEx() does not support encoding arbitrary ASN.1
6296 // structures, like SigningCertificateV2 from RFC 5035, so let's build it
6299 // Count the certificate hash and put it to aHash.
6300 // 2.16.840.1.101.3.4.2.1, i.e. sha256.
6301 std::vector
<unsigned char> aSHA256
{0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01};
6303 HCRYPTPROV hProv
= 0;
6304 if (!CryptAcquireContext(&hProv
, nullptr, nullptr, PROV_RSA_AES
, CRYPT_VERIFYCONTEXT
))
6306 SAL_WARN("vcl.pdfwriter", "CryptAcquireContext() failed");
6310 HCRYPTHASH hHash
= 0;
6311 if (!CryptCreateHash(hProv
, CALG_SHA_256
, 0, 0, &hHash
))
6313 SAL_WARN("vcl.pdfwriter", "CryptCreateHash() failed");
6317 if (!CryptHashData(hHash
, reinterpret_cast<const BYTE
*>(rContext
.m_pDerEncoded
), rContext
.m_nDerEncoded
, 0))
6319 SAL_WARN("vcl.pdfwriter", "CryptHashData() failed");
6324 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, nullptr, &nHash
, 0))
6326 SAL_WARN("vcl.pdfwriter", "CryptGetHashParam() failed to provide the hash length");
6330 std::vector
<unsigned char> aHash(nHash
);
6331 if (!CryptGetHashParam(hHash
, HP_HASHVAL
, aHash
.data(), &nHash
, 0))
6333 SAL_WARN("vcl.pdfwriter", "CryptGetHashParam() failed to provide the hash");
6337 CryptDestroyHash(hHash
);
6338 CryptReleaseContext(hProv
, 0);
6340 // Collect info for IssuerSerial.
6341 BYTE
* pIssuer
= pCertContext
->pCertInfo
->Issuer
.pbData
;
6342 DWORD nIssuer
= pCertContext
->pCertInfo
->Issuer
.cbData
;
6343 BYTE
* pSerial
= pCertContext
->pCertInfo
->SerialNumber
.pbData
;
6344 DWORD nSerial
= pCertContext
->pCertInfo
->SerialNumber
.cbData
;
6345 // pSerial is LE, aSerial is BE.
6346 std::vector
<BYTE
> aSerial(nSerial
);
6347 for (size_t i
= 0; i
< nSerial
; ++i
)
6348 aSerial
[i
] = *(pSerial
+ nSerial
- i
- 1);
6350 // We now have all the info to count the lengths.
6351 // The layout of the payload is:
6352 // SEQUENCE: SigningCertificateV2
6353 // SEQUENCE: SEQUENCE OF ESSCertIDv2
6354 // SEQUENCE: ESSCertIDv2
6355 // SEQUENCE: AlgorithmIdentifier
6356 // OBJECT: algorithm
6358 // OCTET STRING: certHash
6359 // SEQUENCE: IssuerSerial
6360 // SEQUENCE: GeneralNames
6362 // SEQUENCE: Issuer blob
6363 // INTEGER: CertificateSerialNumber
6365 size_t nAlgorithm
= 1 + GetDERLengthOfLength(aSHA256
.size()) + aSHA256
.size();
6366 size_t nParameters
= 1 + GetDERLengthOfLength(1);
6367 size_t nAlgorithmIdentifier
= 1 + GetDERLengthOfLength(nAlgorithm
+ nParameters
) + nAlgorithm
+ nParameters
;
6368 size_t nCertHash
= 1 + GetDERLengthOfLength(aHash
.size()) + aHash
.size();
6369 size_t nName
= 1 + GetDERLengthOfLength(nIssuer
) + nIssuer
;
6370 size_t nGeneralNames
= 1 + GetDERLengthOfLength(nName
) + nName
;
6371 size_t nCertificateSerialNumber
= 1 + GetDERLengthOfLength(nSerial
) + nSerial
;
6372 size_t nIssuerSerial
= 1 + GetDERLengthOfLength(nGeneralNames
+ nCertificateSerialNumber
) + nGeneralNames
+ nCertificateSerialNumber
;
6373 size_t nESSCertIDv2
= 1 + GetDERLengthOfLength(nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
) + nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
;
6374 size_t nESSCertIDv2s
= 1 + GetDERLengthOfLength(nESSCertIDv2
) + nESSCertIDv2
;
6376 // Write SigningCertificateV2.
6377 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
6378 WriteDERLength(rEncodedCertificate
, nESSCertIDv2s
);
6379 // Write SEQUENCE OF ESSCertIDv2.
6380 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
6381 WriteDERLength(rEncodedCertificate
, nESSCertIDv2
);
6382 // Write ESSCertIDv2.
6383 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
6384 WriteDERLength(rEncodedCertificate
, nAlgorithmIdentifier
+ nCertHash
+ nIssuerSerial
);
6385 // Write AlgorithmIdentifier.
6386 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
6387 WriteDERLength(rEncodedCertificate
, nAlgorithm
+ nParameters
);
6389 rEncodedCertificate
.WriteUInt8(nASN1_OBJECT_IDENTIFIER
);
6390 WriteDERLength(rEncodedCertificate
, aSHA256
.size());
6391 rEncodedCertificate
.WriteBytes(aSHA256
.data(), aSHA256
.size());
6392 // Write parameters.
6393 rEncodedCertificate
.WriteUInt8(nASN1_NULL
);
6394 rEncodedCertificate
.WriteUInt8(0);
6396 rEncodedCertificate
.WriteUInt8(nASN1_OCTET_STRING
);
6397 WriteDERLength(rEncodedCertificate
, aHash
.size());
6398 rEncodedCertificate
.WriteBytes(aHash
.data(), aHash
.size());
6399 // Write IssuerSerial.
6400 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
6401 WriteDERLength(rEncodedCertificate
, nGeneralNames
+ nCertificateSerialNumber
);
6402 // Write GeneralNames.
6403 rEncodedCertificate
.WriteUInt8(nASN1_SEQUENCE
| nASN1_CONSTRUCTED
);
6404 WriteDERLength(rEncodedCertificate
, nName
);
6406 rEncodedCertificate
.WriteUInt8(nASN1_TAGGED_CONSTRUCTED
| 4);
6407 WriteDERLength(rEncodedCertificate
, nIssuer
);
6408 rEncodedCertificate
.WriteBytes(pIssuer
, nIssuer
);
6409 // Write CertificateSerialNumber.
6410 rEncodedCertificate
.WriteUInt8(nASN1_INTEGER
);
6411 WriteDERLength(rEncodedCertificate
, nSerial
);
6412 rEncodedCertificate
.WriteBytes(aSerial
.data(), aSerial
.size());
6416 } // anonymous namespace
6420 bool PDFWriter::Sign(PDFSignContext
& rContext
)
6424 CERTCertificate
*cert
= CERT_DecodeCertFromPackage(reinterpret_cast<char *>(rContext
.m_pDerEncoded
), rContext
.m_nDerEncoded
);
6428 SAL_WARN("vcl.pdfwriter", "CERT_DecodeCertFromPackage failed");
6432 HashContextScope
hc(HASH_Create(HASH_AlgSHA256
));
6435 SAL_WARN("vcl.pdfwriter", "HASH_Create failed");
6439 HASH_Begin(hc
.get());
6441 HASH_Update(hc
.get(), static_cast<const unsigned char*>(rContext
.m_pByteRange1
), rContext
.m_nByteRange1
);
6443 HASH_Update(hc
.get(), static_cast<const unsigned char*>(rContext
.m_pByteRange2
), rContext
.m_nByteRange2
);
6446 unsigned char hash
[SHA256_LENGTH
];
6448 HASH_End(hc
.get(), digest
.data
, &digest
.len
, SHA256_LENGTH
);
6453 FILE *out
= fopen("PDFWRITER.hash.data", "wb");
6454 fwrite(hash
, SHA256_LENGTH
, 1, out
);
6459 PRTime now
= PR_Now();
6460 NSSCMSSignedData
*cms_sd
;
6461 NSSCMSSignerInfo
*cms_signer
;
6462 NSSCMSMessage
*cms_msg
= CreateCMSMessage(nullptr, &cms_sd
, &cms_signer
, cert
, &digest
);
6466 char *pass(strdup(OUStringToOString( rContext
.m_aSignPassword
, RTL_TEXTENCODING_UTF8
).getStr()));
6469 OStringBuffer response_buffer
;
6470 TimeStampResp response
;
6471 SECItem response_item
;
6472 NSSCMSAttribute timestamp
;
6474 SECItem
*valuesp
[2];
6475 valuesp
[0] = values
;
6476 valuesp
[1] = nullptr;
6479 if( !rContext
.m_aSignTSA
.isEmpty() )
6481 // Create another CMS message with the same contents as cms_msg, because it doesn't seem
6482 // possible to encode a message twice (once to get something to timestamp, and then after
6483 // adding the timestamp attribute).
6485 NSSCMSSignedData
*ts_cms_sd
;
6486 NSSCMSSignerInfo
*ts_cms_signer
;
6487 NSSCMSMessage
*ts_cms_msg
= CreateCMSMessage(&now
, &ts_cms_sd
, &ts_cms_signer
, cert
, &digest
);
6494 SECItem ts_cms_output
;
6495 ts_cms_output
.data
= nullptr;
6496 ts_cms_output
.len
= 0;
6497 PLArenaPool
*ts_arena
= PORT_NewArena(10000);
6498 NSSCMSEncoderContext
*ts_cms_ecx
;
6499 ts_cms_ecx
= NSS_CMSEncoder_Start(ts_cms_msg
, nullptr, nullptr, &ts_cms_output
, ts_arena
, PDFSigningPKCS7PasswordCallback
, pass
, nullptr, nullptr, nullptr, nullptr);
6501 if (NSS_CMSEncoder_Finish(ts_cms_ecx
) != SECSuccess
)
6503 SAL_WARN("vcl.pdfwriter", "NSS_CMSEncoder_Finish failed");
6508 // I have compared the ts_cms_output produced here with the cms_output produced below, with
6509 // the DONTCALLADDUNAUTHATTR env var set (i.e. without actually calling
6510 // my_NSS_CMSSignerInfo_AddUnauthAttr()), and they are identical.
6514 FILE *out
= fopen("PDFWRITER.ts_cms.data", "wb");
6515 fwrite(ts_cms_output
.data
, ts_cms_output
.len
, 1, out
);
6520 HashContextScope
ts_hc(HASH_Create(HASH_AlgSHA256
));
6523 SAL_WARN("vcl.pdfwriter", "HASH_Create failed");
6528 HASH_Begin(ts_hc
.get());
6529 HASH_Update(ts_hc
.get(), ts_cms_signer
->encDigest
.data
, ts_cms_signer
->encDigest
.len
);
6531 unsigned char ts_hash
[SHA256_LENGTH
];
6532 ts_digest
.type
= siBuffer
;
6533 ts_digest
.data
= ts_hash
;
6534 HASH_End(ts_hc
.get(), ts_digest
.data
, &ts_digest
.len
, SHA256_LENGTH
);
6539 FILE *out
= fopen("PDFWRITER.ts_hash.data", "wb");
6540 fwrite(ts_hash
, SHA256_LENGTH
, 1, out
);
6545 unsigned char cOne
= 1;
6546 src
.version
.type
= siUnsignedInteger
;
6547 src
.version
.data
= &cOne
;
6548 src
.version
.len
= sizeof(cOne
);
6550 src
.messageImprint
.hashAlgorithm
.algorithm
.data
= nullptr;
6551 src
.messageImprint
.hashAlgorithm
.parameters
.data
= nullptr;
6552 SECOID_SetAlgorithmID(nullptr, &src
.messageImprint
.hashAlgorithm
, SEC_OID_SHA256
, nullptr);
6553 src
.messageImprint
.hashedMessage
= ts_digest
;
6555 src
.reqPolicy
.type
= siBuffer
;
6556 src
.reqPolicy
.data
= nullptr;
6557 src
.reqPolicy
.len
= 0;
6559 unsigned int nNonce
= comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32
);
6560 src
.nonce
.type
= siUnsignedInteger
;
6561 src
.nonce
.data
= reinterpret_cast<unsigned char*>(&nNonce
);
6562 src
.nonce
.len
= sizeof(nNonce
);
6564 src
.certReq
.type
= siUnsignedInteger
;
6565 src
.certReq
.data
= &cOne
;
6566 src
.certReq
.len
= sizeof(cOne
);
6568 src
.extensions
= nullptr;
6570 SECItem
* timestamp_request
= SEC_ASN1EncodeItem(nullptr, nullptr, &src
, TimeStampReq_Template
);
6571 if (timestamp_request
== nullptr)
6573 SAL_WARN("vcl.pdfwriter", "SEC_ASN1EncodeItem failed");
6578 if (timestamp_request
->data
== nullptr)
6580 SAL_WARN("vcl.pdfwriter", "SEC_ASN1EncodeItem succeeded but got NULL data");
6582 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
6586 SAL_INFO("vcl.pdfwriter", "request length=" << timestamp_request
->len
);
6590 FILE *out
= fopen("PDFWRITER.timestampreq.data", "wb");
6591 fwrite(timestamp_request
->data
, timestamp_request
->len
, 1, out
);
6596 // Send time stamp request to TSA server, receive response
6598 CURL
* curl
= curl_easy_init();
6600 struct curl_slist
* slist
= nullptr;
6604 SAL_WARN("vcl.pdfwriter", "curl_easy_init failed");
6606 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
6610 SAL_INFO("vcl.pdfwriter", "Setting curl to verbose: " << (curl_easy_setopt(curl
, CURLOPT_VERBOSE
, 1) == CURLE_OK
? "OK" : "FAIL"));
6612 if ((rc
= curl_easy_setopt(curl
, CURLOPT_URL
, OUStringToOString(rContext
.m_aSignTSA
, RTL_TEXTENCODING_UTF8
).getStr())) != CURLE_OK
)
6614 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_URL) failed: " << curl_easy_strerror(rc
));
6616 curl_easy_cleanup(curl
);
6617 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
6621 slist
= curl_slist_append(slist
, "Content-Type: application/timestamp-query");
6622 slist
= curl_slist_append(slist
, "Accept: application/timestamp-reply");
6624 if ((rc
= curl_easy_setopt(curl
, CURLOPT_HTTPHEADER
, slist
)) != CURLE_OK
)
6626 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_HTTPHEADER) failed: " << curl_easy_strerror(rc
));
6628 curl_slist_free_all(slist
);
6629 curl_easy_cleanup(curl
);
6630 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
6634 if ((rc
= curl_easy_setopt(curl
, CURLOPT_POSTFIELDSIZE
, static_cast<long>(timestamp_request
->len
))) != CURLE_OK
||
6635 (rc
= curl_easy_setopt(curl
, CURLOPT_POSTFIELDS
, timestamp_request
->data
)) != CURLE_OK
)
6637 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_POSTFIELDSIZE or CURLOPT_POSTFIELDS) failed: " << curl_easy_strerror(rc
));
6639 curl_easy_cleanup(curl
);
6640 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
6644 if ((rc
= curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, &response_buffer
)) != CURLE_OK
||
6645 (rc
= curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, AppendToBuffer
)) != CURLE_OK
)
6647 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_WRITEDATA or CURLOPT_WRITEFUNCTION) failed: " << curl_easy_strerror(rc
));
6649 curl_easy_cleanup(curl
);
6650 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
6654 if ((rc
= curl_easy_setopt(curl
, CURLOPT_POST
, 1)) != CURLE_OK
)
6656 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_POST) failed: " << curl_easy_strerror(rc
));
6658 curl_easy_cleanup(curl
);
6659 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
6663 char error_buffer
[CURL_ERROR_SIZE
];
6664 if ((rc
= curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, error_buffer
)) != CURLE_OK
)
6666 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_ERRORBUFFER) failed: " << curl_easy_strerror(rc
));
6668 curl_easy_cleanup(curl
);
6669 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
6673 // Use a ten second timeout
6674 if ((rc
= curl_easy_setopt(curl
, CURLOPT_TIMEOUT
, 10)) != CURLE_OK
||
6675 (rc
= curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
, 10)) != CURLE_OK
)
6677 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_TIMEOUT or CURLOPT_CONNECTTIMEOUT) failed: " << curl_easy_strerror(rc
));
6679 curl_easy_cleanup(curl
);
6680 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
6684 if (curl_easy_perform(curl
) != CURLE_OK
)
6686 SAL_WARN("vcl.pdfwriter", "curl_easy_perform failed: " << error_buffer
);
6688 curl_easy_cleanup(curl
);
6689 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
6693 SAL_INFO("vcl.pdfwriter", "PDF signing: got response, length=" << response_buffer
.getLength());
6697 FILE *out
= fopen("PDFWRITER.reply.data", "wb");
6698 fwrite(response_buffer
.getStr(), response_buffer
.getLength(), 1, out
);
6703 curl_slist_free_all(slist
);
6704 curl_easy_cleanup(curl
);
6705 SECITEM_FreeItem(timestamp_request
, PR_TRUE
);
6707 memset(&response
, 0, sizeof(response
));
6709 response_item
.type
= siBuffer
;
6710 response_item
.data
= reinterpret_cast<unsigned char*>(const_cast<char*>(response_buffer
.getStr()));
6711 response_item
.len
= response_buffer
.getLength();
6713 if (SEC_ASN1DecodeItem(nullptr, &response
, TimeStampResp_Template
, &response_item
) != SECSuccess
)
6715 SAL_WARN("vcl.pdfwriter", "SEC_ASN1DecodeItem failed");
6720 SAL_INFO("vcl.pdfwriter", "TimeStampResp received and decoded, status=" << PKIStatusInfoToString(response
.status
));
6722 if (response
.status
.status
.len
!= 1 ||
6723 (response
.status
.status
.data
[0] != 0 && response
.status
.status
.data
[0] != 1))
6725 SAL_WARN("vcl.pdfwriter", "Timestamp request was not granted");
6730 // timestamp.type filled in below
6732 // Not sure if we actually need two entries in the values array, now when valuesp is an
6733 // array too, the pointer to the values array followed by a null pointer. But I don't feel
6734 // like experimenting.
6735 values
[0] = response
.timeStampToken
;
6736 values
[1].type
= siBuffer
;
6737 values
[1].data
= nullptr;
6740 timestamp
.values
= valuesp
;
6742 typetag
.oid
.data
= nullptr;
6743 // id-aa-timeStampToken OBJECT IDENTIFIER ::= { iso(1)
6744 // member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)
6745 // smime(16) aa(2) 14 }
6746 if (my_SEC_StringToOID(&typetag
.oid
, "1.2.840.113549.1.9.16.2.14", 0) != SECSuccess
)
6748 SAL_WARN("vcl.pdfwriter", "SEC_StringToOID failed");
6752 typetag
.offset
= SEC_OID_UNKNOWN
; // ???
6753 typetag
.desc
= "id-aa-timeStampToken";
6754 typetag
.mechanism
= CKM_SHA_1
; // ???
6755 typetag
.supportedExtension
= UNSUPPORTED_CERT_EXTENSION
; // ???
6756 timestamp
.typeTag
= &typetag
;
6758 timestamp
.type
= typetag
.oid
; // ???
6760 timestamp
.encoded
= PR_TRUE
; // ???
6763 if (getenv("DONTCALLADDUNAUTHATTR"))
6767 if (my_NSS_CMSSignerInfo_AddUnauthAttr(cms_signer
, ×tamp
) != SECSuccess
)
6769 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_AddUnauthAttr failed");
6775 // Add the signing certificate as a signed attribute.
6776 ESSCertIDv2
* aCertIDs
[2];
6777 ESSCertIDv2 aCertID
;
6778 // Write ESSCertIDv2.hashAlgorithm.
6779 aCertID
.hashAlgorithm
.algorithm
.data
= nullptr;
6780 aCertID
.hashAlgorithm
.parameters
.data
= nullptr;
6781 SECOID_SetAlgorithmID(nullptr, &aCertID
.hashAlgorithm
, SEC_OID_SHA256
, nullptr);
6782 // Write ESSCertIDv2.certHash.
6783 SECItem aCertHashItem
;
6784 unsigned char aCertHash
[SHA256_LENGTH
];
6785 HashContextScope
aCertHashContext(HASH_Create(HASH_AlgSHA256
));
6786 if (!aCertHashContext
.get())
6788 SAL_WARN("vcl.pdfwriter", "HASH_Create() failed");
6792 HASH_Begin(aCertHashContext
.get());
6793 HASH_Update(aCertHashContext
.get(), reinterpret_cast<const unsigned char *>(rContext
.m_pDerEncoded
), rContext
.m_nDerEncoded
);
6794 aCertHashItem
.type
= siBuffer
;
6795 aCertHashItem
.data
= aCertHash
;
6796 HASH_End(aCertHashContext
.get(), aCertHashItem
.data
, &aCertHashItem
.len
, SHA256_LENGTH
);
6797 aCertID
.certHash
= aCertHashItem
;
6798 // Write ESSCertIDv2.issuerSerial.
6799 IssuerSerial aSerial
;
6801 aName
.name
= cert
->issuer
;
6802 aSerial
.issuer
.names
= aName
;
6803 aSerial
.serialNumber
= cert
->serialNumber
;
6804 aCertID
.issuerSerial
= aSerial
;
6805 // Write SigningCertificateV2.certs.
6806 aCertIDs
[0] = &aCertID
;
6807 aCertIDs
[1] = nullptr;
6808 SigningCertificateV2 aCertificate
;
6809 aCertificate
.certs
= &aCertIDs
[0];
6810 SECItem
* pEncodedCertificate
= SEC_ASN1EncodeItem(nullptr, nullptr, &aCertificate
, SigningCertificateV2Template
);
6811 if (!pEncodedCertificate
)
6813 SAL_WARN("vcl.pdfwriter", "SEC_ASN1EncodeItem() failed");
6818 NSSCMSAttribute aAttribute
;
6819 SECItem aAttributeValues
[2];
6820 SECItem
* pAttributeValues
[2];
6821 pAttributeValues
[0] = aAttributeValues
;
6822 pAttributeValues
[1] = nullptr;
6823 aAttributeValues
[0] = *pEncodedCertificate
;
6824 aAttributeValues
[1].type
= siBuffer
;
6825 aAttributeValues
[1].data
= nullptr;
6826 aAttributeValues
[1].len
= 0;
6827 aAttribute
.values
= pAttributeValues
;
6829 SECOidData aOidData
;
6830 aOidData
.oid
.data
= nullptr;
6832 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
6833 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
6834 * smime(16) id-aa(2) 47 }
6836 if (my_SEC_StringToOID(&aOidData
.oid
, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess
)
6838 SAL_WARN("vcl.pdfwriter", "my_SEC_StringToOID() failed");
6842 aOidData
.offset
= SEC_OID_UNKNOWN
;
6843 aOidData
.desc
= "id-aa-signingCertificateV2";
6844 aOidData
.mechanism
= CKM_SHA_1
;
6845 aOidData
.supportedExtension
= UNSUPPORTED_CERT_EXTENSION
;
6846 aAttribute
.typeTag
= &aOidData
;
6847 aAttribute
.type
= aOidData
.oid
;
6848 aAttribute
.encoded
= PR_TRUE
;
6850 if (my_NSS_CMSSignerInfo_AddAuthAttr(cms_signer
, &aAttribute
) != SECSuccess
)
6852 SAL_WARN("vcl.pdfwriter", "my_NSS_CMSSignerInfo_AddAuthAttr() failed");
6858 cms_output
.data
= nullptr;
6860 PLArenaPool
*arena
= PORT_NewArena(10000);
6861 NSSCMSEncoderContext
*cms_ecx
;
6863 // Possibly it would work to even just pass NULL for the password callback function and its
6864 // argument here. After all, at least with the hardware token and associated software I tested
6865 // with, the software itself pops up a dialog asking for the PIN (password). But I am not going
6866 // to test it and risk locking up my token...
6868 cms_ecx
= NSS_CMSEncoder_Start(cms_msg
, nullptr, nullptr, &cms_output
, arena
, PDFSigningPKCS7PasswordCallback
, pass
, nullptr, nullptr, nullptr, nullptr);
6872 SAL_WARN("vcl.pdfwriter", "NSS_CMSEncoder_Start failed");
6877 if (NSS_CMSEncoder_Finish(cms_ecx
) != SECSuccess
)
6879 SAL_WARN("vcl.pdfwriter", "NSS_CMSEncoder_Finish failed");
6888 FILE *out
= fopen("PDFWRITER.cms.data", "wb");
6889 fwrite(cms_output
.data
, cms_output
.len
, 1, out
);
6894 if (cms_output
.len
*2 > MAX_SIGNATURE_CONTENT_LENGTH
)
6896 SAL_WARN("vcl.pdfwriter", "Signature requires more space (" << cms_output
.len
*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH
<< ")");
6897 NSS_CMSMessage_Destroy(cms_msg
);
6901 for (unsigned int i
= 0; i
< cms_output
.len
; i
++)
6902 appendHex(cms_output
.data
[i
], rContext
.m_rCMSHexBuffer
);
6904 SECITEM_FreeItem(pEncodedCertificate
, PR_TRUE
);
6905 NSS_CMSMessage_Destroy(cms_msg
);
6910 PCCERT_CONTEXT pCertContext
= CertCreateCertificateContext(X509_ASN_ENCODING
| PKCS_7_ASN_ENCODING
, reinterpret_cast<const BYTE
*>(rContext
.m_pDerEncoded
), rContext
.m_nDerEncoded
);
6911 if (pCertContext
== nullptr)
6913 SAL_WARN("vcl.pdfwriter", "CertCreateCertificateContext failed: " << WindowsErrorString(GetLastError()));
6917 CRYPT_SIGN_MESSAGE_PARA aPara
;
6919 memset(&aPara
, 0, sizeof(aPara
));
6920 aPara
.cbSize
= sizeof(aPara
);
6921 aPara
.dwMsgEncodingType
= PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
;
6922 aPara
.pSigningCert
= pCertContext
;
6923 aPara
.HashAlgorithm
.pszObjId
= const_cast<LPSTR
>(szOID_NIST_sha256
);
6924 aPara
.HashAlgorithm
.Parameters
.cbData
= 0;
6926 aPara
.rgpMsgCert
= &pCertContext
;
6928 HCRYPTPROV hCryptProv
;
6932 if (!CryptAcquireCertificatePrivateKey(pCertContext
,
6933 CRYPT_ACQUIRE_CACHE_FLAG
,
6939 SAL_WARN("vcl.pdfwriter", "CryptAcquireCertificatePrivateKey failed: " << WindowsErrorString(GetLastError()));
6940 CertFreeCertificateContext(pCertContext
);
6943 assert(!bFreeNeeded
);
6945 CMSG_SIGNER_ENCODE_INFO aSignerInfo
;
6947 memset(&aSignerInfo
, 0, sizeof(aSignerInfo
));
6948 aSignerInfo
.cbSize
= sizeof(aSignerInfo
);
6949 aSignerInfo
.pCertInfo
= pCertContext
->pCertInfo
;
6950 aSignerInfo
.hCryptProv
= hCryptProv
;
6951 aSignerInfo
.dwKeySpec
= nKeySpec
;
6952 aSignerInfo
.HashAlgorithm
.pszObjId
= const_cast<LPSTR
>(szOID_NIST_sha256
);
6953 aSignerInfo
.HashAlgorithm
.Parameters
.cbData
= 0;
6955 // Add the signing certificate as a signed attribute.
6956 CRYPT_INTEGER_BLOB aCertificateBlob
;
6957 SvMemoryStream aEncodedCertificate
;
6958 if (!CreateSigningCertificateAttribute(rContext
, pCertContext
, aEncodedCertificate
))
6960 SAL_WARN("vcl.pdfwriter", "CreateSigningCertificateAttribute() failed");
6963 aCertificateBlob
.pbData
= const_cast<BYTE
*>(static_cast<const BYTE
*>(aEncodedCertificate
.GetData()));
6964 aCertificateBlob
.cbData
= aEncodedCertificate
.GetSize();
6965 CRYPT_ATTRIBUTE aCertificateAttribute
;
6967 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
6968 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
6969 * smime(16) id-aa(2) 47 }
6971 aCertificateAttribute
.pszObjId
= const_cast<LPSTR
>("1.2.840.113549.1.9.16.2.47");
6972 aCertificateAttribute
.cValue
= 1;
6973 aCertificateAttribute
.rgValue
= &aCertificateBlob
;
6974 aSignerInfo
.cAuthAttr
= 1;
6975 aSignerInfo
.rgAuthAttr
= &aCertificateAttribute
;
6977 CMSG_SIGNED_ENCODE_INFO aSignedInfo
;
6978 memset(&aSignedInfo
, 0, sizeof(aSignedInfo
));
6979 aSignedInfo
.cbSize
= sizeof(aSignedInfo
);
6980 aSignedInfo
.cSigners
= 1;
6981 aSignedInfo
.rgSigners
= &aSignerInfo
;
6983 CERT_BLOB aCertBlob
;
6985 aCertBlob
.cbData
= pCertContext
->cbCertEncoded
;
6986 aCertBlob
.pbData
= pCertContext
->pbCertEncoded
;
6988 aSignedInfo
.cCertEncoded
= 1;
6989 aSignedInfo
.rgCertEncoded
= &aCertBlob
;
6991 HCRYPTMSG hMsg
= CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
6999 SAL_WARN("vcl.pdfwriter", "CryptMsgOpenToEncode failed: " << WindowsErrorString(GetLastError()));
7000 CertFreeCertificateContext(pCertContext
);
7004 if (!CryptMsgUpdate(hMsg
, static_cast<const BYTE
*>(rContext
.m_pByteRange1
), rContext
.m_nByteRange1
, FALSE
) ||
7005 !CryptMsgUpdate(hMsg
, static_cast<const BYTE
*>(rContext
.m_pByteRange2
), rContext
.m_nByteRange2
, TRUE
))
7007 SAL_WARN("vcl.pdfwriter", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
7008 CryptMsgClose(hMsg
);
7009 CertFreeCertificateContext(pCertContext
);
7013 PCRYPT_TIMESTAMP_CONTEXT pTsContext
= nullptr;
7015 if( !rContext
.m_aSignTSA
.isEmpty() )
7017 PointerTo_CryptRetrieveTimeStamp crts
= reinterpret_cast<PointerTo_CryptRetrieveTimeStamp
>(GetProcAddress(LoadLibrary("crypt32.dll"), "CryptRetrieveTimeStamp"));
7020 SAL_WARN("vcl.pdfwriter", "Could not find the CryptRetrieveTimeStamp function in crypt32.dll: " << WindowsErrorString(GetLastError()));
7021 CryptMsgClose(hMsg
);
7022 CertFreeCertificateContext(pCertContext
);
7026 HCRYPTMSG hDecodedMsg
= CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
7034 SAL_WARN("vcl.pdfwriter", "CryptMsgOpenToDecode failed: " << WindowsErrorString(GetLastError()));
7035 CryptMsgClose(hMsg
);
7036 CertFreeCertificateContext(pCertContext
);
7040 DWORD nTsSigLen
= 0;
7042 if (!CryptMsgGetParam(hMsg
, CMSG_BARE_CONTENT_PARAM
, 0, nullptr, &nTsSigLen
))
7044 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
7045 CryptMsgClose(hDecodedMsg
);
7046 CryptMsgClose(hMsg
);
7047 CertFreeCertificateContext(pCertContext
);
7051 SAL_INFO("vcl.pdfwriter", "nTsSigLen=" << nTsSigLen
);
7053 std::unique_ptr
<BYTE
[]> pTsSig(new BYTE
[nTsSigLen
]);
7055 if (!CryptMsgGetParam(hMsg
, CMSG_BARE_CONTENT_PARAM
, 0, pTsSig
.get(), &nTsSigLen
))
7057 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
7058 CryptMsgClose(hDecodedMsg
);
7059 CryptMsgClose(hMsg
);
7060 CertFreeCertificateContext(pCertContext
);
7064 if (!CryptMsgUpdate(hDecodedMsg
, pTsSig
.get(), nTsSigLen
, TRUE
))
7066 SAL_WARN("vcl.pdfwriter", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
7067 CryptMsgClose(hDecodedMsg
);
7068 CryptMsgClose(hMsg
);
7069 CertFreeCertificateContext(pCertContext
);
7073 DWORD nDecodedSignerInfoLen
= 0;
7074 if (!CryptMsgGetParam(hDecodedMsg
, CMSG_SIGNER_INFO_PARAM
, 0, nullptr, &nDecodedSignerInfoLen
))
7076 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsErrorString(GetLastError()));
7077 CryptMsgClose(hDecodedMsg
);
7078 CryptMsgClose(hMsg
);
7079 CertFreeCertificateContext(pCertContext
);
7083 std::unique_ptr
<BYTE
[]> pDecodedSignerInfoBuf(new BYTE
[nDecodedSignerInfoLen
]);
7085 if (!CryptMsgGetParam(hDecodedMsg
, CMSG_SIGNER_INFO_PARAM
, 0, pDecodedSignerInfoBuf
.get(), &nDecodedSignerInfoLen
))
7087 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsErrorString(GetLastError()));
7088 CryptMsgClose(hDecodedMsg
);
7089 CryptMsgClose(hMsg
);
7090 CertFreeCertificateContext(pCertContext
);
7094 CMSG_SIGNER_INFO
*pDecodedSignerInfo
= reinterpret_cast<CMSG_SIGNER_INFO
*>(pDecodedSignerInfoBuf
.get());
7096 CRYPT_TIMESTAMP_PARA aTsPara
;
7097 unsigned int nNonce
= comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32
);
7099 aTsPara
.pszTSAPolicyId
= nullptr;
7100 aTsPara
.fRequestCerts
= TRUE
;
7101 aTsPara
.Nonce
.cbData
= sizeof(nNonce
);
7102 aTsPara
.Nonce
.pbData
= reinterpret_cast<BYTE
*>(&nNonce
);
7103 aTsPara
.cExtension
= 0;
7104 aTsPara
.rgExtension
= nullptr;
7106 if (!(*crts
)(SAL_W(rContext
.m_aSignTSA
.getStr()),
7111 pDecodedSignerInfo
->EncryptedHash
.pbData
,
7112 pDecodedSignerInfo
->EncryptedHash
.cbData
,
7117 SAL_WARN("vcl.pdfwriter", "CryptRetrieveTimeStamp failed: " << WindowsErrorString(GetLastError()));
7118 CryptMsgClose(hDecodedMsg
);
7119 CryptMsgClose(hMsg
);
7120 CertFreeCertificateContext(pCertContext
);
7124 SAL_INFO("vcl.pdfwriter", "Time stamp size is " << pTsContext
->cbEncoded
<< " bytes");
7128 FILE *out
= fopen("PDFWRITER.tstoken.data", "wb");
7129 fwrite(pTsContext
->pbEncoded
, pTsContext
->cbEncoded
, 1, out
);
7134 // I tried to use CryptMsgControl() with CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR to add the
7135 // timestamp, but that failed with "The parameter is incorrect". Probably it is too late to
7136 // modify the message once its data has already been encoded as part of the
7137 // CryptMsgGetParam() with CMSG_BARE_CONTENT_PARAM above. So close the message and re-do its
7138 // creation steps, but now with an amended aSignerInfo.
7140 CRYPT_INTEGER_BLOB aTimestampBlob
;
7141 aTimestampBlob
.cbData
= pTsContext
->cbEncoded
;
7142 aTimestampBlob
.pbData
= pTsContext
->pbEncoded
;
7144 CRYPT_ATTRIBUTE aTimestampAttribute
;
7145 aTimestampAttribute
.pszObjId
= const_cast<LPSTR
>(
7146 "1.2.840.113549.1.9.16.2.14");
7147 aTimestampAttribute
.cValue
= 1;
7148 aTimestampAttribute
.rgValue
= &aTimestampBlob
;
7150 aSignerInfo
.cUnauthAttr
= 1;
7151 aSignerInfo
.rgUnauthAttr
= &aTimestampAttribute
;
7153 CryptMsgClose(hMsg
);
7155 hMsg
= CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING
| X509_ASN_ENCODING
,
7162 !CryptMsgUpdate(hMsg
, static_cast<const BYTE
*>(rContext
.m_pByteRange1
), rContext
.m_nByteRange1
, FALSE
) ||
7163 !CryptMsgUpdate(hMsg
, static_cast<const BYTE
*>(rContext
.m_pByteRange1
), rContext
.m_nByteRange2
, TRUE
))
7165 SAL_WARN("vcl.pdfwriter", "Re-creating the message failed: " << WindowsErrorString(GetLastError()));
7166 CryptMemFree(pTsContext
);
7167 CryptMsgClose(hDecodedMsg
);
7168 CryptMsgClose(hMsg
);
7169 CertFreeCertificateContext(pCertContext
);
7173 CryptMsgClose(hDecodedMsg
);
7178 if (!CryptMsgGetParam(hMsg
, CMSG_CONTENT_PARAM
, 0, nullptr, &nSigLen
))
7180 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
7182 CryptMemFree(pTsContext
);
7183 CryptMsgClose(hMsg
);
7184 CertFreeCertificateContext(pCertContext
);
7188 if (nSigLen
*2 > MAX_SIGNATURE_CONTENT_LENGTH
)
7190 SAL_WARN("vcl.pdfwriter", "Signature requires more space (" << nSigLen
*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH
<< ")");
7192 CryptMemFree(pTsContext
);
7193 CryptMsgClose(hMsg
);
7194 CertFreeCertificateContext(pCertContext
);
7198 SAL_INFO("vcl.pdfwriter", "Signature size is " << nSigLen
<< " bytes");
7199 std::unique_ptr
<BYTE
[]> pSig(new BYTE
[nSigLen
]);
7201 if (!CryptMsgGetParam(hMsg
, CMSG_CONTENT_PARAM
, 0, pSig
.get(), &nSigLen
))
7203 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
7205 CryptMemFree(pTsContext
);
7206 CryptMsgClose(hMsg
);
7207 CertFreeCertificateContext(pCertContext
);
7213 FILE *out
= fopen("PDFWRITER.signature.data", "wb");
7214 fwrite(pSig
.get(), nSigLen
, 1, out
);
7219 // Release resources
7221 CryptMemFree(pTsContext
);
7222 CryptMsgClose(hMsg
);
7223 CertFreeCertificateContext(pCertContext
);
7225 for (unsigned int i
= 0; i
< nSigLen
; i
++)
7226 appendHex(pSig
[i
], rContext
.m_rCMSHexBuffer
);
7232 bool PDFWriterImpl::finalizeSignature()
7235 if (!m_aContext
.SignCertificate
.is())
7238 // 1- calculate last ByteRange value
7239 sal_uInt64 nOffset
= ~0U;
7240 CHECK_RETURN( (osl::File::E_None
== m_aFile
.getPos(nOffset
) ) );
7242 sal_Int64 nLastByteRangeNo
= nOffset
- (m_nSignatureContentOffset
+ MAX_SIGNATURE_CONTENT_LENGTH
+ 1);
7244 // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position
7245 sal_uInt64 nWritten
= 0;
7246 CHECK_RETURN( (osl::File::E_None
== m_aFile
.setPos(osl_Pos_Absolut
, m_nSignatureLastByteRangeNoOffset
) ) );
7247 OStringBuffer
aByteRangeNo( 256 );
7248 aByteRangeNo
.append( nLastByteRangeNo
);
7249 aByteRangeNo
.append( " ]" );
7251 if (m_aFile
.write(aByteRangeNo
.getStr(), aByteRangeNo
.getLength(), nWritten
) != osl::File::E_None
)
7253 CHECK_RETURN( (osl::File::E_None
== m_aFile
.setPos(osl_Pos_Absolut
, nOffset
)) );
7257 // 3- create the PKCS#7 object using NSS
7258 css::uno::Sequence
< sal_Int8
> derEncoded
= m_aContext
.SignCertificate
->getEncoded();
7260 if (!derEncoded
.hasElements())
7263 sal_Int8
* n_derArray
= derEncoded
.getArray();
7264 sal_Int32 n_derLength
= derEncoded
.getLength();
7268 // Prepare buffer and calculate PDF file digest
7269 CHECK_RETURN( (osl::File::E_None
== m_aFile
.setPos(osl_Pos_Absolut
, 0)) );
7271 std::unique_ptr
<char[]> buffer1(new char[m_nSignatureContentOffset
+ 1]);
7272 sal_uInt64 bytesRead1
;
7274 //FIXME: Check if hash is calculated from the correct byterange
7275 CHECK_RETURN( (osl::File::E_None
== m_aFile
.read(buffer1
.get(), m_nSignatureContentOffset
- 1 , bytesRead1
)) );
7276 if (bytesRead1
!= (sal_uInt64
)m_nSignatureContentOffset
- 1)
7277 SAL_WARN("vcl.pdfwriter", "First buffer read failed");
7279 CHECK_RETURN( (osl::File::E_None
== m_aFile
.setPos(osl_Pos_Absolut
, m_nSignatureContentOffset
+ MAX_SIGNATURE_CONTENT_LENGTH
+ 1)) );
7280 std::unique_ptr
<char[]> buffer2(new char[nLastByteRangeNo
+ 1]);
7281 sal_uInt64 bytesRead2
;
7282 CHECK_RETURN( (osl::File::E_None
== m_aFile
.read(buffer2
.get(), nLastByteRangeNo
, bytesRead2
)) );
7283 if (bytesRead2
!= (sal_uInt64
) nLastByteRangeNo
)
7284 SAL_WARN("vcl.pdfwriter", "Second buffer read failed");
7286 OStringBuffer cms_hexbuffer
;
7287 PDFWriter::PDFSignContext
aSignContext(cms_hexbuffer
);
7288 aSignContext
.m_pDerEncoded
= n_derArray
;
7289 aSignContext
.m_nDerEncoded
= n_derLength
;
7290 aSignContext
.m_pByteRange1
= buffer1
.get();
7291 aSignContext
.m_nByteRange1
= bytesRead1
;
7292 aSignContext
.m_pByteRange2
= buffer2
.get();
7293 aSignContext
.m_nByteRange2
= bytesRead2
;
7294 aSignContext
.m_aSignTSA
= m_aContext
.SignTSA
;
7295 aSignContext
.m_aSignPassword
= m_aContext
.SignPassword
;
7296 if (!PDFWriter::Sign(aSignContext
))
7298 SAL_WARN("vcl.pdfwriter", "PDFWriter::Sign() failed");
7302 assert(cms_hexbuffer
.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH
);
7304 // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
7306 CHECK_RETURN( (osl::File::E_None
== m_aFile
.setPos(osl_Pos_Absolut
, m_nSignatureContentOffset
)) );
7307 m_aFile
.write(cms_hexbuffer
.getStr(), cms_hexbuffer
.getLength(), nWritten
);
7309 CHECK_RETURN( (osl::File::E_None
== m_aFile
.setPos(osl_Pos_Absolut
, nOffset
)) );
7314 // Prepare buffer and calculate PDF file digest
7315 CHECK_RETURN( (osl::File::E_None
== m_aFile
.setPos(osl_Pos_Absolut
, 0)) );
7317 std::unique_ptr
<char[]> buffer1(new char[m_nSignatureContentOffset
- 1]);
7318 sal_uInt64 bytesRead1
;
7320 if (osl::File::E_None
!= m_aFile
.read(buffer1
.get(), m_nSignatureContentOffset
- 1 , bytesRead1
) ||
7321 bytesRead1
!= (sal_uInt64
)m_nSignatureContentOffset
- 1)
7323 SAL_WARN("vcl.pdfwriter", "First buffer read failed");
7327 std::unique_ptr
<char[]> buffer2(new char[nLastByteRangeNo
]);
7328 sal_uInt64 bytesRead2
;
7330 if (osl::File::E_None
!= m_aFile
.setPos(osl_Pos_Absolut
, m_nSignatureContentOffset
+ MAX_SIGNATURE_CONTENT_LENGTH
+ 1) ||
7331 osl::File::E_None
!= m_aFile
.read(buffer2
.get(), nLastByteRangeNo
, bytesRead2
) ||
7332 bytesRead2
!= (sal_uInt64
) nLastByteRangeNo
)
7334 SAL_WARN("vcl.pdfwriter", "Second buffer read failed");
7338 OStringBuffer cms_hexbuffer
;
7339 PDFWriter::PDFSignContext
aSignContext(cms_hexbuffer
);
7340 aSignContext
.m_pDerEncoded
= n_derArray
;
7341 aSignContext
.m_nDerEncoded
= n_derLength
;
7342 aSignContext
.m_pByteRange1
= buffer1
.get();
7343 aSignContext
.m_nByteRange1
= bytesRead1
;
7344 aSignContext
.m_pByteRange2
= buffer2
.get();
7345 aSignContext
.m_nByteRange2
= bytesRead2
;
7346 aSignContext
.m_aSignTSA
= m_aContext
.SignTSA
;
7347 aSignContext
.m_aSignPassword
= m_aContext
.SignPassword
;
7348 if (!PDFWriter::Sign(aSignContext
))
7350 SAL_WARN("vcl.pdfwriter", "PDFWriter::Sign() failed");
7354 assert(cms_hexbuffer
.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH
);
7356 // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
7358 CHECK_RETURN( (osl::File::E_None
== m_aFile
.setPos(osl_Pos_Absolut
, m_nSignatureContentOffset
)) );
7359 m_aFile
.write(cms_hexbuffer
.getStr(), cms_hexbuffer
.getLength(), nWritten
);
7361 CHECK_RETURN( (osl::File::E_None
== m_aFile
.setPos(osl_Pos_Absolut
, nOffset
)) );
7367 #else // !HAVE_FEATURE_NSS
7368 bool PDFWriter::Sign(PDFSignContext
& /*rContext*/)
7375 sal_Int32
PDFWriterImpl::emitInfoDict( )
7377 sal_Int32 nObject
= createObject();
7379 if( updateObject( nObject
) )
7381 OStringBuffer
aLine( 1024 );
7382 aLine
.append( nObject
);
7383 aLine
.append( " 0 obj\n"
7385 if( !m_aContext
.DocumentInfo
.Title
.isEmpty() )
7387 aLine
.append( "/Title" );
7388 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Title
, nObject
, aLine
);
7389 aLine
.append( "\n" );
7391 if( !m_aContext
.DocumentInfo
.Author
.isEmpty() )
7393 aLine
.append( "/Author" );
7394 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Author
, nObject
, aLine
);
7395 aLine
.append( "\n" );
7397 if( !m_aContext
.DocumentInfo
.Subject
.isEmpty() )
7399 aLine
.append( "/Subject" );
7400 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Subject
, nObject
, aLine
);
7401 aLine
.append( "\n" );
7403 if( !m_aContext
.DocumentInfo
.Keywords
.isEmpty() )
7405 aLine
.append( "/Keywords" );
7406 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Keywords
, nObject
, aLine
);
7407 aLine
.append( "\n" );
7409 if( !m_aContext
.DocumentInfo
.Creator
.isEmpty() )
7411 aLine
.append( "/Creator" );
7412 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Creator
, nObject
, aLine
);
7413 aLine
.append( "\n" );
7415 if( !m_aContext
.DocumentInfo
.Producer
.isEmpty() )
7417 aLine
.append( "/Producer" );
7418 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Producer
, nObject
, aLine
);
7419 aLine
.append( "\n" );
7422 aLine
.append( "/CreationDate" );
7423 appendLiteralStringEncrypt( m_aCreationDateString
, nObject
, aLine
);
7424 aLine
.append( ">>\nendobj\n\n" );
7425 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
7434 // Part of this function may be shared with method appendDest.
7435 sal_Int32
PDFWriterImpl::emitNamedDestinations()
7437 sal_Int32 nCount
= m_aNamedDests
.size();
7439 return 0;//define internal error
7441 //get the object number for all the destinations
7442 sal_Int32 nObject
= createObject();
7444 if( updateObject( nObject
) )
7446 //emit the dictionary
7447 OStringBuffer
aLine( 1024 );
7448 aLine
.append( nObject
);
7449 aLine
.append( " 0 obj\n"
7453 for( nDestID
= 0; nDestID
< nCount
; nDestID
++ )
7455 const PDFNamedDest
& rDest
= m_aNamedDests
[ nDestID
];
7456 // In order to correctly function both under an Internet browser and
7457 // directly with a reader (provided the reader has the feature) we
7458 // need to set the name of the destination the same way it will be encoded
7459 // in an Internet link
7460 INetURLObject
aLocalURL( "http://ahost.ax" ); //dummy location, won't be used
7461 aLocalURL
.SetMark( rDest
.m_aDestName
);
7463 const OUString aName
= aLocalURL
.GetMark( INetURLObject::DecodeMechanism::NONE
); //same coding as
7464 // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
7465 const PDFPage
& rDestPage
= m_aPages
[ rDest
.m_nPage
];
7467 aLine
.append( '/' );
7468 appendDestinationName( aName
, aLine
); // this conversion must be done when forming the link to target ( see in emitCatalog )
7469 aLine
.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
7470 //maps the preceding character properly
7471 aLine
.append( rDestPage
.m_nPageObject
);
7472 aLine
.append( " 0 R" );
7474 switch( rDest
.m_eType
)
7476 case PDFWriter::DestAreaType::XYZ
:
7478 aLine
.append( "/XYZ " );
7479 appendFixedInt( rDest
.m_aRect
.Left(), aLine
);
7480 aLine
.append( ' ' );
7481 appendFixedInt( rDest
.m_aRect
.Bottom(), aLine
);
7482 aLine
.append( " 0" );
7484 case PDFWriter::DestAreaType::FitRectangle
:
7485 aLine
.append( "/FitR " );
7486 appendFixedInt( rDest
.m_aRect
.Left(), aLine
);
7487 aLine
.append( ' ' );
7488 appendFixedInt( rDest
.m_aRect
.Top(), aLine
);
7489 aLine
.append( ' ' );
7490 appendFixedInt( rDest
.m_aRect
.Right(), aLine
);
7491 aLine
.append( ' ' );
7492 appendFixedInt( rDest
.m_aRect
.Bottom(), aLine
);
7495 aLine
.append( "]\n" );
7499 aLine
.append( ">>\nendobj\n\n" );
7500 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
7509 // emits the output intent dictionary
7510 sal_Int32
PDFWriterImpl::emitOutputIntent()
7515 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
7517 OStringBuffer
aLine( 1024 );
7518 sal_Int32 nICCObject
= createObject();
7519 sal_Int32 nStreamLengthObject
= createObject();
7521 aLine
.append( nICCObject
);
7522 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
7523 aLine
.append( " 0 obj\n<</N 3/Length " );
7524 aLine
.append( nStreamLengthObject
);
7525 aLine
.append( " 0 R" );
7526 if (!g_bDebugDisableCompression
)
7527 aLine
.append( "/Filter/FlateDecode" );
7528 aLine
.append( ">>\nstream\n" );
7529 if ( !updateObject( nICCObject
) ) return 0;
7530 if ( !writeBuffer( aLine
.getStr(), aLine
.getLength() ) ) return 0;
7532 sal_uInt64 nBeginStreamPos
= 0;
7533 m_aFile
.getPos(nBeginStreamPos
);
7535 checkAndEnableStreamEncryption( nICCObject
);
7536 cmsHPROFILE hProfile
= cmsCreate_sRGBProfile();
7537 //force ICC profile version 2.1
7538 cmsSetProfileVersion(hProfile
, 2.1);
7539 cmsUInt32Number nBytesNeeded
= 0;
7540 cmsSaveProfileToMem(hProfile
, nullptr, &nBytesNeeded
);
7543 std::vector
<unsigned char> aBuffer(nBytesNeeded
);
7544 cmsSaveProfileToMem(hProfile
, &aBuffer
[0], &nBytesNeeded
);
7545 cmsCloseProfile(hProfile
);
7546 bool written
= writeBuffer( &aBuffer
[0], (sal_Int32
) aBuffer
.size() );
7547 disableStreamEncryption();
7549 sal_uInt64 nEndStreamPos
= 0;
7550 m_aFile
.getPos(nEndStreamPos
);
7554 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
7556 aLine
.setLength( 0 );
7558 //emit the stream length object
7559 if ( !updateObject( nStreamLengthObject
) ) return 0;
7560 aLine
.setLength( 0 );
7561 aLine
.append( nStreamLengthObject
);
7562 aLine
.append( " 0 obj\n" );
7563 aLine
.append( (sal_Int64
)(nEndStreamPos
-nBeginStreamPos
) );
7564 aLine
.append( "\nendobj\n\n" );
7565 if ( !writeBuffer( aLine
.getStr(), aLine
.getLength() ) ) return 0;
7566 aLine
.setLength( 0 );
7568 //emit the OutputIntent dictionary
7569 sal_Int32 nOIObject
= createObject();
7570 if ( !updateObject( nOIObject
) ) return 0;
7571 aLine
.append( nOIObject
);
7572 aLine
.append( " 0 obj\n"
7573 "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
7575 OUString
aComment( "sRGB IEC61966-2.1" );
7576 appendLiteralStringEncrypt( aComment
,nOIObject
, aLine
);
7577 aLine
.append("/DestOutputProfile ");
7578 aLine
.append( nICCObject
);
7579 aLine
.append( " 0 R>>\nendobj\n\n" );
7580 if ( !writeBuffer( aLine
.getStr(), aLine
.getLength() ) ) return 0;
7585 // formats the string for the XML stream
7586 static void escapeStringXML( const OUString
& rStr
, OUString
&rValue
)
7588 const sal_Unicode
* pUni
= rStr
.getStr();
7589 int nLen
= rStr
.getLength();
7590 for( ; nLen
; nLen
--, pUni
++ )
7610 rValue
+= OUStringLiteral1( *pUni
);
7616 // emits the document metadata
7617 sal_Int32
PDFWriterImpl::emitDocumentMetadata()
7622 //get the object number for all the destinations
7623 sal_Int32 nObject
= createObject();
7625 if( updateObject( nObject
) )
7627 // the following string are written in UTF-8 unicode
7628 OStringBuffer
aMetadataStream( 8192 );
7630 aMetadataStream
.append( "<?xpacket begin=\"" );
7631 // these lines write Unicode "zero width non-breaking space character" (U+FEFF)
7632 // (aka byte-order mark ) used as a byte-order marker.
7633 aMetadataStream
.append( OUStringToOString( OUString( u
'\xFEFF' ), RTL_TEXTENCODING_UTF8
) );
7634 aMetadataStream
.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
7635 aMetadataStream
.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
7636 aMetadataStream
.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
7637 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
7638 aMetadataStream
.append( " <rdf:Description rdf:about=\"\"\n" );
7639 aMetadataStream
.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
7640 aMetadataStream
.append( " <pdfaid:part>1</pdfaid:part>\n" );
7641 aMetadataStream
.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" );
7642 aMetadataStream
.append( " </rdf:Description>\n" );
7643 //... Dublin Core properties go here
7644 if( !m_aContext
.DocumentInfo
.Title
.isEmpty() ||
7645 !m_aContext
.DocumentInfo
.Author
.isEmpty() ||
7646 !m_aContext
.DocumentInfo
.Subject
.isEmpty() )
7648 aMetadataStream
.append( " <rdf:Description rdf:about=\"\"\n" );
7649 aMetadataStream
.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
7650 if( !m_aContext
.DocumentInfo
.Title
.isEmpty() )
7652 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
7653 aMetadataStream
.append( " <dc:title>\n" );
7654 aMetadataStream
.append( " <rdf:Alt>\n" );
7655 aMetadataStream
.append( " <rdf:li xml:lang=\"x-default\">" );
7657 escapeStringXML( m_aContext
.DocumentInfo
.Title
, aTitle
);
7658 aMetadataStream
.append( OUStringToOString( aTitle
, RTL_TEXTENCODING_UTF8
) );
7659 aMetadataStream
.append( "</rdf:li>\n" );
7660 aMetadataStream
.append( " </rdf:Alt>\n" );
7661 aMetadataStream
.append( " </dc:title>\n" );
7663 if( !m_aContext
.DocumentInfo
.Author
.isEmpty() )
7665 aMetadataStream
.append( " <dc:creator>\n" );
7666 aMetadataStream
.append( " <rdf:Seq>\n" );
7667 aMetadataStream
.append( " <rdf:li>" );
7669 escapeStringXML( m_aContext
.DocumentInfo
.Author
, aAuthor
);
7670 aMetadataStream
.append( OUStringToOString( aAuthor
, RTL_TEXTENCODING_UTF8
) );
7671 aMetadataStream
.append( "</rdf:li>\n" );
7672 aMetadataStream
.append( " </rdf:Seq>\n" );
7673 aMetadataStream
.append( " </dc:creator>\n" );
7675 if( !m_aContext
.DocumentInfo
.Subject
.isEmpty() )
7677 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
7678 aMetadataStream
.append( " <dc:description>\n" );
7679 aMetadataStream
.append( " <rdf:Alt>\n" );
7680 aMetadataStream
.append( " <rdf:li xml:lang=\"x-default\">" );
7682 escapeStringXML( m_aContext
.DocumentInfo
.Subject
, aSubject
);
7683 aMetadataStream
.append( OUStringToOString( aSubject
, RTL_TEXTENCODING_UTF8
) );
7684 aMetadataStream
.append( "</rdf:li>\n" );
7685 aMetadataStream
.append( " </rdf:Alt>\n" );
7686 aMetadataStream
.append( " </dc:description>\n" );
7688 aMetadataStream
.append( " </rdf:Description>\n" );
7691 //... PDF properties go here
7692 if( !m_aContext
.DocumentInfo
.Producer
.isEmpty() ||
7693 !m_aContext
.DocumentInfo
.Keywords
.isEmpty() )
7695 aMetadataStream
.append( " <rdf:Description rdf:about=\"\"\n" );
7696 aMetadataStream
.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
7697 if( !m_aContext
.DocumentInfo
.Producer
.isEmpty() )
7699 aMetadataStream
.append( " <pdf:Producer>" );
7701 escapeStringXML( m_aContext
.DocumentInfo
.Producer
, aProducer
);
7702 aMetadataStream
.append( OUStringToOString( aProducer
, RTL_TEXTENCODING_UTF8
) );
7703 aMetadataStream
.append( "</pdf:Producer>\n" );
7705 if( !m_aContext
.DocumentInfo
.Keywords
.isEmpty() )
7707 aMetadataStream
.append( " <pdf:Keywords>" );
7709 escapeStringXML( m_aContext
.DocumentInfo
.Keywords
, aKeywords
);
7710 aMetadataStream
.append( OUStringToOString( aKeywords
, RTL_TEXTENCODING_UTF8
) );
7711 aMetadataStream
.append( "</pdf:Keywords>\n" );
7713 aMetadataStream
.append( " </rdf:Description>\n" );
7716 aMetadataStream
.append( " <rdf:Description rdf:about=\"\"\n" );
7717 aMetadataStream
.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
7718 if( !m_aContext
.DocumentInfo
.Creator
.isEmpty() )
7720 aMetadataStream
.append( " <xmp:CreatorTool>" );
7722 escapeStringXML( m_aContext
.DocumentInfo
.Creator
, aCreator
);
7723 aMetadataStream
.append( OUStringToOString( aCreator
, RTL_TEXTENCODING_UTF8
) );
7724 aMetadataStream
.append( "</xmp:CreatorTool>\n" );
7727 aMetadataStream
.append( " <xmp:CreateDate>" );
7728 aMetadataStream
.append( m_aCreationMetaDateString
);
7729 aMetadataStream
.append( "</xmp:CreateDate>\n" );
7731 aMetadataStream
.append( " </rdf:Description>\n" );
7732 aMetadataStream
.append( " </rdf:RDF>\n" );
7733 aMetadataStream
.append( "</x:xmpmeta>\n" );
7736 for( sal_Int32 nSpaces
= 1; nSpaces
<= 2100; nSpaces
++ )
7738 aMetadataStream
.append( " " );
7739 if( nSpaces
% 100 == 0 )
7740 aMetadataStream
.append( "\n" );
7743 aMetadataStream
.append( "<?xpacket end=\"w\"?>\n" );
7745 OStringBuffer
aMetadataObj( 1024 );
7747 aMetadataObj
.append( nObject
);
7748 aMetadataObj
.append( " 0 obj\n" );
7750 aMetadataObj
.append( "<</Type/Metadata/Subtype/XML/Length " );
7752 aMetadataObj
.append( aMetadataStream
.getLength() );
7753 aMetadataObj
.append( ">>\nstream\n" );
7754 if ( !writeBuffer( aMetadataObj
.getStr(), aMetadataObj
.getLength() ) )
7757 if ( !writeBuffer( aMetadataStream
.getStr(), aMetadataStream
.getLength() ) )
7760 aMetadataObj
.setLength( 0 );
7761 aMetadataObj
.append( "\nendstream\nendobj\n\n" );
7762 if( ! writeBuffer( aMetadataObj
.getStr(), aMetadataObj
.getLength() ) )
7771 bool PDFWriterImpl::emitTrailer()
7774 sal_Int32 nDocInfoObject
= emitInfoDict( );
7776 sal_Int32 nSecObject
= 0;
7778 if( m_aContext
.Encryption
.Encrypt() )
7780 //emit the security information
7781 //must be emitted as indirect dictionary object, since
7782 //Acrobat Reader 5 works only with this kind of implementation
7783 nSecObject
= createObject();
7785 if( updateObject( nSecObject
) )
7787 OStringBuffer
aLineS( 1024 );
7788 aLineS
.append( nSecObject
);
7789 aLineS
.append( " 0 obj\n"
7790 "<</Filter/Standard/V " );
7791 // check the version
7792 aLineS
.append( "2/Length 128/R 3" );
7794 // emit the owner password, must not be encrypted
7795 aLineS
.append( "/O(" );
7796 appendLiteralString( reinterpret_cast<char*>(&m_aContext
.Encryption
.OValue
[0]), sal_Int32(m_aContext
.Encryption
.OValue
.size()), aLineS
);
7797 aLineS
.append( ")/U(" );
7798 appendLiteralString( reinterpret_cast<char*>(&m_aContext
.Encryption
.UValue
[0]), sal_Int32(m_aContext
.Encryption
.UValue
.size()), aLineS
);
7799 aLineS
.append( ")/P " );// the permission set
7800 aLineS
.append( m_nAccessPermissions
);
7801 aLineS
.append( ">>\nendobj\n\n" );
7802 if( !writeBuffer( aLineS
.getStr(), aLineS
.getLength() ) )
7810 sal_uInt64 nXRefOffset
= 0;
7811 CHECK_RETURN( (osl::File::E_None
== m_aFile
.getPos(nXRefOffset
)) );
7812 CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
7814 sal_Int32 nObjects
= m_aObjects
.size();
7815 OStringBuffer aLine
;
7816 aLine
.append( "0 " );
7817 aLine
.append( (sal_Int32
)(nObjects
+1) );
7818 aLine
.append( "\n" );
7819 aLine
.append( "0000000000 65535 f \n" );
7820 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
7822 for( sal_Int32 i
= 0; i
< nObjects
; i
++ )
7824 aLine
.setLength( 0 );
7825 OString aOffset
= OString::number( m_aObjects
[i
] );
7826 for( sal_Int32 j
= 0; j
< (10-aOffset
.getLength()); j
++ )
7827 aLine
.append( '0' );
7828 aLine
.append( aOffset
);
7829 aLine
.append( " 00000 n \n" );
7830 SAL_WARN_IF( aLine
.getLength() != 20, "vcl.pdfwriter", "invalid xref entry" );
7831 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
7834 // prepare document checksum
7835 OStringBuffer
aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5
+1 );
7838 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
7839 rtl_digest_getMD5( m_aDocDigest
, nMD5Sum
, sizeof(nMD5Sum
) );
7840 for(sal_uInt8 i
: nMD5Sum
)
7841 appendHex( i
, aDocChecksum
);
7843 // document id set in setDocInfo method
7845 aLine
.setLength( 0 );
7846 aLine
.append( "trailer\n"
7848 aLine
.append( (sal_Int32
)(nObjects
+1) );
7849 aLine
.append( "/Root " );
7850 aLine
.append( m_nCatalogObject
);
7851 aLine
.append( " 0 R\n" );
7854 aLine
.append( "/Encrypt ");
7855 aLine
.append( nSecObject
);
7856 aLine
.append( " 0 R\n" );
7858 if( nDocInfoObject
)
7860 aLine
.append( "/Info " );
7861 aLine
.append( nDocInfoObject
);
7862 aLine
.append( " 0 R\n" );
7864 if( ! m_aContext
.Encryption
.DocumentIdentifier
.empty() )
7866 aLine
.append( "/ID [ <" );
7867 for( std::vector
< sal_uInt8
>::const_iterator it
= m_aContext
.Encryption
.DocumentIdentifier
.begin();
7868 it
!= m_aContext
.Encryption
.DocumentIdentifier
.end(); ++it
)
7870 appendHex( sal_Int8(*it
), aLine
);
7874 for( std::vector
< sal_uInt8
>::const_iterator it
= m_aContext
.Encryption
.DocumentIdentifier
.begin();
7875 it
!= m_aContext
.Encryption
.DocumentIdentifier
.end(); ++it
)
7877 appendHex( sal_Int8(*it
), aLine
);
7879 aLine
.append( "> ]\n" );
7881 if( !aDocChecksum
.isEmpty() )
7883 aLine
.append( "/DocChecksum /" );
7884 aLine
.append( aDocChecksum
.makeStringAndClear() );
7885 aLine
.append( "\n" );
7887 if( m_aAdditionalStreams
.size() > 0 )
7889 aLine
.append( "/AdditionalStreams [" );
7890 for(const PDFAddStream
& rAdditionalStream
: m_aAdditionalStreams
)
7892 aLine
.append( "/" );
7893 appendName( rAdditionalStream
.m_aMimeType
, aLine
);
7894 aLine
.append( " " );
7895 aLine
.append( rAdditionalStream
.m_nStreamObject
);
7896 aLine
.append( " 0 R\n" );
7898 aLine
.append( "]\n" );
7900 aLine
.append( ">>\n"
7902 aLine
.append( (sal_Int64
)nXRefOffset
);
7905 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
7910 struct AnnotationSortEntry
7912 sal_Int32 nTabOrder
;
7914 sal_Int32 nWidgetIndex
;
7916 AnnotationSortEntry( sal_Int32 nTab
, sal_Int32 nObj
, sal_Int32 nI
) :
7923 struct AnnotSortContainer
7925 std::set
< sal_Int32
> aObjects
;
7926 std::vector
< AnnotationSortEntry
> aSortedAnnots
;
7929 struct AnnotSorterLess
7931 std::vector
< PDFWriterImpl::PDFWidget
>& m_rWidgets
;
7933 explicit AnnotSorterLess( std::vector
< PDFWriterImpl::PDFWidget
>& rWidgets
) : m_rWidgets( rWidgets
) {}
7935 bool operator()( const AnnotationSortEntry
& rLeft
, const AnnotationSortEntry
& rRight
)
7937 if( rLeft
.nTabOrder
< rRight
.nTabOrder
)
7939 if( rRight
.nTabOrder
< rLeft
.nTabOrder
)
7941 if( rLeft
.nWidgetIndex
< 0 && rRight
.nWidgetIndex
< 0 )
7943 if( rRight
.nWidgetIndex
< 0 )
7945 if( rLeft
.nWidgetIndex
< 0 )
7947 // remember: widget rects are in PDF coordinates, so they are ordered down up
7948 if( m_rWidgets
[ rLeft
.nWidgetIndex
].m_aRect
.Top() >
7949 m_rWidgets
[ rRight
.nWidgetIndex
].m_aRect
.Top() )
7951 if( m_rWidgets
[ rRight
.nWidgetIndex
].m_aRect
.Top() >
7952 m_rWidgets
[ rLeft
.nWidgetIndex
].m_aRect
.Top() )
7954 if( m_rWidgets
[ rLeft
.nWidgetIndex
].m_aRect
.Left() <
7955 m_rWidgets
[ rRight
.nWidgetIndex
].m_aRect
.Left() )
7961 void PDFWriterImpl::sortWidgets()
7963 // sort widget annotations on each page as per their
7964 // TabOrder attribute
7965 std::unordered_map
< sal_Int32
, AnnotSortContainer
> sorted
;
7966 int nWidgets
= m_aWidgets
.size();
7967 for( int nW
= 0; nW
< nWidgets
; nW
++ )
7969 const PDFWidget
& rWidget
= m_aWidgets
[nW
];
7970 if( rWidget
.m_nPage
>= 0 )
7972 AnnotSortContainer
& rCont
= sorted
[ rWidget
.m_nPage
];
7973 // optimize vector allocation
7974 if( rCont
.aSortedAnnots
.empty() )
7975 rCont
.aSortedAnnots
.reserve( m_aPages
[ rWidget
.m_nPage
].m_aAnnotations
.size() );
7976 // insert widget to tab sorter
7977 // RadioButtons are not page annotations, only their individual check boxes are
7978 if( rWidget
.m_eType
!= PDFWriter::RadioButton
)
7980 rCont
.aObjects
.insert( rWidget
.m_nObject
);
7981 rCont
.aSortedAnnots
.push_back( AnnotationSortEntry( rWidget
.m_nTabOrder
, rWidget
.m_nObject
, nW
) );
7985 for( std::unordered_map
< sal_Int32
, AnnotSortContainer
>::iterator it
= sorted
.begin(); it
!= sorted
.end(); ++it
)
7987 // append entries for non widget annotations
7988 PDFPage
& rPage
= m_aPages
[ it
->first
];
7989 unsigned int nAnnots
= rPage
.m_aAnnotations
.size();
7990 for( unsigned int nA
= 0; nA
< nAnnots
; nA
++ )
7991 if( it
->second
.aObjects
.find( rPage
.m_aAnnotations
[nA
] ) == it
->second
.aObjects
.end())
7992 it
->second
.aSortedAnnots
.push_back( AnnotationSortEntry( 10000, rPage
.m_aAnnotations
[nA
], -1 ) );
7994 AnnotSorterLess
aLess( m_aWidgets
);
7995 std::stable_sort( it
->second
.aSortedAnnots
.begin(), it
->second
.aSortedAnnots
.end(), aLess
);
7997 if( it
->second
.aSortedAnnots
.size() == nAnnots
)
7999 for( unsigned int nA
= 0; nA
< nAnnots
; nA
++ )
8000 rPage
.m_aAnnotations
[nA
] = it
->second
.aSortedAnnots
[nA
].nObject
;
8004 SAL_WARN( "vcl.pdfwriter", "wrong number of sorted annotations" );
8005 #if OSL_DEBUG_LEVEL > 0
8006 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions "
8007 "on page nr " << (long int)it
->first
<< ", " <<
8008 (long int)it
->second
.aSortedAnnots
.size() << " sorted and " <<
8009 (long int)nAnnots
<< " unsorted");
8014 // FIXME: implement tab order in structure tree for PDF 1.5
8019 public cppu::WeakImplHelper
< css::io::XOutputStream
>
8021 PDFWriterImpl
* m_pWriter
;
8024 explicit PDFStreamIf( PDFWriterImpl
* pWriter
) : m_pWriter( pWriter
), m_bWrite( true ) {}
8026 virtual void SAL_CALL
writeBytes( const css::uno::Sequence
< sal_Int8
>& aData
) override
;
8027 virtual void SAL_CALL
flush() override
;
8028 virtual void SAL_CALL
closeOutput() override
;
8032 void SAL_CALL
PDFStreamIf::writeBytes( const css::uno::Sequence
< sal_Int8
>& aData
)
8034 if( m_bWrite
&& aData
.getLength() )
8036 sal_Int32 nBytes
= aData
.getLength();
8037 m_pWriter
->writeBuffer( aData
.getConstArray(), nBytes
);
8041 void SAL_CALL
PDFStreamIf::flush()
8045 void SAL_CALL
PDFStreamIf::closeOutput()
8050 bool PDFWriterImpl::emitAdditionalStreams()
8052 unsigned int nStreams
= m_aAdditionalStreams
.size();
8053 for( unsigned int i
= 0; i
< nStreams
; i
++ )
8055 PDFAddStream
& rStream
= m_aAdditionalStreams
[i
];
8056 rStream
.m_nStreamObject
= createObject();
8057 sal_Int32 nSizeObject
= createObject();
8059 if( ! updateObject( rStream
.m_nStreamObject
) )
8062 OStringBuffer aLine
;
8063 aLine
.append( rStream
.m_nStreamObject
);
8064 aLine
.append( " 0 obj\n<</Length " );
8065 aLine
.append( nSizeObject
);
8066 aLine
.append( " 0 R" );
8067 if( rStream
.m_bCompress
)
8068 aLine
.append( "/Filter/FlateDecode" );
8069 aLine
.append( ">>\nstream\n" );
8070 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
8072 sal_uInt64 nBeginStreamPos
= 0, nEndStreamPos
= 0;
8073 if( osl::File::E_None
!= m_aFile
.getPos(nBeginStreamPos
) )
8078 if( rStream
.m_bCompress
)
8081 checkAndEnableStreamEncryption( rStream
.m_nStreamObject
);
8082 css::uno::Reference
< css::io::XOutputStream
> xStream( new PDFStreamIf( this ) );
8083 assert(rStream
.m_pStream
);
8084 if (!rStream
.m_pStream
)
8086 rStream
.m_pStream
->write( xStream
);
8088 delete rStream
.m_pStream
;
8089 rStream
.m_pStream
= nullptr;
8090 disableStreamEncryption();
8092 if( rStream
.m_bCompress
)
8095 if (osl::File::E_None
!= m_aFile
.getPos(nEndStreamPos
))
8101 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
8103 // emit stream length object
8104 if( ! updateObject( nSizeObject
) )
8106 aLine
.setLength( 0 );
8107 aLine
.append( nSizeObject
);
8108 aLine
.append( " 0 obj\n" );
8109 aLine
.append( (sal_Int64
)(nEndStreamPos
-nBeginStreamPos
) );
8110 aLine
.append( "\nendobj\n\n" );
8111 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
8117 bool PDFWriterImpl::emit()
8121 // resort structure tree and annotations if necessary
8122 // needed for widget tab order
8125 #if HAVE_FEATURE_NSS
8126 if( m_aContext
.SignPDF
)
8128 // sign the document
8129 PDFWriter::SignatureWidget aSignature
;
8130 aSignature
.Name
= "Signature1";
8131 createControl( aSignature
, 0 );
8135 // emit additional streams
8136 CHECK_RETURN( emitAdditionalStreams() );
8139 CHECK_RETURN( emitCatalog() );
8141 #if HAVE_FEATURE_NSS
8142 if (m_nSignatureObject
!= -1) // if document is signed, emit sigdict
8144 if( !emitSignature() )
8146 m_aErrors
.insert( PDFWriter::Error_Signature_Failed
);
8153 CHECK_RETURN( emitTrailer() );
8155 #if HAVE_FEATURE_NSS
8156 if (m_nSignatureObject
!= -1) // finalize the signature
8158 if( !finalizeSignature() )
8160 m_aErrors
.insert( PDFWriter::Error_Signature_Failed
);
8173 sal_Int32
PDFWriterImpl::getSystemFont( const vcl::Font
& i_rFont
)
8175 getReferenceDevice()->Push();
8176 getReferenceDevice()->SetFont( i_rFont
);
8177 getReferenceDevice()->ImplNewFont();
8179 const PhysicalFontFace
* pDevFont
= m_pReferenceDevice
->mpFontInstance
->maFontSelData
.mpFontData
;
8180 sal_Int32 nFontID
= 0;
8181 FontEmbedData::iterator it
= m_aSystemFonts
.find( pDevFont
);
8182 if( it
!= m_aSystemFonts
.end() )
8183 nFontID
= it
->second
.m_nNormalFontID
;
8186 nFontID
= m_nNextFID
++;
8187 m_aSystemFonts
[ pDevFont
] = EmbedFont();
8188 m_aSystemFonts
[ pDevFont
].m_nNormalFontID
= nFontID
;
8191 getReferenceDevice()->Pop();
8192 getReferenceDevice()->ImplNewFont();
8197 void PDFWriterImpl::registerGlyphs( int nGlyphs
,
8198 const GlyphItem
** pGlyphs
,
8199 sal_Int32
* pGlyphWidths
,
8200 sal_Ucs
* pCodeUnits
,
8201 sal_Int32
* pCodeUnitsPerGlyph
,
8202 sal_uInt8
* pMappedGlyphs
,
8203 sal_Int32
* pMappedFontObjects
,
8204 const PhysicalFontFace
* pFallbackFonts
[] )
8206 SalGraphics
*pGraphics
= m_pReferenceDevice
->GetGraphics();
8211 const PhysicalFontFace
* pDevFont
= m_pReferenceDevice
->mpFontInstance
->maFontSelData
.mpFontData
;
8212 sal_Ucs
* pCurUnicode
= pCodeUnits
;
8213 for( int i
= 0; i
< nGlyphs
; pCurUnicode
+= pCodeUnitsPerGlyph
[i
] , i
++ )
8215 const int nFontGlyphId
= pGlyphs
[i
]->maGlyphId
;
8216 const PhysicalFontFace
* pCurrentFont
= pFallbackFonts
[i
] ? pFallbackFonts
[i
] : pDevFont
;
8218 FontSubset
& rSubset
= m_aSubsets
[ pCurrentFont
];
8219 // search for font specific glyphID
8220 FontMapping::iterator it
= rSubset
.m_aMapping
.find( nFontGlyphId
);
8221 if( it
!= rSubset
.m_aMapping
.end() )
8223 pMappedFontObjects
[i
] = it
->second
.m_nFontID
;
8224 pMappedGlyphs
[i
] = it
->second
.m_nSubsetGlyphID
;
8228 // create new subset if necessary
8229 if( rSubset
.m_aSubsets
.empty()
8230 || (rSubset
.m_aSubsets
.back().m_aMapping
.size() > 254) )
8232 rSubset
.m_aSubsets
.push_back( FontEmit( m_nNextFID
++ ) );
8236 pMappedFontObjects
[i
] = rSubset
.m_aSubsets
.back().m_nFontID
;
8237 // create new glyph in subset
8238 sal_uInt8 nNewId
= sal::static_int_cast
<sal_uInt8
>(rSubset
.m_aSubsets
.back().m_aMapping
.size()+1);
8239 pMappedGlyphs
[i
] = nNewId
;
8241 // add new glyph to emitted font subset
8242 GlyphEmit
& rNewGlyphEmit
= rSubset
.m_aSubsets
.back().m_aMapping
[ nFontGlyphId
];
8243 rNewGlyphEmit
.setGlyphId( nNewId
);
8244 for( sal_Int32 n
= 0; n
< pCodeUnitsPerGlyph
[i
]; n
++ )
8245 rNewGlyphEmit
.addCode( pCurUnicode
[n
] );
8247 // add new glyph to font mapping
8248 Glyph
& rNewGlyph
= rSubset
.m_aMapping
[ nFontGlyphId
];
8249 rNewGlyph
.m_nFontID
= pMappedFontObjects
[i
];
8250 rNewGlyph
.m_nSubsetGlyphID
= nNewId
;
8252 if (!getReferenceDevice()->AcquireGraphics())
8254 pGlyphWidths
[i
] = m_aFontCache
.getGlyphWidth( pCurrentFont
,
8256 pGlyphs
[i
]->IsVertical(),
8261 void PDFWriterImpl::drawRelief( SalLayout
& rLayout
, const OUString
& rText
, bool bTextLines
)
8263 push( PushFlags::ALL
);
8265 FontRelief eRelief
= m_aCurrentPDFState
.m_aFont
.GetRelief();
8267 Color aTextColor
= m_aCurrentPDFState
.m_aFont
.GetColor();
8268 Color aTextLineColor
= m_aCurrentPDFState
.m_aTextLineColor
;
8269 Color aOverlineColor
= m_aCurrentPDFState
.m_aOverlineColor
;
8270 Color
aReliefColor( COL_LIGHTGRAY
);
8271 if( aTextColor
== COL_BLACK
)
8272 aTextColor
= Color( COL_WHITE
);
8273 if( aTextLineColor
== COL_BLACK
)
8274 aTextLineColor
= Color( COL_WHITE
);
8275 if( aOverlineColor
== COL_BLACK
)
8276 aOverlineColor
= Color( COL_WHITE
);
8277 if( aTextColor
== COL_WHITE
)
8278 aReliefColor
= Color( COL_BLACK
);
8280 Font aSetFont
= m_aCurrentPDFState
.m_aFont
;
8281 aSetFont
.SetRelief( FontRelief::NONE
);
8282 aSetFont
.SetShadow( false );
8284 aSetFont
.SetColor( aReliefColor
);
8285 setTextLineColor( aReliefColor
);
8286 setOverlineColor( aReliefColor
);
8287 setFont( aSetFont
);
8288 long nOff
= 1 + getReferenceDevice()->mnDPIX
/300;
8289 if( eRelief
== FontRelief::Engraved
)
8292 rLayout
.DrawOffset() += Point( nOff
, nOff
);
8293 updateGraphicsState();
8294 drawLayout( rLayout
, rText
, bTextLines
);
8296 rLayout
.DrawOffset() -= Point( nOff
, nOff
);
8297 setTextLineColor( aTextLineColor
);
8298 setOverlineColor( aOverlineColor
);
8299 aSetFont
.SetColor( aTextColor
);
8300 setFont( aSetFont
);
8301 updateGraphicsState();
8302 drawLayout( rLayout
, rText
, bTextLines
);
8304 // clean up the mess
8308 void PDFWriterImpl::drawShadow( SalLayout
& rLayout
, const OUString
& rText
, bool bTextLines
)
8310 Font aSaveFont
= m_aCurrentPDFState
.m_aFont
;
8311 Color aSaveTextLineColor
= m_aCurrentPDFState
.m_aTextLineColor
;
8312 Color aSaveOverlineColor
= m_aCurrentPDFState
.m_aOverlineColor
;
8314 Font
& rFont
= m_aCurrentPDFState
.m_aFont
;
8315 if( rFont
.GetColor() == Color( COL_BLACK
) || rFont
.GetColor().GetLuminance() < 8 )
8316 rFont
.SetColor( Color( COL_LIGHTGRAY
) );
8318 rFont
.SetColor( Color( COL_BLACK
) );
8319 rFont
.SetShadow( false );
8320 rFont
.SetOutline( false );
8322 setTextLineColor( rFont
.GetColor() );
8323 setOverlineColor( rFont
.GetColor() );
8324 updateGraphicsState();
8326 long nOff
= 1 + ((m_pReferenceDevice
->mpFontInstance
->mnLineHeight
-24)/24);
8327 if( rFont
.IsOutline() )
8329 rLayout
.DrawBase() += Point( nOff
, nOff
);
8330 drawLayout( rLayout
, rText
, bTextLines
);
8331 rLayout
.DrawBase() -= Point( nOff
, nOff
);
8333 setFont( aSaveFont
);
8334 setTextLineColor( aSaveTextLineColor
);
8335 setOverlineColor( aSaveOverlineColor
);
8336 updateGraphicsState();
8339 void PDFWriterImpl::drawVerticalGlyphs(
8340 const std::vector
<PDFWriterImpl::PDFGlyph
>& rGlyphs
,
8341 OStringBuffer
& rLine
,
8342 const Point
& rAlignOffset
,
8343 const Matrix3
& rRotScale
,
8347 sal_Int32 nFontHeight
)
8350 Point
aCurPos( rGlyphs
[0].m_aPos
);
8351 aCurPos
= m_pReferenceDevice
->PixelToLogic( aCurPos
);
8352 aCurPos
+= rAlignOffset
;
8353 for( size_t i
= 0; i
< rGlyphs
.size(); i
++ )
8355 // have to emit each glyph on its own
8356 double fDeltaAngle
= 0.0;
8357 double fYScale
= 1.0;
8358 double fTempXScale
= fXScale
;
8359 double fSkewB
= fSkew
;
8360 double fSkewA
= 0.0;
8363 if (rGlyphs
[i
].m_bVertical
)
8365 fDeltaAngle
= M_PI
/2.0;
8366 aDeltaPos
.X() = m_pReferenceDevice
->GetFontMetric().GetAscent();
8367 aDeltaPos
.Y() = (int)((double)m_pReferenceDevice
->GetFontMetric().GetDescent() * fXScale
);
8373 aDeltaPos
+= (m_pReferenceDevice
->PixelToLogic( Point( (int)((double)nXOffset
/fXScale
), 0 ) ) - m_pReferenceDevice
->PixelToLogic( Point() ) );
8374 if( i
< rGlyphs
.size()-1 )
8375 // #i120627# the text on the Y axis is reversed when export ppt file to PDF format
8377 long nOffsetX
= rGlyphs
[i
+1].m_aPos
.X() - rGlyphs
[i
].m_aPos
.X();
8378 long nOffsetY
= rGlyphs
[i
+1].m_aPos
.Y() - rGlyphs
[i
].m_aPos
.Y();
8379 nXOffset
+= (int)sqrt(double(nOffsetX
*nOffsetX
+ nOffsetY
*nOffsetY
));
8381 if( ! rGlyphs
[i
].m_nGlyphId
)
8384 aDeltaPos
= rRotScale
.transform( aDeltaPos
);
8387 if( fSkewB
!= 0.0 || fSkewA
!= 0.0 )
8388 aMat
.skew( fSkewA
, fSkewB
);
8389 aMat
.scale( fTempXScale
, fYScale
);
8390 aMat
.rotate( fAngle
+fDeltaAngle
);
8391 aMat
.translate( aCurPos
.X()+aDeltaPos
.X(), aCurPos
.Y()+aDeltaPos
.Y() );
8392 aMat
.append( m_aPages
.back(), rLine
);
8393 rLine
.append( " Tm" );
8394 if( i
== 0 || rGlyphs
[i
-1].m_nMappedFontId
!= rGlyphs
[i
].m_nMappedFontId
)
8396 rLine
.append( " /F" );
8397 rLine
.append( rGlyphs
[i
].m_nMappedFontId
);
8398 rLine
.append( ' ' );
8399 m_aPages
.back().appendMappedLength( nFontHeight
, rLine
);
8400 rLine
.append( " Tf" );
8402 rLine
.append( "<" );
8403 appendHex( rGlyphs
[i
].m_nMappedGlyphId
, rLine
);
8404 rLine
.append( ">Tj\n" );
8408 void PDFWriterImpl::drawHorizontalGlyphs(
8409 const std::vector
<PDFWriterImpl::PDFGlyph
>& rGlyphs
,
8410 OStringBuffer
& rLine
,
8411 const Point
& rAlignOffset
,
8415 sal_Int32 nFontHeight
,
8416 sal_Int32 nPixelFontHeight
8419 // horizontal (= normal) case
8421 // fill in run end indices
8422 // end is marked by index of the first glyph of the next run
8423 // a run is marked by same mapped font id and same Y position
8424 std::vector
< sal_uInt32
> aRunEnds
;
8425 aRunEnds
.reserve( rGlyphs
.size() );
8426 for( size_t i
= 1; i
< rGlyphs
.size(); i
++ )
8428 if( rGlyphs
[i
].m_nMappedFontId
!= rGlyphs
[i
-1].m_nMappedFontId
||
8429 rGlyphs
[i
].m_aPos
.Y() != rGlyphs
[i
-1].m_aPos
.Y() )
8431 aRunEnds
.push_back(i
);
8434 // last run ends at last glyph
8435 aRunEnds
.push_back( rGlyphs
.size() );
8437 // loop over runs of the same font
8438 sal_uInt32 nBeginRun
= 0;
8439 for( size_t nRun
= 0; nRun
< aRunEnds
.size(); nRun
++ )
8441 // setup text matrix
8442 Point aCurPos
= rGlyphs
[nBeginRun
].m_aPos
;
8443 // back transformation to current coordinate system
8444 aCurPos
= m_pReferenceDevice
->PixelToLogic( aCurPos
);
8445 aCurPos
+= rAlignOffset
;
8446 // the first run can be set with "Td" operator
8447 // subsequent use of that operator would move
8448 // the textline matrix relative to what was set before
8449 // making use of that would drive us into rounding issues
8451 if( nRun
== 0 && fAngle
== 0.0 && fXScale
== 1.0 && fSkew
== 0.0 )
8453 m_aPages
.back().appendPoint( aCurPos
, rLine
);
8454 rLine
.append( " Td " );
8459 aMat
.skew( 0.0, fSkew
);
8460 aMat
.scale( fXScale
, 1.0 );
8461 aMat
.rotate( fAngle
);
8462 aMat
.translate( aCurPos
.X(), aCurPos
.Y() );
8463 aMat
.append( m_aPages
.back(), rLine
);
8464 rLine
.append( " Tm\n" );
8466 // set up correct font
8467 rLine
.append( "/F" );
8468 rLine
.append( rGlyphs
[nBeginRun
].m_nMappedFontId
);
8469 rLine
.append( ' ' );
8470 m_aPages
.back().appendMappedLength( nFontHeight
, rLine
);
8471 rLine
.append( " Tf" );
8473 // output glyphs using Tj or TJ
8474 OStringBuffer
aKernedLine( 256 ), aUnkernedLine( 256 );
8475 aKernedLine
.append( "[<" );
8476 aUnkernedLine
.append( '<' );
8477 appendHex( rGlyphs
[nBeginRun
].m_nMappedGlyphId
, aKernedLine
);
8478 appendHex( rGlyphs
[nBeginRun
].m_nMappedGlyphId
, aUnkernedLine
);
8481 bool bNeedKern
= false;
8482 for( sal_uInt32 nPos
= nBeginRun
+1; nPos
< aRunEnds
[nRun
]; nPos
++ )
8484 appendHex( rGlyphs
[nPos
].m_nMappedGlyphId
, aUnkernedLine
);
8485 // check if default glyph positioning is sufficient
8486 const Point aThisPos
= aMat
.transform( rGlyphs
[nPos
].m_aPos
);
8487 const Point aPrevPos
= aMat
.transform( rGlyphs
[nPos
-1].m_aPos
);
8488 double fAdvance
= aThisPos
.X() - aPrevPos
.X();
8489 fAdvance
*= 1000.0 / nPixelFontHeight
;
8490 const sal_Int32 nAdjustment
= (sal_Int32
)(rGlyphs
[nPos
-1].m_nNativeWidth
- fAdvance
+ 0.5);
8491 if( nAdjustment
!= 0 )
8493 // apply individual glyph positioning
8495 aKernedLine
.append( ">" );
8496 aKernedLine
.append( nAdjustment
);
8497 aKernedLine
.append( "<" );
8499 appendHex( rGlyphs
[nPos
].m_nMappedGlyphId
, aKernedLine
);
8501 aKernedLine
.append( ">]TJ\n" );
8502 aUnkernedLine
.append( ">Tj\n" );
8504 (bNeedKern
? aKernedLine
: aUnkernedLine
).makeStringAndClear() );
8506 // set beginning of next run
8507 nBeginRun
= aRunEnds
[nRun
];
8511 void PDFWriterImpl::drawLayout( SalLayout
& rLayout
, const OUString
& rText
, bool bTextLines
)
8513 // relief takes precedence over shadow (see outdev3.cxx)
8514 if( m_aCurrentPDFState
.m_aFont
.GetRelief() != FontRelief::NONE
)
8516 drawRelief( rLayout
, rText
, bTextLines
);
8519 else if( m_aCurrentPDFState
.m_aFont
.IsShadow() )
8520 drawShadow( rLayout
, rText
, bTextLines
);
8522 OStringBuffer
aLine( 512 );
8524 const int nMaxGlyphs
= 256;
8526 const GlyphItem
* pGlyphs
[nMaxGlyphs
] = { nullptr };
8527 const PhysicalFontFace
* pFallbackFonts
[nMaxGlyphs
] = { nullptr };
8528 sal_Int32 pGlyphWidths
[nMaxGlyphs
];
8529 sal_uInt8 pMappedGlyphs
[nMaxGlyphs
];
8530 sal_Int32 pMappedFontObjects
[nMaxGlyphs
];
8531 std::vector
<sal_Ucs
> aCodeUnits
;
8532 aCodeUnits
.reserve(nMaxGlyphs
);
8533 std::vector
<sal_Int32
> aCodeUnitsPerGlyph
;
8534 aCodeUnits
.reserve(nMaxGlyphs
);
8535 bool bVertical
= m_aCurrentPDFState
.m_aFont
.IsVertical();
8538 int nMinCharPos
= 0, nMaxCharPos
= rText
.getLength()-1;
8539 double fXScale
= 1.0;
8541 sal_Int32 nPixelFontHeight
= m_pReferenceDevice
->mpFontInstance
->maFontSelData
.mnHeight
;
8542 TextAlign eAlign
= m_aCurrentPDFState
.m_aFont
.GetAlignment();
8544 // transform font height back to current units
8545 // note: the layout calculates in outdevs device pixel !!
8546 sal_Int32 nFontHeight
= m_pReferenceDevice
->ImplDevicePixelToLogicHeight( nPixelFontHeight
);
8547 if( m_aCurrentPDFState
.m_aFont
.GetAverageFontWidth() )
8549 Font
aFont( m_aCurrentPDFState
.m_aFont
);
8550 aFont
.SetAverageFontWidth( 0 );
8551 FontMetric aMetric
= m_pReferenceDevice
->GetFontMetric( aFont
);
8552 if( aMetric
.GetAverageFontWidth() != m_aCurrentPDFState
.m_aFont
.GetAverageFontWidth() )
8555 (double)m_aCurrentPDFState
.m_aFont
.GetAverageFontWidth() /
8556 (double)aMetric
.GetAverageFontWidth();
8558 // force state before GetFontMetric
8559 m_pReferenceDevice
->ImplNewFont();
8562 // perform artificial italics if necessary
8563 if( ( m_aCurrentPDFState
.m_aFont
.GetItalic() == ITALIC_NORMAL
||
8564 m_aCurrentPDFState
.m_aFont
.GetItalic() == ITALIC_OBLIQUE
) &&
8565 !( m_pReferenceDevice
->mpFontInstance
->maFontSelData
.mpFontData
->GetItalic() == ITALIC_NORMAL
||
8566 m_pReferenceDevice
->mpFontInstance
->maFontSelData
.mpFontData
->GetItalic() == ITALIC_OBLIQUE
)
8572 // if the mapmode is distorted we need to adjust for that also
8573 if( m_aCurrentPDFState
.m_aMapMode
.GetScaleX() != m_aCurrentPDFState
.m_aMapMode
.GetScaleY() )
8575 fXScale
*= double(m_aCurrentPDFState
.m_aMapMode
.GetScaleX()) / double(m_aCurrentPDFState
.m_aMapMode
.GetScaleY());
8578 int nAngle
= m_aCurrentPDFState
.m_aFont
.GetOrientation();
8582 nAngle
= nAngle
% 3600;
8583 double fAngle
= (double)nAngle
* M_PI
/ 1800.0;
8586 aRotScale
.scale( fXScale
, 1.0 );
8588 aRotScale
.rotate( -fAngle
);
8591 bool bABold
= false;
8592 // artificial bold necessary ?
8593 if( m_pReferenceDevice
->mpFontInstance
->maFontSelData
.mpFontData
->GetWeight() <= WEIGHT_MEDIUM
&&
8594 m_pReferenceDevice
->mpFontInstance
->maFontSelData
.GetWeight() > WEIGHT_MEDIUM
)
8597 aLine
.append( "q " );
8601 // setup text colors (if necessary)
8602 Color
aStrokeColor( COL_TRANSPARENT
);
8603 Color
aNonStrokeColor( COL_TRANSPARENT
);
8605 if( m_aCurrentPDFState
.m_aFont
.IsOutline() )
8607 aStrokeColor
= m_aCurrentPDFState
.m_aFont
.GetColor();
8608 aNonStrokeColor
= Color( COL_WHITE
);
8611 aNonStrokeColor
= m_aCurrentPDFState
.m_aFont
.GetColor();
8613 aStrokeColor
= m_aCurrentPDFState
.m_aFont
.GetColor();
8615 if( aStrokeColor
!= Color( COL_TRANSPARENT
) && aStrokeColor
!= m_aCurrentPDFState
.m_aLineColor
)
8618 aLine
.append( "q " );
8620 appendStrokingColor( aStrokeColor
, aLine
);
8621 aLine
.append( "\n" );
8623 if( aNonStrokeColor
!= Color( COL_TRANSPARENT
) && aNonStrokeColor
!= m_aCurrentPDFState
.m_aFillColor
)
8626 aLine
.append( "q " );
8628 appendNonStrokingColor( aNonStrokeColor
, aLine
);
8629 aLine
.append( "\n" );
8632 // begin text object
8633 aLine
.append( "BT\n" );
8634 // outline attribute ?
8635 if( m_aCurrentPDFState
.m_aFont
.IsOutline() || bABold
)
8637 // set correct text mode, set stroke width
8638 aLine
.append( "2 Tr " ); // fill, then stroke
8640 if( m_aCurrentPDFState
.m_aFont
.IsOutline() )
8642 // unclear what to do in case of outline and artificial bold
8643 // for the time being outline wins
8644 aLine
.append( "0.25 w \n" );
8648 double fW
= (double)m_aCurrentPDFState
.m_aFont
.GetFontHeight() / 30.0;
8649 m_aPages
.back().appendMappedLength( fW
, aLine
);
8650 aLine
.append ( " w\n" );
8654 FontMetric aRefDevFontMetric
= m_pReferenceDevice
->GetFontMetric();
8656 // collect the glyphs into a single array
8657 const int nTmpMaxGlyphs
= rLayout
.GetOrientation() ? 1 : nMaxGlyphs
; // #i97991# temporary workaround for #i87686#
8658 std::vector
< PDFGlyph
> aGlyphs
;
8659 aGlyphs
.reserve( nTmpMaxGlyphs
);
8660 // first get all the glyphs and register them; coordinates still in Pixel
8662 while ((nGlyphs
= rLayout
.GetNextGlyphs(nTmpMaxGlyphs
, pGlyphs
, aGNGlyphPos
, nIndex
, pFallbackFonts
)) != 0)
8665 for( int i
= 0; i
< nGlyphs
; i
++ )
8667 // default case: 1 glyph is one unicode
8668 aCodeUnitsPerGlyph
.push_back(1);
8669 if (pGlyphs
[i
]->mnCharPos
>= nMinCharPos
&& pGlyphs
[i
]->mnCharPos
<= nMaxCharPos
)
8672 // try to handle ligatures and such
8675 nChars
= pGlyphs
[i
+1]->mnCharPos
- pGlyphs
[i
]->mnCharPos
;
8676 int start
= pGlyphs
[i
]->mnCharPos
;
8677 // #i115618# fix for simple RTL+CTL cases
8678 // supports RTL ligatures. TODO: more complex CTL, etc.
8682 start
= pGlyphs
[i
+1]->mnCharPos
+ 1;
8684 else if (nChars
== 0)
8686 aCodeUnitsPerGlyph
.back() = nChars
;
8687 for( int n
= 0; n
< nChars
; n
++ )
8688 aCodeUnits
.push_back( rText
[ start
+ n
] );
8691 aCodeUnits
.push_back(rText
[pGlyphs
[i
]->mnCharPos
]);
8694 aCodeUnits
.push_back( 0 );
8695 // note: in case of ctl one character may result
8696 // in multiple glyphs. The current SalLayout
8697 // implementations set -1 then to indicate that no direct
8698 // mapping is possible
8701 registerGlyphs( nGlyphs
, pGlyphs
, pGlyphWidths
, aCodeUnits
.data(), aCodeUnitsPerGlyph
.data(), pMappedGlyphs
, pMappedFontObjects
, pFallbackFonts
);
8703 for( int i
= 0; i
< nGlyphs
; i
++ )
8705 aGlyphs
.push_back( PDFGlyph( aGNGlyphPos
,
8707 pGlyphs
[i
]->maGlyphId
,
8708 pMappedFontObjects
[i
],
8710 pGlyphs
[i
]->IsVertical() ) );
8712 aGNGlyphPos
.Y() += pGlyphs
[i
]->mnNewWidth
/rLayout
.GetUnitsPerPixel();
8714 aGNGlyphPos
.X() += pGlyphs
[i
]->mnNewWidth
/rLayout
.GetUnitsPerPixel();
8718 // Avoid fill color when map mode is in pixels, the below code assumes
8720 bool bPixel
= m_aCurrentPDFState
.m_aMapMode
.GetMapUnit() == MapUnit::MapPixel
;
8721 if (m_aCurrentPDFState
.m_aFont
.GetFillColor() != Color(COL_TRANSPARENT
) && !bPixel
)
8723 // PDF doesn't have a text fill color, so draw a rectangle before
8724 // drawing the actual text.
8725 push(PushFlags::FILLCOLOR
| PushFlags::LINECOLOR
);
8726 setFillColor(m_aCurrentPDFState
.m_aFont
.GetFillColor());
8727 // Avoid border around the rectangle for Writer shape text.
8728 setLineColor(Color(COL_TRANSPARENT
));
8730 // The rectangle is the bounding box of the text, but also includes
8731 // ascent / descent to match the on-screen rendering.
8732 tools::Rectangle aRectangle
;
8733 // This is the top left of the text without ascent / descent.
8734 aRectangle
.SetPos(m_pReferenceDevice
->PixelToLogic(rLayout
.GetDrawPosition()));
8735 aRectangle
.setY(aRectangle
.getY() - aRefDevFontMetric
.GetAscent());
8736 aRectangle
.SetSize(m_pReferenceDevice
->PixelToLogic(Size(rLayout
.GetTextWidth(), 0)));
8737 // This includes ascent / descent.
8738 aRectangle
.setHeight(aRefDevFontMetric
.GetLineHeight());
8740 LogicalFontInstance
* pFontInstance
= m_pReferenceDevice
->mpFontInstance
;
8741 if (pFontInstance
->mnOrientation
)
8743 // Adapt rectangle for rotated text.
8744 tools::Polygon
aPolygon(aRectangle
);
8745 aPolygon
.Rotate(m_pReferenceDevice
->PixelToLogic(rLayout
.GetDrawPosition()), pFontInstance
->mnOrientation
);
8746 drawPolygon(aPolygon
);
8749 drawRectangle(aRectangle
);
8755 if ( eAlign
== ALIGN_BOTTOM
)
8756 aAlignOffset
.Y() -= aRefDevFontMetric
.GetDescent();
8757 else if ( eAlign
== ALIGN_TOP
)
8758 aAlignOffset
.Y() += aRefDevFontMetric
.GetAscent();
8759 if( aAlignOffset
.X() || aAlignOffset
.Y() )
8760 aAlignOffset
= aRotScale
.transform( aAlignOffset
);
8762 /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
8763 string contained only on of the UTF16 BOMs
8765 if( ! aGlyphs
.empty() )
8768 drawVerticalGlyphs( aGlyphs
, aLine
, aAlignOffset
, aRotScale
, fAngle
, fXScale
, fSkew
, nFontHeight
);
8770 drawHorizontalGlyphs( aGlyphs
, aLine
, aAlignOffset
, fAngle
, fXScale
, fSkew
, nFontHeight
, nPixelFontHeight
);
8774 aLine
.append( "ET\n" );
8776 aLine
.append( "Q\n" );
8778 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8780 // draw eventual textlines
8781 FontStrikeout eStrikeout
= m_aCurrentPDFState
.m_aFont
.GetStrikeout();
8782 FontLineStyle eUnderline
= m_aCurrentPDFState
.m_aFont
.GetUnderline();
8783 FontLineStyle eOverline
= m_aCurrentPDFState
.m_aFont
.GetOverline();
8786 ( eUnderline
!= LINESTYLE_NONE
&& eUnderline
!= LINESTYLE_DONTKNOW
) ||
8787 ( eOverline
!= LINESTYLE_NONE
&& eOverline
!= LINESTYLE_DONTKNOW
) ||
8788 ( eStrikeout
!= STRIKEOUT_NONE
&& eStrikeout
!= STRIKEOUT_DONTKNOW
)
8792 bool bUnderlineAbove
= OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState
.m_aFont
);
8793 if( m_aCurrentPDFState
.m_aFont
.IsWordLineMode() )
8795 Point aPos
, aStartPt
;
8796 sal_Int32 nWidth
= 0;
8797 const GlyphItem
* pGlyph
;
8799 while (rLayout
.GetNextGlyphs(1, &pGlyph
, aPos
, nStart
))
8801 if (!pGlyph
->IsSpacing())
8806 nWidth
+= pGlyph
->mnNewWidth
;
8808 else if( nWidth
> 0 )
8810 drawTextLine( m_pReferenceDevice
->PixelToLogic( aStartPt
),
8811 m_pReferenceDevice
->ImplDevicePixelToLogicWidth( nWidth
),
8812 eStrikeout
, eUnderline
, eOverline
, bUnderlineAbove
);
8819 drawTextLine( m_pReferenceDevice
->PixelToLogic( aStartPt
),
8820 m_pReferenceDevice
->ImplDevicePixelToLogicWidth( nWidth
),
8821 eStrikeout
, eUnderline
, eOverline
, bUnderlineAbove
);
8826 Point aStartPt
= rLayout
.GetDrawPosition();
8827 int nWidth
= rLayout
.GetTextWidth() / rLayout
.GetUnitsPerPixel();
8828 drawTextLine( m_pReferenceDevice
->PixelToLogic( aStartPt
),
8829 m_pReferenceDevice
->ImplDevicePixelToLogicWidth( nWidth
),
8830 eStrikeout
, eUnderline
, eOverline
, bUnderlineAbove
);
8834 // write eventual emphasis marks
8835 if( m_aCurrentPDFState
.m_aFont
.GetEmphasisMark() & FontEmphasisMark::Style
)
8837 tools::PolyPolygon aEmphPoly
;
8838 tools::Rectangle aEmphRect1
;
8839 tools::Rectangle aEmphRect2
;
8844 FontEmphasisMark nEmphMark
;
8846 push( PushFlags::ALL
);
8848 aLine
.setLength( 0 );
8849 aLine
.append( "q\n" );
8851 nEmphMark
= OutputDevice::ImplGetEmphasisMarkStyle( m_aCurrentPDFState
.m_aFont
);
8852 if ( nEmphMark
& FontEmphasisMark::PosBelow
)
8853 nEmphHeight
= m_pReferenceDevice
->mnEmphasisDescent
;
8855 nEmphHeight
= m_pReferenceDevice
->mnEmphasisAscent
;
8856 m_pReferenceDevice
->ImplGetEmphasisMark( aEmphPoly
,
8863 m_pReferenceDevice
->ImplDevicePixelToLogicWidth(nEmphHeight
) );
8864 if ( bEmphPolyLine
)
8866 setLineColor( m_aCurrentPDFState
.m_aFont
.GetColor() );
8867 setFillColor( Color( COL_TRANSPARENT
) );
8871 setFillColor( m_aCurrentPDFState
.m_aFont
.GetColor() );
8872 setLineColor( Color( COL_TRANSPARENT
) );
8874 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8876 Point aOffset
= Point(0,0);
8878 if ( nEmphMark
& FontEmphasisMark::PosBelow
)
8879 aOffset
.Y() += m_pReferenceDevice
->mpFontInstance
->mxFontMetric
->GetDescent() + nEmphYOff
;
8881 aOffset
.Y() -= m_pReferenceDevice
->mpFontInstance
->mxFontMetric
->GetAscent() + nEmphYOff
;
8883 long nEmphWidth2
= nEmphWidth
/ 2;
8884 long nEmphHeight2
= nEmphHeight
/ 2;
8885 aOffset
+= Point( nEmphWidth2
, nEmphHeight2
);
8887 if ( eAlign
== ALIGN_BOTTOM
)
8888 aOffset
.Y() -= m_pReferenceDevice
->mpFontInstance
->mxFontMetric
->GetDescent();
8889 else if ( eAlign
== ALIGN_TOP
)
8890 aOffset
.Y() += m_pReferenceDevice
->mpFontInstance
->mxFontMetric
->GetAscent();
8893 const GlyphItem
* pGlyph
;
8895 while (rLayout
.GetNextGlyphs(1, &pGlyph
, aPos
, nStart
))
8897 if (pGlyph
->IsSpacing())
8899 Point aAdjOffset
= aOffset
;
8900 aAdjOffset
.X() += (pGlyph
->mnNewWidth
- nEmphWidth
) / 2;
8901 aAdjOffset
= aRotScale
.transform( aAdjOffset
);
8903 aAdjOffset
-= Point( nEmphWidth2
, nEmphHeight2
);
8906 aPos
= m_pReferenceDevice
->PixelToLogic( aPos
);
8907 drawEmphasisMark( aPos
.X(), aPos
.Y(),
8908 aEmphPoly
, bEmphPolyLine
,
8909 aEmphRect1
, aEmphRect2
);
8913 writeBuffer( "Q\n", 2 );
8918 void PDFWriterImpl::drawEmphasisMark( long nX
, long nY
,
8919 const tools::PolyPolygon
& rPolyPoly
, bool bPolyLine
,
8920 const tools::Rectangle
& rRect1
, const tools::Rectangle
& rRect2
)
8922 // TODO: pass nWidth as width of this mark
8925 if ( rPolyPoly
.Count() )
8929 tools::Polygon aPoly
= rPolyPoly
.GetObject( 0 );
8930 aPoly
.Move( nX
, nY
);
8931 drawPolyLine( aPoly
);
8935 tools::PolyPolygon aPolyPoly
= rPolyPoly
;
8936 aPolyPoly
.Move( nX
, nY
);
8937 drawPolyPolygon( aPolyPoly
);
8941 if ( !rRect1
.IsEmpty() )
8943 tools::Rectangle
aRect( Point( nX
+rRect1
.Left(),
8944 nY
+rRect1
.Top() ), rRect1
.GetSize() );
8945 drawRectangle( aRect
);
8948 if ( !rRect2
.IsEmpty() )
8950 tools::Rectangle
aRect( Point( nX
+rRect2
.Left(),
8951 nY
+rRect2
.Top() ), rRect2
.GetSize() );
8953 drawRectangle( aRect
);
8957 void PDFWriterImpl::drawText( const Point
& rPos
, const OUString
& rText
, sal_Int32 nIndex
, sal_Int32 nLen
, bool bTextLines
)
8961 updateGraphicsState();
8963 // get a layout from the OuputDevice's SalGraphics
8964 // this also enforces font substitution and sets the font on SalGraphics
8965 SalLayout
* pLayout
= m_pReferenceDevice
->ImplLayout( rText
, nIndex
, nLen
, rPos
);
8968 drawLayout( *pLayout
, rText
, bTextLines
);
8973 void PDFWriterImpl::drawTextArray( const Point
& rPos
, const OUString
& rText
, const long* pDXArray
, sal_Int32 nIndex
, sal_Int32 nLen
)
8975 MARK( "drawText with array" );
8977 updateGraphicsState();
8979 // get a layout from the OuputDevice's SalGraphics
8980 // this also enforces font substitution and sets the font on SalGraphics
8981 SalLayout
* pLayout
= m_pReferenceDevice
->ImplLayout( rText
, nIndex
, nLen
, rPos
, 0, pDXArray
);
8984 drawLayout( *pLayout
, rText
, true );
8989 void PDFWriterImpl::drawStretchText( const Point
& rPos
, sal_uLong nWidth
, const OUString
& rText
, sal_Int32 nIndex
, sal_Int32 nLen
)
8991 MARK( "drawStretchText" );
8993 updateGraphicsState();
8995 // get a layout from the OuputDevice's SalGraphics
8996 // this also enforces font substitution and sets the font on SalGraphics
8997 SalLayout
* pLayout
= m_pReferenceDevice
->ImplLayout( rText
, nIndex
, nLen
, rPos
, nWidth
);
9000 drawLayout( *pLayout
, rText
, true );
9005 void PDFWriterImpl::drawText( const tools::Rectangle
& rRect
, const OUString
& rOrigStr
, DrawTextFlags nStyle
)
9007 long nWidth
= rRect
.GetWidth();
9008 long nHeight
= rRect
.GetHeight();
9010 if ( nWidth
<= 0 || nHeight
<= 0 )
9013 MARK( "drawText with rectangle" );
9015 updateGraphicsState();
9017 // clip with rectangle
9018 OStringBuffer aLine
;
9019 aLine
.append( "q " );
9020 m_aPages
.back().appendRect( rRect
, aLine
);
9021 aLine
.append( " W* n\n" );
9022 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9024 // if disabled text is needed, put in here
9026 Point aPos
= rRect
.TopLeft();
9028 long nTextHeight
= m_pReferenceDevice
->GetTextHeight();
9029 sal_Int32 nMnemonicPos
= -1;
9031 OUString aStr
= rOrigStr
;
9032 if ( nStyle
& DrawTextFlags::Mnemonic
)
9033 aStr
= OutputDevice::GetNonMnemonicString( aStr
, nMnemonicPos
);
9036 if ( nStyle
& DrawTextFlags::MultiLine
)
9039 ImplMultiTextLineInfo aMultiLineInfo
;
9040 ImplTextLineInfo
* pLineInfo
;
9043 sal_Int32 nFormatLines
;
9047 vcl::DefaultTextLayout
aLayout( *m_pReferenceDevice
);
9048 OutputDevice::ImplGetTextLines( aMultiLineInfo
, nWidth
, aStr
, nStyle
, aLayout
);
9049 nLines
= nHeight
/nTextHeight
;
9050 nFormatLines
= aMultiLineInfo
.Count();
9053 if ( nFormatLines
> nLines
)
9055 if ( nStyle
& DrawTextFlags::EndEllipsis
)
9058 nFormatLines
= nLines
-1;
9060 pLineInfo
= aMultiLineInfo
.GetLine( nFormatLines
);
9061 aLastLine
= convertLineEnd(aStr
.copy(pLineInfo
->GetIndex()), LINEEND_LF
);
9062 // replace line feed by space
9063 aLastLine
= aLastLine
.replace('\n', ' ');
9064 aLastLine
= m_pReferenceDevice
->GetEllipsisString( aLastLine
, nWidth
, nStyle
);
9065 nStyle
&= ~DrawTextFlags(DrawTextFlags::VCenter
| DrawTextFlags::Bottom
);
9066 nStyle
|= DrawTextFlags::Top
;
9070 // vertical alignment
9071 if ( nStyle
& DrawTextFlags::Bottom
)
9072 aPos
.Y() += nHeight
-(nFormatLines
*nTextHeight
);
9073 else if ( nStyle
& DrawTextFlags::VCenter
)
9074 aPos
.Y() += (nHeight
-(nFormatLines
*nTextHeight
))/2;
9076 // draw all lines excluding the last
9077 for ( i
= 0; i
< nFormatLines
; i
++ )
9079 pLineInfo
= aMultiLineInfo
.GetLine( i
);
9080 if ( nStyle
& DrawTextFlags::Right
)
9081 aPos
.X() += nWidth
-pLineInfo
->GetWidth();
9082 else if ( nStyle
& DrawTextFlags::Center
)
9083 aPos
.X() += (nWidth
-pLineInfo
->GetWidth())/2;
9084 sal_Int32 nIndex
= pLineInfo
->GetIndex();
9085 sal_Int32 nLineLen
= pLineInfo
->GetLen();
9086 drawText( aPos
, aStr
, nIndex
, nLineLen
);
9087 // mnemonics should not appear in documents,
9088 // if the need arises, put them in here
9089 aPos
.Y() += nTextHeight
;
9090 aPos
.X() = rRect
.Left();
9093 // output last line left adjusted since it was shortened
9094 if (!aLastLine
.isEmpty())
9095 drawText( aPos
, aLastLine
, 0, aLastLine
.getLength() );
9100 long nTextWidth
= m_pReferenceDevice
->GetTextWidth( aStr
);
9102 // Evt. Text kuerzen
9103 if ( nTextWidth
> nWidth
)
9105 if ( nStyle
& (DrawTextFlags::EndEllipsis
| DrawTextFlags::PathEllipsis
| DrawTextFlags::NewsEllipsis
) )
9107 aStr
= m_pReferenceDevice
->GetEllipsisString( aStr
, nWidth
, nStyle
);
9108 nStyle
&= ~DrawTextFlags(DrawTextFlags::Center
| DrawTextFlags::Right
);
9109 nStyle
|= DrawTextFlags::Left
;
9110 nTextWidth
= m_pReferenceDevice
->GetTextWidth( aStr
);
9114 // vertical alignment
9115 if ( nStyle
& DrawTextFlags::Right
)
9116 aPos
.X() += nWidth
-nTextWidth
;
9117 else if ( nStyle
& DrawTextFlags::Center
)
9118 aPos
.X() += (nWidth
-nTextWidth
)/2;
9120 if ( nStyle
& DrawTextFlags::Bottom
)
9121 aPos
.Y() += nHeight
-nTextHeight
;
9122 else if ( nStyle
& DrawTextFlags::VCenter
)
9123 aPos
.Y() += (nHeight
-nTextHeight
)/2;
9125 // mnemonics should be inserted here if the need arises
9127 // draw the actual text
9128 drawText( aPos
, aStr
, 0, aStr
.getLength() );
9131 // reset clip region to original value
9132 aLine
.setLength( 0 );
9133 aLine
.append( "Q\n" );
9134 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9137 void PDFWriterImpl::drawLine( const Point
& rStart
, const Point
& rStop
)
9141 updateGraphicsState();
9143 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) )
9146 OStringBuffer aLine
;
9147 m_aPages
.back().appendPoint( rStart
, aLine
);
9148 aLine
.append( " m " );
9149 m_aPages
.back().appendPoint( rStop
, aLine
);
9150 aLine
.append( " l S\n" );
9152 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9155 void PDFWriterImpl::drawLine( const Point
& rStart
, const Point
& rStop
, const LineInfo
& rInfo
)
9157 MARK( "drawLine with LineInfo" );
9158 updateGraphicsState();
9160 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) )
9163 if( rInfo
.GetStyle() == LineStyle::Solid
&& rInfo
.GetWidth() < 2 )
9165 drawLine( rStart
, rStop
);
9169 OStringBuffer aLine
;
9171 aLine
.append( "q " );
9172 if( m_aPages
.back().appendLineInfo( rInfo
, aLine
) )
9174 m_aPages
.back().appendPoint( rStart
, aLine
);
9175 aLine
.append( " m " );
9176 m_aPages
.back().appendPoint( rStop
, aLine
);
9177 aLine
.append( " l S Q\n" );
9179 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9183 PDFWriter::ExtLineInfo aInfo
;
9184 convertLineInfoToExtLineInfo( rInfo
, aInfo
);
9185 Point aPolyPoints
[2] = { rStart
, rStop
};
9186 tools::Polygon
aPoly( 2, aPolyPoints
);
9187 drawPolyLine( aPoly
, aInfo
);
9191 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
9193 void PDFWriterImpl::drawWaveTextLine( OStringBuffer
& aLine
, long nWidth
, FontLineStyle eTextLine
, Color aColor
, bool bIsAbove
)
9195 // note: units in pFontInstance are ref device pixel
9196 LogicalFontInstance
* pFontInstance
= m_pReferenceDevice
->mpFontInstance
;
9197 long nLineHeight
= 0;
9200 appendStrokingColor( aColor
, aLine
);
9201 aLine
.append( "\n" );
9205 if ( !pFontInstance
->mxFontMetric
->GetAboveWavelineUnderlineSize() )
9206 m_pReferenceDevice
->ImplInitAboveTextLineSize();
9207 nLineHeight
= HCONV( pFontInstance
->mxFontMetric
->GetAboveWavelineUnderlineSize() );
9208 nLinePos
= HCONV( pFontInstance
->mxFontMetric
->GetAboveWavelineUnderlineOffset() );
9212 if ( !pFontInstance
->mxFontMetric
->GetWavelineUnderlineSize() )
9213 m_pReferenceDevice
->ImplInitTextLineSize();
9214 nLineHeight
= HCONV( pFontInstance
->mxFontMetric
->GetWavelineUnderlineSize() );
9215 nLinePos
= HCONV( pFontInstance
->mxFontMetric
->GetWavelineUnderlineOffset() );
9217 if ( (eTextLine
== LINESTYLE_SMALLWAVE
) && (nLineHeight
> 3) )
9220 long nLineWidth
= getReferenceDevice()->mnDPIX
/450;
9224 if ( eTextLine
== LINESTYLE_BOLDWAVE
)
9225 nLineWidth
= 3*nLineWidth
;
9227 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineWidth
, aLine
);
9228 aLine
.append( " w " );
9230 if ( eTextLine
== LINESTYLE_DOUBLEWAVE
)
9232 long nOrgLineHeight
= nLineHeight
;
9234 if ( nLineHeight
< 2 )
9236 if ( nOrgLineHeight
> 1 )
9241 long nLineDY
= nOrgLineHeight
-(nLineHeight
*2);
9242 if ( nLineDY
< nLineWidth
)
9243 nLineDY
= nLineWidth
;
9244 long nLineDY2
= nLineDY
/2;
9248 nLinePos
-= nLineWidth
-nLineDY2
;
9250 m_aPages
.back().appendWaveLine( nWidth
, -nLinePos
, 2*nLineHeight
, aLine
);
9252 nLinePos
+= nLineWidth
+nLineDY
;
9253 m_aPages
.back().appendWaveLine( nWidth
, -nLinePos
, 2*nLineHeight
, aLine
);
9257 if ( eTextLine
!= LINESTYLE_BOLDWAVE
)
9258 nLinePos
-= nLineWidth
/2;
9259 m_aPages
.back().appendWaveLine( nWidth
, -nLinePos
, nLineHeight
, aLine
);
9263 void PDFWriterImpl::drawStraightTextLine( OStringBuffer
& aLine
, long nWidth
, FontLineStyle eTextLine
, Color aColor
, bool bIsAbove
)
9265 // note: units in pFontInstance are ref device pixel
9266 LogicalFontInstance
* pFontInstance
= m_pReferenceDevice
->mpFontInstance
;
9267 long nLineHeight
= 0;
9271 if ( eTextLine
> LINESTYLE_BOLDWAVE
)
9272 eTextLine
= LINESTYLE_SINGLE
;
9274 switch ( eTextLine
)
9276 case LINESTYLE_SINGLE
:
9277 case LINESTYLE_DOTTED
:
9278 case LINESTYLE_DASH
:
9279 case LINESTYLE_LONGDASH
:
9280 case LINESTYLE_DASHDOT
:
9281 case LINESTYLE_DASHDOTDOT
:
9284 if ( !pFontInstance
->mxFontMetric
->GetAboveUnderlineSize() )
9285 m_pReferenceDevice
->ImplInitAboveTextLineSize();
9286 nLineHeight
= HCONV( pFontInstance
->mxFontMetric
->GetAboveUnderlineSize() );
9287 nLinePos
= HCONV( pFontInstance
->mxFontMetric
->GetAboveUnderlineOffset() );
9291 if ( !pFontInstance
->mxFontMetric
->GetUnderlineSize() )
9292 m_pReferenceDevice
->ImplInitTextLineSize();
9293 nLineHeight
= HCONV( pFontInstance
->mxFontMetric
->GetUnderlineSize() );
9294 nLinePos
= HCONV( pFontInstance
->mxFontMetric
->GetUnderlineOffset() );
9297 case LINESTYLE_BOLD
:
9298 case LINESTYLE_BOLDDOTTED
:
9299 case LINESTYLE_BOLDDASH
:
9300 case LINESTYLE_BOLDLONGDASH
:
9301 case LINESTYLE_BOLDDASHDOT
:
9302 case LINESTYLE_BOLDDASHDOTDOT
:
9305 if ( !pFontInstance
->mxFontMetric
->GetAboveBoldUnderlineSize() )
9306 m_pReferenceDevice
->ImplInitAboveTextLineSize();
9307 nLineHeight
= HCONV( pFontInstance
->mxFontMetric
->GetAboveBoldUnderlineSize() );
9308 nLinePos
= HCONV( pFontInstance
->mxFontMetric
->GetAboveBoldUnderlineOffset() );
9312 if ( !pFontInstance
->mxFontMetric
->GetBoldUnderlineSize() )
9313 m_pReferenceDevice
->ImplInitTextLineSize();
9314 nLineHeight
= HCONV( pFontInstance
->mxFontMetric
->GetBoldUnderlineSize() );
9315 nLinePos
= HCONV( pFontInstance
->mxFontMetric
->GetBoldUnderlineOffset() );
9316 nLinePos
+= nLineHeight
/2;
9319 case LINESTYLE_DOUBLE
:
9322 if ( !pFontInstance
->mxFontMetric
->GetAboveDoubleUnderlineSize() )
9323 m_pReferenceDevice
->ImplInitAboveTextLineSize();
9324 nLineHeight
= HCONV( pFontInstance
->mxFontMetric
->GetAboveDoubleUnderlineSize() );
9325 nLinePos
= HCONV( pFontInstance
->mxFontMetric
->GetAboveDoubleUnderlineOffset1() );
9326 nLinePos2
= HCONV( pFontInstance
->mxFontMetric
->GetAboveDoubleUnderlineOffset2() );
9330 if ( !pFontInstance
->mxFontMetric
->GetDoubleUnderlineSize() )
9331 m_pReferenceDevice
->ImplInitTextLineSize();
9332 nLineHeight
= HCONV( pFontInstance
->mxFontMetric
->GetDoubleUnderlineSize() );
9333 nLinePos
= HCONV( pFontInstance
->mxFontMetric
->GetDoubleUnderlineOffset1() );
9334 nLinePos2
= HCONV( pFontInstance
->mxFontMetric
->GetDoubleUnderlineOffset2() );
9343 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
);
9344 aLine
.append( " w " );
9345 appendStrokingColor( aColor
, aLine
);
9346 aLine
.append( "\n" );
9348 switch ( eTextLine
)
9350 case LINESTYLE_DOTTED
:
9351 case LINESTYLE_BOLDDOTTED
:
9352 aLine
.append( "[ " );
9353 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
, false );
9354 aLine
.append( " ] 0 d\n" );
9356 case LINESTYLE_DASH
:
9357 case LINESTYLE_LONGDASH
:
9358 case LINESTYLE_BOLDDASH
:
9359 case LINESTYLE_BOLDLONGDASH
:
9361 sal_Int32 nDashLength
= 4*nLineHeight
;
9362 sal_Int32 nVoidLength
= 2*nLineHeight
;
9363 if ( ( eTextLine
== LINESTYLE_LONGDASH
) || ( eTextLine
== LINESTYLE_BOLDLONGDASH
) )
9364 nDashLength
= 8*nLineHeight
;
9366 aLine
.append( "[ " );
9367 m_aPages
.back().appendMappedLength( nDashLength
, aLine
, false );
9368 aLine
.append( ' ' );
9369 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
9370 aLine
.append( " ] 0 d\n" );
9373 case LINESTYLE_DASHDOT
:
9374 case LINESTYLE_BOLDDASHDOT
:
9376 sal_Int32 nDashLength
= 4*nLineHeight
;
9377 sal_Int32 nVoidLength
= 2*nLineHeight
;
9378 aLine
.append( "[ " );
9379 m_aPages
.back().appendMappedLength( nDashLength
, aLine
, false );
9380 aLine
.append( ' ' );
9381 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
9382 aLine
.append( ' ' );
9383 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
, false );
9384 aLine
.append( ' ' );
9385 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
9386 aLine
.append( " ] 0 d\n" );
9389 case LINESTYLE_DASHDOTDOT
:
9390 case LINESTYLE_BOLDDASHDOTDOT
:
9392 sal_Int32 nDashLength
= 4*nLineHeight
;
9393 sal_Int32 nVoidLength
= 2*nLineHeight
;
9394 aLine
.append( "[ " );
9395 m_aPages
.back().appendMappedLength( nDashLength
, aLine
, false );
9396 aLine
.append( ' ' );
9397 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
9398 aLine
.append( ' ' );
9399 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
, false );
9400 aLine
.append( ' ' );
9401 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
9402 aLine
.append( ' ' );
9403 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
, false );
9404 aLine
.append( ' ' );
9405 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
9406 aLine
.append( " ] 0 d\n" );
9413 aLine
.append( "0 " );
9414 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos
), aLine
);
9415 aLine
.append( " m " );
9416 m_aPages
.back().appendMappedLength( (sal_Int32
)nWidth
, aLine
, false );
9417 aLine
.append( ' ' );
9418 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos
), aLine
);
9419 aLine
.append( " l S\n" );
9420 if ( eTextLine
== LINESTYLE_DOUBLE
)
9422 aLine
.append( "0 " );
9423 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos2
-nLineHeight
), aLine
);
9424 aLine
.append( " m " );
9425 m_aPages
.back().appendMappedLength( (sal_Int32
)nWidth
, aLine
, false );
9426 aLine
.append( ' ' );
9427 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos2
-nLineHeight
), aLine
);
9428 aLine
.append( " l S\n" );
9433 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer
& aLine
, long nWidth
, FontStrikeout eStrikeout
, Color aColor
)
9435 // note: units in pFontInstance are ref device pixel
9436 LogicalFontInstance
* pFontInstance
= m_pReferenceDevice
->mpFontInstance
;
9437 long nLineHeight
= 0;
9441 if ( eStrikeout
> STRIKEOUT_X
)
9442 eStrikeout
= STRIKEOUT_SINGLE
;
9444 switch ( eStrikeout
)
9446 case STRIKEOUT_SINGLE
:
9447 if ( !pFontInstance
->mxFontMetric
->GetStrikeoutSize() )
9448 m_pReferenceDevice
->ImplInitTextLineSize();
9449 nLineHeight
= HCONV( pFontInstance
->mxFontMetric
->GetStrikeoutSize() );
9450 nLinePos
= HCONV( pFontInstance
->mxFontMetric
->GetStrikeoutOffset() );
9452 case STRIKEOUT_BOLD
:
9453 if ( !pFontInstance
->mxFontMetric
->GetBoldStrikeoutSize() )
9454 m_pReferenceDevice
->ImplInitTextLineSize();
9455 nLineHeight
= HCONV( pFontInstance
->mxFontMetric
->GetBoldStrikeoutSize() );
9456 nLinePos
= HCONV( pFontInstance
->mxFontMetric
->GetBoldStrikeoutOffset() );
9458 case STRIKEOUT_DOUBLE
:
9459 if ( !pFontInstance
->mxFontMetric
->GetDoubleStrikeoutSize() )
9460 m_pReferenceDevice
->ImplInitTextLineSize();
9461 nLineHeight
= HCONV( pFontInstance
->mxFontMetric
->GetDoubleStrikeoutSize() );
9462 nLinePos
= HCONV( pFontInstance
->mxFontMetric
->GetDoubleStrikeoutOffset1() );
9463 nLinePos2
= HCONV( pFontInstance
->mxFontMetric
->GetDoubleStrikeoutOffset2() );
9471 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
);
9472 aLine
.append( " w " );
9473 appendStrokingColor( aColor
, aLine
);
9474 aLine
.append( "\n" );
9476 aLine
.append( "0 " );
9477 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos
), aLine
);
9478 aLine
.append( " m " );
9479 m_aPages
.back().appendMappedLength( (sal_Int32
)nWidth
, aLine
);
9480 aLine
.append( ' ' );
9481 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos
), aLine
);
9482 aLine
.append( " l S\n" );
9484 if ( eStrikeout
== STRIKEOUT_DOUBLE
)
9486 aLine
.append( "0 " );
9487 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos2
-nLineHeight
), aLine
);
9488 aLine
.append( " m " );
9489 m_aPages
.back().appendMappedLength( (sal_Int32
)nWidth
, aLine
);
9490 aLine
.append( ' ' );
9491 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos2
-nLineHeight
), aLine
);
9492 aLine
.append( " l S\n" );
9497 void PDFWriterImpl::drawStrikeoutChar( const Point
& rPos
, long nWidth
, FontStrikeout eStrikeout
)
9499 //See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
9502 OUString aStrikeoutChar
= eStrikeout
== STRIKEOUT_SLASH
? OUString( "/" ) : OUString( "X" );
9503 OUString aStrikeout
= aStrikeoutChar
;
9504 while( m_pReferenceDevice
->GetTextWidth( aStrikeout
) < nWidth
)
9505 aStrikeout
+= aStrikeout
;
9507 // do not get broader than nWidth modulo 1 character
9508 while( m_pReferenceDevice
->GetTextWidth( aStrikeout
) >= nWidth
)
9509 aStrikeout
= aStrikeout
.replaceAt( 0, 1, "" );
9510 aStrikeout
+= aStrikeoutChar
;
9511 bool bShadow
= m_aCurrentPDFState
.m_aFont
.IsShadow();
9514 Font aFont
= m_aCurrentPDFState
.m_aFont
;
9515 aFont
.SetShadow( false );
9517 updateGraphicsState();
9520 // strikeout string is left aligned non-CTL text
9521 ComplexTextLayoutFlags nOrigTLM
= m_pReferenceDevice
->GetLayoutMode();
9522 m_pReferenceDevice
->SetLayoutMode(ComplexTextLayoutFlags::BiDiStrong
);
9524 push( PushFlags::CLIPREGION
);
9525 FontMetric aRefDevFontMetric
= m_pReferenceDevice
->GetFontMetric();
9526 tools::Rectangle aRect
;
9527 aRect
.Left() = rPos
.X();
9528 aRect
.Right() = aRect
.Left()+nWidth
;
9529 aRect
.Bottom() = rPos
.Y()+aRefDevFontMetric
.GetDescent();
9530 aRect
.Top() = rPos
.Y()-aRefDevFontMetric
.GetAscent();
9532 LogicalFontInstance
* pFontInstance
= m_pReferenceDevice
->mpFontInstance
;
9533 if (pFontInstance
->mnOrientation
)
9535 tools::Polygon
aPoly( aRect
);
9536 aPoly
.Rotate( rPos
, pFontInstance
->mnOrientation
);
9537 aRect
= aPoly
.GetBoundRect();
9540 intersectClipRegion( aRect
);
9541 drawText( rPos
, aStrikeout
, 0, aStrikeout
.getLength(), false );
9544 m_pReferenceDevice
->SetLayoutMode( nOrigTLM
);
9548 Font aFont
= m_aCurrentPDFState
.m_aFont
;
9549 aFont
.SetShadow( true );
9551 updateGraphicsState();
9555 void PDFWriterImpl::drawTextLine( const Point
& rPos
, long nWidth
, FontStrikeout eStrikeout
, FontLineStyle eUnderline
, FontLineStyle eOverline
, bool bUnderlineAbove
)
9558 ( ((eStrikeout
== STRIKEOUT_NONE
)||(eStrikeout
== STRIKEOUT_DONTKNOW
)) &&
9559 ((eUnderline
== LINESTYLE_NONE
)||(eUnderline
== LINESTYLE_DONTKNOW
)) &&
9560 ((eOverline
== LINESTYLE_NONE
)||(eOverline
== LINESTYLE_DONTKNOW
)) ) )
9563 MARK( "drawTextLine" );
9564 updateGraphicsState();
9566 // note: units in pFontInstance are ref device pixel
9567 LogicalFontInstance
* pFontInstance
= m_pReferenceDevice
->mpFontInstance
;
9568 Color aUnderlineColor
= m_aCurrentPDFState
.m_aTextLineColor
;
9569 Color aOverlineColor
= m_aCurrentPDFState
.m_aOverlineColor
;
9570 Color aStrikeoutColor
= m_aCurrentPDFState
.m_aFont
.GetColor();
9571 bool bStrikeoutDone
= false;
9572 bool bUnderlineDone
= false;
9573 bool bOverlineDone
= false;
9575 if ( (eStrikeout
== STRIKEOUT_SLASH
) || (eStrikeout
== STRIKEOUT_X
) )
9577 drawStrikeoutChar( rPos
, nWidth
, eStrikeout
);
9578 bStrikeoutDone
= true;
9582 TextAlign eAlign
= m_aCurrentPDFState
.m_aFont
.GetAlignment();
9583 if( eAlign
== ALIGN_TOP
)
9584 aPos
.Y() += HCONV( pFontInstance
->mxFontMetric
->GetAscent() );
9585 else if( eAlign
== ALIGN_BOTTOM
)
9586 aPos
.Y() -= HCONV( pFontInstance
->mxFontMetric
->GetDescent() );
9588 OStringBuffer
aLine( 512 );
9590 aLine
.append( "q " );
9592 // rotate and translate matrix
9593 double fAngle
= (double)m_aCurrentPDFState
.m_aFont
.GetOrientation() * M_PI
/ 1800.0;
9595 aMat
.rotate( fAngle
);
9596 aMat
.translate( aPos
.X(), aPos
.Y() );
9597 aMat
.append( m_aPages
.back(), aLine
);
9598 aLine
.append( " cm\n" );
9600 if ( aUnderlineColor
.GetTransparency() != 0 )
9601 aUnderlineColor
= aStrikeoutColor
;
9603 if ( (eUnderline
== LINESTYLE_SMALLWAVE
) ||
9604 (eUnderline
== LINESTYLE_WAVE
) ||
9605 (eUnderline
== LINESTYLE_DOUBLEWAVE
) ||
9606 (eUnderline
== LINESTYLE_BOLDWAVE
) )
9608 drawWaveTextLine( aLine
, nWidth
, eUnderline
, aUnderlineColor
, bUnderlineAbove
);
9609 bUnderlineDone
= true;
9612 if ( (eOverline
== LINESTYLE_SMALLWAVE
) ||
9613 (eOverline
== LINESTYLE_WAVE
) ||
9614 (eOverline
== LINESTYLE_DOUBLEWAVE
) ||
9615 (eOverline
== LINESTYLE_BOLDWAVE
) )
9617 drawWaveTextLine( aLine
, nWidth
, eOverline
, aOverlineColor
, true );
9618 bOverlineDone
= true;
9621 if ( !bUnderlineDone
)
9623 drawStraightTextLine( aLine
, nWidth
, eUnderline
, aUnderlineColor
, bUnderlineAbove
);
9626 if ( !bOverlineDone
)
9628 drawStraightTextLine( aLine
, nWidth
, eOverline
, aOverlineColor
, true );
9631 if ( !bStrikeoutDone
)
9633 drawStrikeoutLine( aLine
, nWidth
, eStrikeout
, aStrikeoutColor
);
9636 aLine
.append( "Q\n" );
9637 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9640 void PDFWriterImpl::drawPolygon( const tools::Polygon
& rPoly
)
9642 MARK( "drawPolygon" );
9644 updateGraphicsState();
9646 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
9647 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
9650 int nPoints
= rPoly
.GetSize();
9651 OStringBuffer
aLine( 20 * nPoints
);
9652 m_aPages
.back().appendPolygon( rPoly
, aLine
);
9653 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
9654 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
9655 aLine
.append( "B*\n" );
9656 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
9657 aLine
.append( "S\n" );
9659 aLine
.append( "f*\n" );
9661 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9664 void PDFWriterImpl::drawPolyPolygon( const tools::PolyPolygon
& rPolyPoly
)
9666 MARK( "drawPolyPolygon" );
9668 updateGraphicsState();
9670 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
9671 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
9674 int nPolygons
= rPolyPoly
.Count();
9676 OStringBuffer
aLine( 40 * nPolygons
);
9677 m_aPages
.back().appendPolyPolygon( rPolyPoly
, aLine
);
9678 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
9679 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
9680 aLine
.append( "B*\n" );
9681 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
9682 aLine
.append( "S\n" );
9684 aLine
.append( "f*\n" );
9686 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9689 void PDFWriterImpl::drawTransparent( const tools::PolyPolygon
& rPolyPoly
, sal_uInt32 nTransparentPercent
)
9691 SAL_WARN_IF( nTransparentPercent
> 100, "vcl.pdfwriter", "invalid alpha value" );
9692 nTransparentPercent
= nTransparentPercent
% 100;
9694 MARK( "drawTransparent" );
9696 updateGraphicsState();
9698 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
9699 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
9702 if( m_bIsPDF_A1
|| m_aContext
.Version
< PDFWriter::PDFVersion::PDF_1_4
)
9704 m_aErrors
.insert( m_bIsPDF_A1
?
9705 PDFWriter::Warning_Transparency_Omitted_PDFA
:
9706 PDFWriter::Warning_Transparency_Omitted_PDF13
);
9708 drawPolyPolygon( rPolyPoly
);
9713 m_aTransparentObjects
.push_back( TransparencyEmit() );
9714 // FIXME: polygons with beziers may yield incorrect bound rect
9715 m_aTransparentObjects
.back().m_aBoundRect
= rPolyPoly
.GetBoundRect();
9716 // convert rectangle to default user space
9717 m_aPages
.back().convertRect( m_aTransparentObjects
.back().m_aBoundRect
);
9718 m_aTransparentObjects
.back().m_nObject
= createObject();
9719 m_aTransparentObjects
.back().m_nExtGStateObject
= createObject();
9720 m_aTransparentObjects
.back().m_fAlpha
= (double)(100-nTransparentPercent
) / 100.0;
9721 m_aTransparentObjects
.back().m_pContentStream
= new SvMemoryStream( 256, 256 );
9722 // create XObject's content stream
9723 OStringBuffer
aContent( 256 );
9724 m_aPages
.back().appendPolyPolygon( rPolyPoly
, aContent
);
9725 if( m_aCurrentPDFState
.m_aLineColor
!= Color( COL_TRANSPARENT
) &&
9726 m_aCurrentPDFState
.m_aFillColor
!= Color( COL_TRANSPARENT
) )
9727 aContent
.append( " B*\n" );
9728 else if( m_aCurrentPDFState
.m_aLineColor
!= Color( COL_TRANSPARENT
) )
9729 aContent
.append( " S\n" );
9731 aContent
.append( " f*\n" );
9732 m_aTransparentObjects
.back().m_pContentStream
->WriteBytes(
9733 aContent
.getStr(), aContent
.getLength() );
9735 OStringBuffer
aObjName( 16 );
9736 aObjName
.append( "Tr" );
9737 aObjName
.append( m_aTransparentObjects
.back().m_nObject
);
9738 OString
aTrName( aObjName
.makeStringAndClear() );
9739 aObjName
.append( "EGS" );
9740 aObjName
.append( m_aTransparentObjects
.back().m_nExtGStateObject
);
9741 OString
aExtName( aObjName
.makeStringAndClear() );
9743 OStringBuffer
aLine( 80 );
9745 aLine
.append( "q /" );
9746 aLine
.append( aExtName
);
9747 aLine
.append( " gs /" );
9748 aLine
.append( aTrName
);
9749 aLine
.append( " Do Q\n" );
9750 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9752 pushResource( ResXObject
, aTrName
, m_aTransparentObjects
.back().m_nObject
);
9753 pushResource( ResExtGState
, aExtName
, m_aTransparentObjects
.back().m_nExtGStateObject
);
9756 void PDFWriterImpl::pushResource( ResourceKind eKind
, const OString
& rResource
, sal_Int32 nObject
)
9763 m_aGlobalResourceDict
.m_aXObjects
[ rResource
] = nObject
;
9764 if( ! m_aOutputStreams
.empty() )
9765 m_aOutputStreams
.front().m_aResourceDict
.m_aXObjects
[ rResource
] = nObject
;
9768 m_aGlobalResourceDict
.m_aExtGStates
[ rResource
] = nObject
;
9769 if( ! m_aOutputStreams
.empty() )
9770 m_aOutputStreams
.front().m_aResourceDict
.m_aExtGStates
[ rResource
] = nObject
;
9773 m_aGlobalResourceDict
.m_aShadings
[ rResource
] = nObject
;
9774 if( ! m_aOutputStreams
.empty() )
9775 m_aOutputStreams
.front().m_aResourceDict
.m_aShadings
[ rResource
] = nObject
;
9778 m_aGlobalResourceDict
.m_aPatterns
[ rResource
] = nObject
;
9779 if( ! m_aOutputStreams
.empty() )
9780 m_aOutputStreams
.front().m_aResourceDict
.m_aPatterns
[ rResource
] = nObject
;
9786 void PDFWriterImpl::beginRedirect( SvStream
* pStream
, const tools::Rectangle
& rTargetRect
)
9788 push( PushFlags::ALL
);
9790 // force reemitting clip region inside the new stream, and
9791 // prevent emitting an unbalanced "Q" at the start
9793 // this is needed to point m_aCurrentPDFState at the pushed state
9794 // ... but it's pointless to actually write into the "outer" stream here!
9795 updateGraphicsState(NOWRITE
);
9797 m_aOutputStreams
.push_front( StreamRedirect() );
9798 m_aOutputStreams
.front().m_pStream
= pStream
;
9799 m_aOutputStreams
.front().m_aMapMode
= m_aMapMode
;
9801 if( !rTargetRect
.IsEmpty() )
9803 m_aOutputStreams
.front().m_aTargetRect
=
9804 lcl_convert( m_aGraphicsStack
.front().m_aMapMode
,
9806 getReferenceDevice(),
9808 Point aDelta
= m_aOutputStreams
.front().m_aTargetRect
.BottomLeft();
9809 long nPageHeight
= pointToPixel(m_aPages
[m_nCurrentPage
].getHeight());
9810 aDelta
.Y() = -(nPageHeight
- m_aOutputStreams
.front().m_aTargetRect
.Bottom());
9811 m_aMapMode
.SetOrigin( m_aMapMode
.GetOrigin() + aDelta
);
9814 // setup graphics state for independent object stream
9816 // force reemitting colors
9817 m_aCurrentPDFState
.m_aLineColor
= Color( COL_TRANSPARENT
);
9818 m_aCurrentPDFState
.m_aFillColor
= Color( COL_TRANSPARENT
);
9821 SvStream
* PDFWriterImpl::endRedirect()
9823 SvStream
* pStream
= nullptr;
9824 if( ! m_aOutputStreams
.empty() )
9826 pStream
= m_aOutputStreams
.front().m_pStream
;
9827 m_aMapMode
= m_aOutputStreams
.front().m_aMapMode
;
9828 m_aOutputStreams
.pop_front();
9833 m_aCurrentPDFState
.m_aLineColor
= Color( COL_TRANSPARENT
);
9834 m_aCurrentPDFState
.m_aFillColor
= Color( COL_TRANSPARENT
);
9836 // needed after pop() to set m_aCurrentPDFState
9837 updateGraphicsState(NOWRITE
);
9842 void PDFWriterImpl::beginTransparencyGroup()
9844 updateGraphicsState();
9845 if( m_aContext
.Version
>= PDFWriter::PDFVersion::PDF_1_4
)
9846 beginRedirect( new SvMemoryStream( 1024, 1024 ), tools::Rectangle() );
9849 void PDFWriterImpl::endTransparencyGroup( const tools::Rectangle
& rBoundingBox
, sal_uInt32 nTransparentPercent
)
9851 SAL_WARN_IF( nTransparentPercent
> 100, "vcl.pdfwriter", "invalid alpha value" );
9852 nTransparentPercent
= nTransparentPercent
% 100;
9854 if( m_aContext
.Version
>= PDFWriter::PDFVersion::PDF_1_4
)
9857 m_aTransparentObjects
.push_back( TransparencyEmit() );
9858 m_aTransparentObjects
.back().m_aBoundRect
= rBoundingBox
;
9859 // convert rectangle to default user space
9860 m_aPages
.back().convertRect( m_aTransparentObjects
.back().m_aBoundRect
);
9861 m_aTransparentObjects
.back().m_nObject
= createObject();
9862 m_aTransparentObjects
.back().m_fAlpha
= (double)(100-nTransparentPercent
) / 100.0;
9863 // get XObject's content stream
9864 m_aTransparentObjects
.back().m_pContentStream
= static_cast<SvMemoryStream
*>(endRedirect());
9865 m_aTransparentObjects
.back().m_nExtGStateObject
= createObject();
9867 OStringBuffer
aObjName( 16 );
9868 aObjName
.append( "Tr" );
9869 aObjName
.append( m_aTransparentObjects
.back().m_nObject
);
9870 OString
aTrName( aObjName
.makeStringAndClear() );
9871 aObjName
.append( "EGS" );
9872 aObjName
.append( m_aTransparentObjects
.back().m_nExtGStateObject
);
9873 OString
aExtName( aObjName
.makeStringAndClear() );
9875 OStringBuffer
aLine( 80 );
9877 aLine
.append( "q /" );
9878 aLine
.append( aExtName
);
9879 aLine
.append( " gs /" );
9880 aLine
.append( aTrName
);
9881 aLine
.append( " Do Q\n" );
9882 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9884 pushResource( ResXObject
, aTrName
, m_aTransparentObjects
.back().m_nObject
);
9885 pushResource( ResExtGState
, aExtName
, m_aTransparentObjects
.back().m_nExtGStateObject
);
9889 void PDFWriterImpl::drawRectangle( const tools::Rectangle
& rRect
)
9891 MARK( "drawRectangle" );
9893 updateGraphicsState();
9895 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
9896 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
9899 OStringBuffer
aLine( 40 );
9900 m_aPages
.back().appendRect( rRect
, aLine
);
9902 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
9903 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
9904 aLine
.append( " B*\n" );
9905 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
9906 aLine
.append( " S\n" );
9908 aLine
.append( " f*\n" );
9910 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9913 void PDFWriterImpl::drawRectangle( const tools::Rectangle
& rRect
, sal_uInt32 nHorzRound
, sal_uInt32 nVertRound
)
9915 MARK( "drawRectangle with rounded edges" );
9917 if( !nHorzRound
&& !nVertRound
)
9918 drawRectangle( rRect
);
9920 updateGraphicsState();
9922 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
9923 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
9926 if( nHorzRound
> (sal_uInt32
)rRect
.GetWidth()/2 )
9927 nHorzRound
= rRect
.GetWidth()/2;
9928 if( nVertRound
> (sal_uInt32
)rRect
.GetHeight()/2 )
9929 nVertRound
= rRect
.GetHeight()/2;
9932 const double kappa
= 0.5522847498;
9933 const sal_uInt32 kx
= (sal_uInt32
)((kappa
*(double)nHorzRound
)+0.5);
9934 const sal_uInt32 ky
= (sal_uInt32
)((kappa
*(double)nVertRound
)+0.5);
9936 aPoints
[1] = Point( rRect
.TopLeft().X() + nHorzRound
, rRect
.TopLeft().Y() );
9937 aPoints
[0] = Point( aPoints
[1].X() - kx
, aPoints
[1].Y() );
9938 aPoints
[2] = Point( rRect
.TopRight().X()+1 - nHorzRound
, aPoints
[1].Y() );
9939 aPoints
[3] = Point( aPoints
[2].X()+kx
, aPoints
[2].Y() );
9941 aPoints
[5] = Point( rRect
.TopRight().X()+1, rRect
.TopRight().Y()+nVertRound
);
9942 aPoints
[4] = Point( aPoints
[5].X(), aPoints
[5].Y()-ky
);
9943 aPoints
[6] = Point( aPoints
[5].X(), rRect
.BottomRight().Y()+1 - nVertRound
);
9944 aPoints
[7] = Point( aPoints
[6].X(), aPoints
[6].Y()+ky
);
9946 aPoints
[9] = Point( rRect
.BottomRight().X()+1-nHorzRound
, rRect
.BottomRight().Y()+1 );
9947 aPoints
[8] = Point( aPoints
[9].X()+kx
, aPoints
[9].Y() );
9948 aPoints
[10] = Point( rRect
.BottomLeft().X() + nHorzRound
, aPoints
[9].Y() );
9949 aPoints
[11] = Point( aPoints
[10].X()-kx
, aPoints
[10].Y() );
9951 aPoints
[13] = Point( rRect
.BottomLeft().X(), rRect
.BottomLeft().Y()+1-nVertRound
);
9952 aPoints
[12] = Point( aPoints
[13].X(), aPoints
[13].Y()+ky
);
9953 aPoints
[14] = Point( rRect
.TopLeft().X(), rRect
.TopLeft().Y()+nVertRound
);
9954 aPoints
[15] = Point( aPoints
[14].X(), aPoints
[14].Y()-ky
);
9956 OStringBuffer
aLine( 80 );
9957 m_aPages
.back().appendPoint( aPoints
[1], aLine
);
9958 aLine
.append( " m " );
9959 m_aPages
.back().appendPoint( aPoints
[2], aLine
);
9960 aLine
.append( " l " );
9961 m_aPages
.back().appendPoint( aPoints
[3], aLine
);
9962 aLine
.append( ' ' );
9963 m_aPages
.back().appendPoint( aPoints
[4], aLine
);
9964 aLine
.append( ' ' );
9965 m_aPages
.back().appendPoint( aPoints
[5], aLine
);
9966 aLine
.append( " c\n" );
9967 m_aPages
.back().appendPoint( aPoints
[6], aLine
);
9968 aLine
.append( " l " );
9969 m_aPages
.back().appendPoint( aPoints
[7], aLine
);
9970 aLine
.append( ' ' );
9971 m_aPages
.back().appendPoint( aPoints
[8], aLine
);
9972 aLine
.append( ' ' );
9973 m_aPages
.back().appendPoint( aPoints
[9], aLine
);
9974 aLine
.append( " c\n" );
9975 m_aPages
.back().appendPoint( aPoints
[10], aLine
);
9976 aLine
.append( " l " );
9977 m_aPages
.back().appendPoint( aPoints
[11], aLine
);
9978 aLine
.append( ' ' );
9979 m_aPages
.back().appendPoint( aPoints
[12], aLine
);
9980 aLine
.append( ' ' );
9981 m_aPages
.back().appendPoint( aPoints
[13], aLine
);
9982 aLine
.append( " c\n" );
9983 m_aPages
.back().appendPoint( aPoints
[14], aLine
);
9984 aLine
.append( " l " );
9985 m_aPages
.back().appendPoint( aPoints
[15], aLine
);
9986 aLine
.append( ' ' );
9987 m_aPages
.back().appendPoint( aPoints
[0], aLine
);
9988 aLine
.append( ' ' );
9989 m_aPages
.back().appendPoint( aPoints
[1], aLine
);
9990 aLine
.append( " c " );
9992 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
9993 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
9994 aLine
.append( "b*\n" );
9995 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
9996 aLine
.append( "s\n" );
9998 aLine
.append( "f*\n" );
10000 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10003 void PDFWriterImpl::drawEllipse( const tools::Rectangle
& rRect
)
10005 MARK( "drawEllipse" );
10007 updateGraphicsState();
10009 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
10010 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
10014 const double kappa
= 0.5522847498;
10015 const sal_uInt32 kx
= (sal_uInt32
)((kappa
*(double)rRect
.GetWidth()/2.0)+0.5);
10016 const sal_uInt32 ky
= (sal_uInt32
)((kappa
*(double)rRect
.GetHeight()/2.0)+0.5);
10018 aPoints
[1] = Point( rRect
.TopLeft().X() + rRect
.GetWidth()/2, rRect
.TopLeft().Y() );
10019 aPoints
[0] = Point( aPoints
[1].X() - kx
, aPoints
[1].Y() );
10020 aPoints
[2] = Point( aPoints
[1].X() + kx
, aPoints
[1].Y() );
10022 aPoints
[4] = Point( rRect
.TopRight().X()+1, rRect
.TopRight().Y() + rRect
.GetHeight()/2 );
10023 aPoints
[3] = Point( aPoints
[4].X(), aPoints
[4].Y() - ky
);
10024 aPoints
[5] = Point( aPoints
[4].X(), aPoints
[4].Y() + ky
);
10026 aPoints
[7] = Point( rRect
.BottomLeft().X() + rRect
.GetWidth()/2, rRect
.BottomLeft().Y()+1 );
10027 aPoints
[6] = Point( aPoints
[7].X() + kx
, aPoints
[7].Y() );
10028 aPoints
[8] = Point( aPoints
[7].X() - kx
, aPoints
[7].Y() );
10030 aPoints
[10] = Point( rRect
.TopLeft().X(), rRect
.TopLeft().Y() + rRect
.GetHeight()/2 );
10031 aPoints
[9] = Point( aPoints
[10].X(), aPoints
[10].Y() + ky
);
10032 aPoints
[11] = Point( aPoints
[10].X(), aPoints
[10].Y() - ky
);
10034 OStringBuffer
aLine( 80 );
10035 m_aPages
.back().appendPoint( aPoints
[1], aLine
);
10036 aLine
.append( " m " );
10037 m_aPages
.back().appendPoint( aPoints
[2], aLine
);
10038 aLine
.append( ' ' );
10039 m_aPages
.back().appendPoint( aPoints
[3], aLine
);
10040 aLine
.append( ' ' );
10041 m_aPages
.back().appendPoint( aPoints
[4], aLine
);
10042 aLine
.append( " c\n" );
10043 m_aPages
.back().appendPoint( aPoints
[5], aLine
);
10044 aLine
.append( ' ' );
10045 m_aPages
.back().appendPoint( aPoints
[6], aLine
);
10046 aLine
.append( ' ' );
10047 m_aPages
.back().appendPoint( aPoints
[7], aLine
);
10048 aLine
.append( " c\n" );
10049 m_aPages
.back().appendPoint( aPoints
[8], aLine
);
10050 aLine
.append( ' ' );
10051 m_aPages
.back().appendPoint( aPoints
[9], aLine
);
10052 aLine
.append( ' ' );
10053 m_aPages
.back().appendPoint( aPoints
[10], aLine
);
10054 aLine
.append( " c\n" );
10055 m_aPages
.back().appendPoint( aPoints
[11], aLine
);
10056 aLine
.append( ' ' );
10057 m_aPages
.back().appendPoint( aPoints
[0], aLine
);
10058 aLine
.append( ' ' );
10059 m_aPages
.back().appendPoint( aPoints
[1], aLine
);
10060 aLine
.append( " c " );
10062 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
10063 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
10064 aLine
.append( "b*\n" );
10065 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
10066 aLine
.append( "s\n" );
10068 aLine
.append( "f*\n" );
10070 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10073 static double calcAngle( const tools::Rectangle
& rRect
, const Point
& rPoint
)
10075 Point
aOrigin((rRect
.Left()+rRect
.Right()+1)/2,
10076 (rRect
.Top()+rRect
.Bottom()+1)/2);
10077 Point aPoint
= rPoint
- aOrigin
;
10079 double fX
= (double)aPoint
.X();
10080 double fY
= (double)-aPoint
.Y();
10082 if ((rRect
.GetHeight() == 0) || (rRect
.GetWidth() == 0))
10083 throw o3tl::divide_by_zero();
10085 if( rRect
.GetWidth() > rRect
.GetHeight() )
10086 fY
= fY
*((double)rRect
.GetWidth()/(double)rRect
.GetHeight());
10087 else if( rRect
.GetHeight() > rRect
.GetWidth() )
10088 fX
= fX
*((double)rRect
.GetHeight()/(double)rRect
.GetWidth());
10089 return atan2( fY
, fX
);
10092 void PDFWriterImpl::drawArc( const tools::Rectangle
& rRect
, const Point
& rStart
, const Point
& rStop
, bool bWithPie
, bool bWithChord
)
10096 updateGraphicsState();
10098 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
10099 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
10102 // calculate start and stop angles
10103 const double fStartAngle
= calcAngle( rRect
, rStart
);
10104 double fStopAngle
= calcAngle( rRect
, rStop
);
10105 while( fStopAngle
< fStartAngle
)
10106 fStopAngle
+= 2.0*M_PI
;
10107 const int nFragments
= (int)((fStopAngle
-fStartAngle
)/(M_PI
/2.0))+1;
10108 const double fFragmentDelta
= (fStopAngle
-fStartAngle
)/(double)nFragments
;
10109 const double kappa
= fabs( 4.0 * (1.0-cos(fFragmentDelta
/2.0))/sin(fFragmentDelta
/2.0) / 3.0);
10110 const double halfWidth
= (double)rRect
.GetWidth()/2.0;
10111 const double halfHeight
= (double)rRect
.GetHeight()/2.0;
10113 const Point
aCenter( (rRect
.Left()+rRect
.Right()+1)/2,
10114 (rRect
.Top()+rRect
.Bottom()+1)/2 );
10116 OStringBuffer
aLine( 30*nFragments
);
10117 Point
aPoint( (int)(halfWidth
* cos(fStartAngle
) ),
10118 -(int)(halfHeight
* sin(fStartAngle
) ) );
10120 m_aPages
.back().appendPoint( aPoint
, aLine
);
10121 aLine
.append( " m " );
10122 if( !basegfx::fTools::equal(fStartAngle
, fStopAngle
) )
10124 for( int i
= 0; i
< nFragments
; i
++ )
10126 const double fStartFragment
= fStartAngle
+ (double)i
*fFragmentDelta
;
10127 const double fStopFragment
= fStartFragment
+ fFragmentDelta
;
10128 aPoint
= Point( (int)(halfWidth
* (cos(fStartFragment
) - kappa
*sin(fStartFragment
) ) ),
10129 -(int)(halfHeight
* (sin(fStartFragment
) + kappa
*cos(fStartFragment
) ) ) );
10131 m_aPages
.back().appendPoint( aPoint
, aLine
);
10132 aLine
.append( ' ' );
10134 aPoint
= Point( (int)(halfWidth
* (cos(fStopFragment
) + kappa
*sin(fStopFragment
) ) ),
10135 -(int)(halfHeight
* (sin(fStopFragment
) - kappa
*cos(fStopFragment
) ) ) );
10137 m_aPages
.back().appendPoint( aPoint
, aLine
);
10138 aLine
.append( ' ' );
10140 aPoint
= Point( (int)(halfWidth
* cos(fStopFragment
) ),
10141 -(int)(halfHeight
* sin(fStopFragment
) ) );
10143 m_aPages
.back().appendPoint( aPoint
, aLine
);
10144 aLine
.append( " c\n" );
10147 if( bWithChord
|| bWithPie
)
10151 m_aPages
.back().appendPoint( aCenter
, aLine
);
10152 aLine
.append( " l " );
10154 aLine
.append( "h " );
10156 if( ! bWithChord
&& ! bWithPie
)
10157 aLine
.append( "S\n" );
10158 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
10159 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
10160 aLine
.append( "B*\n" );
10161 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
10162 aLine
.append( "S\n" );
10164 aLine
.append( "f*\n" );
10166 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10169 void PDFWriterImpl::drawPolyLine( const tools::Polygon
& rPoly
)
10171 MARK( "drawPolyLine" );
10173 sal_uInt16 nPoints
= rPoly
.GetSize();
10177 updateGraphicsState();
10179 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) )
10182 OStringBuffer
aLine( 20 * nPoints
);
10183 m_aPages
.back().appendPolygon( rPoly
, aLine
, rPoly
[0] == rPoly
[nPoints
-1] );
10184 aLine
.append( "S\n" );
10186 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10189 void PDFWriterImpl::drawPolyLine( const tools::Polygon
& rPoly
, const LineInfo
& rInfo
)
10191 MARK( "drawPolyLine with LineInfo" );
10193 updateGraphicsState();
10195 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) )
10198 OStringBuffer aLine
;
10199 aLine
.append( "q " );
10200 if( m_aPages
.back().appendLineInfo( rInfo
, aLine
) )
10202 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10203 drawPolyLine( rPoly
);
10204 writeBuffer( "Q\n", 2 );
10208 PDFWriter::ExtLineInfo aInfo
;
10209 convertLineInfoToExtLineInfo( rInfo
, aInfo
);
10210 drawPolyLine( rPoly
, aInfo
);
10214 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo
& rIn
, PDFWriter::ExtLineInfo
& rOut
)
10216 SAL_WARN_IF( rIn
.GetStyle() != LineStyle::Dash
, "vcl.pdfwriter", "invalid conversion" );
10217 rOut
.m_fLineWidth
= rIn
.GetWidth();
10218 rOut
.m_fTransparency
= 0.0;
10219 rOut
.m_eCap
= PDFWriter::capButt
;
10220 rOut
.m_eJoin
= PDFWriter::joinMiter
;
10221 rOut
.m_fMiterLimit
= 10;
10222 rOut
.m_aDashArray
.clear();
10224 // add DashDot to DashArray
10225 const int nDashes
= rIn
.GetDashCount();
10226 const int nDashLen
= rIn
.GetDashLen();
10227 const int nDistance
= rIn
.GetDistance();
10229 for( int n
= 0; n
< nDashes
; n
++ )
10231 rOut
.m_aDashArray
.push_back( nDashLen
);
10232 rOut
.m_aDashArray
.push_back( nDistance
);
10234 const int nDots
= rIn
.GetDotCount();
10235 const int nDotLen
= rIn
.GetDotLen();
10237 for( int n
= 0; n
< nDots
; n
++ )
10239 rOut
.m_aDashArray
.push_back( nDotLen
);
10240 rOut
.m_aDashArray
.push_back( nDistance
);
10244 switch(rIn
.GetLineJoin())
10246 case basegfx::B2DLineJoin::Bevel
:
10248 rOut
.m_eJoin
= PDFWriter::joinBevel
;
10251 // Pdf has no 'none' lineJoin, default is miter
10252 case basegfx::B2DLineJoin::NONE
:
10253 case basegfx::B2DLineJoin::Miter
:
10255 rOut
.m_eJoin
= PDFWriter::joinMiter
;
10258 case basegfx::B2DLineJoin::Round
:
10260 rOut
.m_eJoin
= PDFWriter::joinRound
;
10266 switch(rIn
.GetLineCap())
10268 default: /* css::drawing::LineCap_BUTT */
10270 rOut
.m_eCap
= PDFWriter::capButt
;
10273 case css::drawing::LineCap_ROUND
:
10275 rOut
.m_eCap
= PDFWriter::capRound
;
10278 case css::drawing::LineCap_SQUARE
:
10280 rOut
.m_eCap
= PDFWriter::capSquare
;
10286 void PDFWriterImpl::drawPolyLine( const tools::Polygon
& rPoly
, const PDFWriter::ExtLineInfo
& rInfo
)
10288 MARK( "drawPolyLine with ExtLineInfo" );
10290 updateGraphicsState();
10292 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) )
10295 if( rInfo
.m_fTransparency
>= 1.0 )
10298 if( rInfo
.m_fTransparency
!= 0.0 )
10299 beginTransparencyGroup();
10301 OStringBuffer aLine
;
10302 aLine
.append( "q " );
10303 m_aPages
.back().appendMappedLength( rInfo
.m_fLineWidth
, aLine
);
10304 aLine
.append( " w" );
10305 if( rInfo
.m_aDashArray
.size() < 10 ) // implementation limit of acrobat reader
10307 switch( rInfo
.m_eCap
)
10310 case PDFWriter::capButt
: aLine
.append( " 0 J" );break;
10311 case PDFWriter::capRound
: aLine
.append( " 1 J" );break;
10312 case PDFWriter::capSquare
: aLine
.append( " 2 J" );break;
10314 switch( rInfo
.m_eJoin
)
10317 case PDFWriter::joinMiter
:
10319 double fLimit
= rInfo
.m_fMiterLimit
;
10320 if( rInfo
.m_fLineWidth
< rInfo
.m_fMiterLimit
)
10321 fLimit
= fLimit
/ rInfo
.m_fLineWidth
;
10324 aLine
.append( " 0 j " );
10325 appendDouble( fLimit
, aLine
);
10326 aLine
.append( " M" );
10329 case PDFWriter::joinRound
: aLine
.append( " 1 j" );break;
10330 case PDFWriter::joinBevel
: aLine
.append( " 2 j" );break;
10332 if( rInfo
.m_aDashArray
.size() > 0 )
10334 aLine
.append( " [ " );
10335 for( std::vector
<double>::const_iterator it
= rInfo
.m_aDashArray
.begin();
10336 it
!= rInfo
.m_aDashArray
.end(); ++it
)
10338 m_aPages
.back().appendMappedLength( *it
, aLine
);
10339 aLine
.append( ' ' );
10341 aLine
.append( "] 0 d" );
10343 aLine
.append( "\n" );
10344 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10345 drawPolyLine( rPoly
);
10349 basegfx::B2DPolygon
aPoly(rPoly
.getB2DPolygon());
10350 basegfx::B2DPolyPolygon aPolyPoly
;
10352 basegfx::tools::applyLineDashing(aPoly
, rInfo
.m_aDashArray
, &aPolyPoly
);
10354 // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
10355 // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
10356 // this line needs to be removed and the loop below adapted accordingly
10357 aPolyPoly
= basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly
);
10359 const sal_uInt32
nPolygonCount(aPolyPoly
.count());
10361 for( sal_uInt32 nPoly
= 0; nPoly
< nPolygonCount
; nPoly
++ )
10363 aLine
.append( (nPoly
!= 0 && (nPoly
& 7) == 0) ? "\n" : " " );
10364 aPoly
= aPolyPoly
.getB2DPolygon( nPoly
);
10365 const sal_uInt32
nPointCount(aPoly
.count());
10369 const sal_uInt32
nEdgeCount(aPoly
.isClosed() ? nPointCount
: nPointCount
- 1);
10370 basegfx::B2DPoint
aCurrent(aPoly
.getB2DPoint(0));
10372 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
10375 aLine
.append( " " );
10376 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
10377 const basegfx::B2DPoint
aNext(aPoly
.getB2DPoint(nNextIndex
));
10379 m_aPages
.back().appendPoint( Point( FRound(aCurrent
.getX()),
10380 FRound(aCurrent
.getY()) ),
10382 aLine
.append( " m " );
10383 m_aPages
.back().appendPoint( Point( FRound(aNext
.getX()),
10384 FRound(aNext
.getY()) ),
10386 aLine
.append( " l" );
10388 // prepare next edge
10393 aLine
.append( " S " );
10394 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10396 writeBuffer( "Q\n", 2 );
10398 if( rInfo
.m_fTransparency
!= 0.0 )
10400 // FIXME: actually this may be incorrect with bezier polygons
10401 tools::Rectangle
aBoundRect( rPoly
.GetBoundRect() );
10402 // avoid clipping with thick lines
10403 if( rInfo
.m_fLineWidth
> 0.0 )
10405 sal_Int32 nLW
= sal_Int32(rInfo
.m_fLineWidth
);
10406 aBoundRect
.Top() -= nLW
;
10407 aBoundRect
.Left() -= nLW
;
10408 aBoundRect
.Right() += nLW
;
10409 aBoundRect
.Bottom() += nLW
;
10411 endTransparencyGroup( aBoundRect
, (sal_uInt16
)(100.0*rInfo
.m_fTransparency
) );
10415 void PDFWriterImpl::drawPixel( const Point
& rPoint
, const Color
& rColor
)
10417 MARK( "drawPixel" );
10419 Color aColor
= ( rColor
== Color( COL_TRANSPARENT
) ? m_aGraphicsStack
.front().m_aLineColor
: rColor
);
10421 if( aColor
== Color( COL_TRANSPARENT
) )
10424 // pixels are drawn in line color, so have to set
10425 // the nonstroking color to line color
10426 Color aOldFillColor
= m_aGraphicsStack
.front().m_aFillColor
;
10427 setFillColor( aColor
);
10429 updateGraphicsState();
10431 OStringBuffer
aLine( 20 );
10432 m_aPages
.back().appendPoint( rPoint
, aLine
);
10433 aLine
.append( ' ' );
10434 appendDouble( 1.0/double(getReferenceDevice()->GetDPIX()), aLine
);
10435 aLine
.append( ' ' );
10436 appendDouble( 1.0/double(getReferenceDevice()->GetDPIY()), aLine
);
10437 aLine
.append( " re f\n" );
10438 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10440 setFillColor( aOldFillColor
);
10443 void PDFWriterImpl::writeTransparentObject( TransparencyEmit
& rObject
)
10445 CHECK_RETURN2( updateObject( rObject
.m_nObject
) );
10447 bool bFlateFilter
= compressStream( rObject
.m_pContentStream
);
10448 rObject
.m_pContentStream
->Seek( STREAM_SEEK_TO_END
);
10449 sal_uLong nSize
= rObject
.m_pContentStream
->Tell();
10450 rObject
.m_pContentStream
->Seek( STREAM_SEEK_TO_BEGIN
);
10451 #if OSL_DEBUG_LEVEL > 1
10452 emitComment( "PDFWriterImpl::writeTransparentObject" );
10454 OStringBuffer
aLine( 512 );
10455 CHECK_RETURN2( updateObject( rObject
.m_nObject
) );
10456 aLine
.append( rObject
.m_nObject
);
10457 aLine
.append( " 0 obj\n"
10458 "<</Type/XObject\n"
10461 appendFixedInt( rObject
.m_aBoundRect
.Left(), aLine
);
10462 aLine
.append( ' ' );
10463 appendFixedInt( rObject
.m_aBoundRect
.Top(), aLine
);
10464 aLine
.append( ' ' );
10465 appendFixedInt( rObject
.m_aBoundRect
.Right(), aLine
);
10466 aLine
.append( ' ' );
10467 appendFixedInt( rObject
.m_aBoundRect
.Bottom()+1, aLine
);
10468 aLine
.append( " ]\n" );
10469 if( ! rObject
.m_pSoftMaskStream
)
10471 if( ! m_bIsPDF_A1
)
10473 aLine
.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
10476 /* #i42884# the PDF reference recommends that each Form XObject
10477 * should have a resource dict; alas if that is the same object
10478 * as the one of the page it triggers an endless recursion in
10479 * acroread 5 (6 and up have that fixed). Since we have only one
10480 * resource dict anyway, let's use the one from the page by NOT
10481 * emitting a Resources entry.
10484 aLine
.append( "/Length " );
10485 aLine
.append( (sal_Int32
)(nSize
) );
10486 aLine
.append( "\n" );
10488 aLine
.append( "/Filter/FlateDecode\n" );
10489 aLine
.append( ">>\n"
10491 CHECK_RETURN2( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10492 checkAndEnableStreamEncryption( rObject
.m_nObject
);
10493 CHECK_RETURN2( writeBuffer( rObject
.m_pContentStream
->GetData(), nSize
) );
10494 disableStreamEncryption();
10495 aLine
.setLength( 0 );
10499 CHECK_RETURN2( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10501 // write ExtGState dict for this XObject
10502 aLine
.setLength( 0 );
10503 aLine
.append( rObject
.m_nExtGStateObject
);
10504 aLine
.append( " 0 obj\n"
10506 if( ! rObject
.m_pSoftMaskStream
)
10510 aLine
.append( "/CA 1.0/ca 1.0" );
10511 m_aErrors
.insert( PDFWriter::Warning_Transparency_Omitted_PDFA
);
10515 aLine
.append( "/CA " );
10516 appendDouble( rObject
.m_fAlpha
, aLine
);
10519 appendDouble( rObject
.m_fAlpha
, aLine
);
10521 aLine
.append( "\n" );
10527 aLine
.append( "/SMask/None" );
10528 m_aErrors
.insert( PDFWriter::Warning_Transparency_Omitted_PDFA
);
10532 rObject
.m_pSoftMaskStream
->Seek( STREAM_SEEK_TO_END
);
10533 sal_Int32 nMaskSize
= (sal_Int32
)rObject
.m_pSoftMaskStream
->Tell();
10534 rObject
.m_pSoftMaskStream
->Seek( STREAM_SEEK_TO_BEGIN
);
10535 sal_Int32 nMaskObject
= createObject();
10536 aLine
.append( "/SMask<</Type/Mask/S/Luminosity/G " );
10537 aLine
.append( nMaskObject
);
10538 aLine
.append( " 0 R>>\n" );
10540 OStringBuffer aMask
;
10541 aMask
.append( nMaskObject
);
10542 aMask
.append( " 0 obj\n"
10543 "<</Type/XObject\n"
10546 appendFixedInt( rObject
.m_aBoundRect
.Left(), aMask
);
10547 aMask
.append( ' ' );
10548 appendFixedInt( rObject
.m_aBoundRect
.Top(), aMask
);
10549 aMask
.append( ' ' );
10550 appendFixedInt( rObject
.m_aBoundRect
.Right(), aMask
);
10551 aMask
.append( ' ' );
10552 appendFixedInt( rObject
.m_aBoundRect
.Bottom()+1, aMask
);
10553 aMask
.append( "]\n" );
10555 /* #i42884# see above */
10556 aMask
.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
10557 aMask
.append( "/Length " );
10558 aMask
.append( nMaskSize
);
10559 aMask
.append( ">>\n"
10561 CHECK_RETURN2( updateObject( nMaskObject
) );
10562 checkAndEnableStreamEncryption( nMaskObject
);
10563 CHECK_RETURN2( writeBuffer( aMask
.getStr(), aMask
.getLength() ) );
10564 CHECK_RETURN2( writeBuffer( rObject
.m_pSoftMaskStream
->GetData(), nMaskSize
) );
10565 disableStreamEncryption();
10566 aMask
.setLength( 0 );
10567 aMask
.append( "\nendstream\n"
10569 CHECK_RETURN2( writeBuffer( aMask
.getStr(), aMask
.getLength() ) );
10572 aLine
.append( ">>\n"
10574 CHECK_RETURN2( updateObject( rObject
.m_nExtGStateObject
) );
10575 CHECK_RETURN2( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10578 bool PDFWriterImpl::writeGradientFunction( GradientEmit
& rObject
)
10580 // LO internal gradient -> PDF shading type:
10581 // * GradientStyle::Linear: axial shading, using sampled-function with 2 samples
10582 // [t=0:colorStart, t=1:colorEnd]
10583 // * GradientStyle::Axial: axial shading, using sampled-function with 3 samples
10584 // [t=0:colorEnd, t=0.5:colorStart, t=1:colorEnd]
10585 // * other styles: function shading with aSize.Width() * aSize.Height() samples
10586 sal_Int32 nFunctionObject
= createObject();
10587 CHECK_RETURN( updateObject( nFunctionObject
) );
10589 ScopedVclPtrInstance
< VirtualDevice
> aDev
;
10590 aDev
->SetOutputSizePixel( rObject
.m_aSize
);
10591 aDev
->SetMapMode( MapMode( MapUnit::MapPixel
) );
10592 if( m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
)
10593 aDev
->SetDrawMode( aDev
->GetDrawMode() |
10594 ( DrawModeFlags::GrayLine
| DrawModeFlags::GrayFill
| DrawModeFlags::GrayText
|
10595 DrawModeFlags::GrayBitmap
| DrawModeFlags::GrayGradient
) );
10596 aDev
->DrawGradient( tools::Rectangle( Point( 0, 0 ), rObject
.m_aSize
), rObject
.m_aGradient
);
10598 Bitmap aSample
= aDev
->GetBitmap( Point( 0, 0 ), rObject
.m_aSize
);
10599 Bitmap::ScopedReadAccess
pAccess(aSample
);
10601 Size aSize
= aSample
.GetSizePixel();
10603 sal_Int32 nStreamLengthObject
= createObject();
10604 #if OSL_DEBUG_LEVEL > 1
10605 emitComment( "PDFWriterImpl::writeGradientFunction" );
10607 OStringBuffer
aLine( 120 );
10608 aLine
.append( nFunctionObject
);
10609 aLine
.append( " 0 obj\n"
10610 "<</FunctionType 0\n");
10611 switch (rObject
.m_aGradient
.GetStyle())
10613 case GradientStyle::Linear
:
10614 case GradientStyle::Axial
:
10615 aLine
.append("/Domain[ 0 1]\n");
10618 aLine
.append("/Domain[ 0 1 0 1]\n");
10620 aLine
.append("/Size[ " );
10621 switch (rObject
.m_aGradient
.GetStyle())
10623 case GradientStyle::Linear
:
10626 case GradientStyle::Axial
:
10630 aLine
.append( (sal_Int32
)aSize
.Width() );
10631 aLine
.append( ' ' );
10632 aLine
.append( (sal_Int32
)aSize
.Height() );
10634 aLine
.append( " ]\n"
10635 "/BitsPerSample 8\n"
10636 "/Range[ 0 1 0 1 0 1 ]\n"
10639 aLine
.append( nStreamLengthObject
);
10640 if (!g_bDebugDisableCompression
)
10641 aLine
.append( " 0 R\n"
10642 "/Filter/FlateDecode"
10646 aLine
.append( " 0 R\n"
10649 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10651 sal_uInt64 nStartStreamPos
= 0;
10652 CHECK_RETURN( (osl::File::E_None
== m_aFile
.getPos(nStartStreamPos
)) );
10654 checkAndEnableStreamEncryption( nFunctionObject
);
10655 beginCompression();
10657 switch (rObject
.m_aGradient
.GetStyle())
10659 case GradientStyle::Axial
:
10660 aCol
[0] = rObject
.m_aGradient
.GetEndColor().GetRed();
10661 aCol
[1] = rObject
.m_aGradient
.GetEndColor().GetGreen();
10662 aCol
[2] = rObject
.m_aGradient
.GetEndColor().GetBlue();
10663 CHECK_RETURN( writeBuffer( aCol
, 3 ) );
10665 case GradientStyle::Linear
:
10667 aCol
[0] = rObject
.m_aGradient
.GetStartColor().GetRed();
10668 aCol
[1] = rObject
.m_aGradient
.GetStartColor().GetGreen();
10669 aCol
[2] = rObject
.m_aGradient
.GetStartColor().GetBlue();
10670 CHECK_RETURN( writeBuffer( aCol
, 3 ) );
10672 aCol
[0] = rObject
.m_aGradient
.GetEndColor().GetRed();
10673 aCol
[1] = rObject
.m_aGradient
.GetEndColor().GetGreen();
10674 aCol
[2] = rObject
.m_aGradient
.GetEndColor().GetBlue();
10675 CHECK_RETURN( writeBuffer( aCol
, 3 ) );
10679 for( int y
= aSize
.Height()-1; y
>= 0; y
-- )
10681 for( long x
= 0; x
< aSize
.Width(); x
++ )
10683 BitmapColor aColor
= pAccess
->GetColor( y
, x
);
10684 aCol
[0] = aColor
.GetRed();
10685 aCol
[1] = aColor
.GetGreen();
10686 aCol
[2] = aColor
.GetBlue();
10687 CHECK_RETURN( writeBuffer( aCol
, 3 ) );
10692 disableStreamEncryption();
10694 sal_uInt64 nEndStreamPos
= 0;
10695 CHECK_RETURN( (osl::File::E_None
== m_aFile
.getPos(nEndStreamPos
)) );
10697 aLine
.setLength( 0 );
10698 aLine
.append( "\nendstream\nendobj\n\n" );
10699 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10701 // write stream length
10702 CHECK_RETURN( updateObject( nStreamLengthObject
) );
10703 aLine
.setLength( 0 );
10704 aLine
.append( nStreamLengthObject
);
10705 aLine
.append( " 0 obj\n" );
10706 aLine
.append( (sal_Int64
)(nEndStreamPos
-nStartStreamPos
) );
10707 aLine
.append( "\nendobj\n\n" );
10708 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10710 CHECK_RETURN( updateObject( rObject
.m_nObject
) );
10711 aLine
.setLength( 0 );
10712 aLine
.append( rObject
.m_nObject
);
10713 aLine
.append( " 0 obj\n");
10714 switch (rObject
.m_aGradient
.GetStyle())
10716 case GradientStyle::Linear
:
10717 case GradientStyle::Axial
:
10718 aLine
.append("<</ShadingType 2\n");
10721 aLine
.append("<</ShadingType 1\n");
10723 aLine
.append("/ColorSpace/DeviceRGB\n"
10724 "/AntiAlias true\n");
10726 // Determination of shading axis
10727 // See: OutputDevice::ImplDrawLinearGradient for reference
10728 tools::Rectangle aRect
;
10729 aRect
.Left() = aRect
.Top() = 0;
10730 aRect
.Right() = aSize
.Width();
10731 aRect
.Bottom() = aSize
.Height();
10733 tools::Rectangle aBoundRect
;
10735 sal_uInt16 nAngle
= rObject
.m_aGradient
.GetAngle() % 3600;
10736 rObject
.m_aGradient
.GetBoundRect( aRect
, aBoundRect
, aCenter
);
10738 const bool bLinear
= (rObject
.m_aGradient
.GetStyle() == GradientStyle::Linear
);
10739 double fBorder
= aBoundRect
.GetHeight() * rObject
.m_aGradient
.GetBorder() / 100.0;
10745 aBoundRect
.Bottom() -= fBorder
;
10748 aBoundRect
.Top() += fBorder
;
10751 switch (rObject
.m_aGradient
.GetStyle())
10753 case GradientStyle::Linear
:
10754 case GradientStyle::Axial
:
10756 aLine
.append("/Domain[ 0 1 ]\n"
10758 tools::Polygon
aPoly( 2 );
10759 aPoly
[0] = aBoundRect
.BottomCenter();
10760 aPoly
[1] = aBoundRect
.TopCenter();
10761 aPoly
.Rotate( aCenter
, 3600 - nAngle
);
10763 aLine
.append( (sal_Int32
) aPoly
[0].X() );
10764 aLine
.append( " " );
10765 aLine
.append( (sal_Int32
) aPoly
[0].Y() );
10766 aLine
.append( " " );
10767 aLine
.append( (sal_Int32
) aPoly
[1].X());
10768 aLine
.append( " ");
10769 aLine
.append( (sal_Int32
) aPoly
[1].Y());
10770 aLine
.append( " ]\n");
10771 aLine
.append("/Extend [true true]\n");
10775 aLine
.append("/Domain[ 0 1 0 1 ]\n"
10777 aLine
.append( (sal_Int32
)aSize
.Width() );
10778 aLine
.append( " 0 0 " );
10779 aLine
.append( (sal_Int32
)aSize
.Height() );
10780 aLine
.append( " 0 0 ]\n");
10782 aLine
.append("/Function " );
10783 aLine
.append( nFunctionObject
);
10784 aLine
.append( " 0 R\n"
10787 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10792 void PDFWriterImpl::writeJPG( JPGEmit
& rObject
)
10794 if (rObject
.m_aReferenceXObject
.m_aPDFData
.hasElements() && !m_aContext
.UseReferenceXObject
)
10796 writeReferenceXObject(rObject
.m_aReferenceXObject
);
10800 CHECK_RETURN2( rObject
.m_pStream
);
10801 CHECK_RETURN2( updateObject( rObject
.m_nObject
) );
10803 sal_Int32 nLength
= 0;
10804 rObject
.m_pStream
->Seek( STREAM_SEEK_TO_END
);
10805 nLength
= rObject
.m_pStream
->Tell();
10806 rObject
.m_pStream
->Seek( STREAM_SEEK_TO_BEGIN
);
10808 sal_Int32 nMaskObject
= 0;
10809 if( !!rObject
.m_aMask
)
10811 if( rObject
.m_aMask
.GetBitCount() == 1 ||
10812 ( rObject
.m_aMask
.GetBitCount() == 8 && m_aContext
.Version
>= PDFWriter::PDFVersion::PDF_1_4
&& !m_bIsPDF_A1
)
10815 nMaskObject
= createObject();
10817 else if( m_bIsPDF_A1
)
10818 m_aErrors
.insert( PDFWriter::Warning_Transparency_Omitted_PDFA
);
10819 else if( m_aContext
.Version
< PDFWriter::PDFVersion::PDF_1_4
)
10820 m_aErrors
.insert( PDFWriter::Warning_Transparency_Omitted_PDF13
);
10823 #if OSL_DEBUG_LEVEL > 1
10824 emitComment( "PDFWriterImpl::writeJPG" );
10827 OStringBuffer
aLine(200);
10828 aLine
.append( rObject
.m_nObject
);
10829 aLine
.append( " 0 obj\n"
10830 "<</Type/XObject/Subtype/Image/Width " );
10831 aLine
.append( (sal_Int32
)rObject
.m_aID
.m_aPixelSize
.Width() );
10832 aLine
.append( " /Height " );
10833 aLine
.append( (sal_Int32
)rObject
.m_aID
.m_aPixelSize
.Height() );
10834 aLine
.append( " /BitsPerComponent 8 " );
10835 if( rObject
.m_bTrueColor
)
10836 aLine
.append( "/ColorSpace/DeviceRGB" );
10838 aLine
.append( "/ColorSpace/DeviceGray" );
10839 aLine
.append( "/Filter/DCTDecode/Length " );
10840 aLine
.append( nLength
);
10843 aLine
.append( rObject
.m_aMask
.GetBitCount() == 1 ? " /Mask " : " /SMask " );
10844 aLine
.append( nMaskObject
);
10845 aLine
.append( " 0 R " );
10847 aLine
.append( ">>\nstream\n" );
10848 CHECK_RETURN2( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10850 checkAndEnableStreamEncryption( rObject
.m_nObject
);
10851 CHECK_RETURN2( writeBuffer( rObject
.m_pStream
->GetData(), nLength
) );
10852 disableStreamEncryption();
10854 aLine
.setLength( 0 );
10855 aLine
.append( "\nendstream\nendobj\n\n" );
10856 CHECK_RETURN2( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10861 aEmit
.m_nObject
= nMaskObject
;
10862 if( rObject
.m_aMask
.GetBitCount() == 1 )
10863 aEmit
.m_aBitmap
= BitmapEx( rObject
.m_aMask
, rObject
.m_aMask
);
10864 else if( rObject
.m_aMask
.GetBitCount() == 8 )
10865 aEmit
.m_aBitmap
= BitmapEx( rObject
.m_aMask
, AlphaMask( rObject
.m_aMask
) );
10866 writeBitmapObject( aEmit
, true );
10869 writeReferenceXObject(rObject
.m_aReferenceXObject
);
10872 sal_Int32
PDFWriterImpl::copyExternalResource(SvMemoryStream
& rDocBuffer
, filter::PDFObjectElement
& rObject
, std::map
<sal_Int32
, sal_Int32
>& rCopiedResources
)
10874 auto it
= rCopiedResources
.find(rObject
.GetObjectValue());
10875 if (it
!= rCopiedResources
.end())
10876 // This resource was already copied once, nothing to do.
10879 sal_Int32 nObject
= createObject();
10880 // Remember what is the ID of this object in our output.
10881 rCopiedResources
[rObject
.GetObjectValue()] = nObject
;
10882 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::copyExternalResource: " << rObject
.GetObjectValue() << " -> " << nObject
);
10884 OStringBuffer aLine
;
10885 aLine
.append(nObject
);
10886 aLine
.append(" 0 obj\n");
10887 if (rObject
.GetDictionary())
10889 aLine
.append("<<");
10891 // Complex case: can't copy the dictionary byte array as is, as it may contain references.
10892 bool bDone
= false;
10893 sal_uInt64 nCopyStart
= 0;
10894 for (auto pReference
: rObject
.GetDictionaryReferences())
10898 filter::PDFObjectElement
* pReferenced
= pReference
->LookupObject();
10901 // Copy the referenced object.
10902 sal_Int32 nRef
= copyExternalResource(rDocBuffer
, *pReferenced
, rCopiedResources
);
10904 sal_uInt64 nReferenceStart
= pReference
->GetObjectElement().GetLocation();
10905 sal_uInt64 nReferenceEnd
= pReference
->GetOffset();
10906 sal_uInt64 nOffset
= 0;
10907 if (nCopyStart
== 0)
10908 // Dict start -> reference start.
10909 nOffset
= rObject
.GetDictionaryOffset();
10911 // Previous reference end -> reference start.
10912 nOffset
= nCopyStart
;
10913 aLine
.append(static_cast<const sal_Char
*>(rDocBuffer
.GetData()) + nOffset
, nReferenceStart
- nOffset
);
10914 // Write the updated reference.
10916 aLine
.append(nRef
);
10917 aLine
.append(" 0 R");
10918 // Start copying here next time.
10919 nCopyStart
= nReferenceEnd
;
10928 // Copy the last part here, in the complex case.
10929 sal_uInt64 nDictEnd
= rObject
.GetDictionaryOffset() + rObject
.GetDictionaryLength();
10930 aLine
.append(static_cast<const sal_Char
*>(rDocBuffer
.GetData()) + nCopyStart
, nDictEnd
- nCopyStart
);
10933 // Can copy it as-is.
10934 aLine
.append(static_cast<const sal_Char
*>(rDocBuffer
.GetData()) + rObject
.GetDictionaryOffset(), rObject
.GetDictionaryLength());
10936 aLine
.append(">>\n");
10939 if (filter::PDFStreamElement
* pStream
= rObject
.GetStream())
10941 aLine
.append("stream\n");
10942 SvMemoryStream
& rStream
= pStream
->GetMemory();
10943 aLine
.append(static_cast<const sal_Char
*>(rStream
.GetData()), rStream
.GetSize());
10944 aLine
.append("\nendstream\n");
10947 if (filter::PDFArrayElement
* pArray
= rObject
.GetArray())
10951 const std::vector
<filter::PDFElement
*>& rElements
= pArray
->GetElements();
10952 bool bDone
= false;
10953 // Complex case: can't copy the array byte array as is, as it may contain references.
10954 sal_uInt64 nCopyStart
= 0;
10955 for (const auto pElement
: rElements
)
10957 auto pReference
= dynamic_cast<filter::PDFReferenceElement
*>(pElement
);
10960 filter::PDFObjectElement
* pReferenced
= pReference
->LookupObject();
10963 // Copy the referenced object.
10964 sal_Int32 nRef
= copyExternalResource(rDocBuffer
, *pReferenced
, rCopiedResources
);
10966 sal_uInt64 nReferenceStart
= pReference
->GetObjectElement().GetLocation();
10967 sal_uInt64 nReferenceEnd
= pReference
->GetOffset();
10968 sal_uInt64 nOffset
= 0;
10969 if (nCopyStart
== 0)
10970 // Array start -> reference start.
10971 nOffset
= rObject
.GetArrayOffset();
10973 // Previous reference end -> reference start.
10974 nOffset
= nCopyStart
;
10975 aLine
.append(static_cast<const sal_Char
*>(rDocBuffer
.GetData()) + nOffset
, nReferenceStart
- nOffset
);
10977 // Write the updated reference.
10979 aLine
.append(nRef
);
10980 aLine
.append(" 0 R");
10981 // Start copying here next time.
10982 nCopyStart
= nReferenceEnd
;
10991 // Copy the last part here, in the complex case.
10992 sal_uInt64 nArrEnd
= rObject
.GetArrayOffset() + rObject
.GetArrayLength();
10993 aLine
.append(static_cast<const sal_Char
*>(rDocBuffer
.GetData()) + nCopyStart
, nArrEnd
- nCopyStart
);
10996 // Can copy it as-is.
10997 aLine
.append(static_cast<const sal_Char
*>(rDocBuffer
.GetData()) + rObject
.GetArrayOffset(), rObject
.GetArrayLength());
10999 aLine
.append("]\n");
11002 // If the object has a number element outside a dictionary or array, copy that.
11003 if (filter::PDFNumberElement
* pNumber
= rObject
.GetNumberElement())
11005 aLine
.append(static_cast<const sal_Char
*>(rDocBuffer
.GetData()) + pNumber
->GetLocation(), pNumber
->GetLength());
11006 aLine
.append("\n");
11010 aLine
.append("endobj\n\n");
11012 // We have the whole object, now write it to the output.
11013 if (!updateObject(nObject
))
11015 if (!writeBuffer(aLine
.getStr(), aLine
.getLength()))
11021 OString
PDFWriterImpl::copyExternalResources(filter::PDFObjectElement
& rPage
, const OString
& rKind
, std::map
<sal_Int32
, sal_Int32
>& rCopiedResources
)
11023 // A name - object ID map, IDs as they appear in our output, not the
11025 std::map
<OString
, sal_Int32
> aRet
;
11027 // Get the rKind subset of the resource dictionary.
11028 std::map
<OString
, filter::PDFElement
*> aItems
;
11029 if (auto pResources
= dynamic_cast<filter::PDFDictionaryElement
*>(rPage
.Lookup("Resources")))
11031 // Resources is a direct dictionary.
11032 if (auto pDictionary
= dynamic_cast<filter::PDFDictionaryElement
*>(pResources
->LookupElement(rKind
)))
11033 aItems
= pDictionary
->GetItems();
11035 else if (filter::PDFObjectElement
* pPageResources
= rPage
.LookupObject("Resources"))
11037 // Resources is an indirect object.
11038 filter::PDFElement
* pValue
= pPageResources
->Lookup(rKind
);
11039 if (auto pDictionary
= dynamic_cast<filter::PDFDictionaryElement
*>(pValue
))
11040 // Kind is a direct dictionary.
11041 aItems
= pDictionary
->GetItems();
11042 else if (filter::PDFObjectElement
* pObject
= pPageResources
->LookupObject(rKind
))
11043 // Kind is an indirect object.
11044 aItems
= pObject
->GetDictionaryItems();
11046 if (aItems
.empty())
11049 SvMemoryStream
& rDocBuffer
= rPage
.GetDocument().GetEditBuffer();
11051 for (const auto& rItem
: aItems
)
11053 // For each item copy it over to our output then insert it into aRet.
11054 auto pReference
= dynamic_cast<filter::PDFReferenceElement
*>(rItem
.second
);
11058 filter::PDFObjectElement
* pValue
= pReference
->LookupObject();
11062 // Then copying over an object copy its dictionary and its stream.
11063 sal_Int32 nObject
= copyExternalResource(rDocBuffer
, *pValue
, rCopiedResources
);
11064 aRet
[rItem
.first
] = nObject
;
11067 // Build the dictionary entry string.
11068 OString sRet
= "/" + rKind
+ "<<";
11069 for (const auto& rPair
: aRet
)
11071 sRet
+= "/" + rPair
.first
+ " " + OString::number(rPair
.second
) + " 0 R";
11078 void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit
& rEmit
)
11080 if (rEmit
.m_nFormObject
<= 0)
11083 // Count /Matrix and /BBox.
11084 // vcl::ImportPDF() works with 96 DPI so use the same values here, too.
11085 sal_Int32 nOldDPIX
= getReferenceDevice()->GetDPIX();
11086 getReferenceDevice()->SetDPIX(96);
11087 sal_Int32 nOldDPIY
= getReferenceDevice()->GetDPIY();
11088 getReferenceDevice()->SetDPIY(96);
11089 Size aSize
= getReferenceDevice()->PixelToLogic(rEmit
.m_aPixelSize
, MapMode(m_aMapMode
.GetMapUnit()));
11090 getReferenceDevice()->SetDPIX(nOldDPIX
);
11091 getReferenceDevice()->SetDPIY(nOldDPIY
);
11092 double fScaleX
= 1.0 / aSize
.Width();
11093 double fScaleY
= 1.0 / aSize
.Height();
11095 sal_Int32 nWrappedFormObject
= 0;
11096 if (!m_aContext
.UseReferenceXObject
)
11098 // Parse the PDF data, we need that to write the PDF dictionary of our
11100 SvMemoryStream aPDFStream
;
11101 aPDFStream
.WriteBytes(rEmit
.m_aPDFData
.getArray(), rEmit
.m_aPDFData
.getLength());
11102 aPDFStream
.Seek(0);
11103 filter::PDFDocument aPDFDocument
;
11104 if (!aPDFDocument
.Read(aPDFStream
))
11106 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: reading the PDF document failed");
11109 std::vector
<filter::PDFObjectElement
*> aPages
= aPDFDocument
.GetPages();
11110 if (aPages
.empty())
11112 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no pages");
11116 filter::PDFObjectElement
* pPage
= aPages
[0];
11119 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no page");
11123 std::vector
<filter::PDFObjectElement
*> aContentStreams
;
11124 if (filter::PDFObjectElement
* pContentStream
= pPage
->LookupObject("Contents"))
11125 aContentStreams
.push_back(pContentStream
);
11126 else if (auto pArray
= dynamic_cast<filter::PDFArrayElement
*>(pPage
->Lookup("Contents")))
11128 for (const auto pElement
: pArray
->GetElements())
11130 auto pReference
= dynamic_cast<filter::PDFReferenceElement
*>(pElement
);
11134 filter::PDFObjectElement
* pObject
= pReference
->LookupObject();
11138 aContentStreams
.push_back(pObject
);
11142 if (aContentStreams
.empty())
11144 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no content stream");
11148 // Maps from source object id (PDF image) to target object id (export result).
11149 std::map
<sal_Int32
, sal_Int32
> aCopiedResources
;
11151 nWrappedFormObject
= createObject();
11152 // Write the form XObject wrapped below. This is a separate object from
11153 // the wrapper, this way there is no need to alter the stream contents.
11155 OStringBuffer aLine
;
11156 aLine
.append(nWrappedFormObject
);
11157 aLine
.append(" 0 obj\n");
11158 aLine
.append("<< /Type /XObject");
11159 aLine
.append(" /Subtype /Form");
11160 aLine
.append(" /Resources <<");
11161 static const std::initializer_list
<OString
> aKeys
=
11169 for (const auto& rKey
: aKeys
)
11170 aLine
.append(copyExternalResources(*pPage
, rKey
, aCopiedResources
));
11171 aLine
.append(">>");
11172 aLine
.append(" /BBox [ 0 0 ");
11173 aLine
.append(aSize
.Width());
11175 aLine
.append(aSize
.Height());
11176 aLine
.append(" ]");
11178 if (!g_bDebugDisableCompression
)
11179 aLine
.append(" /Filter/FlateDecode");
11180 aLine
.append(" /Length ");
11182 SvMemoryStream aStream
;
11183 for (auto pContent
: aContentStreams
)
11185 filter::PDFStreamElement
* pPageStream
= pContent
->GetStream();
11188 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: contents has no stream");
11192 SvMemoryStream
& rPageStream
= pPageStream
->GetMemory();
11194 auto pFilter
= dynamic_cast<filter::PDFNameElement
*>(pContent
->Lookup("Filter"));
11197 if (pFilter
->GetValue() != "FlateDecode")
11200 SvMemoryStream aMemoryStream
;
11202 rPageStream
.Seek(0);
11203 aZCodec
.BeginCompression();
11204 aZCodec
.Decompress(rPageStream
, aMemoryStream
);
11205 if (!aZCodec
.EndCompression())
11207 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: decompression failed");
11211 aStream
.WriteBytes(aMemoryStream
.GetData(), aMemoryStream
.GetSize());
11214 aStream
.WriteBytes(rPageStream
.GetData(), rPageStream
.GetSize());
11217 compressStream(&aStream
);
11218 sal_Int32 nLength
= aStream
.Tell();
11219 aLine
.append(nLength
);
11221 aLine
.append(">>\nstream\n");
11222 // Copy the original page streams to the form XObject stream.
11223 aLine
.append(static_cast<const sal_Char
*>(aStream
.GetData()), aStream
.GetSize());
11224 aLine
.append("\nendstream\nendobj\n\n");
11225 if (!updateObject(nWrappedFormObject
))
11227 if (!writeBuffer(aLine
.getStr(), aLine
.getLength()))
11231 OStringBuffer aLine
;
11232 if (!updateObject(rEmit
.m_nFormObject
))
11235 // Now have all the info to write the form XObject.
11236 aLine
.append(rEmit
.m_nFormObject
);
11237 aLine
.append(" 0 obj\n");
11238 aLine
.append("<< /Type /XObject");
11239 aLine
.append(" /Subtype /Form");
11240 aLine
.append(" /Resources << /XObject<<");
11242 sal_Int32 nObject
= m_aContext
.UseReferenceXObject
? rEmit
.m_nBitmapObject
: nWrappedFormObject
;
11243 aLine
.append(" /Im");
11244 aLine
.append(nObject
);
11246 aLine
.append(nObject
);
11247 aLine
.append(" 0 R");
11249 aLine
.append(">> >>");
11250 aLine
.append(" /Matrix [ ");
11251 appendDouble(fScaleX
, aLine
);
11252 aLine
.append(" 0 0 ");
11253 appendDouble(fScaleY
, aLine
);
11254 aLine
.append(" 0 0 ]");
11255 aLine
.append(" /BBox [ 0 0 ");
11256 aLine
.append(aSize
.Width());
11258 aLine
.append(aSize
.Height());
11259 aLine
.append(" ]\n");
11261 if (m_aContext
.UseReferenceXObject
&& rEmit
.m_nEmbeddedObject
> 0)
11263 // Write the reference dictionary.
11264 aLine
.append("/Ref<< /F << /Type /Filespec /F (<embedded file>) /EF << /F ");
11265 aLine
.append(rEmit
.m_nEmbeddedObject
);
11266 aLine
.append(" 0 R >> >> /Page 0 >>\n");
11269 aLine
.append("/Length ");
11271 OStringBuffer aStream
;
11272 aStream
.append("q ");
11273 if (m_aContext
.UseReferenceXObject
)
11275 // Reference XObject markup is used, just refer to the fallback bitmap
11277 aStream
.append(aSize
.Width());
11278 aStream
.append(" 0 0 ");
11279 aStream
.append(aSize
.Height());
11280 aStream
.append(" 0 0 cm\n");
11281 aStream
.append("/Im");
11282 aStream
.append(rEmit
.m_nBitmapObject
);
11283 aStream
.append(" Do\n");
11287 // Reset line width to the default.
11288 aStream
.append(" 1 w\n");
11290 // No reference XObject, draw the form XObject containing the original
11292 aStream
.append("/Im");
11293 aStream
.append(nWrappedFormObject
);
11294 aStream
.append(" Do\n");
11296 aStream
.append("Q");
11297 aLine
.append(aStream
.getLength());
11299 aLine
.append(">>\nstream\n");
11300 aLine
.append(aStream
.getStr());
11301 aLine
.append("\nendstream\nendobj\n\n");
11302 CHECK_RETURN2(writeBuffer(aLine
.getStr(), aLine
.getLength()));
11307 unsigned char reverseByte(unsigned char b
)
11309 b
= (b
& 0xF0) >> 4 | (b
& 0x0F) << 4;
11310 b
= (b
& 0xCC) >> 2 | (b
& 0x33) << 2;
11311 b
= (b
& 0xAA) >> 1 | (b
& 0x55) << 1;
11315 //tdf#103051 convert any N1BitLsbPal to N1BitMsbPal
11316 Bitmap
getExportBitmap(const Bitmap
&rBitmap
)
11318 Bitmap::ScopedReadAccess
pAccess(const_cast<Bitmap
&>(rBitmap
));
11319 const ScanlineFormat eFormat
= pAccess
->GetScanlineFormat();
11320 if (eFormat
!= ScanlineFormat::N1BitLsbPal
)
11322 Bitmap
aNewBmp(rBitmap
);
11323 Bitmap::ScopedWriteAccess
xWriteAcc(aNewBmp
);
11324 const int nScanLineBytes
= (pAccess
->Width() + 7U) / 8U;
11325 for (long nY
= 0L; nY
< xWriteAcc
->Height(); ++nY
)
11327 Scanline pBitSwap
= xWriteAcc
->GetScanline(nY
);
11328 for (int x
= 0; x
< nScanLineBytes
; ++x
)
11329 pBitSwap
[x
] = reverseByte(pBitSwap
[x
]);
11335 bool PDFWriterImpl::writeBitmapObject( BitmapEmit
& rObject
, bool bMask
)
11337 if (rObject
.m_aReferenceXObject
.m_aPDFData
.hasElements() && !m_aContext
.UseReferenceXObject
)
11339 writeReferenceXObject(rObject
.m_aReferenceXObject
);
11343 CHECK_RETURN( updateObject( rObject
.m_nObject
) );
11346 Color
aTransparentColor( COL_TRANSPARENT
);
11347 bool bWriteMask
= false;
11350 aBitmap
= getExportBitmap(rObject
.m_aBitmap
.GetBitmap());
11351 if( rObject
.m_aBitmap
.IsAlpha() )
11353 if( m_aContext
.Version
>= PDFWriter::PDFVersion::PDF_1_4
)
11355 // else draw without alpha channel
11359 switch( rObject
.m_aBitmap
.GetTransparentType() )
11361 case TransparentType::NONE
:
11363 case TransparentType::Color
:
11364 aTransparentColor
= rObject
.m_aBitmap
.GetTransparentColor();
11366 case TransparentType::Bitmap
:
11374 if( m_aContext
.Version
< PDFWriter::PDFVersion::PDF_1_4
|| ! rObject
.m_aBitmap
.IsAlpha() )
11376 aBitmap
= getExportBitmap(rObject
.m_aBitmap
.GetMask());
11377 aBitmap
.Convert( BmpConversion::N1BitThreshold
);
11378 SAL_WARN_IF( aBitmap
.GetBitCount() != 1, "vcl.pdfwriter", "mask conversion failed" );
11380 else if( aBitmap
.GetBitCount() != 8 )
11382 aBitmap
= getExportBitmap(rObject
.m_aBitmap
.GetAlpha().GetBitmap());
11383 aBitmap
.Convert( BmpConversion::N8BitGreys
);
11384 SAL_WARN_IF( aBitmap
.GetBitCount() != 8, "vcl.pdfwriter", "alpha mask conversion failed" );
11388 Bitmap::ScopedReadAccess
pAccess(aBitmap
);
11391 sal_Int32 nBitsPerComponent
;
11392 switch( aBitmap
.GetBitCount() )
11398 bTrueColor
= false;
11399 nBitsPerComponent
= aBitmap
.GetBitCount();
11403 nBitsPerComponent
= 8;
11407 sal_Int32 nStreamLengthObject
= createObject();
11408 sal_Int32 nMaskObject
= 0;
11410 #if OSL_DEBUG_LEVEL > 1
11411 emitComment( "PDFWriterImpl::writeBitmapObject" );
11413 OStringBuffer
aLine(1024);
11414 aLine
.append( rObject
.m_nObject
);
11415 aLine
.append( " 0 obj\n"
11416 "<</Type/XObject/Subtype/Image/Width " );
11417 aLine
.append( (sal_Int32
)aBitmap
.GetSizePixel().Width() );
11418 aLine
.append( "/Height " );
11419 aLine
.append( (sal_Int32
)aBitmap
.GetSizePixel().Height() );
11420 aLine
.append( "/BitsPerComponent " );
11421 aLine
.append( nBitsPerComponent
);
11422 aLine
.append( "/Length " );
11423 aLine
.append( nStreamLengthObject
);
11424 aLine
.append( " 0 R\n" );
11425 if (!g_bDebugDisableCompression
)
11427 if( nBitsPerComponent
!= 1 )
11429 aLine
.append( "/Filter/FlateDecode" );
11433 aLine
.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
11434 aLine
.append( (sal_Int32
)aBitmap
.GetSizePixel().Width() );
11435 aLine
.append( ">>\n" );
11440 aLine
.append( "/ColorSpace" );
11442 aLine
.append( "/DeviceRGB\n" );
11443 else if( aBitmap
.HasGreyPalette() )
11445 aLine
.append( "/DeviceGray\n" );
11446 if( aBitmap
.GetBitCount() == 1 )
11448 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
11449 sal_Int32 nBlackIndex
= pAccess
->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK
) ) );
11450 SAL_WARN_IF( nBlackIndex
!= 0 && nBlackIndex
!= 1, "vcl.pdfwriter", "wrong black index" );
11451 if( nBlackIndex
== 1 )
11452 aLine
.append( "/Decode[1 0]\n" );
11457 aLine
.append( "[ /Indexed/DeviceRGB " );
11458 aLine
.append( (sal_Int32
)(pAccess
->GetPaletteEntryCount()-1) );
11459 aLine
.append( "\n<" );
11460 if( m_aContext
.Encryption
.Encrypt() )
11462 enableStringEncryption( rObject
.m_nObject
);
11463 //check encryption buffer size
11464 if( checkEncryptionBufferSize( pAccess
->GetPaletteEntryCount()*3 ) )
11467 //fill the encryption buffer
11468 for( sal_uInt16 i
= 0; i
< pAccess
->GetPaletteEntryCount(); i
++ )
11470 const BitmapColor
& rColor
= pAccess
->GetPaletteColor( i
);
11471 m_pEncryptionBuffer
[nChar
++] = rColor
.GetRed();
11472 m_pEncryptionBuffer
[nChar
++] = rColor
.GetGreen();
11473 m_pEncryptionBuffer
[nChar
++] = rColor
.GetBlue();
11475 //encrypt the colorspace lookup table
11476 rtl_cipher_encodeARCFOUR( m_aCipher
, m_pEncryptionBuffer
, nChar
, m_pEncryptionBuffer
, nChar
);
11477 //now queue the data for output
11479 for( sal_uInt16 i
= 0; i
< pAccess
->GetPaletteEntryCount(); i
++ )
11481 appendHex(m_pEncryptionBuffer
[nChar
++], aLine
);
11482 appendHex(m_pEncryptionBuffer
[nChar
++], aLine
);
11483 appendHex(m_pEncryptionBuffer
[nChar
++], aLine
);
11487 else //no encryption requested (PDF/A-1a program flow drops here)
11489 for( sal_uInt16 i
= 0; i
< pAccess
->GetPaletteEntryCount(); i
++ )
11491 const BitmapColor
& rColor
= pAccess
->GetPaletteColor( i
);
11492 appendHex( rColor
.GetRed(), aLine
);
11493 appendHex( rColor
.GetGreen(), aLine
);
11494 appendHex( rColor
.GetBlue(), aLine
);
11497 aLine
.append( ">\n]\n" );
11502 if( aBitmap
.GetBitCount() == 1 )
11504 aLine
.append( "/ImageMask true\n" );
11505 sal_Int32 nBlackIndex
= pAccess
->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK
) ) );
11506 SAL_WARN_IF( nBlackIndex
!= 0 && nBlackIndex
!= 1, "vcl.pdfwriter", "wrong black index" );
11508 aLine
.append( "/Decode[ 1 0 ]\n" );
11510 aLine
.append( "/Decode[ 0 1 ]\n" );
11512 else if( aBitmap
.GetBitCount() == 8 )
11514 aLine
.append( "/ColorSpace/DeviceGray\n"
11515 "/Decode [ 1 0 ]\n" );
11519 if( ! bMask
&& m_aContext
.Version
> PDFWriter::PDFVersion::PDF_1_2
&& !m_bIsPDF_A1
)
11523 nMaskObject
= createObject();
11524 if( rObject
.m_aBitmap
.IsAlpha() && m_aContext
.Version
> PDFWriter::PDFVersion::PDF_1_3
)
11525 aLine
.append( "/SMask " );
11527 aLine
.append( "/Mask " );
11528 aLine
.append( nMaskObject
);
11529 aLine
.append( " 0 R\n" );
11531 else if( aTransparentColor
!= Color( COL_TRANSPARENT
) )
11533 aLine
.append( "/Mask[ " );
11536 aLine
.append( (sal_Int32
)aTransparentColor
.GetRed() );
11537 aLine
.append( ' ' );
11538 aLine
.append( (sal_Int32
)aTransparentColor
.GetRed() );
11539 aLine
.append( ' ' );
11540 aLine
.append( (sal_Int32
)aTransparentColor
.GetGreen() );
11541 aLine
.append( ' ' );
11542 aLine
.append( (sal_Int32
)aTransparentColor
.GetGreen() );
11543 aLine
.append( ' ' );
11544 aLine
.append( (sal_Int32
)aTransparentColor
.GetBlue() );
11545 aLine
.append( ' ' );
11546 aLine
.append( (sal_Int32
)aTransparentColor
.GetBlue() );
11550 sal_Int32 nIndex
= pAccess
->GetBestPaletteIndex( BitmapColor( aTransparentColor
) );
11551 aLine
.append( nIndex
);
11553 aLine
.append( " ]\n" );
11556 else if( m_bIsPDF_A1
&& (bWriteMask
|| aTransparentColor
!= Color( COL_TRANSPARENT
)) )
11557 m_aErrors
.insert( PDFWriter::Warning_Transparency_Omitted_PDFA
);
11559 aLine
.append( ">>\n"
11561 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
11562 sal_uInt64 nStartPos
= 0;
11563 CHECK_RETURN( (osl::File::E_None
== m_aFile
.getPos(nStartPos
)) );
11565 checkAndEnableStreamEncryption( rObject
.m_nObject
);
11566 if (!g_bDebugDisableCompression
&& nBitsPerComponent
== 1)
11568 writeG4Stream(pAccess
.get());
11572 beginCompression();
11573 if( ! bTrueColor
|| pAccess
->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb
)
11575 //With PDF bitmaps, each row is padded to a BYTE boundary (multiple of 8 bits).
11576 const int nScanLineBytes
= ((pAccess
->GetBitCount() * pAccess
->Width()) + 7U) / 8U;
11578 for( long i
= 0; i
< pAccess
->Height(); i
++ )
11580 CHECK_RETURN( writeBuffer( pAccess
->GetScanline( i
), nScanLineBytes
) );
11585 const int nScanLineBytes
= pAccess
->Width()*3;
11586 std::unique_ptr
<sal_uInt8
[]> xCol(new sal_uInt8
[nScanLineBytes
]);
11587 for( long y
= 0; y
< pAccess
->Height(); y
++ )
11589 for( long x
= 0; x
< pAccess
->Width(); x
++ )
11591 BitmapColor aColor
= pAccess
->GetColor( y
, x
);
11592 xCol
[3*x
+0] = aColor
.GetRed();
11593 xCol
[3*x
+1] = aColor
.GetGreen();
11594 xCol
[3*x
+2] = aColor
.GetBlue();
11596 CHECK_RETURN(writeBuffer(xCol
.get(), nScanLineBytes
));
11601 disableStreamEncryption();
11603 sal_uInt64 nEndPos
= 0;
11604 CHECK_RETURN( (osl::File::E_None
== m_aFile
.getPos(nEndPos
)) );
11605 aLine
.setLength( 0 );
11606 aLine
.append( "\nendstream\nendobj\n\n" );
11607 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
11608 CHECK_RETURN( updateObject( nStreamLengthObject
) );
11609 aLine
.setLength( 0 );
11610 aLine
.append( nStreamLengthObject
);
11611 aLine
.append( " 0 obj\n" );
11612 aLine
.append( (sal_Int64
)(nEndPos
-nStartPos
) );
11613 aLine
.append( "\nendobj\n\n" );
11614 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
11619 aEmit
.m_nObject
= nMaskObject
;
11620 aEmit
.m_aBitmap
= rObject
.m_aBitmap
;
11621 return writeBitmapObject( aEmit
, true );
11624 writeReferenceXObject(rObject
.m_aReferenceXObject
);
11629 void PDFWriterImpl::createEmbeddedFile(const Graphic
& rGraphic
, ReferenceXObjectEmit
& rEmit
, sal_Int32 nBitmapObject
)
11631 // The bitmap object is always a valid identifier, even if the graphic has
11633 rEmit
.m_nBitmapObject
= nBitmapObject
;
11635 if (!rGraphic
.getPdfData().hasElements())
11638 if (m_aContext
.UseReferenceXObject
)
11640 // Store the original PDF data as an embedded file.
11641 m_aEmbeddedFiles
.push_back(PDFEmbeddedFile());
11642 m_aEmbeddedFiles
.back().m_nObject
= createObject();
11643 m_aEmbeddedFiles
.back().m_aData
= rGraphic
.getPdfData();
11645 rEmit
.m_nEmbeddedObject
= m_aEmbeddedFiles
.back().m_nObject
;
11648 rEmit
.m_aPDFData
= rGraphic
.getPdfData();
11650 rEmit
.m_nFormObject
= createObject();
11651 rEmit
.m_aPixelSize
= rGraphic
.GetBitmap().GetPrefSize();
11654 void PDFWriterImpl::drawJPGBitmap( SvStream
& rDCTData
, bool bIsTrueColor
, const Size
& rSizePixel
, const tools::Rectangle
& rTargetArea
, const Bitmap
& rMask
, const Graphic
& rGraphic
)
11656 MARK( "drawJPGBitmap" );
11658 OStringBuffer
aLine( 80 );
11659 updateGraphicsState();
11661 // #i40055# sanity check
11662 if( ! (rTargetArea
.GetWidth() && rTargetArea
.GetHeight() ) )
11664 if( ! (rSizePixel
.Width() && rSizePixel
.Height()) )
11667 rDCTData
.Seek( 0 );
11668 if( bIsTrueColor
&& m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
)
11670 // need to convert to grayscale;
11671 // load stream to bitmap and draw the bitmap instead
11673 GraphicConverter::Import( rDCTData
, aGraphic
, ConvertDataFormat::JPG
);
11674 Bitmap
aBmp( aGraphic
.GetBitmap() );
11675 if( !!rMask
&& rMask
.GetSizePixel() == aBmp
.GetSizePixel() )
11677 BitmapEx
aBmpEx( aBmp
, rMask
);
11678 drawBitmap( rTargetArea
.TopLeft(), rTargetArea
.GetSize(), aBmpEx
);
11681 drawBitmap( rTargetArea
.TopLeft(), rTargetArea
.GetSize(), aBmp
);
11685 SvMemoryStream
* pStream
= new SvMemoryStream
;
11686 pStream
->WriteStream( rDCTData
);
11687 pStream
->Seek( STREAM_SEEK_TO_END
);
11690 aID
.m_aPixelSize
= rSizePixel
;
11691 aID
.m_nSize
= pStream
->Tell();
11692 pStream
->Seek( STREAM_SEEK_TO_BEGIN
);
11693 aID
.m_nChecksum
= vcl_get_checksum( 0, pStream
->GetData(), aID
.m_nSize
);
11694 if( ! rMask
.IsEmpty() )
11695 aID
.m_nMaskChecksum
= rMask
.GetChecksum();
11697 std::list
< JPGEmit
>::const_iterator it
;
11698 for( it
= m_aJPGs
.begin(); it
!= m_aJPGs
.end() && ! (aID
== it
->m_aID
); ++it
)
11700 if( it
== m_aJPGs
.end() )
11702 m_aJPGs
.emplace( m_aJPGs
.begin() );
11703 JPGEmit
& rEmit
= m_aJPGs
.front();
11704 if (!rGraphic
.getPdfData().hasElements() || m_aContext
.UseReferenceXObject
)
11705 rEmit
.m_nObject
= createObject();
11707 rEmit
.m_pStream
.reset( pStream
);
11708 rEmit
.m_bTrueColor
= bIsTrueColor
;
11709 if( !! rMask
&& rMask
.GetSizePixel() == rSizePixel
)
11710 rEmit
.m_aMask
= rMask
;
11711 createEmbeddedFile(rGraphic
, rEmit
.m_aReferenceXObject
, rEmit
.m_nObject
);
11713 it
= m_aJPGs
.begin();
11718 aLine
.append( "q " );
11719 sal_Int32 nCheckWidth
= 0;
11720 m_aPages
.back().appendMappedLength( (sal_Int32
)rTargetArea
.GetWidth(), aLine
, false, &nCheckWidth
);
11721 aLine
.append( " 0 0 " );
11722 sal_Int32 nCheckHeight
= 0;
11723 m_aPages
.back().appendMappedLength( (sal_Int32
)rTargetArea
.GetHeight(), aLine
, true, &nCheckHeight
);
11724 aLine
.append( ' ' );
11725 m_aPages
.back().appendPoint( rTargetArea
.BottomLeft(), aLine
);
11726 aLine
.append( " cm\n/Im" );
11727 sal_Int32 nObject
= it
->m_aReferenceXObject
.getObject();
11728 aLine
.append(nObject
);
11729 aLine
.append( " Do Q\n" );
11730 if( nCheckWidth
== 0 || nCheckHeight
== 0 )
11732 // #i97512# avoid invalid current matrix
11733 aLine
.setLength( 0 );
11734 aLine
.append( "\n%jpeg image /Im" );
11735 aLine
.append( it
->m_nObject
);
11736 aLine
.append( " scaled to zero size, omitted\n" );
11738 writeBuffer( aLine
.getStr(), aLine
.getLength() );
11740 OStringBuffer
aObjName( 16 );
11741 aObjName
.append( "Im" );
11742 aObjName
.append(nObject
);
11743 pushResource( ResXObject
, aObjName
.makeStringAndClear(), nObject
);
11747 void PDFWriterImpl::drawBitmap( const Point
& rDestPoint
, const Size
& rDestSize
, const BitmapEmit
& rBitmap
, const Color
& rFillColor
)
11749 OStringBuffer
aLine( 80 );
11750 updateGraphicsState();
11752 aLine
.append( "q " );
11753 if( rFillColor
!= Color( COL_TRANSPARENT
) )
11755 appendNonStrokingColor( rFillColor
, aLine
);
11756 aLine
.append( ' ' );
11758 sal_Int32 nCheckWidth
= 0;
11759 m_aPages
.back().appendMappedLength( (sal_Int32
)rDestSize
.Width(), aLine
, false, &nCheckWidth
);
11760 aLine
.append( " 0 0 " );
11761 sal_Int32 nCheckHeight
= 0;
11762 m_aPages
.back().appendMappedLength( (sal_Int32
)rDestSize
.Height(), aLine
, true, &nCheckHeight
);
11763 aLine
.append( ' ' );
11764 m_aPages
.back().appendPoint( rDestPoint
+ Point( 0, rDestSize
.Height()-1 ), aLine
);
11765 aLine
.append( " cm\n/Im" );
11766 sal_Int32 nObject
= rBitmap
.m_aReferenceXObject
.getObject();
11767 aLine
.append(nObject
);
11768 aLine
.append( " Do Q\n" );
11769 if( nCheckWidth
== 0 || nCheckHeight
== 0 )
11771 // #i97512# avoid invalid current matrix
11772 aLine
.setLength( 0 );
11773 aLine
.append( "\n%bitmap image /Im" );
11774 aLine
.append( rBitmap
.m_nObject
);
11775 aLine
.append( " scaled to zero size, omitted\n" );
11777 writeBuffer( aLine
.getStr(), aLine
.getLength() );
11780 const PDFWriterImpl::BitmapEmit
& PDFWriterImpl::createBitmapEmit( const BitmapEx
& i_rBitmap
, const Graphic
& rGraphic
)
11782 BitmapEx
aBitmap( i_rBitmap
);
11783 if( m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
)
11785 BmpConversion eConv
= BmpConversion::N8BitGreys
;
11786 int nDepth
= aBitmap
.GetBitmap().GetBitCount();
11788 eConv
= BmpConversion::N4BitGreys
;
11790 aBitmap
.Convert( eConv
);
11793 aID
.m_aPixelSize
= aBitmap
.GetSizePixel();
11794 aID
.m_nSize
= aBitmap
.GetBitCount();
11795 aID
.m_nChecksum
= aBitmap
.GetBitmap().GetChecksum();
11796 aID
.m_nMaskChecksum
= 0;
11797 if( aBitmap
.IsAlpha() )
11798 aID
.m_nMaskChecksum
= aBitmap
.GetAlpha().GetChecksum();
11801 Bitmap aMask
= aBitmap
.GetMask();
11802 if( ! aMask
.IsEmpty() )
11803 aID
.m_nMaskChecksum
= aMask
.GetChecksum();
11805 std::list
< BitmapEmit
>::const_iterator it
;
11806 for( it
= m_aBitmaps
.begin(); it
!= m_aBitmaps
.end(); ++it
)
11808 if( aID
== it
->m_aID
)
11811 if( it
== m_aBitmaps
.end() )
11813 m_aBitmaps
.push_front( BitmapEmit() );
11814 m_aBitmaps
.front().m_aID
= aID
;
11815 m_aBitmaps
.front().m_aBitmap
= aBitmap
;
11816 if (!rGraphic
.getPdfData().hasElements() || m_aContext
.UseReferenceXObject
)
11817 m_aBitmaps
.front().m_nObject
= createObject();
11818 createEmbeddedFile(rGraphic
, m_aBitmaps
.front().m_aReferenceXObject
, m_aBitmaps
.front().m_nObject
);
11819 it
= m_aBitmaps
.begin();
11822 OStringBuffer
aObjName( 16 );
11823 aObjName
.append( "Im" );
11824 sal_Int32 nObject
= it
->m_aReferenceXObject
.getObject();
11825 aObjName
.append(nObject
);
11826 pushResource( ResXObject
, aObjName
.makeStringAndClear(), nObject
);
11831 void PDFWriterImpl::drawBitmap( const Point
& rDestPoint
, const Size
& rDestSize
, const Bitmap
& rBitmap
, const Graphic
& rGraphic
)
11833 MARK( "drawBitmap (Bitmap)" );
11835 // #i40055# sanity check
11836 if( ! (rDestSize
.Width() && rDestSize
.Height()) )
11839 const BitmapEmit
& rEmit
= createBitmapEmit( BitmapEx( rBitmap
), rGraphic
);
11840 drawBitmap( rDestPoint
, rDestSize
, rEmit
, Color( COL_TRANSPARENT
) );
11843 void PDFWriterImpl::drawBitmap( const Point
& rDestPoint
, const Size
& rDestSize
, const BitmapEx
& rBitmap
)
11845 MARK( "drawBitmap (BitmapEx)" );
11847 // #i40055# sanity check
11848 if( ! (rDestSize
.Width() && rDestSize
.Height()) )
11851 const BitmapEmit
& rEmit
= createBitmapEmit( rBitmap
, Graphic() );
11852 drawBitmap( rDestPoint
, rDestSize
, rEmit
, Color( COL_TRANSPARENT
) );
11855 sal_Int32
PDFWriterImpl::createGradient( const Gradient
& rGradient
, const Size
& rSize
)
11857 Size
aPtSize( lcl_convert( m_aGraphicsStack
.front().m_aMapMode
,
11858 MapMode( MapUnit::MapPoint
),
11859 getReferenceDevice(),
11861 // check if we already have this gradient
11862 std::list
<GradientEmit
>::iterator it
;
11863 // rounding to point will generally lose some pixels
11864 // round up to point boundary
11866 aPtSize
.Height()++;
11867 for( it
= m_aGradients
.begin(); it
!= m_aGradients
.end(); ++it
)
11869 if( it
->m_aGradient
== rGradient
)
11871 if( it
->m_aSize
== aPtSize
)
11875 if( it
== m_aGradients
.end() )
11877 m_aGradients
.push_front( GradientEmit() );
11878 m_aGradients
.front().m_aGradient
= rGradient
;
11879 m_aGradients
.front().m_nObject
= createObject();
11880 m_aGradients
.front().m_aSize
= aPtSize
;
11881 it
= m_aGradients
.begin();
11884 OStringBuffer
aObjName( 16 );
11885 aObjName
.append( 'P' );
11886 aObjName
.append( it
->m_nObject
);
11887 pushResource( ResShading
, aObjName
.makeStringAndClear(), it
->m_nObject
);
11889 return it
->m_nObject
;
11892 void PDFWriterImpl::drawGradient( const tools::Rectangle
& rRect
, const Gradient
& rGradient
)
11894 MARK( "drawGradient (Rectangle)" );
11896 if( m_aContext
.Version
== PDFWriter::PDFVersion::PDF_1_2
)
11898 drawRectangle( rRect
);
11902 sal_Int32 nGradient
= createGradient( rGradient
, rRect
.GetSize() );
11904 Point
aTranslate( rRect
.BottomLeft() );
11905 aTranslate
+= Point( 0, 1 );
11907 updateGraphicsState();
11909 OStringBuffer
aLine( 80 );
11910 aLine
.append( "q 1 0 0 1 " );
11911 m_aPages
.back().appendPoint( aTranslate
, aLine
);
11912 aLine
.append( " cm " );
11913 // if a stroke is appended reset the clip region before stroke
11914 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
11915 aLine
.append( "q " );
11916 aLine
.append( "0 0 " );
11917 m_aPages
.back().appendMappedLength( (sal_Int32
)rRect
.GetWidth(), aLine
, false );
11918 aLine
.append( ' ' );
11919 m_aPages
.back().appendMappedLength( (sal_Int32
)rRect
.GetHeight(), aLine
);
11920 aLine
.append( " re W n\n" );
11922 aLine
.append( "/P" );
11923 aLine
.append( nGradient
);
11924 aLine
.append( " sh " );
11925 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
11927 aLine
.append( "Q 0 0 " );
11928 m_aPages
.back().appendMappedLength( (sal_Int32
)rRect
.GetWidth(), aLine
, false );
11929 aLine
.append( ' ' );
11930 m_aPages
.back().appendMappedLength( (sal_Int32
)rRect
.GetHeight(), aLine
);
11931 aLine
.append( " re S " );
11933 aLine
.append( "Q\n" );
11934 writeBuffer( aLine
.getStr(), aLine
.getLength() );
11937 void PDFWriterImpl::drawHatch( const tools::PolyPolygon
& rPolyPoly
, const Hatch
& rHatch
)
11939 MARK( "drawHatch" );
11941 updateGraphicsState();
11943 if( rPolyPoly
.Count() )
11945 tools::PolyPolygon
aPolyPoly( rPolyPoly
);
11947 aPolyPoly
.Optimize( PolyOptimizeFlags::NO_SAME
);
11948 push( PushFlags::LINECOLOR
);
11949 setLineColor( rHatch
.GetColor() );
11950 getReferenceDevice()->DrawHatch( aPolyPoly
, rHatch
, false );
11955 void PDFWriterImpl::drawWallpaper( const tools::Rectangle
& rRect
, const Wallpaper
& rWall
)
11957 MARK( "drawWallpaper" );
11959 bool bDrawColor
= false;
11960 bool bDrawGradient
= false;
11961 bool bDrawBitmap
= false;
11964 Point aBmpPos
= rRect
.TopLeft();
11966 if( rWall
.IsBitmap() )
11968 aBitmap
= rWall
.GetBitmap();
11969 aBmpSize
= lcl_convert( aBitmap
.GetPrefMapMode(),
11971 getReferenceDevice(),
11972 aBitmap
.GetPrefSize() );
11973 tools::Rectangle
aRect( rRect
);
11974 if( rWall
.IsRect() )
11976 aRect
= rWall
.GetRect();
11977 aBmpPos
= aRect
.TopLeft();
11978 aBmpSize
= aRect
.GetSize();
11980 if( rWall
.GetStyle() != WallpaperStyle::Scale
)
11982 if( rWall
.GetStyle() != WallpaperStyle::Tile
)
11984 bDrawBitmap
= true;
11985 if( rWall
.IsGradient() )
11986 bDrawGradient
= true;
11989 switch( rWall
.GetStyle() )
11991 case WallpaperStyle::TopLeft
:
11993 case WallpaperStyle::Top
:
11994 aBmpPos
.X() += (aRect
.GetWidth()-aBmpSize
.Width())/2;
11996 case WallpaperStyle::Left
:
11997 aBmpPos
.Y() += (aRect
.GetHeight()-aBmpSize
.Height())/2;
11999 case WallpaperStyle::TopRight
:
12000 aBmpPos
.X() += aRect
.GetWidth()-aBmpSize
.Width();
12002 case WallpaperStyle::Center
:
12003 aBmpPos
.X() += (aRect
.GetWidth()-aBmpSize
.Width())/2;
12004 aBmpPos
.Y() += (aRect
.GetHeight()-aBmpSize
.Height())/2;
12006 case WallpaperStyle::Right
:
12007 aBmpPos
.X() += aRect
.GetWidth()-aBmpSize
.Width();
12008 aBmpPos
.Y() += (aRect
.GetHeight()-aBmpSize
.Height())/2;
12010 case WallpaperStyle::BottomLeft
:
12011 aBmpPos
.Y() += aRect
.GetHeight()-aBmpSize
.Height();
12013 case WallpaperStyle::Bottom
:
12014 aBmpPos
.X() += (aRect
.GetWidth()-aBmpSize
.Width())/2;
12015 aBmpPos
.Y() += aRect
.GetHeight()-aBmpSize
.Height();
12017 case WallpaperStyle::BottomRight
:
12018 aBmpPos
.X() += aRect
.GetWidth()-aBmpSize
.Width();
12019 aBmpPos
.Y() += aRect
.GetHeight()-aBmpSize
.Height();
12027 const BitmapEmit
& rEmit
= createBitmapEmit( BitmapEx( aBitmap
), Graphic() );
12029 // convert to page coordinates; this needs to be done here
12030 // since the emit does not know the page anymore
12031 tools::Rectangle
aConvertRect( aBmpPos
, aBmpSize
);
12032 m_aPages
.back().convertRect( aConvertRect
);
12034 OStringBuffer
aNameBuf(16);
12035 aNameBuf
.append( "Im" );
12036 aNameBuf
.append( rEmit
.m_nObject
);
12037 OString
aImageName( aNameBuf
.makeStringAndClear() );
12039 // push the pattern
12040 OStringBuffer
aTilingStream( 32 );
12041 appendFixedInt( aConvertRect
.GetWidth(), aTilingStream
);
12042 aTilingStream
.append( " 0 0 " );
12043 appendFixedInt( aConvertRect
.GetHeight(), aTilingStream
);
12044 aTilingStream
.append( " 0 0 cm\n/" );
12045 aTilingStream
.append( aImageName
);
12046 aTilingStream
.append( " Do\n" );
12048 m_aTilings
.push_back( TilingEmit() );
12049 m_aTilings
.back().m_nObject
= createObject();
12050 m_aTilings
.back().m_aRectangle
= tools::Rectangle( Point( 0, 0 ), aConvertRect
.GetSize() );
12051 m_aTilings
.back().m_pTilingStream
= new SvMemoryStream();
12052 m_aTilings
.back().m_pTilingStream
->WriteBytes(
12053 aTilingStream
.getStr(), aTilingStream
.getLength() );
12054 // phase the tiling so wallpaper begins on upper left
12055 if ((aConvertRect
.GetWidth() == 0) || (aConvertRect
.GetHeight() == 0))
12056 throw o3tl::divide_by_zero();
12057 m_aTilings
.back().m_aTransform
.matrix
[2] = double(aConvertRect
.Left() % aConvertRect
.GetWidth()) / fDivisor
;
12058 m_aTilings
.back().m_aTransform
.matrix
[5] = double(aConvertRect
.Top() % aConvertRect
.GetHeight()) / fDivisor
;
12059 m_aTilings
.back().m_aResources
.m_aXObjects
[aImageName
] = rEmit
.m_nObject
;
12061 updateGraphicsState();
12063 OStringBuffer
aObjName( 16 );
12064 aObjName
.append( 'P' );
12065 aObjName
.append( m_aTilings
.back().m_nObject
);
12066 OString
aPatternName( aObjName
.makeStringAndClear() );
12067 pushResource( ResPattern
, aPatternName
, m_aTilings
.back().m_nObject
);
12069 // fill a rRect with the pattern
12070 OStringBuffer
aLine( 100 );
12071 aLine
.append( "q /Pattern cs /" );
12072 aLine
.append( aPatternName
);
12073 aLine
.append( " scn " );
12074 m_aPages
.back().appendRect( rRect
, aLine
);
12075 aLine
.append( " f Q\n" );
12076 writeBuffer( aLine
.getStr(), aLine
.getLength() );
12081 aBmpPos
= aRect
.TopLeft();
12082 aBmpSize
= aRect
.GetSize();
12083 bDrawBitmap
= true;
12086 if( aBitmap
.IsTransparent() )
12088 if( rWall
.IsGradient() )
12089 bDrawGradient
= true;
12094 else if( rWall
.IsGradient() )
12095 bDrawGradient
= true;
12099 if( bDrawGradient
)
12101 drawGradient( rRect
, rWall
.GetGradient() );
12105 Color aOldLineColor
= m_aGraphicsStack
.front().m_aLineColor
;
12106 Color aOldFillColor
= m_aGraphicsStack
.front().m_aFillColor
;
12107 setLineColor( Color( COL_TRANSPARENT
) );
12108 setFillColor( rWall
.GetColor() );
12109 drawRectangle( rRect
);
12110 setLineColor( aOldLineColor
);
12111 setFillColor( aOldFillColor
);
12115 // set temporary clip region since aBmpPos and aBmpSize
12116 // may be outside rRect
12117 OStringBuffer
aLine( 20 );
12118 aLine
.append( "q " );
12119 m_aPages
.back().appendRect( rRect
, aLine
);
12120 aLine
.append( " W n\n" );
12121 writeBuffer( aLine
.getStr(), aLine
.getLength() );
12122 drawBitmap( aBmpPos
, aBmpSize
, aBitmap
);
12123 writeBuffer( "Q\n", 2 );
12127 void PDFWriterImpl::updateGraphicsState(Mode
const mode
)
12129 OStringBuffer
aLine( 256 );
12130 GraphicsState
& rNewState
= m_aGraphicsStack
.front();
12131 // first set clip region since it might invalidate everything else
12133 if( (rNewState
.m_nUpdateFlags
& GraphicsStateUpdateFlags::ClipRegion
) )
12135 rNewState
.m_nUpdateFlags
&= ~GraphicsStateUpdateFlags::ClipRegion
;
12137 if( m_aCurrentPDFState
.m_bClipRegion
!= rNewState
.m_bClipRegion
||
12138 ( rNewState
.m_bClipRegion
&& m_aCurrentPDFState
.m_aClipRegion
!= rNewState
.m_aClipRegion
) )
12140 if( m_aCurrentPDFState
.m_bClipRegion
)
12142 aLine
.append( "Q " );
12143 // invalidate everything but the clip region
12144 m_aCurrentPDFState
= GraphicsState();
12145 rNewState
.m_nUpdateFlags
= ~GraphicsStateUpdateFlags::ClipRegion
;
12147 if( rNewState
.m_bClipRegion
)
12149 // clip region is always stored in private PDF mapmode
12150 MapMode aNewMapMode
= rNewState
.m_aMapMode
;
12151 rNewState
.m_aMapMode
= m_aMapMode
;
12152 getReferenceDevice()->SetMapMode( rNewState
.m_aMapMode
);
12153 m_aCurrentPDFState
.m_aMapMode
= rNewState
.m_aMapMode
;
12155 aLine
.append("q ");
12156 if ( rNewState
.m_aClipRegion
.count() )
12158 m_aPages
.back().appendPolyPolygon( rNewState
.m_aClipRegion
, aLine
);
12159 aLine
.append( "W* n\n" );
12162 rNewState
.m_aMapMode
= aNewMapMode
;
12163 getReferenceDevice()->SetMapMode( rNewState
.m_aMapMode
);
12164 m_aCurrentPDFState
.m_aMapMode
= rNewState
.m_aMapMode
;
12169 if( (rNewState
.m_nUpdateFlags
& GraphicsStateUpdateFlags::MapMode
) )
12171 rNewState
.m_nUpdateFlags
&= ~GraphicsStateUpdateFlags::MapMode
;
12172 getReferenceDevice()->SetMapMode( rNewState
.m_aMapMode
);
12175 if( (rNewState
.m_nUpdateFlags
& GraphicsStateUpdateFlags::Font
) )
12177 rNewState
.m_nUpdateFlags
&= ~GraphicsStateUpdateFlags::Font
;
12178 getReferenceDevice()->SetFont( rNewState
.m_aFont
);
12179 getReferenceDevice()->ImplNewFont();
12182 if( (rNewState
.m_nUpdateFlags
& GraphicsStateUpdateFlags::LayoutMode
) )
12184 rNewState
.m_nUpdateFlags
&= ~GraphicsStateUpdateFlags::LayoutMode
;
12185 getReferenceDevice()->SetLayoutMode( rNewState
.m_nLayoutMode
);
12188 if( (rNewState
.m_nUpdateFlags
& GraphicsStateUpdateFlags::DigitLanguage
) )
12190 rNewState
.m_nUpdateFlags
&= ~GraphicsStateUpdateFlags::DigitLanguage
;
12191 getReferenceDevice()->SetDigitLanguage( rNewState
.m_aDigitLanguage
);
12194 if( (rNewState
.m_nUpdateFlags
& GraphicsStateUpdateFlags::LineColor
) )
12196 rNewState
.m_nUpdateFlags
&= ~GraphicsStateUpdateFlags::LineColor
;
12197 if( m_aCurrentPDFState
.m_aLineColor
!= rNewState
.m_aLineColor
&&
12198 rNewState
.m_aLineColor
!= Color( COL_TRANSPARENT
) )
12200 appendStrokingColor( rNewState
.m_aLineColor
, aLine
);
12201 aLine
.append( "\n" );
12205 if( (rNewState
.m_nUpdateFlags
& GraphicsStateUpdateFlags::FillColor
) )
12207 rNewState
.m_nUpdateFlags
&= ~GraphicsStateUpdateFlags::FillColor
;
12208 if( m_aCurrentPDFState
.m_aFillColor
!= rNewState
.m_aFillColor
&&
12209 rNewState
.m_aFillColor
!= Color( COL_TRANSPARENT
) )
12211 appendNonStrokingColor( rNewState
.m_aFillColor
, aLine
);
12212 aLine
.append( "\n" );
12216 if( (rNewState
.m_nUpdateFlags
& GraphicsStateUpdateFlags::TransparentPercent
) )
12218 rNewState
.m_nUpdateFlags
&= ~GraphicsStateUpdateFlags::TransparentPercent
;
12219 if( m_aContext
.Version
>= PDFWriter::PDFVersion::PDF_1_4
&& m_aCurrentPDFState
.m_nTransparentPercent
!= rNewState
.m_nTransparentPercent
)
12221 // TODO: switch extended graphicsstate
12225 // everything is up to date now
12226 m_aCurrentPDFState
= m_aGraphicsStack
.front();
12227 if ((mode
!= NOWRITE
) && !aLine
.isEmpty())
12228 writeBuffer( aLine
.getStr(), aLine
.getLength() );
12231 /* #i47544# imitate OutputDevice behaviour:
12232 * if a font with a nontransparent color is set, it overwrites the current
12233 * text color. OTOH setting the text color will overwrite the color of the font.
12235 void PDFWriterImpl::setFont( const vcl::Font
& rFont
)
12237 Color aColor
= rFont
.GetColor();
12238 if( aColor
== Color( COL_TRANSPARENT
) )
12239 aColor
= m_aGraphicsStack
.front().m_aFont
.GetColor();
12240 m_aGraphicsStack
.front().m_aFont
= rFont
;
12241 m_aGraphicsStack
.front().m_aFont
.SetColor( aColor
);
12242 m_aGraphicsStack
.front().m_nUpdateFlags
|= GraphicsStateUpdateFlags::Font
;
12245 void PDFWriterImpl::push( PushFlags nFlags
)
12247 OSL_ENSURE( !m_aGraphicsStack
.empty(), "invalid graphics stack" );
12248 m_aGraphicsStack
.push_front( m_aGraphicsStack
.front() );
12249 m_aGraphicsStack
.front().m_nFlags
= nFlags
;
12252 void PDFWriterImpl::pop()
12254 OSL_ENSURE( m_aGraphicsStack
.size() > 1, "pop without push" );
12255 if( m_aGraphicsStack
.size() < 2 )
12258 GraphicsState aState
= m_aGraphicsStack
.front();
12259 m_aGraphicsStack
.pop_front();
12260 GraphicsState
& rOld
= m_aGraphicsStack
.front();
12262 // move those parameters back that were not pushed
12263 // in the first place
12264 if( ! (aState
.m_nFlags
& PushFlags::LINECOLOR
) )
12265 setLineColor( aState
.m_aLineColor
);
12266 if( ! (aState
.m_nFlags
& PushFlags::FILLCOLOR
) )
12267 setFillColor( aState
.m_aFillColor
);
12268 if( ! (aState
.m_nFlags
& PushFlags::FONT
) )
12269 setFont( aState
.m_aFont
);
12270 if( ! (aState
.m_nFlags
& PushFlags::TEXTCOLOR
) )
12271 setTextColor( aState
.m_aFont
.GetColor() );
12272 if( ! (aState
.m_nFlags
& PushFlags::MAPMODE
) )
12273 setMapMode( aState
.m_aMapMode
);
12274 if( ! (aState
.m_nFlags
& PushFlags::CLIPREGION
) )
12276 // do not use setClipRegion here
12277 // it would convert again assuming the current mapmode
12278 rOld
.m_aClipRegion
= aState
.m_aClipRegion
;
12279 rOld
.m_bClipRegion
= aState
.m_bClipRegion
;
12281 if( ! (aState
.m_nFlags
& PushFlags::TEXTLINECOLOR
) )
12282 setTextLineColor( aState
.m_aTextLineColor
);
12283 if( ! (aState
.m_nFlags
& PushFlags::OVERLINECOLOR
) )
12284 setOverlineColor( aState
.m_aOverlineColor
);
12285 if( ! (aState
.m_nFlags
& PushFlags::TEXTALIGN
) )
12286 setTextAlign( aState
.m_aFont
.GetAlignment() );
12287 if( ! (aState
.m_nFlags
& PushFlags::TEXTFILLCOLOR
) )
12288 setTextFillColor( aState
.m_aFont
.GetFillColor() );
12289 if( ! (aState
.m_nFlags
& PushFlags::REFPOINT
) )
12293 // invalidate graphics state
12294 m_aGraphicsStack
.front().m_nUpdateFlags
= GraphicsStateUpdateFlags::All
;
12297 void PDFWriterImpl::setMapMode( const MapMode
& rMapMode
)
12299 m_aGraphicsStack
.front().m_aMapMode
= rMapMode
;
12300 getReferenceDevice()->SetMapMode( rMapMode
);
12301 m_aCurrentPDFState
.m_aMapMode
= rMapMode
;
12304 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon
& rRegion
)
12306 basegfx::B2DPolyPolygon aRegion
= getReferenceDevice()->LogicToPixel( rRegion
, m_aGraphicsStack
.front().m_aMapMode
);
12307 aRegion
= getReferenceDevice()->PixelToLogic( aRegion
, m_aMapMode
);
12308 m_aGraphicsStack
.front().m_aClipRegion
= aRegion
;
12309 m_aGraphicsStack
.front().m_bClipRegion
= true;
12310 m_aGraphicsStack
.front().m_nUpdateFlags
|= GraphicsStateUpdateFlags::ClipRegion
;
12313 void PDFWriterImpl::moveClipRegion( sal_Int32 nX
, sal_Int32 nY
)
12315 if( m_aGraphicsStack
.front().m_bClipRegion
&& m_aGraphicsStack
.front().m_aClipRegion
.count() )
12317 Point
aPoint( lcl_convert( m_aGraphicsStack
.front().m_aMapMode
,
12319 getReferenceDevice(),
12320 Point( nX
, nY
) ) );
12321 aPoint
-= lcl_convert( m_aGraphicsStack
.front().m_aMapMode
,
12323 getReferenceDevice(),
12325 basegfx::B2DHomMatrix aMat
;
12326 aMat
.translate( aPoint
.X(), aPoint
.Y() );
12327 m_aGraphicsStack
.front().m_aClipRegion
.transform( aMat
);
12328 m_aGraphicsStack
.front().m_nUpdateFlags
|= GraphicsStateUpdateFlags::ClipRegion
;
12332 void PDFWriterImpl::intersectClipRegion( const tools::Rectangle
& rRect
)
12334 basegfx::B2DPolyPolygon
aRect( basegfx::tools::createPolygonFromRect(
12335 basegfx::B2DRectangle( rRect
.Left(), rRect
.Top(), rRect
.Right(), rRect
.Bottom() ) ) );
12336 intersectClipRegion( aRect
);
12339 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon
& rRegion
)
12341 basegfx::B2DPolyPolygon
aRegion( getReferenceDevice()->LogicToPixel( rRegion
, m_aGraphicsStack
.front().m_aMapMode
) );
12342 aRegion
= getReferenceDevice()->PixelToLogic( aRegion
, m_aMapMode
);
12343 m_aGraphicsStack
.front().m_nUpdateFlags
|= GraphicsStateUpdateFlags::ClipRegion
;
12344 if( m_aGraphicsStack
.front().m_bClipRegion
)
12346 basegfx::B2DPolyPolygon
aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack
.front().m_aClipRegion
) );
12347 aRegion
= basegfx::tools::prepareForPolygonOperation( aRegion
);
12348 m_aGraphicsStack
.front().m_aClipRegion
= basegfx::tools::solvePolygonOperationAnd( aOld
, aRegion
);
12352 m_aGraphicsStack
.front().m_aClipRegion
= aRegion
;
12353 m_aGraphicsStack
.front().m_bClipRegion
= true;
12358 void PDFWriterImpl::createNote( const tools::Rectangle
& rRect
, const PDFNote
& rNote
, sal_Int32 nPageNr
)
12361 nPageNr
= m_nCurrentPage
;
12363 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
12366 m_aNotes
.push_back( PDFNoteEntry() );
12367 m_aNotes
.back().m_nObject
= createObject();
12368 m_aNotes
.back().m_aContents
= rNote
;
12369 m_aNotes
.back().m_aRect
= rRect
;
12370 // convert to default user space now, since the mapmode may change
12371 m_aPages
[nPageNr
].convertRect( m_aNotes
.back().m_aRect
);
12373 // insert note to page's annotation list
12374 m_aPages
[ nPageNr
].m_aAnnotations
.push_back( m_aNotes
.back().m_nObject
);
12377 sal_Int32
PDFWriterImpl::createLink( const tools::Rectangle
& rRect
, sal_Int32 nPageNr
)
12380 nPageNr
= m_nCurrentPage
;
12382 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
12385 sal_Int32 nRet
= m_aLinks
.size();
12387 m_aLinks
.push_back( PDFLink() );
12388 m_aLinks
.back().m_nObject
= createObject();
12389 m_aLinks
.back().m_nPage
= nPageNr
;
12390 m_aLinks
.back().m_aRect
= rRect
;
12391 // convert to default user space now, since the mapmode may change
12392 m_aPages
[nPageNr
].convertRect( m_aLinks
.back().m_aRect
);
12394 // insert link to page's annotation list
12395 m_aPages
[ nPageNr
].m_aAnnotations
.push_back( m_aLinks
.back().m_nObject
);
12400 sal_Int32
PDFWriterImpl::createScreen(const tools::Rectangle
& rRect
, sal_Int32 nPageNr
)
12403 nPageNr
= m_nCurrentPage
;
12405 if (nPageNr
< 0 || nPageNr
>= static_cast<sal_Int32
>(m_aPages
.size()))
12408 sal_Int32 nRet
= m_aScreens
.size();
12410 m_aScreens
.push_back(PDFScreen());
12411 m_aScreens
.back().m_nObject
= createObject();
12412 m_aScreens
.back().m_nPage
= nPageNr
;
12413 m_aScreens
.back().m_aRect
= rRect
;
12414 // Convert to default user space now, since the mapmode may change.
12415 m_aPages
[nPageNr
].convertRect(m_aScreens
.back().m_aRect
);
12417 // Insert link to page's annotation list.
12418 m_aPages
[nPageNr
].m_aAnnotations
.push_back(m_aScreens
.back().m_nObject
);
12423 sal_Int32
PDFWriterImpl::createNamedDest( const OUString
& sDestName
, const tools::Rectangle
& rRect
, sal_Int32 nPageNr
, PDFWriter::DestAreaType eType
)
12426 nPageNr
= m_nCurrentPage
;
12428 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
12431 sal_Int32 nRet
= m_aNamedDests
.size();
12433 m_aNamedDests
.push_back( PDFNamedDest() );
12434 m_aNamedDests
.back().m_aDestName
= sDestName
;
12435 m_aNamedDests
.back().m_nPage
= nPageNr
;
12436 m_aNamedDests
.back().m_eType
= eType
;
12437 m_aNamedDests
.back().m_aRect
= rRect
;
12438 // convert to default user space now, since the mapmode may change
12439 m_aPages
[nPageNr
].convertRect( m_aNamedDests
.back().m_aRect
);
12444 sal_Int32
PDFWriterImpl::createDest( const tools::Rectangle
& rRect
, sal_Int32 nPageNr
, PDFWriter::DestAreaType eType
)
12447 nPageNr
= m_nCurrentPage
;
12449 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
12452 sal_Int32 nRet
= m_aDests
.size();
12454 m_aDests
.push_back( PDFDest() );
12455 m_aDests
.back().m_nPage
= nPageNr
;
12456 m_aDests
.back().m_eType
= eType
;
12457 m_aDests
.back().m_aRect
= rRect
;
12458 // convert to default user space now, since the mapmode may change
12459 m_aPages
[nPageNr
].convertRect( m_aDests
.back().m_aRect
);
12464 sal_Int32
PDFWriterImpl::registerDestReference( sal_Int32 nDestId
, const tools::Rectangle
& rRect
, sal_Int32 nPageNr
, PDFWriter::DestAreaType eType
)
12466 return m_aDestinationIdTranslation
[ nDestId
] = createDest( rRect
, nPageNr
, eType
);
12469 void PDFWriterImpl::setLinkDest( sal_Int32 nLinkId
, sal_Int32 nDestId
)
12471 if( nLinkId
< 0 || nLinkId
>= (sal_Int32
)m_aLinks
.size() )
12473 if( nDestId
< 0 || nDestId
>= (sal_Int32
)m_aDests
.size() )
12476 m_aLinks
[ nLinkId
].m_nDest
= nDestId
;
12479 void PDFWriterImpl::setLinkURL( sal_Int32 nLinkId
, const OUString
& rURL
)
12481 if( nLinkId
< 0 || nLinkId
>= (sal_Int32
)m_aLinks
.size() )
12484 m_aLinks
[ nLinkId
].m_nDest
= -1;
12486 using namespace ::com::sun::star
;
12488 if (!m_xTrans
.is())
12490 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getProcessComponentContext() );
12491 m_xTrans
= util::URLTransformer::create(xContext
);
12495 aURL
.Complete
= rURL
;
12497 m_xTrans
->parseStrict( aURL
);
12499 m_aLinks
[ nLinkId
].m_aURL
= aURL
.Complete
;
12502 void PDFWriterImpl::setScreenURL(sal_Int32 nScreenId
, const OUString
& rURL
)
12504 if (nScreenId
< 0 || nScreenId
>= static_cast<sal_Int32
>(m_aScreens
.size()))
12507 m_aScreens
[nScreenId
].m_aURL
= rURL
;
12510 void PDFWriterImpl::setScreenStream(sal_Int32 nScreenId
, const OUString
& rURL
)
12512 if (nScreenId
< 0 || nScreenId
>= static_cast<sal_Int32
>(m_aScreens
.size()))
12515 m_aScreens
[nScreenId
].m_aTempFileURL
= rURL
;
12516 m_aScreens
[nScreenId
].m_nTempFileObject
= createObject();
12519 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId
, sal_Int32 nPropertyId
)
12521 m_aLinkPropertyMap
[ nPropertyId
] = nLinkId
;
12524 sal_Int32
PDFWriterImpl::createOutlineItem( sal_Int32 nParent
, const OUString
& rText
, sal_Int32 nDestID
)
12527 sal_Int32 nNewItem
= m_aOutline
.size();
12528 m_aOutline
.push_back( PDFOutlineEntry() );
12530 // set item attributes
12531 setOutlineItemParent( nNewItem
, nParent
);
12532 setOutlineItemText( nNewItem
, rText
);
12533 setOutlineItemDest( nNewItem
, nDestID
);
12538 void PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem
, sal_Int32 nNewParent
)
12540 if( nItem
< 1 || nItem
>= (sal_Int32
)m_aOutline
.size() )
12543 if( nNewParent
< 0 || nNewParent
>= (sal_Int32
)m_aOutline
.size() || nNewParent
== nItem
)
12547 // insert item to new parent's list of children
12548 m_aOutline
[ nNewParent
].m_aChildren
.push_back( nItem
);
12551 void PDFWriterImpl::setOutlineItemText( sal_Int32 nItem
, const OUString
& rText
)
12553 if( nItem
< 1 || nItem
>= (sal_Int32
)m_aOutline
.size() )
12556 m_aOutline
[ nItem
].m_aTitle
= psp::WhitespaceToSpace( rText
);
12559 void PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem
, sal_Int32 nDestID
)
12561 if( nItem
< 1 || nItem
>= (sal_Int32
)m_aOutline
.size() ) // item does not exist
12563 if( nDestID
< 0 || nDestID
>= (sal_Int32
)m_aDests
.size() ) // dest does not exist
12565 m_aOutline
[nItem
].m_nDestID
= nDestID
;
12568 const sal_Char
* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType
)
12570 static std::map
< PDFWriter::StructElement
, const char* > aTagStrings
;
12571 if( aTagStrings
.empty() )
12573 aTagStrings
[ PDFWriter::NonStructElement
] = "NonStruct";
12574 aTagStrings
[ PDFWriter::Document
] = "Document";
12575 aTagStrings
[ PDFWriter::Part
] = "Part";
12576 aTagStrings
[ PDFWriter::Article
] = "Art";
12577 aTagStrings
[ PDFWriter::Section
] = "Sect";
12578 aTagStrings
[ PDFWriter::Division
] = "Div";
12579 aTagStrings
[ PDFWriter::BlockQuote
] = "BlockQuote";
12580 aTagStrings
[ PDFWriter::Caption
] = "Caption";
12581 aTagStrings
[ PDFWriter::TOC
] = "TOC";
12582 aTagStrings
[ PDFWriter::TOCI
] = "TOCI";
12583 aTagStrings
[ PDFWriter::Index
] = "Index";
12584 aTagStrings
[ PDFWriter::Paragraph
] = "P";
12585 aTagStrings
[ PDFWriter::Heading
] = "H";
12586 aTagStrings
[ PDFWriter::H1
] = "H1";
12587 aTagStrings
[ PDFWriter::H2
] = "H2";
12588 aTagStrings
[ PDFWriter::H3
] = "H3";
12589 aTagStrings
[ PDFWriter::H4
] = "H4";
12590 aTagStrings
[ PDFWriter::H5
] = "H5";
12591 aTagStrings
[ PDFWriter::H6
] = "H6";
12592 aTagStrings
[ PDFWriter::List
] = "L";
12593 aTagStrings
[ PDFWriter::ListItem
] = "LI";
12594 aTagStrings
[ PDFWriter::LILabel
] = "Lbl";
12595 aTagStrings
[ PDFWriter::LIBody
] = "LBody";
12596 aTagStrings
[ PDFWriter::Table
] = "Table";
12597 aTagStrings
[ PDFWriter::TableRow
] = "TR";
12598 aTagStrings
[ PDFWriter::TableHeader
] = "TH";
12599 aTagStrings
[ PDFWriter::TableData
] = "TD";
12600 aTagStrings
[ PDFWriter::Span
] = "Span";
12601 aTagStrings
[ PDFWriter::Quote
] = "Quote";
12602 aTagStrings
[ PDFWriter::Note
] = "Note";
12603 aTagStrings
[ PDFWriter::Reference
] = "Reference";
12604 aTagStrings
[ PDFWriter::BibEntry
] = "BibEntry";
12605 aTagStrings
[ PDFWriter::Code
] = "Code";
12606 aTagStrings
[ PDFWriter::Link
] = "Link";
12607 aTagStrings
[ PDFWriter::Figure
] = "Figure";
12608 aTagStrings
[ PDFWriter::Formula
] = "Formula";
12609 aTagStrings
[ PDFWriter::Form
] = "Form";
12612 std::map
< PDFWriter::StructElement
, const char* >::const_iterator it
= aTagStrings
.find( eType
);
12614 return it
!= aTagStrings
.end() ? it
->second
: "Div";
12617 void PDFWriterImpl::beginStructureElementMCSeq()
12619 if( m_bEmitStructure
&&
12620 m_nCurrentStructElement
> 0 && // StructTreeRoot
12621 ! m_aStructure
[ m_nCurrentStructElement
].m_bOpenMCSeq
// already opened sequence
12624 PDFStructureElement
& rEle
= m_aStructure
[ m_nCurrentStructElement
];
12625 OStringBuffer
aLine( 128 );
12626 sal_Int32 nMCID
= m_aPages
[ m_nCurrentPage
].m_aMCIDParents
.size();
12627 aLine
.append( "/" );
12628 if( !rEle
.m_aAlias
.isEmpty() )
12629 aLine
.append( rEle
.m_aAlias
);
12631 aLine
.append( getStructureTag( rEle
.m_eType
) );
12632 aLine
.append( "<</MCID " );
12633 aLine
.append( nMCID
);
12634 aLine
.append( ">>BDC\n" );
12635 writeBuffer( aLine
.getStr(), aLine
.getLength() );
12637 // update the element's content list
12638 #if OSL_DEBUG_LEVEL > 1
12639 SAL_INFO("vcl.pdfwriter", "beginning marked content id " << nMCID
<< " on page object "
12640 << m_aPages
[ m_nCurrentPage
].m_nPageObject
<< ", structure first page = "
12641 << rEle
.m_nFirstPageObject
);
12643 rEle
.m_aKids
.push_back( PDFStructureElementKid( nMCID
, m_aPages
[m_nCurrentPage
].m_nPageObject
) );
12644 // update the page's mcid parent list
12645 m_aPages
[ m_nCurrentPage
].m_aMCIDParents
.push_back( rEle
.m_nObject
);
12646 // mark element MC sequence as open
12647 rEle
.m_bOpenMCSeq
= true;
12649 // handle artifacts
12650 else if( ! m_bEmitStructure
&& m_aContext
.Tagged
&&
12651 m_nCurrentStructElement
> 0 &&
12652 m_aStructure
[ m_nCurrentStructElement
].m_eType
== PDFWriter::NonStructElement
&&
12653 ! m_aStructure
[ m_nCurrentStructElement
].m_bOpenMCSeq
// already opened sequence
12656 OStringBuffer
aLine( 128 );
12657 aLine
.append( "/Artifact BMC\n" );
12658 writeBuffer( aLine
.getStr(), aLine
.getLength() );
12659 // mark element MC sequence as open
12660 m_aStructure
[ m_nCurrentStructElement
].m_bOpenMCSeq
= true;
12664 void PDFWriterImpl::endStructureElementMCSeq()
12666 if( m_nCurrentStructElement
> 0 && // StructTreeRoot
12667 ( m_bEmitStructure
|| m_aStructure
[ m_nCurrentStructElement
].m_eType
== PDFWriter::NonStructElement
) &&
12668 m_aStructure
[ m_nCurrentStructElement
].m_bOpenMCSeq
// must have an opened MC sequence
12671 writeBuffer( "EMC\n", 4 );
12672 m_aStructure
[ m_nCurrentStructElement
].m_bOpenMCSeq
= false;
12676 bool PDFWriterImpl::checkEmitStructure()
12678 bool bEmit
= false;
12679 if( m_aContext
.Tagged
)
12682 sal_Int32 nEle
= m_nCurrentStructElement
;
12683 while( nEle
> 0 && nEle
< sal_Int32(m_aStructure
.size()) )
12685 if( m_aStructure
[ nEle
].m_eType
== PDFWriter::NonStructElement
)
12690 nEle
= m_aStructure
[ nEle
].m_nParentElement
;
12696 sal_Int32
PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType
, const OUString
& rAlias
)
12698 if( m_nCurrentPage
< 0 )
12701 if( ! m_aContext
.Tagged
)
12704 // close eventual current MC sequence
12705 endStructureElementMCSeq();
12707 if( m_nCurrentStructElement
== 0 &&
12708 eType
!= PDFWriter::Document
&& eType
!= PDFWriter::NonStructElement
)
12710 // struct tree root hit, but not beginning document
12711 // this might happen with setCurrentStructureElement
12712 // silently insert structure into document again if one properly exists
12713 if( ! m_aStructure
[ 0 ].m_aChildren
.empty() )
12715 PDFWriter::StructElement childType
= PDFWriter::NonStructElement
;
12716 sal_Int32 nNewCurElement
= 0;
12717 const std::list
< sal_Int32
>& rRootChildren
= m_aStructure
[0].m_aChildren
;
12718 for( std::list
< sal_Int32
>::const_iterator it
= rRootChildren
.begin();
12719 childType
!= PDFWriter::Document
&& it
!= rRootChildren
.end(); ++it
)
12721 nNewCurElement
= *it
;
12722 childType
= m_aStructure
[ nNewCurElement
].m_eType
;
12724 if( childType
== PDFWriter::Document
)
12726 m_nCurrentStructElement
= nNewCurElement
;
12727 SAL_WARN( "vcl.pdfwriter", "Structure element inserted to StructTreeRoot that is not a document" );
12730 OSL_FAIL( "document structure in disorder !" );
12734 OSL_FAIL( "PDF document structure MUST be contained in a Document element" );
12738 sal_Int32 nNewId
= sal_Int32(m_aStructure
.size());
12739 m_aStructure
.push_back( PDFStructureElement() );
12740 PDFStructureElement
& rEle
= m_aStructure
.back();
12741 rEle
.m_eType
= eType
;
12742 rEle
.m_nOwnElement
= nNewId
;
12743 rEle
.m_nParentElement
= m_nCurrentStructElement
;
12744 rEle
.m_nFirstPageObject
= m_aPages
[ m_nCurrentPage
].m_nPageObject
;
12745 m_aStructure
[ m_nCurrentStructElement
].m_aChildren
.push_back( nNewId
);
12746 m_nCurrentStructElement
= nNewId
;
12748 // handle alias names
12749 if( !rAlias
.isEmpty() && eType
!= PDFWriter::NonStructElement
)
12751 OStringBuffer
aNameBuf( rAlias
.getLength() );
12752 appendName( rAlias
, aNameBuf
);
12753 OString
aAliasName( aNameBuf
.makeStringAndClear() );
12754 rEle
.m_aAlias
= aAliasName
;
12755 m_aRoleMap
[ aAliasName
] = getStructureTag( eType
);
12758 #if OSL_DEBUG_LEVEL > 1
12759 OStringBuffer
aLine( "beginStructureElement " );
12760 aLine
.append( m_nCurrentStructElement
);
12761 aLine
.append( ": " );
12762 aLine
.append( getStructureTag( eType
) );
12763 if( !rEle
.m_aAlias
.isEmpty() )
12765 aLine
.append( " aliased as \"" );
12766 aLine
.append( rEle
.m_aAlias
);
12767 aLine
.append( '\"' );
12769 emitComment( aLine
.getStr() );
12772 // check whether to emit structure henceforth
12773 m_bEmitStructure
= checkEmitStructure();
12775 if( m_bEmitStructure
) // don't create nonexistent objects
12777 rEle
.m_nObject
= createObject();
12778 // update parent's kids list
12779 m_aStructure
[ rEle
.m_nParentElement
].m_aKids
.push_back(PDFStructureElementKid(rEle
.m_nObject
));
12784 void PDFWriterImpl::endStructureElement()
12786 if( m_nCurrentPage
< 0 )
12789 if( ! m_aContext
.Tagged
)
12792 if( m_nCurrentStructElement
== 0 )
12794 // hit the struct tree root, that means there is an endStructureElement
12795 // without corresponding beginStructureElement
12799 // end the marked content sequence
12800 endStructureElementMCSeq();
12802 #if OSL_DEBUG_LEVEL > 1
12803 OStringBuffer
aLine( "endStructureElement " );
12804 aLine
.append( m_nCurrentStructElement
);
12805 aLine
.append( ": " );
12806 aLine
.append( getStructureTag( m_aStructure
[ m_nCurrentStructElement
].m_eType
) );
12807 if( !m_aStructure
[ m_nCurrentStructElement
].m_aAlias
.isEmpty() )
12809 aLine
.append( " aliased as \"" );
12810 aLine
.append( m_aStructure
[ m_nCurrentStructElement
].m_aAlias
);
12811 aLine
.append( '\"' );
12815 // "end" the structure element, the parent becomes current element
12816 m_nCurrentStructElement
= m_aStructure
[ m_nCurrentStructElement
].m_nParentElement
;
12818 // check whether to emit structure henceforth
12819 m_bEmitStructure
= checkEmitStructure();
12821 #if OSL_DEBUG_LEVEL > 1
12822 if( m_bEmitStructure
)
12823 emitComment( aLine
.getStr() );
12828 * This function adds an internal structure list container to overcome the 8191 elements array limitation
12829 * in kids element emission.
12830 * Recursive function
12833 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement
& rEle
)
12835 if( rEle
.m_eType
== PDFWriter::NonStructElement
&&
12836 rEle
.m_nOwnElement
!= rEle
.m_nParentElement
)
12839 for( std::list
< sal_Int32
>::const_iterator it
= rEle
.m_aChildren
.begin(); it
!= rEle
.m_aChildren
.end(); ++it
)
12841 if( *it
> 0 && *it
< sal_Int32(m_aStructure
.size()) )
12843 PDFStructureElement
& rChild
= m_aStructure
[ *it
];
12844 if( rChild
.m_eType
!= PDFWriter::NonStructElement
)
12846 //triggered when a child of the rEle element is found
12847 if( rChild
.m_nParentElement
== rEle
.m_nOwnElement
)
12848 addInternalStructureContainer( rChild
);//examine the child
12851 OSL_FAIL( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
12852 #if OSL_DEBUG_LEVEL > 1
12853 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id " << *it
);
12860 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
12861 #if OSL_DEBUG_LEVEL > 1
12862 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::addInternalStructureContainer: invalid child structure id " << *it
);
12867 if( rEle
.m_nOwnElement
!= rEle
.m_nParentElement
)
12869 if( !rEle
.m_aKids
.empty() )
12871 if( rEle
.m_aKids
.size() > ncMaxPDFArraySize
) {
12872 //then we need to add the containers for the kids elements
12873 // a list to be used for the new kid element
12874 std::list
< PDFStructureElementKid
> aNewKids
;
12875 std::list
< sal_Int32
> aNewChildren
;
12877 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
12878 OStringBuffer
aNameBuf( "Div" );
12879 OString
aAliasName( aNameBuf
.makeStringAndClear() );
12880 m_aRoleMap
[ aAliasName
] = getStructureTag( PDFWriter::Division
);
12882 while( rEle
.m_aKids
.size() > ncMaxPDFArraySize
)
12884 sal_Int32 nCurrentStructElement
= rEle
.m_nOwnElement
;
12885 sal_Int32 nNewId
= sal_Int32(m_aStructure
.size());
12886 m_aStructure
.push_back( PDFStructureElement() );
12887 PDFStructureElement
& rEleNew
= m_aStructure
.back();
12888 rEleNew
.m_aAlias
= aAliasName
;
12889 rEleNew
.m_eType
= PDFWriter::Division
; // a new Div type container
12890 rEleNew
.m_nOwnElement
= nNewId
;
12891 rEleNew
.m_nParentElement
= nCurrentStructElement
;
12892 //inherit the same page as the first child to be reparented
12893 rEleNew
.m_nFirstPageObject
= m_aStructure
[ rEle
.m_aChildren
.front() ].m_nFirstPageObject
;
12894 rEleNew
.m_nObject
= createObject();//assign a PDF object number
12895 //add the object to the kid list of the parent
12896 aNewKids
.push_back( PDFStructureElementKid( rEleNew
.m_nObject
) );
12897 aNewChildren
.push_back( nNewId
);
12899 std::list
< sal_Int32
>::iterator
aChildEndIt( rEle
.m_aChildren
.begin() );
12900 std::list
< PDFStructureElementKid
>::iterator
aKidEndIt( rEle
.m_aKids
.begin() );
12901 advance( aChildEndIt
, ncMaxPDFArraySize
);
12902 advance( aKidEndIt
, ncMaxPDFArraySize
);
12904 rEleNew
.m_aKids
.splice( rEleNew
.m_aKids
.begin(),
12906 rEle
.m_aKids
.begin(),
12908 rEleNew
.m_aChildren
.splice( rEleNew
.m_aChildren
.begin(),
12910 rEle
.m_aChildren
.begin(),
12912 // set the kid's new parent
12913 for( std::list
< sal_Int32
>::const_iterator it
= rEleNew
.m_aChildren
.begin();
12914 it
!= rEleNew
.m_aChildren
.end(); ++it
)
12916 m_aStructure
[ *it
].m_nParentElement
= nNewId
;
12919 //finally add the new kids resulting from the container added
12920 rEle
.m_aKids
.insert( rEle
.m_aKids
.begin(), aNewKids
.begin(), aNewKids
.end() );
12921 rEle
.m_aChildren
.insert( rEle
.m_aChildren
.begin(), aNewChildren
.begin(), aNewChildren
.end() );
12927 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle
)
12929 bool bSuccess
= false;
12931 if( m_aContext
.Tagged
&& nEle
>= 0 && nEle
< sal_Int32(m_aStructure
.size()) )
12933 // end eventual previous marked content sequence
12934 endStructureElementMCSeq();
12936 m_nCurrentStructElement
= nEle
;
12937 m_bEmitStructure
= checkEmitStructure();
12938 #if OSL_DEBUG_LEVEL > 1
12939 OStringBuffer
aLine( "setCurrentStructureElement " );
12940 aLine
.append( m_nCurrentStructElement
);
12941 aLine
.append( ": " );
12942 aLine
.append( getStructureTag( m_aStructure
[ m_nCurrentStructElement
].m_eType
) );
12943 if( !m_aStructure
[ m_nCurrentStructElement
].m_aAlias
.isEmpty() )
12945 aLine
.append( " aliased as \"" );
12946 aLine
.append( m_aStructure
[ m_nCurrentStructElement
].m_aAlias
);
12947 aLine
.append( '\"' );
12949 if( ! m_bEmitStructure
)
12950 aLine
.append( " (inside NonStruct)" );
12951 emitComment( aLine
.getStr() );
12959 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr
, enum PDFWriter::StructAttributeValue eVal
)
12961 if( !m_aContext
.Tagged
)
12964 bool bInsert
= false;
12965 if( m_nCurrentStructElement
> 0 && m_bEmitStructure
)
12967 PDFWriter::StructElement eType
= m_aStructure
[ m_nCurrentStructElement
].m_eType
;
12970 case PDFWriter::Placement
:
12971 if( eVal
== PDFWriter::Block
||
12972 eVal
== PDFWriter::Inline
||
12973 eVal
== PDFWriter::Before
||
12974 eVal
== PDFWriter::Start
||
12975 eVal
== PDFWriter::End
)
12978 case PDFWriter::WritingMode
:
12979 if( eVal
== PDFWriter::LrTb
||
12980 eVal
== PDFWriter::RlTb
||
12981 eVal
== PDFWriter::TbRl
)
12986 case PDFWriter::TextAlign
:
12987 if( eVal
== PDFWriter::Start
||
12988 eVal
== PDFWriter::Center
||
12989 eVal
== PDFWriter::End
||
12990 eVal
== PDFWriter::Justify
)
12992 if( eType
== PDFWriter::Paragraph
||
12993 eType
== PDFWriter::Heading
||
12994 eType
== PDFWriter::H1
||
12995 eType
== PDFWriter::H2
||
12996 eType
== PDFWriter::H3
||
12997 eType
== PDFWriter::H4
||
12998 eType
== PDFWriter::H5
||
12999 eType
== PDFWriter::H6
||
13000 eType
== PDFWriter::List
||
13001 eType
== PDFWriter::ListItem
||
13002 eType
== PDFWriter::LILabel
||
13003 eType
== PDFWriter::LIBody
||
13004 eType
== PDFWriter::Table
||
13005 eType
== PDFWriter::TableRow
||
13006 eType
== PDFWriter::TableHeader
||
13007 eType
== PDFWriter::TableData
)
13013 case PDFWriter::Width
:
13014 case PDFWriter::Height
:
13015 if( eVal
== PDFWriter::Auto
)
13017 if( eType
== PDFWriter::Figure
||
13018 eType
== PDFWriter::Formula
||
13019 eType
== PDFWriter::Form
||
13020 eType
== PDFWriter::Table
||
13021 eType
== PDFWriter::TableHeader
||
13022 eType
== PDFWriter::TableData
)
13028 case PDFWriter::BlockAlign
:
13029 if( eVal
== PDFWriter::Before
||
13030 eVal
== PDFWriter::Middle
||
13031 eVal
== PDFWriter::After
||
13032 eVal
== PDFWriter::Justify
)
13034 if( eType
== PDFWriter::TableHeader
||
13035 eType
== PDFWriter::TableData
)
13041 case PDFWriter::InlineAlign
:
13042 if( eVal
== PDFWriter::Start
||
13043 eVal
== PDFWriter::Center
||
13044 eVal
== PDFWriter::End
)
13046 if( eType
== PDFWriter::TableHeader
||
13047 eType
== PDFWriter::TableData
)
13053 case PDFWriter::LineHeight
:
13054 if( eVal
== PDFWriter::Normal
||
13055 eVal
== PDFWriter::Auto
)
13057 // only for ILSE and BLSE
13058 if( eType
== PDFWriter::Paragraph
||
13059 eType
== PDFWriter::Heading
||
13060 eType
== PDFWriter::H1
||
13061 eType
== PDFWriter::H2
||
13062 eType
== PDFWriter::H3
||
13063 eType
== PDFWriter::H4
||
13064 eType
== PDFWriter::H5
||
13065 eType
== PDFWriter::H6
||
13066 eType
== PDFWriter::List
||
13067 eType
== PDFWriter::ListItem
||
13068 eType
== PDFWriter::LILabel
||
13069 eType
== PDFWriter::LIBody
||
13070 eType
== PDFWriter::Table
||
13071 eType
== PDFWriter::TableRow
||
13072 eType
== PDFWriter::TableHeader
||
13073 eType
== PDFWriter::TableData
||
13074 eType
== PDFWriter::Span
||
13075 eType
== PDFWriter::Quote
||
13076 eType
== PDFWriter::Note
||
13077 eType
== PDFWriter::Reference
||
13078 eType
== PDFWriter::BibEntry
||
13079 eType
== PDFWriter::Code
||
13080 eType
== PDFWriter::Link
)
13086 case PDFWriter::TextDecorationType
:
13087 if( eVal
== PDFWriter::NONE
||
13088 eVal
== PDFWriter::Underline
||
13089 eVal
== PDFWriter::Overline
||
13090 eVal
== PDFWriter::LineThrough
)
13092 // only for ILSE and BLSE
13093 if( eType
== PDFWriter::Paragraph
||
13094 eType
== PDFWriter::Heading
||
13095 eType
== PDFWriter::H1
||
13096 eType
== PDFWriter::H2
||
13097 eType
== PDFWriter::H3
||
13098 eType
== PDFWriter::H4
||
13099 eType
== PDFWriter::H5
||
13100 eType
== PDFWriter::H6
||
13101 eType
== PDFWriter::List
||
13102 eType
== PDFWriter::ListItem
||
13103 eType
== PDFWriter::LILabel
||
13104 eType
== PDFWriter::LIBody
||
13105 eType
== PDFWriter::Table
||
13106 eType
== PDFWriter::TableRow
||
13107 eType
== PDFWriter::TableHeader
||
13108 eType
== PDFWriter::TableData
||
13109 eType
== PDFWriter::Span
||
13110 eType
== PDFWriter::Quote
||
13111 eType
== PDFWriter::Note
||
13112 eType
== PDFWriter::Reference
||
13113 eType
== PDFWriter::BibEntry
||
13114 eType
== PDFWriter::Code
||
13115 eType
== PDFWriter::Link
)
13121 case PDFWriter::ListNumbering
:
13122 if( eVal
== PDFWriter::NONE
||
13123 eVal
== PDFWriter::Disc
||
13124 eVal
== PDFWriter::Circle
||
13125 eVal
== PDFWriter::Square
||
13126 eVal
== PDFWriter::Decimal
||
13127 eVal
== PDFWriter::UpperRoman
||
13128 eVal
== PDFWriter::LowerRoman
||
13129 eVal
== PDFWriter::UpperAlpha
||
13130 eVal
== PDFWriter::LowerAlpha
)
13132 if( eType
== PDFWriter::List
)
13141 m_aStructure
[ m_nCurrentStructElement
].m_aAttributes
[ eAttr
] = PDFStructureAttribute( eVal
);
13142 #if OSL_DEBUG_LEVEL > 1
13143 else if( m_nCurrentStructElement
> 0 && m_bEmitStructure
)
13144 SAL_INFO("vcl.pdfwriter",
13145 "rejecting setStructureAttribute( " << getAttributeTag( eAttr
)
13146 << ", " << getAttributeValueTag( eVal
)
13147 << " ) on " << getStructureTag( m_aStructure
[ m_nCurrentStructElement
].m_eType
)
13148 << " (" << m_aStructure
[ m_nCurrentStructElement
].m_aAlias
.getStr()
13155 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr
, sal_Int32 nValue
)
13157 if( ! m_aContext
.Tagged
)
13160 bool bInsert
= false;
13161 if( m_nCurrentStructElement
> 0 && m_bEmitStructure
)
13163 if( eAttr
== PDFWriter::Language
)
13165 m_aStructure
[ m_nCurrentStructElement
].m_aLocale
= LanguageTag( LanguageType(nValue
) ).getLocale();
13169 PDFWriter::StructElement eType
= m_aStructure
[ m_nCurrentStructElement
].m_eType
;
13172 case PDFWriter::SpaceBefore
:
13173 case PDFWriter::SpaceAfter
:
13174 case PDFWriter::StartIndent
:
13175 case PDFWriter::EndIndent
:
13177 if( eType
== PDFWriter::Paragraph
||
13178 eType
== PDFWriter::Heading
||
13179 eType
== PDFWriter::H1
||
13180 eType
== PDFWriter::H2
||
13181 eType
== PDFWriter::H3
||
13182 eType
== PDFWriter::H4
||
13183 eType
== PDFWriter::H5
||
13184 eType
== PDFWriter::H6
||
13185 eType
== PDFWriter::List
||
13186 eType
== PDFWriter::ListItem
||
13187 eType
== PDFWriter::LILabel
||
13188 eType
== PDFWriter::LIBody
||
13189 eType
== PDFWriter::Table
||
13190 eType
== PDFWriter::TableRow
||
13191 eType
== PDFWriter::TableHeader
||
13192 eType
== PDFWriter::TableData
)
13197 case PDFWriter::TextIndent
:
13198 // paragraph like BLSE and additional elements
13199 if( eType
== PDFWriter::Paragraph
||
13200 eType
== PDFWriter::Heading
||
13201 eType
== PDFWriter::H1
||
13202 eType
== PDFWriter::H2
||
13203 eType
== PDFWriter::H3
||
13204 eType
== PDFWriter::H4
||
13205 eType
== PDFWriter::H5
||
13206 eType
== PDFWriter::H6
||
13207 eType
== PDFWriter::LILabel
||
13208 eType
== PDFWriter::LIBody
||
13209 eType
== PDFWriter::TableHeader
||
13210 eType
== PDFWriter::TableData
)
13215 case PDFWriter::Width
:
13216 case PDFWriter::Height
:
13217 if( eType
== PDFWriter::Figure
||
13218 eType
== PDFWriter::Formula
||
13219 eType
== PDFWriter::Form
||
13220 eType
== PDFWriter::Table
||
13221 eType
== PDFWriter::TableHeader
||
13222 eType
== PDFWriter::TableData
)
13227 case PDFWriter::LineHeight
:
13228 case PDFWriter::BaselineShift
:
13229 // only for ILSE and BLSE
13230 if( eType
== PDFWriter::Paragraph
||
13231 eType
== PDFWriter::Heading
||
13232 eType
== PDFWriter::H1
||
13233 eType
== PDFWriter::H2
||
13234 eType
== PDFWriter::H3
||
13235 eType
== PDFWriter::H4
||
13236 eType
== PDFWriter::H5
||
13237 eType
== PDFWriter::H6
||
13238 eType
== PDFWriter::List
||
13239 eType
== PDFWriter::ListItem
||
13240 eType
== PDFWriter::LILabel
||
13241 eType
== PDFWriter::LIBody
||
13242 eType
== PDFWriter::Table
||
13243 eType
== PDFWriter::TableRow
||
13244 eType
== PDFWriter::TableHeader
||
13245 eType
== PDFWriter::TableData
||
13246 eType
== PDFWriter::Span
||
13247 eType
== PDFWriter::Quote
||
13248 eType
== PDFWriter::Note
||
13249 eType
== PDFWriter::Reference
||
13250 eType
== PDFWriter::BibEntry
||
13251 eType
== PDFWriter::Code
||
13252 eType
== PDFWriter::Link
)
13257 case PDFWriter::RowSpan
:
13258 case PDFWriter::ColSpan
:
13259 // only for table cells
13260 if( eType
== PDFWriter::TableHeader
||
13261 eType
== PDFWriter::TableData
)
13266 case PDFWriter::LinkAnnotation
:
13267 if( eType
== PDFWriter::Link
)
13275 m_aStructure
[ m_nCurrentStructElement
].m_aAttributes
[ eAttr
] = PDFStructureAttribute( nValue
);
13276 #if OSL_DEBUG_LEVEL > 1
13277 else if( m_nCurrentStructElement
> 0 && m_bEmitStructure
)
13278 SAL_INFO("vcl.pdfwriter",
13279 "rejecting setStructureAttributeNumerical( " << getAttributeTag( eAttr
)
13280 << ", " << (int)nValue
13281 << " ) on " << getStructureTag( m_aStructure
[ m_nCurrentStructElement
].m_eType
)
13282 << " (" << m_aStructure
[ m_nCurrentStructElement
].m_aAlias
.getStr()
13289 void PDFWriterImpl::setStructureBoundingBox( const tools::Rectangle
& rRect
)
13291 sal_Int32 nPageNr
= m_nCurrentPage
;
13292 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() || !m_aContext
.Tagged
)
13295 if( m_nCurrentStructElement
> 0 && m_bEmitStructure
)
13297 PDFWriter::StructElement eType
= m_aStructure
[ m_nCurrentStructElement
].m_eType
;
13298 if( eType
== PDFWriter::Figure
||
13299 eType
== PDFWriter::Formula
||
13300 eType
== PDFWriter::Form
||
13301 eType
== PDFWriter::Table
)
13303 m_aStructure
[ m_nCurrentStructElement
].m_aBBox
= rRect
;
13304 // convert to default user space now, since the mapmode may change
13305 m_aPages
[nPageNr
].convertRect( m_aStructure
[ m_nCurrentStructElement
].m_aBBox
);
13310 void PDFWriterImpl::setActualText( const OUString
& rText
)
13312 if( m_aContext
.Tagged
&& m_nCurrentStructElement
> 0 && m_bEmitStructure
)
13314 m_aStructure
[ m_nCurrentStructElement
].m_aActualText
= rText
;
13318 void PDFWriterImpl::setAlternateText( const OUString
& rText
)
13320 if( m_aContext
.Tagged
&& m_nCurrentStructElement
> 0 && m_bEmitStructure
)
13322 m_aStructure
[ m_nCurrentStructElement
].m_aAltText
= rText
;
13326 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds
, sal_Int32 nPageNr
)
13329 nPageNr
= m_nCurrentPage
;
13331 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
13334 m_aPages
[ nPageNr
].m_nDuration
= nSeconds
;
13337 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType
, sal_uInt32 nMilliSec
, sal_Int32 nPageNr
)
13340 nPageNr
= m_nCurrentPage
;
13342 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
13345 m_aPages
[ nPageNr
].m_eTransition
= eType
;
13346 m_aPages
[ nPageNr
].m_nTransTime
= nMilliSec
;
13349 void PDFWriterImpl::ensureUniqueRadioOnValues()
13351 // loop over radio groups
13352 for( std::map
<sal_Int32
,sal_Int32
>::const_iterator group
= m_aRadioGroupWidgets
.begin();
13353 group
!= m_aRadioGroupWidgets
.end(); ++group
)
13355 PDFWidget
& rGroupWidget
= m_aWidgets
[ group
->second
];
13356 // check whether all kids have a unique OnValue
13357 std::unordered_map
< OUString
, sal_Int32
, OUStringHash
> aOnValues
;
13358 int nChildren
= rGroupWidget
.m_aKidsIndex
.size();
13359 bool bIsUnique
= true;
13360 for( int nKid
= 0; nKid
< nChildren
&& bIsUnique
; nKid
++ )
13362 int nKidIndex
= rGroupWidget
.m_aKidsIndex
[nKid
];
13363 const OUString
& rVal
= m_aWidgets
[nKidIndex
].m_aOnValue
;
13364 #if OSL_DEBUG_LEVEL > 1
13365 SAL_INFO("vcl.pdfwriter", "OnValue: " << rVal
);
13367 if( aOnValues
.find( rVal
) == aOnValues
.end() )
13369 aOnValues
[ rVal
] = 1;
13378 #if OSL_DEBUG_LEVEL > 1
13379 SAL_INFO("vcl.pdfwriter", "enforcing unique OnValues" );
13381 // make unique by using ascending OnValues
13382 for( int nKid
= 0; nKid
< nChildren
; nKid
++ )
13384 int nKidIndex
= rGroupWidget
.m_aKidsIndex
[nKid
];
13385 PDFWidget
& rKid
= m_aWidgets
[nKidIndex
];
13386 rKid
.m_aOnValue
= OUString::number( nKid
+1 );
13387 if( rKid
.m_aValue
!= "Off" )
13388 rKid
.m_aValue
= rKid
.m_aOnValue
;
13391 // finally move the "Yes" appearance to the OnValue appearance
13392 for( int nKid
= 0; nKid
< nChildren
; nKid
++ )
13394 int nKidIndex
= rGroupWidget
.m_aKidsIndex
[nKid
];
13395 PDFWidget
& rKid
= m_aWidgets
[nKidIndex
];
13396 PDFAppearanceMap::iterator app_it
= rKid
.m_aAppearances
.find( "N" );
13397 if( app_it
!= rKid
.m_aAppearances
.end() )
13399 PDFAppearanceStreams::iterator stream_it
= app_it
->second
.find( "Yes" );
13400 if( stream_it
!= app_it
->second
.end() )
13402 SvMemoryStream
* pStream
= stream_it
->second
;
13403 app_it
->second
.erase( stream_it
);
13404 OStringBuffer
aBuf( rKid
.m_aOnValue
.getLength()*2 );
13405 appendName( rKid
.m_aOnValue
, aBuf
);
13406 (app_it
->second
)[ aBuf
.makeStringAndClear() ] = pStream
;
13408 #if OSL_DEBUG_LEVEL > 1
13410 SAL_INFO("vcl.pdfwriter", "error: RadioButton without \"Yes\" stream" );
13413 // update selected radio button
13414 if( rKid
.m_aValue
!= "Off" )
13416 rGroupWidget
.m_aValue
= rKid
.m_aValue
;
13422 sal_Int32
PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget
& rBtn
)
13424 sal_Int32 nRadioGroupWidget
= -1;
13426 std::map
< sal_Int32
, sal_Int32
>::const_iterator it
= m_aRadioGroupWidgets
.find( rBtn
.RadioGroup
);
13428 if( it
== m_aRadioGroupWidgets
.end() )
13430 m_aRadioGroupWidgets
[ rBtn
.RadioGroup
] = nRadioGroupWidget
=
13431 sal_Int32(m_aWidgets
.size());
13433 // new group, insert the radiobutton
13434 m_aWidgets
.push_back( PDFWidget() );
13435 m_aWidgets
.back().m_nObject
= createObject();
13436 m_aWidgets
.back().m_nPage
= m_nCurrentPage
;
13437 m_aWidgets
.back().m_eType
= PDFWriter::RadioButton
;
13438 m_aWidgets
.back().m_nRadioGroup
= rBtn
.RadioGroup
;
13439 m_aWidgets
.back().m_nFlags
|= 0x0000C000; // NoToggleToOff and Radio bits
13441 createWidgetFieldName( sal_Int32(m_aWidgets
.size()-1), rBtn
);
13444 nRadioGroupWidget
= it
->second
;
13446 return nRadioGroupWidget
;
13449 sal_Int32
PDFWriterImpl::createControl( const PDFWriter::AnyWidget
& rControl
, sal_Int32 nPageNr
)
13452 nPageNr
= m_nCurrentPage
;
13454 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
13457 bool sigHidden(true);
13458 sal_Int32 nNewWidget
= m_aWidgets
.size();
13459 m_aWidgets
.push_back( PDFWidget() );
13461 m_aWidgets
.back().m_nObject
= createObject();
13462 m_aWidgets
.back().m_aRect
= rControl
.Location
;
13463 m_aWidgets
.back().m_nPage
= nPageNr
;
13464 m_aWidgets
.back().m_eType
= rControl
.getType();
13466 sal_Int32 nRadioGroupWidget
= -1;
13467 // for unknown reasons the radio buttons of a radio group must not have a
13468 // field name, else the buttons are in fact check boxes -
13469 // that is multiple buttons of the radio group can be selected
13470 if( rControl
.getType() == PDFWriter::RadioButton
)
13471 nRadioGroupWidget
= findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget
&>(rControl
) );
13474 createWidgetFieldName( nNewWidget
, rControl
);
13477 // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
13478 PDFWidget
& rNewWidget
= m_aWidgets
[nNewWidget
];
13479 rNewWidget
.m_aDescription
= rControl
.Description
;
13480 rNewWidget
.m_aText
= rControl
.Text
;
13481 rNewWidget
.m_nTextStyle
= rControl
.TextStyle
&
13482 ( DrawTextFlags::Left
| DrawTextFlags::Center
| DrawTextFlags::Right
| DrawTextFlags::Top
|
13483 DrawTextFlags::VCenter
| DrawTextFlags::Bottom
|
13484 DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
);
13485 rNewWidget
.m_nTabOrder
= rControl
.TabOrder
;
13487 // various properties are set via the flags (/Ff) property of the field dict
13488 if( rControl
.ReadOnly
)
13489 rNewWidget
.m_nFlags
|= 1;
13490 if( rControl
.getType() == PDFWriter::PushButton
)
13492 const PDFWriter::PushButtonWidget
& rBtn
= static_cast<const PDFWriter::PushButtonWidget
&>(rControl
);
13493 if( rNewWidget
.m_nTextStyle
== DrawTextFlags::NONE
)
13494 rNewWidget
.m_nTextStyle
=
13495 DrawTextFlags::Center
| DrawTextFlags::VCenter
|
13496 DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
;
13498 rNewWidget
.m_nFlags
|= 0x00010000;
13499 if( !rBtn
.URL
.isEmpty() )
13500 rNewWidget
.m_aListEntries
.push_back( rBtn
.URL
);
13501 rNewWidget
.m_bSubmit
= rBtn
.Submit
;
13502 rNewWidget
.m_bSubmitGet
= rBtn
.SubmitGet
;
13503 rNewWidget
.m_nDest
= rBtn
.Dest
;
13504 createDefaultPushButtonAppearance( rNewWidget
, rBtn
);
13506 else if( rControl
.getType() == PDFWriter::RadioButton
)
13508 const PDFWriter::RadioButtonWidget
& rBtn
= static_cast<const PDFWriter::RadioButtonWidget
&>(rControl
);
13509 if( rNewWidget
.m_nTextStyle
== DrawTextFlags::NONE
)
13510 rNewWidget
.m_nTextStyle
=
13511 DrawTextFlags::VCenter
| DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
;
13512 /* PDF sees a RadioButton group as one radio button with
13513 * children which are in turn check boxes
13515 * so we need to create a radio button on demand for a new group
13516 * and insert a checkbox for each RadioButtonWidget as its child
13518 rNewWidget
.m_eType
= PDFWriter::CheckBox
;
13519 rNewWidget
.m_nRadioGroup
= rBtn
.RadioGroup
;
13521 SAL_WARN_IF( nRadioGroupWidget
< 0 || nRadioGroupWidget
>= (sal_Int32
)m_aWidgets
.size(), "vcl.pdfwriter", "no radio group parent" );
13523 PDFWidget
& rRadioButton
= m_aWidgets
[nRadioGroupWidget
];
13524 rRadioButton
.m_aKids
.push_back( rNewWidget
.m_nObject
);
13525 rRadioButton
.m_aKidsIndex
.push_back( nNewWidget
);
13526 rNewWidget
.m_nParent
= rRadioButton
.m_nObject
;
13528 rNewWidget
.m_aValue
= "Off";
13529 rNewWidget
.m_aOnValue
= rBtn
.OnValue
;
13530 if( rRadioButton
.m_aValue
.isEmpty() && rBtn
.Selected
)
13532 rNewWidget
.m_aValue
= rNewWidget
.m_aOnValue
;
13533 rRadioButton
.m_aValue
= rNewWidget
.m_aOnValue
;
13535 createDefaultRadioButtonAppearance( rNewWidget
, rBtn
);
13537 // union rect of radio group
13538 tools::Rectangle aRect
= rNewWidget
.m_aRect
;
13539 m_aPages
[ nPageNr
].convertRect( aRect
);
13540 rRadioButton
.m_aRect
.Union( aRect
);
13542 else if( rControl
.getType() == PDFWriter::CheckBox
)
13544 const PDFWriter::CheckBoxWidget
& rBox
= static_cast<const PDFWriter::CheckBoxWidget
&>(rControl
);
13545 if( rNewWidget
.m_nTextStyle
== DrawTextFlags::NONE
)
13546 rNewWidget
.m_nTextStyle
=
13547 DrawTextFlags::VCenter
| DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
;
13549 rNewWidget
.m_aValue
= rBox
.Checked
? OUString("Yes") : OUString("Off" );
13550 // create default appearance before m_aRect gets transformed
13551 createDefaultCheckBoxAppearance( rNewWidget
, rBox
);
13553 else if( rControl
.getType() == PDFWriter::ListBox
)
13555 if( rNewWidget
.m_nTextStyle
== DrawTextFlags::NONE
)
13556 rNewWidget
.m_nTextStyle
= DrawTextFlags::VCenter
;
13558 const PDFWriter::ListBoxWidget
& rLstBox
= static_cast<const PDFWriter::ListBoxWidget
&>(rControl
);
13559 rNewWidget
.m_aListEntries
= rLstBox
.Entries
;
13560 rNewWidget
.m_aSelectedEntries
= rLstBox
.SelectedEntries
;
13561 rNewWidget
.m_aValue
= rLstBox
.Text
;
13562 if( rLstBox
.DropDown
)
13563 rNewWidget
.m_nFlags
|= 0x00020000;
13564 if( rLstBox
.MultiSelect
&& !rLstBox
.DropDown
&& m_aContext
.Version
> PDFWriter::PDFVersion::PDF_1_3
)
13565 rNewWidget
.m_nFlags
|= 0x00200000;
13567 createDefaultListBoxAppearance( rNewWidget
, rLstBox
);
13569 else if( rControl
.getType() == PDFWriter::ComboBox
)
13571 if( rNewWidget
.m_nTextStyle
== DrawTextFlags::NONE
)
13572 rNewWidget
.m_nTextStyle
= DrawTextFlags::VCenter
;
13574 const PDFWriter::ComboBoxWidget
& rBox
= static_cast<const PDFWriter::ComboBoxWidget
&>(rControl
);
13575 rNewWidget
.m_aValue
= rBox
.Text
;
13576 rNewWidget
.m_aListEntries
= rBox
.Entries
;
13577 rNewWidget
.m_nFlags
|= 0x00060000; // combo and edit flag
13579 PDFWriter::ListBoxWidget aLBox
;
13580 aLBox
.Name
= rBox
.Name
;
13581 aLBox
.Description
= rBox
.Description
;
13582 aLBox
.Text
= rBox
.Text
;
13583 aLBox
.TextStyle
= rBox
.TextStyle
;
13584 aLBox
.ReadOnly
= rBox
.ReadOnly
;
13585 aLBox
.Border
= rBox
.Border
;
13586 aLBox
.BorderColor
= rBox
.BorderColor
;
13587 aLBox
.Background
= rBox
.Background
;
13588 aLBox
.BackgroundColor
= rBox
.BackgroundColor
;
13589 aLBox
.TextFont
= rBox
.TextFont
;
13590 aLBox
.TextColor
= rBox
.TextColor
;
13591 aLBox
.DropDown
= true;
13592 aLBox
.MultiSelect
= false;
13593 aLBox
.Entries
= rBox
.Entries
;
13595 createDefaultListBoxAppearance( rNewWidget
, aLBox
);
13597 else if( rControl
.getType() == PDFWriter::Edit
)
13599 if( rNewWidget
.m_nTextStyle
== DrawTextFlags::NONE
)
13600 rNewWidget
.m_nTextStyle
= DrawTextFlags::Left
| DrawTextFlags::VCenter
;
13602 const PDFWriter::EditWidget
& rEdit
= static_cast<const PDFWriter::EditWidget
&>(rControl
);
13603 if( rEdit
.MultiLine
)
13605 rNewWidget
.m_nFlags
|= 0x00001000;
13606 rNewWidget
.m_nTextStyle
|= DrawTextFlags::MultiLine
| DrawTextFlags::WordBreak
;
13608 if( rEdit
.Password
)
13609 rNewWidget
.m_nFlags
|= 0x00002000;
13610 if( rEdit
.FileSelect
&& m_aContext
.Version
> PDFWriter::PDFVersion::PDF_1_3
)
13611 rNewWidget
.m_nFlags
|= 0x00100000;
13612 rNewWidget
.m_nMaxLen
= rEdit
.MaxLen
;
13613 rNewWidget
.m_aValue
= rEdit
.Text
;
13615 createDefaultEditAppearance( rNewWidget
, rEdit
);
13617 #if HAVE_FEATURE_NSS
13618 else if( rControl
.getType() == PDFWriter::Signature
)
13622 rNewWidget
.m_aRect
= tools::Rectangle(0, 0, 0, 0);
13624 m_nSignatureObject
= createObject();
13625 rNewWidget
.m_aValue
= OUString::number( m_nSignatureObject
);
13626 rNewWidget
.m_aValue
+= " 0 R";
13627 // let's add a fake appearance
13628 rNewWidget
.m_aAppearances
[ "N" ][ "Standard" ] = new SvMemoryStream();
13632 // if control is a hidden signature, do not convert coordinates since we
13633 // need /Rect [ 0 0 0 0 ]
13634 if ( ! ( ( rControl
.getType() == PDFWriter::Signature
) && ( sigHidden
) ) )
13636 // convert to default user space now, since the mapmode may change
13637 // note: create default appearances before m_aRect gets transformed
13638 m_aPages
[ nPageNr
].convertRect( rNewWidget
.m_aRect
);
13641 // insert widget to page's annotation list
13642 m_aPages
[ nPageNr
].m_aAnnotations
.push_back( rNewWidget
.m_nObject
);
13644 // mark page as having widgets
13645 m_aPages
[ nPageNr
].m_bHasWidgets
= true;
13650 void PDFWriterImpl::addStream( const OUString
& rMimeType
, PDFOutputStream
* pStream
)
13654 m_aAdditionalStreams
.push_back( PDFAddStream() );
13655 PDFAddStream
& rStream
= m_aAdditionalStreams
.back();
13656 rStream
.m_aMimeType
= !rMimeType
.isEmpty()
13657 ? OUString( rMimeType
)
13658 : OUString( "application/octet-stream" );
13659 rStream
.m_pStream
= pStream
;
13660 rStream
.m_bCompress
= false;
13664 void PDFWriterImpl::MARK( const char* pString
)
13666 beginStructureElementMCSeq();
13667 if (g_bDebugDisableCompression
)
13668 emitComment( pString
);
13671 sal_Int32
PDFWriterImpl::ReferenceXObjectEmit::getObject() const
13673 if (m_nFormObject
> 0)
13674 return m_nFormObject
;
13676 return m_nBitmapObject
;
13679 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */