update emoji autocorrect entries from po-files
[LibreOffice.git] / vcl / source / gdi / pdfwriter_impl.cxx
blob0b7fa46b446cdec0fc950b68e8f3fdf422ab8976
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/types.h>
22 #include <math.h>
23 #include <algorithm>
24 #include <lcms2.h>
26 #include <basegfx/matrix/b2dhommatrix.hxx>
27 #include <basegfx/polygon/b2dpolygon.hxx>
28 #include <basegfx/polygon/b2dpolygontools.hxx>
29 #include <basegfx/polygon/b2dpolypolygon.hxx>
30 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
31 #include <basegfx/polygon/b2dpolypolygontools.hxx>
32 #include <boost/scoped_array.hpp>
33 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
34 #include <com/sun/star/util/URL.hpp>
35 #include <com/sun/star/util/URLTransformer.hpp>
36 #include <comphelper/processfactory.hxx>
37 #include <comphelper/random.hxx>
38 #include <comphelper/string.hxx>
39 #include <cppuhelper/implbase1.hxx>
40 #include <i18nlangtag/languagetag.hxx>
41 #include <o3tl/numeric.hxx>
42 #include <osl/file.hxx>
43 #include <osl/thread.h>
44 #include <rtl/crc.h>
45 #include <rtl/digest.h>
46 #include <rtl/ustrbuf.hxx>
47 #include <tools/debug.hxx>
48 #include <tools/fract.hxx>
49 #include <tools/stream.hxx>
50 #include <tools/urlobj.hxx>
51 #include <tools/zcodec.hxx>
52 #include <vcl/bitmapex.hxx>
53 #include <vcl/bmpacc.hxx>
54 #include <vcl/cvtgrf.hxx>
55 #include <vcl/image.hxx>
56 #include <vcl/lineinfo.hxx>
57 #include <vcl/metric.hxx>
58 #include <vcl/settings.hxx>
59 #include <vcl/strhelper.hxx>
60 #include <vcl/svapp.hxx>
61 #include <vcl/virdev.hxx>
63 #include "fontsubset.hxx"
64 #include "outdev.h"
65 #include "PhysicalFontFace.hxx"
66 #include "salgdi.hxx"
67 #include "sallayout.hxx"
68 #include "textlayout.hxx"
70 #include "pdfwriter_impl.hxx"
72 #if !defined(ANDROID) && !defined(IOS) && !defined(_WIN32)
73 // NSS headers for PDF signing
74 #include "nss.h"
75 #include "cert.h"
76 #include "hasht.h"
77 #include "secerr.h"
78 #include "sechash.h"
79 #include "cms.h"
80 #include "cmst.h"
82 // We use curl for RFC3161 time stamp requests
83 #include <curl/curl.h>
84 #endif
86 #ifdef _WIN32
87 // WinCrypt headers for PDF signing
88 // Note: this uses Windows 7 APIs and requires the relevant data types;
89 // the functions that don't exist in WinXP must be looked up at runtime!
90 #undef _WIN32_WINNT
91 #define _WIN32_WINNT _WIN32_WINNT_WIN7
92 #include <prewin.h>
93 #include <wincrypt.h>
94 #include <postwin.h>
95 #include <comphelper/windowserrorstring.hxx>
96 #endif
98 #include <config_eot.h>
100 #if ENABLE_EOT
101 #include <libeot/libeot.h>
102 #endif
104 using namespace vcl;
106 #if (OSL_DEBUG_LEVEL < 3)
107 #define COMPRESS_PAGES
108 #else
109 #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams
110 #endif
112 #if !defined(ANDROID) && !defined(IOS)
113 // Is this length truly the maximum possible, or just a number that
114 // seemed large enough when the author tested this (with some type of
115 // certificates)? I suspect the latter.
117 // Used to be 0x4000 = 16384, but a sample signed PDF (produced by
118 // some other software) provided by the customer has a signature
119 // content that is 30000 bytes. The SampleSignedPDFDocument.pdf from
120 // Adobe has one that is 21942 bytes. So let's be careful. Pity this
121 // can't be dynamic, at least not without restructuring the code. Also
122 // note that the checks in the code for this being too small
123 // apparently are broken, if this overflows you end up with an invalid
124 // PDF. Need to fix that.
126 #define MAX_SIGNATURE_CONTENT_LENGTH 50000
127 #endif
129 #ifdef DO_TEST_PDF
130 class PDFTestOutputStream : public PDFOutputStream
132 public:
133 virtual ~PDFTestOutputStream();
134 virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream );
137 PDFTestOutputStream::~PDFTestOutputStream()
141 void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream )
143 OString aStr( "lalala\ntest\ntest\ntest" );
144 com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() );
145 memcpy( aData.getArray(), aStr.getStr(), aStr.getLength() );
146 xStream->writeBytes( aData );
149 // this test code cannot be used to test PDF/A-1 because it forces
150 // control item (widgets) to bypass the structure controlling
151 // the embedding of such elements in actual run
152 void doTestCode()
154 static const char* pHome = getenv( "HOME" );
155 OUString aTestFile( "file://" );
156 aTestFile += OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 );
157 aTestFile += "/pdf_export_test.pdf";
159 PDFWriter::PDFWriterContext aContext;
160 aContext.URL = aTestFile;
161 aContext.Version = PDFWriter::PDF_1_4;
162 aContext.Tagged = true;
163 aContext.InitialPage = 2;
164 aContext.DocumentInfo.Title = "PDF export test document";
165 aContext.DocumentInfo.Producer = "VCL";
167 aContext.SignPDF = true;
168 aContext.SignLocation = "Burdur";
169 aContext.SignReason = "Some valid reason to sign";
170 aContext.SignContact = "signer@example.com";
172 com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder > xEnc;
173 PDFWriter aWriter( aContext, xEnc );
174 aWriter.NewPage( 595, 842 );
175 aWriter.BeginStructureElement( PDFWriter::Document );
176 // set duration of 3 sec for first page
177 aWriter.SetAutoAdvanceTime( 3 );
178 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
180 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
181 aWriter.SetLineColor( Color( COL_LIGHTGREEN ) );
182 aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
184 aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
185 aWriter.SetTextColor( Color( COL_BLACK ) );
186 aWriter.SetLineColor( Color( COL_BLACK ) );
187 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
189 Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
190 aWriter.DrawRect( aRect );
191 aWriter.DrawText( aRect, OUString( "Link annot 1" ) );
192 sal_Int32 nFirstLink = aWriter.CreateLink( aRect );
193 PDFNote aNote;
194 aNote.Title = "A small test note";
195 aNote.Contents = "There is no business like show business like no business i know. Everything about it is appealing.";
196 aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote );
198 Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
199 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
200 aWriter.DrawRect( aTargetRect );
201 aWriter.DrawText( aTargetRect, "Dest second link" );
202 sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect );
204 aWriter.BeginStructureElement( PDFWriter::Section );
205 aWriter.BeginStructureElement( PDFWriter::Heading );
206 aWriter.DrawText( Point(4500, 9000), "A small structure test" );
207 aWriter.EndStructureElement();
208 aWriter.BeginStructureElement( PDFWriter::Paragraph );
209 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
210 aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline );
211 aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
212 "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.",
213 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak
215 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." );
216 aWriter.SetAlternateText( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." );
217 aWriter.EndStructureElement();
218 aWriter.BeginStructureElement( PDFWriter::Paragraph );
219 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
220 aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
221 "This paragraph is nothing special either but ends on the next page structurewise",
222 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak
225 aWriter.NewPage( 595, 842 );
226 // test AddStream interface
227 aWriter.AddStream( "text/plain", new PDFTestOutputStream(), true );
228 // set transitional mode
229 aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 );
230 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
231 aWriter.SetTextColor( Color( COL_BLACK ) );
232 aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
233 aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
234 "Here's where all things come to an end ... well at least the paragraph from the last page.",
235 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak
237 aWriter.EndStructureElement();
239 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
240 // disable structure
241 aWriter.BeginStructureElement( PDFWriter::NonStructElement );
242 aWriter.DrawRect( aRect );
243 aWriter.BeginStructureElement( PDFWriter::Paragraph );
244 aWriter.DrawText( aRect, "Link annot 2" );
245 sal_Int32 nSecondLink = aWriter.CreateLink( aRect );
247 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
248 aWriter.BeginStructureElement( PDFWriter::ListItem );
249 aWriter.DrawRect( aTargetRect );
250 aWriter.DrawText( aTargetRect, "Dest first link" );
251 sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect );
252 // enable structure
253 aWriter.EndStructureElement();
255 aWriter.EndStructureElement();
256 aWriter.EndStructureElement();
257 aWriter.BeginStructureElement( PDFWriter::Figure );
258 aWriter.BeginStructureElement( PDFWriter::Caption );
259 aWriter.DrawText( Point( 4500, 9000 ), "Some drawing stuff inside the structure" );
260 aWriter.EndStructureElement();
262 // test clipping
263 basegfx::B2DPolyPolygon aClip;
264 basegfx::B2DPolygon aClipPoly;
265 aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) );
266 aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) );
267 aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) );
268 aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) );
269 aClipPoly.setClosed( true );
270 aClip.append( aClipPoly );
272 aWriter.Push( PushFlags::CLIPREGION | PushFlags::FILLCOLOR );
273 aWriter.SetClipRegion( aClip );
274 aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
275 aWriter.MoveClipRegion( 1000, 500 );
276 aWriter.SetFillColor( Color( COL_RED ) );
277 aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
278 aWriter.Pop();
279 // test transparency
280 // draw background
281 Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
282 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
283 aWriter.DrawRect( aTranspRect );
284 aWriter.BeginTransparencyGroup();
286 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
287 aWriter.DrawEllipse( aTranspRect );
288 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
289 aWriter.DrawText( aTranspRect,
290 "Some transparent text",
291 DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
293 aWriter.EndTransparencyGroup( aTranspRect, 50 );
295 // prepare an alpha mask
296 Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
297 BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess();
298 for( int nX = 0; nX < 256; nX++ )
299 for( int nY = 0; nY < 256; nY++ )
300 pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) );
301 aTransMask.ReleaseAccess( pAcc );
302 aTransMask.SetPrefMapMode( MAP_MM );
303 aTransMask.SetPrefSize( Size( 10, 10 ) );
305 aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask );
307 aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
308 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
309 aWriter.DrawRect( aTranspRect );
310 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
311 aWriter.DrawEllipse( aTranspRect );
312 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
313 aWriter.DrawText( aTranspRect,
314 "Some transparent text",
315 DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
316 aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
317 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
318 aWriter.DrawRect( aTranspRect );
320 Bitmap aImageBmp( Size( 256, 256 ), 24 );
321 pAcc = aImageBmp.AcquireWriteAccess();
322 pAcc->SetFillColor( Color( 0xff, 0, 0xff ) );
323 pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
324 aImageBmp.ReleaseAccess( pAcc );
325 BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) );
326 aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx );
328 aWriter.EndStructureElement();
329 aWriter.EndStructureElement();
331 LineInfo aLI( LINE_DASH, 3 );
332 aLI.SetDashCount( 2 );
333 aLI.SetDashLen( 50 );
334 aLI.SetDotCount( 2 );
335 aLI.SetDotLen( 25 );
336 aLI.SetDistance( 15 );
337 Point aLIPoints[] = { Point( 4000, 10000 ),
338 Point( 8000, 12000 ),
339 Point( 3000, 19000 ) };
340 Polygon aLIPoly( 3, aLIPoints );
341 aWriter.SetLineColor( Color( COL_BLUE ) );
342 aWriter.SetFillColor();
343 aWriter.DrawPolyLine( aLIPoly, aLI );
345 aLI.SetDashCount( 4 );
346 aLIPoly.Move( 1000, 1000 );
347 aWriter.DrawPolyLine( aLIPoly, aLI );
349 aWriter.NewPage( 595, 842 );
350 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
351 Wallpaper aWall( aTransMask );
352 aWall.SetStyle( WALLPAPER_TILE );
353 aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall );
355 aWriter.NewPage( 595, 842 );
356 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
357 aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
358 aWriter.SetTextColor( Color( COL_BLACK ) );
359 aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
360 aWriter.DrawRect( aRect );
361 aWriter.DrawText( aRect, "www.heise.de" );
362 sal_Int32 nURILink = aWriter.CreateLink( aRect );
363 aWriter.SetLinkURL( nURILink, OUString( "http://www.heise.de" ) );
365 aWriter.SetLinkDest( nFirstLink, nFirstDest );
366 aWriter.SetLinkDest( nSecondLink, nSecondDest );
368 // include a button
369 PDFWriter::PushButtonWidget aBtn;
370 aBtn.Name = "testButton";
371 aBtn.Description = "A test button";
372 aBtn.Text = "hit me";
373 aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
374 aBtn.Border = aBtn.Background = true;
375 aWriter.CreateControl( aBtn );
377 // include a uri button
378 PDFWriter::PushButtonWidget aUriBtn;
379 aUriBtn.Name = "wwwButton";
380 aUriBtn.Description = "A URI button";
381 aUriBtn.Text = "to www";
382 aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
383 aUriBtn.Border = aUriBtn.Background = true;
384 aUriBtn.URL = "http://www.heise.de";
385 aWriter.CreateControl( aUriBtn );
387 // include a dest button
388 PDFWriter::PushButtonWidget aDstBtn;
389 aDstBtn.Name = "destButton";
390 aDstBtn.Description = "A Dest button";
391 aDstBtn.Text = "to paragraph";
392 aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
393 aDstBtn.Border = aDstBtn.Background = true;
394 aDstBtn.Dest = nFirstDest;
395 aWriter.CreateControl( aDstBtn );
397 PDFWriter::CheckBoxWidget aCBox;
398 aCBox.Name = "textCheckBox";
399 aCBox.Description = "A test check box";
400 aCBox.Text = "check me";
401 aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
402 aCBox.Checked = true;
403 aCBox.Border = aCBox.Background = false;
404 aWriter.CreateControl( aCBox );
406 PDFWriter::CheckBoxWidget aCBox2;
407 aCBox2.Name = "textCheckBox2";
408 aCBox2.Description = "Another test check box";
409 aCBox2.Text = "check me right";
410 aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
411 aCBox2.Checked = true;
412 aCBox2.Border = aCBox2.Background = false;
413 aCBox2.ButtonIsLeft = false;
414 aWriter.CreateControl( aCBox2 );
416 PDFWriter::RadioButtonWidget aRB1;
417 aRB1.Name = "rb1_1";
418 aRB1.Description = "radio 1 button 1";
419 aRB1.Text = "Despair";
420 aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
421 aRB1.Selected = true;
422 aRB1.RadioGroup = 1;
423 aRB1.Border = aRB1.Background = true;
424 aRB1.ButtonIsLeft = false;
425 aRB1.BorderColor = Color( COL_LIGHTGREEN );
426 aRB1.BackgroundColor = Color( COL_LIGHTBLUE );
427 aRB1.TextColor = Color( COL_LIGHTRED );
428 aRB1.TextFont = Font( OUString( "Courier" ), Size( 0, 800 ) );
429 aWriter.CreateControl( aRB1 );
431 PDFWriter::RadioButtonWidget aRB2;
432 aRB2.Name = "rb2_1";
433 aRB2.Description = "radio 2 button 1";
434 aRB2.Text = "Joy";
435 aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
436 aRB2.Selected = true;
437 aRB2.RadioGroup = 2;
438 aWriter.CreateControl( aRB2 );
440 PDFWriter::RadioButtonWidget aRB3;
441 aRB3.Name = "rb1_2";
442 aRB3.Description = "radio 1 button 2";
443 aRB3.Text = "Desperation";
444 aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
445 aRB3.Selected = true;
446 aRB3.RadioGroup = 1;
447 aWriter.CreateControl( aRB3 );
449 PDFWriter::EditWidget aEditBox;
450 aEditBox.Name = "testEdit";
451 aEditBox.Description = "A test edit field";
452 aEditBox.Text = "A little test text";
453 aEditBox.TextStyle = DrawTextFlags::Left | DrawTextFlags::VCenter;
454 aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
455 aEditBox.MaxLen = 100;
456 aEditBox.Border = aEditBox.Background = true;
457 aEditBox.BorderColor = Color( COL_BLACK );
458 aWriter.CreateControl( aEditBox );
460 // normal list box
461 PDFWriter::ListBoxWidget aLstBox;
462 aLstBox.Name = "testListBox";
463 aLstBox.Text = "One";
464 aLstBox.Description = "select me";
465 aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
466 aLstBox.Sort = true;
467 aLstBox.MultiSelect = true;
468 aLstBox.Border = aLstBox.Background = true;
469 aLstBox.BorderColor = Color( COL_BLACK );
470 aLstBox.Entries.push_back( OUString( "One" ) );
471 aLstBox.Entries.push_back( OUString( "Two" ) );
472 aLstBox.Entries.push_back( OUString( "Three" ) );
473 aLstBox.Entries.push_back( OUString( "Four" ) );
474 aLstBox.SelectedEntries.push_back( 1 );
475 aLstBox.SelectedEntries.push_back( 2 );
476 aWriter.CreateControl( aLstBox );
478 // dropdown list box
479 aLstBox.Name = "testDropDownListBox";
480 aLstBox.DropDown = true;
481 aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
482 aWriter.CreateControl( aLstBox );
484 // combo box
485 PDFWriter::ComboBoxWidget aComboBox;
486 aComboBox.Name = "testComboBox";
487 aComboBox.Text = "test a combobox";
488 aComboBox.Entries.push_back( OUString( "Larry" ) );
489 aComboBox.Entries.push_back( OUString( "Curly" ) );
490 aComboBox.Entries.push_back( OUString( "Moe" ) );
491 aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
492 aWriter.CreateControl( aComboBox );
494 // test outlines
495 sal_Int32 nPage1OL = aWriter.CreateOutlineItem();
496 aWriter.SetOutlineItemText( nPage1OL, OUString( "Page 1" ) );
497 aWriter.SetOutlineItemDest( nPage1OL, nSecondDest );
498 aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2" ), nSecondDest );
499 aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 revisited" ), nSecondDest );
500 aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 again" ), nSecondDest );
501 sal_Int32 nPage2OL = aWriter.CreateOutlineItem();
502 aWriter.SetOutlineItemText( nPage2OL, OUString( "Page 2" ) );
503 aWriter.CreateOutlineItem( nPage2OL, OUString( "Dest 1" ), nFirstDest );
505 aWriter.EndStructureElement(); // close document
507 aWriter.Emit();
509 #endif
511 static const sal_Int32 nLog10Divisor = 1;
512 static const double fDivisor = 10.0;
514 static inline double pixelToPoint( double px ) { return px/fDivisor; }
515 static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); }
517 const sal_uInt8 PDFWriterImpl::s_nPadString[32] =
519 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
520 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
523 static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
525 static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
526 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
527 rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
528 rBuffer.append( pHexDigits[ nInt & 15 ] );
531 static void appendName( const OUString& rStr, OStringBuffer& rBuffer )
533 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
534 // I guess than when reading the #xx sequence it will count for a single character.
535 OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) );
536 const sal_Char* pStr = aStr.getStr();
537 int nLen = aStr.getLength();
538 for( int i = 0; i < nLen; i++ )
540 /* #i16920# PDF recommendation: output UTF8, any byte
541 * outside the interval [33(=ASCII'!');126(=ASCII'~')]
542 * should be escaped hexadecimal
543 * for the sake of ghostscript which also reads PDF
544 * but has a narrower acceptance rate we only pass
545 * alphanumerics and '-' literally.
547 if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) ||
548 (pStr[i] >= 'a' && pStr[i] <= 'z' ) ||
549 (pStr[i] >= '0' && pStr[i] <= '9' ) ||
550 pStr[i] == '-' )
552 rBuffer.append( pStr[i] );
554 else
556 rBuffer.append( '#' );
557 appendHex( (sal_Int8)pStr[i], rBuffer );
562 static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer )
564 //FIXME i59651 see above
565 while( pStr && *pStr )
567 if( (*pStr >= 'A' && *pStr <= 'Z' ) ||
568 (*pStr >= 'a' && *pStr <= 'z' ) ||
569 (*pStr >= '0' && *pStr <= '9' ) ||
570 *pStr == '-' )
572 rBuffer.append( *pStr );
574 else
576 rBuffer.append( '#' );
577 appendHex( (sal_Int8)*pStr, rBuffer );
579 pStr++;
583 //used only to emit encoded passwords
584 static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer )
586 while( nLength )
588 switch( *pStr )
590 case '\n' :
591 rBuffer.append( "\\n" );
592 break;
593 case '\r' :
594 rBuffer.append( "\\r" );
595 break;
596 case '\t' :
597 rBuffer.append( "\\t" );
598 break;
599 case '\b' :
600 rBuffer.append( "\\b" );
601 break;
602 case '\f' :
603 rBuffer.append( "\\f" );
604 break;
605 case '(' :
606 case ')' :
607 case '\\' :
608 rBuffer.append( "\\" );
609 rBuffer.append( (sal_Char) *pStr );
610 break;
611 default:
612 rBuffer.append( (sal_Char) *pStr );
613 break;
615 pStr++;
616 nLength--;
620 /**--->i56629
621 * Convert a string before using it.
623 * This string conversion function is needed because the destination name
624 * in a PDF file seen through an Internet browser should be
625 * specially crafted, in order to be used directly by the browser.
626 * In this way the fragment part of a hyperlink to a PDF file (e.g. something
627 * as 'test1/test2/a-file.pdf\#thefragment) will be (hopefully) interpreted by the
628 * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
629 * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
630 * and go to named destination thefragment using default zoom'.
631 * The conversion is needed because in case of a fragment in the form: Slide%201
632 * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
633 * using this conversion, in both the generated named destinations, fragment and GoToR
634 * destination.
636 * The names for destinations are name objects and so they don't need to be encrypted
637 * even though they expose the content of PDF file (e.g. guessing the PDF content from the
638 * destination name).
640 * Fhurter limitation: it is advisable to use standard ASCII characters for
641 * OOo bookmarks.
643 static void appendDestinationName( const OUString& rString, OStringBuffer& rBuffer )
645 const sal_Unicode* pStr = rString.getStr();
646 sal_Int32 nLen = rString.getLength();
647 for( int i = 0; i < nLen; i++ )
649 sal_Unicode aChar = pStr[i];
650 if( (aChar >= '0' && aChar <= '9' ) ||
651 (aChar >= 'a' && aChar <= 'z' ) ||
652 (aChar >= 'A' && aChar <= 'Z' ) ||
653 aChar == '-' )
655 rBuffer.append((sal_Char)aChar);
657 else
659 sal_Int8 aValueHigh = sal_Int8(aChar >> 8);
660 if(aValueHigh > 0)
661 appendHex( aValueHigh, rBuffer );
662 appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
666 //<--- i56629
668 static void appendUnicodeTextString( const OUString& rString, OStringBuffer& rBuffer )
670 rBuffer.append( "FEFF" );
671 const sal_Unicode* pStr = rString.getStr();
672 sal_Int32 nLen = rString.getLength();
673 for( int i = 0; i < nLen; i++ )
675 sal_Unicode aChar = pStr[i];
676 appendHex( (sal_Int8)(aChar >> 8), rBuffer );
677 appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
681 void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl )
683 /* #i80258# previously we use appendName here
684 however we need a slightly different coding scheme than the normal
685 name encoding for field names
687 const OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ? i_rControl.Name : i_rControl.Text;
688 OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) );
689 const sal_Char* pStr = aStr.getStr();
690 int nLen = aStr.getLength();
692 OStringBuffer aBuffer( rName.getLength()+64 );
693 for( int i = 0; i < nLen; i++ )
695 /* #i16920# PDF recommendation: output UTF8, any byte
696 * outside the interval [32(=ASCII' ');126(=ASCII'~')]
697 * should be escaped hexadecimal
699 if( (pStr[i] >= 32 && pStr[i] <= 126 ) )
700 aBuffer.append( pStr[i] );
701 else
703 aBuffer.append( '#' );
704 appendHex( (sal_Int8)pStr[i], aBuffer );
708 OString aFullName( aBuffer.makeStringAndClear() );
710 /* #i82785# create hierarchical fields down to the for each dot in i_rName */
711 sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0;
712 OString aPartialName;
713 OString aDomain;
716 nLastTokenIndex = nTokenIndex;
717 aPartialName = aFullName.getToken( 0, '.', nTokenIndex );
718 if( nTokenIndex != -1 )
720 // find or create a hierarchical field
721 // first find the fully qualified name up to this field
722 aDomain = aFullName.copy( 0, nTokenIndex-1 );
723 std::unordered_map< OString, sal_Int32, OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
724 if( it == m_aFieldNameMap.end() )
726 // create new hierarchy field
727 sal_Int32 nNewWidget = m_aWidgets.size();
728 m_aWidgets.push_back( PDFWidget() );
729 m_aWidgets[nNewWidget].m_nObject = createObject();
730 m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy;
731 m_aWidgets[nNewWidget].m_aName = aPartialName;
732 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
733 m_aFieldNameMap[aDomain] = nNewWidget;
734 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
735 if( nLastTokenIndex > 0 )
737 // this field is not a root field and
738 // needs to be inserted to its parent
739 OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) );
740 it = m_aFieldNameMap.find( aParentDomain );
741 OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" );
742 if( it != m_aFieldNameMap.end() )
744 OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" );
745 if( it->second < sal_Int32(m_aWidgets.size()) )
747 PDFWidget& rParentField( m_aWidgets[it->second] );
748 rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject );
749 rParentField.m_aKidsIndex.push_back( nNewWidget );
750 m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject;
755 else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy )
757 // this is invalid, someone tries to have a terminal field as parent
758 // example: a button with the name foo.bar exists and
759 // another button is named foo.bar.no
760 // workaround: put the second terminal field as much up in the hierarchy as
761 // necessary to have a non-terminal field as parent (or none at all)
762 // since it->second already is terminal, we just need to use its parent
763 aDomain.clear();
764 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
765 if( nLastTokenIndex > 0 )
767 aDomain = aFullName.copy( 0, nLastTokenIndex-1 );
768 OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() );
769 aBuf.append( aDomain );
770 aBuf.append( '.' );
771 aBuf.append( aPartialName );
772 aFullName = aBuf.makeStringAndClear();
774 else
775 aFullName = aPartialName;
776 break;
779 } while( nTokenIndex != -1 );
781 // insert widget into its hierarchy field
782 if( !aDomain.isEmpty() )
784 std::unordered_map< OString, sal_Int32, OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
785 if( it != m_aFieldNameMap.end() )
787 OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" );
788 if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) )
790 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject;
791 m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject);
792 m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex );
797 if( aPartialName.isEmpty() )
799 // how funny, an empty field name
800 if( i_rControl.getType() == PDFWriter::RadioButton )
802 aPartialName = "RadioGroup";
803 aPartialName += OString::number( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup );
805 else
806 aPartialName = OString( "Widget" );
809 if( ! m_aContext.AllowDuplicateFieldNames )
811 std::unordered_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aFullName );
813 if( it != m_aFieldNameMap.end() ) // not unique
815 std::unordered_map< OString, sal_Int32, OStringHash >::const_iterator check_it;
816 OString aTry;
817 sal_Int32 nTry = 2;
820 OStringBuffer aUnique( aFullName.getLength() + 16 );
821 aUnique.append( aFullName );
822 aUnique.append( '_' );
823 aUnique.append( nTry++ );
824 aTry = aUnique.makeStringAndClear();
825 check_it = m_aFieldNameMap.find( aTry );
826 } while( check_it != m_aFieldNameMap.end() );
827 aFullName = aTry;
828 m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
829 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
831 else
832 m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
835 // finally
836 m_aWidgets[i_nWidgetIndex].m_aName = aPartialName;
839 static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor )
841 if( nValue < 0 )
843 rBuffer.append( '-' );
844 nValue = -nValue;
846 sal_Int32 nFactor = 1, nDiv = nPrecision;
847 while( nDiv-- )
848 nFactor *= 10;
850 sal_Int32 nInt = nValue / nFactor;
851 rBuffer.append( nInt );
852 if( nFactor > 1 )
854 sal_Int32 nDecimal = nValue % nFactor;
855 if( nDecimal )
857 rBuffer.append( '.' );
858 // omit trailing zeros
859 while( (nDecimal % 10) == 0 )
860 nDecimal /= 10;
861 rBuffer.append( nDecimal );
866 // appends a double. PDF does not accept exponential format, only fixed point
867 static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 )
869 bool bNeg = false;
870 if( fValue < 0.0 )
872 bNeg = true;
873 fValue=-fValue;
876 sal_Int64 nInt = (sal_Int64)fValue;
877 fValue -= (double)nInt;
878 // optimizing hardware may lead to a value of 1.0 after the subtraction
879 if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision )
881 nInt++;
882 fValue = 0.0;
884 sal_Int64 nFrac = 0;
885 if( fValue )
887 fValue *= pow( 10.0, (double)nPrecision );
888 nFrac = (sal_Int64)fValue;
890 if( bNeg && ( nInt || nFrac ) )
891 rBuffer.append( '-' );
892 rBuffer.append( nInt );
893 if( nFrac )
895 int i;
896 rBuffer.append( '.' );
897 sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5);
898 for ( i = 0; ( i < nPrecision ) && nFrac; i++ )
900 sal_Int64 nNumb = nFrac / nBound;
901 nFrac -= nNumb * nBound;
902 rBuffer.append( nNumb );
903 nBound /= 10;
908 static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey = false )
911 if( rColor != Color( COL_TRANSPARENT ) )
913 if( bConvertToGrey )
915 sal_uInt8 cByte = rColor.GetLuminance();
916 appendDouble( (double)cByte / 255.0, rBuffer );
918 else
920 appendDouble( (double)rColor.GetRed() / 255.0, rBuffer );
921 rBuffer.append( ' ' );
922 appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer );
923 rBuffer.append( ' ' );
924 appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer );
929 void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
931 if( rColor != Color( COL_TRANSPARENT ) )
933 bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
934 appendColor( rColor, rBuffer, bGrey );
935 rBuffer.append( bGrey ? " G" : " RG" );
939 void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
941 if( rColor != Color( COL_TRANSPARENT ) )
943 bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
944 appendColor( rColor, rBuffer, bGrey );
945 rBuffer.append( bGrey ? " g" : " rg" );
949 // matrix helper class
950 // TODO: use basegfx matrix class instead or derive from it
951 namespace vcl // TODO: use anonymous namespace to keep this class local
953 /* for sparse matrices of the form (2D linear transformations)
954 * f[0] f[1] 0
955 * f[2] f[3] 0
956 * f[4] f[5] 1
958 class Matrix3
960 double f[6];
962 void set( double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; }
963 public:
964 Matrix3();
965 ~Matrix3() {}
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 );
971 bool invert();
973 void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL );
975 Point transform( const Point& rPoint ) const;
979 Matrix3::Matrix3()
981 // initialize to unity
982 f[0] = 1.0;
983 f[1] = 0.0;
984 f[2] = 0.0;
985 f[3] = 1.0;
986 f[4] = 0.0;
987 f[5] = 0.0;
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 )
998 double fn[6];
999 double tb = tan( beta );
1000 fn[0] = f[0] + f[2]*tb;
1001 fn[1] = f[1];
1002 fn[2] = f[2] + f[3]*tb;
1003 fn[3] = f[3];
1004 fn[4] = f[4] + f[5]*tb;
1005 fn[5] = f[5];
1006 if( alpha != 0.0 )
1008 double ta = tan( alpha );
1009 fn[1] += f[0]*ta;
1010 fn[3] += f[2]*ta;
1011 fn[5] += f[4]*ta;
1013 set( fn );
1016 void Matrix3::scale( double sx, double sy )
1018 double fn[6];
1019 fn[0] = sx*f[0];
1020 fn[1] = sy*f[1];
1021 fn[2] = sx*f[2];
1022 fn[3] = sy*f[3];
1023 fn[4] = sx*f[4];
1024 fn[5] = sy*f[5];
1025 set( fn );
1028 void Matrix3::rotate( double angle )
1030 double fn[6];
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;
1039 set( fn );
1042 void Matrix3::translate( double tx, double ty )
1044 f[4] += tx;
1045 f[5] += ty;
1048 bool 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 )
1053 f[4] = -f[4];
1054 f[5] = -f[5];
1055 return true;
1058 // check determinant
1059 const double fDet = f[0]*f[3]-f[1]*f[2];
1060 if( fDet == 0.0 )
1061 return false;
1063 // invert the matrix
1064 double fn[6];
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]);
1074 set( fn );
1075 return true;
1078 void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack )
1080 appendDouble( f[0], rBuffer );
1081 rBuffer.append( ' ' );
1082 appendDouble( f[1], rBuffer );
1083 rBuffer.append( ' ' );
1084 appendDouble( f[2], rBuffer );
1085 rBuffer.append( ' ' );
1086 appendDouble( f[3], rBuffer );
1087 rBuffer.append( ' ' );
1088 rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack );
1091 static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList )
1093 if( rList.empty() )
1094 return;
1095 rBuf.append( '/' );
1096 rBuf.append( pPrefix );
1097 rBuf.append( "<<" );
1098 int ni = 0;
1099 for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it )
1101 if( !it->first.isEmpty() && it->second > 0 )
1103 rBuf.append( '/' );
1104 rBuf.append( it->first );
1105 rBuf.append( ' ' );
1106 rBuf.append( it->second );
1107 rBuf.append( " 0 R" );
1108 if( ((++ni) & 7) == 0 )
1109 rBuf.append( '\n' );
1112 rBuf.append( ">>\n" );
1115 void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject )
1117 rBuf.append( "<</Font " );
1118 rBuf.append( nFontDictObject );
1119 rBuf.append( " 0 R\n" );
1120 appendResourceMap( rBuf, "XObject", m_aXObjects );
1121 appendResourceMap( rBuf, "ExtGState", m_aExtGStates );
1122 appendResourceMap( rBuf, "Shading", m_aShadings );
1123 appendResourceMap( rBuf, "Pattern", m_aPatterns );
1124 rBuf.append( "/ProcSet[/PDF/Text" );
1125 if( !m_aXObjects.empty() )
1126 rBuf.append( "/ImageC/ImageI/ImageB" );
1127 rBuf.append( "]\n>>\n" );
1130 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
1132 m_pWriter( pWriter ),
1133 m_nPageWidth( nPageWidth ),
1134 m_nPageHeight( nPageHeight ),
1135 m_eOrientation( eOrientation ),
1136 m_nPageObject( 0 ), // invalid object number
1137 m_nPageIndex( -1 ), // invalid index
1138 m_nStreamLengthObject( 0 ),
1139 m_nBeginStreamPos( 0 ),
1140 m_eTransition( PDFWriter::Regular ),
1141 m_nTransTime( 0 ),
1142 m_nDuration( 0 ),
1143 m_bHasWidgets( false )
1145 // object ref must be only ever updated in emit()
1146 m_nPageObject = m_pWriter->createObject();
1149 PDFWriterImpl::PDFPage::~PDFPage()
1153 void PDFWriterImpl::PDFPage::beginStream()
1155 #if OSL_DEBUG_LEVEL > 1
1157 OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" );
1158 m_pWriter->emitComment( aLine.getStr() );
1160 #endif
1161 m_aStreamObjects.push_back(m_pWriter->createObject());
1162 if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) )
1163 return;
1165 m_nStreamLengthObject = m_pWriter->createObject();
1166 // write content stream header
1167 OStringBuffer aLine;
1168 aLine.append( m_aStreamObjects.back() );
1169 aLine.append( " 0 obj\n<</Length " );
1170 aLine.append( m_nStreamLengthObject );
1171 aLine.append( " 0 R" );
1172 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1173 aLine.append( "/Filter/FlateDecode" );
1174 #endif
1175 aLine.append( ">>\nstream\n" );
1176 if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) )
1177 return;
1178 if (osl::File::E_None != m_pWriter->m_aFile.getPos(m_nBeginStreamPos))
1180 m_pWriter->m_aFile.close();
1181 m_pWriter->m_bOpen = false;
1183 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1184 m_pWriter->beginCompression();
1185 #endif
1186 m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() );
1189 void PDFWriterImpl::PDFPage::endStream()
1191 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1192 m_pWriter->endCompression();
1193 #endif
1194 sal_uInt64 nEndStreamPos;
1195 if (osl::File::E_None != m_pWriter->m_aFile.getPos(nEndStreamPos))
1197 m_pWriter->m_aFile.close();
1198 m_pWriter->m_bOpen = false;
1199 return;
1201 m_pWriter->disableStreamEncryption();
1202 if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1203 return;
1204 // emit stream length object
1205 if( ! m_pWriter->updateObject( m_nStreamLengthObject ) )
1206 return;
1207 OStringBuffer aLine;
1208 aLine.append( m_nStreamLengthObject );
1209 aLine.append( " 0 obj\n" );
1210 aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) );
1211 aLine.append( "\nendobj\n\n" );
1212 m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1215 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject )
1217 // emit page object
1218 if( ! m_pWriter->updateObject( m_nPageObject ) )
1219 return false;
1220 OStringBuffer aLine;
1222 aLine.append( m_nPageObject );
1223 aLine.append( " 0 obj\n"
1224 "<</Type/Page/Parent " );
1225 aLine.append( nParentObject );
1226 aLine.append( " 0 R" );
1227 aLine.append( "/Resources " );
1228 aLine.append( m_pWriter->getResourceDictObj() );
1229 aLine.append( " 0 R" );
1230 if( m_nPageWidth && m_nPageHeight )
1232 aLine.append( "/MediaBox[0 0 " );
1233 aLine.append( m_nPageWidth );
1234 aLine.append( ' ' );
1235 aLine.append( m_nPageHeight );
1236 aLine.append( "]" );
1238 switch( m_eOrientation )
1240 case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
1241 case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
1242 case PDFWriter::Portrait: aLine.append( "/Rotate 0\n" );break;
1244 case PDFWriter::Inherit:
1245 default:
1246 break;
1248 int nAnnots = m_aAnnotations.size();
1249 if( nAnnots > 0 )
1251 aLine.append( "/Annots[\n" );
1252 for( int i = 0; i < nAnnots; i++ )
1254 aLine.append( m_aAnnotations[i] );
1255 aLine.append( " 0 R" );
1256 aLine.append( ((i+1)%15) ? " " : "\n" );
1258 aLine.append( "]\n" );
1260 if( m_aMCIDParents.size() > 0 )
1262 OStringBuffer aStructParents( 1024 );
1263 aStructParents.append( "[ " );
1264 int nParents = m_aMCIDParents.size();
1265 for( int i = 0; i < nParents; i++ )
1267 aStructParents.append( m_aMCIDParents[i] );
1268 aStructParents.append( " 0 R" );
1269 aStructParents.append( ((i%10) == 9) ? "\n" : " " );
1271 aStructParents.append( "]" );
1272 m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() );
1274 aLine.append( "/StructParents " );
1275 aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) );
1276 aLine.append( "\n" );
1278 if( m_nDuration > 0 )
1280 aLine.append( "/Dur " );
1281 aLine.append( (sal_Int32)m_nDuration );
1282 aLine.append( "\n" );
1284 if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 )
1286 // transition duration
1287 aLine.append( "/Trans<</D " );
1288 appendDouble( (double)m_nTransTime/1000.0, aLine, 3 );
1289 aLine.append( "\n" );
1290 const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL;
1291 switch( m_eTransition )
1293 case PDFWriter::SplitHorizontalInward:
1294 pStyle = "Split"; pDm = "H"; pM = "I"; break;
1295 case PDFWriter::SplitHorizontalOutward:
1296 pStyle = "Split"; pDm = "H"; pM = "O"; break;
1297 case PDFWriter::SplitVerticalInward:
1298 pStyle = "Split"; pDm = "V"; pM = "I"; break;
1299 case PDFWriter::SplitVerticalOutward:
1300 pStyle = "Split"; pDm = "V"; pM = "O"; break;
1301 case PDFWriter::BlindsHorizontal:
1302 pStyle = "Blinds"; pDm = "H"; break;
1303 case PDFWriter::BlindsVertical:
1304 pStyle = "Blinds"; pDm = "V"; break;
1305 case PDFWriter::BoxInward:
1306 pStyle = "Box"; pM = "I"; break;
1307 case PDFWriter::BoxOutward:
1308 pStyle = "Box"; pM = "O"; break;
1309 case PDFWriter::WipeLeftToRight:
1310 pStyle = "Wipe"; pDi = "0"; break;
1311 case PDFWriter::WipeBottomToTop:
1312 pStyle = "Wipe"; pDi = "90"; break;
1313 case PDFWriter::WipeRightToLeft:
1314 pStyle = "Wipe"; pDi = "180"; break;
1315 case PDFWriter::WipeTopToBottom:
1316 pStyle = "Wipe"; pDi = "270"; break;
1317 case PDFWriter::Dissolve:
1318 pStyle = "Dissolve"; break;
1319 case PDFWriter::GlitterLeftToRight:
1320 pStyle = "Glitter"; pDi = "0"; break;
1321 case PDFWriter::GlitterTopToBottom:
1322 pStyle = "Glitter"; pDi = "270"; break;
1323 case PDFWriter::GlitterTopLeftToBottomRight:
1324 pStyle = "Glitter"; pDi = "315"; break;
1325 case PDFWriter::Regular:
1326 break;
1328 // transition style
1329 if( pStyle )
1331 aLine.append( "/S/" );
1332 aLine.append( pStyle );
1333 aLine.append( "\n" );
1335 if( pDm )
1337 aLine.append( "/Dm/" );
1338 aLine.append( pDm );
1339 aLine.append( "\n" );
1341 if( pM )
1343 aLine.append( "/M/" );
1344 aLine.append( pM );
1345 aLine.append( "\n" );
1347 if( pDi )
1349 aLine.append( "/Di " );
1350 aLine.append( pDi );
1351 aLine.append( "\n" );
1353 aLine.append( ">>\n" );
1355 if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 )
1357 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1359 aLine.append( "/Contents" );
1360 unsigned int nStreamObjects = m_aStreamObjects.size();
1361 if( nStreamObjects > 1 )
1362 aLine.append( '[' );
1363 for( unsigned int i = 0; i < m_aStreamObjects.size(); i++ )
1365 aLine.append( ' ' );
1366 aLine.append( m_aStreamObjects[i] );
1367 aLine.append( " 0 R" );
1369 if( nStreamObjects > 1 )
1370 aLine.append( ']' );
1371 aLine.append( ">>\nendobj\n\n" );
1372 return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1375 namespace vcl
1377 template < class GEOMETRY >
1378 GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
1380 GEOMETRY aPoint;
1381 if ( MAP_PIXEL == _rSource.GetMapUnit() )
1383 aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
1385 else
1387 aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
1389 return aPoint;
1393 void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const
1395 if( pOutPoint )
1397 Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1398 m_pWriter->m_aMapMode,
1399 m_pWriter->getReferenceDevice(),
1400 rPoint ) );
1401 *pOutPoint = aPoint;
1404 Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1405 m_pWriter->m_aMapMode,
1406 m_pWriter->getReferenceDevice(),
1407 rPoint ) );
1409 sal_Int32 nValue = aPoint.X();
1410 if( bNeg )
1411 nValue = -nValue;
1413 appendFixedInt( nValue, rBuffer );
1415 rBuffer.append( ' ' );
1417 nValue = pointToPixel(getHeight()) - aPoint.Y();
1418 if( bNeg )
1419 nValue = -nValue;
1421 appendFixedInt( nValue, rBuffer );
1424 void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const
1426 double fValue = pixelToPoint(rPoint.getX());
1428 appendDouble( fValue, rBuffer, nLog10Divisor );
1429 rBuffer.append( ' ' );
1430 fValue = double(getHeight()) - pixelToPoint(rPoint.getY());
1431 appendDouble( fValue, rBuffer, nLog10Divisor );
1434 void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const
1436 appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer );
1437 rBuffer.append( ' ' );
1438 appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false );
1439 rBuffer.append( ' ' );
1440 appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true );
1441 rBuffer.append( " re" );
1444 void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const
1446 Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1447 m_pWriter->m_aMapMode,
1448 m_pWriter->getReferenceDevice(),
1449 rRect.BottomLeft() + Point( 0, 1 )
1451 Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1452 m_pWriter->m_aMapMode,
1453 m_pWriter->getReferenceDevice(),
1454 rRect.GetSize() );
1455 rRect.Left() = aLL.X();
1456 rRect.Right() = aLL.X() + aSize.Width();
1457 rRect.Top() = pointToPixel(getHeight()) - aLL.Y();
1458 rRect.Bottom() = rRect.Top() + aSize.Height();
1461 void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1463 sal_uInt16 nPoints = rPoly.GetSize();
1465 * #108582# applications do weird things
1467 sal_uInt32 nBufLen = rBuffer.getLength();
1468 if( nPoints > 0 )
1470 const sal_uInt8* pFlagArray = rPoly.GetConstFlagAry();
1471 appendPoint( rPoly[0], rBuffer );
1472 rBuffer.append( " m\n" );
1473 for( sal_uInt16 i = 1; i < nPoints; i++ )
1475 if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 )
1477 // bezier
1478 DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" );
1479 appendPoint( rPoly[i], rBuffer );
1480 rBuffer.append( " " );
1481 appendPoint( rPoly[i+1], rBuffer );
1482 rBuffer.append( " " );
1483 appendPoint( rPoly[i+2], rBuffer );
1484 rBuffer.append( " c" );
1485 i += 2; // add additionally consumed points
1487 else
1489 // line
1490 appendPoint( rPoly[i], rBuffer );
1491 rBuffer.append( " l" );
1493 if( (rBuffer.getLength() - nBufLen) > 65 )
1495 rBuffer.append( "\n" );
1496 nBufLen = rBuffer.getLength();
1498 else
1499 rBuffer.append( " " );
1501 if( bClose )
1502 rBuffer.append( "h\n" );
1506 void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1508 basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1509 m_pWriter->m_aMapMode,
1510 m_pWriter->getReferenceDevice(),
1511 rPoly ) );
1513 if( basegfx::tools::isRectangle( aPoly ) )
1515 basegfx::B2DRange aRange( aPoly.getB2DRange() );
1516 basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() );
1517 appendPixelPoint( aBL, rBuffer );
1518 rBuffer.append( ' ' );
1519 appendMappedLength( aRange.getWidth(), rBuffer, false, NULL, nLog10Divisor );
1520 rBuffer.append( ' ' );
1521 appendMappedLength( aRange.getHeight(), rBuffer, true, NULL, nLog10Divisor );
1522 rBuffer.append( " re\n" );
1523 return;
1525 sal_uInt32 nPoints = aPoly.count();
1526 if( nPoints > 0 )
1528 sal_uInt32 nBufLen = rBuffer.getLength();
1529 basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) );
1530 appendPixelPoint( aLastPoint, rBuffer );
1531 rBuffer.append( " m\n" );
1532 for( sal_uInt32 i = 1; i <= nPoints; i++ )
1534 if( i != nPoints || aPoly.isClosed() )
1536 sal_uInt32 nCurPoint = i % nPoints;
1537 sal_uInt32 nLastPoint = i-1;
1538 basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) );
1539 if( aPoly.isNextControlPointUsed( nLastPoint ) &&
1540 aPoly.isPrevControlPointUsed( nCurPoint ) )
1542 appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1543 rBuffer.append( ' ' );
1544 appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1545 rBuffer.append( ' ' );
1546 appendPixelPoint( aPoint, rBuffer );
1547 rBuffer.append( " c" );
1549 else if( aPoly.isNextControlPointUsed( nLastPoint ) )
1551 appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1552 rBuffer.append( ' ' );
1553 appendPixelPoint( aPoint, rBuffer );
1554 rBuffer.append( " y" );
1556 else if( aPoly.isPrevControlPointUsed( nCurPoint ) )
1558 appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1559 rBuffer.append( ' ' );
1560 appendPixelPoint( aPoint, rBuffer );
1561 rBuffer.append( " v" );
1563 else
1565 appendPixelPoint( aPoint, rBuffer );
1566 rBuffer.append( " l" );
1568 if( (rBuffer.getLength() - nBufLen) > 65 )
1570 rBuffer.append( "\n" );
1571 nBufLen = rBuffer.getLength();
1573 else
1574 rBuffer.append( " " );
1577 if( bClose )
1578 rBuffer.append( "h\n" );
1582 void PDFWriterImpl::PDFPage::appendPolyPolygon( const tools::PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1584 sal_uInt16 nPolygons = rPolyPoly.Count();
1585 for( sal_uInt16 n = 0; n < nPolygons; n++ )
1586 appendPolygon( rPolyPoly[n], rBuffer, bClose );
1589 void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1591 sal_uInt32 nPolygons = rPolyPoly.count();
1592 for( sal_uInt32 n = 0; n < nPolygons; n++ )
1593 appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer, bClose );
1596 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1598 sal_Int32 nValue = nLength;
1599 if ( nLength < 0 )
1601 rBuffer.append( '-' );
1602 nValue = -nLength;
1604 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1605 m_pWriter->m_aMapMode,
1606 m_pWriter->getReferenceDevice(),
1607 Size( nValue, nValue ) ) );
1608 nValue = bVertical ? aSize.Height() : aSize.Width();
1609 if( pOutLength )
1610 *pOutLength = ((nLength < 0 ) ? -nValue : nValue);
1612 appendFixedInt( nValue, rBuffer, 1 );
1615 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength, sal_Int32 nPrecision ) const
1617 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1618 m_pWriter->m_aMapMode,
1619 m_pWriter->getReferenceDevice(),
1620 Size( 1000, 1000 ) ) );
1621 if( pOutLength )
1622 *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0);
1623 fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0);
1624 appendDouble( fLength, rBuffer, nPrecision );
1627 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const
1629 if(LINE_DASH == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen())
1631 // dashed and non-degraded case, check for implementation limits of dash array
1632 // in PDF reader apps (e.g. acroread)
1633 if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10)
1635 return false;
1639 if(basegfx::B2DLINEJOIN_NONE != rInfo.GetLineJoin())
1641 // LineJoin used, ExtLineInfo required
1642 return false;
1645 if(com::sun::star::drawing::LineCap_BUTT != rInfo.GetLineCap())
1647 // LineCap used, ExtLineInfo required
1648 return false;
1651 if( rInfo.GetStyle() == LINE_DASH )
1653 rBuffer.append( "[ " );
1654 if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case
1656 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1657 rBuffer.append( ' ' );
1658 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1659 rBuffer.append( ' ' );
1661 else
1663 for( int n = 0; n < rInfo.GetDashCount(); n++ )
1665 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1666 rBuffer.append( ' ' );
1667 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1668 rBuffer.append( ' ' );
1670 for( int m = 0; m < rInfo.GetDotCount(); m++ )
1672 appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer );
1673 rBuffer.append( ' ' );
1674 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1675 rBuffer.append( ' ' );
1678 rBuffer.append( "] 0 d\n" );
1681 if( rInfo.GetWidth() > 1 )
1683 appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer );
1684 rBuffer.append( " w\n" );
1686 else if( rInfo.GetWidth() == 0 )
1688 // "pixel" line
1689 appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->GetDPIX()), rBuffer );
1690 rBuffer.append( " w\n" );
1693 return true;
1696 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const
1698 if( nWidth <= 0 )
1699 return;
1700 if( nDelta < 1 )
1701 nDelta = 1;
1703 rBuffer.append( "0 " );
1704 appendMappedLength( nY, rBuffer, true );
1705 rBuffer.append( " m\n" );
1706 for( sal_Int32 n = 0; n < nWidth; )
1708 n += nDelta;
1709 appendMappedLength( n, rBuffer, false );
1710 rBuffer.append( ' ' );
1711 appendMappedLength( nDelta+nY, rBuffer, true );
1712 rBuffer.append( ' ' );
1713 n += nDelta;
1714 appendMappedLength( n, rBuffer, false );
1715 rBuffer.append( ' ' );
1716 appendMappedLength( nY, rBuffer, true );
1717 rBuffer.append( " v " );
1718 if( n < nWidth )
1720 n += nDelta;
1721 appendMappedLength( n, rBuffer, false );
1722 rBuffer.append( ' ' );
1723 appendMappedLength( nY-nDelta, rBuffer, true );
1724 rBuffer.append( ' ' );
1725 n += nDelta;
1726 appendMappedLength( n, rBuffer, false );
1727 rBuffer.append( ' ' );
1728 appendMappedLength( nY, rBuffer, true );
1729 rBuffer.append( " v\n" );
1732 rBuffer.append( "S\n" );
1735 PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext,
1736 const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc,
1737 PDFWriter& i_rOuterFace)
1739 m_pReferenceDevice( NULL ),
1740 m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ),
1741 m_nCurrentStructElement( 0 ),
1742 m_bEmitStructure( true ),
1743 m_bNewMCID( false ),
1744 m_nNextFID( 1 ),
1745 m_nInheritedPageWidth( 595 ), // default A4
1746 m_nInheritedPageHeight( 842 ), // default A4
1747 m_eInheritedOrientation( PDFWriter::Portrait ),
1748 m_nCurrentPage( -1 ),
1749 m_nCatalogObject(0),
1750 m_nSignatureObject( -1 ),
1751 m_nSignatureContentOffset( 0 ),
1752 m_nSignatureLastByteRangeNoOffset( 0 ),
1753 m_nResourceDict( -1 ),
1754 m_nFontDictObject( -1 ),
1755 m_aContext(rContext),
1756 m_aFile(m_aContext.URL),
1757 m_bOpen(false),
1758 m_pCodec( NULL ),
1759 m_pMemStream(NULL),
1760 m_aDocDigest( rtl_digest_createMD5() ),
1761 m_aCipher( (rtlCipher)NULL ),
1762 m_aDigest( NULL ),
1763 m_nKeyLength(0),
1764 m_nRC4KeyLength(0),
1765 m_bEncryptThisStream( false ),
1766 m_nAccessPermissions(0),
1767 m_pEncryptionBuffer( NULL ),
1768 m_nEncryptionBufferSize( 0 ),
1769 m_bIsPDF_A1( false ),
1770 m_rOuterFace( i_rOuterFace )
1772 #ifdef DO_TEST_PDF
1773 static bool bOnce = true;
1774 if( bOnce )
1776 bOnce = false;
1777 doTestCode();
1779 #endif
1780 m_aStructure.push_back( PDFStructureElement() );
1781 m_aStructure[0].m_nOwnElement = 0;
1782 m_aStructure[0].m_nParentElement = 0;
1784 Font aFont;
1785 aFont.SetName( OUString( "Times" ) );
1786 aFont.SetSize( Size( 0, 12 ) );
1788 GraphicsState aState;
1789 aState.m_aMapMode = m_aMapMode;
1790 aState.m_aFont = aFont;
1791 m_aGraphicsStack.push_front( aState );
1793 osl::File::RC aError = m_aFile.open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
1794 if (aError != osl::File::E_None)
1796 if (aError == osl::File::E_EXIST)
1798 aError = m_aFile.open(osl_File_OpenFlag_Write);
1799 if (aError == osl::File::E_None)
1800 aError = m_aFile.setSize(0);
1803 if (aError != osl::File::E_None)
1804 return;
1806 m_bOpen = true;
1808 // setup DocInfo
1809 setupDocInfo();
1811 /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1812 m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1813 m_aDigest = rtl_digest_createMD5();
1815 /* the size of the Codec default maximum */
1816 /* is this 0x4000 required to be the same as MAX_SIGNATURE_CONTENT_LENGTH or just coincidentally the same at the moment? */
1817 if (!checkEncryptionBufferSize(0x4000))
1819 m_aFile.close();
1820 m_bOpen = false;
1821 return;
1824 if( xEnc.is() )
1825 prepareEncryption( xEnc );
1827 if( m_aContext.Encryption.Encrypt() )
1829 // sanity check
1830 if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE ||
1831 m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE ||
1832 m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH
1835 // the field lengths are invalid ? This was not setup by initEncryption.
1836 // do not encrypt after all
1837 m_aContext.Encryption.OValue.clear();
1838 m_aContext.Encryption.UValue.clear();
1839 OSL_ENSURE( false, "encryption data failed sanity check, encryption disabled" );
1841 else // setup key lengths
1842 m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength );
1845 // write header
1846 OStringBuffer aBuffer( 20 );
1847 aBuffer.append( "%PDF-" );
1848 switch( m_aContext.Version )
1850 case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break;
1851 case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break;
1852 case PDFWriter::PDF_A_1:
1853 default:
1854 case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break;
1855 case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break;
1857 // append something binary as comment (suggested in PDF Reference)
1858 aBuffer.append( "\n%\303\244\303\274\303\266\303\237\n" );
1859 if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) )
1861 m_aFile.close();
1862 m_bOpen = false;
1863 return;
1866 // insert outline root
1867 m_aOutline.push_back( PDFOutlineEntry() );
1869 m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1);
1870 if( m_bIsPDF_A1 )
1871 m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour
1874 PDFWriterImpl::~PDFWriterImpl()
1876 if( m_aDocDigest )
1877 rtl_digest_destroyMD5( m_aDocDigest );
1878 m_pReferenceDevice.disposeAndClear();
1880 if( m_aCipher )
1881 rtl_cipher_destroyARCFOUR( m_aCipher );
1882 if( m_aDigest )
1883 rtl_digest_destroyMD5( m_aDigest );
1885 rtl_freeMemory( m_pEncryptionBuffer );
1888 void PDFWriterImpl::setupDocInfo()
1890 std::vector< sal_uInt8 > aId;
1891 computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString );
1892 if( m_aContext.Encryption.DocumentIdentifier.empty() )
1893 m_aContext.Encryption.DocumentIdentifier = aId;
1896 void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier,
1897 const vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
1898 OString& o_rCString1,
1899 OString& o_rCString2
1902 o_rIdentifier.clear();
1904 //build the document id
1905 OString aInfoValuesOut;
1906 OStringBuffer aID( 1024 );
1907 if( !i_rDocInfo.Title.isEmpty() )
1908 appendUnicodeTextString( i_rDocInfo.Title, aID );
1909 if( !i_rDocInfo.Author.isEmpty() )
1910 appendUnicodeTextString( i_rDocInfo.Author, aID );
1911 if( !i_rDocInfo.Subject.isEmpty() )
1912 appendUnicodeTextString( i_rDocInfo.Subject, aID );
1913 if( !i_rDocInfo.Keywords.isEmpty() )
1914 appendUnicodeTextString( i_rDocInfo.Keywords, aID );
1915 if( !i_rDocInfo.Creator.isEmpty() )
1916 appendUnicodeTextString( i_rDocInfo.Creator, aID );
1917 if( !i_rDocInfo.Producer.isEmpty() )
1918 appendUnicodeTextString( i_rDocInfo.Producer, aID );
1920 TimeValue aTVal, aGMT;
1921 oslDateTime aDT;
1922 osl_getSystemTime( &aGMT );
1923 osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
1924 osl_getDateTimeFromTimeValue( &aTVal, &aDT );
1925 OStringBuffer aCreationDateString(64), aCreationMetaDateString(64);
1926 aCreationDateString.append( "D:" );
1927 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1928 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1929 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1930 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1931 aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1932 aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1933 aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1934 aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1935 aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1936 aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1937 aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1938 aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1939 aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1940 aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1942 //--> i59651, we fill the Metadata date string as well, if PDF/A is requested
1943 // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1944 // local time zone offset UTC only, whereas Acrobat 8 seems
1945 // to use the localtime notation only
1946 // according to a recommendation in XMP Specification (Jan 2004, page 75)
1947 // the Acrobat way seems the right approach
1948 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1949 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1950 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1951 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1952 aCreationMetaDateString.append( "-" );
1953 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1954 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1955 aCreationMetaDateString.append( "-" );
1956 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1957 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1958 aCreationMetaDateString.append( "T" );
1959 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1960 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1961 aCreationMetaDateString.append( ":" );
1962 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1963 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1964 aCreationMetaDateString.append( ":" );
1965 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1966 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1968 sal_uInt32 nDelta = 0;
1969 if( aGMT.Seconds > aTVal.Seconds )
1971 aCreationDateString.append( "-" );
1972 nDelta = aGMT.Seconds-aTVal.Seconds;
1973 aCreationMetaDateString.append( "-" );
1975 else if( aGMT.Seconds < aTVal.Seconds )
1977 aCreationDateString.append( "+" );
1978 nDelta = aTVal.Seconds-aGMT.Seconds;
1979 aCreationMetaDateString.append( "+" );
1981 else
1983 aCreationDateString.append( "Z" );
1984 aCreationMetaDateString.append( "Z" );
1987 if( nDelta )
1989 aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1990 aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1991 aCreationDateString.append( "'" );
1992 aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1993 aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1995 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1996 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1997 aCreationMetaDateString.append( ":" );
1998 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1999 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
2001 aCreationDateString.append( "'" );
2002 aID.append( aCreationDateString.getStr(), aCreationDateString.getLength() );
2004 aInfoValuesOut = aID.makeStringAndClear();
2005 o_rCString1 = aCreationDateString.makeStringAndClear();
2006 o_rCString2 = aCreationMetaDateString.makeStringAndClear();
2008 rtlDigest aDigest = rtl_digest_createMD5();
2009 OSL_ENSURE( aDigest != NULL, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" );
2010 if( aDigest )
2012 rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) );
2013 if( nError == rtl_Digest_E_None )
2014 nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() );
2015 if( nError == rtl_Digest_E_None )
2017 o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 );
2018 //the binary form of the doc id is needed for encryption stuff
2019 rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 );
2021 rtl_digest_destroyMD5(aDigest);
2025 /* i12626 methods */
2027 check if the Unicode string must be encrypted or not, perform the requested task,
2028 append the string as unicode hex, encrypted if needed
2030 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2032 rOutBuffer.append( "<" );
2033 if( m_aContext.Encryption.Encrypt() )
2035 const sal_Unicode* pStr = rInString.getStr();
2036 sal_Int32 nLen = rInString.getLength();
2037 //prepare a unicode string, encrypt it
2038 if( checkEncryptionBufferSize( nLen*2 ) )
2040 enableStringEncryption( nInObjectNumber );
2041 sal_uInt8 *pCopy = m_pEncryptionBuffer;
2042 sal_Int32 nChars = 2;
2043 *pCopy++ = 0xFE;
2044 *pCopy++ = 0xFF;
2045 // we need to prepare a byte stream from the unicode string buffer
2046 for( int i = 0; i < nLen; i++ )
2048 sal_Unicode aUnChar = pStr[i];
2049 *pCopy++ = (sal_uInt8)( aUnChar >> 8 );
2050 *pCopy++ = (sal_uInt8)( aUnChar & 255 );
2051 nChars += 2;
2053 //encrypt in place
2054 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars );
2055 //now append, hexadecimal (appendHex), the encrypted result
2056 for(int i = 0; i < nChars; i++)
2057 appendHex( m_pEncryptionBuffer[i], rOutBuffer );
2060 else
2061 appendUnicodeTextString( rInString, rOutBuffer );
2062 rOutBuffer.append( ">" );
2065 inline void PDFWriterImpl::appendLiteralStringEncrypt( OStringBuffer& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2067 rOutBuffer.append( "(" );
2068 sal_Int32 nChars = rInString.getLength();
2069 //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
2070 if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) )
2072 //encrypt the string in a buffer, then append it
2073 enableStringEncryption( nInObjectNumber );
2074 rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars );
2075 appendLiteralString( reinterpret_cast<sal_Char*>(m_pEncryptionBuffer), nChars, rOutBuffer );
2077 else
2078 appendLiteralString( rInString.getStr(), nChars , rOutBuffer );
2079 rOutBuffer.append( ")" );
2082 inline void PDFWriterImpl::appendLiteralStringEncrypt( const OString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2084 OStringBuffer aBufferString( rInString );
2085 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2088 void PDFWriterImpl::appendLiteralStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc )
2090 OString aBufferString( OUStringToOString( rInString, nEnc ) );
2091 sal_Int32 nLen = aBufferString.getLength();
2092 OStringBuffer aBuf( nLen );
2093 const sal_Char* pT = aBufferString.getStr();
2095 for( sal_Int32 i = 0; i < nLen; i++, pT++ )
2097 if( (*pT & 0x80) == 0 )
2098 aBuf.append( *pT );
2099 else
2101 aBuf.append( '<' );
2102 appendHex( *pT, aBuf );
2103 aBuf.append( '>' );
2106 aBufferString = aBuf.makeStringAndClear();
2107 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2110 /* end i12626 methods */
2112 void PDFWriterImpl::emitComment( const char* pComment )
2114 OStringBuffer aLine( 64 );
2115 aLine.append( "% " );
2116 aLine.append( (const sal_Char*)pComment );
2117 aLine.append( "\n" );
2118 writeBuffer( aLine.getStr(), aLine.getLength() );
2121 bool PDFWriterImpl::compressStream( SvMemoryStream* pStream )
2123 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2124 pStream->Seek( STREAM_SEEK_TO_END );
2125 sal_uLong nEndPos = pStream->Tell();
2126 pStream->Seek( STREAM_SEEK_TO_BEGIN );
2127 ZCodec pCodec( 0x4000, 0x4000 );
2128 SvMemoryStream aStream;
2129 pCodec.BeginCompression();
2130 pCodec.Write( aStream, static_cast<const sal_uInt8*>(pStream->GetData()), nEndPos );
2131 pCodec.EndCompression();
2132 nEndPos = aStream.Tell();
2133 pStream->Seek( STREAM_SEEK_TO_BEGIN );
2134 aStream.Seek( STREAM_SEEK_TO_BEGIN );
2135 pStream->SetStreamSize( nEndPos );
2136 pStream->Write( aStream.GetData(), nEndPos );
2137 return true;
2138 #else
2139 (void)pStream;
2140 return false;
2141 #endif
2144 void PDFWriterImpl::beginCompression()
2146 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2147 m_pCodec = new ZCodec( 0x4000, 0x4000 );
2148 m_pMemStream = new SvMemoryStream();
2149 m_pCodec->BeginCompression();
2150 #endif
2153 void PDFWriterImpl::endCompression()
2155 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2156 if( m_pCodec )
2158 m_pCodec->EndCompression();
2159 delete m_pCodec;
2160 m_pCodec = NULL;
2161 sal_uInt64 nLen = m_pMemStream->Tell();
2162 m_pMemStream->Seek( 0 );
2163 writeBuffer( m_pMemStream->GetData(), nLen );
2164 delete m_pMemStream;
2165 m_pMemStream = NULL;
2167 #endif
2170 bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes )
2172 if( ! m_bOpen ) // we are already down the drain
2173 return false;
2175 if( ! nBytes ) // huh ?
2176 return true;
2178 if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2180 m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END );
2181 m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) );
2182 return true;
2185 sal_uInt64 nWritten;
2186 if( m_pCodec )
2188 m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes );
2189 nWritten = nBytes;
2191 else
2193 bool buffOK = true;
2194 if( m_bEncryptThisStream )
2196 /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
2197 if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) )
2198 rtl_cipher_encodeARCFOUR( m_aCipher,
2199 pBuffer, static_cast<sal_Size>(nBytes),
2200 m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) );
2203 const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer : pBuffer;
2204 if( m_aDocDigest )
2205 rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) );
2207 if (m_aFile.write(pWriteBuffer, nBytes, nWritten) != osl::File::E_None)
2208 nWritten = 0;
2210 if( nWritten != nBytes )
2212 m_aFile.close();
2213 m_bOpen = false;
2217 return nWritten == nBytes;
2220 OutputDevice* PDFWriterImpl::getReferenceDevice()
2222 if( ! m_pReferenceDevice )
2224 VclPtrInstance<VirtualDevice> pVDev( 0 );
2226 m_pReferenceDevice = pVDev;
2228 if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 )
2229 pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 );
2230 else
2231 pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy );
2233 pVDev->SetOutputSizePixel( Size( 640, 480 ) );
2234 pVDev->SetMapMode( MAP_MM );
2236 m_pReferenceDevice->mpPDFWriter = this;
2237 m_pReferenceDevice->ImplUpdateFontData( true );
2239 return m_pReferenceDevice;
2242 class ImplPdfBuiltinFontData : public PhysicalFontFace
2244 private:
2245 const PDFWriterImpl::BuiltinFont& mrBuiltin;
2247 public:
2248 enum {PDF_FONT_MAGIC = 0xBDFF0A1C };
2249 ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& );
2250 const PDFWriterImpl::BuiltinFont& GetBuiltinFont() const { return mrBuiltin; }
2252 virtual PhysicalFontFace* Clone() const SAL_OVERRIDE { return new ImplPdfBuiltinFontData(*this); }
2253 virtual ImplFontEntry* CreateFontInstance( FontSelectPattern& ) const SAL_OVERRIDE;
2254 virtual sal_IntPtr GetFontId() const SAL_OVERRIDE { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); }
2257 inline const ImplPdfBuiltinFontData* GetPdfFontData( const PhysicalFontFace* pFontData )
2259 const ImplPdfBuiltinFontData* pFD = NULL;
2260 if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) )
2261 pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData );
2262 return pFD;
2265 static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin )
2267 ImplDevFontAttributes aDFA;
2268 aDFA.SetFamilyName( OUString::createFromAscii( rBuiltin.m_pName ) );
2269 aDFA.SetStyleName( OUString::createFromAscii( rBuiltin.m_pStyleName ) );
2270 aDFA.SetFamilyType( rBuiltin.m_eFamily );
2271 aDFA.SetSymbolFlag( rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 );
2272 aDFA.SetPitch( rBuiltin.m_ePitch );
2273 aDFA.SetWeight( rBuiltin.m_eWeight );
2274 aDFA.SetItalic( rBuiltin.m_eItalic );
2275 aDFA.SetWidthType( rBuiltin.m_eWidthType );
2277 aDFA.mbOrientation = true;
2278 aDFA.mbDevice = true;
2279 aDFA.mnQuality = 50000;
2280 aDFA.mbSubsettable = false;
2281 aDFA.mbEmbeddable = false;
2282 return aDFA;
2285 ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin )
2286 : PhysicalFontFace( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ),
2287 mrBuiltin( rBuiltin )
2290 ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( FontSelectPattern& rFSD ) const
2292 ImplFontEntry* pEntry = new ImplFontEntry( rFSD );
2293 return pEntry;
2296 // - PDFWriterImpl -
2298 sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
2300 endPage();
2301 m_nCurrentPage = m_aPages.size();
2302 m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) );
2303 m_aPages.back().m_nPageIndex = m_nCurrentPage;
2304 m_aPages.back().beginStream();
2306 // setup global graphics state
2307 // linewidth is "1 pixel" by default
2308 OStringBuffer aBuf( 16 );
2309 appendDouble( 72.0/double(getReferenceDevice()->GetDPIX()), aBuf );
2310 aBuf.append( " w\n" );
2311 writeBuffer( aBuf.getStr(), aBuf.getLength() );
2313 return m_nCurrentPage;
2316 void PDFWriterImpl::endPage()
2318 if( m_aPages.begin() != m_aPages.end() )
2320 // close eventual MC sequence
2321 endStructureElementMCSeq();
2323 // sanity check
2324 if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2326 OSL_FAIL( "redirection across pages !!!" );
2327 m_aOutputStreams.clear(); // leak !
2328 m_aMapMode.SetOrigin( Point() );
2331 m_aGraphicsStack.clear();
2332 m_aGraphicsStack.push_back( GraphicsState() );
2334 // this should pop the PDF graphics stack if necessary
2335 updateGraphicsState();
2337 m_aPages.back().endStream();
2339 // reset the default font
2340 Font aFont;
2341 aFont.SetName( OUString( "Times" ) );
2342 aFont.SetSize( Size( 0, 12 ) );
2344 m_aCurrentPDFState = m_aGraphicsStack.front();
2345 m_aGraphicsStack.front().m_aFont = aFont;
2347 for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin();
2348 it != m_aBitmaps.end(); ++it )
2350 if( ! it->m_aBitmap.IsEmpty() )
2352 writeBitmapObject( *it );
2353 it->m_aBitmap = BitmapEx();
2356 for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg )
2358 if( jpeg->m_pStream )
2360 writeJPG( *jpeg );
2361 delete jpeg->m_pStream;
2362 jpeg->m_pStream = NULL;
2363 jpeg->m_aMask = Bitmap();
2366 for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin();
2367 t != m_aTransparentObjects.end(); ++t )
2369 if( t->m_pContentStream )
2371 writeTransparentObject( *t );
2372 delete t->m_pContentStream;
2373 t->m_pContentStream = NULL;
2379 sal_Int32 PDFWriterImpl::createObject()
2381 m_aObjects.push_back( ~0U );
2382 return m_aObjects.size();
2385 bool PDFWriterImpl::updateObject( sal_Int32 n )
2387 if( ! m_bOpen )
2388 return false;
2390 sal_uInt64 nOffset = ~0U;
2391 osl::File::RC aError = m_aFile.getPos(nOffset);
2392 DBG_ASSERT( aError == osl::File::E_None, "could not register object" );
2393 if (aError != osl::File::E_None)
2395 m_aFile.close();
2396 m_bOpen = false;
2398 m_aObjects[ n-1 ] = nOffset;
2399 return aError == osl::File::E_None;
2402 #define CHECK_RETURN( x ) if( !(x) ) return 0
2404 sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject )
2406 if( nObject > 0 )
2408 OStringBuffer aLine( 1024 );
2410 aLine.append( nObject );
2411 aLine.append( " 0 obj\n"
2412 "<</Nums[\n" );
2413 sal_Int32 nTreeItems = m_aStructParentTree.size();
2414 for( sal_Int32 n = 0; n < nTreeItems; n++ )
2416 aLine.append( n );
2417 aLine.append( ' ' );
2418 aLine.append( m_aStructParentTree[n] );
2419 aLine.append( "\n" );
2421 aLine.append( "]>>\nendobj\n\n" );
2422 CHECK_RETURN( updateObject( nObject ) );
2423 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2425 return nObject;
2428 const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr )
2430 static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings;
2431 // fill maps once
2432 if( aAttributeStrings.empty() )
2434 aAttributeStrings[ PDFWriter::Placement ] = "Placement";
2435 aAttributeStrings[ PDFWriter::WritingMode ] = "WritingMode";
2436 aAttributeStrings[ PDFWriter::SpaceBefore ] = "SpaceBefore";
2437 aAttributeStrings[ PDFWriter::SpaceAfter ] = "SpaceAfter";
2438 aAttributeStrings[ PDFWriter::StartIndent ] = "StartIndent";
2439 aAttributeStrings[ PDFWriter::EndIndent ] = "EndIndent";
2440 aAttributeStrings[ PDFWriter::TextIndent ] = "TextIndent";
2441 aAttributeStrings[ PDFWriter::TextAlign ] = "TextAlign";
2442 aAttributeStrings[ PDFWriter::Width ] = "Width";
2443 aAttributeStrings[ PDFWriter::Height ] = "Height";
2444 aAttributeStrings[ PDFWriter::BlockAlign ] = "BlockAlign";
2445 aAttributeStrings[ PDFWriter::InlineAlign ] = "InlineAlign";
2446 aAttributeStrings[ PDFWriter::LineHeight ] = "LineHeight";
2447 aAttributeStrings[ PDFWriter::BaselineShift ] = "BaselineShift";
2448 aAttributeStrings[ PDFWriter::TextDecorationType ] = "TextDecorationType";
2449 aAttributeStrings[ PDFWriter::ListNumbering ] = "ListNumbering";
2450 aAttributeStrings[ PDFWriter::RowSpan ] = "RowSpan";
2451 aAttributeStrings[ PDFWriter::ColSpan ] = "ColSpan";
2452 aAttributeStrings[ PDFWriter::LinkAnnotation ] = "LinkAnnotation";
2455 std::map< PDFWriter::StructAttribute, const char* >::const_iterator it =
2456 aAttributeStrings.find( eAttr );
2458 #if OSL_DEBUG_LEVEL > 1
2459 if( it == aAttributeStrings.end() )
2460 fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr );
2461 #endif
2463 return it != aAttributeStrings.end() ? it->second : "";
2466 const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal )
2468 static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings;
2470 if( aValueStrings.empty() )
2472 aValueStrings[ PDFWriter::NONE ] = "None";
2473 aValueStrings[ PDFWriter::Block ] = "Block";
2474 aValueStrings[ PDFWriter::Inline ] = "Inline";
2475 aValueStrings[ PDFWriter::Before ] = "Before";
2476 aValueStrings[ PDFWriter::After ] = "After";
2477 aValueStrings[ PDFWriter::Start ] = "Start";
2478 aValueStrings[ PDFWriter::End ] = "End";
2479 aValueStrings[ PDFWriter::LrTb ] = "LrTb";
2480 aValueStrings[ PDFWriter::RlTb ] = "RlTb";
2481 aValueStrings[ PDFWriter::TbRl ] = "TbRl";
2482 aValueStrings[ PDFWriter::Center ] = "Center";
2483 aValueStrings[ PDFWriter::Justify ] = "Justify";
2484 aValueStrings[ PDFWriter::Auto ] = "Auto";
2485 aValueStrings[ PDFWriter::Middle ] = "Middle";
2486 aValueStrings[ PDFWriter::Normal ] = "Normal";
2487 aValueStrings[ PDFWriter::Underline ] = "Underline";
2488 aValueStrings[ PDFWriter::Overline ] = "Overline";
2489 aValueStrings[ PDFWriter::LineThrough ] = "LineThrough";
2490 aValueStrings[ PDFWriter::Disc ] = "Disc";
2491 aValueStrings[ PDFWriter::Circle ] = "Circle";
2492 aValueStrings[ PDFWriter::Square ] = "Square";
2493 aValueStrings[ PDFWriter::Decimal ] = "Decimal";
2494 aValueStrings[ PDFWriter::UpperRoman ] = "UpperRoman";
2495 aValueStrings[ PDFWriter::LowerRoman ] = "LowerRoman";
2496 aValueStrings[ PDFWriter::UpperAlpha ] = "UpperAlpha";
2497 aValueStrings[ PDFWriter::LowerAlpha ] = "LowerAlpha";
2500 std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it =
2501 aValueStrings.find( eVal );
2503 #if OSL_DEBUG_LEVEL > 1
2504 if( it == aValueStrings.end() )
2505 fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal );
2506 #endif
2508 return it != aValueStrings.end() ? it->second : "";
2511 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt )
2513 o_rLine.append( "/" );
2514 o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) );
2516 if( i_rVal.eValue != PDFWriter::Invalid )
2518 o_rLine.append( "/" );
2519 o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) );
2521 else
2523 // numerical value
2524 o_rLine.append( " " );
2525 if( i_bIsFixedInt )
2526 appendFixedInt( i_rVal.nValue, o_rLine );
2527 else
2528 o_rLine.append( i_rVal.nValue );
2530 o_rLine.append( "\n" );
2533 OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
2535 // create layout, list and table attribute sets
2536 OStringBuffer aLayout(256), aList(64), aTable(64);
2537 for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin();
2538 it != i_rEle.m_aAttributes.end(); ++it )
2540 if( it->first == PDFWriter::ListNumbering )
2541 appendStructureAttributeLine( it->first, it->second, aList, true );
2542 else if( it->first == PDFWriter::RowSpan ||
2543 it->first == PDFWriter::ColSpan )
2544 appendStructureAttributeLine( it->first, it->second, aTable, false );
2545 else if( it->first == PDFWriter::LinkAnnotation )
2547 sal_Int32 nLink = it->second.nValue;
2548 std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
2549 m_aLinkPropertyMap.find( nLink );
2550 if( link_it != m_aLinkPropertyMap.end() )
2551 nLink = link_it->second;
2552 if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() )
2554 // update struct parent of link
2555 OStringBuffer aStructParentEntry( 32 );
2556 aStructParentEntry.append( i_rEle.m_nObject );
2557 aStructParentEntry.append( " 0 R" );
2558 m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() );
2559 m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1;
2561 sal_Int32 nRefObject = createObject();
2562 OStringBuffer aRef( 256 );
2563 aRef.append( nRefObject );
2564 aRef.append( " 0 obj\n"
2565 "<</Type/OBJR/Obj " );
2566 aRef.append( m_aLinks[ nLink ].m_nObject );
2567 aRef.append( " 0 R>>\n"
2568 "endobj\n\n"
2570 if (updateObject(nRefObject))
2572 writeBuffer( aRef.getStr(), aRef.getLength() );
2575 i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) );
2577 else
2579 OSL_FAIL( "unresolved link id for Link structure" );
2580 #if OSL_DEBUG_LEVEL > 1
2581 fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink );
2583 OStringBuffer aLine( "unresolved link id " );
2584 aLine.append( nLink );
2585 aLine.append( " for Link structure" );
2586 emitComment( aLine.getStr() );
2588 #endif
2591 else
2592 appendStructureAttributeLine( it->first, it->second, aLayout, true );
2594 if( ! i_rEle.m_aBBox.IsEmpty() )
2596 aLayout.append( "/BBox[" );
2597 appendFixedInt( i_rEle.m_aBBox.Left(), aLayout );
2598 aLayout.append( " " );
2599 appendFixedInt( i_rEle.m_aBBox.Top(), aLayout );
2600 aLayout.append( " " );
2601 appendFixedInt( i_rEle.m_aBBox.Right(), aLayout );
2602 aLayout.append( " " );
2603 appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout );
2604 aLayout.append( "]\n" );
2607 std::vector< sal_Int32 > aAttribObjects;
2608 if( !aLayout.isEmpty() )
2610 aAttribObjects.push_back( createObject() );
2611 if (updateObject( aAttribObjects.back() ))
2613 OStringBuffer aObj( 64 );
2614 aObj.append( aAttribObjects.back() );
2615 aObj.append( " 0 obj\n"
2616 "<</O/Layout\n" );
2617 aLayout.append( ">>\nendobj\n\n" );
2618 writeBuffer( aObj.getStr(), aObj.getLength() );
2619 writeBuffer( aLayout.getStr(), aLayout.getLength() );
2622 if( !aList.isEmpty() )
2624 aAttribObjects.push_back( createObject() );
2625 if (updateObject( aAttribObjects.back() ))
2627 OStringBuffer aObj( 64 );
2628 aObj.append( aAttribObjects.back() );
2629 aObj.append( " 0 obj\n"
2630 "<</O/List\n" );
2631 aList.append( ">>\nendobj\n\n" );
2632 writeBuffer( aObj.getStr(), aObj.getLength() );
2633 writeBuffer( aList.getStr(), aList.getLength() );
2636 if( !aTable.isEmpty() )
2638 aAttribObjects.push_back( createObject() );
2639 if (updateObject( aAttribObjects.back() ))
2641 OStringBuffer aObj( 64 );
2642 aObj.append( aAttribObjects.back() );
2643 aObj.append( " 0 obj\n"
2644 "<</O/Table\n" );
2645 aTable.append( ">>\nendobj\n\n" );
2646 writeBuffer( aObj.getStr(), aObj.getLength() );
2647 writeBuffer( aTable.getStr(), aTable.getLength() );
2651 OStringBuffer aRet( 64 );
2652 if( aAttribObjects.size() > 1 )
2653 aRet.append( " [" );
2654 for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin();
2655 at_it != aAttribObjects.end(); ++at_it )
2657 aRet.append( " " );
2658 aRet.append( *at_it );
2659 aRet.append( " 0 R" );
2661 if( aAttribObjects.size() > 1 )
2662 aRet.append( " ]" );
2663 return aRet.makeStringAndClear();
2666 sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
2669 // do not emit NonStruct and its children
2670 rEle.m_eType == PDFWriter::NonStructElement &&
2671 rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root
2673 return 0;
2675 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
2677 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
2679 PDFStructureElement& rChild = m_aStructure[ *it ];
2680 if( rChild.m_eType != PDFWriter::NonStructElement )
2682 if( rChild.m_nParentElement == rEle.m_nOwnElement )
2683 emitStructure( rChild );
2684 else
2686 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure element" );
2687 #if OSL_DEBUG_LEVEL > 1
2688 fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
2689 #endif
2693 else
2695 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
2696 #if OSL_DEBUG_LEVEL > 1
2697 fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
2698 #endif
2702 OStringBuffer aLine( 512 );
2703 aLine.append( rEle.m_nObject );
2704 aLine.append( " 0 obj\n"
2705 "<</Type" );
2706 sal_Int32 nParentTree = -1;
2707 if( rEle.m_nOwnElement == rEle.m_nParentElement )
2709 nParentTree = createObject();
2710 CHECK_RETURN( nParentTree );
2711 aLine.append( "/StructTreeRoot\n" );
2712 aLine.append( "/ParentTree " );
2713 aLine.append( nParentTree );
2714 aLine.append( " 0 R\n" );
2715 if( ! m_aRoleMap.empty() )
2717 aLine.append( "/RoleMap<<" );
2718 for( std::unordered_map<OString,OString,OStringHash>::const_iterator
2719 it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it )
2721 aLine.append( '/' );
2722 aLine.append(it->first);
2723 aLine.append( '/' );
2724 aLine.append( it->second );
2725 aLine.append( '\n' );
2727 aLine.append( ">>\n" );
2730 else
2732 aLine.append( "/StructElem\n"
2733 "/S/" );
2734 if( !rEle.m_aAlias.isEmpty() )
2735 aLine.append( rEle.m_aAlias );
2736 else
2737 aLine.append( getStructureTag( rEle.m_eType ) );
2738 aLine.append( "\n"
2739 "/P " );
2740 aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject );
2741 aLine.append( " 0 R\n"
2742 "/Pg " );
2743 aLine.append( rEle.m_nFirstPageObject );
2744 aLine.append( " 0 R\n" );
2745 if( !rEle.m_aActualText.isEmpty() )
2747 aLine.append( "/ActualText" );
2748 appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine );
2749 aLine.append( "\n" );
2751 if( !rEle.m_aAltText.isEmpty() )
2753 aLine.append( "/Alt" );
2754 appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine );
2755 aLine.append( "\n" );
2758 if( (! rEle.m_aBBox.IsEmpty()) || (! rEle.m_aAttributes.empty()) )
2760 OString aAttribs = emitStructureAttributes( rEle );
2761 if( !aAttribs.isEmpty() )
2763 aLine.append( "/A" );
2764 aLine.append( aAttribs );
2765 aLine.append( "\n" );
2768 if( !rEle.m_aLocale.Language.isEmpty() )
2770 /* PDF allows only RFC 3066, which is only partly BCP 47 and does not
2771 * include script tags and others.
2772 * http://pdf.editme.com/pdfua-naturalLanguageSpecification
2773 * http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf#page=886
2774 * https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf#M13.9.19332.1Heading.97.Natural.Language.Specification
2775 * */
2776 LanguageTag aLanguageTag( rEle.m_aLocale);
2777 OUString aLanguage, aScript, aCountry;
2778 aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
2779 if (!aLanguage.isEmpty())
2781 OUStringBuffer aLocBuf( 16 );
2782 aLocBuf.append( aLanguage );
2783 if( !aCountry.isEmpty() )
2785 aLocBuf.append( '-' );
2786 aLocBuf.append( aCountry );
2788 aLine.append( "/Lang" );
2789 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine );
2790 aLine.append( "\n" );
2793 if( ! rEle.m_aKids.empty() )
2795 unsigned int i = 0;
2796 aLine.append( "/K[" );
2797 for( std::list< PDFStructureElementKid >::const_iterator it =
2798 rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ )
2800 if( it->nMCID == -1 )
2802 aLine.append( it->nObject );
2803 aLine.append( " 0 R" );
2804 aLine.append( ( (i & 15) == 15 ) ? "\n" : " " );
2806 else
2808 if( it->nObject == rEle.m_nFirstPageObject )
2810 aLine.append( it->nMCID );
2811 aLine.append( " " );
2813 else
2815 aLine.append( "<</Type/MCR/Pg " );
2816 aLine.append( it->nObject );
2817 aLine.append( " 0 R /MCID " );
2818 aLine.append( it->nMCID );
2819 aLine.append( ">>\n" );
2823 aLine.append( "]\n" );
2825 aLine.append( ">>\nendobj\n\n" );
2827 CHECK_RETURN( updateObject( rEle.m_nObject ) );
2828 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2830 CHECK_RETURN( emitStructParentTree( nParentTree ) );
2832 return rEle.m_nObject;
2835 bool PDFWriterImpl::emitGradients()
2837 for( std::list<GradientEmit>::iterator it = m_aGradients.begin();
2838 it != m_aGradients.end(); ++it )
2840 if ( !writeGradientFunction( *it ) ) return false;
2842 return true;
2845 bool PDFWriterImpl::emitTilings()
2847 OStringBuffer aTilingObj( 1024 );
2849 for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it )
2851 DBG_ASSERT( it->m_pTilingStream, "tiling without stream" );
2852 if( ! it->m_pTilingStream )
2853 continue;
2855 aTilingObj.setLength( 0 );
2857 #if OSL_DEBUG_LEVEL > 1
2858 emitComment( "PDFWriterImpl::emitTilings" );
2859 #endif
2861 sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left();
2862 sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top();
2863 sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth();
2864 sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight();
2865 if( it->m_aCellSize.Width() == 0 )
2866 it->m_aCellSize.Width() = nW;
2867 if( it->m_aCellSize.Height() == 0 )
2868 it->m_aCellSize.Height() = nH;
2870 bool bDeflate = compressStream( it->m_pTilingStream );
2871 it->m_pTilingStream->Seek( STREAM_SEEK_TO_END );
2872 sal_Size nTilingStreamSize = it->m_pTilingStream->Tell();
2873 it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN );
2875 // write pattern object
2876 aTilingObj.append( it->m_nObject );
2877 aTilingObj.append( " 0 obj\n" );
2878 aTilingObj.append( "<</Type/Pattern/PatternType 1\n"
2879 "/PaintType 1\n"
2880 "/TilingType 2\n"
2881 "/BBox[" );
2882 appendFixedInt( nX, aTilingObj );
2883 aTilingObj.append( ' ' );
2884 appendFixedInt( nY, aTilingObj );
2885 aTilingObj.append( ' ' );
2886 appendFixedInt( nX+nW, aTilingObj );
2887 aTilingObj.append( ' ' );
2888 appendFixedInt( nY+nH, aTilingObj );
2889 aTilingObj.append( "]\n"
2890 "/XStep " );
2891 appendFixedInt( it->m_aCellSize.Width(), aTilingObj );
2892 aTilingObj.append( "\n"
2893 "/YStep " );
2894 appendFixedInt( it->m_aCellSize.Height(), aTilingObj );
2895 aTilingObj.append( "\n" );
2896 if( it->m_aTransform.matrix[0] != 1.0 ||
2897 it->m_aTransform.matrix[1] != 0.0 ||
2898 it->m_aTransform.matrix[3] != 0.0 ||
2899 it->m_aTransform.matrix[4] != 1.0 ||
2900 it->m_aTransform.matrix[2] != 0.0 ||
2901 it->m_aTransform.matrix[5] != 0.0 )
2903 aTilingObj.append( "/Matrix [" );
2904 // TODO: scaling, mirroring on y, etc
2905 appendDouble( it->m_aTransform.matrix[0], aTilingObj );
2906 aTilingObj.append( ' ' );
2907 appendDouble( it->m_aTransform.matrix[1], aTilingObj );
2908 aTilingObj.append( ' ' );
2909 appendDouble( it->m_aTransform.matrix[3], aTilingObj );
2910 aTilingObj.append( ' ' );
2911 appendDouble( it->m_aTransform.matrix[4], aTilingObj );
2912 aTilingObj.append( ' ' );
2913 appendDouble( it->m_aTransform.matrix[2], aTilingObj );
2914 aTilingObj.append( ' ' );
2915 appendDouble( it->m_aTransform.matrix[5], aTilingObj );
2916 aTilingObj.append( "]\n" );
2918 aTilingObj.append( "/Resources" );
2919 it->m_aResources.append( aTilingObj, getFontDictObject() );
2920 if( bDeflate )
2921 aTilingObj.append( "/Filter/FlateDecode" );
2922 aTilingObj.append( "/Length " );
2923 aTilingObj.append( (sal_Int32)nTilingStreamSize );
2924 aTilingObj.append( ">>\nstream\n" );
2925 if ( !updateObject( it->m_nObject ) ) return false;
2926 if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false;
2927 checkAndEnableStreamEncryption( it->m_nObject );
2928 bool written = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize );
2929 delete it->m_pTilingStream;
2930 it->m_pTilingStream = NULL;
2931 if( !written )
2932 return false;
2933 disableStreamEncryption();
2934 aTilingObj.setLength( 0 );
2935 aTilingObj.append( "\nendstream\nendobj\n\n" );
2936 if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false;
2938 return true;
2941 sal_Int32 PDFWriterImpl::emitBuiltinFont( const PhysicalFontFace* pFont, sal_Int32 nFontObject )
2943 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
2944 if( !pFD )
2945 return 0;
2946 const BuiltinFont& rBuiltinFont = pFD->GetBuiltinFont();
2948 OStringBuffer aLine( 1024 );
2950 if( nFontObject <= 0 )
2951 nFontObject = createObject();
2952 CHECK_RETURN( updateObject( nFontObject ) );
2953 aLine.append( nFontObject );
2954 aLine.append( " 0 obj\n"
2955 "<</Type/Font/Subtype/Type1/BaseFont/" );
2956 appendName( rBuiltinFont.m_pPSName, aLine );
2957 aLine.append( "\n" );
2958 if( rBuiltinFont.m_eCharSet == RTL_TEXTENCODING_MS_1252 )
2959 aLine.append( "/Encoding/WinAnsiEncoding\n" );
2960 aLine.append( ">>\nendobj\n\n" );
2961 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2962 return nFontObject;
2965 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const PhysicalFontFace* pFont, EmbedFont& rEmbed )
2967 std::map< sal_Int32, sal_Int32 > aRet;
2969 sal_Int32 nFontDescriptor = 0;
2970 OString aSubType( "/Type1" );
2971 FontSubsetInfo aInfo;
2972 // fill in dummy values
2973 aInfo.m_nAscent = 1000;
2974 aInfo.m_nDescent = 200;
2975 aInfo.m_nCapHeight = 1000;
2976 aInfo.m_aFontBBox = Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) );
2977 aInfo.m_aPSName = pFont->GetFamilyName();
2978 sal_Int32 pWidths[256];
2979 memset( pWidths, 0, sizeof(pWidths) );
2981 SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics();
2983 assert(pGraphics);
2985 if( pFont->IsEmbeddable() )
2987 const unsigned char* pFontData = NULL;
2988 long nFontLen = 0;
2989 sal_Ucs nEncodedCodes[256];
2990 sal_Int32 pEncWidths[256];
2992 //TODO: surely this is utterly broken because GetEmbedFontData loops over the uninitialized nEncodedCodes as input
2993 pFontData = static_cast<const unsigned char*>(pGraphics->GetEmbedFontData( pFont, nEncodedCodes, pEncWidths, 256, aInfo, &nFontLen ));
2995 if( pFontData )
2997 pGraphics->FreeEmbedFontData( pFontData, nFontLen );
2998 for( int i = 0; i < 256; i++ )
3000 if( nEncodedCodes[i] >= 32 && nEncodedCodes[i] < 256 )
3002 pWidths[i] = pEncWidths[ i ];
3007 else if( pFont->mbSubsettable )
3009 aSubType = OString( "/TrueType" );
3010 Int32Vector aGlyphWidths;
3011 Ucs2UIntMap aUnicodeMap;
3012 pGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap );
3014 OUString aTmpName;
3015 osl_createTempFile( NULL, NULL, &aTmpName.pData );
3016 sal_GlyphId aGlyphIds[ 256 ];
3017 sal_uInt8 pEncoding[ 256 ];
3018 sal_Int32 pDuWidths[ 256 ];
3020 memset( aGlyphIds, 0, sizeof( aGlyphIds ) );
3021 memset( pEncoding, 0, sizeof( pEncoding ) );
3022 memset( pDuWidths, 0, sizeof( pDuWidths ) );
3024 for( sal_Ucs c = 32; c < 256; c++ )
3026 pEncoding[c] = c;
3027 aGlyphIds[c] = 0;
3028 if( aUnicodeMap.find( c ) != aUnicodeMap.end() )
3029 pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ];
3031 //TODO: surely this is utterly broken because aGlyphIds is just all zeros, if we
3032 //had the right glyphids here then I imagine we could replace pDuWidths with
3033 //pWidths and remove pWidths assignment above. i.e. start with the glyph ids
3034 //and map those to unicode rather than try and reverse map them ?
3035 pGraphics->CreateFontSubset( aTmpName, pFont, aGlyphIds, pEncoding, pDuWidths, 256, aInfo );
3036 osl_removeFile( aTmpName.pData );
3038 else
3040 OSL_FAIL( "system font neither embeddable nor subsettable" );
3043 // write font descriptor
3044 nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 );
3045 if( nFontDescriptor )
3047 // write font object
3048 sal_Int32 nObject = createObject();
3049 if( updateObject( nObject ) )
3051 OStringBuffer aLine( 1024 );
3052 aLine.append( nObject );
3053 aLine.append( " 0 obj\n"
3054 "<</Type/Font/Subtype" );
3055 aLine.append( aSubType );
3056 aLine.append( "/BaseFont/" );
3057 appendName( aInfo.m_aPSName, aLine );
3058 aLine.append( "\n" );
3059 if( !pFont->IsSymbolFont() )
3060 aLine.append( "/Encoding/WinAnsiEncoding\n" );
3061 aLine.append( "/FirstChar 32 /LastChar 255\n"
3062 "/Widths[" );
3063 for( int i = 32; i < 256; i++ )
3065 aLine.append( pWidths[i] );
3066 aLine.append( ((i&15) == 15) ? "\n" : " " );
3068 aLine.append( "]\n"
3069 "/FontDescriptor " );
3070 aLine.append( nFontDescriptor );
3071 aLine.append( " 0 R>>\n"
3072 "endobj\n\n" );
3073 writeBuffer( aLine.getStr(), aLine.getLength() );
3075 aRet[ rEmbed.m_nNormalFontID ] = nObject;
3079 return aRet;
3082 typedef int ThreeInts[3];
3083 static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen,
3084 ThreeInts& rSegmentLengths )
3086 if( !pFontBytes || (nByteLen < 0) )
3087 return false;
3088 const unsigned char* pPtr = pFontBytes;
3089 const unsigned char* pEnd = pFontBytes + nByteLen;
3091 for( int i = 0; i < 3; ++i) {
3092 // read segment1 header
3093 if( pPtr+6 >= pEnd )
3094 return false;
3095 if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) )
3096 return false;
3097 const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2];
3098 if( nLen <= 0)
3099 return false;
3100 rSegmentLengths[i] = nLen;
3101 pPtr += nLen + 6;
3104 // read segment-end header
3105 if( pPtr+2 >= pEnd )
3106 return false;
3107 if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) )
3108 return false;
3110 return true;
3113 struct FontException : public std::exception
3117 // TODO: always subset instead of embedding the full font => this method becomes obsolete then
3118 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const PhysicalFontFace* pFont, EmbedFont& rEmbed )
3120 std::map< sal_Int32, sal_Int32 > aRet;
3122 sal_Int32 nStreamObject = 0;
3123 sal_Int32 nFontDescriptor = 0;
3125 SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics();
3127 assert(pGraphics);
3129 // prepare font encoding
3130 std::set<sal_Unicode> const * pPriority(0);
3131 const Ucs2SIntMap *const pEncoding =
3132 pGraphics->GetFontEncodingVector( pFont, nullptr, &pPriority );
3133 sal_Int32 nToUnicodeStream = 0;
3134 sal_uInt8 nEncoding[256];
3135 sal_Ucs nEncodedCodes[256];
3136 std::vector<sal_Ucs> aUnicodes;
3137 aUnicodes.reserve( 256 );
3138 sal_Int32 pUnicodesPerGlyph[256];
3139 sal_Int32 pEncToUnicodeIndex[256];
3140 if( pEncoding )
3142 memset( nEncoding, 0, sizeof(nEncoding) );
3143 memset( nEncodedCodes, 0, sizeof(nEncodedCodes) );
3144 memset( pUnicodesPerGlyph, 0, sizeof(pUnicodesPerGlyph) );
3145 memset( pEncToUnicodeIndex, 0, sizeof(pEncToUnicodeIndex) );
3146 for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it )
3148 if(it->second == -1)
3149 continue;
3150 sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff);
3151 SAL_WARN_IF(nCode != it->second, "vcl.gdi", "emitEmbeddedFont: FIXME: cannot handle Type 1 font with code points > 256");
3152 //We're not doing this right here. We have taken a unicode-to-font_index map
3153 //and are trying to generate a font_index-to-unicode mapping from it
3154 //Which assumes that there is a 1-to-1 mapping there, but that might not be
3155 //true.
3157 //Instead perhaps we could try and get the GetFontCharMap and loop
3158 //over sal_UCS4 GetCharFromIndex( int nCharIndex ) const from 0 to 255
3159 //to build it up
3160 if (nEncoding[nCode] != 0)
3162 // should not have 2 identical mappings
3163 assert(nEncodedCodes[nCode] != it->first);
3164 if (pPriority)
3166 bool bExist = pPriority->find(nEncodedCodes[nCode]) != pPriority->end();
3167 bool bIter = pPriority->find(it->first) != pPriority->end();
3168 SAL_WARN_IF(bExist && bIter, "vcl.gdi", "both are preferred? odd...");
3169 if (bExist)
3171 continue;
3173 // note: aUnicodes will contain the old one but that
3174 // does not matter because there's nothing iterating it
3176 else
3178 // is this fallback important? let's prefer lower one.
3179 // actually the map is sorted so just rely on that
3180 assert(nEncodedCodes[nCode] < it->first);
3181 SAL_WARN("vcl.gdi", "emitEmbeddedFont: ignoring code " << nCode << " mapping to " << it->first << " in favor of " << nEncodedCodes[nCode]);
3182 continue;
3185 nEncodedCodes[ nCode ] = it->first;
3186 nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode );
3187 pEncToUnicodeIndex[ nCode ] = static_cast<sal_Int32>(aUnicodes.size());
3188 aUnicodes.push_back( it->first );
3189 pUnicodesPerGlyph[ nCode ] = 1;
3193 FontSubsetInfo aInfo;
3194 sal_Int32 pWidths[256];
3195 const unsigned char* pFontData = NULL;
3196 long nFontLen = 0;
3197 sal_Int32 nLength1, nLength2;
3200 if( (pFontData = static_cast<const unsigned char*>(pGraphics->GetEmbedFontData(pFont, nEncodedCodes, pWidths, 256, aInfo, &nFontLen))) != NULL )
3202 if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 )
3203 throw FontException();
3204 // see whether it is pfb or pfa; if it is a pfb, fill ranges
3205 // of 6 bytes that are not part of the font program
3206 std::list< int > aSections;
3207 std::list< int >::const_iterator it;
3208 int nIndex = 0;
3209 while( (nIndex < nFontLen-1) && pFontData[nIndex] == 0x80 )
3211 aSections.push_back( nIndex );
3212 if( pFontData[nIndex+1] == 0x03 )
3213 break;
3214 sal_Int32 nBytes =
3215 ((sal_Int32)pFontData[nIndex+2]) |
3216 ((sal_Int32)pFontData[nIndex+3]) << 8 |
3217 ((sal_Int32)pFontData[nIndex+4]) << 16 |
3218 ((sal_Int32)pFontData[nIndex+5]) << 24;
3219 nIndex += nBytes+6;
3222 // search for eexec
3223 // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below
3224 nIndex = 0;
3225 int nEndAsciiIndex;
3226 int nBeginBinaryIndex;
3227 int nEndBinaryIndex;
3230 while( nIndex < nFontLen-4 &&
3231 ( pFontData[nIndex] != 'e' ||
3232 pFontData[nIndex+1] != 'e' ||
3233 pFontData[nIndex+2] != 'x' ||
3234 pFontData[nIndex+3] != 'e' ||
3235 pFontData[nIndex+4] != 'c'
3239 ++nIndex;
3241 // check whether we are in a excluded section
3242 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3244 } while( it != aSections.end() && nIndex < nFontLen-4 );
3245 // this should end the ascii part
3246 if( nIndex > nFontLen-5 )
3247 throw FontException();
3249 nEndAsciiIndex = nIndex+4;
3250 // now count backwards until we can account for 512 '0'
3251 // which is the endmarker of the (hopefully) binary data
3252 // do not count the pfb header sections
3253 int nFound = 0;
3254 nIndex = nFontLen-1;
3255 while( nIndex > 0 && nFound < 512 )
3257 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3259 if( it == aSections.end() )
3261 // inside the 512 '0' block there may only be whitespace
3262 // according to T1 spec; probably it would be to simple
3263 // if all fonts complied
3264 if( pFontData[nIndex] == '0' )
3265 nFound++;
3266 else if( nFound > 0 &&
3267 pFontData[nIndex] != '\r' &&
3268 pFontData[nIndex] != '\t' &&
3269 pFontData[nIndex] != '\n' &&
3270 pFontData[nIndex] != ' ' )
3271 break;
3273 nIndex--;
3276 if( nIndex < 1 || nIndex <= nEndAsciiIndex )
3277 throw FontException();
3279 // nLength3 is the rest of the file - excluding any section headers
3280 // nIndex now points before the first of the 512 '0' characters marking the
3281 // fixed content portion
3282 sal_Int32 nLength3 = nFontLen - nIndex - 1;
3283 for( it = aSections.begin(); it != aSections.end(); ++it )
3285 // special case: nIndex inside a section marker
3286 if( nIndex >= (*it) && (*it)+6 > nIndex )
3287 nLength3 -= (*it)+6 - nIndex;
3288 else if( *it >= nIndex )
3290 if( *it < nFontLen - 6 )
3291 nLength3 -= 6;
3292 else // the last section 0x8003 is only 2 bytes after all
3293 nLength3 -= (nFontLen - *it);
3297 // there may be whitespace to ignore before the 512 '0'
3298 while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' )
3300 nIndex--;
3301 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3303 if( it != aSections.end() )
3305 nIndex = (*it)-1;
3306 break; // this is surely a binary boundary, in ascii case it wouldn't matter
3309 nEndBinaryIndex = nIndex;
3311 // search for beginning of binary section
3312 nBeginBinaryIndex = nEndAsciiIndex;
3315 nBeginBinaryIndex++;
3316 for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it )
3318 } while( nBeginBinaryIndex < nEndBinaryIndex &&
3319 ( pFontData[nBeginBinaryIndex] == '\r' ||
3320 pFontData[nBeginBinaryIndex] == '\n' ||
3321 it != aSections.end() ) );
3323 // it seems to be vital to copy the exact whitespace between binary data
3324 // and eexec, else a invalid font results. so make nEndAsciiIndex
3325 // always immediate in front of nBeginBinaryIndex
3326 nEndAsciiIndex = nBeginBinaryIndex-1;
3327 for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it )
3329 if( it != aSections.end() )
3330 nEndAsciiIndex = (*it)-1;
3332 nLength1 = nEndAsciiIndex+1; // including the last character
3333 for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it )
3334 nLength1 -= 6; // decrease by pfb section size
3336 // if the first four bytes are all ascii hex characters, then binary data
3337 // has to be converted to real binary data
3338 for( nIndex = 0; nIndex < 4 &&
3339 ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) ||
3340 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) ||
3341 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' )
3342 ); ++nIndex )
3344 bool bConvertHexData = true;
3345 if( nIndex < 4 )
3347 bConvertHexData = false;
3348 nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte
3349 for( it = aSections.begin(); it != aSections.end(); ++it )
3350 if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex )
3351 nLength2 -= 6;
3353 else
3355 // count the hex ascii characters to get nLength2
3356 nLength2 = 0;
3357 int nNextSectionIndex = 0;
3358 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3360 if( it != aSections.end() )
3361 nNextSectionIndex = *it;
3362 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3364 if( nIndex == nNextSectionIndex )
3366 nIndex += 6;
3367 ++it;
3368 nNextSectionIndex = (it == aSections.end() ? 0 : *it );
3370 if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) ||
3371 ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) ||
3372 ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) )
3373 nLength2++;
3375 DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" );
3376 nLength2 /= 2;
3379 // now we can actually write the font stream !
3380 #if OSL_DEBUG_LEVEL > 1
3381 emitComment( " PDFWriterImpl::emitEmbeddedFont" );
3382 #endif
3383 OStringBuffer aLine( 512 );
3384 nStreamObject = createObject();
3385 if( !updateObject(nStreamObject))
3386 throw FontException();
3387 sal_Int32 nStreamLengthObject = createObject();
3388 aLine.append( nStreamObject );
3389 aLine.append( " 0 obj\n"
3390 "<</Length " );
3391 aLine.append( nStreamLengthObject );
3392 aLine.append( " 0 R"
3393 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3394 "/Filter/FlateDecode"
3395 #endif
3396 "/Length1 " );
3397 aLine.append( nLength1 );
3398 aLine.append( " /Length2 " );
3399 aLine.append( nLength2 );
3400 aLine.append( " /Length3 ");
3401 aLine.append( nLength3 );
3402 aLine.append( ">>\n"
3403 "stream\n" );
3404 if( !writeBuffer( aLine.getStr(), aLine.getLength() ) )
3405 throw FontException();
3407 sal_uInt64 nBeginStreamPos = 0;
3408 m_aFile.getPos(nBeginStreamPos);
3410 beginCompression();
3411 checkAndEnableStreamEncryption( nStreamObject );
3413 // write ascii section
3414 if( aSections.begin() == aSections.end() )
3416 if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) )
3417 throw FontException();
3419 else
3421 // first section always starts at 0
3422 it = aSections.begin();
3423 nIndex = (*it)+6;
3424 ++it;
3425 while( *it < nEndAsciiIndex )
3427 if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) )
3428 throw FontException();
3429 nIndex = (*it)+6;
3430 ++it;
3432 // write partial last section
3433 if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) )
3434 throw FontException();
3437 // write binary section
3438 if( ! bConvertHexData )
3440 if( aSections.begin() == aSections.end() )
3442 if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) )
3443 throw FontException();
3445 else
3447 for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it )
3449 // write first partial section
3450 if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) )
3451 throw FontException();
3452 // write following sections
3453 while( it != aSections.end() )
3455 nIndex = (*it)+6;
3456 ++it;
3457 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3459 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3460 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3461 throw FontException();
3466 else
3468 boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] );
3469 memset( pWriteBuffer.get(), 0, nLength2 );
3470 int nWriteIndex = 0;
3472 int nNextSectionIndex = 0;
3473 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3475 if( it != aSections.end() )
3476 nNextSectionIndex = *it;
3477 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3479 if( nIndex == nNextSectionIndex )
3481 nIndex += 6;
3482 ++it;
3483 nNextSectionIndex = (it == aSections.end() ? nFontLen : *it );
3485 unsigned char cNibble = 0x80;
3486 if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' )
3487 cNibble = pFontData[nIndex] - '0';
3488 else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' )
3489 cNibble = pFontData[nIndex] - 'a' + 10;
3490 else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' )
3491 cNibble = pFontData[nIndex] - 'A' + 10;
3492 if( cNibble != 0x80 )
3494 if( !(nWriteIndex & 1 ) )
3495 cNibble <<= 4;
3496 pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble;
3497 nWriteIndex++;
3500 if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) )
3501 throw FontException();
3502 if( aSections.empty() )
3504 if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) )
3505 throw FontException();
3507 else
3509 // write rest of this section
3510 if( nIndex < nNextSectionIndex )
3512 if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) )
3513 throw FontException();
3515 // write following sections
3516 while( it != aSections.end() )
3518 nIndex = (*it)+6;
3519 ++it;
3520 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3522 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3523 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3524 throw FontException();
3529 endCompression();
3530 disableStreamEncryption();
3532 sal_uInt64 nEndStreamPos = 0;
3533 m_aFile.getPos(nEndStreamPos);
3535 // and finally close the stream
3536 aLine.setLength( 0 );
3537 aLine.append( "\nendstream\nendobj\n\n" );
3538 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3539 throw FontException();
3541 // write stream length object
3542 aLine.setLength( 0 );
3543 if( ! updateObject( nStreamLengthObject ) )
3544 throw FontException();
3545 aLine.append( nStreamLengthObject );
3546 aLine.append( " 0 obj\n" );
3547 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) );
3548 aLine.append( "\nendobj\n\n" );
3549 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3550 throw FontException();
3552 else
3554 OStringBuffer aErrorComment( 256 );
3555 aErrorComment.append( "GetEmbedFontData failed for font \"" );
3556 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3557 aErrorComment.append( '\"' );
3558 if( pFont->GetSlant() == ITALIC_NORMAL )
3559 aErrorComment.append( " italic" );
3560 else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3561 aErrorComment.append( " oblique" );
3562 aErrorComment.append( " weight=" );
3563 aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3564 emitComment( aErrorComment.getStr() );
3567 if( nStreamObject )
3569 // write font descriptor
3570 nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject );
3573 if( nFontDescriptor )
3575 if( pEncoding )
3576 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, SAL_N_ELEMENTS(nEncoding) );
3578 // write font object
3579 sal_Int32 nObject = createObject();
3580 if( ! updateObject( nObject ) )
3581 throw FontException();
3583 OStringBuffer aLine( 1024 );
3584 aLine.append( nObject );
3585 aLine.append( " 0 obj\n"
3586 "<</Type/Font/Subtype/Type1/BaseFont/" );
3587 appendName( aInfo.m_aPSName, aLine );
3588 aLine.append( "\n" );
3589 if( !pFont->IsSymbolFont() && pEncoding == 0 )
3590 aLine.append( "/Encoding/WinAnsiEncoding\n" );
3591 if( nToUnicodeStream )
3593 aLine.append( "/ToUnicode " );
3594 aLine.append( nToUnicodeStream );
3595 aLine.append( " 0 R\n" );
3597 aLine.append( "/FirstChar 0 /LastChar 255\n"
3598 "/Widths[" );
3599 for( int i = 0; i < 256; i++ )
3601 aLine.append( pWidths[i] );
3602 aLine.append( ((i&15) == 15) ? "\n" : " " );
3604 aLine.append( "]\n"
3605 "/FontDescriptor " );
3606 aLine.append( nFontDescriptor );
3607 aLine.append( " 0 R>>\n"
3608 "endobj\n\n" );
3609 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3610 throw FontException();
3612 aRet[ rEmbed.m_nNormalFontID ] = nObject;
3614 // write additional encodings
3615 for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it )
3617 sal_Int32 aEncWidths[ 256 ];
3618 // emit encoding dict
3619 sal_Int32 nEncObject = createObject();
3620 if( ! updateObject( nEncObject ) )
3621 throw FontException();
3623 OutputDevice* pRef = getReferenceDevice();
3624 pRef->Push( PushFlags::FONT | PushFlags::MAPMODE );
3625 pRef->SetMapMode( MapMode( MAP_PIXEL ) );
3626 Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) );
3627 aFont.SetWeight( pFont->GetWeight() );
3628 aFont.SetItalic( pFont->GetSlant() );
3629 aFont.SetPitch( pFont->GetPitch() );
3630 pRef->SetFont( aFont );
3631 pRef->ImplNewFont();
3633 aLine.setLength( 0 );
3634 aLine.append( nEncObject );
3635 aLine.append( " 0 obj\n"
3636 "<</Type/Encoding/Differences[ 0\n" );
3637 int nEncoded = 0;
3638 aUnicodes.clear();
3639 for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it )
3641 OUString aStr( str_it->m_aUnicode );
3642 aEncWidths[nEncoded] = pRef->GetTextWidth( aStr );
3643 nEncodedCodes[nEncoded] = str_it->m_aUnicode;
3644 nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded);
3645 pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size());
3646 aUnicodes.push_back( nEncodedCodes[nEncoded] );
3647 pUnicodesPerGlyph[nEncoded] = 1;
3649 aLine.append( " /" );
3650 aLine.append( str_it->m_aName );
3651 if( !((++nEncoded) & 15) )
3652 aLine.append( "\n" );
3654 aLine.append( "]>>\n"
3655 "endobj\n\n" );
3657 pRef->Pop();
3659 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3660 throw FontException();
3662 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded );
3664 nObject = createObject();
3665 if( ! updateObject( nObject ) )
3666 throw FontException();
3668 aLine.setLength( 0 );
3669 aLine.append( nObject );
3670 aLine.append( " 0 obj\n"
3671 "<</Type/Font/Subtype/Type1/BaseFont/" );
3672 appendName( aInfo.m_aPSName, aLine );
3673 aLine.append( "\n" );
3674 aLine.append( "/Encoding " );
3675 aLine.append( nEncObject );
3676 aLine.append( " 0 R\n" );
3677 if( nToUnicodeStream )
3679 aLine.append( "/ToUnicode " );
3680 aLine.append( nToUnicodeStream );
3681 aLine.append( " 0 R\n" );
3683 aLine.append( "/FirstChar 0\n"
3684 "/LastChar " );
3685 aLine.append( (sal_Int32)(nEncoded-1) );
3686 aLine.append( "\n"
3687 "/Widths[" );
3688 for( int i = 0; i < nEncoded; i++ )
3690 aLine.append( aEncWidths[i] );
3691 aLine.append( ((i&15) == 15) ? "\n" : " " );
3693 aLine.append( " ]\n"
3694 "/FontDescriptor " );
3695 aLine.append( nFontDescriptor );
3696 aLine.append( " 0 R>>\n"
3697 "endobj\n\n" );
3698 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3699 throw FontException();
3701 aRet[ enc_it->m_nFontID ] = nObject;
3705 catch( FontException& )
3707 // these do nothing in case there was no compression or encryption ongoing
3708 endCompression();
3709 disableStreamEncryption();
3712 if( pFontData )
3713 pGraphics->FreeEmbedFontData( pFontData, nFontLen );
3715 return aRet;
3718 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
3720 if( nSubsetID )
3722 for( int i = 0; i < 6; i++ )
3724 int nOffset = (nSubsetID % 26);
3725 nSubsetID /= 26;
3726 rBuffer.append( (sal_Char)('A'+nOffset) );
3728 rBuffer.append( '+' );
3730 appendName( rPSName, rBuffer );
3733 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding,
3734 sal_Ucs* pUnicodes,
3735 sal_Int32* pUnicodesPerGlyph,
3736 sal_Int32* pEncToUnicodeIndex,
3737 int nGlyphs )
3739 int nMapped = 0, n = 0;
3740 for( n = 0; n < nGlyphs; n++ )
3741 if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3742 nMapped++;
3744 if( nMapped == 0 )
3745 return 0;
3747 sal_Int32 nStream = createObject();
3748 CHECK_RETURN( updateObject( nStream ) );
3750 OStringBuffer aContents( 1024 );
3751 aContents.append(
3752 "/CIDInit/ProcSet findresource begin\n"
3753 "12 dict begin\n"
3754 "begincmap\n"
3755 "/CIDSystemInfo<<\n"
3756 "/Registry (Adobe)\n"
3757 "/Ordering (UCS)\n"
3758 "/Supplement 0\n"
3759 ">> def\n"
3760 "/CMapName/Adobe-Identity-UCS def\n"
3761 "/CMapType 2 def\n"
3762 "1 begincodespacerange\n"
3763 "<00> <FF>\n"
3764 "endcodespacerange\n"
3766 int nCount = 0;
3767 for( n = 0; n < nGlyphs; n++ )
3769 if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3771 if( (nCount % 100) == 0 )
3773 if( nCount )
3774 aContents.append( "endbfchar\n" );
3775 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) );
3776 aContents.append( " beginbfchar\n" );
3778 aContents.append( '<' );
3779 appendHex( (sal_Int8)pEncoding[n], aContents );
3780 aContents.append( "> <" );
3781 // TODO: handle unicodes>U+FFFF
3782 sal_Int32 nIndex = pEncToUnicodeIndex[n];
3783 for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ )
3785 appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents );
3786 appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents );
3788 aContents.append( ">\n" );
3789 nCount++;
3792 aContents.append( "endbfchar\n"
3793 "endcmap\n"
3794 "CMapName currentdict /CMap defineresource pop\n"
3795 "end\n"
3796 "end\n" );
3797 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3798 ZCodec pCodec( 0x4000, 0x4000 );
3799 SvMemoryStream aStream;
3800 pCodec.BeginCompression();
3801 pCodec.Write( aStream, reinterpret_cast<const sal_uInt8*>(aContents.getStr()), aContents.getLength() );
3802 pCodec.EndCompression();
3803 #endif
3805 #if OSL_DEBUG_LEVEL > 1
3806 emitComment( "PDFWriterImpl::createToUnicodeCMap" );
3807 #endif
3808 OStringBuffer aLine( 40 );
3810 aLine.append( nStream );
3811 aLine.append( " 0 obj\n<</Length " );
3812 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3813 sal_Int32 nLen = (sal_Int32)aStream.Tell();
3814 aStream.Seek( 0 );
3815 aLine.append( nLen );
3816 aLine.append( "/Filter/FlateDecode" );
3817 #else
3818 aLine.append( aContents.getLength() );
3819 #endif
3820 aLine.append( ">>\nstream\n" );
3821 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3822 checkAndEnableStreamEncryption( nStream );
3823 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3824 CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
3825 #else
3826 CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
3827 #endif
3828 disableStreamEncryption();
3829 aLine.setLength( 0 );
3830 aLine.append( "\nendstream\n"
3831 "endobj\n\n" );
3832 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3833 return nStream;
3836 sal_Int32 PDFWriterImpl::emitFontDescriptor( const PhysicalFontFace* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
3838 OStringBuffer aLine( 1024 );
3839 // get font flags, see PDF reference 1.4 p. 358
3840 // possibly characters outside Adobe standard encoding
3841 // so set Symbolic flag
3842 sal_Int32 nFontFlags = (1<<2);
3843 if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE )
3844 nFontFlags |= (1 << 6);
3845 if( pFont->GetPitch() == PITCH_FIXED )
3846 nFontFlags |= 1;
3847 if( pFont->GetFamilyType() == FAMILY_SCRIPT )
3848 nFontFlags |= (1 << 3);
3849 else if( pFont->GetFamilyType() == FAMILY_ROMAN )
3850 nFontFlags |= (1 << 1);
3852 sal_Int32 nFontDescriptor = createObject();
3853 CHECK_RETURN( updateObject( nFontDescriptor ) );
3854 aLine.setLength( 0 );
3855 aLine.append( nFontDescriptor );
3856 aLine.append( " 0 obj\n"
3857 "<</Type/FontDescriptor/FontName/" );
3858 appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
3859 aLine.append( "\n"
3860 "/Flags " );
3861 aLine.append( nFontFlags );
3862 aLine.append( "\n"
3863 "/FontBBox[" );
3864 // note: Top and Bottom are reversed in VCL and PDF rectangles
3865 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() );
3866 aLine.append( ' ' );
3867 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() );
3868 aLine.append( ' ' );
3869 aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() );
3870 aLine.append( ' ' );
3871 aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) );
3872 aLine.append( "]/ItalicAngle " );
3873 if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL )
3874 aLine.append( "-30" );
3875 else
3876 aLine.append( "0" );
3877 aLine.append( "\n"
3878 "/Ascent " );
3879 aLine.append( (sal_Int32)rInfo.m_nAscent );
3880 aLine.append( "\n"
3881 "/Descent " );
3882 aLine.append( (sal_Int32)-rInfo.m_nDescent );
3883 aLine.append( "\n"
3884 "/CapHeight " );
3885 aLine.append( (sal_Int32)rInfo.m_nCapHeight );
3886 // According to PDF reference 1.4 StemV is required
3887 // seems a tad strange to me, but well ...
3888 aLine.append( "\n"
3889 "/StemV 80\n" );
3890 if( nFontStream )
3892 aLine.append( "/FontFile" );
3893 switch( rInfo.m_nFontType )
3895 case FontSubsetInfo::SFNT_TTF:
3896 aLine.append( '2' );
3897 break;
3898 case FontSubsetInfo::TYPE1_PFA:
3899 case FontSubsetInfo::TYPE1_PFB:
3900 case FontSubsetInfo::ANY_TYPE1:
3901 break;
3902 default:
3903 OSL_FAIL( "unknown fonttype in PDF font descriptor" );
3904 return 0;
3906 aLine.append( ' ' );
3907 aLine.append( nFontStream );
3908 aLine.append( " 0 R\n" );
3910 aLine.append( ">>\n"
3911 "endobj\n\n" );
3912 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3914 return nFontDescriptor;
3917 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const
3919 for( std::map< sal_Int32, sal_Int32 >::const_iterator it =
3920 m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it )
3922 rDict.append( m_aBuiltinFonts[it->first].getNameObject() );
3923 rDict.append( ' ' );
3924 rDict.append( it->second );
3925 rDict.append( " 0 R" );
3929 bool PDFWriterImpl::emitFonts()
3931 SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics();
3933 if (!pGraphics)
3934 return false;
3936 OStringBuffer aLine( 1024 );
3938 std::map< sal_Int32, sal_Int32 > aFontIDToObject;
3940 OUString aTmpName;
3941 osl_createTempFile( NULL, NULL, &aTmpName.pData );
3942 for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it )
3944 for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit )
3946 sal_GlyphId aGlyphIds[ 256 ];
3947 sal_Int32 pWidths[ 256 ];
3948 sal_uInt8 pEncoding[ 256 ];
3949 sal_Int32 pEncToUnicodeIndex[ 256 ];
3950 sal_Int32 pUnicodesPerGlyph[ 256 ];
3951 std::vector<sal_Ucs> aUnicodes;
3952 aUnicodes.reserve( 256 );
3953 int nGlyphs = 1;
3954 // fill arrays and prepare encoding index map
3955 sal_Int32 nToUnicodeStream = 0;
3957 memset( aGlyphIds, 0, sizeof( aGlyphIds ) );
3958 memset( pEncoding, 0, sizeof( pEncoding ) );
3959 memset( pUnicodesPerGlyph, 0, sizeof( pUnicodesPerGlyph ) );
3960 memset( pEncToUnicodeIndex, 0, sizeof( pEncToUnicodeIndex ) );
3961 for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit )
3963 sal_uInt8 nEnc = fit->second.getGlyphId();
3965 DBG_ASSERT( aGlyphIds[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" );
3966 DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" );
3968 aGlyphIds[ nEnc ] = fit->first;
3969 pEncoding[ nEnc ] = nEnc;
3970 pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size());
3971 pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes();
3972 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ )
3973 aUnicodes.push_back( fit->second.getCode( n ) );
3974 if( fit->second.getCode(0) )
3975 nToUnicodeStream = 1;
3976 if( nGlyphs < 256 )
3977 nGlyphs++;
3978 else
3980 OSL_FAIL( "too many glyphs for subset" );
3983 FontSubsetInfo aSubsetInfo;
3984 if( pGraphics->CreateFontSubset( aTmpName, it->first, aGlyphIds, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
3986 // create font stream
3987 osl::File aFontFile(aTmpName);
3988 if (osl::File::E_None != aFontFile.open(osl_File_OpenFlag_Read)) return false;
3989 // get file size
3990 sal_uInt64 nLength1;
3991 if ( osl::File::E_None != aFontFile.setPos(osl_Pos_End, 0) ) return false;
3992 if ( osl::File::E_None != aFontFile.getPos(nLength1) ) return false;
3993 if ( osl::File::E_None != aFontFile.setPos(osl_Pos_Absolut, 0) ) return false;
3995 #if OSL_DEBUG_LEVEL > 1
3996 emitComment( "PDFWriterImpl::emitFonts" );
3997 #endif
3998 sal_Int32 nFontStream = createObject();
3999 sal_Int32 nStreamLengthObject = createObject();
4000 if ( !updateObject( nFontStream ) ) return false;
4001 aLine.setLength( 0 );
4002 aLine.append( nFontStream );
4003 aLine.append( " 0 obj\n"
4004 "<</Length " );
4005 aLine.append( (sal_Int32)nStreamLengthObject );
4006 aLine.append( " 0 R"
4007 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
4008 "/Filter/FlateDecode"
4009 #endif
4010 "/Length1 " );
4012 sal_uInt64 nStartPos = 0;
4013 if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF )
4015 aLine.append( (sal_Int32)nLength1 );
4017 aLine.append( ">>\n"
4018 "stream\n" );
4019 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
4020 if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false;
4022 // copy font file
4023 beginCompression();
4024 checkAndEnableStreamEncryption( nFontStream );
4025 sal_Bool bEOF = sal_False;
4028 char buf[8192];
4029 sal_uInt64 nRead;
4030 if ( osl::File::E_None != aFontFile.read(buf, sizeof(buf), nRead) ) return false;
4031 if ( !writeBuffer( buf, nRead ) ) return false;
4032 if ( osl::File::E_None != aFontFile.isEndOfFile(&bEOF) ) return false;
4033 } while( ! bEOF );
4035 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 )
4037 // TODO: implement
4038 OSL_FAIL( "PDFWriterImpl does not support CFF-font subsets yet!" );
4040 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA?
4042 boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] );
4044 sal_uInt64 nBytesRead = 0;
4045 if ( osl::File::E_None != aFontFile.read(pBuffer.get(), nLength1, nBytesRead) ) return false;
4046 DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" );
4047 if ( osl::File::E_None != aFontFile.setPos(osl_Pos_Absolut, 0) ) return false;
4048 // get the PFB-segment lengths
4049 ThreeInts aSegmentLengths = {0,0,0};
4050 getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths );
4051 // the lengths below are mandatory for PDF-exported Type1 fonts
4052 // because the PFB segment headers get stripped! WhyOhWhy.
4053 aLine.append( (sal_Int32)aSegmentLengths[0] );
4054 aLine.append( "/Length2 " );
4055 aLine.append( (sal_Int32)aSegmentLengths[1] );
4056 aLine.append( "/Length3 " );
4057 aLine.append( (sal_Int32)aSegmentLengths[2] );
4059 aLine.append( ">>\n"
4060 "stream\n" );
4061 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
4062 if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false;
4064 // emit PFB-sections without section headers
4065 beginCompression();
4066 checkAndEnableStreamEncryption( nFontStream );
4067 if ( !writeBuffer( &pBuffer[6], aSegmentLengths[0] ) ) return false;
4068 if ( !writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) ) return false;
4069 if ( !writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ) return false;
4071 else
4073 fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType);
4074 aLine.append( "0 >>\nstream\n" );
4077 endCompression();
4078 disableStreamEncryption();
4079 // close the file
4080 aFontFile.close();
4082 sal_uInt64 nEndPos = 0;
4083 if ( osl::File::E_None != m_aFile.getPos(nEndPos) ) return false;
4084 // end the stream
4085 aLine.setLength( 0 );
4086 aLine.append( "\nendstream\nendobj\n\n" );
4087 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
4089 // emit stream length object
4090 if ( !updateObject( nStreamLengthObject ) ) return false;
4091 aLine.setLength( 0 );
4092 aLine.append( nStreamLengthObject );
4093 aLine.append( " 0 obj\n" );
4094 aLine.append( (sal_Int64)(nEndPos-nStartPos) );
4095 aLine.append( "\nendobj\n\n" );
4096 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
4098 // write font descriptor
4099 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream );
4101 if( nToUnicodeStream )
4102 nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs );
4104 sal_Int32 nFontObject = createObject();
4105 if ( !updateObject( nFontObject ) ) return false;
4106 aLine.setLength( 0 );
4107 aLine.append( nFontObject );
4109 aLine.append( " 0 obj\n" );
4110 aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ?
4111 "<</Type/Font/Subtype/Type1/BaseFont/" :
4112 "<</Type/Font/Subtype/TrueType/BaseFont/" );
4113 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine );
4114 aLine.append( "\n"
4115 "/FirstChar 0\n"
4116 "/LastChar " );
4117 aLine.append( (sal_Int32)(nGlyphs-1) );
4118 aLine.append( "\n"
4119 "/Widths[" );
4120 for( int i = 0; i < nGlyphs; i++ )
4122 aLine.append( pWidths[ i ] );
4123 aLine.append( ((i & 15) == 15) ? "\n" : " " );
4125 aLine.append( "]\n"
4126 "/FontDescriptor " );
4127 aLine.append( nFontDescriptor );
4128 aLine.append( " 0 R\n" );
4129 if( nToUnicodeStream )
4131 aLine.append( "/ToUnicode " );
4132 aLine.append( nToUnicodeStream );
4133 aLine.append( " 0 R\n" );
4135 aLine.append( ">>\n"
4136 "endobj\n\n" );
4137 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
4139 aFontIDToObject[ lit->m_nFontID ] = nFontObject;
4141 else
4143 const PhysicalFontFace* pFont = it->first;
4144 OStringBuffer aErrorComment( 256 );
4145 aErrorComment.append( "CreateFontSubset failed for font \"" );
4146 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
4147 aErrorComment.append( '\"' );
4148 if( pFont->GetSlant() == ITALIC_NORMAL )
4149 aErrorComment.append( " italic" );
4150 else if( pFont->GetSlant() == ITALIC_OBLIQUE )
4151 aErrorComment.append( " oblique" );
4152 aErrorComment.append( " weight=" );
4153 aErrorComment.append( sal_Int32(pFont->GetWeight()) );
4154 emitComment( aErrorComment.getStr() );
4158 osl_removeFile( aTmpName.pData );
4160 // emit embedded fonts
4161 for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit )
4163 std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second );
4164 for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4166 if ( !fit->second ) return false;
4167 aFontIDToObject[ fit->first ] = fit->second;
4171 // emit system fonts
4172 for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit )
4174 std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second );
4175 for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4177 if ( !fit->second ) return false;
4178 aFontIDToObject[ fit->first ] = fit->second;
4182 OStringBuffer aFontDict( 1024 );
4183 aFontDict.append( getFontDictObject() );
4184 aFontDict.append( " 0 obj\n"
4185 "<<" );
4186 int ni = 0;
4187 for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit )
4189 aFontDict.append( "/F" );
4190 aFontDict.append( mit->first );
4191 aFontDict.append( ' ' );
4192 aFontDict.append( mit->second );
4193 aFontDict.append( " 0 R" );
4194 if( ((++ni) & 7) == 0 )
4195 aFontDict.append( '\n' );
4197 // emit builtin font for widget appearances / variable text
4198 for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin();
4199 it != m_aBuiltinFontToObjectMap.end(); ++it )
4201 ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]);
4202 it->second = emitBuiltinFont( &aData, it->second );
4204 appendBuiltinFontsToDict( aFontDict );
4205 aFontDict.append( "\n>>\nendobj\n\n" );
4207 if ( !updateObject( getFontDictObject() ) ) return false;
4208 if ( !writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) ) return false;
4209 return true;
4212 sal_Int32 PDFWriterImpl::emitResources()
4214 // emit shadings
4215 if( ! m_aGradients.empty() )
4216 CHECK_RETURN( emitGradients() );
4217 // emit tilings
4218 if( ! m_aTilings.empty() )
4219 CHECK_RETURN( emitTilings() );
4221 // emit font dict
4222 CHECK_RETURN( emitFonts() );
4224 // emit Resource dict
4225 OStringBuffer aLine( 512 );
4226 sal_Int32 nResourceDict = getResourceDictObj();
4227 CHECK_RETURN( updateObject( nResourceDict ) );
4228 aLine.setLength( 0 );
4229 aLine.append( nResourceDict );
4230 aLine.append( " 0 obj\n" );
4231 m_aGlobalResourceDict.append( aLine, getFontDictObject() );
4232 aLine.append( "endobj\n\n" );
4233 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4234 return nResourceDict;
4237 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
4238 sal_Int32 nItemLevel,
4239 sal_Int32 nCurrentItemId )
4241 /* The /Count number of an item is
4242 positive: the number of visible subitems
4243 negative: the negative number of subitems that will become visible if
4244 the item gets opened
4245 see PDF ref 1.4 p 478
4248 sal_Int32 nCount = 0;
4250 if( m_aContext.OpenBookmarkLevels < 0 || // all levels arevisible
4251 m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible
4254 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4255 sal_Int32 nChildren = rItem.m_aChildren.size();
4256 for( sal_Int32 i = 0; i < nChildren; i++ )
4257 nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4258 rCounts[nCurrentItemId] = nCount;
4259 // return 1 (this item) + visible sub items
4260 if( nCount < 0 )
4261 nCount = 0;
4262 nCount++;
4264 else
4266 // this bookmark level is invisible
4267 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4268 sal_Int32 nChildren = rItem.m_aChildren.size();
4269 rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
4270 for( sal_Int32 i = 0; i < nChildren; i++ )
4271 updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4272 nCount = -1;
4275 return nCount;
4278 sal_Int32 PDFWriterImpl::emitOutline()
4280 int i, nItems = m_aOutline.size();
4282 // do we have an outline at all ?
4283 if( nItems < 2 )
4284 return 0;
4286 // reserve object numbers for all outline items
4287 for( i = 0; i < nItems; ++i )
4288 m_aOutline[i].m_nObject = createObject();
4290 // update all parent, next and prev object ids
4291 for( i = 0; i < nItems; ++i )
4293 PDFOutlineEntry& rItem = m_aOutline[i];
4294 int nChildren = rItem.m_aChildren.size();
4296 if( nChildren )
4298 for( int n = 0; n < nChildren; ++n )
4300 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
4302 rChild.m_nParentObject = rItem.m_nObject;
4303 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
4304 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
4310 // calculate Count entries for all items
4311 std::vector< sal_Int32 > aCounts( nItems );
4312 updateOutlineItemCount( aCounts, 0, 0 );
4314 // emit hierarchy
4315 for( i = 0; i < nItems; ++i )
4317 PDFOutlineEntry& rItem = m_aOutline[i];
4318 OStringBuffer aLine( 1024 );
4320 CHECK_RETURN( updateObject( rItem.m_nObject ) );
4321 aLine.append( rItem.m_nObject );
4322 aLine.append( " 0 obj\n" );
4323 aLine.append( "<<" );
4324 // number of visible children (all levels)
4325 if( i > 0 || aCounts[0] > 0 )
4327 aLine.append( "/Count " );
4328 aLine.append( aCounts[i] );
4330 if( ! rItem.m_aChildren.empty() )
4332 // children list: First, Last
4333 aLine.append( "/First " );
4334 aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
4335 aLine.append( " 0 R/Last " );
4336 aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
4337 aLine.append( " 0 R\n" );
4339 if( i > 0 )
4341 // Title, Dest, Parent, Prev, Next
4342 aLine.append( "/Title" );
4343 appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
4344 aLine.append( "\n" );
4345 // Dest is not required
4346 if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() )
4348 aLine.append( "/Dest" );
4349 appendDest( rItem.m_nDestID, aLine );
4351 aLine.append( "/Parent " );
4352 aLine.append( rItem.m_nParentObject );
4353 aLine.append( " 0 R" );
4354 if( rItem.m_nPrevObject )
4356 aLine.append( "/Prev " );
4357 aLine.append( rItem.m_nPrevObject );
4358 aLine.append( " 0 R" );
4360 if( rItem.m_nNextObject )
4362 aLine.append( "/Next " );
4363 aLine.append( rItem.m_nNextObject );
4364 aLine.append( " 0 R" );
4367 aLine.append( ">>\nendobj\n\n" );
4368 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4371 return m_aOutline[0].m_nObject;
4374 #undef CHECK_RETURN
4375 #define CHECK_RETURN( x ) if( !x ) return false
4377 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
4379 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() )
4381 #if OSL_DEBUG_LEVEL > 1
4382 fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID );
4383 #endif
4384 return false;
4387 const PDFDest& rDest = m_aDests[ nDestID ];
4388 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
4390 rBuffer.append( '[' );
4391 rBuffer.append( rDestPage.m_nPageObject );
4392 rBuffer.append( " 0 R" );
4394 switch( rDest.m_eType )
4396 case PDFWriter::XYZ:
4397 default:
4398 rBuffer.append( "/XYZ " );
4399 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4400 rBuffer.append( ' ' );
4401 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4402 rBuffer.append( " 0" );
4403 break;
4404 case PDFWriter::Fit:
4405 rBuffer.append( "/Fit" );
4406 break;
4407 case PDFWriter::FitRectangle:
4408 rBuffer.append( "/FitR " );
4409 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4410 rBuffer.append( ' ' );
4411 appendFixedInt( rDest.m_aRect.Top(), rBuffer );
4412 rBuffer.append( ' ' );
4413 appendFixedInt( rDest.m_aRect.Right(), rBuffer );
4414 rBuffer.append( ' ' );
4415 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4416 break;
4417 case PDFWriter::FitHorizontal:
4418 rBuffer.append( "/FitH " );
4419 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4420 break;
4421 case PDFWriter::FitVertical:
4422 rBuffer.append( "/FitV " );
4423 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4424 break;
4425 case PDFWriter::FitPageBoundingBox:
4426 rBuffer.append( "/FitB" );
4427 break;
4428 case PDFWriter::FitPageBoundingBoxHorizontal:
4429 rBuffer.append( "/FitBH " );
4430 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4431 break;
4432 case PDFWriter::FitPageBoundingBoxVertical:
4433 rBuffer.append( "/FitBV " );
4434 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4435 break;
4437 rBuffer.append( ']' );
4439 return true;
4442 bool PDFWriterImpl::emitLinkAnnotations()
4444 int nAnnots = m_aLinks.size();
4445 for( int i = 0; i < nAnnots; i++ )
4447 const PDFLink& rLink = m_aLinks[i];
4448 if( ! updateObject( rLink.m_nObject ) )
4449 continue;
4451 OStringBuffer aLine( 1024 );
4452 aLine.append( rLink.m_nObject );
4453 aLine.append( " 0 obj\n" );
4454 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4455 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4456 aLine.append( "<</Type/Annot" );
4457 if( m_bIsPDF_A1 )
4458 aLine.append( "/F 4" );
4459 aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
4461 appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
4462 aLine.append( ' ' );
4463 appendFixedInt( rLink.m_aRect.Top(), aLine );
4464 aLine.append( ' ' );
4465 appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
4466 aLine.append( ' ' );
4467 appendFixedInt( rLink.m_aRect.Bottom(), aLine );
4468 aLine.append( "]" );
4469 if( rLink.m_nDest >= 0 )
4471 aLine.append( "/Dest" );
4472 appendDest( rLink.m_nDest, aLine );
4474 else
4476 /*--->i56629
4477 destination is external to the document, so
4478 we check in the following sequence:
4480 if target type is neither .pdf, nor .od[tpgs], then
4481 check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
4482 end processing
4483 else if target is .od[tpgs]: then
4484 if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file
4485 processing continue
4487 if (new)target is .pdf : then
4488 if GotToR is requested, then
4489 convert the target in GoToR where the fragment of the URI is
4490 considered the named destination in the target file, set relative or absolute as requested
4491 else strip the fragment from URL and then set URI or 'launch application' as requested
4494 // FIXME: check if the decode mechanisms for URL processing throughout this implementation
4495 // are the correct one!!
4497 // extract target file type
4498 INetURLObject aDocumentURL( m_aContext.BaseURL );
4499 INetURLObject aTargetURL( rLink.m_aURL );
4500 sal_Int32 nSetGoToRMode = 0;
4501 bool bTargetHasPDFExtension = false;
4502 INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
4503 bool bIsUNCPath = false;
4505 // check if the protocol is a known one, or if there is no protocol at all (on target only)
4506 // if there is no protocol, make the target relative to the current document directory
4507 // getting the needed URL information from the current document path
4508 if( eTargetProtocol == INetProtocol::NotValid )
4510 if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.startsWith("\\\\\\\\"))
4512 bIsUNCPath = true;
4514 else
4516 INetURLObject aNewBase( aDocumentURL );//duplicate document URL
4517 aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
4518 //target document
4519 aNewBase.insertName( rLink.m_aURL );
4520 aTargetURL = aNewBase;//reassign the new target URL
4521 //recompute the target protocol, with the new URL
4522 //normal URL processing resumes
4523 eTargetProtocol = aTargetURL.GetProtocol();
4527 OUString aFileExtension = aTargetURL.GetFileExtension();
4529 // Check if the URL ends in '/': if yes it's a directory,
4530 // it will be forced to a URI link.
4531 // possibly a malformed URI, leave it as it is, force as URI
4532 if( aTargetURL.hasFinalSlash() )
4533 m_aContext.DefaultLinkAction = PDFWriter::URIAction;
4535 if( !aFileExtension.isEmpty() )
4537 if( m_aContext.ConvertOOoTargetToPDFTarget )
4539 bool bChangeFileExtensionToPDF = false;
4540 //examine the file type (.odm .odt. .odp, odg, ods)
4541 if( aFileExtension.equalsIgnoreAsciiCase( "odm" ) )
4542 bChangeFileExtensionToPDF = true;
4543 if( aFileExtension.equalsIgnoreAsciiCase( "odt" ) )
4544 bChangeFileExtensionToPDF = true;
4545 else if( aFileExtension.equalsIgnoreAsciiCase( "odp" ) )
4546 bChangeFileExtensionToPDF = true;
4547 else if( aFileExtension.equalsIgnoreAsciiCase( "odg" ) )
4548 bChangeFileExtensionToPDF = true;
4549 else if( aFileExtension.equalsIgnoreAsciiCase( "ods" ) )
4550 bChangeFileExtensionToPDF = true;
4551 if( bChangeFileExtensionToPDF )
4552 aTargetURL.setExtension(OUString( "pdf" ) );
4554 //check if extension is pdf, see if GoToR should be forced
4555 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase( "pdf" );
4556 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
4557 nSetGoToRMode++;
4559 //prepare the URL, if relative or not
4560 INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
4561 //queue the string common to all types of actions
4562 aLine.append( "/A<</Type/Action/S");
4563 if( bIsUNCPath ) // handle Win UNC paths
4565 aLine.append( "/Launch/Win<</F" );
4566 // INetURLObject is not good with UNC paths, use original path
4567 appendLiteralStringEncrypt( rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4568 aLine.append( ">>" );
4570 else
4572 bool bSetRelative = false;
4573 bool bFileSpec = false;
4574 //check if relative file link is requested and if the protocol is 'file://'
4575 if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INetProtocol::File )
4576 bSetRelative = true;
4578 OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is,
4579 if( nSetGoToRMode == 0 )
4581 switch( m_aContext.DefaultLinkAction )
4583 default:
4584 case PDFWriter::URIAction :
4585 case PDFWriter::URIActionDestination :
4586 aLine.append( "/URI/URI" );
4587 break;
4588 case PDFWriter::LaunchAction:
4589 // now:
4590 // if a launch action is requested and the hyperlink target has a fragment
4591 // and the target file does not have a pdf extension, or it's not a 'file:://'
4592 // protocol then force the uri action on it
4593 // This code will permit the correct opening of application on web pages,
4594 // the one that normally have fragments (but I may be wrong...)
4595 // and will force the use of URI when the protocol is not file:
4596 if( (!aFragment.isEmpty() && !bTargetHasPDFExtension) ||
4597 eTargetProtocol != INetProtocol::File )
4599 aLine.append( "/URI/URI" );
4601 else
4603 aLine.append( "/Launch/F" );
4604 bFileSpec = true;
4606 break;
4610 //fragment are encoded in the same way as in the named destination processing
4611 if( nSetGoToRMode )
4613 //add the fragment
4614 OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET );
4615 aLine.append("/GoToR");
4616 aLine.append("/F");
4617 bFileSpec = true;
4618 appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark,
4619 INetURLObject::WAS_ENCODED,
4620 INetURLObject::DECODE_WITH_CHARSET ) :
4621 aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4622 if( !aFragment.isEmpty() )
4624 aLine.append("/D/");
4625 appendDestinationName( aFragment , aLine );
4628 else
4630 // change the fragment to accommodate the bookmark (only if the file extension
4631 // is PDF and the requested action is of the correct type)
4632 if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination &&
4633 bTargetHasPDFExtension && !aFragment.isEmpty() )
4635 OStringBuffer aLineLoc( 1024 );
4636 appendDestinationName( aFragment , aLineLoc );
4637 //substitute the fragment
4638 aTargetURL.SetMark( OStringToOUString(aLineLoc.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US) );
4640 OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE );
4641 appendLiteralStringEncrypt(bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL,
4642 INetURLObject::WAS_ENCODED,
4643 bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE
4645 aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4647 //<--- i56629
4649 aLine.append( ">>\n" );
4651 if( rLink.m_nStructParent > 0 )
4653 aLine.append( "/StructParent " );
4654 aLine.append( rLink.m_nStructParent );
4656 aLine.append( ">>\nendobj\n\n" );
4657 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4660 return true;
4663 bool PDFWriterImpl::emitNoteAnnotations()
4665 // emit note annotations
4666 int nAnnots = m_aNotes.size();
4667 for( int i = 0; i < nAnnots; i++ )
4669 const PDFNoteEntry& rNote = m_aNotes[i];
4670 if( ! updateObject( rNote.m_nObject ) )
4671 return false;
4673 OStringBuffer aLine( 1024 );
4674 aLine.append( rNote.m_nObject );
4675 aLine.append( " 0 obj\n" );
4676 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4677 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4678 aLine.append( "<</Type/Annot" );
4679 if( m_bIsPDF_A1 )
4680 aLine.append( "/F 4" );
4681 aLine.append( "/Subtype/Text/Rect[" );
4683 appendFixedInt( rNote.m_aRect.Left(), aLine );
4684 aLine.append( ' ' );
4685 appendFixedInt( rNote.m_aRect.Top(), aLine );
4686 aLine.append( ' ' );
4687 appendFixedInt( rNote.m_aRect.Right(), aLine );
4688 aLine.append( ' ' );
4689 appendFixedInt( rNote.m_aRect.Bottom(), aLine );
4690 aLine.append( "]" );
4692 // contents of the note (type text string)
4693 aLine.append( "/Contents\n" );
4694 appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
4695 aLine.append( "\n" );
4697 // optional title
4698 if( !rNote.m_aContents.Title.isEmpty() )
4700 aLine.append( "/T" );
4701 appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
4702 aLine.append( "\n" );
4705 aLine.append( ">>\nendobj\n\n" );
4706 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4708 return true;
4711 Font PDFWriterImpl::replaceFont( const vcl::Font& rControlFont, const vcl::Font& rAppSetFont )
4713 bool bAdjustSize = false;
4715 Font aFont( rControlFont );
4716 if( aFont.GetName().isEmpty() )
4718 aFont = rAppSetFont;
4719 if( rControlFont.GetHeight() )
4720 aFont.SetSize( Size( 0, rControlFont.GetHeight() ) );
4721 else
4722 bAdjustSize = true;
4723 if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
4724 aFont.SetItalic( rControlFont.GetItalic() );
4725 if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
4726 aFont.SetWeight( rControlFont.GetWeight() );
4728 else if( ! aFont.GetHeight() )
4730 aFont.SetSize( rAppSetFont.GetSize() );
4731 bAdjustSize = true;
4733 if( bAdjustSize )
4735 Size aFontSize = aFont.GetSize();
4736 OutputDevice* pDefDev = Application::GetDefaultDevice();
4737 aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
4738 aFont.SetSize( aFontSize );
4740 return aFont;
4743 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const vcl::Font& rFont )
4745 sal_Int32 nBest = 4; // default to Helvetica
4746 OUString aFontName( rFont.GetName() );
4747 aFontName = aFontName.toAsciiLowerCase();
4749 if( aFontName.indexOf( "times" ) != -1 )
4750 nBest = 8;
4751 else if( aFontName.indexOf( "courier" ) != -1 )
4752 nBest = 0;
4753 else if( aFontName.indexOf( "dingbats" ) != -1 )
4754 nBest = 13;
4755 else if( aFontName.indexOf( "symbol" ) != -1 )
4756 nBest = 12;
4757 if( nBest < 12 )
4759 if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
4760 nBest += 1;
4761 if( rFont.GetWeight() > WEIGHT_MEDIUM )
4762 nBest += 2;
4765 if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() )
4766 m_aBuiltinFontToObjectMap[ nBest ] = createObject();
4768 return nBest;
4771 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
4773 return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1;
4776 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget )
4778 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4780 // save graphics state
4781 push( PushFlags::ALL );
4783 // transform relative to control's coordinates since an
4784 // appearance stream is a form XObject
4785 // this relies on the m_aRect member of rButton NOT already being transformed
4786 // to default user space
4787 if( rWidget.Background || rWidget.Border )
4789 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) );
4790 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) );
4791 drawRectangle( rWidget.Location );
4793 // prepare font to use
4794 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
4795 setFont( aFont );
4796 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
4798 drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
4800 // create DA string while local mapmode is still in place
4801 // (that is before endRedirect())
4802 OStringBuffer aDA( 256 );
4803 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
4804 Font aDummyFont( OUString( "Helvetica" ), aFont.GetSize() );
4805 sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont );
4806 aDA.append( ' ' );
4807 aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() );
4808 aDA.append( ' ' );
4809 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4810 aDA.append( " Tf" );
4811 rButton.m_aDAString = aDA.makeStringAndClear();
4813 pop();
4815 rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
4817 /* seems like a bad hack but at least works in both AR5 and 6:
4818 we draw the button ourselves and tell AR
4819 the button would be totally transparent with no text
4821 One would expect that simply setting a normal appearance
4822 should suffice, but no, as soon as the user actually presses
4823 the button and an action is tied to it (gasp! a button that
4824 does something) the appearance gets replaced by some crap that AR
4825 creates on the fly even if no DA or MK is given. On AR6 at least
4826 the DA and MK work as expected, but on AR5 this creates a region
4827 filled with the background color but nor text. Urgh.
4829 rButton.m_aMKDict = "/BC [] /BG [] /CA";
4830 rButton.m_aMKDictCAString = "";
4833 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern,
4834 const PDFWriter::AnyWidget& rWidget,
4835 const StyleSettings& rSettings )
4837 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
4839 if( rWidget.Background || rWidget.Border )
4841 if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) )
4843 sal_Int32 nDelta = getReferenceDevice()->GetDPIX() / 500;
4844 if( nDelta < 1 )
4845 nDelta = 1;
4846 setLineColor( Color( COL_TRANSPARENT ) );
4847 Rectangle aRect = rIntern.m_aRect;
4848 setFillColor( rSettings.GetLightBorderColor() );
4849 drawRectangle( aRect );
4850 aRect.Left() += nDelta; aRect.Top() += nDelta;
4851 aRect.Right() -= nDelta; aRect.Bottom() -= nDelta;
4852 setFillColor( rSettings.GetFieldColor() );
4853 drawRectangle( aRect );
4854 setFillColor( rSettings.GetLightColor() );
4855 drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
4856 drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
4857 setFillColor( rSettings.GetDarkShadowColor() );
4858 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
4859 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
4861 else
4863 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) );
4864 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4865 drawRectangle( rIntern.m_aRect );
4868 if( rWidget.Border )
4870 // adjust edit area accounting for border
4871 sal_Int32 nDelta = aFont.GetHeight()/4;
4872 if( nDelta < 1 )
4873 nDelta = 1;
4874 rIntern.m_aRect.Left() += nDelta;
4875 rIntern.m_aRect.Top() += nDelta;
4876 rIntern.m_aRect.Right() -= nDelta;
4877 rIntern.m_aRect.Bottom()-= nDelta;
4880 return aFont;
4883 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget )
4885 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4886 SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
4888 push( PushFlags::ALL );
4890 // prepare font to use, draw field border
4891 Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
4892 sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
4894 // prepare DA string
4895 OStringBuffer aDA( 32 );
4896 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4897 aDA.append( ' ' );
4898 if( m_aContext.FieldsUseSystemFonts )
4900 aDA.append( "/F" );
4901 aDA.append( nBest );
4903 OStringBuffer aDR( 32 );
4904 aDR.append( "/Font " );
4905 aDR.append( getFontDictObject() );
4906 aDR.append( " 0 R" );
4907 rEdit.m_aDRDict = aDR.makeStringAndClear();
4909 else
4910 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4911 aDA.append( ' ' );
4912 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4913 aDA.append( " Tf" );
4915 /* create an empty appearance stream, let the viewer create
4916 the appearance at runtime. This is because AR5 seems to
4917 paint the widget appearance always, and a dynamically created
4918 appearance on top of it. AR6 is well behaved in that regard, so
4919 that behaviour seems to be a bug. Anyway this empty appearance
4920 relies on /NeedAppearances in the AcroForm dictionary set to "true"
4922 beginRedirect( pEditStream, rEdit.m_aRect );
4923 OStringBuffer aAppearance( 32 );
4924 aAppearance.append( "/Tx BMC\nEMC\n" );
4925 writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4927 endRedirect();
4928 pop();
4930 rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
4932 rEdit.m_aDAString = aDA.makeStringAndClear();
4935 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget )
4937 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4938 SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
4940 push( PushFlags::ALL );
4942 // prepare font to use, draw field border
4943 Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
4944 sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
4946 beginRedirect( pListBoxStream, rBox.m_aRect );
4947 OStringBuffer aAppearance( 64 );
4949 setLineColor( Color( COL_TRANSPARENT ) );
4950 setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
4951 drawRectangle( rBox.m_aRect );
4953 // empty appearance, see createDefaultEditAppearance for reference
4954 aAppearance.append( "/Tx BMC\nEMC\n" );
4955 writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4957 endRedirect();
4958 pop();
4960 rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
4962 // prepare DA string
4963 OStringBuffer aDA( 256 );
4964 // prepare DA string
4965 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4966 aDA.append( ' ' );
4967 if( m_aContext.FieldsUseSystemFonts )
4969 aDA.append( "/F" );
4970 aDA.append( nBest );
4972 OStringBuffer aDR( 32 );
4973 aDR.append( "/Font " );
4974 aDR.append( getFontDictObject() );
4975 aDR.append( " 0 R" );
4976 rBox.m_aDRDict = aDR.makeStringAndClear();
4978 else
4979 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4980 aDA.append( ' ' );
4981 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4982 aDA.append( " Tf" );
4983 rBox.m_aDAString = aDA.makeStringAndClear();
4986 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget )
4988 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4990 // save graphics state
4991 push( PushFlags::ALL );
4993 if( rWidget.Background || rWidget.Border )
4995 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
4996 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4997 drawRectangle( rBox.m_aRect );
5000 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5001 setFont( aFont );
5002 Size aFontSize = aFont.GetSize();
5003 if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5004 aFontSize.Height() = rBox.m_aRect.GetHeight();
5005 sal_Int32 nDelta = aFontSize.Height()/10;
5006 if( nDelta < 1 )
5007 nDelta = 1;
5009 Rectangle aCheckRect, aTextRect;
5010 if( rWidget.ButtonIsLeft )
5012 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
5013 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5014 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5015 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5017 // #i74206# handle small controls without text area
5018 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5020 aCheckRect.Right() -= nDelta;
5021 aCheckRect.Top() += nDelta/2;
5022 aCheckRect.Bottom() -= nDelta - (nDelta/2);
5025 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5026 aTextRect.Top() = rBox.m_aRect.Top();
5027 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5028 aTextRect.Bottom() = rBox.m_aRect.Bottom();
5030 else
5032 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5033 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5034 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5035 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5037 // #i74206# handle small controls without text area
5038 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5040 aCheckRect.Left() += nDelta;
5041 aCheckRect.Top() += nDelta/2;
5042 aCheckRect.Bottom() -= nDelta - (nDelta/2);
5045 aTextRect.Left() = rBox.m_aRect.Left();
5046 aTextRect.Top() = rBox.m_aRect.Top();
5047 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5048 aTextRect.Bottom() = rBox.m_aRect.Bottom();
5050 setLineColor( Color( COL_BLACK ) );
5051 setFillColor( Color( COL_TRANSPARENT ) );
5052 OStringBuffer aLW( 32 );
5053 aLW.append( "q " );
5054 m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
5055 aLW.append( " w " );
5056 writeBuffer( aLW.getStr(), aLW.getLength() );
5057 drawRectangle( aCheckRect );
5058 writeBuffer( " Q\n", 3 );
5059 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5060 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5062 pop();
5064 OStringBuffer aDA( 256 );
5065 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5066 sal_Int32 nBest = getBestBuiltinFont( Font( OUString( "ZapfDingbats" ), aFont.GetSize() ) );
5067 aDA.append( ' ' );
5068 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5069 aDA.append( " 0 Tf" );
5070 rBox.m_aDAString = aDA.makeStringAndClear();
5071 rBox.m_aMKDict = "/CA";
5072 rBox.m_aMKDictCAString = "8";
5073 rBox.m_aRect = aCheckRect;
5075 // create appearance streams
5076 sal_Char cMark = '8';
5077 sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)];
5078 nCharXOffset *= aCheckRect.GetHeight();
5079 nCharXOffset /= 2000;
5080 sal_Int32 nCharYOffset = 1000-
5081 (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative
5082 nCharYOffset *= aCheckRect.GetHeight();
5083 nCharYOffset /= 2000;
5085 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5086 beginRedirect( pCheckStream, aCheckRect );
5087 aDA.append( "/Tx BMC\nq BT\n" );
5088 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5089 aDA.append( ' ' );
5090 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5091 aDA.append( ' ' );
5092 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5093 aDA.append( " Tf\n" );
5094 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
5095 aDA.append( " " );
5096 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
5097 aDA.append( " Td (" );
5098 aDA.append( cMark );
5099 aDA.append( ") Tj\nET\nQ\nEMC\n" );
5100 writeBuffer( aDA.getStr(), aDA.getLength() );
5101 endRedirect();
5102 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5104 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5105 beginRedirect( pUncheckStream, aCheckRect );
5106 writeBuffer( "/Tx BMC\nEMC\n", 12 );
5107 endRedirect();
5108 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5111 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget )
5113 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5115 // save graphics state
5116 push( PushFlags::ALL );
5118 if( rWidget.Background || rWidget.Border )
5120 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5121 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5122 drawRectangle( rBox.m_aRect );
5125 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5126 setFont( aFont );
5127 Size aFontSize = aFont.GetSize();
5128 if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5129 aFontSize.Height() = rBox.m_aRect.GetHeight();
5130 sal_Int32 nDelta = aFontSize.Height()/10;
5131 if( nDelta < 1 )
5132 nDelta = 1;
5134 Rectangle aCheckRect, aTextRect;
5135 if( rWidget.ButtonIsLeft )
5137 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
5138 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5139 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5140 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5142 // #i74206# handle small controls without text area
5143 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5145 aCheckRect.Right() -= nDelta;
5146 aCheckRect.Top() += nDelta/2;
5147 aCheckRect.Bottom() -= nDelta - (nDelta/2);
5150 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5151 aTextRect.Top() = rBox.m_aRect.Top();
5152 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5153 aTextRect.Bottom() = rBox.m_aRect.Bottom();
5155 else
5157 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5158 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5159 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5160 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5162 // #i74206# handle small controls without text area
5163 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5165 aCheckRect.Left() += nDelta;
5166 aCheckRect.Top() += nDelta/2;
5167 aCheckRect.Bottom() -= nDelta - (nDelta/2);
5170 aTextRect.Left() = rBox.m_aRect.Left();
5171 aTextRect.Top() = rBox.m_aRect.Top();
5172 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5173 aTextRect.Bottom() = rBox.m_aRect.Bottom();
5175 setLineColor( Color( COL_BLACK ) );
5176 setFillColor( Color( COL_TRANSPARENT ) );
5177 OStringBuffer aLW( 32 );
5178 aLW.append( "q " );
5179 m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
5180 aLW.append( " w " );
5181 writeBuffer( aLW.getStr(), aLW.getLength() );
5182 drawEllipse( aCheckRect );
5183 writeBuffer( " Q\n", 3 );
5184 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5185 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5187 pop();
5189 OStringBuffer aDA( 256 );
5190 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5191 sal_Int32 nBest = getBestBuiltinFont( Font( OUString( "ZapfDingbats" ), aFont.GetSize() ) );
5192 aDA.append( ' ' );
5193 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5194 aDA.append( " 0 Tf" );
5195 rBox.m_aDAString = aDA.makeStringAndClear();
5196 //to encrypt this (el)
5197 rBox.m_aMKDict = "/CA";
5198 //after this assignement, to m_aMKDic cannot be added anything
5199 rBox.m_aMKDictCAString = "l";
5201 rBox.m_aRect = aCheckRect;
5203 // create appearance streams
5204 push( PushFlags::ALL);
5205 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5207 beginRedirect( pCheckStream, aCheckRect );
5208 aDA.append( "/Tx BMC\nq BT\n" );
5209 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5210 aDA.append( ' ' );
5211 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5212 aDA.append( ' ' );
5213 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5214 aDA.append( " Tf\n0 0 Td\nET\nQ\n" );
5215 writeBuffer( aDA.getStr(), aDA.getLength() );
5216 setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5217 setLineColor( Color( COL_TRANSPARENT ) );
5218 aCheckRect.Left() += 3*nDelta;
5219 aCheckRect.Top() += 3*nDelta;
5220 aCheckRect.Bottom() -= 3*nDelta;
5221 aCheckRect.Right() -= 3*nDelta;
5222 drawEllipse( aCheckRect );
5223 writeBuffer( "\nEMC\n", 5 );
5224 endRedirect();
5226 pop();
5227 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5229 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5230 beginRedirect( pUncheckStream, aCheckRect );
5231 writeBuffer( "/Tx BMC\nEMC\n", 12 );
5232 endRedirect();
5233 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5236 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
5238 // TODO: check and insert default streams
5239 OString aStandardAppearance;
5240 switch( rWidget.m_eType )
5242 case PDFWriter::CheckBox:
5243 aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
5244 break;
5245 default:
5246 break;
5249 if( !rWidget.m_aAppearances.empty() )
5251 rAnnotDict.append( "/AP<<\n" );
5252 for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it )
5254 rAnnotDict.append( "/" );
5255 rAnnotDict.append( dict_it->first );
5256 bool bUseSubDict = (dict_it->second.size() > 1);
5257 rAnnotDict.append( bUseSubDict ? "<<" : " " );
5259 for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin();
5260 stream_it != dict_it->second.end(); ++stream_it )
5262 SvMemoryStream* pApppearanceStream = stream_it->second;
5263 dict_it->second[ stream_it->first ] = NULL;
5265 bool bDeflate = compressStream( pApppearanceStream );
5267 pApppearanceStream->Seek( STREAM_SEEK_TO_END );
5268 sal_Int64 nStreamLen = pApppearanceStream->Tell();
5269 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
5270 sal_Int32 nObject = createObject();
5271 CHECK_RETURN( updateObject( nObject ) );
5272 #if OSL_DEBUG_LEVEL > 1
5273 emitComment( "PDFWriterImpl::emitAppearances" );
5274 #endif
5275 OStringBuffer aLine;
5276 aLine.append( nObject );
5278 aLine.append( " 0 obj\n"
5279 "<</Type/XObject\n"
5280 "/Subtype/Form\n"
5281 "/BBox[0 0 " );
5282 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
5283 aLine.append( " " );
5284 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
5285 aLine.append( "]\n"
5286 "/Resources " );
5287 aLine.append( getResourceDictObj() );
5288 aLine.append( " 0 R\n"
5289 "/Length " );
5290 aLine.append( nStreamLen );
5291 aLine.append( "\n" );
5292 if( bDeflate )
5293 aLine.append( "/Filter/FlateDecode\n" );
5294 aLine.append( ">>\nstream\n" );
5295 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5296 checkAndEnableStreamEncryption( nObject );
5297 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
5298 disableStreamEncryption();
5299 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
5301 if( bUseSubDict )
5303 rAnnotDict.append( " /" );
5304 rAnnotDict.append( stream_it->first );
5305 rAnnotDict.append( " " );
5307 rAnnotDict.append( nObject );
5308 rAnnotDict.append( " 0 R" );
5310 delete pApppearanceStream;
5313 rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
5315 rAnnotDict.append( ">>\n" );
5316 if( !aStandardAppearance.isEmpty() )
5318 rAnnotDict.append( "/AS /" );
5319 rAnnotDict.append( aStandardAppearance );
5320 rAnnotDict.append( "\n" );
5324 return true;
5327 bool PDFWriterImpl::emitWidgetAnnotations()
5329 ensureUniqueRadioOnValues();
5331 int nAnnots = m_aWidgets.size();
5332 for( int a = 0; a < nAnnots; a++ )
5334 PDFWidget& rWidget = m_aWidgets[a];
5336 OStringBuffer aLine( 1024 );
5337 OStringBuffer aValue( 256 );
5338 aLine.append( rWidget.m_nObject );
5339 aLine.append( " 0 obj\n"
5340 "<<" );
5341 if( rWidget.m_eType != PDFWriter::Hierarchy )
5343 // emit widget annotation only for terminal fields
5344 if( rWidget.m_aKids.empty() )
5346 int iRectMargin;
5348 aLine.append( "/Type/Annot/Subtype/Widget/F " );
5350 if (rWidget.m_eType == PDFWriter::Signature)
5352 aLine.append( "132\n" ); // Print & Locked
5353 iRectMargin = 0;
5355 else
5357 aLine.append( "4\n" );
5358 iRectMargin = 1;
5361 aLine.append("/Rect[" );
5362 appendFixedInt( rWidget.m_aRect.Left()-iRectMargin, aLine );
5363 aLine.append( ' ' );
5364 appendFixedInt( rWidget.m_aRect.Top()+iRectMargin, aLine );
5365 aLine.append( ' ' );
5366 appendFixedInt( rWidget.m_aRect.Right()+iRectMargin, aLine );
5367 aLine.append( ' ' );
5368 appendFixedInt( rWidget.m_aRect.Bottom()-iRectMargin, aLine );
5369 aLine.append( "]\n" );
5371 aLine.append( "/FT/" );
5372 switch( rWidget.m_eType )
5374 case PDFWriter::RadioButton:
5375 case PDFWriter::CheckBox:
5376 // for radio buttons only the RadioButton field, not the
5377 // CheckBox children should have a value, else acrobat reader
5378 // does not always check the right button
5379 // of course real check boxes (not belonging to a radio group)
5380 // need their values, too
5381 if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
5383 aValue.append( "/" );
5384 // check for radio group with all buttons unpressed
5385 if( rWidget.m_aValue.isEmpty() )
5386 aValue.append( "Off" );
5387 else
5388 appendName( rWidget.m_aValue, aValue );
5390 // fall-through
5391 case PDFWriter::PushButton:
5392 aLine.append( "Btn" );
5393 break;
5394 case PDFWriter::ListBox:
5395 if( rWidget.m_nFlags & 0x200000 ) // multiselect
5397 aValue.append( "[" );
5398 for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
5400 sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
5401 if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
5402 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
5404 aValue.append( "]" );
5406 else if( rWidget.m_aSelectedEntries.size() > 0 &&
5407 rWidget.m_aSelectedEntries[0] >= 0 &&
5408 rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
5410 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
5412 else
5413 appendUnicodeTextStringEncrypt( OUString(), rWidget.m_nObject, aValue );
5414 aLine.append( "Ch" );
5415 break;
5416 case PDFWriter::ComboBox:
5417 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5418 aLine.append( "Ch" );
5419 break;
5420 case PDFWriter::Edit:
5421 aLine.append( "Tx" );
5422 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5423 break;
5424 case PDFWriter::Signature:
5425 aLine.append( "Sig" );
5426 aValue.append(OUStringToOString(rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US));
5427 break;
5428 case PDFWriter::Hierarchy: // make the compiler happy
5429 break;
5431 aLine.append( "\n" );
5432 aLine.append( "/P " );
5433 aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
5434 aLine.append( " 0 R\n" );
5436 if( rWidget.m_nParent )
5438 aLine.append( "/Parent " );
5439 aLine.append( rWidget.m_nParent );
5440 aLine.append( " 0 R\n" );
5442 if( rWidget.m_aKids.size() )
5444 aLine.append( "/Kids[" );
5445 for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ )
5447 aLine.append( rWidget.m_aKids[i] );
5448 aLine.append( " 0 R" );
5449 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5451 aLine.append( "]\n" );
5453 if( !rWidget.m_aName.isEmpty() )
5455 aLine.append( "/T" );
5456 appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
5457 aLine.append( "\n" );
5459 if( m_aContext.Version > PDFWriter::PDF_1_2 && !rWidget.m_aDescription.isEmpty() )
5461 // the alternate field name should be unicode able since it is
5462 // supposed to be used in UI
5463 aLine.append( "/TU" );
5464 appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
5465 aLine.append( "\n" );
5468 if( rWidget.m_nFlags )
5470 aLine.append( "/Ff " );
5471 aLine.append( rWidget.m_nFlags );
5472 aLine.append( "\n" );
5474 if( !aValue.isEmpty() )
5476 OString aVal = aValue.makeStringAndClear();
5477 aLine.append( "/V " );
5478 aLine.append( aVal );
5479 aLine.append( "\n"
5480 "/DV " );
5481 aLine.append( aVal );
5482 aLine.append( "\n" );
5484 if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
5486 sal_Int32 nTI = -1;
5487 aLine.append( "/Opt[\n" );
5488 sal_Int32 i = 0;
5489 for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i )
5491 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine );
5492 aLine.append( "\n" );
5493 if( *it == rWidget.m_aValue )
5494 nTI = i;
5496 aLine.append( "]\n" );
5497 if( nTI > 0 )
5499 aLine.append( "/TI " );
5500 aLine.append( nTI );
5501 aLine.append( "\n" );
5502 if( rWidget.m_nFlags & 0x200000 ) // Multiselect
5504 aLine.append( "/I [" );
5505 aLine.append( nTI );
5506 aLine.append( "]\n" );
5510 if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
5512 aLine.append( "/MaxLen " );
5513 aLine.append( rWidget.m_nMaxLen );
5514 aLine.append( "\n" );
5516 if( rWidget.m_eType == PDFWriter::PushButton )
5518 if(!m_bIsPDF_A1)
5520 OStringBuffer aDest;
5521 if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) )
5523 aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
5524 aLine.append( aDest.makeStringAndClear() );
5525 aLine.append( ">>>>\n" );
5527 else if( rWidget.m_aListEntries.empty() )
5529 // create a reset form action
5530 aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
5532 else if( rWidget.m_bSubmit )
5534 // create a submit form action
5535 aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
5536 appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() );
5537 aLine.append( "/Flags " );
5539 sal_Int32 nFlags = 0;
5540 switch( m_aContext.SubmitFormat )
5542 case PDFWriter::HTML:
5543 nFlags |= 4;
5544 break;
5545 case PDFWriter::XML:
5546 if( m_aContext.Version > PDFWriter::PDF_1_3 )
5547 nFlags |= 32;
5548 break;
5549 case PDFWriter::PDF:
5550 if( m_aContext.Version > PDFWriter::PDF_1_3 )
5551 nFlags |= 256;
5552 break;
5553 case PDFWriter::FDF:
5554 default:
5555 break;
5557 if( rWidget.m_bSubmitGet )
5558 nFlags |= 8;
5559 aLine.append( nFlags );
5560 aLine.append( ">>>>\n" );
5562 else
5564 // create a URI action
5565 aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
5566 aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
5567 aLine.append( ")>>>>\n" );
5570 else
5571 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA );
5573 if( !rWidget.m_aDAString.isEmpty() )
5575 if( !rWidget.m_aDRDict.isEmpty() )
5577 aLine.append( "/DR<<" );
5578 aLine.append( rWidget.m_aDRDict );
5579 aLine.append( ">>\n" );
5581 else
5583 aLine.append( "/DR<</Font<<" );
5584 appendBuiltinFontsToDict( aLine );
5585 aLine.append( ">>>>\n" );
5587 aLine.append( "/DA" );
5588 appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
5589 aLine.append( "\n" );
5590 if( rWidget.m_nTextStyle & DrawTextFlags::Center )
5591 aLine.append( "/Q 1\n" );
5592 else if( rWidget.m_nTextStyle & DrawTextFlags::Right )
5593 aLine.append( "/Q 2\n" );
5595 // appearance charactristics for terminal fields
5596 // which are supposed to have an appearance constructed
5597 // by the viewer application
5598 if( !rWidget.m_aMKDict.isEmpty() )
5600 aLine.append( "/MK<<" );
5601 aLine.append( rWidget.m_aMKDict );
5602 //add the CA string, encrypting it
5603 appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
5604 aLine.append( ">>\n" );
5607 CHECK_RETURN( emitAppearances( rWidget, aLine ) );
5609 aLine.append( ">>\n"
5610 "endobj\n\n" );
5611 CHECK_RETURN( updateObject( rWidget.m_nObject ) );
5612 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5614 return true;
5617 bool PDFWriterImpl::emitAnnotations()
5619 if( m_aPages.size() < 1 )
5620 return false;
5622 CHECK_RETURN( emitLinkAnnotations() );
5623 CHECK_RETURN( emitNoteAnnotations() );
5624 CHECK_RETURN( emitWidgetAnnotations() );
5626 return true;
5629 #undef CHECK_RETURN
5630 #define CHECK_RETURN( x ) if( !x ) return false
5632 bool PDFWriterImpl::emitCatalog()
5634 // build page tree
5635 // currently there is only one node that contains all leaves
5637 // first create a page tree node id
5638 sal_Int32 nTreeNode = createObject();
5640 // emit global resource dictionary (page emit needs it)
5641 CHECK_RETURN( emitResources() );
5643 // emit all pages
5644 for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it )
5645 if( ! it->emit( nTreeNode ) )
5646 return false;
5648 sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
5650 sal_Int32 nOutlineDict = emitOutline();
5652 // emit Output intent i59651
5653 sal_Int32 nOutputIntentObject = emitOutputIntent();
5655 // emit metadata
5656 sal_Int32 nMetadataObject = emitDocumentMetadata();
5658 sal_Int32 nStructureDict = 0;
5659 if(m_aStructure.size() > 1)
5661 // check if dummy structure containers are needed
5662 addInternalStructureContainer(m_aStructure[0]);
5663 nStructureDict = m_aStructure[0].m_nObject = createObject();
5664 emitStructure( m_aStructure[ 0 ] );
5667 // adjust tree node file offset
5668 if( ! updateObject( nTreeNode ) )
5669 return false;
5671 // emit tree node
5672 OStringBuffer aLine( 2048 );
5673 aLine.append( nTreeNode );
5674 aLine.append( " 0 obj\n" );
5675 aLine.append( "<</Type/Pages\n" );
5676 aLine.append( "/Resources " );
5677 aLine.append( getResourceDictObj() );
5678 aLine.append( " 0 R\n" );
5680 switch( m_eInheritedOrientation )
5682 case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
5683 case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
5685 case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant
5686 case PDFWriter::Portrait:
5687 default:
5688 break;
5690 sal_Int32 nMediaBoxWidth = 0;
5691 sal_Int32 nMediaBoxHeight = 0;
5692 if( m_aPages.empty() ) // sanity check, this should not happen
5694 nMediaBoxWidth = m_nInheritedPageWidth;
5695 nMediaBoxHeight = m_nInheritedPageHeight;
5697 else
5699 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter )
5701 if( iter->m_nPageWidth > nMediaBoxWidth )
5702 nMediaBoxWidth = iter->m_nPageWidth;
5703 if( iter->m_nPageHeight > nMediaBoxHeight )
5704 nMediaBoxHeight = iter->m_nPageHeight;
5707 aLine.append( "/MediaBox[ 0 0 " );
5708 aLine.append( nMediaBoxWidth );
5709 aLine.append( ' ' );
5710 aLine.append( nMediaBoxHeight );
5711 aLine.append( " ]\n"
5712 "/Kids[ " );
5713 unsigned int i = 0;
5714 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ )
5716 aLine.append( iter->m_nPageObject );
5717 aLine.append( " 0 R" );
5718 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5720 aLine.append( "]\n"
5721 "/Count " );
5722 aLine.append( (sal_Int32)m_aPages.size() );
5723 aLine.append( ">>\n"
5724 "endobj\n\n" );
5725 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5727 // emit annotation objects
5728 CHECK_RETURN( emitAnnotations() );
5730 // emit Catalog
5731 m_nCatalogObject = createObject();
5732 if( ! updateObject( m_nCatalogObject ) )
5733 return false;
5734 aLine.setLength( 0 );
5735 aLine.append( m_nCatalogObject );
5736 aLine.append( " 0 obj\n"
5737 "<</Type/Catalog/Pages " );
5738 aLine.append( nTreeNode );
5739 aLine.append( " 0 R\n" );
5740 //--->i56629
5741 // check if there are named destinations to emit (root must be inside the catalog)
5742 if( nNamedDestinationsDictionary )
5744 aLine.append("/Dests ");
5745 aLine.append( nNamedDestinationsDictionary );
5746 aLine.append( " 0 R\n" );
5748 //<----
5749 if( m_aContext.PageLayout != PDFWriter::DefaultLayout )
5750 switch( m_aContext.PageLayout )
5752 default :
5753 case PDFWriter::SinglePage :
5754 aLine.append( "/PageLayout/SinglePage\n" );
5755 break;
5756 case PDFWriter::Continuous :
5757 aLine.append( "/PageLayout/OneColumn\n" );
5758 break;
5759 case PDFWriter::ContinuousFacing :
5760 // the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5761 aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5762 break;
5764 if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode )
5765 switch( m_aContext.PDFDocumentMode )
5767 default :
5768 aLine.append( "/PageMode/UseNone\n" );
5769 break;
5770 case PDFWriter::UseOutlines :
5771 aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5772 break;
5773 case PDFWriter::UseThumbs :
5774 aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5775 break;
5777 else if( m_aContext.OpenInFullScreenMode )
5778 aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5780 OStringBuffer aInitPageRef;
5781 if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() )
5783 aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5784 aInitPageRef.append( " 0 R" );
5786 else
5787 aInitPageRef.append( "0" );
5789 switch( m_aContext.PDFDocumentAction )
5791 case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default
5792 default:
5793 if( aInitPageRef.getLength() > 1 )
5795 aLine.append( "/OpenAction[" );
5796 aLine.append( aInitPageRef.makeStringAndClear() );
5797 aLine.append( " /XYZ null null 0]\n" );
5799 break;
5800 case PDFWriter::FitInWindow :
5801 aLine.append( "/OpenAction[" );
5802 aLine.append( aInitPageRef.makeStringAndClear() );
5803 aLine.append( " /Fit]\n" ); //Open fit page
5804 break;
5805 case PDFWriter::FitWidth :
5806 aLine.append( "/OpenAction[" );
5807 aLine.append( aInitPageRef.makeStringAndClear() );
5808 aLine.append( " /FitH " );
5809 aLine.append( m_nInheritedPageHeight );//Open fit width
5810 aLine.append( "]\n" );
5811 break;
5812 case PDFWriter::FitVisible :
5813 aLine.append( "/OpenAction[" );
5814 aLine.append( aInitPageRef.makeStringAndClear() );
5815 aLine.append( " /FitBH " );
5816 aLine.append( m_nInheritedPageHeight );//Open fit visible
5817 aLine.append( "]\n" );
5818 break;
5819 case PDFWriter::ActionZoom :
5820 aLine.append( "/OpenAction[" );
5821 aLine.append( aInitPageRef.makeStringAndClear() );
5822 aLine.append( " /XYZ null null " );
5823 if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5824 aLine.append( (double)m_aContext.Zoom/100.0 );
5825 else
5826 aLine.append( "0" );
5827 aLine.append( "]\n" );
5828 break;
5831 // viewer preferences, if we had some, then emit
5832 if( m_aContext.HideViewerToolbar ||
5833 ( m_aContext.Version > PDFWriter::PDF_1_3 && !m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle ) ||
5834 m_aContext.HideViewerMenubar ||
5835 m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
5836 m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
5837 m_aContext.OpenInFullScreenMode )
5839 aLine.append( "/ViewerPreferences<<" );
5840 if( m_aContext.HideViewerToolbar )
5841 aLine.append( "/HideToolbar true\n" );
5842 if( m_aContext.HideViewerMenubar )
5843 aLine.append( "/HideMenubar true\n" );
5844 if( m_aContext.HideViewerWindowControls )
5845 aLine.append( "/HideWindowUI true\n" );
5846 if( m_aContext.FitWindow )
5847 aLine.append( "/FitWindow true\n" );
5848 if( m_aContext.CenterWindow )
5849 aLine.append( "/CenterWindow true\n" );
5850 if( m_aContext.Version > PDFWriter::PDF_1_3 && !m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle )
5851 aLine.append( "/DisplayDocTitle true\n" );
5852 if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing )
5853 aLine.append( "/Direction/R2L\n" );
5854 if( m_aContext.OpenInFullScreenMode )
5855 switch( m_aContext.PDFDocumentMode )
5857 default :
5858 case PDFWriter::ModeDefault :
5859 aLine.append( "/NonFullScreenPageMode/UseNone\n" );
5860 break;
5861 case PDFWriter::UseOutlines :
5862 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
5863 break;
5864 case PDFWriter::UseThumbs :
5865 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
5866 break;
5868 aLine.append( ">>\n" );
5871 if( nOutlineDict )
5873 aLine.append( "/Outlines " );
5874 aLine.append( nOutlineDict );
5875 aLine.append( " 0 R\n" );
5877 if( nStructureDict )
5879 aLine.append( "/StructTreeRoot " );
5880 aLine.append( nStructureDict );
5881 aLine.append( " 0 R\n" );
5883 if( !m_aContext.DocumentLocale.Language.isEmpty() )
5885 /* PDF allows only RFC 3066, see above in emitStructure(). */
5886 LanguageTag aLanguageTag( m_aContext.DocumentLocale);
5887 OUString aLanguage, aScript, aCountry;
5888 aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
5889 if (!aLanguage.isEmpty())
5891 OUStringBuffer aLocBuf( 16 );
5892 aLocBuf.append( aLanguage );
5893 if( !aCountry.isEmpty() )
5895 aLocBuf.append( '-' );
5896 aLocBuf.append( aCountry );
5898 aLine.append( "/Lang" );
5899 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
5900 aLine.append( "\n" );
5903 if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 )
5905 aLine.append( "/MarkInfo<</Marked true>>\n" );
5907 if( m_aWidgets.size() > 0 )
5909 aLine.append( "/AcroForm<</Fields[\n" );
5910 int nWidgets = m_aWidgets.size();
5911 int nOut = 0;
5912 for( int j = 0; j < nWidgets; j++ )
5914 // output only root fields
5915 if( m_aWidgets[j].m_nParent < 1 )
5917 aLine.append( m_aWidgets[j].m_nObject );
5918 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
5921 aLine.append( "\n]" );
5923 #if !defined(ANDROID) && !defined(IOS)
5924 if (m_nSignatureObject != -1)
5925 aLine.append( "/SigFlags 3");
5926 #endif
5928 aLine.append( "/DR " );
5929 aLine.append( getResourceDictObj() );
5930 aLine.append( " 0 R" );
5931 // /NeedAppearances must not be used if PDF is signed
5932 if( m_bIsPDF_A1
5933 #if !defined(ANDROID) && !defined(IOS)
5934 || ( m_nSignatureObject != -1 )
5935 #endif
5937 aLine.append( ">>\n" );
5938 else
5939 aLine.append( "/NeedAppearances true>>\n" );
5942 //--->i59651
5943 //check if there is a Metadata object
5944 if( nOutputIntentObject )
5946 aLine.append("/OutputIntents[");
5947 aLine.append( nOutputIntentObject );
5948 aLine.append( " 0 R]" );
5951 if( nMetadataObject )
5953 aLine.append("/Metadata ");
5954 aLine.append( nMetadataObject );
5955 aLine.append( " 0 R" );
5957 //<----
5958 aLine.append( ">>\n"
5959 "endobj\n\n" );
5960 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5962 return true;
5965 #if !defined(ANDROID) && !defined(IOS)
5967 bool PDFWriterImpl::emitSignature()
5969 if( !updateObject( m_nSignatureObject ) )
5970 return false;
5972 OStringBuffer aLine( 0x5000 );
5973 aLine.append( m_nSignatureObject );
5974 aLine.append( " 0 obj\n" );
5975 aLine.append("<</Contents <" );
5977 sal_uInt64 nOffset = ~0U;
5978 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) );
5980 m_nSignatureContentOffset = nOffset + aLine.getLength();
5982 // reserve some space for the PKCS#7 object
5983 OStringBuffer aContentFiller( MAX_SIGNATURE_CONTENT_LENGTH );
5984 comphelper::string::padToLength(aContentFiller, MAX_SIGNATURE_CONTENT_LENGTH, '0');
5985 aLine.append( aContentFiller.makeStringAndClear() );
5986 aLine.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached");
5988 if( !m_aContext.DocumentInfo.Author.isEmpty() )
5990 aLine.append( "/Name" );
5991 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, m_nSignatureObject, aLine );
5994 aLine.append( " /M ");
5995 appendLiteralStringEncrypt( m_aCreationDateString, m_nSignatureObject, aLine );
5997 aLine.append( " /ByteRange [ 0 ");
5998 aLine.append( m_nSignatureContentOffset - 1, 10 );
5999 aLine.append( " " );
6000 aLine.append( m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1, 10 );
6001 aLine.append( " " );
6003 m_nSignatureLastByteRangeNoOffset = nOffset + aLine.getLength();
6005 // mark the last ByteRange no and add some space. Now, we don't know
6006 // how many bytes we need for this ByteRange value
6007 // The real value will be overwritten in the finalizeSignature method
6008 OStringBuffer aByteRangeFiller( 100 );
6009 comphelper::string::padToLength(aByteRangeFiller, 100, ' ');
6010 aLine.append( aByteRangeFiller.makeStringAndClear() );
6011 aLine.append(" /Filter/Adobe.PPKMS");
6013 //emit reason, location and contactinfo
6014 if ( !m_aContext.SignReason.isEmpty() )
6016 aLine.append("/Reason");
6017 appendUnicodeTextStringEncrypt( m_aContext.SignReason, m_nSignatureObject, aLine );
6020 if ( !m_aContext.SignLocation.isEmpty() )
6022 aLine.append("/Location");
6023 appendUnicodeTextStringEncrypt( m_aContext.SignLocation, m_nSignatureObject, aLine );
6026 if ( !m_aContext.SignContact.isEmpty() )
6028 aLine.append("/ContactInfo");
6029 appendUnicodeTextStringEncrypt( m_aContext.SignContact, m_nSignatureObject, aLine );
6032 aLine.append(" >>\nendobj\n\n" );
6034 if (!writeBuffer( aLine.getStr(), aLine.getLength() ))
6035 return false;
6037 return true;
6040 #if !defined(ANDROID) && !defined(IOS) && !defined(_WIN32)
6042 namespace {
6043 #if 0
6045 #endif
6047 char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo * /*slot*/, PRBool /*retry*/, void *arg)
6049 return PL_strdup(static_cast<char *>(arg));
6052 class HashContextScope {
6053 HASHContext *mpPtr;
6054 public:
6055 HashContextScope(HASHContext *pPtr) : mpPtr(pPtr) {}
6056 ~HashContextScope() { clear(); }
6057 void clear() { if (mpPtr) { HASH_Destroy(mpPtr); } mpPtr = NULL; }
6058 HASHContext *get() { return mpPtr; }
6061 // ASN.1 used in the (much simpler) time stamp request. From RFC3161
6062 // and other sources.
6065 AlgorithmIdentifier ::= SEQUENCE {
6066 algorithm OBJECT IDENTIFIER,
6067 parameters ANY DEFINED BY algorithm OPTIONAL }
6068 -- contains a value of the type
6069 -- registered for use with the
6070 -- algorithm object identifier value
6072 MessageImprint ::= SEQUENCE {
6073 hashAlgorithm AlgorithmIdentifier,
6074 hashedMessage OCTET STRING }
6077 typedef struct {
6078 SECAlgorithmID hashAlgorithm;
6079 SECItem hashedMessage;
6080 } MessageImprint;
6083 Extension ::= SEQUENCE {
6084 extnID OBJECT IDENTIFIER,
6085 critical BOOLEAN DEFAULT FALSE,
6086 extnValue OCTET STRING }
6089 typedef struct {
6090 SECItem extnID;
6091 SECItem critical;
6092 SECItem extnValue;
6093 } Extension;
6096 Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
6100 TSAPolicyId ::= OBJECT IDENTIFIER
6102 TimeStampReq ::= SEQUENCE {
6103 version INTEGER { v1(1) },
6104 messageImprint MessageImprint,
6105 --a hash algorithm OID and the hash value of the data to be
6106 --time-stamped
6107 reqPolicy TSAPolicyId OPTIONAL,
6108 nonce INTEGER OPTIONAL,
6109 certReq BOOLEAN DEFAULT FALSE,
6110 extensions [0] IMPLICIT Extensions OPTIONAL }
6113 typedef struct {
6114 SECItem version;
6115 MessageImprint messageImprint;
6116 SECItem reqPolicy;
6117 SECItem nonce;
6118 SECItem certReq;
6119 Extension *extensions;
6120 } TimeStampReq;
6122 // (Partial) ASN.1 for the time stamp responce. Very complicated. Pulled
6123 // together from varuous RFCs.
6126 Accuracy ::= SEQUENCE {
6127 seconds INTEGER OPTIONAL,
6128 millis [0] INTEGER (1..999) OPTIONAL,
6129 micros [1] INTEGER (1..999) OPTIONAL }
6131 PKIStatus ::= INTEGER {
6132 granted (0),
6133 -- when the PKIStatus contains the value zero a TimeStampToken, as requested, is present.
6134 grantedWithMods (1),
6135 -- when the PKIStatus contains the value one a TimeStampToken, with modifications, is present.
6136 rejection (2),
6137 waiting (3),
6138 revocationWarning (4),
6139 -- this message contains a warning that a revocation is
6140 -- imminent
6141 revocationNotification (5)
6142 -- notification that a revocation has occurred
6145 PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String
6146 -- text encoded as UTF-8 String [RFC3629] (note: each
6147 -- UTF8String MAY include an [RFC3066] language tag
6148 -- to indicate the language of the contained text
6149 -- see [RFC2482] for details)
6151 PKIFailureInfo ::= BIT STRING {
6152 badAlg (0),
6153 -- unrecognized or unsupported Algorithm Identifier
6154 badRequest (2),
6155 -- transaction not permitted or supported
6156 badDataFormat (5),
6157 -- the data submitted has the wrong format
6158 timeNotAvailable (14),
6159 -- the TSA's time source is not available
6160 unacceptedPolicy (15),
6161 -- the requested TSA policy is not supported by the TSA.
6162 unacceptedExtension (16),
6163 -- the requested extension is not supported by the TSA.
6164 addInfoNotAvailable (17),
6165 -- the additional information requested could not be understood
6166 -- or is not available
6167 systemFailure (25)
6168 -- the request cannot be handled due to system failure
6171 PKIStatusInfo ::= SEQUENCE {
6172 status PKIStatus,
6173 statusString PKIFreeText OPTIONAL,
6174 failInfo PKIFailureInfo OPTIONAL }
6176 ContentType ::= OBJECT IDENTIFIER
6178 ContentInfo ::= SEQUENCE {
6179 contentType ContentType,
6180 content [0] EXPLICIT ANY DEFINED BY contentType }
6182 CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) }
6184 DigestAlgorithmIdentifier ::= AlgorithmIdentifier
6186 DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
6188 ContentType ::= OBJECT IDENTIFIER
6190 EncapsulatedContentInfo ::= SEQUENCE {
6191 eContentType ContentType,
6192 eContent [0] EXPLICIT OCTET STRING OPTIONAL }
6194 OtherCertificateFormat ::= SEQUENCE {
6195 otherCertFormat OBJECT IDENTIFIER,
6196 otherCert ANY DEFINED BY otherCertFormat }
6198 CertificateChoices ::= CHOICE {
6199 certificate Certificate,
6200 extendedCertificate [0] IMPLICIT ExtendedCertificate, -- Obsolete
6201 v1AttrCert [1] IMPLICIT AttributeCertificateV1, -- Obsolete
6202 v2AttrCert [2] IMPLICIT AttributeCertificateV2,
6203 other [3] IMPLICIT OtherCertificateFormat }
6205 CertificateSet ::= SET OF CertificateChoices
6207 CertificateList ::= SEQUENCE {
6208 tbsCertList TBSCertList,
6209 signatureAlgorithm AlgorithmIdentifier,
6210 signatureValue BIT STRING }
6212 TBSCertList ::= SEQUENCE {
6213 version Version OPTIONAL,
6214 -- if present, MUST be v2
6215 signature AlgorithmIdentifier,
6216 issuer Name,
6217 thisUpdate Time,
6218 nextUpdate Time OPTIONAL,
6219 revokedCertificates SEQUENCE OF SEQUENCE {
6220 userCertificate CertificateSerialNumber,
6221 revocationDate Time,
6222 crlEntryExtensions Extensions OPTIONAL
6223 -- if present, version MUST be v2
6224 } OPTIONAL,
6225 crlExtensions [0] EXPLICIT Extensions OPTIONAL
6226 -- if present, version MUST be v2
6229 OtherRevocationInfoFormat ::= SEQUENCE {
6230 otherRevInfoFormat OBJECT IDENTIFIER,
6231 otherRevInfo ANY DEFINED BY otherRevInfoFormat }
6233 RevocationInfoChoice ::= CHOICE {
6234 crl CertificateList,
6235 other [1] IMPLICIT OtherRevocationInfoFormat }
6237 RevocationInfoChoices ::= SET OF RevocationInfoChoice
6239 SignerIdentifier ::= CHOICE {
6240 issuerAndSerialNumber IssuerAndSerialNumber,
6241 subjectKeyIdentifier [0] SubjectKeyIdentifier }
6243 AttributeValue ::= ANY
6245 Attribute ::= SEQUENCE {
6246 attrType OBJECT IDENTIFIER,
6247 attrValues SET OF AttributeValue }
6249 SignedAttributes ::= SET SIZE (1..MAX) OF Attribute
6251 SignatureValue ::= OCTET STRING
6253 UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute
6255 SignerInfo ::= SEQUENCE {
6256 version CMSVersion,
6257 sid SignerIdentifier,
6258 digestAlgorithm DigestAlgorithmIdentifier,
6259 signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
6260 signatureAlgorithm SignatureAlgorithmIdentifier,
6261 signature SignatureValue,
6262 unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
6264 SignerInfos ::= SET OF SignerInfo
6266 SignedData ::= SEQUENCE {
6267 version CMSVersion,
6268 digestAlgorithms DigestAlgorithmIdentifiers,
6269 encapContentInfo EncapsulatedContentInfo,
6270 certificates [0] IMPLICIT CertificateSet OPTIONAL,
6271 crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
6272 signerInfos SignerInfos }
6274 TimeStampToken ::= ContentInfo
6275 -- contentType is id-signedData as defined in [CMS]
6276 -- content is SignedData as defined in([CMS])
6277 -- eContentType within SignedData is id-ct-TSTInfo
6278 -- eContent within SignedData is TSTInfo
6280 TSTInfo ::= SEQUENCE {
6281 version INTEGER { v1(1) },
6282 policy TSAPolicyId,
6283 messageImprint MessageImprint,
6284 -- MUST have the same value as the similar field in
6285 -- TimeStampReq
6286 serialNumber INTEGER,
6287 -- Time-Stamping users MUST be ready to accommodate integers
6288 -- up to 160 bits.
6289 genTime GeneralizedTime,
6290 accuracy Accuracy OPTIONAL,
6291 ordering BOOLEAN DEFAULT FALSE,
6292 nonce INTEGER OPTIONAL,
6293 -- MUST be present if the similar field was present
6294 -- in TimeStampReq. In that case it MUST have the same value.
6295 tsa [0] GeneralName OPTIONAL,
6296 extensions [1] IMPLICIT Extensions OPTIONAL }
6298 TimeStampResp ::= SEQUENCE {
6299 status PKIStatusInfo,
6300 timeStampToken TimeStampToken OPTIONAL }
6303 const SEC_ASN1Template MessageImprint_Template[] =
6305 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(MessageImprint) },
6306 { SEC_ASN1_INLINE, offsetof(MessageImprint, hashAlgorithm), SECOID_AlgorithmIDTemplate, 0 },
6307 { SEC_ASN1_OCTET_STRING, offsetof(MessageImprint, hashedMessage), 0, 0 },
6308 { 0, 0, 0, 0 }
6311 const SEC_ASN1Template Extension_Template[] =
6313 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(Extension) },
6314 { SEC_ASN1_OBJECT_ID, offsetof(Extension, extnID), 0, 0 },
6315 { SEC_ASN1_BOOLEAN, offsetof(Extension, critical), 0, 0 },
6316 { SEC_ASN1_OCTET_STRING, offsetof(Extension, extnValue), 0, 0 },
6317 { 0, 0, 0, 0 }
6320 const SEC_ASN1Template Extensions_Template[] =
6322 { SEC_ASN1_SEQUENCE_OF, 0, Extension_Template, 0 }
6325 const SEC_ASN1Template TimeStampReq_Template[] =
6327 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(TimeStampReq) },
6328 { SEC_ASN1_INTEGER, offsetof(TimeStampReq, version), 0, 0 },
6329 { SEC_ASN1_INLINE, offsetof(TimeStampReq, messageImprint), MessageImprint_Template, 0 },
6330 { SEC_ASN1_OBJECT_ID | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, reqPolicy), 0, 0 },
6331 { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, nonce), 0, 0 },
6332 { SEC_ASN1_BOOLEAN | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, certReq), 0, 0 },
6333 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(TimeStampReq, extensions), Extensions_Template, 0 },
6334 { 0, 0, 0, 0 }
6337 typedef struct {
6338 SECItem status;
6339 SECItem statusString;
6340 SECItem failInfo;
6341 } PKIStatusInfo;
6343 const SEC_ASN1Template PKIStatusInfo_Template[] =
6345 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(PKIStatusInfo) },
6346 { SEC_ASN1_INTEGER, offsetof(PKIStatusInfo, status), 0, 0 },
6347 { SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, statusString), 0, 0 },
6348 { SEC_ASN1_BIT_STRING | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, failInfo), 0, 0 },
6349 { 0, 0, 0, 0 }
6352 const SEC_ASN1Template Any_Template[] =
6354 { SEC_ASN1_ANY, 0, NULL, sizeof(SECItem) }
6357 typedef struct {
6358 PKIStatusInfo status;
6359 SECItem timeStampToken;
6360 } TimeStampResp;
6362 const SEC_ASN1Template TimeStampResp_Template[] =
6364 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(TimeStampResp) },
6365 { SEC_ASN1_INLINE, offsetof(TimeStampResp, status), PKIStatusInfo_Template, 0 },
6366 { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, offsetof(TimeStampResp, timeStampToken), Any_Template, 0 },
6367 { 0, 0, 0, 0 }
6370 /* Will see if these are needed or not
6371 typedef struct {
6372 SECItem seconds;
6373 SECItem millis;
6374 SECItem micros;
6375 } Accuracy;
6377 const SEC_ASN1Template Integer_Template[] =
6379 { SEC_ASN1_INTEGER, 0, NULL, sizeof(SECItem) }
6382 const SEC_ASN1Template Accuracy_Template[] =
6384 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(Accuracy) },
6385 { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(Accuracy, seconds), 0, 0 },
6386 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(Accuracy, millis), Integer_Template, 0 },
6387 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 1, offsetof(Accuracy, micros), Integer_Template, 0 },
6388 { 0, 0, 0, 0 }
6392 size_t AppendToBuffer(char *ptr, size_t size, size_t nmemb, void *userdata)
6394 OStringBuffer *pBuffer = static_cast<OStringBuffer*>(userdata);
6395 pBuffer->append(ptr, size*nmemb);
6397 return size*nmemb;
6400 OUString PKIStatusToString(int n)
6402 switch (n)
6404 case 0: return OUString("granted");
6405 case 1: return OUString("grantedWithMods");
6406 case 2: return OUString("rejection");
6407 case 3: return OUString("waiting");
6408 case 4: return OUString("revocationWarning");
6409 case 5: return OUString("revocationNotification");
6410 default: return "unknown (" + OUString::number(n) + ")";
6414 OUString PKIStatusInfoToString(const PKIStatusInfo& rStatusInfo)
6416 OUString result;
6418 result += "{status=";
6419 if (rStatusInfo.status.len == 1)
6420 result += PKIStatusToString(rStatusInfo.status.data[0]);
6421 else
6422 result += "unknown (len=" + OUString::number(rStatusInfo.status.len);
6424 // FIXME: Perhaps look at rStatusInfo.statusString.data but note
6425 // that we of course can't assume it contains proper UTF-8. After
6426 // all, it is data from an external source. Also, RFC3161 claims
6427 // it should be a SEQUENCE (1..MAX) OF UTF8String, but another
6428 // source claimed it would be a single UTF8String, hmm?
6430 // FIXME: Worth it to decode failInfo to cleartext, probably not at least as long as this is only for a SAL_INFO
6432 result += "}";
6434 return result;
6437 // SEC_StringToOID() and NSS_CMSSignerInfo_AddUnauthAttr() are
6438 // not exported from libsmime, so copy them here. Sigh.
6440 SECStatus
6441 my_SEC_StringToOID(PLArenaPool *pool, SECItem *to, const char *from, PRUint32 len)
6443 PRUint32 decimal_numbers = 0;
6444 PRUint32 result_bytes = 0;
6445 SECStatus rv;
6446 PRUint8 result[1024];
6448 static const PRUint32 max_decimal = (0xffffffff / 10);
6449 static const char OIDstring[] = {"OID."};
6451 if (!from || !to) {
6452 PORT_SetError(SEC_ERROR_INVALID_ARGS);
6453 return SECFailure;
6455 if (!len) {
6456 len = PL_strlen(from);
6458 if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4)) {
6459 from += 4; /* skip leading "OID." if present */
6460 len -= 4;
6462 if (!len) {
6463 bad_data:
6464 PORT_SetError(SEC_ERROR_BAD_DATA);
6465 return SECFailure;
6467 do {
6468 PRUint32 decimal = 0;
6469 while (len > 0 && isdigit(*from)) {
6470 PRUint32 addend = (*from++ - '0');
6471 --len;
6472 if (decimal > max_decimal) /* overflow */
6473 goto bad_data;
6474 decimal = (decimal * 10) + addend;
6475 if (decimal < addend) /* overflow */
6476 goto bad_data;
6478 if (len != 0 && *from != '.') {
6479 goto bad_data;
6481 if (decimal_numbers == 0) {
6482 if (decimal > 2)
6483 goto bad_data;
6484 result[0] = decimal * 40;
6485 result_bytes = 1;
6486 } else if (decimal_numbers == 1) {
6487 if (decimal > 40)
6488 goto bad_data;
6489 result[0] += decimal;
6490 } else {
6491 /* encode the decimal number, */
6492 PRUint8 * rp;
6493 PRUint32 num_bytes = 0;
6494 PRUint32 tmp = decimal;
6495 while (tmp) {
6496 num_bytes++;
6497 tmp >>= 7;
6499 if (!num_bytes )
6500 ++num_bytes; /* use one byte for a zero value */
6501 if (num_bytes + result_bytes > sizeof result)
6502 goto bad_data;
6503 tmp = num_bytes;
6504 rp = result + result_bytes - 1;
6505 rp[tmp] = (PRUint8)(decimal & 0x7f);
6506 decimal >>= 7;
6507 while (--tmp > 0) {
6508 rp[tmp] = (PRUint8)(decimal | 0x80);
6509 decimal >>= 7;
6511 result_bytes += num_bytes;
6513 ++decimal_numbers;
6514 if (len > 0) { /* skip trailing '.' */
6515 ++from;
6516 --len;
6518 } while (len > 0);
6519 /* now result contains result_bytes of data */
6520 if (to->data && to->len >= result_bytes) {
6521 PORT_Memcpy(to->data, result, to->len = result_bytes);
6522 rv = SECSuccess;
6523 } else {
6524 SECItem result_item = {siBuffer, NULL, 0 };
6525 result_item.data = result;
6526 result_item.len = result_bytes;
6527 rv = SECITEM_CopyItem(pool, to, &result_item);
6529 return rv;
6532 NSSCMSAttribute *
6533 my_NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only)
6535 SECOidData *oid;
6536 NSSCMSAttribute *attr1, *attr2;
6538 if (attrs == NULL)
6539 return NULL;
6541 oid = SECOID_FindOIDByTag(oidtag);
6542 if (oid == NULL)
6543 return NULL;
6545 while ((attr1 = *attrs++) != NULL) {
6546 if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data,
6547 oid->oid.data,
6548 oid->oid.len) == 0)
6549 break;
6552 if (attr1 == NULL)
6553 return NULL;
6555 if (!only)
6556 return attr1;
6558 while ((attr2 = *attrs++) != NULL) {
6559 if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data,
6560 oid->oid.data,
6561 oid->oid.len) == 0)
6562 break;
6565 if (attr2 != NULL)
6566 return NULL;
6568 return attr1;
6571 SECStatus
6572 my_NSS_CMSArray_Add(PLArenaPool *poolp, void ***array, void *obj)
6574 void **p;
6575 int n;
6576 void **dest;
6578 PORT_Assert(array != NULL);
6579 if (array == NULL)
6580 return SECFailure;
6582 if (*array == NULL) {
6583 dest = static_cast<void **>(PORT_ArenaAlloc(poolp, 2 * sizeof(void *)));
6584 n = 0;
6585 } else {
6586 n = 0; p = *array;
6587 while (*p++)
6588 n++;
6589 dest = static_cast<void **>(PORT_ArenaGrow (poolp,
6590 *array,
6591 (n + 1) * sizeof(void *),
6592 (n + 2) * sizeof(void *)));
6595 if (dest == NULL)
6596 return SECFailure;
6598 dest[n] = obj;
6599 dest[n+1] = NULL;
6600 *array = dest;
6601 return SECSuccess;
6604 SECOidTag
6605 my_NSS_CMSAttribute_GetType(NSSCMSAttribute *attr)
6607 SECOidData *typetag;
6609 typetag = SECOID_FindOID(&(attr->type));
6610 if (typetag == NULL)
6611 return SEC_OID_UNKNOWN;
6613 return typetag->offset;
6616 SECStatus
6617 my_NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr)
6619 NSSCMSAttribute *oattr;
6620 void *mark;
6621 SECOidTag type;
6623 mark = PORT_ArenaMark(poolp);
6625 /* find oidtag of attr */
6626 type = my_NSS_CMSAttribute_GetType(attr);
6628 /* see if we have one already */
6629 oattr = my_NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
6630 PORT_Assert (oattr == NULL);
6631 if (oattr != NULL)
6632 goto loser; /* XXX or would it be better to replace it? */
6634 /* no, shove it in */
6635 if (my_NSS_CMSArray_Add(poolp, reinterpret_cast<void ***>(attrs), (void *)attr) != SECSuccess)
6636 goto loser;
6638 PORT_ArenaUnmark(poolp, mark);
6639 return SECSuccess;
6641 loser:
6642 PORT_ArenaRelease(poolp, mark);
6643 return SECFailure;
6646 SECStatus
6647 my_NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
6649 return my_NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
6652 NSSCMSMessage *CreateCMSMessage(PRTime time,
6653 NSSCMSSignedData **cms_sd,
6654 NSSCMSSignerInfo **cms_signer,
6655 CERTCertificate *cert,
6656 SECItem *digest)
6658 NSSCMSMessage *result = NSS_CMSMessage_Create(NULL);
6659 if (!result)
6661 SAL_WARN("vcl.pdfwriter", "NSS_CMSMessage_Create failed");
6662 return NULL;
6665 *cms_sd = NSS_CMSSignedData_Create(result);
6666 if (!*cms_sd)
6668 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_Create failed");
6669 NSS_CMSMessage_Destroy(result);
6670 return NULL;
6673 NSSCMSContentInfo *cms_cinfo = NSS_CMSMessage_GetContentInfo(result);
6674 if (NSS_CMSContentInfo_SetContent_SignedData(result, cms_cinfo, *cms_sd) != SECSuccess)
6676 SAL_WARN("vcl.pdfwriter", "NSS_CMSContentInfo_SetContent_SignedData failed");
6677 NSS_CMSSignedData_Destroy(*cms_sd);
6678 NSS_CMSMessage_Destroy(result);
6679 return NULL;
6682 cms_cinfo = NSS_CMSSignedData_GetContentInfo(*cms_sd);
6684 // Attach NULL data as detached data
6685 if (NSS_CMSContentInfo_SetContent_Data(result, cms_cinfo, NULL, PR_TRUE) != SECSuccess)
6687 SAL_WARN("vcl.pdfwriter", "NSS_CMSContentInfo_SetContent_Data failed");
6688 NSS_CMSSignedData_Destroy(*cms_sd);
6689 NSS_CMSMessage_Destroy(result);
6690 return NULL;
6693 *cms_signer = NSS_CMSSignerInfo_Create(result, cert, SEC_OID_SHA1);
6694 if (!*cms_signer)
6696 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_Create failed");
6697 NSS_CMSSignedData_Destroy(*cms_sd);
6698 NSS_CMSMessage_Destroy(result);
6699 return NULL;
6702 if (NSS_CMSSignerInfo_AddSigningTime(*cms_signer, time) != SECSuccess)
6704 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_AddSigningTime failed");
6705 NSS_CMSSignedData_Destroy(*cms_sd);
6706 NSS_CMSMessage_Destroy(result);
6707 return NULL;
6710 if (NSS_CMSSignerInfo_IncludeCerts(*cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess)
6712 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_IncludeCerts failed");
6713 NSS_CMSSignedData_Destroy(*cms_sd);
6714 NSS_CMSMessage_Destroy(result);
6715 return NULL;
6718 if (NSS_CMSSignedData_AddCertificate(*cms_sd, cert) != SECSuccess)
6720 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_AddCertificate failed");
6721 NSS_CMSSignedData_Destroy(*cms_sd);
6722 NSS_CMSMessage_Destroy(result);
6723 return NULL;
6726 if (NSS_CMSSignedData_AddSignerInfo(*cms_sd, *cms_signer) != SECSuccess)
6728 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_AddSignerInfo failed");
6729 NSS_CMSSignedData_Destroy(*cms_sd);
6730 NSS_CMSMessage_Destroy(result);
6731 return NULL;
6734 if (NSS_CMSSignedData_SetDigestValue(*cms_sd, SEC_OID_SHA1, digest) != SECSuccess)
6736 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_SetDigestValue failed");
6737 NSS_CMSSignedData_Destroy(*cms_sd);
6738 NSS_CMSMessage_Destroy(result);
6739 return NULL;
6742 return result;
6745 #if 0
6747 #endif
6748 } // anonymous namespace
6750 #endif // !defined(ANDROID) && !defined(IOS) && !defined(_WIN32)
6752 #ifdef _WIN32
6754 typedef BOOL (WINAPI *PointerTo_CryptRetrieveTimeStamp)(LPCWSTR wszUrl,
6755 DWORD dwRetrievalFlags,
6756 DWORD dwTimeout,
6757 LPCSTR pszHashId,
6758 const CRYPT_TIMESTAMP_PARA *pPara,
6759 const BYTE *pbData,
6760 DWORD cbData,
6761 PCRYPT_TIMESTAMP_CONTEXT *ppTsContext,
6762 PCCERT_CONTEXT *ppTsSigner,
6763 HCERTSTORE phStore);
6765 #endif
6767 bool PDFWriterImpl::finalizeSignature()
6770 if (!m_aContext.SignCertificate.is())
6771 return false;
6773 // 1- calculate last ByteRange value
6774 sal_uInt64 nOffset = ~0U;
6775 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) );
6777 sal_Int64 nLastByteRangeNo = nOffset - (m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1);
6779 // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position
6780 sal_uInt64 nWritten = 0;
6781 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureLastByteRangeNoOffset) ) );
6782 OStringBuffer aByteRangeNo( 256 );
6783 aByteRangeNo.append( nLastByteRangeNo, 10);
6784 aByteRangeNo.append( " ]" );
6786 if (m_aFile.write(aByteRangeNo.getStr(), aByteRangeNo.getLength(), nWritten) != osl::File::E_None)
6788 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
6789 return false;
6792 // 3- create the PKCS#7 object using NSS
6793 com::sun::star::uno::Sequence< sal_Int8 > derEncoded = m_aContext.SignCertificate->getEncoded();
6795 if (!derEncoded.hasElements())
6796 return false;
6798 sal_Int8* n_derArray = derEncoded.getArray();
6799 sal_Int32 n_derLength = derEncoded.getLength();
6801 #ifndef _WIN32
6803 CERTCertificate *cert = CERT_DecodeCertFromPackage(reinterpret_cast<char *>(n_derArray), n_derLength);
6805 if (!cert)
6807 SAL_WARN("vcl.pdfwriter", "CERT_DecodeCertFromPackage failed");
6808 return false;
6811 // Prepare buffer and calculate PDF file digest
6812 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, 0)) );
6814 HashContextScope hc(HASH_Create(HASH_AlgSHA1));
6815 if (!hc.get())
6817 SAL_WARN("vcl.pdfwriter", "HASH_Create failed");
6818 return false;
6821 HASH_Begin(hc.get());
6823 boost::scoped_array<char> buffer(new char[m_nSignatureContentOffset + 1]);
6824 sal_uInt64 bytesRead;
6826 //FIXME: Check if SHA1 is calculated from the correct byterange
6827 CHECK_RETURN( (osl::File::E_None == m_aFile.read(buffer.get(), m_nSignatureContentOffset - 1 , bytesRead)) );
6828 if (bytesRead != (sal_uInt64)m_nSignatureContentOffset - 1)
6829 SAL_WARN("vcl.pdfwriter", "First buffer read failed");
6831 HASH_Update(hc.get(), reinterpret_cast<const unsigned char*>(buffer.get()), bytesRead);
6833 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1)) );
6834 buffer.reset(new char[nLastByteRangeNo + 1]);
6835 CHECK_RETURN( (osl::File::E_None == m_aFile.read(buffer.get(), nLastByteRangeNo, bytesRead)) );
6836 if (bytesRead != (sal_uInt64) nLastByteRangeNo)
6837 SAL_WARN("vcl.pdfwriter", "Second buffer read failed");
6839 HASH_Update(hc.get(), reinterpret_cast<const unsigned char*>(buffer.get()), bytesRead);
6841 SECItem digest;
6842 unsigned char hash[SHA1_LENGTH];
6843 digest.data = hash;
6844 HASH_End(hc.get(), digest.data, &digest.len, SHA1_LENGTH);
6845 hc.clear();
6847 #ifdef DBG_UTIL
6849 FILE *out = fopen("PDFWRITER.hash.data", "wb");
6850 fwrite(hash, SHA1_LENGTH, 1, out);
6851 fclose(out);
6853 #endif
6855 PRTime now = PR_Now();
6856 NSSCMSSignedData *cms_sd;
6857 NSSCMSSignerInfo *cms_signer;
6858 NSSCMSMessage *cms_msg = CreateCMSMessage(now, &cms_sd, &cms_signer, cert, &digest);
6859 if (!cms_msg)
6860 return false;
6862 char *pass(strdup(OUStringToOString( m_aContext.SignPassword, RTL_TEXTENCODING_UTF8 ).getStr()));
6864 TimeStampReq src;
6865 OStringBuffer response_buffer;
6866 TimeStampResp response;
6867 SECItem response_item;
6868 NSSCMSAttribute timestamp;
6869 SECItem values[2];
6870 SECItem *valuesp[2];
6871 valuesp[0] = values;
6872 valuesp[1] = NULL;
6873 SECOidData typetag;
6875 if( !m_aContext.SignTSA.isEmpty() )
6877 // Create another CMS message with the same contents as cms_msg, because it doesn't seem
6878 // possible to encode a message twice (once to get something to timestamp, and then after
6879 // adding the timestamp attribute).
6881 NSSCMSSignedData *ts_cms_sd;
6882 NSSCMSSignerInfo *ts_cms_signer;
6883 NSSCMSMessage *ts_cms_msg = CreateCMSMessage(now, &ts_cms_sd, &ts_cms_signer, cert, &digest);
6884 if (!ts_cms_msg)
6886 free(pass);
6887 return false;
6890 SECItem ts_cms_output;
6891 ts_cms_output.data = 0;
6892 ts_cms_output.len = 0;
6893 PLArenaPool *ts_arena = PORT_NewArena(10000);
6894 NSSCMSEncoderContext *ts_cms_ecx;
6895 ts_cms_ecx = NSS_CMSEncoder_Start(ts_cms_msg, NULL, NULL, &ts_cms_output, ts_arena, PDFSigningPKCS7PasswordCallback, pass, NULL, NULL, NULL, NULL);
6897 if (NSS_CMSEncoder_Finish(ts_cms_ecx) != SECSuccess)
6899 SAL_WARN("vcl.pdfwriter", "NSS_CMSEncoder_Finish failed");
6900 free(pass);
6901 return false;
6904 // I have compared the ts_cms_output produced here with the cms_output produced below, with
6905 // the DONTCALLADDUNAUTHATTR env var set (i.e. without actually calling
6906 // my_NSS_CMSSignerInfo_AddUnauthAttr()), and they are identical.
6908 #ifdef DBG_UTIL
6910 FILE *out = fopen("PDFWRITER.ts_cms.data", "wb");
6911 fwrite(ts_cms_output.data, ts_cms_output.len, 1, out);
6912 fclose(out);
6914 #endif
6916 HashContextScope ts_hc(HASH_Create(HASH_AlgSHA1));
6917 if (!ts_hc.get())
6919 SAL_WARN("vcl.pdfwriter", "HASH_Create failed");
6920 free(pass);
6921 return false;
6924 HASH_Begin(ts_hc.get());
6925 HASH_Update(ts_hc.get(), ts_cms_signer->encDigest.data, ts_cms_signer->encDigest.len);
6926 SECItem ts_digest;
6927 unsigned char ts_hash[SHA1_LENGTH];
6928 ts_digest.type = siBuffer;
6929 ts_digest.data = ts_hash;
6930 HASH_End(ts_hc.get(), ts_digest.data, &ts_digest.len, SHA1_LENGTH);
6931 ts_hc.clear();
6933 #ifdef DBG_UTIL
6935 FILE *out = fopen("PDFWRITER.ts_hash.data", "wb");
6936 fwrite(ts_hash, SHA1_LENGTH, 1, out);
6937 fclose(out);
6939 #endif
6941 unsigned char cOne = 1;
6942 src.version.type = siUnsignedInteger;
6943 src.version.data = &cOne;
6944 src.version.len = sizeof(cOne);
6946 src.messageImprint.hashAlgorithm.algorithm.data = NULL;
6947 src.messageImprint.hashAlgorithm.parameters.data = NULL;
6948 SECOID_SetAlgorithmID(NULL, &src.messageImprint.hashAlgorithm, SEC_OID_SHA1, NULL);
6949 src.messageImprint.hashedMessage = ts_digest;
6951 src.reqPolicy.type = siBuffer;
6952 src.reqPolicy.data = NULL;
6953 src.reqPolicy.len = 0;
6955 unsigned int nNonce = comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32);
6956 src.nonce.type = siUnsignedInteger;
6957 src.nonce.data = reinterpret_cast<unsigned char*>(&nNonce);
6958 src.nonce.len = sizeof(nNonce);
6960 src.certReq.type = siUnsignedInteger;
6961 src.certReq.data = &cOne;
6962 src.certReq.len = sizeof(cOne);
6964 src.extensions = NULL;
6966 SECItem* timestamp_request = SEC_ASN1EncodeItem(NULL, NULL, &src, TimeStampReq_Template);
6967 if (timestamp_request == NULL)
6969 SAL_WARN("vcl.pdfwriter", "SEC_ASN1EncodeItem failed");
6970 free(pass);
6971 return false;
6974 if (timestamp_request->data == NULL)
6976 SAL_WARN("vcl.pdfwriter", "SEC_ASN1EncodeItem succeeded but got NULL data");
6977 free(pass);
6978 SECITEM_FreeItem(timestamp_request, PR_TRUE);
6979 return false;
6982 SAL_INFO("vcl.pdfwriter", "request length=" << timestamp_request->len);
6984 #ifdef DBG_UTIL
6986 FILE *out = fopen("PDFWRITER.timestampreq.data", "wb");
6987 fwrite(timestamp_request->data, timestamp_request->len, 1, out);
6988 fclose(out);
6990 #endif
6992 // Send time stamp request to TSA server, receive response
6994 CURL* curl = curl_easy_init();
6995 CURLcode rc;
6996 struct curl_slist* slist = NULL;
6998 if (!curl)
7000 SAL_WARN("vcl.pdfwriter", "curl_easy_init failed");
7001 free(pass);
7002 SECITEM_FreeItem(timestamp_request, PR_TRUE);
7003 return false;
7006 SAL_INFO("vcl.pdfwriter", "Setting curl to verbose: " << (curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L) == CURLE_OK ? "OK" : "FAIL"));
7008 if ((rc = curl_easy_setopt(curl, CURLOPT_URL, OUStringToOString(m_aContext.SignTSA, RTL_TEXTENCODING_UTF8).getStr())) != CURLE_OK)
7010 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_URL) failed: " << curl_easy_strerror(rc));
7011 free(pass);
7012 curl_easy_cleanup(curl);
7013 SECITEM_FreeItem(timestamp_request, PR_TRUE);
7014 return false;
7017 slist = curl_slist_append(slist, "Content-Type: application/timestamp-query");
7018 slist = curl_slist_append(slist, "Accept: application/timestamp-reply");
7020 if ((rc = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist)) != CURLE_OK)
7022 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_HTTPHEADER) failed: " << curl_easy_strerror(rc));
7023 free(pass);
7024 curl_slist_free_all(slist);
7025 curl_easy_cleanup(curl);
7026 SECITEM_FreeItem(timestamp_request, PR_TRUE);
7027 return false;
7030 if ((rc = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<long>(timestamp_request->len))) != CURLE_OK ||
7031 (rc = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, timestamp_request->data)) != CURLE_OK)
7033 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_POSTFIELDSIZE or CURLOPT_POSTFIELDS) failed: " << curl_easy_strerror(rc));
7034 free(pass);
7035 curl_easy_cleanup(curl);
7036 SECITEM_FreeItem(timestamp_request, PR_TRUE);
7037 return false;
7040 if ((rc = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buffer)) != CURLE_OK ||
7041 (rc = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, AppendToBuffer)) != CURLE_OK)
7043 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_WRITEDATA or CURLOPT_WRITEFUNCTION) failed: " << curl_easy_strerror(rc));
7044 free(pass);
7045 curl_easy_cleanup(curl);
7046 SECITEM_FreeItem(timestamp_request, PR_TRUE);
7047 return false;
7050 if ((rc = curl_easy_setopt(curl, CURLOPT_POST, 1l)) != CURLE_OK)
7052 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_POST) failed: " << curl_easy_strerror(rc));
7053 free(pass);
7054 curl_easy_cleanup(curl);
7055 SECITEM_FreeItem(timestamp_request, PR_TRUE);
7056 return false;
7059 char error_buffer[CURL_ERROR_SIZE];
7060 if ((rc = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer)) != CURLE_OK)
7062 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_ERRORBUFFER) failed: " << curl_easy_strerror(rc));
7063 free(pass);
7064 curl_easy_cleanup(curl);
7065 SECITEM_FreeItem(timestamp_request, PR_TRUE);
7066 return false;
7069 // Use a ten second timeout
7070 if ((rc = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10l)) != CURLE_OK ||
7071 (rc = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10l)) != CURLE_OK)
7073 SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_TIMEOUT or CURLOPT_CONNECTTIMEOUT) failed: " << curl_easy_strerror(rc));
7074 free(pass);
7075 curl_easy_cleanup(curl);
7076 SECITEM_FreeItem(timestamp_request, PR_TRUE);
7077 return false;
7080 if (curl_easy_perform(curl) != CURLE_OK)
7082 SAL_WARN("vcl.pdfwriter", "curl_easy_perform failed: " << error_buffer);
7083 free(pass);
7084 curl_easy_cleanup(curl);
7085 SECITEM_FreeItem(timestamp_request, PR_TRUE);
7086 return false;
7089 SAL_INFO("vcl.pdfwriter", "PDF signing: got response, length=" << response_buffer.getLength());
7091 #ifdef DBG_UTIL
7093 FILE *out = fopen("PDFWRITER.reply.data", "wb");
7094 fwrite(response_buffer.getStr(), response_buffer.getLength(), 1, out);
7095 fclose(out);
7097 #endif
7099 curl_slist_free_all(slist);
7100 curl_easy_cleanup(curl);
7101 SECITEM_FreeItem(timestamp_request, PR_TRUE);
7103 memset(&response, 0, sizeof(response));
7105 response_item.type = siBuffer;
7106 response_item.data = reinterpret_cast<unsigned char*>(const_cast<char*>(response_buffer.getStr()));
7107 response_item.len = response_buffer.getLength();
7109 if (SEC_ASN1DecodeItem(NULL, &response, TimeStampResp_Template, &response_item) != SECSuccess)
7111 SAL_WARN("vcl.pdfwriter", "SEC_ASN1DecodeItem failed");
7112 free(pass);
7113 return false;
7116 SAL_INFO("vcl.pdfwriter", "TimeStampResp received and decoded, status=" << PKIStatusInfoToString(response.status));
7118 if (response.status.status.len != 1 ||
7119 (response.status.status.data[0] != 0 && response.status.status.data[0] != 1))
7121 SAL_WARN("vcl.pdfwriter", "Timestamp request was not granted");
7122 free(pass);
7123 return false;
7126 // timestamp.type filled in below
7128 // Not sure if we actually need two entries in the values array, now when valuesp is an
7129 // array too, the pointer to the values array followed by a null pointer. But I don't feel
7130 // like experimenting.
7131 values[0] = response.timeStampToken;
7132 values[1].type = siBuffer;
7133 values[1].data = NULL;
7134 values[1].len = 0;
7136 timestamp.values = valuesp;
7138 typetag.oid.data = NULL;
7139 // id-aa-timeStampToken OBJECT IDENTIFIER ::= { iso(1)
7140 // member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)
7141 // smime(16) aa(2) 14 }
7142 if (my_SEC_StringToOID(NULL, &typetag.oid, "1.2.840.113549.1.9.16.2.14", 0) != SECSuccess)
7144 SAL_WARN("vcl.pdfwriter", "SEC_StringToOID failed");
7145 free(pass);
7146 return false;
7148 typetag.offset = SEC_OID_UNKNOWN; // ???
7149 typetag.desc = "id-aa-timeStampToken";
7150 typetag.mechanism = CKM_SHA_1; // ???
7151 typetag.supportedExtension = UNSUPPORTED_CERT_EXTENSION; // ???
7152 timestamp.typeTag = &typetag;
7154 timestamp.type = typetag.oid; // ???
7156 timestamp.encoded = PR_TRUE; // ???
7158 #ifdef DBG_UTIL
7159 if (getenv("DONTCALLADDUNAUTHATTR"))
7161 else
7162 #endif
7163 if (my_NSS_CMSSignerInfo_AddUnauthAttr(cms_signer, &timestamp) != SECSuccess)
7165 SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_AddUnauthAttr failed");
7166 free(pass);
7167 return false;
7171 SECItem cms_output;
7172 cms_output.data = 0;
7173 cms_output.len = 0;
7174 PLArenaPool *arena = PORT_NewArena(10000);
7175 NSSCMSEncoderContext *cms_ecx;
7177 // Possibly it would work to even just pass NULL for the password callback function and its
7178 // argument here. After all, at least with the hardware token and associated software I tested
7179 // with, the software itself pops up a dialog asking for the PIN (password). But I am not going
7180 // to test it and risk locking up my token...
7182 cms_ecx = NSS_CMSEncoder_Start(cms_msg, NULL, NULL, &cms_output, arena, PDFSigningPKCS7PasswordCallback, pass, NULL, NULL, NULL, NULL);
7184 if (!cms_ecx)
7186 SAL_WARN("vcl.pdfwriter", "NSS_CMSEncoder_Start failed");
7187 free(pass);
7188 return false;
7191 if (NSS_CMSEncoder_Finish(cms_ecx) != SECSuccess)
7193 SAL_WARN("vcl.pdfwriter", "NSS_CMSEncoder_Finish failed");
7194 free(pass);
7195 return false;
7198 free(pass);
7200 #ifdef DBG_UTIL
7202 FILE *out = fopen("PDFWRITER.cms.data", "wb");
7203 fwrite(cms_output.data, cms_output.len, 1, out);
7204 fclose(out);
7206 #endif
7208 if (cms_output.len*2 > MAX_SIGNATURE_CONTENT_LENGTH)
7210 SAL_WARN("vcl.pdfwriter", "Signature requires more space (" << cms_output.len*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH << ")");
7211 NSS_CMSMessage_Destroy(cms_msg);
7212 return false;
7215 OStringBuffer cms_hexbuffer;
7217 for (unsigned int i = 0; i < cms_output.len ; i++)
7218 appendHex(cms_output.data[i], cms_hexbuffer);
7220 assert(cms_hexbuffer.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH);
7222 // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
7223 nWritten = 0;
7224 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset)) );
7225 m_aFile.write(cms_hexbuffer.getStr(), cms_hexbuffer.getLength(), nWritten);
7227 NSS_CMSMessage_Destroy(cms_msg);
7229 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
7230 return true;
7232 #else
7234 // Prepare buffer and calculate PDF file digest
7235 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, 0)) );
7237 boost::scoped_array<char> buffer1(new char[m_nSignatureContentOffset - 1]);
7238 sal_uInt64 bytesRead1;
7240 if (osl::File::E_None != m_aFile.read(buffer1.get(), m_nSignatureContentOffset - 1 , bytesRead1) ||
7241 bytesRead1 != (sal_uInt64)m_nSignatureContentOffset - 1)
7243 SAL_WARN("vcl.pdfwriter", "First buffer read failed");
7244 return false;
7247 boost::scoped_array<char> buffer2(new char[nLastByteRangeNo]);
7248 sal_uInt64 bytesRead2;
7250 if (osl::File::E_None != m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1) ||
7251 osl::File::E_None != m_aFile.read(buffer2.get(), nLastByteRangeNo, bytesRead2) ||
7252 bytesRead2 != (sal_uInt64) nLastByteRangeNo)
7254 SAL_WARN("vcl.pdfwriter", "Second buffer read failed");
7255 return false;
7258 OString pass = OUStringToOString( m_aContext.SignPassword, RTL_TEXTENCODING_UTF8 );
7260 PCCERT_CONTEXT pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, reinterpret_cast<const BYTE*>(n_derArray), n_derLength);
7261 if (pCertContext == NULL)
7263 SAL_WARN("vcl.pdfwriter", "CertCreateCertificateContext failed: " << WindowsErrorString(GetLastError()));
7264 return false;
7267 CRYPT_SIGN_MESSAGE_PARA aPara;
7269 memset(&aPara, 0, sizeof(aPara));
7270 aPara.cbSize = sizeof(aPara);
7271 aPara.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
7272 aPara.pSigningCert = pCertContext;
7273 aPara.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA;
7274 aPara.HashAlgorithm.Parameters.cbData = 0;
7275 aPara.cMsgCert = 1;
7276 aPara.rgpMsgCert = &pCertContext;
7278 HCRYPTPROV hCryptProv;
7279 DWORD nKeySpec;
7280 BOOL bFreeNeeded;
7282 if (!CryptAcquireCertificatePrivateKey(pCertContext,
7283 CRYPT_ACQUIRE_CACHE_FLAG,
7284 NULL,
7285 &hCryptProv,
7286 &nKeySpec,
7287 &bFreeNeeded))
7289 SAL_WARN("vcl.pdfwriter", "CryptAcquireCertificatePrivateKey failed: " << WindowsErrorString(GetLastError()));
7290 CertFreeCertificateContext(pCertContext);
7291 return false;
7293 assert(!bFreeNeeded);
7295 CMSG_SIGNER_ENCODE_INFO aSignerInfo;
7297 memset(&aSignerInfo, 0, sizeof(aSignerInfo));
7298 aSignerInfo.cbSize = sizeof(aSignerInfo);
7299 aSignerInfo.pCertInfo = pCertContext->pCertInfo;
7300 aSignerInfo.hCryptProv = hCryptProv;
7301 aSignerInfo.dwKeySpec = nKeySpec;
7302 aSignerInfo.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA;
7303 aSignerInfo.HashAlgorithm.Parameters.cbData = 0;
7305 CMSG_SIGNED_ENCODE_INFO aSignedInfo;
7306 memset(&aSignedInfo, 0, sizeof(aSignedInfo));
7307 aSignedInfo.cbSize = sizeof(aSignedInfo);
7308 aSignedInfo.cSigners = 1;
7309 aSignedInfo.rgSigners = &aSignerInfo;
7311 CERT_BLOB aCertBlob;
7313 aCertBlob.cbData = pCertContext->cbCertEncoded;
7314 aCertBlob.pbData = pCertContext->pbCertEncoded;
7316 aSignedInfo.cCertEncoded = 1;
7317 aSignedInfo.rgCertEncoded = &aCertBlob;
7319 HCRYPTMSG hMsg;
7320 if (!(hMsg = CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
7321 CMSG_DETACHED_FLAG,
7322 CMSG_SIGNED,
7323 &aSignedInfo,
7324 NULL,
7325 NULL)))
7327 SAL_WARN("vcl.pdfwriter", "CryptMsgOpenToEncode failed: " << WindowsErrorString(GetLastError()));
7328 CertFreeCertificateContext(pCertContext);
7329 return false;
7332 if (!CryptMsgUpdate(hMsg, (const BYTE *)buffer1.get(), bytesRead1, FALSE) ||
7333 !CryptMsgUpdate(hMsg, (const BYTE *)buffer2.get(), bytesRead2, TRUE))
7335 SAL_WARN("vcl.pdfwriter", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
7336 CryptMsgClose(hMsg);
7337 CertFreeCertificateContext(pCertContext);
7338 return false;
7341 PCRYPT_TIMESTAMP_CONTEXT pTsContext = NULL;
7343 if( !m_aContext.SignTSA.isEmpty() )
7345 PointerTo_CryptRetrieveTimeStamp crts = (PointerTo_CryptRetrieveTimeStamp) GetProcAddress(LoadLibrary("crypt32.dll"), "CryptRetrieveTimeStamp");
7346 if (!crts)
7348 SAL_WARN("vcl.pdfwriter", "Could not find the CryptRetrieveTimeStamp function in crypt32.dll: " << WindowsErrorString(GetLastError()));
7349 CryptMsgClose(hMsg);
7350 CertFreeCertificateContext(pCertContext);
7351 return false;
7354 HCRYPTMSG hDecodedMsg;
7355 if (!(hDecodedMsg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
7356 CMSG_DETACHED_FLAG,
7357 CMSG_SIGNED,
7358 NULL,
7359 NULL,
7360 NULL)))
7362 SAL_WARN("vcl.pdfwriter", "CryptMsgOpenToDecode failed: " << WindowsErrorString(GetLastError()));
7363 CryptMsgClose(hMsg);
7364 CertFreeCertificateContext(pCertContext);
7365 return false;
7368 DWORD nTsSigLen = 0;
7370 if (!CryptMsgGetParam(hMsg, CMSG_BARE_CONTENT_PARAM, 0, NULL, &nTsSigLen))
7372 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
7373 CryptMsgClose(hDecodedMsg);
7374 CryptMsgClose(hMsg);
7375 CertFreeCertificateContext(pCertContext);
7376 return false;
7379 SAL_INFO("vcl.pdfwriter", "nTsSigLen=" << nTsSigLen);
7381 boost::scoped_array<BYTE> pTsSig(new BYTE[nTsSigLen]);
7383 if (!CryptMsgGetParam(hMsg, CMSG_BARE_CONTENT_PARAM, 0, pTsSig.get(), &nTsSigLen))
7385 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
7386 CryptMsgClose(hDecodedMsg);
7387 CryptMsgClose(hMsg);
7388 CertFreeCertificateContext(pCertContext);
7389 return false;
7392 if (!CryptMsgUpdate(hDecodedMsg, pTsSig.get(), nTsSigLen, TRUE))
7394 SAL_WARN("vcl.pdfwriter", "CryptMsgUpdate failed: " << WindowsErrorString(GetLastError()));
7395 CryptMsgClose(hDecodedMsg);
7396 CryptMsgClose(hMsg);
7397 CertFreeCertificateContext(pCertContext);
7398 return false;
7401 DWORD nDecodedSignerInfoLen = 0;
7402 if (!CryptMsgGetParam(hDecodedMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &nDecodedSignerInfoLen))
7404 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsErrorString(GetLastError()));
7405 CryptMsgClose(hDecodedMsg);
7406 CryptMsgClose(hMsg);
7407 CertFreeCertificateContext(pCertContext);
7408 return false;
7411 boost::scoped_array<BYTE> pDecodedSignerInfoBuf(new BYTE[nDecodedSignerInfoLen]);
7413 if (!CryptMsgGetParam(hDecodedMsg, CMSG_SIGNER_INFO_PARAM, 0, pDecodedSignerInfoBuf.get(), &nDecodedSignerInfoLen))
7415 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsErrorString(GetLastError()));
7416 CryptMsgClose(hDecodedMsg);
7417 CryptMsgClose(hMsg);
7418 CertFreeCertificateContext(pCertContext);
7419 return false;
7422 CMSG_SIGNER_INFO *pDecodedSignerInfo = (CMSG_SIGNER_INFO *) pDecodedSignerInfoBuf.get();
7424 CRYPT_TIMESTAMP_PARA aTsPara;
7425 unsigned int nNonce = comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32);
7427 aTsPara.pszTSAPolicyId = NULL;
7428 aTsPara.fRequestCerts = TRUE;
7429 aTsPara.Nonce.cbData = sizeof(nNonce);
7430 aTsPara.Nonce.pbData = (BYTE *)&nNonce;
7431 aTsPara.cExtension = 0;
7432 aTsPara.rgExtension = NULL;
7434 if (!(*crts)(m_aContext.SignTSA.getStr(),
7436 10000,
7437 szOID_NIST_sha256,
7438 &aTsPara,
7439 pDecodedSignerInfo->EncryptedHash.pbData,
7440 pDecodedSignerInfo->EncryptedHash.cbData,
7441 &pTsContext,
7442 NULL,
7443 NULL))
7445 SAL_WARN("vcl.pdfwriter", "CryptRetrieveTimeStamp failed: " << WindowsErrorString(GetLastError()));
7446 CryptMsgClose(hDecodedMsg);
7447 CryptMsgClose(hMsg);
7448 CertFreeCertificateContext(pCertContext);
7449 return false;
7452 SAL_INFO("vcl.pdfwriter", "Time stamp size is " << pTsContext->cbEncoded << " bytes");
7454 #ifdef DBG_UTIL
7456 FILE *out = fopen("PDFWRITER.tstoken.data", "wb");
7457 fwrite(pTsContext->pbEncoded, pTsContext->cbEncoded, 1, out);
7458 fclose(out);
7460 #endif
7462 // I tried to use CryptMsgControl() with CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR to add the
7463 // timestamp, but that failed with "The parameter is incorrect". Probably it is too late to
7464 // modify the message once its data has already been encoded as part of the
7465 // CryptMsgGetParam() with CMSG_BARE_CONTENT_PARAM above. So close the message and re-do its
7466 // creation steps, but now with an amended aSignerInfo.
7468 CRYPT_INTEGER_BLOB aTimestampBlob;
7469 aTimestampBlob.cbData = pTsContext->cbEncoded;
7470 aTimestampBlob.pbData = pTsContext->pbEncoded;
7472 CRYPT_ATTRIBUTE aTimestampAttribute;
7473 aTimestampAttribute.pszObjId = "1.2.840.113549.1.9.16.2.14";
7474 aTimestampAttribute.cValue = 1;
7475 aTimestampAttribute.rgValue = &aTimestampBlob;
7477 aSignerInfo.cUnauthAttr = 1;
7478 aSignerInfo.rgUnauthAttr = &aTimestampAttribute;
7480 CryptMsgClose(hMsg);
7482 if (!(hMsg = CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
7483 CMSG_DETACHED_FLAG,
7484 CMSG_SIGNED,
7485 &aSignedInfo,
7486 NULL,
7487 NULL)) ||
7488 !CryptMsgUpdate(hMsg, (const BYTE *)buffer1.get(), bytesRead1, FALSE) ||
7489 !CryptMsgUpdate(hMsg, (const BYTE *)buffer2.get(), bytesRead2, TRUE))
7491 SAL_WARN("vcl.pdfwriter", "Re-creating the message failed: " << WindowsErrorString(GetLastError()));
7492 CryptMemFree(pTsContext);
7493 CryptMsgClose(hDecodedMsg);
7494 CryptMsgClose(hMsg);
7495 CertFreeCertificateContext(pCertContext);
7496 return false;
7499 CryptMsgClose(hDecodedMsg);
7502 DWORD nSigLen = 0;
7504 if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, NULL, &nSigLen))
7506 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
7507 if (pTsContext)
7508 CryptMemFree(pTsContext);
7509 CryptMsgClose(hMsg);
7510 CertFreeCertificateContext(pCertContext);
7511 return false;
7514 if (nSigLen*2 > MAX_SIGNATURE_CONTENT_LENGTH)
7516 SAL_WARN("vcl.pdfwriter", "Signature requires more space (" << nSigLen*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH << ")");
7517 if (pTsContext)
7518 CryptMemFree(pTsContext);
7519 CryptMsgClose(hMsg);
7520 CertFreeCertificateContext(pCertContext);
7521 return false;
7524 SAL_INFO("vcl.pdfwriter", "Signature size is " << nSigLen << " bytes");
7525 boost::scoped_array<BYTE> pSig(new BYTE[nSigLen]);
7527 if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, pSig.get(), &nSigLen))
7529 SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsErrorString(GetLastError()));
7530 if (pTsContext)
7531 CryptMemFree(pTsContext);
7532 CryptMsgClose(hMsg);
7533 CertFreeCertificateContext(pCertContext);
7534 return false;
7537 #ifdef DBG_UTIL
7539 FILE *out = fopen("PDFWRITER.signature.data", "wb");
7540 fwrite(pSig.get(), nSigLen, 1, out);
7541 fclose(out);
7543 #endif
7545 // Release resources
7546 if (pTsContext)
7547 CryptMemFree(pTsContext);
7548 CryptMsgClose(hMsg);
7549 CertFreeCertificateContext(pCertContext);
7551 OStringBuffer cms_hexbuffer;
7553 for (unsigned int i = 0; i < nSigLen ; i++)
7554 appendHex(pSig[i], cms_hexbuffer);
7556 // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
7557 nWritten = 0;
7558 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset)) );
7559 m_aFile.write(cms_hexbuffer.getStr(), cms_hexbuffer.getLength(), nWritten);
7561 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
7563 return true;
7564 #endif
7567 #endif
7569 sal_Int32 PDFWriterImpl::emitInfoDict( )
7571 sal_Int32 nObject = createObject();
7573 if( updateObject( nObject ) )
7575 OStringBuffer aLine( 1024 );
7576 aLine.append( nObject );
7577 aLine.append( " 0 obj\n"
7578 "<<" );
7579 if( !m_aContext.DocumentInfo.Title.isEmpty() )
7581 aLine.append( "/Title" );
7582 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine );
7583 aLine.append( "\n" );
7585 if( !m_aContext.DocumentInfo.Author.isEmpty() )
7587 aLine.append( "/Author" );
7588 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine );
7589 aLine.append( "\n" );
7591 if( !m_aContext.DocumentInfo.Subject.isEmpty() )
7593 aLine.append( "/Subject" );
7594 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine );
7595 aLine.append( "\n" );
7597 if( !m_aContext.DocumentInfo.Keywords.isEmpty() )
7599 aLine.append( "/Keywords" );
7600 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine );
7601 aLine.append( "\n" );
7603 if( !m_aContext.DocumentInfo.Creator.isEmpty() )
7605 aLine.append( "/Creator" );
7606 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine );
7607 aLine.append( "\n" );
7609 if( !m_aContext.DocumentInfo.Producer.isEmpty() )
7611 aLine.append( "/Producer" );
7612 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine );
7613 aLine.append( "\n" );
7616 aLine.append( "/CreationDate" );
7617 appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine );
7618 aLine.append( ">>\nendobj\n\n" );
7619 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
7620 nObject = 0;
7622 else
7623 nObject = 0;
7625 return nObject;
7628 //--->i56629
7629 // Part of this function may be shared with method appendDest.
7630 sal_Int32 PDFWriterImpl::emitNamedDestinations()
7632 sal_Int32 nCount = m_aNamedDests.size();
7633 if( nCount <= 0 )
7634 return 0;//define internal error
7636 //get the object number for all the destinations
7637 sal_Int32 nObject = createObject();
7639 if( updateObject( nObject ) )
7641 //emit the dictionary
7642 OStringBuffer aLine( 1024 );
7643 aLine.append( nObject );
7644 aLine.append( " 0 obj\n"
7645 "<<" );
7647 sal_Int32 nDestID;
7648 for( nDestID = 0; nDestID < nCount; nDestID++ )
7650 const PDFNamedDest& rDest = m_aNamedDests[ nDestID ];
7651 // In order to correctly function both under an Internet browser and
7652 // directly with a reader (provided the reader has the feature) we
7653 // need to set the name of the destination the same way it will be encoded
7654 // in an Internet link
7655 INetURLObject aLocalURL(
7656 OUString( "http://ahost.ax" ) ); //dummy location, won't be used
7657 aLocalURL.SetMark( rDest.m_aDestName );
7659 const OUString aName = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as
7660 // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
7661 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
7663 aLine.append( '/' );
7664 appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
7665 aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
7666 //maps the preceding character properly
7667 aLine.append( rDestPage.m_nPageObject );
7668 aLine.append( " 0 R" );
7670 switch( rDest.m_eType )
7672 case PDFWriter::XYZ:
7673 default:
7674 aLine.append( "/XYZ " );
7675 appendFixedInt( rDest.m_aRect.Left(), aLine );
7676 aLine.append( ' ' );
7677 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
7678 aLine.append( " 0" );
7679 break;
7680 case PDFWriter::Fit:
7681 aLine.append( "/Fit" );
7682 break;
7683 case PDFWriter::FitRectangle:
7684 aLine.append( "/FitR " );
7685 appendFixedInt( rDest.m_aRect.Left(), aLine );
7686 aLine.append( ' ' );
7687 appendFixedInt( rDest.m_aRect.Top(), aLine );
7688 aLine.append( ' ' );
7689 appendFixedInt( rDest.m_aRect.Right(), aLine );
7690 aLine.append( ' ' );
7691 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
7692 break;
7693 case PDFWriter::FitHorizontal:
7694 aLine.append( "/FitH " );
7695 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
7696 break;
7697 case PDFWriter::FitVertical:
7698 aLine.append( "/FitV " );
7699 appendFixedInt( rDest.m_aRect.Left(), aLine );
7700 break;
7701 case PDFWriter::FitPageBoundingBox:
7702 aLine.append( "/FitB" );
7703 break;
7704 case PDFWriter::FitPageBoundingBoxHorizontal:
7705 aLine.append( "/FitBH " );
7706 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
7707 break;
7708 case PDFWriter::FitPageBoundingBoxVertical:
7709 aLine.append( "/FitBV " );
7710 appendFixedInt( rDest.m_aRect.Left(), aLine );
7711 break;
7713 aLine.append( "]\n" );
7716 //close
7717 aLine.append( ">>\nendobj\n\n" );
7718 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
7719 nObject = 0;
7721 else
7722 nObject = 0;
7724 return nObject;
7726 //<--- i56629
7728 //--->i59651
7729 // emits the output intent dictionary
7730 sal_Int32 PDFWriterImpl::emitOutputIntent()
7732 if( !m_bIsPDF_A1 )
7733 return 0;
7735 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
7737 OStringBuffer aLine( 1024 );
7738 sal_Int32 nICCObject = createObject();
7739 sal_Int32 nStreamLengthObject = createObject();
7741 aLine.append( nICCObject );
7742 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
7743 aLine.append( " 0 obj\n<</N 3/Length " );
7744 aLine.append( nStreamLengthObject );
7745 aLine.append( " 0 R" );
7746 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
7747 aLine.append( "/Filter/FlateDecode" );
7748 #endif
7749 aLine.append( ">>\nstream\n" );
7750 if ( !updateObject( nICCObject ) ) return 0;
7751 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
7752 //get file position
7753 sal_uInt64 nBeginStreamPos = 0;
7754 m_aFile.getPos(nBeginStreamPos);
7755 beginCompression();
7756 checkAndEnableStreamEncryption( nICCObject );
7757 cmsHPROFILE hProfile = cmsCreate_sRGBProfile();
7758 //force ICC profile version 2.1
7759 cmsSetProfileVersion(hProfile, 2.1);
7760 cmsUInt32Number nBytesNeeded = 0;
7761 cmsSaveProfileToMem(hProfile, NULL, &nBytesNeeded);
7762 if (!nBytesNeeded)
7763 return 0;
7764 std::vector<unsigned char> xBuffer(nBytesNeeded);
7765 cmsSaveProfileToMem(hProfile, &xBuffer[0], &nBytesNeeded);
7766 cmsCloseProfile(hProfile);
7767 bool written = writeBuffer( &xBuffer[0], (sal_Int32) xBuffer.size() );
7768 disableStreamEncryption();
7769 endCompression();
7770 sal_uInt64 nEndStreamPos = 0;
7771 m_aFile.getPos(nEndStreamPos);
7773 if( !written )
7774 return 0;
7775 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
7776 return 0 ;
7777 aLine.setLength( 0 );
7779 //emit the stream length object
7780 if ( !updateObject( nStreamLengthObject ) ) return 0;
7781 aLine.setLength( 0 );
7782 aLine.append( nStreamLengthObject );
7783 aLine.append( " 0 obj\n" );
7784 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
7785 aLine.append( "\nendobj\n\n" );
7786 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
7787 aLine.setLength( 0 );
7789 //emit the OutputIntent dictionary
7790 sal_Int32 nOIObject = createObject();
7791 if ( !updateObject( nOIObject ) ) return 0;
7792 aLine.append( nOIObject );
7793 aLine.append( " 0 obj\n"
7794 "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
7796 OUString aComment( "sRGB IEC61966-2.1" );
7797 appendLiteralStringEncrypt( aComment ,nOIObject, aLine );
7798 aLine.append("/DestOutputProfile ");
7799 aLine.append( nICCObject );
7800 aLine.append( " 0 R>>\nendobj\n\n" );;
7801 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
7803 return nOIObject;
7806 // formats the string for the XML stream
7807 static void escapeStringXML( const OUString& rStr, OUString &rValue)
7809 const sal_Unicode* pUni = rStr.getStr();
7810 int nLen = rStr.getLength();
7811 for( ; nLen; nLen--, pUni++ )
7813 switch( *pUni )
7815 case sal_Unicode('&'):
7816 rValue += "&amp;";
7817 break;
7818 case sal_Unicode('<'):
7819 rValue += "&lt;";
7820 break;
7821 case sal_Unicode('>'):
7822 rValue += "&gt;";
7823 break;
7824 case sal_Unicode('\''):
7825 rValue += "&apos;";
7826 break;
7827 case sal_Unicode('"'):
7828 rValue += "&quot;";
7829 break;
7830 default:
7831 rValue += OUString( *pUni );
7832 break;
7837 // emits the document metadata
7838 sal_Int32 PDFWriterImpl::emitDocumentMetadata()
7840 if( !m_bIsPDF_A1 )
7841 return 0;
7843 //get the object number for all the destinations
7844 sal_Int32 nObject = createObject();
7846 if( updateObject( nObject ) )
7848 // the following string are written in UTF-8 unicode
7849 OStringBuffer aMetadataStream( 8192 );
7851 aMetadataStream.append( "<?xpacket begin=\"" );
7852 // these lines write Unicode "zero width non-breaking space character" (U+FEFF)
7853 // (aka byte-order mark ) used as a byte-order marker.
7854 aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) );
7855 aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
7856 aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
7857 aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
7858 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
7859 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
7860 aMetadataStream.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
7861 aMetadataStream.append( " <pdfaid:part>1</pdfaid:part>\n" );
7862 aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" );
7863 aMetadataStream.append( " </rdf:Description>\n" );
7864 //... Dublin Core properties go here
7865 if( !m_aContext.DocumentInfo.Title.isEmpty() ||
7866 !m_aContext.DocumentInfo.Author.isEmpty() ||
7867 !m_aContext.DocumentInfo.Subject.isEmpty() )
7869 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
7870 aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
7871 if( !m_aContext.DocumentInfo.Title.isEmpty() )
7873 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
7874 aMetadataStream.append( " <dc:title>\n" );
7875 aMetadataStream.append( " <rdf:Alt>\n" );
7876 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
7877 OUString aTitle;
7878 escapeStringXML( m_aContext.DocumentInfo.Title, aTitle );
7879 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) );
7880 aMetadataStream.append( "</rdf:li>\n" );
7881 aMetadataStream.append( " </rdf:Alt>\n" );
7882 aMetadataStream.append( " </dc:title>\n" );
7884 if( !m_aContext.DocumentInfo.Author.isEmpty() )
7886 aMetadataStream.append( " <dc:creator>\n" );
7887 aMetadataStream.append( " <rdf:Seq>\n" );
7888 aMetadataStream.append( " <rdf:li>" );
7889 OUString aAuthor;
7890 escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor );
7891 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) );
7892 aMetadataStream.append( "</rdf:li>\n" );
7893 aMetadataStream.append( " </rdf:Seq>\n" );
7894 aMetadataStream.append( " </dc:creator>\n" );
7896 if( !m_aContext.DocumentInfo.Subject.isEmpty() )
7898 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
7899 aMetadataStream.append( " <dc:description>\n" );
7900 aMetadataStream.append( " <rdf:Alt>\n" );
7901 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
7902 OUString aSubject;
7903 escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject );
7904 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) );
7905 aMetadataStream.append( "</rdf:li>\n" );
7906 aMetadataStream.append( " </rdf:Alt>\n" );
7907 aMetadataStream.append( " </dc:description>\n" );
7909 aMetadataStream.append( " </rdf:Description>\n" );
7912 //... PDF properties go here
7913 if( !m_aContext.DocumentInfo.Producer.isEmpty() ||
7914 !m_aContext.DocumentInfo.Keywords.isEmpty() )
7916 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
7917 aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
7918 if( !m_aContext.DocumentInfo.Producer.isEmpty() )
7920 aMetadataStream.append( " <pdf:Producer>" );
7921 OUString aProducer;
7922 escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer );
7923 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) );
7924 aMetadataStream.append( "</pdf:Producer>\n" );
7926 if( !m_aContext.DocumentInfo.Keywords.isEmpty() )
7928 aMetadataStream.append( " <pdf:Keywords>" );
7929 OUString aKeywords;
7930 escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords );
7931 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) );
7932 aMetadataStream.append( "</pdf:Keywords>\n" );
7934 aMetadataStream.append( " </rdf:Description>\n" );
7937 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
7938 aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
7939 if( !m_aContext.DocumentInfo.Creator.isEmpty() )
7941 aMetadataStream.append( " <xmp:CreatorTool>" );
7942 OUString aCreator;
7943 escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator );
7944 aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) );
7945 aMetadataStream.append( "</xmp:CreatorTool>\n" );
7947 //creation date
7948 aMetadataStream.append( " <xmp:CreateDate>" );
7949 aMetadataStream.append( m_aCreationMetaDateString );
7950 aMetadataStream.append( "</xmp:CreateDate>\n" );
7952 aMetadataStream.append( " </rdf:Description>\n" );
7953 aMetadataStream.append( " </rdf:RDF>\n" );
7954 aMetadataStream.append( "</x:xmpmeta>\n" );
7956 //add the padding
7957 for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ )
7959 aMetadataStream.append( " " );
7960 if( nSpaces % 100 == 0 )
7961 aMetadataStream.append( "\n" );
7964 aMetadataStream.append( "<?xpacket end=\"w\"?>\n" );
7966 OStringBuffer aMetadataObj( 1024 );
7968 aMetadataObj.append( nObject );
7969 aMetadataObj.append( " 0 obj\n" );
7971 aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
7973 aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() );
7974 aMetadataObj.append( ">>\nstream\n" );
7975 if ( !writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
7976 return 0;
7977 //emit the stream
7978 if ( !writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) )
7979 return 0;
7981 aMetadataObj.setLength( 0 );
7982 aMetadataObj.append( "\nendstream\nendobj\n\n" );
7983 if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
7984 nObject = 0;
7986 else
7987 nObject = 0;
7989 return nObject;
7991 //<---i59651
7993 bool PDFWriterImpl::emitTrailer()
7995 // emit doc info
7996 sal_Int32 nDocInfoObject = emitInfoDict( );
7998 sal_Int32 nSecObject = 0;
8000 if( m_aContext.Encryption.Encrypt() )
8002 //emit the security information
8003 //must be emitted as indirect dictionary object, since
8004 //Acrobat Reader 5 works only with this kind of implementation
8005 nSecObject = createObject();
8007 if( updateObject( nSecObject ) )
8009 OStringBuffer aLineS( 1024 );
8010 aLineS.append( nSecObject );
8011 aLineS.append( " 0 obj\n"
8012 "<</Filter/Standard/V " );
8013 // check the version
8014 if( m_aContext.Encryption.Security128bit )
8015 aLineS.append( "2/Length 128/R 3" );
8016 else
8017 aLineS.append( "1/R 2" );
8019 // emit the owner password, must not be encrypted
8020 aLineS.append( "/O(" );
8021 appendLiteralString( reinterpret_cast<char*>(&m_aContext.Encryption.OValue[0]), sal_Int32(m_aContext.Encryption.OValue.size()), aLineS );
8022 aLineS.append( ")/U(" );
8023 appendLiteralString( reinterpret_cast<char*>(&m_aContext.Encryption.UValue[0]), sal_Int32(m_aContext.Encryption.UValue.size()), aLineS );
8024 aLineS.append( ")/P " );// the permission set
8025 aLineS.append( m_nAccessPermissions );
8026 aLineS.append( ">>\nendobj\n\n" );
8027 if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) )
8028 nSecObject = 0;
8030 else
8031 nSecObject = 0;
8033 // emit xref table
8034 // remember start
8035 sal_uInt64 nXRefOffset = 0;
8036 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nXRefOffset )) );
8037 CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
8039 sal_Int32 nObjects = m_aObjects.size();
8040 OStringBuffer aLine;
8041 aLine.append( "0 " );
8042 aLine.append( (sal_Int32)(nObjects+1) );
8043 aLine.append( "\n" );
8044 aLine.append( "0000000000 65535 f \n" );
8045 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8047 for( sal_Int32 i = 0; i < nObjects; i++ )
8049 aLine.setLength( 0 );
8050 OString aOffset = OString::number( m_aObjects[i] );
8051 for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
8052 aLine.append( '0' );
8053 aLine.append( aOffset );
8054 aLine.append( " 00000 n \n" );
8055 DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" );
8056 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8059 // prepare document checksum
8060 OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
8061 if( m_aDocDigest )
8063 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
8064 rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) );
8065 for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ )
8066 appendHex( nMD5Sum[i], aDocChecksum );
8068 // document id set in setDocInfo method
8069 // emit trailer
8070 aLine.setLength( 0 );
8071 aLine.append( "trailer\n"
8072 "<</Size " );
8073 aLine.append( (sal_Int32)(nObjects+1) );
8074 aLine.append( "/Root " );
8075 aLine.append( m_nCatalogObject );
8076 aLine.append( " 0 R\n" );
8077 if( nSecObject )
8079 aLine.append( "/Encrypt ");
8080 aLine.append( nSecObject );
8081 aLine.append( " 0 R\n" );
8083 if( nDocInfoObject )
8085 aLine.append( "/Info " );
8086 aLine.append( nDocInfoObject );
8087 aLine.append( " 0 R\n" );
8089 if( ! m_aContext.Encryption.DocumentIdentifier.empty() )
8091 aLine.append( "/ID [ <" );
8092 for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
8093 it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
8095 appendHex( sal_Int8(*it), aLine );
8097 aLine.append( ">\n"
8098 "<" );
8099 for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
8100 it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
8102 appendHex( sal_Int8(*it), aLine );
8104 aLine.append( "> ]\n" );
8106 if( !aDocChecksum.isEmpty() )
8108 aLine.append( "/DocChecksum /" );
8109 aLine.append( aDocChecksum.makeStringAndClear() );
8110 aLine.append( "\n" );
8112 if( m_aAdditionalStreams.size() > 0 )
8114 aLine.append( "/AdditionalStreams [" );
8115 for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ )
8117 aLine.append( "/" );
8118 appendName( m_aAdditionalStreams[i].m_aMimeType, aLine );
8119 aLine.append( " " );
8120 aLine.append( m_aAdditionalStreams[i].m_nStreamObject );
8121 aLine.append( " 0 R\n" );
8123 aLine.append( "]\n" );
8125 aLine.append( ">>\n"
8126 "startxref\n" );
8127 aLine.append( (sal_Int64)nXRefOffset );
8128 aLine.append( "\n"
8129 "%%EOF\n" );
8130 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8132 return true;
8135 struct AnnotationSortEntry
8137 sal_Int32 nTabOrder;
8138 sal_Int32 nObject;
8139 sal_Int32 nWidgetIndex;
8141 AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
8142 nTabOrder( nTab ),
8143 nObject( nObj ),
8144 nWidgetIndex( nI )
8148 struct AnnotSortContainer
8150 std::set< sal_Int32 > aObjects;
8151 std::vector< AnnotationSortEntry > aSortedAnnots;
8154 struct AnnotSorterLess
8156 std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets;
8158 AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {}
8160 bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
8162 if( rLeft.nTabOrder < rRight.nTabOrder )
8163 return true;
8164 if( rRight.nTabOrder < rLeft.nTabOrder )
8165 return false;
8166 if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
8167 return false;
8168 if( rRight.nWidgetIndex < 0 )
8169 return true;
8170 if( rLeft.nWidgetIndex < 0 )
8171 return false;
8172 // remember: widget rects are in PDF coordinates, so they are ordered down up
8173 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
8174 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
8175 return true;
8176 if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
8177 m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
8178 return false;
8179 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
8180 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
8181 return true;
8182 return false;
8186 void PDFWriterImpl::sortWidgets()
8188 // sort widget annotations on each page as per their
8189 // TabOrder attribute
8190 std::unordered_map< sal_Int32, AnnotSortContainer > sorted;
8191 int nWidgets = m_aWidgets.size();
8192 for( int nW = 0; nW < nWidgets; nW++ )
8194 const PDFWidget& rWidget = m_aWidgets[nW];
8195 if( rWidget.m_nPage >= 0 )
8197 AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
8198 // optimize vector allocation
8199 if( rCont.aSortedAnnots.empty() )
8200 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
8201 // insert widget to tab sorter
8202 // RadioButtons are not page annotations, only their individual check boxes are
8203 if( rWidget.m_eType != PDFWriter::RadioButton )
8205 rCont.aObjects.insert( rWidget.m_nObject );
8206 rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) );
8210 for( std::unordered_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it )
8212 // append entries for non widget annotations
8213 PDFPage& rPage = m_aPages[ it->first ];
8214 unsigned int nAnnots = rPage.m_aAnnotations.size();
8215 for( unsigned int nA = 0; nA < nAnnots; nA++ )
8216 if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end())
8217 it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) );
8219 AnnotSorterLess aLess( m_aWidgets );
8220 std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess );
8221 // sanity check
8222 if( it->second.aSortedAnnots.size() == nAnnots)
8224 for( unsigned int nA = 0; nA < nAnnots; nA++ )
8225 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject;
8227 else
8229 DBG_ASSERT( false, "wrong number of sorted annotations" );
8230 #if OSL_DEBUG_LEVEL > 0
8231 fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n"
8232 " %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots );
8233 #endif
8237 // FIXME: implement tab order in structure tree for PDF 1.5
8240 namespace vcl {
8241 class PDFStreamIf :
8242 public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream >
8244 PDFWriterImpl* m_pWriter;
8245 bool m_bWrite;
8246 public:
8247 PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
8248 virtual ~PDFStreamIf();
8250 virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw(std::exception) SAL_OVERRIDE;
8251 virtual void SAL_CALL flush() throw(std::exception) SAL_OVERRIDE;
8252 virtual void SAL_CALL closeOutput() throw(std::exception) SAL_OVERRIDE;
8256 PDFStreamIf::~PDFStreamIf()
8260 void SAL_CALL PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw(std::exception)
8262 if( m_bWrite && aData.getLength() )
8264 sal_Int32 nBytes = aData.getLength();
8265 m_pWriter->writeBuffer( aData.getConstArray(), nBytes );
8269 void SAL_CALL PDFStreamIf::flush() throw(std::exception)
8273 void SAL_CALL PDFStreamIf::closeOutput() throw(std::exception)
8275 m_bWrite = false;
8278 bool PDFWriterImpl::emitAdditionalStreams()
8280 unsigned int nStreams = m_aAdditionalStreams.size();
8281 for( unsigned int i = 0; i < nStreams; i++ )
8283 PDFAddStream& rStream = m_aAdditionalStreams[i];
8284 rStream.m_nStreamObject = createObject();
8285 sal_Int32 nSizeObject = createObject();
8287 if( ! updateObject( rStream.m_nStreamObject ) )
8288 return false;
8290 OStringBuffer aLine;
8291 aLine.append( rStream.m_nStreamObject );
8292 aLine.append( " 0 obj\n<</Length " );
8293 aLine.append( nSizeObject );
8294 aLine.append( " 0 R" );
8295 if( rStream.m_bCompress )
8296 aLine.append( "/Filter/FlateDecode" );
8297 aLine.append( ">>\nstream\n" );
8298 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
8299 return false;
8300 sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0;
8301 if( osl::File::E_None != m_aFile.getPos(nBeginStreamPos) )
8303 m_aFile.close();
8304 m_bOpen = false;
8306 if( rStream.m_bCompress )
8307 beginCompression();
8309 checkAndEnableStreamEncryption( rStream.m_nStreamObject );
8310 com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) );
8311 assert(rStream.m_pStream);
8312 if (!rStream.m_pStream)
8313 return false;
8314 rStream.m_pStream->write( xStream );
8315 xStream.clear();
8316 delete rStream.m_pStream;
8317 rStream.m_pStream = NULL;
8318 disableStreamEncryption();
8320 if( rStream.m_bCompress )
8321 endCompression();
8323 if (osl::File::E_None != m_aFile.getPos(nEndStreamPos))
8325 m_aFile.close();
8326 m_bOpen = false;
8327 return false;
8329 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
8330 return false ;
8331 // emit stream length object
8332 if( ! updateObject( nSizeObject ) )
8333 return false;
8334 aLine.setLength( 0 );
8335 aLine.append( nSizeObject );
8336 aLine.append( " 0 obj\n" );
8337 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
8338 aLine.append( "\nendobj\n\n" );
8339 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
8340 return false;
8342 return true;
8345 bool PDFWriterImpl::emit()
8347 endPage();
8349 // resort structure tree and annotations if necessary
8350 // needed for widget tab order
8351 sortWidgets();
8353 #if !defined(ANDROID) && !defined(IOS)
8354 if( m_aContext.SignPDF )
8356 // sign the document
8357 PDFWriter::SignatureWidget aSignature;
8358 aSignature.Name = "Signature1";
8359 createControl( aSignature, 0 );
8361 #endif
8363 // emit additional streams
8364 CHECK_RETURN( emitAdditionalStreams() );
8366 // emit catalog
8367 CHECK_RETURN( emitCatalog() );
8369 #if !defined(ANDROID) && !defined(IOS)
8370 if (m_nSignatureObject != -1) // if document is signed, emit sigdict
8372 if( !emitSignature() )
8374 m_aErrors.insert( PDFWriter::Error_Signature_Failed );
8375 return false;
8378 #endif
8380 // emit trailer
8381 CHECK_RETURN( emitTrailer() );
8383 #if !defined(ANDROID) && !defined(IOS)
8384 if (m_nSignatureObject != -1) // finalize the signature
8386 if( !finalizeSignature() )
8388 m_aErrors.insert( PDFWriter::Error_Signature_Failed );
8389 return false;
8392 #endif
8394 m_aFile.close();
8395 m_bOpen = false;
8397 return true;
8401 sal_Int32 PDFWriterImpl::getSystemFont( const vcl::Font& i_rFont )
8403 getReferenceDevice()->Push();
8404 getReferenceDevice()->SetFont( i_rFont );
8405 getReferenceDevice()->ImplNewFont();
8407 const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
8408 sal_Int32 nFontID = 0;
8409 FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont );
8410 if( it != m_aSystemFonts.end() )
8411 nFontID = it->second.m_nNormalFontID;
8412 else
8414 nFontID = m_nNextFID++;
8415 m_aSystemFonts[ pDevFont ] = EmbedFont();
8416 m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID;
8419 getReferenceDevice()->Pop();
8420 getReferenceDevice()->ImplNewFont();
8422 return nFontID;
8425 bool PDFWriterImpl::registerGlyphs( int nGlyphs,
8426 sal_GlyphId* pGlyphs,
8427 sal_Int32* pGlyphWidths,
8428 sal_Ucs* pUnicodes,
8429 sal_Int32* pUnicodesPerGlyph,
8430 sal_uInt8* pMappedGlyphs,
8431 sal_Int32* pMappedFontObjects,
8432 const PhysicalFontFace* pFallbackFonts[] )
8434 SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics();
8436 if (!pGraphics)
8437 return false;
8439 const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
8440 sal_Ucs* pCurUnicode = pUnicodes;
8441 for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ )
8443 const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB);
8444 const PhysicalFontFace* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont;
8446 if( pCurrentFont->mbSubsettable )
8448 FontSubset& rSubset = m_aSubsets[ pCurrentFont ];
8449 // search for font specific glyphID
8450 FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId );
8451 if( it != rSubset.m_aMapping.end() )
8453 pMappedFontObjects[i] = it->second.m_nFontID;
8454 pMappedGlyphs[i] = it->second.m_nSubsetGlyphID;
8456 else
8458 // create new subset if necessary
8459 if( rSubset.m_aSubsets.empty()
8460 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
8462 rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) );
8465 // copy font id
8466 pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID;
8467 // create new glyph in subset
8468 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
8469 pMappedGlyphs[i] = nNewId;
8471 // add new glyph to emitted font subset
8472 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
8473 rNewGlyphEmit.setGlyphId( nNewId );
8474 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ )
8475 rNewGlyphEmit.addCode( pCurUnicode[n] );
8477 // add new glyph to font mapping
8478 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
8479 rNewGlyph.m_nFontID = pMappedFontObjects[i];
8480 rNewGlyph.m_nSubsetGlyphID = nNewId;
8482 if (!getReferenceDevice()->AcquireGraphics())
8483 return false;
8484 const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0);
8485 pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont,
8486 nFontGlyphId,
8487 bVertical,
8488 pGraphics );
8490 else if( pCurrentFont->IsEmbeddable() )
8492 sal_Int32 nFontID = 0;
8493 FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
8494 if( it != m_aEmbeddedFonts.end() )
8495 nFontID = it->second.m_nNormalFontID;
8496 else
8498 nFontID = m_nNextFID++;
8499 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
8500 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
8502 EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont];
8504 const Ucs2SIntMap* pEncoding = NULL;
8505 const Ucs2OStrMap* pNonEncoded = NULL;
8506 if (!getReferenceDevice()->AcquireGraphics())
8507 return false;
8508 pEncoding = pGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded, 0);
8510 Ucs2SIntMap::const_iterator enc_it;
8511 Ucs2OStrMap::const_iterator nonenc_it;
8513 sal_Int32 nCurFontID = nFontID;
8514 sal_Ucs cChar = *pCurUnicode;
8515 if( pEncoding )
8517 enc_it = pEncoding->find( cChar );
8518 if( enc_it != pEncoding->end() && enc_it->second > 0 )
8520 DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" );
8521 cChar = (sal_Ucs)enc_it->second;
8523 else if( (enc_it == pEncoding->end() || enc_it->second == -1) &&
8524 pNonEncoded &&
8525 (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() )
8527 nCurFontID = 0;
8528 // find non encoded glyph
8529 for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it )
8531 if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() )
8533 nCurFontID = nec_it->m_nFontID;
8534 cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ];
8535 break;
8538 if( nCurFontID == 0 ) // new nonencoded glyph
8540 if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 )
8542 rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() );
8543 rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++;
8545 EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back();
8546 rEncoding.m_aEncVector.push_back( EmbedCode() );
8547 rEncoding.m_aEncVector.back().m_aUnicode = cChar;
8548 rEncoding.m_aEncVector.back().m_aName = nonenc_it->second;
8549 rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1);
8550 nCurFontID = rEncoding.m_nFontID;
8551 cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ];
8554 else
8555 pEncoding = NULL;
8557 if( ! pEncoding )
8559 if( cChar & 0xff00 )
8561 // some characters can be used by conversion
8562 if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area
8563 cChar -= 0xf000;
8564 else
8566 OString aChar(&cChar, 1, RTL_TEXTENCODING_MS_1252);
8567 cChar = ((sal_Ucs)aChar[0]) & 0x00ff;
8572 pMappedGlyphs[ i ] = (sal_Int8)cChar;
8573 pMappedFontObjects[ i ] = nCurFontID;
8574 pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont,
8575 (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR,
8576 false,
8577 pGraphics );
8580 return true;
8583 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const OUString& rText, bool bTextLines )
8585 push( PushFlags::ALL );
8587 FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief();
8589 Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor();
8590 Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
8591 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
8592 Color aReliefColor( COL_LIGHTGRAY );
8593 if( aTextColor == COL_BLACK )
8594 aTextColor = Color( COL_WHITE );
8595 if( aTextLineColor == COL_BLACK )
8596 aTextLineColor = Color( COL_WHITE );
8597 if( aOverlineColor == COL_BLACK )
8598 aOverlineColor = Color( COL_WHITE );
8599 if( aTextColor == COL_WHITE )
8600 aReliefColor = Color( COL_BLACK );
8602 Font aSetFont = m_aCurrentPDFState.m_aFont;
8603 aSetFont.SetRelief( RELIEF_NONE );
8604 aSetFont.SetShadow( false );
8606 aSetFont.SetColor( aReliefColor );
8607 setTextLineColor( aReliefColor );
8608 setOverlineColor( aReliefColor );
8609 setFont( aSetFont );
8610 long nOff = 1 + getReferenceDevice()->mnDPIX/300;
8611 if( eRelief == RELIEF_ENGRAVED )
8612 nOff = -nOff;
8614 rLayout.DrawOffset() += Point( nOff, nOff );
8615 updateGraphicsState();
8616 drawLayout( rLayout, rText, bTextLines );
8618 rLayout.DrawOffset() -= Point( nOff, nOff );
8619 setTextLineColor( aTextLineColor );
8620 setOverlineColor( aOverlineColor );
8621 aSetFont.SetColor( aTextColor );
8622 setFont( aSetFont );
8623 updateGraphicsState();
8624 drawLayout( rLayout, rText, bTextLines );
8626 // clean up the mess
8627 pop();
8630 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const OUString& rText, bool bTextLines )
8632 Font aSaveFont = m_aCurrentPDFState.m_aFont;
8633 Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
8634 Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
8636 Font& rFont = m_aCurrentPDFState.m_aFont;
8637 if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 )
8638 rFont.SetColor( Color( COL_LIGHTGRAY ) );
8639 else
8640 rFont.SetColor( Color( COL_BLACK ) );
8641 rFont.SetShadow( false );
8642 rFont.SetOutline( false );
8643 setFont( rFont );
8644 setTextLineColor( rFont.GetColor() );
8645 setOverlineColor( rFont.GetColor() );
8646 updateGraphicsState();
8648 long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24);
8649 if( rFont.IsOutline() )
8650 nOff++;
8651 rLayout.DrawBase() += Point( nOff, nOff );
8652 drawLayout( rLayout, rText, bTextLines );
8653 rLayout.DrawBase() -= Point( nOff, nOff );
8655 setFont( aSaveFont );
8656 setTextLineColor( aSaveTextLineColor );
8657 setOverlineColor( aSaveOverlineColor );
8658 updateGraphicsState();
8661 void PDFWriterImpl::drawVerticalGlyphs(
8662 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
8663 OStringBuffer& rLine,
8664 const Point& rAlignOffset,
8665 const Matrix3& rRotScale,
8666 double fAngle,
8667 double fXScale,
8668 double fSkew,
8669 sal_Int32 nFontHeight )
8671 long nXOffset = 0;
8672 Point aCurPos( rGlyphs[0].m_aPos );
8673 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
8674 aCurPos += rAlignOffset;
8675 for( size_t i = 0; i < rGlyphs.size(); i++ )
8677 // have to emit each glyph on its own
8678 double fDeltaAngle = 0.0;
8679 double fYScale = 1.0;
8680 double fTempXScale = fXScale;
8681 double fSkewB = fSkew;
8682 double fSkewA = 0.0;
8684 Point aDeltaPos;
8685 if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL )
8687 fDeltaAngle = M_PI/2.0;
8688 aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent();
8689 aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale);
8690 fYScale = fXScale;
8691 fTempXScale = 1.0;
8692 fSkewA = -fSkewB;
8693 fSkewB = 0.0;
8695 else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR )
8697 fDeltaAngle = -M_PI/2.0;
8698 aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale);
8699 aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent();
8700 fYScale = fXScale;
8701 fTempXScale = 1.0;
8702 fSkewA = fSkewB;
8703 fSkewB = 0.0;
8705 aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) );
8706 if( i < rGlyphs.size()-1 )
8707 // #i120627# the text on the Y axis is reversed when export ppt file to PDF format
8709 long nOffsetX = rGlyphs[i+1].m_aPos.X() - rGlyphs[i].m_aPos.X();
8710 long nOffsetY = rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
8711 nXOffset += (int)sqrt(double(nOffsetX*nOffsetX + nOffsetY*nOffsetY));
8713 if( ! rGlyphs[i].m_nGlyphId )
8714 continue;
8716 aDeltaPos = rRotScale.transform( aDeltaPos );
8718 Matrix3 aMat;
8719 if( fSkewB != 0.0 || fSkewA != 0.0 )
8720 aMat.skew( fSkewA, fSkewB );
8721 aMat.scale( fTempXScale, fYScale );
8722 aMat.rotate( fAngle+fDeltaAngle );
8723 aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
8724 aMat.append( m_aPages.back(), rLine );
8725 rLine.append( " Tm" );
8726 if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
8728 rLine.append( " /F" );
8729 rLine.append( rGlyphs[i].m_nMappedFontId );
8730 rLine.append( ' ' );
8731 m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
8732 rLine.append( " Tf" );
8734 rLine.append( "<" );
8735 appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
8736 rLine.append( ">Tj\n" );
8740 void PDFWriterImpl::drawHorizontalGlyphs(
8741 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
8742 OStringBuffer& rLine,
8743 const Point& rAlignOffset,
8744 double fAngle,
8745 double fXScale,
8746 double fSkew,
8747 sal_Int32 nFontHeight,
8748 sal_Int32 nPixelFontHeight
8751 // horizontal (= normal) case
8753 // fill in run end indices
8754 // end is marked by index of the first glyph of the next run
8755 // a run is marked by same mapped font id and same Y position
8756 std::vector< sal_uInt32 > aRunEnds;
8757 aRunEnds.reserve( rGlyphs.size() );
8758 for( size_t i = 1; i < rGlyphs.size(); i++ )
8760 if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
8761 rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
8763 aRunEnds.push_back(i);
8766 // last run ends at last glyph
8767 aRunEnds.push_back( rGlyphs.size() );
8769 // loop over runs of the same font
8770 sal_uInt32 nBeginRun = 0;
8771 for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
8773 // setup text matrix
8774 Point aCurPos = rGlyphs[nBeginRun].m_aPos;
8775 // back transformation to current coordinate system
8776 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
8777 aCurPos += rAlignOffset;
8778 // the first run can be set with "Td" operator
8779 // subsequent use of that operator would move
8780 // the texline matrix relative to what was set before
8781 // making use of that would drive us into rounding issues
8782 Matrix3 aMat;
8783 if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
8785 m_aPages.back().appendPoint( aCurPos, rLine, false );
8786 rLine.append( " Td " );
8788 else
8790 if( fSkew != 0.0 )
8791 aMat.skew( 0.0, fSkew );
8792 aMat.scale( fXScale, 1.0 );
8793 aMat.rotate( fAngle );
8794 aMat.translate( aCurPos.X(), aCurPos.Y() );
8795 aMat.append( m_aPages.back(), rLine );
8796 rLine.append( " Tm\n" );
8798 // set up correct font
8799 rLine.append( "/F" );
8800 rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
8801 rLine.append( ' ' );
8802 m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
8803 rLine.append( " Tf" );
8805 // output glyphs using Tj or TJ
8806 OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
8807 aKernedLine.append( "[<" );
8808 aUnkernedLine.append( '<' );
8809 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
8810 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
8812 aMat.invert();
8813 bool bNeedKern = false;
8814 for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
8816 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
8817 // check if default glyph positioning is sufficient
8818 const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
8819 const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
8820 double fAdvance = aThisPos.X() - aPrevPos.X();
8821 fAdvance *= 1000.0 / nPixelFontHeight;
8822 const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5);
8823 if( nAdjustment != 0 )
8825 // apply individual glyph positioning
8826 bNeedKern = true;
8827 aKernedLine.append( ">" );
8828 aKernedLine.append( nAdjustment );
8829 aKernedLine.append( "<" );
8831 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
8833 aKernedLine.append( ">]TJ\n" );
8834 aUnkernedLine.append( ">Tj\n" );
8835 rLine.append(
8836 (bNeedKern ? aKernedLine : aUnkernedLine).makeStringAndClear() );
8838 // set beginning of next run
8839 nBeginRun = aRunEnds[nRun];
8843 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool bTextLines )
8845 // relief takes precedence over shadow (see outdev3.cxx)
8846 if( m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE )
8848 drawRelief( rLayout, rText, bTextLines );
8849 return;
8851 else if( m_aCurrentPDFState.m_aFont.IsShadow() )
8852 drawShadow( rLayout, rText, bTextLines );
8854 OStringBuffer aLine( 512 );
8856 const int nMaxGlyphs = 256;
8858 sal_GlyphId pGlyphs[nMaxGlyphs];
8859 sal_Int32 pGlyphWidths[nMaxGlyphs];
8860 sal_uInt8 pMappedGlyphs[nMaxGlyphs];
8861 sal_Int32 pMappedFontObjects[nMaxGlyphs];
8862 std::vector<sal_Ucs> aUnicodes;
8863 aUnicodes.reserve( nMaxGlyphs );
8864 sal_Int32 pUnicodesPerGlyph[nMaxGlyphs];
8865 int pCharPosAry[nMaxGlyphs];
8866 DeviceCoordinate nAdvanceWidths[nMaxGlyphs];
8867 const PhysicalFontFace* pFallbackFonts[nMaxGlyphs] = { NULL };
8868 bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
8869 int nGlyphs;
8870 int nIndex = 0;
8871 int nMinCharPos = 0, nMaxCharPos = rText.getLength()-1;
8872 double fXScale = 1.0;
8873 double fSkew = 0.0;
8874 sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight;
8875 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
8877 // transform font height back to current units
8878 // note: the layout calculates in outdevs device pixel !!
8879 sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
8880 if( m_aCurrentPDFState.m_aFont.GetWidth() )
8882 Font aFont( m_aCurrentPDFState.m_aFont );
8883 aFont.SetWidth( 0 );
8884 FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
8885 if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() )
8887 fXScale =
8888 (double)m_aCurrentPDFState.m_aFont.GetWidth() /
8889 (double)aMetric.GetWidth();
8891 // force state before GetFontMetric
8892 m_pReferenceDevice->ImplNewFont();
8895 // perform artificial italics if necessary
8896 if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
8897 m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
8898 !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL ||
8899 m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE )
8902 fSkew = M_PI/12.0;
8905 // if the mapmode is distorted we need to adjust for that also
8906 if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
8908 fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
8911 int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
8912 // normalize angles
8913 while( nAngle < 0 )
8914 nAngle += 3600;
8915 nAngle = nAngle % 3600;
8916 double fAngle = (double)nAngle * M_PI / 1800.0;
8918 Matrix3 aRotScale;
8919 aRotScale.scale( fXScale, 1.0 );
8920 if( fAngle != 0.0 )
8921 aRotScale.rotate( -fAngle );
8923 bool bPop = false;
8924 bool bABold = false;
8925 // artificial bold necessary ?
8926 if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
8927 m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
8929 if( ! bPop )
8930 aLine.append( "q " );
8931 bPop = true;
8932 bABold = true;
8934 // setup text colors (if necessary)
8935 Color aStrokeColor( COL_TRANSPARENT );
8936 Color aNonStrokeColor( COL_TRANSPARENT );
8938 if( m_aCurrentPDFState.m_aFont.IsOutline() )
8940 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
8941 aNonStrokeColor = Color( COL_WHITE );
8943 else
8944 aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
8945 if( bABold )
8946 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
8948 if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
8950 if( ! bPop )
8951 aLine.append( "q " );
8952 bPop = true;
8953 appendStrokingColor( aStrokeColor, aLine );
8954 aLine.append( "\n" );
8956 if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
8958 if( ! bPop )
8959 aLine.append( "q " );
8960 bPop = true;
8961 appendNonStrokingColor( aNonStrokeColor, aLine );
8962 aLine.append( "\n" );
8965 // begin text object
8966 aLine.append( "BT\n" );
8967 // outline attribute ?
8968 if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
8970 // set correct text mode, set stroke width
8971 aLine.append( "2 Tr " ); // fill, then stroke
8973 if( m_aCurrentPDFState.m_aFont.IsOutline() )
8975 // unclear what to do in case of outline and artificial bold
8976 // for the time being outline wins
8977 aLine.append( "0.25 w \n" );
8979 else
8981 double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0;
8982 m_aPages.back().appendMappedLength( fW, aLine );
8983 aLine.append ( " w\n" );
8987 FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
8989 // collect the glyphs into a single array
8990 const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
8991 std::vector< PDFGlyph > aGlyphs;
8992 aGlyphs.reserve( nTmpMaxGlyphs );
8993 // first get all the glyphs and register them; coordinates still in Pixel
8994 Point aGNGlyphPos;
8995 while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry, pFallbackFonts )) != 0 )
8997 aUnicodes.clear();
8998 for( int i = 0; i < nGlyphs; i++ )
9000 // default case: 1 glyph is one unicode
9001 pUnicodesPerGlyph[i] = 1;
9002 if( (pGlyphs[i] & GF_ISCHAR) )
9004 aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) );
9006 else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
9008 int nChars = 1;
9009 aUnicodes.push_back( rText[ pCharPosAry[i] ] );
9010 pUnicodesPerGlyph[i] = 1;
9011 // try to handle ligatures and such
9012 if( i < nGlyphs-1 )
9014 nChars = pCharPosAry[i+1] - pCharPosAry[i];
9015 // #i115618# fix for simple RTL+CTL cases
9016 // TODO: sanitize for RTL ligatures, more complex CTL, etc.
9017 if( nChars < 0 )
9018 nChars = -nChars;
9019 else if( nChars == 0 )
9020 nChars = 1;
9021 pUnicodesPerGlyph[i] = nChars;
9022 for( int n = 1; n < nChars; n++ )
9023 aUnicodes.push_back( rText[ pCharPosAry[i] + n ] );
9025 // #i36691# hack that is needed because currently the pGlyphs[]
9026 // argument is ignored for embeddable fonts and so the layout
9027 // engine's glyph work is ignored (i.e. char mirroring)
9028 // TODO: a real solution would be to map the layout engine's
9029 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
9030 // back to unicode and then to embeddable font's encoding
9031 if( (getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL) != TEXT_LAYOUT_DEFAULT )
9033 size_t nI = aUnicodes.size()-1;
9034 for( int n = 0; n < nChars; n++, nI-- )
9035 aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI]));
9038 else
9039 aUnicodes.push_back( 0 );
9040 // note: in case of ctl one character may result
9041 // in multiple glyphs. The current SalLayout
9042 // implementations set -1 then to indicate that no direct
9043 // mapping is possible
9046 registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
9048 for( int i = 0; i < nGlyphs; i++ )
9050 aGlyphs.push_back( PDFGlyph( aGNGlyphPos,
9051 pGlyphWidths[i],
9052 pGlyphs[i],
9053 pMappedFontObjects[i],
9054 pMappedGlyphs[i] ) );
9055 if( bVertical )
9056 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
9057 else
9058 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
9062 Point aAlignOffset;
9063 if ( eAlign == ALIGN_BOTTOM )
9064 aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
9065 else if ( eAlign == ALIGN_TOP )
9066 aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
9067 if( aAlignOffset.X() || aAlignOffset.Y() )
9068 aAlignOffset = aRotScale.transform( aAlignOffset );
9070 /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
9071 string contained only on of the UTF16 BOMs
9073 if( ! aGlyphs.empty() )
9075 if( bVertical )
9076 drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
9077 else
9078 drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
9081 // end textobject
9082 aLine.append( "ET\n" );
9083 if( bPop )
9084 aLine.append( "Q\n" );
9086 writeBuffer( aLine.getStr(), aLine.getLength() );
9088 // draw eventual textlines
9089 FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
9090 FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
9091 FontUnderline eOverline = m_aCurrentPDFState.m_aFont.GetOverline();
9092 if( bTextLines &&
9094 ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) ||
9095 ( eOverline != UNDERLINE_NONE && eOverline != UNDERLINE_DONTKNOW ) ||
9096 ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
9100 bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
9101 if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
9103 Point aPos, aStartPt;
9104 sal_Int32 nWidth = 0;
9105 DeviceCoordinate nAdvance = 0;
9106 for( int nStart = 0;;)
9108 sal_GlyphId aGlyphId;
9109 if( !rLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) )
9110 break;
9112 if( !SalLayout::IsSpacingGlyph( aGlyphId ) )
9114 if( !nWidth )
9115 aStartPt = aPos;
9117 nWidth += nAdvance;
9119 else if( nWidth > 0 )
9121 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
9122 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
9123 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
9124 nWidth = 0;
9128 if( nWidth > 0 )
9130 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
9131 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
9132 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
9135 else
9137 Point aStartPt = rLayout.GetDrawPosition();
9138 int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
9139 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
9140 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
9141 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
9145 // write eventual emphasis marks
9146 if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
9148 tools::PolyPolygon aEmphPoly;
9149 Rectangle aEmphRect1;
9150 Rectangle aEmphRect2;
9151 long nEmphYOff;
9152 long nEmphWidth;
9153 long nEmphHeight;
9154 bool bEmphPolyLine;
9155 FontEmphasisMark nEmphMark;
9157 push( PushFlags::ALL );
9159 aLine.setLength( 0 );
9160 aLine.append( "q\n" );
9162 nEmphMark = OutputDevice::ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
9163 if ( nEmphMark & EMPHASISMARK_POS_BELOW )
9164 nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
9165 else
9166 nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
9167 m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
9168 bEmphPolyLine,
9169 aEmphRect1,
9170 aEmphRect2,
9171 nEmphYOff,
9172 nEmphWidth,
9173 nEmphMark,
9174 m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight),
9175 m_pReferenceDevice->mpFontEntry->mnOrientation );
9176 if ( bEmphPolyLine )
9178 setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
9179 setFillColor( Color( COL_TRANSPARENT ) );
9181 else
9183 setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
9184 setLineColor( Color( COL_TRANSPARENT ) );
9186 writeBuffer( aLine.getStr(), aLine.getLength() );
9188 Point aOffset = Point(0,0);
9190 if ( nEmphMark & EMPHASISMARK_POS_BELOW )
9191 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff;
9192 else
9193 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff;
9195 long nEmphWidth2 = nEmphWidth / 2;
9196 long nEmphHeight2 = nEmphHeight / 2;
9197 aOffset += Point( nEmphWidth2, nEmphHeight2 );
9199 if ( eAlign == ALIGN_BOTTOM )
9200 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent;
9201 else if ( eAlign == ALIGN_TOP )
9202 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent;
9204 for( int nStart = 0;;)
9206 Point aPos;
9207 sal_GlyphId aGlyphId;
9208 DeviceCoordinate nAdvance;
9209 if( !rLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) )
9210 break;
9212 if( !SalLayout::IsSpacingGlyph( aGlyphId ) )
9214 Point aAdjOffset = aOffset;
9215 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2;
9216 aAdjOffset = aRotScale.transform( aAdjOffset );
9218 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
9220 aPos += aAdjOffset;
9221 aPos = m_pReferenceDevice->PixelToLogic( aPos );
9222 drawEmphasisMark( aPos.X(), aPos.Y(),
9223 aEmphPoly, bEmphPolyLine,
9224 aEmphRect1, aEmphRect2 );
9228 writeBuffer( "Q\n", 2 );
9229 pop();
9233 void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
9234 const tools::PolyPolygon& rPolyPoly, bool bPolyLine,
9235 const Rectangle& rRect1, const Rectangle& rRect2 )
9237 // TODO: pass nWidth as width of this mark
9238 // long nWidth = 0;
9240 if ( rPolyPoly.Count() )
9242 if ( bPolyLine )
9244 Polygon aPoly = rPolyPoly.GetObject( 0 );
9245 aPoly.Move( nX, nY );
9246 drawPolyLine( aPoly );
9248 else
9250 tools::PolyPolygon aPolyPoly = rPolyPoly;
9251 aPolyPoly.Move( nX, nY );
9252 drawPolyPolygon( aPolyPoly );
9256 if ( !rRect1.IsEmpty() )
9258 Rectangle aRect( Point( nX+rRect1.Left(),
9259 nY+rRect1.Top() ), rRect1.GetSize() );
9260 drawRectangle( aRect );
9263 if ( !rRect2.IsEmpty() )
9265 Rectangle aRect( Point( nX+rRect2.Left(),
9266 nY+rRect2.Top() ), rRect2.GetSize() );
9268 drawRectangle( aRect );
9272 void PDFWriterImpl::drawText( const Point& rPos, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines )
9274 MARK( "drawText" );
9276 updateGraphicsState();
9278 // get a layout from the OuputDevice's SalGraphics
9279 // this also enforces font substitution and sets the font on SalGraphics
9280 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
9281 if( pLayout )
9283 drawLayout( *pLayout, rText, bTextLines );
9284 pLayout->Release();
9288 void PDFWriterImpl::drawTextArray( const Point& rPos, const OUString& rText, const long* pDXArray, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines )
9290 MARK( "drawText with array" );
9292 updateGraphicsState();
9294 // get a layout from the OuputDevice's SalGraphics
9295 // this also enforces font substitution and sets the font on SalGraphics
9296 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
9297 if( pLayout )
9299 drawLayout( *pLayout, rText, bTextLines );
9300 pLayout->Release();
9304 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines )
9306 MARK( "drawStretchText" );
9308 updateGraphicsState();
9310 // get a layout from the OuputDevice's SalGraphics
9311 // this also enforces font substitution and sets the font on SalGraphics
9312 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
9313 if( pLayout )
9315 drawLayout( *pLayout, rText, bTextLines );
9316 pLayout->Release();
9320 void PDFWriterImpl::drawText( const Rectangle& rRect, const OUString& rOrigStr, DrawTextFlags nStyle, bool bTextLines )
9322 long nWidth = rRect.GetWidth();
9323 long nHeight = rRect.GetHeight();
9325 if ( nWidth <= 0 || nHeight <= 0 )
9326 return;
9328 MARK( "drawText with rectangle" );
9330 updateGraphicsState();
9332 // clip with rectangle
9333 OStringBuffer aLine;
9334 aLine.append( "q " );
9335 m_aPages.back().appendRect( rRect, aLine );
9336 aLine.append( " W* n\n" );
9337 writeBuffer( aLine.getStr(), aLine.getLength() );
9339 // if disabled text is needed, put in here
9341 Point aPos = rRect.TopLeft();
9343 long nTextHeight = m_pReferenceDevice->GetTextHeight();
9344 sal_Int32 nMnemonicPos = -1;
9346 OUString aStr = rOrigStr;
9347 if ( nStyle & DrawTextFlags::Mnemonic )
9348 aStr = OutputDevice::GetNonMnemonicString( aStr, nMnemonicPos );
9350 // multiline text
9351 if ( nStyle & DrawTextFlags::MultiLine )
9353 OUString aLastLine;
9354 ImplMultiTextLineInfo aMultiLineInfo;
9355 ImplTextLineInfo* pLineInfo;
9356 sal_Int32 i;
9357 sal_Int32 nLines;
9358 sal_Int32 nFormatLines;
9360 if ( nTextHeight )
9362 vcl::DefaultTextLayout aLayout( *m_pReferenceDevice );
9363 OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout );
9364 nLines = nHeight/nTextHeight;
9365 nFormatLines = aMultiLineInfo.Count();
9366 if ( !nLines )
9367 nLines = 1;
9368 if ( nFormatLines > nLines )
9370 if ( nStyle & DrawTextFlags::EndEllipsis )
9372 // handle last line
9373 nFormatLines = nLines-1;
9375 pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
9376 aLastLine = convertLineEnd(aStr.copy(pLineInfo->GetIndex()), LINEEND_LF);
9377 // replace line feed by space
9378 aLastLine = aLastLine.replace('\n', ' ');
9379 aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
9380 nStyle &= ~DrawTextFlags(DrawTextFlags::VCenter | DrawTextFlags::Bottom);
9381 nStyle |= DrawTextFlags::Top;
9385 // vertical alignment
9386 if ( nStyle & DrawTextFlags::Bottom )
9387 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
9388 else if ( nStyle & DrawTextFlags::VCenter )
9389 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
9391 // draw all lines excluding the last
9392 for ( i = 0; i < nFormatLines; i++ )
9394 pLineInfo = aMultiLineInfo.GetLine( i );
9395 if ( nStyle & DrawTextFlags::Right )
9396 aPos.X() += nWidth-pLineInfo->GetWidth();
9397 else if ( nStyle & DrawTextFlags::Center )
9398 aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
9399 sal_Int32 nIndex = pLineInfo->GetIndex();
9400 sal_Int32 nLineLen = pLineInfo->GetLen();
9401 drawText( aPos, aStr, nIndex, nLineLen, bTextLines );
9402 // mnemonics should not appear in documents,
9403 // if the need arises, put them in here
9404 aPos.Y() += nTextHeight;
9405 aPos.X() = rRect.Left();
9408 // output last line left adjusted since it was shortened
9409 if (!aLastLine.isEmpty())
9410 drawText( aPos, aLastLine, 0, aLastLine.getLength(), bTextLines );
9413 else
9415 long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
9417 // Evt. Text kuerzen
9418 if ( nTextWidth > nWidth )
9420 if ( nStyle & (DrawTextFlags::EndEllipsis | DrawTextFlags::PathEllipsis | DrawTextFlags::NewsEllipsis) )
9422 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
9423 nStyle &= ~DrawTextFlags(DrawTextFlags::Center | DrawTextFlags::Right);
9424 nStyle |= DrawTextFlags::Left;
9425 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
9429 // vertical alignment
9430 if ( nStyle & DrawTextFlags::Right )
9431 aPos.X() += nWidth-nTextWidth;
9432 else if ( nStyle & DrawTextFlags::Center )
9433 aPos.X() += (nWidth-nTextWidth)/2;
9435 if ( nStyle & DrawTextFlags::Bottom )
9436 aPos.Y() += nHeight-nTextHeight;
9437 else if ( nStyle & DrawTextFlags::VCenter )
9438 aPos.Y() += (nHeight-nTextHeight)/2;
9440 // mnemonics should be inserted here if the need arises
9442 // draw the actual text
9443 drawText( aPos, aStr, 0, aStr.getLength(), bTextLines );
9446 // reset clip region to original value
9447 aLine.setLength( 0 );
9448 aLine.append( "Q\n" );
9449 writeBuffer( aLine.getStr(), aLine.getLength() );
9452 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
9454 MARK( "drawLine" );
9456 updateGraphicsState();
9458 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9459 return;
9461 OStringBuffer aLine;
9462 m_aPages.back().appendPoint( rStart, aLine );
9463 aLine.append( " m " );
9464 m_aPages.back().appendPoint( rStop, aLine );
9465 aLine.append( " l S\n" );
9467 writeBuffer( aLine.getStr(), aLine.getLength() );
9470 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
9472 MARK( "drawLine with LineInfo" );
9473 updateGraphicsState();
9475 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9476 return;
9478 if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 )
9480 drawLine( rStart, rStop );
9481 return;
9484 OStringBuffer aLine;
9486 aLine.append( "q " );
9487 if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
9489 m_aPages.back().appendPoint( rStart, aLine );
9490 aLine.append( " m " );
9491 m_aPages.back().appendPoint( rStop, aLine );
9492 aLine.append( " l S Q\n" );
9494 writeBuffer( aLine.getStr(), aLine.getLength() );
9496 else
9498 PDFWriter::ExtLineInfo aInfo;
9499 convertLineInfoToExtLineInfo( rInfo, aInfo );
9500 Point aPolyPoints[2] = { rStart, rStop };
9501 Polygon aPoly( 2, aPolyPoints );
9502 drawPolyLine( aPoly, aInfo );
9506 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
9508 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
9510 // note: units in pFontEntry are ref device pixel
9511 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
9512 long nLineHeight = 0;
9513 long nLinePos = 0;
9515 appendStrokingColor( aColor, aLine );
9516 aLine.append( "\n" );
9518 if ( bIsAbove )
9520 if ( !pFontEntry->maMetric.mnAboveWUnderlineSize )
9521 m_pReferenceDevice->ImplInitAboveTextLineSize();
9522 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize );
9523 nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset );
9525 else
9527 if ( !pFontEntry->maMetric.mnWUnderlineSize )
9528 m_pReferenceDevice->ImplInitTextLineSize();
9529 nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize );
9530 nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset );
9532 if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
9533 nLineHeight = 3;
9535 long nLineWidth = getReferenceDevice()->mnDPIX/450;
9536 if ( ! nLineWidth )
9537 nLineWidth = 1;
9539 if ( eTextLine == UNDERLINE_BOLDWAVE )
9540 nLineWidth = 3*nLineWidth;
9542 m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
9543 aLine.append( " w " );
9545 if ( eTextLine == UNDERLINE_DOUBLEWAVE )
9547 long nOrgLineHeight = nLineHeight;
9548 nLineHeight /= 3;
9549 if ( nLineHeight < 2 )
9551 if ( nOrgLineHeight > 1 )
9552 nLineHeight = 2;
9553 else
9554 nLineHeight = 1;
9556 long nLineDY = nOrgLineHeight-(nLineHeight*2);
9557 if ( nLineDY < nLineWidth )
9558 nLineDY = nLineWidth;
9559 long nLineDY2 = nLineDY/2;
9560 if ( !nLineDY2 )
9561 nLineDY2 = 1;
9563 nLinePos -= nLineWidth-nLineDY2;
9565 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
9567 nLinePos += nLineWidth+nLineDY;
9568 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
9570 else
9572 if ( eTextLine != UNDERLINE_BOLDWAVE )
9573 nLinePos -= nLineWidth/2;
9574 m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
9578 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
9580 // note: units in pFontEntry are ref device pixel
9581 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
9582 long nLineHeight = 0;
9583 long nLinePos = 0;
9584 long nLinePos2 = 0;
9586 if ( eTextLine > UNDERLINE_BOLDWAVE )
9587 eTextLine = UNDERLINE_SINGLE;
9589 switch ( eTextLine )
9591 case UNDERLINE_SINGLE:
9592 case UNDERLINE_DOTTED:
9593 case UNDERLINE_DASH:
9594 case UNDERLINE_LONGDASH:
9595 case UNDERLINE_DASHDOT:
9596 case UNDERLINE_DASHDOTDOT:
9597 if ( bIsAbove )
9599 if ( !pFontEntry->maMetric.mnAboveUnderlineSize )
9600 m_pReferenceDevice->ImplInitAboveTextLineSize();
9601 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize );
9602 nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset );
9604 else
9606 if ( !pFontEntry->maMetric.mnUnderlineSize )
9607 m_pReferenceDevice->ImplInitTextLineSize();
9608 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize );
9609 nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset );
9611 break;
9612 case UNDERLINE_BOLD:
9613 case UNDERLINE_BOLDDOTTED:
9614 case UNDERLINE_BOLDDASH:
9615 case UNDERLINE_BOLDLONGDASH:
9616 case UNDERLINE_BOLDDASHDOT:
9617 case UNDERLINE_BOLDDASHDOTDOT:
9618 if ( bIsAbove )
9620 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize )
9621 m_pReferenceDevice->ImplInitAboveTextLineSize();
9622 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize );
9623 nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset );
9625 else
9627 if ( !pFontEntry->maMetric.mnBUnderlineSize )
9628 m_pReferenceDevice->ImplInitTextLineSize();
9629 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize );
9630 nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset );
9631 nLinePos += nLineHeight/2;
9633 break;
9634 case UNDERLINE_DOUBLE:
9635 if ( bIsAbove )
9637 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize )
9638 m_pReferenceDevice->ImplInitAboveTextLineSize();
9639 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize );
9640 nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 );
9641 nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 );
9643 else
9645 if ( !pFontEntry->maMetric.mnDUnderlineSize )
9646 m_pReferenceDevice->ImplInitTextLineSize();
9647 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize );
9648 nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 );
9649 nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 );
9651 break;
9652 default:
9653 break;
9656 if ( nLineHeight )
9658 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
9659 aLine.append( " w " );
9660 appendStrokingColor( aColor, aLine );
9661 aLine.append( "\n" );
9663 switch ( eTextLine )
9665 case UNDERLINE_DOTTED:
9666 case UNDERLINE_BOLDDOTTED:
9667 aLine.append( "[ " );
9668 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
9669 aLine.append( " ] 0 d\n" );
9670 break;
9671 case UNDERLINE_DASH:
9672 case UNDERLINE_LONGDASH:
9673 case UNDERLINE_BOLDDASH:
9674 case UNDERLINE_BOLDLONGDASH:
9676 sal_Int32 nDashLength = 4*nLineHeight;
9677 sal_Int32 nVoidLength = 2*nLineHeight;
9678 if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) )
9679 nDashLength = 8*nLineHeight;
9681 aLine.append( "[ " );
9682 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
9683 aLine.append( ' ' );
9684 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9685 aLine.append( " ] 0 d\n" );
9687 break;
9688 case UNDERLINE_DASHDOT:
9689 case UNDERLINE_BOLDDASHDOT:
9691 sal_Int32 nDashLength = 4*nLineHeight;
9692 sal_Int32 nVoidLength = 2*nLineHeight;
9693 aLine.append( "[ " );
9694 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
9695 aLine.append( ' ' );
9696 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9697 aLine.append( ' ' );
9698 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
9699 aLine.append( ' ' );
9700 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9701 aLine.append( " ] 0 d\n" );
9703 break;
9704 case UNDERLINE_DASHDOTDOT:
9705 case UNDERLINE_BOLDDASHDOTDOT:
9707 sal_Int32 nDashLength = 4*nLineHeight;
9708 sal_Int32 nVoidLength = 2*nLineHeight;
9709 aLine.append( "[ " );
9710 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
9711 aLine.append( ' ' );
9712 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9713 aLine.append( ' ' );
9714 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
9715 aLine.append( ' ' );
9716 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9717 aLine.append( ' ' );
9718 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
9719 aLine.append( ' ' );
9720 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9721 aLine.append( " ] 0 d\n" );
9723 break;
9724 default:
9725 break;
9728 aLine.append( "0 " );
9729 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
9730 aLine.append( " m " );
9731 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
9732 aLine.append( ' ' );
9733 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
9734 aLine.append( " l S\n" );
9735 if ( eTextLine == UNDERLINE_DOUBLE )
9737 aLine.append( "0 " );
9738 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
9739 aLine.append( " m " );
9740 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
9741 aLine.append( ' ' );
9742 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
9743 aLine.append( " l S\n" );
9748 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
9750 // note: units in pFontEntry are ref device pixel
9751 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
9752 long nLineHeight = 0;
9753 long nLinePos = 0;
9754 long nLinePos2 = 0;
9756 if ( eStrikeout > STRIKEOUT_X )
9757 eStrikeout = STRIKEOUT_SINGLE;
9759 switch ( eStrikeout )
9761 case STRIKEOUT_SINGLE:
9762 if ( !pFontEntry->maMetric.mnStrikeoutSize )
9763 m_pReferenceDevice->ImplInitTextLineSize();
9764 nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize );
9765 nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset );
9766 break;
9767 case STRIKEOUT_BOLD:
9768 if ( !pFontEntry->maMetric.mnBStrikeoutSize )
9769 m_pReferenceDevice->ImplInitTextLineSize();
9770 nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize );
9771 nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset );
9772 break;
9773 case STRIKEOUT_DOUBLE:
9774 if ( !pFontEntry->maMetric.mnDStrikeoutSize )
9775 m_pReferenceDevice->ImplInitTextLineSize();
9776 nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize );
9777 nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 );
9778 nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 );
9779 break;
9780 default:
9781 break;
9784 if ( nLineHeight )
9786 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
9787 aLine.append( " w " );
9788 appendStrokingColor( aColor, aLine );
9789 aLine.append( "\n" );
9791 aLine.append( "0 " );
9792 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
9793 aLine.append( " m " );
9794 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
9795 aLine.append( ' ' );
9796 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
9797 aLine.append( " l S\n" );
9799 if ( eStrikeout == STRIKEOUT_DOUBLE )
9801 aLine.append( "0 " );
9802 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
9803 aLine.append( " m " );
9804 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
9805 aLine.append( ' ' );
9806 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
9807 aLine.append( " l S\n" );
9812 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
9814 //See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
9815 //to tweak this
9817 OUString aStrikeoutChar = eStrikeout == STRIKEOUT_SLASH ? OUString( "/" ) : OUString( "X" );
9818 OUString aStrikeout = aStrikeoutChar;
9819 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
9820 aStrikeout += aStrikeout;
9822 // do not get broader than nWidth modulo 1 character
9823 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
9824 aStrikeout = aStrikeout.replaceAt( 0, 1, "" );
9825 aStrikeout += aStrikeoutChar;
9826 bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
9827 if ( bShadow )
9829 Font aFont = m_aCurrentPDFState.m_aFont;
9830 aFont.SetShadow( false );
9831 setFont( aFont );
9832 updateGraphicsState();
9835 // strikeout string is left aligned non-CTL text
9836 ComplexTextLayoutMode nOrigTLM = m_pReferenceDevice->GetLayoutMode();
9837 m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED );
9839 push( PushFlags::CLIPREGION );
9840 FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
9841 Rectangle aRect;
9842 aRect.Left() = rPos.X();
9843 aRect.Right() = aRect.Left()+nWidth;
9844 aRect.Bottom() = rPos.Y()+aRefDevFontMetric.GetDescent();
9845 aRect.Top() = rPos.Y()-aRefDevFontMetric.GetAscent();
9847 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
9848 if (pFontEntry->mnOrientation)
9850 Polygon aPoly( aRect );
9851 aPoly.Rotate( rPos, pFontEntry->mnOrientation);
9852 aRect = aPoly.GetBoundRect();
9855 intersectClipRegion( aRect );
9856 drawText( rPos, aStrikeout, 0, aStrikeout.getLength(), false );
9857 pop();
9859 m_pReferenceDevice->SetLayoutMode( nOrigTLM );
9861 if ( bShadow )
9863 Font aFont = m_aCurrentPDFState.m_aFont;
9864 aFont.SetShadow( true );
9865 setFont( aFont );
9866 updateGraphicsState();
9870 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove )
9872 if ( !nWidth ||
9873 ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
9874 ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) &&
9875 ((eOverline == UNDERLINE_NONE)||(eOverline == UNDERLINE_DONTKNOW)) ) )
9876 return;
9878 MARK( "drawTextLine" );
9879 updateGraphicsState();
9881 // note: units in pFontEntry are ref device pixel
9882 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
9883 Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
9884 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
9885 Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
9886 bool bStrikeoutDone = false;
9887 bool bUnderlineDone = false;
9888 bool bOverlineDone = false;
9890 if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
9892 drawStrikeoutChar( rPos, nWidth, eStrikeout );
9893 bStrikeoutDone = true;
9896 Point aPos( rPos );
9897 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
9898 if( eAlign == ALIGN_TOP )
9899 aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent );
9900 else if( eAlign == ALIGN_BOTTOM )
9901 aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent );
9903 OStringBuffer aLine( 512 );
9904 // save GS
9905 aLine.append( "q " );
9907 // rotate and translate matrix
9908 double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
9909 Matrix3 aMat;
9910 aMat.rotate( fAngle );
9911 aMat.translate( aPos.X(), aPos.Y() );
9912 aMat.append( m_aPages.back(), aLine );
9913 aLine.append( " cm\n" );
9915 if ( aUnderlineColor.GetTransparency() != 0 )
9916 aUnderlineColor = aStrikeoutColor;
9918 if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
9919 (eUnderline == UNDERLINE_WAVE) ||
9920 (eUnderline == UNDERLINE_DOUBLEWAVE) ||
9921 (eUnderline == UNDERLINE_BOLDWAVE) )
9923 drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
9924 bUnderlineDone = true;
9927 if ( (eOverline == UNDERLINE_SMALLWAVE) ||
9928 (eOverline == UNDERLINE_WAVE) ||
9929 (eOverline == UNDERLINE_DOUBLEWAVE) ||
9930 (eOverline == UNDERLINE_BOLDWAVE) )
9932 drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
9933 bOverlineDone = true;
9936 if ( !bUnderlineDone )
9938 drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
9941 if ( !bOverlineDone )
9943 drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
9946 if ( !bStrikeoutDone )
9948 drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
9951 aLine.append( "Q\n" );
9952 writeBuffer( aLine.getStr(), aLine.getLength() );
9955 void PDFWriterImpl::drawPolygon( const Polygon& rPoly )
9957 MARK( "drawPolygon" );
9959 updateGraphicsState();
9961 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
9962 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
9963 return;
9965 int nPoints = rPoly.GetSize();
9966 OStringBuffer aLine( 20 * nPoints );
9967 m_aPages.back().appendPolygon( rPoly, aLine );
9968 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
9969 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
9970 aLine.append( "B*\n" );
9971 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9972 aLine.append( "S\n" );
9973 else
9974 aLine.append( "f*\n" );
9976 writeBuffer( aLine.getStr(), aLine.getLength() );
9979 void PDFWriterImpl::drawPolyPolygon( const tools::PolyPolygon& rPolyPoly )
9981 MARK( "drawPolyPolygon" );
9983 updateGraphicsState();
9985 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
9986 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
9987 return;
9989 int nPolygons = rPolyPoly.Count();
9991 OStringBuffer aLine( 40 * nPolygons );
9992 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
9993 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
9994 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
9995 aLine.append( "B*\n" );
9996 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9997 aLine.append( "S\n" );
9998 else
9999 aLine.append( "f*\n" );
10001 writeBuffer( aLine.getStr(), aLine.getLength() );
10004 void PDFWriterImpl::drawTransparent( const tools::PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
10006 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
10007 nTransparentPercent = nTransparentPercent % 100;
10009 MARK( "drawTransparent" );
10011 updateGraphicsState();
10013 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
10014 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
10015 return;
10017 if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 )
10019 m_aErrors.insert( m_bIsPDF_A1 ?
10020 PDFWriter::Warning_Transparency_Omitted_PDFA :
10021 PDFWriter::Warning_Transparency_Omitted_PDF13 );
10023 drawPolyPolygon( rPolyPoly );
10024 return;
10027 // create XObject
10028 m_aTransparentObjects.push_back( TransparencyEmit() );
10029 // FIXME: polygons with beziers may yield incorrect bound rect
10030 m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect();
10031 // convert rectangle to default user space
10032 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
10033 m_aTransparentObjects.back().m_nObject = createObject();
10034 m_aTransparentObjects.back().m_nExtGStateObject = createObject();
10035 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
10036 m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 );
10037 // create XObject's content stream
10038 OStringBuffer aContent( 256 );
10039 m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
10040 if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
10041 m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
10042 aContent.append( " B*\n" );
10043 else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
10044 aContent.append( " S\n" );
10045 else
10046 aContent.append( " f*\n" );
10047 m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() );
10049 OStringBuffer aObjName( 16 );
10050 aObjName.append( "Tr" );
10051 aObjName.append( m_aTransparentObjects.back().m_nObject );
10052 OString aTrName( aObjName.makeStringAndClear() );
10053 aObjName.append( "EGS" );
10054 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
10055 OString aExtName( aObjName.makeStringAndClear() );
10057 OStringBuffer aLine( 80 );
10058 // insert XObject
10059 aLine.append( "q /" );
10060 aLine.append( aExtName );
10061 aLine.append( " gs /" );
10062 aLine.append( aTrName );
10063 aLine.append( " Do Q\n" );
10064 writeBuffer( aLine.getStr(), aLine.getLength() );
10066 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
10067 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
10070 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
10072 if( nObject >= 0 )
10074 switch( eKind )
10076 case ResXObject:
10077 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
10078 if( ! m_aOutputStreams.empty() )
10079 m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
10080 break;
10081 case ResExtGState:
10082 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
10083 if( ! m_aOutputStreams.empty() )
10084 m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
10085 break;
10086 case ResShading:
10087 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
10088 if( ! m_aOutputStreams.empty() )
10089 m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
10090 break;
10091 case ResPattern:
10092 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
10093 if( ! m_aOutputStreams.empty() )
10094 m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
10095 break;
10100 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect )
10102 push( PushFlags::ALL );
10104 // force reemitting clip region inside the new stream, and
10105 // prevent emitting an unbalanced "Q" at the start
10106 clearClipRegion();
10107 // this is needed to point m_aCurrentPDFState at the pushed state
10108 // ... but it's pointless to actually write into the "outer" stream here!
10109 updateGraphicsState(NOWRITE);
10111 m_aOutputStreams.push_front( StreamRedirect() );
10112 m_aOutputStreams.front().m_pStream = pStream;
10113 m_aOutputStreams.front().m_aMapMode = m_aMapMode;
10115 if( !rTargetRect.IsEmpty() )
10117 m_aOutputStreams.front().m_aTargetRect =
10118 lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10119 m_aMapMode,
10120 getReferenceDevice(),
10121 rTargetRect );
10122 Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
10123 long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
10124 aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
10125 m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
10128 // setup graphics state for independent object stream
10130 // force reemitting colors
10131 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
10132 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
10135 SvStream* PDFWriterImpl::endRedirect()
10137 SvStream* pStream = NULL;
10138 if( ! m_aOutputStreams.empty() )
10140 pStream = m_aOutputStreams.front().m_pStream;
10141 m_aMapMode = m_aOutputStreams.front().m_aMapMode;
10142 m_aOutputStreams.pop_front();
10145 pop();
10147 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
10148 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
10150 // needed after pop() to set m_aCurrentPDFState
10151 updateGraphicsState(NOWRITE);
10153 return pStream;
10156 void PDFWriterImpl::beginTransparencyGroup()
10158 updateGraphicsState();
10159 if( m_aContext.Version >= PDFWriter::PDF_1_4 )
10160 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
10163 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
10165 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
10166 nTransparentPercent = nTransparentPercent % 100;
10168 if( m_aContext.Version >= PDFWriter::PDF_1_4 )
10170 // create XObject
10171 m_aTransparentObjects.push_back( TransparencyEmit() );
10172 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox;
10173 // convert rectangle to default user space
10174 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
10175 m_aTransparentObjects.back().m_nObject = createObject();
10176 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
10177 // get XObject's content stream
10178 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
10179 m_aTransparentObjects.back().m_nExtGStateObject = createObject();
10181 OStringBuffer aObjName( 16 );
10182 aObjName.append( "Tr" );
10183 aObjName.append( m_aTransparentObjects.back().m_nObject );
10184 OString aTrName( aObjName.makeStringAndClear() );
10185 aObjName.append( "EGS" );
10186 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
10187 OString aExtName( aObjName.makeStringAndClear() );
10189 OStringBuffer aLine( 80 );
10190 // insert XObject
10191 aLine.append( "q /" );
10192 aLine.append( aExtName );
10193 aLine.append( " gs /" );
10194 aLine.append( aTrName );
10195 aLine.append( " Do Q\n" );
10196 writeBuffer( aLine.getStr(), aLine.getLength() );
10198 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
10199 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
10203 void PDFWriterImpl::drawRectangle( const Rectangle& rRect )
10205 MARK( "drawRectangle" );
10207 updateGraphicsState();
10209 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
10210 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
10211 return;
10213 OStringBuffer aLine( 40 );
10214 m_aPages.back().appendRect( rRect, aLine );
10216 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
10217 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
10218 aLine.append( " B*\n" );
10219 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10220 aLine.append( " S\n" );
10221 else
10222 aLine.append( " f*\n" );
10224 writeBuffer( aLine.getStr(), aLine.getLength() );
10227 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
10229 MARK( "drawRectangle with rounded edges" );
10231 if( !nHorzRound && !nVertRound )
10232 drawRectangle( rRect );
10234 updateGraphicsState();
10236 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
10237 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
10238 return;
10240 if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
10241 nHorzRound = rRect.GetWidth()/2;
10242 if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
10243 nVertRound = rRect.GetHeight()/2;
10245 Point aPoints[16];
10246 const double kappa = 0.5522847498;
10247 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
10248 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
10250 aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
10251 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
10252 aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
10253 aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() );
10255 aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
10256 aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky );
10257 aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
10258 aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky );
10260 aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
10261 aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() );
10262 aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
10263 aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
10265 aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
10266 aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
10267 aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
10268 aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
10270 OStringBuffer aLine( 80 );
10271 m_aPages.back().appendPoint( aPoints[1], aLine );
10272 aLine.append( " m " );
10273 m_aPages.back().appendPoint( aPoints[2], aLine );
10274 aLine.append( " l " );
10275 m_aPages.back().appendPoint( aPoints[3], aLine );
10276 aLine.append( ' ' );
10277 m_aPages.back().appendPoint( aPoints[4], aLine );
10278 aLine.append( ' ' );
10279 m_aPages.back().appendPoint( aPoints[5], aLine );
10280 aLine.append( " c\n" );
10281 m_aPages.back().appendPoint( aPoints[6], aLine );
10282 aLine.append( " l " );
10283 m_aPages.back().appendPoint( aPoints[7], aLine );
10284 aLine.append( ' ' );
10285 m_aPages.back().appendPoint( aPoints[8], aLine );
10286 aLine.append( ' ' );
10287 m_aPages.back().appendPoint( aPoints[9], aLine );
10288 aLine.append( " c\n" );
10289 m_aPages.back().appendPoint( aPoints[10], aLine );
10290 aLine.append( " l " );
10291 m_aPages.back().appendPoint( aPoints[11], aLine );
10292 aLine.append( ' ' );
10293 m_aPages.back().appendPoint( aPoints[12], aLine );
10294 aLine.append( ' ' );
10295 m_aPages.back().appendPoint( aPoints[13], aLine );
10296 aLine.append( " c\n" );
10297 m_aPages.back().appendPoint( aPoints[14], aLine );
10298 aLine.append( " l " );
10299 m_aPages.back().appendPoint( aPoints[15], aLine );
10300 aLine.append( ' ' );
10301 m_aPages.back().appendPoint( aPoints[0], aLine );
10302 aLine.append( ' ' );
10303 m_aPages.back().appendPoint( aPoints[1], aLine );
10304 aLine.append( " c " );
10306 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
10307 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
10308 aLine.append( "b*\n" );
10309 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10310 aLine.append( "s\n" );
10311 else
10312 aLine.append( "f*\n" );
10314 writeBuffer( aLine.getStr(), aLine.getLength() );
10317 void PDFWriterImpl::drawEllipse( const Rectangle& rRect )
10319 MARK( "drawEllipse" );
10321 updateGraphicsState();
10323 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
10324 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
10325 return;
10327 Point aPoints[12];
10328 const double kappa = 0.5522847498;
10329 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
10330 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
10332 aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
10333 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
10334 aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() );
10336 aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
10337 aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky );
10338 aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky );
10340 aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
10341 aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() );
10342 aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() );
10344 aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
10345 aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky );
10346 aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
10348 OStringBuffer aLine( 80 );
10349 m_aPages.back().appendPoint( aPoints[1], aLine );
10350 aLine.append( " m " );
10351 m_aPages.back().appendPoint( aPoints[2], aLine );
10352 aLine.append( ' ' );
10353 m_aPages.back().appendPoint( aPoints[3], aLine );
10354 aLine.append( ' ' );
10355 m_aPages.back().appendPoint( aPoints[4], aLine );
10356 aLine.append( " c\n" );
10357 m_aPages.back().appendPoint( aPoints[5], aLine );
10358 aLine.append( ' ' );
10359 m_aPages.back().appendPoint( aPoints[6], aLine );
10360 aLine.append( ' ' );
10361 m_aPages.back().appendPoint( aPoints[7], aLine );
10362 aLine.append( " c\n" );
10363 m_aPages.back().appendPoint( aPoints[8], aLine );
10364 aLine.append( ' ' );
10365 m_aPages.back().appendPoint( aPoints[9], aLine );
10366 aLine.append( ' ' );
10367 m_aPages.back().appendPoint( aPoints[10], aLine );
10368 aLine.append( " c\n" );
10369 m_aPages.back().appendPoint( aPoints[11], aLine );
10370 aLine.append( ' ' );
10371 m_aPages.back().appendPoint( aPoints[0], aLine );
10372 aLine.append( ' ' );
10373 m_aPages.back().appendPoint( aPoints[1], aLine );
10374 aLine.append( " c " );
10376 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
10377 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
10378 aLine.append( "b*\n" );
10379 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10380 aLine.append( "s\n" );
10381 else
10382 aLine.append( "f*\n" );
10384 writeBuffer( aLine.getStr(), aLine.getLength() );
10387 static double calcAngle( const Rectangle& rRect, const Point& rPoint )
10389 Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
10390 (rRect.Top()+rRect.Bottom()+1)/2);
10391 Point aPoint = rPoint - aOrigin;
10393 double fX = (double)aPoint.X();
10394 double fY = (double)-aPoint.Y();
10396 if ((rRect.GetHeight() == 0) || (rRect.GetWidth() == 0))
10397 throw o3tl::divide_by_zero();
10399 if( rRect.GetWidth() > rRect.GetHeight() )
10400 fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
10401 else if( rRect.GetHeight() > rRect.GetWidth() )
10402 fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
10403 return atan2( fY, fX );
10406 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
10408 MARK( "drawArc" );
10410 updateGraphicsState();
10412 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
10413 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
10414 return;
10416 // calculate start and stop angles
10417 const double fStartAngle = calcAngle( rRect, rStart );
10418 double fStopAngle = calcAngle( rRect, rStop );
10419 while( fStopAngle < fStartAngle )
10420 fStopAngle += 2.0*M_PI;
10421 const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
10422 const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
10423 const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
10424 const double halfWidth = (double)rRect.GetWidth()/2.0;
10425 const double halfHeight = (double)rRect.GetHeight()/2.0;
10427 const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
10428 (rRect.Top()+rRect.Bottom()+1)/2 );
10430 OStringBuffer aLine( 30*nFragments );
10431 Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
10432 -(int)(halfHeight * sin(fStartAngle) ) );
10433 aPoint += aCenter;
10434 m_aPages.back().appendPoint( aPoint, aLine );
10435 aLine.append( " m " );
10436 if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
10438 for( int i = 0; i < nFragments; i++ )
10440 const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
10441 const double fStopFragment = fStartFragment + fFragmentDelta;
10442 aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
10443 -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
10444 aPoint += aCenter;
10445 m_aPages.back().appendPoint( aPoint, aLine );
10446 aLine.append( ' ' );
10448 aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
10449 -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
10450 aPoint += aCenter;
10451 m_aPages.back().appendPoint( aPoint, aLine );
10452 aLine.append( ' ' );
10454 aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
10455 -(int)(halfHeight * sin(fStopFragment) ) );
10456 aPoint += aCenter;
10457 m_aPages.back().appendPoint( aPoint, aLine );
10458 aLine.append( " c\n" );
10461 if( bWithChord || bWithPie )
10463 if( bWithPie )
10465 m_aPages.back().appendPoint( aCenter, aLine );
10466 aLine.append( " l " );
10468 aLine.append( "h " );
10470 if( ! bWithChord && ! bWithPie )
10471 aLine.append( "S\n" );
10472 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
10473 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
10474 aLine.append( "B*\n" );
10475 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10476 aLine.append( "S\n" );
10477 else
10478 aLine.append( "f*\n" );
10480 writeBuffer( aLine.getStr(), aLine.getLength() );
10483 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly )
10485 MARK( "drawPolyLine" );
10487 sal_uInt16 nPoints = rPoly.GetSize();
10488 if( nPoints < 2 )
10489 return;
10491 updateGraphicsState();
10493 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
10494 return;
10496 OStringBuffer aLine( 20 * nPoints );
10497 m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
10498 aLine.append( "S\n" );
10500 writeBuffer( aLine.getStr(), aLine.getLength() );
10503 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo )
10505 MARK( "drawPolyLine with LineInfo" );
10507 updateGraphicsState();
10509 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
10510 return;
10512 OStringBuffer aLine;
10513 aLine.append( "q " );
10514 if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
10516 writeBuffer( aLine.getStr(), aLine.getLength() );
10517 drawPolyLine( rPoly );
10518 writeBuffer( "Q\n", 2 );
10520 else
10522 PDFWriter::ExtLineInfo aInfo;
10523 convertLineInfoToExtLineInfo( rInfo, aInfo );
10524 drawPolyLine( rPoly, aInfo );
10528 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
10530 DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" );
10531 rOut.m_fLineWidth = rIn.GetWidth();
10532 rOut.m_fTransparency = 0.0;
10533 rOut.m_eCap = PDFWriter::capButt;
10534 rOut.m_eJoin = PDFWriter::joinMiter;
10535 rOut.m_fMiterLimit = 10;
10536 rOut.m_aDashArray.clear();
10538 // add DashDot to DashArray
10539 const int nDashes = rIn.GetDashCount();
10540 const int nDashLen = rIn.GetDashLen();
10541 const int nDistance = rIn.GetDistance();
10543 for( int n = 0; n < nDashes; n++ )
10545 rOut.m_aDashArray.push_back( nDashLen );
10546 rOut.m_aDashArray.push_back( nDistance );
10548 const int nDots = rIn.GetDotCount();
10549 const int nDotLen = rIn.GetDotLen();
10551 for( int n = 0; n < nDots; n++ )
10553 rOut.m_aDashArray.push_back( nDotLen );
10554 rOut.m_aDashArray.push_back( nDistance );
10557 // add LineJoin
10558 switch(rIn.GetLineJoin())
10560 case basegfx::B2DLINEJOIN_BEVEL :
10562 rOut.m_eJoin = PDFWriter::joinBevel;
10563 break;
10565 default : // basegfx::B2DLINEJOIN_NONE :
10566 // Pdf has no 'none' lineJoin, default is miter
10567 case basegfx::B2DLINEJOIN_MIDDLE :
10568 case basegfx::B2DLINEJOIN_MITER :
10570 rOut.m_eJoin = PDFWriter::joinMiter;
10571 break;
10573 case basegfx::B2DLINEJOIN_ROUND :
10575 rOut.m_eJoin = PDFWriter::joinRound;
10576 break;
10580 // add LineCap
10581 switch(rIn.GetLineCap())
10583 default: /* com::sun::star::drawing::LineCap_BUTT */
10585 rOut.m_eCap = PDFWriter::capButt;
10586 break;
10588 case com::sun::star::drawing::LineCap_ROUND:
10590 rOut.m_eCap = PDFWriter::capRound;
10591 break;
10593 case com::sun::star::drawing::LineCap_SQUARE:
10595 rOut.m_eCap = PDFWriter::capSquare;
10596 break;
10601 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
10603 MARK( "drawPolyLine with ExtLineInfo" );
10605 updateGraphicsState();
10607 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
10608 return;
10610 if( rInfo.m_fTransparency >= 1.0 )
10611 return;
10613 if( rInfo.m_fTransparency != 0.0 )
10614 beginTransparencyGroup();
10616 OStringBuffer aLine;
10617 aLine.append( "q " );
10618 m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
10619 aLine.append( " w" );
10620 if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader
10622 switch( rInfo.m_eCap )
10624 default:
10625 case PDFWriter::capButt: aLine.append( " 0 J" );break;
10626 case PDFWriter::capRound: aLine.append( " 1 J" );break;
10627 case PDFWriter::capSquare: aLine.append( " 2 J" );break;
10629 switch( rInfo.m_eJoin )
10631 default:
10632 case PDFWriter::joinMiter:
10634 double fLimit = rInfo.m_fMiterLimit;
10635 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
10636 fLimit = fLimit / rInfo.m_fLineWidth;
10637 if( fLimit < 1.0 )
10638 fLimit = 1.0;
10639 aLine.append( " 0 j " );
10640 appendDouble( fLimit, aLine );
10641 aLine.append( " M" );
10643 break;
10644 case PDFWriter::joinRound: aLine.append( " 1 j" );break;
10645 case PDFWriter::joinBevel: aLine.append( " 2 j" );break;
10647 if( rInfo.m_aDashArray.size() > 0 )
10649 aLine.append( " [ " );
10650 for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
10651 it != rInfo.m_aDashArray.end(); ++it )
10653 m_aPages.back().appendMappedLength( *it, aLine );
10654 aLine.append( ' ' );
10656 aLine.append( "] 0 d" );
10658 aLine.append( "\n" );
10659 writeBuffer( aLine.getStr(), aLine.getLength() );
10660 drawPolyLine( rPoly );
10662 else
10664 basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
10665 basegfx::B2DPolyPolygon aPolyPoly;
10667 basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
10669 // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
10670 // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
10671 // this line needs to be removed and the loop below adapted accordingly
10672 aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
10674 const sal_uInt32 nPolygonCount(aPolyPoly.count());
10676 for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
10678 aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
10679 aPoly = aPolyPoly.getB2DPolygon( nPoly );
10680 const sal_uInt32 nPointCount(aPoly.count());
10682 if(nPointCount)
10684 const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
10685 basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
10687 for(sal_uInt32 a(0); a < nEdgeCount; a++)
10689 if( a > 0 )
10690 aLine.append( " " );
10691 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
10692 const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
10694 m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
10695 FRound(aCurrent.getY()) ),
10696 aLine );
10697 aLine.append( " m " );
10698 m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
10699 FRound(aNext.getY()) ),
10700 aLine );
10701 aLine.append( " l" );
10703 // prepare next edge
10704 aCurrent = aNext;
10708 aLine.append( " S " );
10709 writeBuffer( aLine.getStr(), aLine.getLength() );
10711 writeBuffer( "Q\n", 2 );
10713 if( rInfo.m_fTransparency != 0.0 )
10715 // FIXME: actually this may be incorrect with bezier polygons
10716 Rectangle aBoundRect( rPoly.GetBoundRect() );
10717 // avoid clipping with thick lines
10718 if( rInfo.m_fLineWidth > 0.0 )
10720 sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
10721 aBoundRect.Top() -= nLW;
10722 aBoundRect.Left() -= nLW;
10723 aBoundRect.Right() += nLW;
10724 aBoundRect.Bottom() += nLW;
10726 endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) );
10730 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
10732 MARK( "drawPixel" );
10734 Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
10736 if( aColor == Color( COL_TRANSPARENT ) )
10737 return;
10739 // pixels are drawn in line color, so have to set
10740 // the nonstroking color to line color
10741 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
10742 setFillColor( aColor );
10744 updateGraphicsState();
10746 OStringBuffer aLine( 20 );
10747 m_aPages.back().appendPoint( rPoint, aLine );
10748 aLine.append( ' ' );
10749 appendDouble( 1.0/double(getReferenceDevice()->GetDPIX()), aLine );
10750 aLine.append( ' ' );
10751 appendDouble( 1.0/double(getReferenceDevice()->GetDPIY()), aLine );
10752 aLine.append( " re f\n" );
10753 writeBuffer( aLine.getStr(), aLine.getLength() );
10755 setFillColor( aOldFillColor );
10758 class AccessReleaser
10760 BitmapReadAccess* m_pAccess;
10761 public:
10762 AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){}
10763 ~AccessReleaser() { delete m_pAccess; }
10766 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
10768 CHECK_RETURN( updateObject( rObject.m_nObject ) );
10770 bool bFlateFilter = compressStream( rObject.m_pContentStream );
10771 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
10772 sal_uLong nSize = rObject.m_pContentStream->Tell();
10773 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
10774 #if OSL_DEBUG_LEVEL > 1
10775 emitComment( "PDFWriterImpl::writeTransparentObject" );
10776 #endif
10777 OStringBuffer aLine( 512 );
10778 CHECK_RETURN( updateObject( rObject.m_nObject ) );
10779 aLine.append( rObject.m_nObject );
10780 aLine.append( " 0 obj\n"
10781 "<</Type/XObject\n"
10782 "/Subtype/Form\n"
10783 "/BBox[ " );
10784 appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
10785 aLine.append( ' ' );
10786 appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
10787 aLine.append( ' ' );
10788 appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
10789 aLine.append( ' ' );
10790 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
10791 aLine.append( " ]\n" );
10792 if( ! rObject.m_pSoftMaskStream )
10794 if( ! m_bIsPDF_A1 )
10796 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
10799 /* #i42884# the PDF reference recommends that each Form XObject
10800 * should have a resource dict; alas if that is the same object
10801 * as the one of the page it triggers an endless recursion in
10802 * acroread 5 (6 and up have that fixed). Since we have only one
10803 * resource dict anyway, let's use the one from the page by NOT
10804 * emitting a Resources entry.
10807 aLine.append( "/Length " );
10808 aLine.append( (sal_Int32)(nSize) );
10809 aLine.append( "\n" );
10810 if( bFlateFilter )
10811 aLine.append( "/Filter/FlateDecode\n" );
10812 aLine.append( ">>\n"
10813 "stream\n" );
10814 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10815 checkAndEnableStreamEncryption( rObject.m_nObject );
10816 CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
10817 disableStreamEncryption();
10818 aLine.setLength( 0 );
10819 aLine.append( "\n"
10820 "endstream\n"
10821 "endobj\n\n" );
10822 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10824 // write ExtGState dict for this XObject
10825 aLine.setLength( 0 );
10826 aLine.append( rObject.m_nExtGStateObject );
10827 aLine.append( " 0 obj\n"
10828 "<<" );
10829 if( ! rObject.m_pSoftMaskStream )
10831 //i59651
10832 if( m_bIsPDF_A1 )
10834 aLine.append( "/CA 1.0/ca 1.0" );
10835 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
10837 else
10839 aLine.append( "/CA " );
10840 appendDouble( rObject.m_fAlpha, aLine );
10841 aLine.append( "\n"
10842 " /ca " );
10843 appendDouble( rObject.m_fAlpha, aLine );
10845 aLine.append( "\n" );
10847 else
10849 if( m_bIsPDF_A1 )
10851 aLine.append( "/SMask/None" );
10852 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
10854 else
10856 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
10857 sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
10858 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
10859 sal_Int32 nMaskObject = createObject();
10860 aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
10861 aLine.append( nMaskObject );
10862 aLine.append( " 0 R>>\n" );
10864 OStringBuffer aMask;
10865 aMask.append( nMaskObject );
10866 aMask.append( " 0 obj\n"
10867 "<</Type/XObject\n"
10868 "/Subtype/Form\n"
10869 "/BBox[" );
10870 appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
10871 aMask.append( ' ' );
10872 appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
10873 aMask.append( ' ' );
10874 appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
10875 aMask.append( ' ' );
10876 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
10877 aMask.append( "]\n" );
10879 /* #i42884# see above */
10880 aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
10881 aMask.append( "/Length " );
10882 aMask.append( nMaskSize );
10883 aMask.append( ">>\n"
10884 "stream\n" );
10885 CHECK_RETURN( updateObject( nMaskObject ) );
10886 checkAndEnableStreamEncryption( nMaskObject );
10887 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
10888 CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
10889 disableStreamEncryption();
10890 aMask.setLength( 0 );
10891 aMask.append( "\nendstream\n"
10892 "endobj\n\n" );
10893 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
10896 aLine.append( ">>\n"
10897 "endobj\n\n" );
10898 CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) );
10899 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10901 return true;
10904 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
10906 // LO internal gradient -> PDF shading type:
10907 // * GradientStyle_LINEAR: axial shading, using sampled-function with 2 samples
10908 // [t=0:colorStart, t=1:colorEnd]
10909 // * GradientStyle_AXIAL: axial shading, using sampled-function with 3 samples
10910 // [t=0:colorEnd, t=0.5:colorStart, t=1:colorEnd]
10911 // * other styles: function shading with aSize.Width() * aSize.Height() samples
10912 sal_Int32 nFunctionObject = createObject();
10913 CHECK_RETURN( updateObject( nFunctionObject ) );
10915 ScopedVclPtrInstance< VirtualDevice > aDev;
10916 aDev->SetOutputSizePixel( rObject.m_aSize );
10917 aDev->SetMapMode( MapMode( MAP_PIXEL ) );
10918 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
10919 aDev->SetDrawMode( aDev->GetDrawMode() |
10920 ( DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText |
10921 DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient ) );
10922 aDev->DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
10924 Bitmap aSample = aDev->GetBitmap( Point( 0, 0 ), rObject.m_aSize );
10925 BitmapReadAccess* pAccess = aSample.AcquireReadAccess();
10926 AccessReleaser aReleaser( pAccess );
10928 Size aSize = aSample.GetSizePixel();
10930 sal_Int32 nStreamLengthObject = createObject();
10931 #if OSL_DEBUG_LEVEL > 1
10932 emitComment( "PDFWriterImpl::writeGradientFunction" );
10933 #endif
10934 OStringBuffer aLine( 120 );
10935 aLine.append( nFunctionObject );
10936 aLine.append( " 0 obj\n"
10937 "<</FunctionType 0\n");
10938 switch (rObject.m_aGradient.GetStyle())
10940 case GradientStyle_LINEAR:
10941 case GradientStyle_AXIAL:
10942 aLine.append("/Domain[ 0 1]\n");
10943 break;
10944 default:
10945 aLine.append("/Domain[ 0 1 0 1]\n");
10947 aLine.append("/Size[ " );
10948 switch (rObject.m_aGradient.GetStyle())
10950 case GradientStyle_LINEAR:
10951 aLine.append('2');
10952 break;
10953 case GradientStyle_AXIAL:
10954 aLine.append('3');
10955 break;
10956 default:
10957 aLine.append( (sal_Int32)aSize.Width() );
10958 aLine.append( ' ' );
10959 aLine.append( (sal_Int32)aSize.Height() );
10961 aLine.append( " ]\n"
10962 "/BitsPerSample 8\n"
10963 "/Range[ 0 1 0 1 0 1 ]\n"
10964 "/Order 3\n"
10965 "/Length " );
10966 aLine.append( nStreamLengthObject );
10967 aLine.append( " 0 R\n"
10968 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
10969 "/Filter/FlateDecode"
10970 #endif
10971 ">>\n"
10972 "stream\n" );
10973 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10975 sal_uInt64 nStartStreamPos = 0;
10976 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nStartStreamPos)) );
10978 checkAndEnableStreamEncryption( nFunctionObject );
10979 beginCompression();
10980 sal_uInt8 aCol[3];
10981 switch (rObject.m_aGradient.GetStyle())
10983 case GradientStyle_AXIAL:
10984 aCol[0] = rObject.m_aGradient.GetEndColor().GetRed();
10985 aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen();
10986 aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue();
10987 CHECK_RETURN( writeBuffer( aCol, 3 ) );
10988 // fall-through
10989 case GradientStyle_LINEAR:
10991 aCol[0] = rObject.m_aGradient.GetStartColor().GetRed();
10992 aCol[1] = rObject.m_aGradient.GetStartColor().GetGreen();
10993 aCol[2] = rObject.m_aGradient.GetStartColor().GetBlue();
10994 CHECK_RETURN( writeBuffer( aCol, 3 ) );
10996 aCol[0] = rObject.m_aGradient.GetEndColor().GetRed();
10997 aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen();
10998 aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue();
10999 CHECK_RETURN( writeBuffer( aCol, 3 ) );
11000 break;
11002 default:
11003 for( int y = aSize.Height()-1; y >= 0; y-- )
11005 for( int x = 0; x < aSize.Width(); x++ )
11007 BitmapColor aColor = pAccess->GetColor( y, x );
11008 aCol[0] = aColor.GetRed();
11009 aCol[1] = aColor.GetGreen();
11010 aCol[2] = aColor.GetBlue();
11011 CHECK_RETURN( writeBuffer( aCol, 3 ) );
11015 endCompression();
11016 disableStreamEncryption();
11018 sal_uInt64 nEndStreamPos = 0;
11019 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nEndStreamPos)) );
11021 aLine.setLength( 0 );
11022 aLine.append( "\nendstream\nendobj\n\n" );
11023 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11025 // write stream length
11026 CHECK_RETURN( updateObject( nStreamLengthObject ) );
11027 aLine.setLength( 0 );
11028 aLine.append( nStreamLengthObject );
11029 aLine.append( " 0 obj\n" );
11030 aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
11031 aLine.append( "\nendobj\n\n" );
11032 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11034 CHECK_RETURN( updateObject( rObject.m_nObject ) );
11035 aLine.setLength( 0 );
11036 aLine.append( rObject.m_nObject );
11037 aLine.append( " 0 obj\n");
11038 switch (rObject.m_aGradient.GetStyle())
11040 case GradientStyle_LINEAR:
11041 case GradientStyle_AXIAL:
11042 aLine.append("<</ShadingType 2\n");
11043 break;
11044 default:
11045 aLine.append("<</ShadingType 1\n");
11047 aLine.append("/ColorSpace/DeviceRGB\n"
11048 "/AntiAlias true\n");
11050 // Determination of shading axis
11051 // See: OutputDevice::ImplDrawLinearGradient for reference
11052 Rectangle aRect;
11053 aRect.Left() = aRect.Top() = 0;
11054 aRect.Right() = aSize.Width();
11055 aRect.Bottom() = aSize.Height();
11057 Rectangle aBoundRect;
11058 Point aCenter;
11059 sal_uInt16 nAngle = rObject.m_aGradient.GetAngle() % 3600;
11060 rObject.m_aGradient.GetBoundRect( aRect, aBoundRect, aCenter );
11062 const bool bLinear = (rObject.m_aGradient.GetStyle() == GradientStyle_LINEAR);
11063 double fBorder = aBoundRect.GetHeight() * rObject.m_aGradient.GetBorder() / 100.0;
11064 if ( !bLinear )
11066 fBorder /= 2.0;
11069 aBoundRect.Bottom() -= fBorder;
11070 if (!bLinear)
11072 aBoundRect.Top() += fBorder;
11075 switch (rObject.m_aGradient.GetStyle())
11077 case GradientStyle_LINEAR:
11078 case GradientStyle_AXIAL:
11080 aLine.append("/Domain[ 0 1 ]\n"
11081 "/Coords[ " );
11082 Polygon aPoly( 2 );
11083 aPoly[0] = aBoundRect.BottomCenter();
11084 aPoly[1] = aBoundRect.TopCenter();
11085 aPoly.Rotate( aCenter, 3600 - nAngle );
11087 aLine.append( (sal_Int32) aPoly[0].X() );
11088 aLine.append( " " );
11089 aLine.append( (sal_Int32) aPoly[0].Y() );
11090 aLine.append( " " );
11091 aLine.append( (sal_Int32) aPoly[1].X());
11092 aLine.append( " ");
11093 aLine.append( (sal_Int32) aPoly[1].Y());
11094 aLine.append( " ]\n");
11095 aLine.append("/Extend [true true]\n");
11096 break;
11098 default:
11099 aLine.append("/Domain[ 0 1 0 1 ]\n"
11100 "/Matrix[ " );
11101 aLine.append( (sal_Int32)aSize.Width() );
11102 aLine.append( " 0 0 " );
11103 aLine.append( (sal_Int32)aSize.Height() );
11104 aLine.append( " 0 0 ]\n");
11106 aLine.append("/Function " );
11107 aLine.append( nFunctionObject );
11108 aLine.append( " 0 R\n"
11109 ">>\n"
11110 "endobj\n\n" );
11111 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11113 return true;
11116 bool PDFWriterImpl::writeJPG( JPGEmit& rObject )
11118 CHECK_RETURN( rObject.m_pStream );
11119 CHECK_RETURN( updateObject( rObject.m_nObject ) );
11121 sal_Int32 nLength = 0;
11122 rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
11123 nLength = rObject.m_pStream->Tell();
11124 rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
11126 sal_Int32 nMaskObject = 0;
11127 if( !!rObject.m_aMask )
11129 if( rObject.m_aMask.GetBitCount() == 1 ||
11130 ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651
11133 nMaskObject = createObject();
11135 else if( m_bIsPDF_A1 )
11136 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
11137 else if( m_aContext.Version < PDFWriter::PDF_1_4 )
11138 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
11141 #if OSL_DEBUG_LEVEL > 1
11142 emitComment( "PDFWriterImpl::writeJPG" );
11143 #endif
11145 OStringBuffer aLine(200);
11146 aLine.append( rObject.m_nObject );
11147 aLine.append( " 0 obj\n"
11148 "<</Type/XObject/Subtype/Image/Width " );
11149 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
11150 aLine.append( " /Height " );
11151 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
11152 aLine.append( " /BitsPerComponent 8 " );
11153 if( rObject.m_bTrueColor )
11154 aLine.append( "/ColorSpace/DeviceRGB" );
11155 else
11156 aLine.append( "/ColorSpace/DeviceGray" );
11157 aLine.append( "/Filter/DCTDecode/Length " );
11158 aLine.append( nLength );
11159 if( nMaskObject )
11161 aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
11162 aLine.append( nMaskObject );
11163 aLine.append( " 0 R " );
11165 aLine.append( ">>\nstream\n" );
11166 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11168 checkAndEnableStreamEncryption( rObject.m_nObject );
11169 CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
11170 disableStreamEncryption();
11172 aLine.setLength( 0 );
11173 aLine.append( "\nendstream\nendobj\n\n" );
11174 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11176 if( nMaskObject )
11178 BitmapEmit aEmit;
11179 aEmit.m_nObject = nMaskObject;
11180 if( rObject.m_aMask.GetBitCount() == 1 )
11181 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
11182 else if( rObject.m_aMask.GetBitCount() == 8 )
11183 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
11184 writeBitmapObject( aEmit, true );
11187 return true;
11190 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
11192 CHECK_RETURN( updateObject( rObject.m_nObject ) );
11194 Bitmap aBitmap;
11195 Color aTransparentColor( COL_TRANSPARENT );
11196 bool bWriteMask = false;
11197 if( ! bMask )
11199 aBitmap = rObject.m_aBitmap.GetBitmap();
11200 if( rObject.m_aBitmap.IsAlpha() )
11202 if( m_aContext.Version >= PDFWriter::PDF_1_4 )
11203 bWriteMask = true;
11204 // else draw without alpha channel
11206 else
11208 switch( rObject.m_aBitmap.GetTransparentType() )
11210 case TRANSPARENT_NONE:
11211 // comes from drawMask function
11212 if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask )
11213 bMask = true;
11214 break;
11215 case TRANSPARENT_COLOR:
11216 aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
11217 break;
11218 case TRANSPARENT_BITMAP:
11219 bWriteMask = true;
11220 break;
11224 else
11226 if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
11228 aBitmap = rObject.m_aBitmap.GetMask();
11229 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
11230 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
11232 else if( aBitmap.GetBitCount() != 8 )
11234 aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap();
11235 aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
11236 DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" );
11240 BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();
11241 AccessReleaser aReleaser( pAccess );
11243 bool bTrueColor;
11244 sal_Int32 nBitsPerComponent;
11245 switch( aBitmap.GetBitCount() )
11247 case 1:
11248 case 2:
11249 case 4:
11250 case 8:
11251 bTrueColor = false;
11252 nBitsPerComponent = aBitmap.GetBitCount();
11253 break;
11254 default:
11255 bTrueColor = true;
11256 nBitsPerComponent = 8;
11257 break;
11260 sal_Int32 nStreamLengthObject = createObject();
11261 sal_Int32 nMaskObject = 0;
11263 #if OSL_DEBUG_LEVEL > 1
11264 emitComment( "PDFWriterImpl::writeBitmapObject" );
11265 #endif
11266 OStringBuffer aLine(1024);
11267 aLine.append( rObject.m_nObject );
11268 aLine.append( " 0 obj\n"
11269 "<</Type/XObject/Subtype/Image/Width " );
11270 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
11271 aLine.append( "/Height " );
11272 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
11273 aLine.append( "/BitsPerComponent " );
11274 aLine.append( nBitsPerComponent );
11275 aLine.append( "/Length " );
11276 aLine.append( nStreamLengthObject );
11277 aLine.append( " 0 R\n" );
11278 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
11279 if( nBitsPerComponent != 1 )
11281 aLine.append( "/Filter/FlateDecode" );
11283 else
11285 aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
11286 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
11287 aLine.append( ">>\n" );
11289 #endif
11290 if( ! bMask )
11292 aLine.append( "/ColorSpace" );
11293 if( bTrueColor )
11294 aLine.append( "/DeviceRGB\n" );
11295 else if( aBitmap.HasGreyPalette() )
11297 aLine.append( "/DeviceGray\n" );
11298 if( aBitmap.GetBitCount() == 1 )
11300 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
11301 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
11302 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
11303 if( nBlackIndex == 1 )
11304 aLine.append( "/Decode[1 0]\n" );
11307 else
11309 aLine.append( "[ /Indexed/DeviceRGB " );
11310 aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
11311 aLine.append( "\n<" );
11312 if( m_aContext.Encryption.Encrypt() )
11314 enableStringEncryption( rObject.m_nObject );
11315 //check encryption buffer size
11316 if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
11318 int nChar = 0;
11319 //fill the encryption buffer
11320 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
11322 const BitmapColor& rColor = pAccess->GetPaletteColor( i );
11323 m_pEncryptionBuffer[nChar++] = rColor.GetRed();
11324 m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
11325 m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
11327 //encrypt the colorspace lookup table
11328 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
11329 //now queue the data for output
11330 nChar = 0;
11331 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
11333 appendHex(m_pEncryptionBuffer[nChar++], aLine );
11334 appendHex(m_pEncryptionBuffer[nChar++], aLine );
11335 appendHex(m_pEncryptionBuffer[nChar++], aLine );
11339 else //no encryption requested (PDF/A-1a program flow drops here)
11341 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
11343 const BitmapColor& rColor = pAccess->GetPaletteColor( i );
11344 appendHex( rColor.GetRed(), aLine );
11345 appendHex( rColor.GetGreen(), aLine );
11346 appendHex( rColor.GetBlue(), aLine );
11349 aLine.append( ">\n]\n" );
11352 else
11354 if( aBitmap.GetBitCount() == 1 )
11356 aLine.append( "/ImageMask true\n" );
11357 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
11358 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
11359 if( nBlackIndex )
11360 aLine.append( "/Decode[ 1 0 ]\n" );
11361 else
11362 aLine.append( "/Decode[ 0 1 ]\n" );
11364 else if( aBitmap.GetBitCount() == 8 )
11366 aLine.append( "/ColorSpace/DeviceGray\n"
11367 "/Decode [ 1 0 ]\n" );
11371 if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651
11373 if( bWriteMask )
11375 nMaskObject = createObject();
11376 if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 )
11377 aLine.append( "/SMask " );
11378 else
11379 aLine.append( "/Mask " );
11380 aLine.append( nMaskObject );
11381 aLine.append( " 0 R\n" );
11383 else if( aTransparentColor != Color( COL_TRANSPARENT ) )
11385 aLine.append( "/Mask[ " );
11386 if( bTrueColor )
11388 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
11389 aLine.append( ' ' );
11390 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
11391 aLine.append( ' ' );
11392 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
11393 aLine.append( ' ' );
11394 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
11395 aLine.append( ' ' );
11396 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
11397 aLine.append( ' ' );
11398 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
11400 else
11402 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
11403 aLine.append( nIndex );
11405 aLine.append( " ]\n" );
11408 else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) )
11409 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
11411 aLine.append( ">>\n"
11412 "stream\n" );
11413 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11414 sal_uInt64 nStartPos = 0;
11415 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nStartPos)) );
11417 checkAndEnableStreamEncryption( rObject.m_nObject );
11418 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
11419 if( nBitsPerComponent == 1 )
11421 writeG4Stream( pAccess );
11423 else
11424 #endif
11426 beginCompression();
11427 if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
11429 const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U );
11431 for( int i = 0; i < pAccess->Height(); i++ )
11433 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
11436 else
11438 const int nScanLineBytes = pAccess->Width()*3;
11439 boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] );
11440 for( int y = 0; y < pAccess->Height(); y++ )
11442 for( int x = 0; x < pAccess->Width(); x++ )
11444 BitmapColor aColor = pAccess->GetColor( y, x );
11445 pCol[3*x+0] = aColor.GetRed();
11446 pCol[3*x+1] = aColor.GetGreen();
11447 pCol[3*x+2] = aColor.GetBlue();
11449 CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) );
11452 endCompression();
11454 disableStreamEncryption();
11456 sal_uInt64 nEndPos = 0;
11457 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nEndPos)) );
11458 aLine.setLength( 0 );
11459 aLine.append( "\nendstream\nendobj\n\n" );
11460 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11461 CHECK_RETURN( updateObject( nStreamLengthObject ) );
11462 aLine.setLength( 0 );
11463 aLine.append( nStreamLengthObject );
11464 aLine.append( " 0 obj\n" );
11465 aLine.append( (sal_Int64)(nEndPos-nStartPos) );
11466 aLine.append( "\nendobj\n\n" );
11467 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11469 if( nMaskObject )
11471 BitmapEmit aEmit;
11472 aEmit.m_nObject = nMaskObject;
11473 aEmit.m_aBitmap = rObject.m_aBitmap;
11474 return writeBitmapObject( aEmit, true );
11477 return true;
11480 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask )
11482 MARK( "drawJPGBitmap" );
11484 OStringBuffer aLine( 80 );
11485 updateGraphicsState();
11487 // #i40055# sanity check
11488 if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
11489 return;
11490 if( ! (rSizePixel.Width() && rSizePixel.Height()) )
11491 return;
11493 rDCTData.Seek( 0 );
11494 if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale )
11496 // need to convert to grayscale;
11497 // load stream to bitmap and draw the bitmap instead
11498 Graphic aGraphic;
11499 GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG );
11500 Bitmap aBmp( aGraphic.GetBitmap() );
11501 if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() )
11503 BitmapEx aBmpEx( aBmp, rMask );
11504 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx );
11506 else
11507 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp );
11508 return;
11511 SvMemoryStream* pStream = new SvMemoryStream;
11512 pStream->WriteStream( rDCTData );
11513 pStream->Seek( STREAM_SEEK_TO_END );
11515 BitmapID aID;
11516 aID.m_aPixelSize = rSizePixel;
11517 aID.m_nSize = pStream->Tell();
11518 pStream->Seek( STREAM_SEEK_TO_BEGIN );
11519 aID.m_nChecksum = vcl_get_checksum( 0, pStream->GetData(), aID.m_nSize );
11520 if( ! rMask.IsEmpty() )
11521 aID.m_nMaskChecksum = rMask.GetChecksum();
11523 std::list< JPGEmit >::const_iterator it;
11524 for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
11526 if( it == m_aJPGs.end() )
11528 m_aJPGs.push_front( JPGEmit() );
11529 JPGEmit& rEmit = m_aJPGs.front();
11530 rEmit.m_nObject = createObject();
11531 rEmit.m_aID = aID;
11532 rEmit.m_pStream = pStream;
11533 rEmit.m_bTrueColor = bIsTrueColor;
11534 if( !! rMask && rMask.GetSizePixel() == rSizePixel )
11535 rEmit.m_aMask = rMask;
11537 it = m_aJPGs.begin();
11539 else
11540 delete pStream;
11542 aLine.append( "q " );
11543 sal_Int32 nCheckWidth = 0;
11544 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth );
11545 aLine.append( " 0 0 " );
11546 sal_Int32 nCheckHeight = 0;
11547 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight );
11548 aLine.append( ' ' );
11549 m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
11550 aLine.append( " cm\n/Im" );
11551 aLine.append( it->m_nObject );
11552 aLine.append( " Do Q\n" );
11553 if( nCheckWidth == 0 || nCheckHeight == 0 )
11555 // #i97512# avoid invalid current matrix
11556 aLine.setLength( 0 );
11557 aLine.append( "\n%jpeg image /Im" );
11558 aLine.append( it->m_nObject );
11559 aLine.append( " scaled to zero size, omitted\n" );
11561 writeBuffer( aLine.getStr(), aLine.getLength() );
11563 OStringBuffer aObjName( 16 );
11564 aObjName.append( "Im" );
11565 aObjName.append( it->m_nObject );
11566 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
11570 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
11572 OStringBuffer aLine( 80 );
11573 updateGraphicsState();
11575 aLine.append( "q " );
11576 if( rFillColor != Color( COL_TRANSPARENT ) )
11578 appendNonStrokingColor( rFillColor, aLine );
11579 aLine.append( ' ' );
11581 sal_Int32 nCheckWidth = 0;
11582 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth );
11583 aLine.append( " 0 0 " );
11584 sal_Int32 nCheckHeight = 0;
11585 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight );
11586 aLine.append( ' ' );
11587 m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
11588 aLine.append( " cm\n/Im" );
11589 aLine.append( rBitmap.m_nObject );
11590 aLine.append( " Do Q\n" );
11591 if( nCheckWidth == 0 || nCheckHeight == 0 )
11593 // #i97512# avoid invalid current matrix
11594 aLine.setLength( 0 );
11595 aLine.append( "\n%bitmap image /Im" );
11596 aLine.append( rBitmap.m_nObject );
11597 aLine.append( " scaled to zero size, omitted\n" );
11599 writeBuffer( aLine.getStr(), aLine.getLength() );
11602 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask )
11604 BitmapEx aBitmap( i_rBitmap );
11605 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
11607 BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS;
11608 int nDepth = aBitmap.GetBitmap().GetBitCount();
11609 if( nDepth <= 4 )
11610 eConv = BMP_CONVERSION_4BIT_GREYS;
11611 if( nDepth > 1 )
11612 aBitmap.Convert( eConv );
11614 BitmapID aID;
11615 aID.m_aPixelSize = aBitmap.GetSizePixel();
11616 aID.m_nSize = aBitmap.GetBitCount();
11617 aID.m_nChecksum = aBitmap.GetBitmap().GetChecksum();
11618 aID.m_nMaskChecksum = 0;
11619 if( aBitmap.IsAlpha() )
11620 aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum();
11621 else
11623 Bitmap aMask = aBitmap.GetMask();
11624 if( ! aMask.IsEmpty() )
11625 aID.m_nMaskChecksum = aMask.GetChecksum();
11627 std::list< BitmapEmit >::const_iterator it;
11628 for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
11630 if( aID == it->m_aID )
11631 break;
11633 if( it == m_aBitmaps.end() )
11635 m_aBitmaps.push_front( BitmapEmit() );
11636 m_aBitmaps.front().m_aID = aID;
11637 m_aBitmaps.front().m_aBitmap = aBitmap;
11638 m_aBitmaps.front().m_nObject = createObject();
11639 m_aBitmaps.front().m_bDrawMask = bDrawMask;
11640 it = m_aBitmaps.begin();
11643 OStringBuffer aObjName( 16 );
11644 aObjName.append( "Im" );
11645 aObjName.append( it->m_nObject );
11646 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
11648 return *it;
11651 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap )
11653 MARK( "drawBitmap (Bitmap)" );
11655 // #i40055# sanity check
11656 if( ! (rDestSize.Width() && rDestSize.Height()) )
11657 return;
11659 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) );
11660 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
11663 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
11665 MARK( "drawBitmap (BitmapEx)" );
11667 // #i40055# sanity check
11668 if( ! (rDestSize.Width() && rDestSize.Height()) )
11669 return;
11671 const BitmapEmit& rEmit = createBitmapEmit( rBitmap );
11672 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
11675 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
11677 Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
11678 MapMode( MAP_POINT ),
11679 getReferenceDevice(),
11680 rSize ) );
11681 // check if we already have this gradient
11682 std::list<GradientEmit>::iterator it;
11683 // rounding to point will generally lose some pixels
11684 // round up to point boundary
11685 aPtSize.Width()++;
11686 aPtSize.Height()++;
11687 for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
11689 if( it->m_aGradient == rGradient )
11691 if( it->m_aSize == aPtSize )
11692 break;
11695 if( it == m_aGradients.end() )
11697 m_aGradients.push_front( GradientEmit() );
11698 m_aGradients.front().m_aGradient = rGradient;
11699 m_aGradients.front().m_nObject = createObject();
11700 m_aGradients.front().m_aSize = aPtSize;
11701 it = m_aGradients.begin();
11704 OStringBuffer aObjName( 16 );
11705 aObjName.append( 'P' );
11706 aObjName.append( it->m_nObject );
11707 pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
11709 return it->m_nObject;
11712 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient )
11714 MARK( "drawGradient (Rectangle)" );
11716 if( m_aContext.Version == PDFWriter::PDF_1_2 )
11718 drawRectangle( rRect );
11719 return;
11722 sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
11724 Point aTranslate( rRect.BottomLeft() );
11725 aTranslate += Point( 0, 1 );
11727 updateGraphicsState();
11729 OStringBuffer aLine( 80 );
11730 aLine.append( "q 1 0 0 1 " );
11731 m_aPages.back().appendPoint( aTranslate, aLine );
11732 aLine.append( " cm " );
11733 // if a stroke is appended reset the clip region before stroke
11734 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
11735 aLine.append( "q " );
11736 aLine.append( "0 0 " );
11737 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
11738 aLine.append( ' ' );
11739 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
11740 aLine.append( " re W n\n" );
11742 aLine.append( "/P" );
11743 aLine.append( nGradient );
11744 aLine.append( " sh " );
11745 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
11747 aLine.append( "Q 0 0 " );
11748 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
11749 aLine.append( ' ' );
11750 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
11751 aLine.append( " re S " );
11753 aLine.append( "Q\n" );
11754 writeBuffer( aLine.getStr(), aLine.getLength() );
11757 void PDFWriterImpl::drawHatch( const tools::PolyPolygon& rPolyPoly, const Hatch& rHatch )
11759 MARK( "drawHatch" );
11761 updateGraphicsState();
11763 if( rPolyPoly.Count() )
11765 tools::PolyPolygon aPolyPoly( rPolyPoly );
11767 aPolyPoly.Optimize( PolyOptimizeFlags::NO_SAME );
11768 push( PushFlags::LINECOLOR );
11769 setLineColor( rHatch.GetColor() );
11770 getReferenceDevice()->DrawHatch( aPolyPoly, rHatch, false );
11771 pop();
11775 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall )
11777 MARK( "drawWallpaper" );
11779 bool bDrawColor = false;
11780 bool bDrawGradient = false;
11781 bool bDrawBitmap = false;
11783 BitmapEx aBitmap;
11784 Point aBmpPos = rRect.TopLeft();
11785 Size aBmpSize;
11786 if( rWall.IsBitmap() )
11788 aBitmap = rWall.GetBitmap();
11789 aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
11790 getMapMode(),
11791 getReferenceDevice(),
11792 aBitmap.GetPrefSize() );
11793 Rectangle aRect( rRect );
11794 if( rWall.IsRect() )
11796 aRect = rWall.GetRect();
11797 aBmpPos = aRect.TopLeft();
11798 aBmpSize = aRect.GetSize();
11800 if( rWall.GetStyle() != WALLPAPER_SCALE )
11802 if( rWall.GetStyle() != WALLPAPER_TILE )
11804 bDrawBitmap = true;
11805 if( rWall.IsGradient() )
11806 bDrawGradient = true;
11807 else
11808 bDrawColor = true;
11809 switch( rWall.GetStyle() )
11811 case WALLPAPER_TOPLEFT:
11812 break;
11813 case WALLPAPER_TOP:
11814 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
11815 break;
11816 case WALLPAPER_LEFT:
11817 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
11818 break;
11819 case WALLPAPER_TOPRIGHT:
11820 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
11821 break;
11822 case WALLPAPER_CENTER:
11823 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
11824 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
11825 break;
11826 case WALLPAPER_RIGHT:
11827 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
11828 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
11829 break;
11830 case WALLPAPER_BOTTOMLEFT:
11831 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
11832 break;
11833 case WALLPAPER_BOTTOM:
11834 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
11835 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
11836 break;
11837 case WALLPAPER_BOTTOMRIGHT:
11838 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
11839 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
11840 break;
11841 default: ;
11844 else
11846 // push the bitmap
11847 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) );
11849 // convert to page coordinates; this needs to be done here
11850 // since the emit does not know the page anymore
11851 Rectangle aConvertRect( aBmpPos, aBmpSize );
11852 m_aPages.back().convertRect( aConvertRect );
11854 OStringBuffer aNameBuf(16);
11855 aNameBuf.append( "Im" );
11856 aNameBuf.append( rEmit.m_nObject );
11857 OString aImageName( aNameBuf.makeStringAndClear() );
11859 // push the pattern
11860 OStringBuffer aTilingStream( 32 );
11861 appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
11862 aTilingStream.append( " 0 0 " );
11863 appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
11864 aTilingStream.append( " 0 0 cm\n/" );
11865 aTilingStream.append( aImageName );
11866 aTilingStream.append( " Do\n" );
11868 m_aTilings.push_back( TilingEmit() );
11869 m_aTilings.back().m_nObject = createObject();
11870 m_aTilings.back().m_aRectangle = Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
11871 m_aTilings.back().m_pTilingStream = new SvMemoryStream();
11872 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() );
11873 // phase the tiling so wallpaper begins on upper left
11874 if ((aConvertRect.GetWidth() == 0) || (aConvertRect.GetHeight() == 0))
11875 throw o3tl::divide_by_zero();
11876 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
11877 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
11878 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
11880 updateGraphicsState();
11882 OStringBuffer aObjName( 16 );
11883 aObjName.append( 'P' );
11884 aObjName.append( m_aTilings.back().m_nObject );
11885 OString aPatternName( aObjName.makeStringAndClear() );
11886 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
11888 // fill a rRect with the pattern
11889 OStringBuffer aLine( 100 );
11890 aLine.append( "q /Pattern cs /" );
11891 aLine.append( aPatternName );
11892 aLine.append( " scn " );
11893 m_aPages.back().appendRect( rRect, aLine );
11894 aLine.append( " f Q\n" );
11895 writeBuffer( aLine.getStr(), aLine.getLength() );
11898 else
11900 aBmpPos = aRect.TopLeft();
11901 aBmpSize = aRect.GetSize();
11902 bDrawBitmap = true;
11905 if( aBitmap.IsTransparent() )
11907 if( rWall.IsGradient() )
11908 bDrawGradient = true;
11909 else
11910 bDrawColor = true;
11913 else if( rWall.IsGradient() )
11914 bDrawGradient = true;
11915 else
11916 bDrawColor = true;
11918 if( bDrawGradient )
11920 drawGradient( rRect, rWall.GetGradient() );
11922 if( bDrawColor )
11924 Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
11925 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
11926 setLineColor( Color( COL_TRANSPARENT ) );
11927 setFillColor( rWall.GetColor() );
11928 drawRectangle( rRect );
11929 setLineColor( aOldLineColor );
11930 setFillColor( aOldFillColor );
11932 if( bDrawBitmap )
11934 // set temporary clip region since aBmpPos and aBmpSize
11935 // may be outside rRect
11936 OStringBuffer aLine( 20 );
11937 aLine.append( "q " );
11938 m_aPages.back().appendRect( rRect, aLine );
11939 aLine.append( " W n\n" );
11940 writeBuffer( aLine.getStr(), aLine.getLength() );
11941 drawBitmap( aBmpPos, aBmpSize, aBitmap );
11942 writeBuffer( "Q\n", 2 );
11946 void PDFWriterImpl::updateGraphicsState(Mode const mode)
11948 OStringBuffer aLine( 256 );
11949 GraphicsState& rNewState = m_aGraphicsStack.front();
11950 // first set clip region since it might invalidate everything else
11952 if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) )
11954 rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion;
11956 if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion ||
11957 ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) )
11959 if( m_aCurrentPDFState.m_bClipRegion )
11961 aLine.append( "Q " );
11962 // invalidate everything but the clip region
11963 m_aCurrentPDFState = GraphicsState();
11964 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion);
11966 if( rNewState.m_bClipRegion )
11968 // clip region is always stored in private PDF mapmode
11969 MapMode aNewMapMode = rNewState.m_aMapMode;
11970 rNewState.m_aMapMode = m_aMapMode;
11971 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
11972 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
11974 aLine.append( "q " );
11975 if( rNewState.m_aClipRegion.count() )
11976 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine );
11977 else
11978 aLine.append( "0 0 m h " ); // NULL clip, i.e. nothing visible
11979 aLine.append( "W* n\n" );
11980 rNewState.m_aMapMode = aNewMapMode;
11981 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
11982 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
11987 if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) )
11989 rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode;
11990 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
11993 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) )
11995 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont;
11996 getReferenceDevice()->SetFont( rNewState.m_aFont );
11997 getReferenceDevice()->ImplNewFont();
12000 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) )
12002 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode;
12003 getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
12006 if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) )
12008 rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage;
12009 getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
12012 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) )
12014 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor;
12015 if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
12016 rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
12018 appendStrokingColor( rNewState.m_aLineColor, aLine );
12019 aLine.append( "\n" );
12023 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) )
12025 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor;
12026 if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
12027 rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
12029 appendNonStrokingColor( rNewState.m_aFillColor, aLine );
12030 aLine.append( "\n" );
12034 if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) )
12036 rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent;
12037 if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent )
12039 // TODO: switch extended graphicsstate
12043 // everything is up to date now
12044 m_aCurrentPDFState = m_aGraphicsStack.front();
12045 if ((mode != NOWRITE) && !aLine.isEmpty())
12046 writeBuffer( aLine.getStr(), aLine.getLength() );
12049 /* #i47544# imitate OutputDevice behaviour:
12050 * if a font with a nontransparent color is set, it overwrites the current
12051 * text color. OTOH setting the text color will overwrite the color of the font.
12053 void PDFWriterImpl::setFont( const vcl::Font& rFont )
12055 Color aColor = rFont.GetColor();
12056 if( aColor == Color( COL_TRANSPARENT ) )
12057 aColor = m_aGraphicsStack.front().m_aFont.GetColor();
12058 m_aGraphicsStack.front().m_aFont = rFont;
12059 m_aGraphicsStack.front().m_aFont.SetColor( aColor );
12060 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont;
12063 void PDFWriterImpl::push( PushFlags nFlags )
12065 OSL_ENSURE( !m_aGraphicsStack.empty(), "invalid graphics stack" );
12066 m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
12067 m_aGraphicsStack.front().m_nFlags = nFlags;
12070 void PDFWriterImpl::pop()
12072 OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" );
12073 if( m_aGraphicsStack.size() < 2 )
12074 return;
12076 GraphicsState aState = m_aGraphicsStack.front();
12077 m_aGraphicsStack.pop_front();
12078 GraphicsState& rOld = m_aGraphicsStack.front();
12080 // move those parameters back that were not pushed
12081 // in the first place
12082 if( ! (aState.m_nFlags & PushFlags::LINECOLOR) )
12083 setLineColor( aState.m_aLineColor );
12084 if( ! (aState.m_nFlags & PushFlags::FILLCOLOR) )
12085 setFillColor( aState.m_aFillColor );
12086 if( ! (aState.m_nFlags & PushFlags::FONT) )
12087 setFont( aState.m_aFont );
12088 if( ! (aState.m_nFlags & PushFlags::TEXTCOLOR) )
12089 setTextColor( aState.m_aFont.GetColor() );
12090 if( ! (aState.m_nFlags & PushFlags::MAPMODE) )
12091 setMapMode( aState.m_aMapMode );
12092 if( ! (aState.m_nFlags & PushFlags::CLIPREGION) )
12094 // do not use setClipRegion here
12095 // it would convert again assuming the current mapmode
12096 rOld.m_aClipRegion = aState.m_aClipRegion;
12097 rOld.m_bClipRegion = aState.m_bClipRegion;
12099 if( ! (aState.m_nFlags & PushFlags::TEXTLINECOLOR ) )
12100 setTextLineColor( aState.m_aTextLineColor );
12101 if( ! (aState.m_nFlags & PushFlags::OVERLINECOLOR ) )
12102 setOverlineColor( aState.m_aOverlineColor );
12103 if( ! (aState.m_nFlags & PushFlags::TEXTALIGN ) )
12104 setTextAlign( aState.m_aFont.GetAlign() );
12105 if( ! (aState.m_nFlags & PushFlags::TEXTFILLCOLOR) )
12106 setTextFillColor( aState.m_aFont.GetFillColor() );
12107 if( ! (aState.m_nFlags & PushFlags::REFPOINT) )
12109 // what ?
12111 // invalidate graphics state
12112 m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U);
12115 void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
12117 m_aGraphicsStack.front().m_aMapMode = rMapMode;
12118 getReferenceDevice()->SetMapMode( rMapMode );
12119 m_aCurrentPDFState.m_aMapMode = rMapMode;
12122 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion )
12124 basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
12125 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
12126 m_aGraphicsStack.front().m_aClipRegion = aRegion;
12127 m_aGraphicsStack.front().m_bClipRegion = true;
12128 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
12131 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
12133 if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() )
12135 Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
12136 m_aMapMode,
12137 getReferenceDevice(),
12138 Point( nX, nY ) ) );
12139 aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
12140 m_aMapMode,
12141 getReferenceDevice(),
12142 Point() );
12143 basegfx::B2DHomMatrix aMat;
12144 aMat.translate( aPoint.X(), aPoint.Y() );
12145 m_aGraphicsStack.front().m_aClipRegion.transform( aMat );
12146 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
12150 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect )
12152 basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect(
12153 basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) );
12154 return intersectClipRegion( aRect );
12157 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion )
12159 basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) );
12160 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
12161 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
12162 if( m_aGraphicsStack.front().m_bClipRegion )
12164 basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) );
12165 aRegion = basegfx::tools::prepareForPolygonOperation( aRegion );
12166 m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion );
12168 else
12170 m_aGraphicsStack.front().m_aClipRegion = aRegion;
12171 m_aGraphicsStack.front().m_bClipRegion = true;
12173 return true;
12176 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
12178 if( nPageNr < 0 )
12179 nPageNr = m_nCurrentPage;
12181 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
12182 return;
12184 m_aNotes.push_back( PDFNoteEntry() );
12185 m_aNotes.back().m_nObject = createObject();
12186 m_aNotes.back().m_aContents = rNote;
12187 m_aNotes.back().m_aRect = rRect;
12188 // convert to default user space now, since the mapmode may change
12189 m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
12191 // insert note to page's annotation list
12192 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
12195 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr )
12197 if( nPageNr < 0 )
12198 nPageNr = m_nCurrentPage;
12200 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
12201 return -1;
12203 sal_Int32 nRet = m_aLinks.size();
12205 m_aLinks.push_back( PDFLink() );
12206 m_aLinks.back().m_nObject = createObject();
12207 m_aLinks.back().m_nPage = nPageNr;
12208 m_aLinks.back().m_aRect = rRect;
12209 // convert to default user space now, since the mapmode may change
12210 m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
12212 // insert link to page's annotation list
12213 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
12215 return nRet;
12218 //--->i56629
12219 sal_Int32 PDFWriterImpl::createNamedDest( const OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
12221 if( nPageNr < 0 )
12222 nPageNr = m_nCurrentPage;
12224 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
12225 return -1;
12227 sal_Int32 nRet = m_aNamedDests.size();
12229 m_aNamedDests.push_back( PDFNamedDest() );
12230 m_aNamedDests.back().m_aDestName = sDestName;
12231 m_aNamedDests.back().m_nPage = nPageNr;
12232 m_aNamedDests.back().m_eType = eType;
12233 m_aNamedDests.back().m_aRect = rRect;
12234 // convert to default user space now, since the mapmode may change
12235 m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
12237 return nRet;
12239 //<---i56629
12241 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
12243 if( nPageNr < 0 )
12244 nPageNr = m_nCurrentPage;
12246 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
12247 return -1;
12249 sal_Int32 nRet = m_aDests.size();
12251 m_aDests.push_back( PDFDest() );
12252 m_aDests.back().m_nPage = nPageNr;
12253 m_aDests.back().m_eType = eType;
12254 m_aDests.back().m_aRect = rRect;
12255 // convert to default user space now, since the mapmode may change
12256 m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
12258 return nRet;
12261 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
12263 return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType );
12266 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
12268 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
12269 return -1;
12270 if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
12271 return -2;
12273 m_aLinks[ nLinkId ].m_nDest = nDestId;
12275 return 0;
12278 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
12280 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
12281 return -1;
12283 m_aLinks[ nLinkId ].m_nDest = -1;
12285 using namespace ::com::sun::star;
12287 if (!m_xTrans.is())
12289 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
12290 m_xTrans = util::URLTransformer::create(xContext);;
12293 util::URL aURL;
12294 aURL.Complete = rURL;
12296 m_xTrans->parseStrict( aURL );
12298 m_aLinks[ nLinkId ].m_aURL = aURL.Complete;
12300 return 0;
12303 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
12305 m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
12308 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
12310 // create new item
12311 sal_Int32 nNewItem = m_aOutline.size();
12312 m_aOutline.push_back( PDFOutlineEntry() );
12314 // set item attributes
12315 setOutlineItemParent( nNewItem, nParent );
12316 setOutlineItemText( nNewItem, rText );
12317 setOutlineItemDest( nNewItem, nDestID );
12319 return nNewItem;
12322 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
12324 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
12325 return -1;
12327 int nRet = 0;
12329 if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
12331 nNewParent = 0;
12332 nRet = -2;
12334 // remove item from previous parent
12335 sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID;
12336 if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() )
12338 PDFOutlineEntry& rParent = m_aOutline[ nParentID ];
12340 for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin();
12341 it != rParent.m_aChildren.end(); ++it )
12343 if( *it == nItem )
12345 rParent.m_aChildren.erase( it );
12346 break;
12351 // insert item to new parent's list of children
12352 m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
12354 return nRet;
12357 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
12359 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
12360 return -1;
12362 m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText );
12363 return 0;
12366 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
12368 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
12369 return -1;
12370 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
12371 return -2;
12372 m_aOutline[nItem].m_nDestID = nDestID;
12373 return 0;
12376 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
12378 static std::map< PDFWriter::StructElement, const char* > aTagStrings;
12379 if( aTagStrings.empty() )
12381 aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
12382 aTagStrings[ PDFWriter::Document ] = "Document";
12383 aTagStrings[ PDFWriter::Part ] = "Part";
12384 aTagStrings[ PDFWriter::Article ] = "Art";
12385 aTagStrings[ PDFWriter::Section ] = "Sect";
12386 aTagStrings[ PDFWriter::Division ] = "Div";
12387 aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote";
12388 aTagStrings[ PDFWriter::Caption ] = "Caption";
12389 aTagStrings[ PDFWriter::TOC ] = "TOC";
12390 aTagStrings[ PDFWriter::TOCI ] = "TOCI";
12391 aTagStrings[ PDFWriter::Index ] = "Index";
12392 aTagStrings[ PDFWriter::Paragraph ] = "P";
12393 aTagStrings[ PDFWriter::Heading ] = "H";
12394 aTagStrings[ PDFWriter::H1 ] = "H1";
12395 aTagStrings[ PDFWriter::H2 ] = "H2";
12396 aTagStrings[ PDFWriter::H3 ] = "H3";
12397 aTagStrings[ PDFWriter::H4 ] = "H4";
12398 aTagStrings[ PDFWriter::H5 ] = "H5";
12399 aTagStrings[ PDFWriter::H6 ] = "H6";
12400 aTagStrings[ PDFWriter::List ] = "L";
12401 aTagStrings[ PDFWriter::ListItem ] = "LI";
12402 aTagStrings[ PDFWriter::LILabel ] = "Lbl";
12403 aTagStrings[ PDFWriter::LIBody ] = "LBody";
12404 aTagStrings[ PDFWriter::Table ] = "Table";
12405 aTagStrings[ PDFWriter::TableRow ] = "TR";
12406 aTagStrings[ PDFWriter::TableHeader ] = "TH";
12407 aTagStrings[ PDFWriter::TableData ] = "TD";
12408 aTagStrings[ PDFWriter::Span ] = "Span";
12409 aTagStrings[ PDFWriter::Quote ] = "Quote";
12410 aTagStrings[ PDFWriter::Note ] = "Note";
12411 aTagStrings[ PDFWriter::Reference ] = "Reference";
12412 aTagStrings[ PDFWriter::BibEntry ] = "BibEntry";
12413 aTagStrings[ PDFWriter::Code ] = "Code";
12414 aTagStrings[ PDFWriter::Link ] = "Link";
12415 aTagStrings[ PDFWriter::Figure ] = "Figure";
12416 aTagStrings[ PDFWriter::Formula ] = "Formula";
12417 aTagStrings[ PDFWriter::Form ] = "Form";
12420 std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
12422 return it != aTagStrings.end() ? it->second : "Div";
12425 void PDFWriterImpl::beginStructureElementMCSeq()
12427 if( m_bEmitStructure &&
12428 m_nCurrentStructElement > 0 && // StructTreeRoot
12429 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
12432 PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
12433 OStringBuffer aLine( 128 );
12434 sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
12435 aLine.append( "/" );
12436 if( !rEle.m_aAlias.isEmpty() )
12437 aLine.append( rEle.m_aAlias );
12438 else
12439 aLine.append( getStructureTag( rEle.m_eType ) );
12440 aLine.append( "<</MCID " );
12441 aLine.append( nMCID );
12442 aLine.append( ">>BDC\n" );
12443 writeBuffer( aLine.getStr(), aLine.getLength() );
12445 // update the element's content list
12446 #if OSL_DEBUG_LEVEL > 1
12447 fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n",
12448 nMCID,
12449 m_aPages[ m_nCurrentPage ].m_nPageObject,
12450 rEle.m_nFirstPageObject );
12451 #endif
12452 rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) );
12453 // update the page's mcid parent list
12454 m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
12455 // mark element MC sequence as open
12456 rEle.m_bOpenMCSeq = true;
12458 // handle artifacts
12459 else if( ! m_bEmitStructure && m_aContext.Tagged &&
12460 m_nCurrentStructElement > 0 &&
12461 m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
12462 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
12465 OStringBuffer aLine( 128 );
12466 aLine.append( "/Artifact BMC\n" );
12467 writeBuffer( aLine.getStr(), aLine.getLength() );
12468 // mark element MC sequence as open
12469 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
12473 void PDFWriterImpl::endStructureElementMCSeq()
12475 if( m_nCurrentStructElement > 0 && // StructTreeRoot
12476 ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
12477 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
12480 writeBuffer( "EMC\n", 4 );
12481 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
12485 bool PDFWriterImpl::checkEmitStructure()
12487 bool bEmit = false;
12488 if( m_aContext.Tagged )
12490 bEmit = true;
12491 sal_Int32 nEle = m_nCurrentStructElement;
12492 while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
12494 if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
12496 bEmit = false;
12497 break;
12499 nEle = m_aStructure[ nEle ].m_nParentElement;
12502 return bEmit;
12505 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const OUString& rAlias )
12507 if( m_nCurrentPage < 0 )
12508 return -1;
12510 if( ! m_aContext.Tagged )
12511 return -1;
12513 // close eventual current MC sequence
12514 endStructureElementMCSeq();
12516 if( m_nCurrentStructElement == 0 &&
12517 eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
12519 // struct tree root hit, but not beginning document
12520 // this might happen with setCurrentStructureElement
12521 // silently insert structure into document again if one properly exists
12522 if( ! m_aStructure[ 0 ].m_aChildren.empty() )
12524 PDFWriter::StructElement childType = PDFWriter::NonStructElement;
12525 sal_Int32 nNewCurElement = 0;
12526 const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
12527 for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
12528 childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
12530 nNewCurElement = *it;
12531 childType = m_aStructure[ nNewCurElement ].m_eType;
12533 if( childType == PDFWriter::Document )
12535 m_nCurrentStructElement = nNewCurElement;
12536 DBG_ASSERT( false, "Structure element inserted to StructTreeRoot that is not a document" );
12538 else {
12539 OSL_FAIL( "document structure in disorder !" );
12542 else {
12543 OSL_FAIL( "PDF document structure MUST be contained in a Document element" );
12547 sal_Int32 nNewId = sal_Int32(m_aStructure.size());
12548 m_aStructure.push_back( PDFStructureElement() );
12549 PDFStructureElement& rEle = m_aStructure.back();
12550 rEle.m_eType = eType;
12551 rEle.m_nOwnElement = nNewId;
12552 rEle.m_nParentElement = m_nCurrentStructElement;
12553 rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject;
12554 m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
12555 m_nCurrentStructElement = nNewId;
12557 // handle alias names
12558 if( !rAlias.isEmpty() && eType != PDFWriter::NonStructElement )
12560 OStringBuffer aNameBuf( rAlias.getLength() );
12561 appendName( rAlias, aNameBuf );
12562 OString aAliasName( aNameBuf.makeStringAndClear() );
12563 rEle.m_aAlias = aAliasName;
12564 m_aRoleMap[ aAliasName ] = getStructureTag( eType );
12567 #if OSL_DEBUG_LEVEL > 1
12568 OStringBuffer aLine( "beginStructureElement " );
12569 aLine.append( m_nCurrentStructElement );
12570 aLine.append( ": " );
12571 aLine.append( getStructureTag( eType ) );
12572 if( !rEle.m_aAlias.isEmpty() )
12574 aLine.append( " aliased as \"" );
12575 aLine.append( rEle.m_aAlias );
12576 aLine.append( '\"' );
12578 emitComment( aLine.getStr() );
12579 #endif
12581 // check whether to emit structure henceforth
12582 m_bEmitStructure = checkEmitStructure();
12584 if( m_bEmitStructure ) // don't create nonexistant objects
12586 rEle.m_nObject = createObject();
12587 // update parent's kids list
12588 m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back(PDFStructureElementKid(rEle.m_nObject));
12590 return nNewId;
12593 void PDFWriterImpl::endStructureElement()
12595 if( m_nCurrentPage < 0 )
12596 return;
12598 if( ! m_aContext.Tagged )
12599 return;
12601 if( m_nCurrentStructElement == 0 )
12603 // hit the struct tree root, that means there is an endStructureElement
12604 // without corresponding beginStructureElement
12605 return;
12608 // end the marked content sequence
12609 endStructureElementMCSeq();
12611 #if OSL_DEBUG_LEVEL > 1
12612 OStringBuffer aLine( "endStructureElement " );
12613 aLine.append( m_nCurrentStructElement );
12614 aLine.append( ": " );
12615 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
12616 if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
12618 aLine.append( " aliased as \"" );
12619 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
12620 aLine.append( '\"' );
12622 #endif
12624 // "end" the structure element, the parent becomes current element
12625 m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
12627 // check whether to emit structure henceforth
12628 m_bEmitStructure = checkEmitStructure();
12630 #if OSL_DEBUG_LEVEL > 1
12631 if( m_bEmitStructure )
12632 emitComment( aLine.getStr() );
12633 #endif
12636 //---> i94258
12638 * This function adds an internal structure list container to overcome the 8191 elements array limitation
12639 * in kids element emission.
12640 * Recursive function
12643 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
12645 if( rEle.m_eType == PDFWriter::NonStructElement &&
12646 rEle.m_nOwnElement != rEle.m_nParentElement )
12647 return;
12649 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
12651 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
12653 PDFStructureElement& rChild = m_aStructure[ *it ];
12654 if( rChild.m_eType != PDFWriter::NonStructElement )
12656 //triggered when a child of the rEle element is found
12657 if( rChild.m_nParentElement == rEle.m_nOwnElement )
12658 addInternalStructureContainer( rChild );//examine the child
12659 else
12661 OSL_FAIL( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
12662 #if OSL_DEBUG_LEVEL > 1
12663 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
12664 #endif
12668 else
12670 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
12671 #if OSL_DEBUG_LEVEL > 1
12672 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
12673 #endif
12677 if( rEle.m_nOwnElement != rEle.m_nParentElement )
12679 if( !rEle.m_aKids.empty() )
12681 if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
12682 //then we need to add the containers for the kids elements
12683 // a list to be used for the new kid element
12684 std::list< PDFStructureElementKid > aNewKids;
12685 std::list< sal_Int32 > aNewChildren;
12687 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
12688 OStringBuffer aNameBuf( "Div" );
12689 OString aAliasName( aNameBuf.makeStringAndClear() );
12690 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
12692 while( rEle.m_aKids.size() > ncMaxPDFArraySize )
12694 sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
12695 sal_Int32 nNewId = sal_Int32(m_aStructure.size());
12696 m_aStructure.push_back( PDFStructureElement() );
12697 PDFStructureElement& rEleNew = m_aStructure.back();
12698 rEleNew.m_aAlias = aAliasName;
12699 rEleNew.m_eType = PDFWriter::Division; // a new Div type container
12700 rEleNew.m_nOwnElement = nNewId;
12701 rEleNew.m_nParentElement = nCurrentStructElement;
12702 //inherit the same page as the first child to be reparented
12703 rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
12704 rEleNew.m_nObject = createObject();//assign a PDF object number
12705 //add the object to the kid list of the parent
12706 aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) );
12707 aNewChildren.push_back( nNewId );
12709 std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
12710 std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
12711 advance( aChildEndIt, ncMaxPDFArraySize );
12712 advance( aKidEndIt, ncMaxPDFArraySize );
12714 rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
12715 rEle.m_aKids,
12716 rEle.m_aKids.begin(),
12717 aKidEndIt );
12718 rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
12719 rEle.m_aChildren,
12720 rEle.m_aChildren.begin(),
12721 aChildEndIt );
12722 // set the kid's new parent
12723 for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
12724 it != rEleNew.m_aChildren.end(); ++it )
12726 m_aStructure[ *it ].m_nParentElement = nNewId;
12729 //finally add the new kids resulting from the container added
12730 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
12731 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
12736 //<--- i94258
12738 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
12740 bool bSuccess = false;
12742 if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
12744 // end eventual previous marked content sequence
12745 endStructureElementMCSeq();
12747 m_nCurrentStructElement = nEle;
12748 m_bEmitStructure = checkEmitStructure();
12749 #if OSL_DEBUG_LEVEL > 1
12750 OStringBuffer aLine( "setCurrentStructureElement " );
12751 aLine.append( m_nCurrentStructElement );
12752 aLine.append( ": " );
12753 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
12754 if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
12756 aLine.append( " aliased as \"" );
12757 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
12758 aLine.append( '\"' );
12760 if( ! m_bEmitStructure )
12761 aLine.append( " (inside NonStruct)" );
12762 emitComment( aLine.getStr() );
12763 #endif
12764 bSuccess = true;
12767 return bSuccess;
12770 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
12772 if( !m_aContext.Tagged )
12773 return false;
12775 bool bInsert = false;
12776 if( m_nCurrentStructElement > 0 && m_bEmitStructure )
12778 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
12779 switch( eAttr )
12781 case PDFWriter::Placement:
12782 if( eVal == PDFWriter::Block ||
12783 eVal == PDFWriter::Inline ||
12784 eVal == PDFWriter::Before ||
12785 eVal == PDFWriter::Start ||
12786 eVal == PDFWriter::End )
12787 bInsert = true;
12788 break;
12789 case PDFWriter::WritingMode:
12790 if( eVal == PDFWriter::LrTb ||
12791 eVal == PDFWriter::RlTb ||
12792 eVal == PDFWriter::TbRl )
12794 bInsert = true;
12796 break;
12797 case PDFWriter::TextAlign:
12798 if( eVal == PDFWriter::Start ||
12799 eVal == PDFWriter::Center ||
12800 eVal == PDFWriter::End ||
12801 eVal == PDFWriter::Justify )
12803 if( eType == PDFWriter::Paragraph ||
12804 eType == PDFWriter::Heading ||
12805 eType == PDFWriter::H1 ||
12806 eType == PDFWriter::H2 ||
12807 eType == PDFWriter::H3 ||
12808 eType == PDFWriter::H4 ||
12809 eType == PDFWriter::H5 ||
12810 eType == PDFWriter::H6 ||
12811 eType == PDFWriter::List ||
12812 eType == PDFWriter::ListItem ||
12813 eType == PDFWriter::LILabel ||
12814 eType == PDFWriter::LIBody ||
12815 eType == PDFWriter::Table ||
12816 eType == PDFWriter::TableRow ||
12817 eType == PDFWriter::TableHeader ||
12818 eType == PDFWriter::TableData )
12820 bInsert = true;
12823 break;
12824 case PDFWriter::Width:
12825 case PDFWriter::Height:
12826 if( eVal == PDFWriter::Auto )
12828 if( eType == PDFWriter::Figure ||
12829 eType == PDFWriter::Formula ||
12830 eType == PDFWriter::Form ||
12831 eType == PDFWriter::Table ||
12832 eType == PDFWriter::TableHeader ||
12833 eType == PDFWriter::TableData )
12835 bInsert = true;
12838 break;
12839 case PDFWriter::BlockAlign:
12840 if( eVal == PDFWriter::Before ||
12841 eVal == PDFWriter::Middle ||
12842 eVal == PDFWriter::After ||
12843 eVal == PDFWriter::Justify )
12845 if( eType == PDFWriter::TableHeader ||
12846 eType == PDFWriter::TableData )
12848 bInsert = true;
12851 break;
12852 case PDFWriter::InlineAlign:
12853 if( eVal == PDFWriter::Start ||
12854 eVal == PDFWriter::Center ||
12855 eVal == PDFWriter::End )
12857 if( eType == PDFWriter::TableHeader ||
12858 eType == PDFWriter::TableData )
12860 bInsert = true;
12863 break;
12864 case PDFWriter::LineHeight:
12865 if( eVal == PDFWriter::Normal ||
12866 eVal == PDFWriter::Auto )
12868 // only for ILSE and BLSE
12869 if( eType == PDFWriter::Paragraph ||
12870 eType == PDFWriter::Heading ||
12871 eType == PDFWriter::H1 ||
12872 eType == PDFWriter::H2 ||
12873 eType == PDFWriter::H3 ||
12874 eType == PDFWriter::H4 ||
12875 eType == PDFWriter::H5 ||
12876 eType == PDFWriter::H6 ||
12877 eType == PDFWriter::List ||
12878 eType == PDFWriter::ListItem ||
12879 eType == PDFWriter::LILabel ||
12880 eType == PDFWriter::LIBody ||
12881 eType == PDFWriter::Table ||
12882 eType == PDFWriter::TableRow ||
12883 eType == PDFWriter::TableHeader ||
12884 eType == PDFWriter::TableData ||
12885 eType == PDFWriter::Span ||
12886 eType == PDFWriter::Quote ||
12887 eType == PDFWriter::Note ||
12888 eType == PDFWriter::Reference ||
12889 eType == PDFWriter::BibEntry ||
12890 eType == PDFWriter::Code ||
12891 eType == PDFWriter::Link )
12893 bInsert = true;
12896 break;
12897 case PDFWriter::TextDecorationType:
12898 if( eVal == PDFWriter::NONE ||
12899 eVal == PDFWriter::Underline ||
12900 eVal == PDFWriter::Overline ||
12901 eVal == PDFWriter::LineThrough )
12903 // only for ILSE and BLSE
12904 if( eType == PDFWriter::Paragraph ||
12905 eType == PDFWriter::Heading ||
12906 eType == PDFWriter::H1 ||
12907 eType == PDFWriter::H2 ||
12908 eType == PDFWriter::H3 ||
12909 eType == PDFWriter::H4 ||
12910 eType == PDFWriter::H5 ||
12911 eType == PDFWriter::H6 ||
12912 eType == PDFWriter::List ||
12913 eType == PDFWriter::ListItem ||
12914 eType == PDFWriter::LILabel ||
12915 eType == PDFWriter::LIBody ||
12916 eType == PDFWriter::Table ||
12917 eType == PDFWriter::TableRow ||
12918 eType == PDFWriter::TableHeader ||
12919 eType == PDFWriter::TableData ||
12920 eType == PDFWriter::Span ||
12921 eType == PDFWriter::Quote ||
12922 eType == PDFWriter::Note ||
12923 eType == PDFWriter::Reference ||
12924 eType == PDFWriter::BibEntry ||
12925 eType == PDFWriter::Code ||
12926 eType == PDFWriter::Link )
12928 bInsert = true;
12931 break;
12932 case PDFWriter::ListNumbering:
12933 if( eVal == PDFWriter::NONE ||
12934 eVal == PDFWriter::Disc ||
12935 eVal == PDFWriter::Circle ||
12936 eVal == PDFWriter::Square ||
12937 eVal == PDFWriter::Decimal ||
12938 eVal == PDFWriter::UpperRoman ||
12939 eVal == PDFWriter::LowerRoman ||
12940 eVal == PDFWriter::UpperAlpha ||
12941 eVal == PDFWriter::LowerAlpha )
12943 if( eType == PDFWriter::List )
12944 bInsert = true;
12946 break;
12947 default: break;
12951 if( bInsert )
12952 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
12953 #if OSL_DEBUG_LEVEL > 1
12954 else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
12955 fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
12956 getAttributeTag( eAttr ),
12957 getAttributeValueTag( eVal ),
12958 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
12959 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
12961 #endif
12963 return bInsert;
12966 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
12968 if( ! m_aContext.Tagged )
12969 return false;
12971 bool bInsert = false;
12972 if( m_nCurrentStructElement > 0 && m_bEmitStructure )
12974 if( eAttr == PDFWriter::Language )
12976 m_aStructure[ m_nCurrentStructElement ].m_aLocale = LanguageTag( (LanguageType)nValue ).getLocale();
12977 return true;
12980 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
12981 switch( eAttr )
12983 case PDFWriter::SpaceBefore:
12984 case PDFWriter::SpaceAfter:
12985 case PDFWriter::StartIndent:
12986 case PDFWriter::EndIndent:
12987 // just for BLSE
12988 if( eType == PDFWriter::Paragraph ||
12989 eType == PDFWriter::Heading ||
12990 eType == PDFWriter::H1 ||
12991 eType == PDFWriter::H2 ||
12992 eType == PDFWriter::H3 ||
12993 eType == PDFWriter::H4 ||
12994 eType == PDFWriter::H5 ||
12995 eType == PDFWriter::H6 ||
12996 eType == PDFWriter::List ||
12997 eType == PDFWriter::ListItem ||
12998 eType == PDFWriter::LILabel ||
12999 eType == PDFWriter::LIBody ||
13000 eType == PDFWriter::Table ||
13001 eType == PDFWriter::TableRow ||
13002 eType == PDFWriter::TableHeader ||
13003 eType == PDFWriter::TableData )
13005 bInsert = true;
13007 break;
13008 case PDFWriter::TextIndent:
13009 // paragraph like BLSE and additional elements
13010 if( eType == PDFWriter::Paragraph ||
13011 eType == PDFWriter::Heading ||
13012 eType == PDFWriter::H1 ||
13013 eType == PDFWriter::H2 ||
13014 eType == PDFWriter::H3 ||
13015 eType == PDFWriter::H4 ||
13016 eType == PDFWriter::H5 ||
13017 eType == PDFWriter::H6 ||
13018 eType == PDFWriter::LILabel ||
13019 eType == PDFWriter::LIBody ||
13020 eType == PDFWriter::TableHeader ||
13021 eType == PDFWriter::TableData )
13023 bInsert = true;
13025 break;
13026 case PDFWriter::Width:
13027 case PDFWriter::Height:
13028 if( eType == PDFWriter::Figure ||
13029 eType == PDFWriter::Formula ||
13030 eType == PDFWriter::Form ||
13031 eType == PDFWriter::Table ||
13032 eType == PDFWriter::TableHeader ||
13033 eType == PDFWriter::TableData )
13035 bInsert = true;
13037 break;
13038 case PDFWriter::LineHeight:
13039 case PDFWriter::BaselineShift:
13040 // only for ILSE and BLSE
13041 if( eType == PDFWriter::Paragraph ||
13042 eType == PDFWriter::Heading ||
13043 eType == PDFWriter::H1 ||
13044 eType == PDFWriter::H2 ||
13045 eType == PDFWriter::H3 ||
13046 eType == PDFWriter::H4 ||
13047 eType == PDFWriter::H5 ||
13048 eType == PDFWriter::H6 ||
13049 eType == PDFWriter::List ||
13050 eType == PDFWriter::ListItem ||
13051 eType == PDFWriter::LILabel ||
13052 eType == PDFWriter::LIBody ||
13053 eType == PDFWriter::Table ||
13054 eType == PDFWriter::TableRow ||
13055 eType == PDFWriter::TableHeader ||
13056 eType == PDFWriter::TableData ||
13057 eType == PDFWriter::Span ||
13058 eType == PDFWriter::Quote ||
13059 eType == PDFWriter::Note ||
13060 eType == PDFWriter::Reference ||
13061 eType == PDFWriter::BibEntry ||
13062 eType == PDFWriter::Code ||
13063 eType == PDFWriter::Link )
13065 bInsert = true;
13067 break;
13068 case PDFWriter::RowSpan:
13069 case PDFWriter::ColSpan:
13070 // only for table cells
13071 if( eType == PDFWriter::TableHeader ||
13072 eType == PDFWriter::TableData )
13074 bInsert = true;
13076 break;
13077 case PDFWriter::LinkAnnotation:
13078 if( eType == PDFWriter::Link )
13079 bInsert = true;
13080 break;
13081 default: break;
13085 if( bInsert )
13086 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
13087 #if OSL_DEBUG_LEVEL > 1
13088 else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
13089 fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
13090 getAttributeTag( eAttr ),
13091 (int)nValue,
13092 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
13093 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() );
13094 #endif
13096 return bInsert;
13099 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect )
13101 sal_Int32 nPageNr = m_nCurrentPage;
13102 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
13103 return;
13105 if( m_nCurrentStructElement > 0 && m_bEmitStructure )
13107 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
13108 if( eType == PDFWriter::Figure ||
13109 eType == PDFWriter::Formula ||
13110 eType == PDFWriter::Form ||
13111 eType == PDFWriter::Table )
13113 m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
13114 // convert to default user space now, since the mapmode may change
13115 m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
13120 void PDFWriterImpl::setActualText( const OUString& rText )
13122 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
13124 m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
13128 void PDFWriterImpl::setAlternateText( const OUString& rText )
13130 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
13132 m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
13136 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
13138 if( nPageNr < 0 )
13139 nPageNr = m_nCurrentPage;
13141 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
13142 return;
13144 m_aPages[ nPageNr ].m_nDuration = nSeconds;
13147 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
13149 if( nPageNr < 0 )
13150 nPageNr = m_nCurrentPage;
13152 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
13153 return;
13155 m_aPages[ nPageNr ].m_eTransition = eType;
13156 m_aPages[ nPageNr ].m_nTransTime = nMilliSec;
13159 void PDFWriterImpl::ensureUniqueRadioOnValues()
13161 // loop over radio groups
13162 for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
13163 group != m_aRadioGroupWidgets.end(); ++group )
13165 PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
13166 // check whether all kids have a unique OnValue
13167 std::unordered_map< OUString, sal_Int32, OUStringHash > aOnValues;
13168 int nChildren = rGroupWidget.m_aKidsIndex.size();
13169 bool bIsUnique = true;
13170 for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
13172 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
13173 const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
13174 #if OSL_DEBUG_LEVEL > 1
13175 fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() );
13176 #endif
13177 if( aOnValues.find( rVal ) == aOnValues.end() )
13179 aOnValues[ rVal ] = 1;
13181 else
13183 bIsUnique = false;
13186 if( ! bIsUnique )
13188 #if OSL_DEBUG_LEVEL > 1
13189 fprintf( stderr, "enforcing unique OnValues\n" );
13190 #endif
13191 // make unique by using ascending OnValues
13192 for( int nKid = 0; nKid < nChildren; nKid++ )
13194 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
13195 PDFWidget& rKid = m_aWidgets[nKidIndex];
13196 rKid.m_aOnValue = OUString::number( nKid+1 );
13197 if( rKid.m_aValue != "Off" )
13198 rKid.m_aValue = rKid.m_aOnValue;
13201 // finally move the "Yes" appearance to the OnValue appearance
13202 for( int nKid = 0; nKid < nChildren; nKid++ )
13204 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
13205 PDFWidget& rKid = m_aWidgets[nKidIndex];
13206 PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
13207 if( app_it != rKid.m_aAppearances.end() )
13209 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
13210 if( stream_it != app_it->second.end() )
13212 SvMemoryStream* pStream = stream_it->second;
13213 app_it->second.erase( stream_it );
13214 OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
13215 appendName( rKid.m_aOnValue, aBuf );
13216 (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
13218 #if OSL_DEBUG_LEVEL > 1
13219 else
13220 fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" );
13221 #endif
13223 // update selected radio button
13224 if( rKid.m_aValue != "Off" )
13226 rGroupWidget.m_aValue = rKid.m_aValue;
13232 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
13234 sal_Int32 nRadioGroupWidget = -1;
13236 std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
13238 if( it == m_aRadioGroupWidgets.end() )
13240 m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
13241 sal_Int32(m_aWidgets.size());
13243 // new group, insert the radiobutton
13244 m_aWidgets.push_back( PDFWidget() );
13245 m_aWidgets.back().m_nObject = createObject();
13246 m_aWidgets.back().m_nPage = m_nCurrentPage;
13247 m_aWidgets.back().m_eType = PDFWriter::RadioButton;
13248 m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
13249 m_aWidgets.back().m_nFlags |= 0x0000C000; // NoToggleToOff and Radio bits
13251 createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn );
13253 else
13254 nRadioGroupWidget = it->second;
13256 return nRadioGroupWidget;
13259 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
13261 if( nPageNr < 0 )
13262 nPageNr = m_nCurrentPage;
13264 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
13265 return -1;
13267 bool sigHidden(true);
13268 sal_Int32 nNewWidget = m_aWidgets.size();
13269 m_aWidgets.push_back( PDFWidget() );
13271 m_aWidgets.back().m_nObject = createObject();
13272 m_aWidgets.back().m_aRect = rControl.Location;
13273 m_aWidgets.back().m_nPage = nPageNr;
13274 m_aWidgets.back().m_eType = rControl.getType();
13276 sal_Int32 nRadioGroupWidget = -1;
13277 // for unknown reasons the radio buttons of a radio group must not have a
13278 // field name, else the buttons are in fact check boxes -
13279 // that is multiple buttons of the radio group can be selected
13280 if( rControl.getType() == PDFWriter::RadioButton )
13281 nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
13282 else
13284 createWidgetFieldName( nNewWidget, rControl );
13287 // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
13288 PDFWidget& rNewWidget = m_aWidgets[nNewWidget];
13289 rNewWidget.m_aDescription = rControl.Description;
13290 rNewWidget.m_aText = rControl.Text;
13291 rNewWidget.m_nTextStyle = rControl.TextStyle &
13292 ( DrawTextFlags::Left | DrawTextFlags::Center | DrawTextFlags::Right | DrawTextFlags::Top |
13293 DrawTextFlags::VCenter | DrawTextFlags::Bottom |
13294 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
13295 rNewWidget.m_nTabOrder = rControl.TabOrder;
13297 // various properties are set via the flags (/Ff) property of the field dict
13298 if( rControl.ReadOnly )
13299 rNewWidget.m_nFlags |= 1;
13300 if( rControl.getType() == PDFWriter::PushButton )
13302 const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
13303 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13304 rNewWidget.m_nTextStyle =
13305 DrawTextFlags::Center | DrawTextFlags::VCenter |
13306 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
13308 rNewWidget.m_nFlags |= 0x00010000;
13309 if( !rBtn.URL.isEmpty() )
13310 rNewWidget.m_aListEntries.push_back( rBtn.URL );
13311 rNewWidget.m_bSubmit = rBtn.Submit;
13312 rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
13313 rNewWidget.m_nDest = rBtn.Dest;
13314 createDefaultPushButtonAppearance( rNewWidget, rBtn );
13316 else if( rControl.getType() == PDFWriter::RadioButton )
13318 const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
13319 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13320 rNewWidget.m_nTextStyle =
13321 DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
13322 /* PDF sees a RadioButton group as one radio button with
13323 * children which are in turn check boxes
13325 * so we need to create a radio button on demand for a new group
13326 * and insert a checkbox for each RadioButtonWidget as its child
13328 rNewWidget.m_eType = PDFWriter::CheckBox;
13329 rNewWidget.m_nRadioGroup = rBtn.RadioGroup;
13331 DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" );
13333 PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
13334 rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
13335 rRadioButton.m_aKidsIndex.push_back( nNewWidget );
13336 rNewWidget.m_nParent = rRadioButton.m_nObject;
13338 rNewWidget.m_aValue = "Off";
13339 rNewWidget.m_aOnValue = rBtn.OnValue;
13340 if( rRadioButton.m_aValue.isEmpty() && rBtn.Selected )
13342 rNewWidget.m_aValue = rNewWidget.m_aOnValue;
13343 rRadioButton.m_aValue = rNewWidget.m_aOnValue;
13345 createDefaultRadioButtonAppearance( rNewWidget, rBtn );
13347 // union rect of radio group
13348 Rectangle aRect = rNewWidget.m_aRect;
13349 m_aPages[ nPageNr ].convertRect( aRect );
13350 rRadioButton.m_aRect.Union( aRect );
13352 else if( rControl.getType() == PDFWriter::CheckBox )
13354 const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
13355 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13356 rNewWidget.m_nTextStyle =
13357 DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
13359 rNewWidget.m_aValue = rBox.Checked ? OUString("Yes") : OUString("Off" );
13360 // create default appearance before m_aRect gets transformed
13361 createDefaultCheckBoxAppearance( rNewWidget, rBox );
13363 else if( rControl.getType() == PDFWriter::ListBox )
13365 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13366 rNewWidget.m_nTextStyle = DrawTextFlags::VCenter;
13368 const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
13369 rNewWidget.m_aListEntries = rLstBox.Entries;
13370 rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
13371 rNewWidget.m_aValue = rLstBox.Text;
13372 if( rLstBox.DropDown )
13373 rNewWidget.m_nFlags |= 0x00020000;
13374 if( rLstBox.Sort )
13375 rNewWidget.m_nFlags |= 0x00080000;
13376 if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 )
13377 rNewWidget.m_nFlags |= 0x00200000;
13379 createDefaultListBoxAppearance( rNewWidget, rLstBox );
13381 else if( rControl.getType() == PDFWriter::ComboBox )
13383 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13384 rNewWidget.m_nTextStyle = DrawTextFlags::VCenter;
13386 const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
13387 rNewWidget.m_aValue = rBox.Text;
13388 rNewWidget.m_aListEntries = rBox.Entries;
13389 rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
13390 if( rBox.Sort )
13391 rNewWidget.m_nFlags |= 0x00080000;
13393 PDFWriter::ListBoxWidget aLBox;
13394 aLBox.Name = rBox.Name;
13395 aLBox.Description = rBox.Description;
13396 aLBox.Text = rBox.Text;
13397 aLBox.TextStyle = rBox.TextStyle;
13398 aLBox.ReadOnly = rBox.ReadOnly;
13399 aLBox.Border = rBox.Border;
13400 aLBox.BorderColor = rBox.BorderColor;
13401 aLBox.Background = rBox.Background;
13402 aLBox.BackgroundColor = rBox.BackgroundColor;
13403 aLBox.TextFont = rBox.TextFont;
13404 aLBox.TextColor = rBox.TextColor;
13405 aLBox.DropDown = true;
13406 aLBox.Sort = rBox.Sort;
13407 aLBox.MultiSelect = false;
13408 aLBox.Entries = rBox.Entries;
13410 createDefaultListBoxAppearance( rNewWidget, aLBox );
13412 else if( rControl.getType() == PDFWriter::Edit )
13414 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13415 rNewWidget.m_nTextStyle = DrawTextFlags::Left | DrawTextFlags::VCenter;
13417 const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl);
13418 if( rEdit.MultiLine )
13420 rNewWidget.m_nFlags |= 0x00001000;
13421 rNewWidget.m_nTextStyle |= DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
13423 if( rEdit.Password )
13424 rNewWidget.m_nFlags |= 0x00002000;
13425 if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 )
13426 rNewWidget.m_nFlags |= 0x00100000;
13427 rNewWidget.m_nMaxLen = rEdit.MaxLen;
13428 rNewWidget.m_aValue = rEdit.Text;
13430 createDefaultEditAppearance( rNewWidget, rEdit );
13432 #if !defined(ANDROID) && !defined(IOS)
13433 else if( rControl.getType() == PDFWriter::Signature)
13435 const PDFWriter::SignatureWidget& rSig = static_cast<const PDFWriter::SignatureWidget&>(rControl);
13436 sigHidden = rSig.SigHidden;
13438 if ( sigHidden )
13439 rNewWidget.m_aRect = Rectangle(0, 0, 0, 0);
13441 m_nSignatureObject = createObject();
13442 rNewWidget.m_aValue = OUString::number( m_nSignatureObject );
13443 rNewWidget.m_aValue += " 0 R";
13444 // let's add a fake appearance
13445 rNewWidget.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
13447 #endif
13449 // if control is a hidden signature, do not convert coordinates since we
13450 // need /Rect [ 0 0 0 0 ]
13451 if ( ! ( ( rControl.getType() == PDFWriter::Signature ) && ( sigHidden ) ) )
13453 // convert to default user space now, since the mapmode may change
13454 // note: create default appearances before m_aRect gets transformed
13455 m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
13458 // insert widget to page's annotation list
13459 m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
13461 // mark page as having widgets
13462 m_aPages[ nPageNr ].m_bHasWidgets = true;
13464 return nNewWidget;
13467 void PDFWriterImpl::addStream( const OUString& rMimeType, PDFOutputStream* pStream, bool bCompress )
13469 if( pStream )
13471 m_aAdditionalStreams.push_back( PDFAddStream() );
13472 PDFAddStream& rStream = m_aAdditionalStreams.back();
13473 rStream.m_aMimeType = !rMimeType.isEmpty()
13474 ? OUString( rMimeType )
13475 : OUString( "application/octet-stream" );
13476 rStream.m_pStream = pStream;
13477 rStream.m_bCompress = bCompress;
13481 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */