1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "sal/config.h"
25 #include <tools/urlobj.hxx>
27 #include <pdfwriter_impl.hxx>
29 #include <basegfx/polygon/b2dpolygon.hxx>
30 #include <basegfx/polygon/b2dpolypolygon.hxx>
31 #include <basegfx/polygon/b2dpolygontools.hxx>
32 #include <basegfx/polygon/b2dpolypolygontools.hxx>
33 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
34 #include <basegfx/matrix/b2dhommatrix.hxx>
36 #include <osl/thread.h>
40 #include <rtl/digest.h>
41 #include <rtl/ustrbuf.hxx>
43 #include <tools/debug.hxx>
44 #include <tools/zcodec.hxx>
45 #include <tools/stream.hxx>
47 #include <i18nlangtag/languagetag.hxx>
49 #include <vcl/virdev.hxx>
50 #include <vcl/bmpacc.hxx>
51 #include <vcl/bitmapex.hxx>
52 #include <vcl/image.hxx>
53 #include <vcl/metric.hxx>
54 #include <vcl/svapp.hxx>
55 #include <vcl/lineinfo.hxx>
56 #include "vcl/cvtgrf.hxx"
57 #include "vcl/strhelper.hxx"
59 #include <fontsubset.hxx>
61 #include <sallayout.hxx>
62 #include <textlayout.hxx>
67 #include <comphelper/processfactory.hxx>
68 #include <comphelper/string.hxx>
70 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
71 #include <com/sun/star/util/URLTransformer.hpp>
72 #include <com/sun/star/util/URL.hpp>
74 #include "cppuhelper/implbase1.hxx"
76 #if !defined(ANDROID) && !defined(IOS)
77 // NSS header files for PDF signing support
89 #if (OSL_DEBUG_LEVEL < 3)
90 #define COMPRESS_PAGES
92 #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams
95 #if !defined(ANDROID) && !defined(IOS)
96 #define MAX_SIGNATURE_CONTENT_LENGTH 0x4000
100 class PDFTestOutputStream
: public PDFOutputStream
103 virtual ~PDFTestOutputStream();
104 virtual void write( const com::sun::star::uno::Reference
< com::sun::star::io::XOutputStream
>& xStream
);
107 PDFTestOutputStream::~PDFTestOutputStream()
111 void PDFTestOutputStream::write( const com::sun::star::uno::Reference
< com::sun::star::io::XOutputStream
>& xStream
)
113 OString
aStr( "lalala\ntest\ntest\ntest" );
114 com::sun::star::uno::Sequence
< sal_Int8
> aData( aStr
.getLength() );
115 memcpy( aData
.getArray(), aStr
.getStr(), aStr
.getLength() );
116 xStream
->writeBytes( aData
);
119 // this test code cannot be used to test PDF/A-1 because it forces
120 // control item (widgets) to bypass the structure controlling
121 // the embedding of such elements in actual run
124 static const char* pHome
= getenv( "HOME" );
125 OUString
aTestFile( "file://" );
126 aTestFile
+= OUString( pHome
, strlen( pHome
), RTL_TEXTENCODING_MS_1252
);
127 aTestFile
+= OUString( "/pdf_export_test.pdf" );
129 PDFWriter::PDFWriterContext aContext
;
130 aContext
.URL
= aTestFile
;
131 aContext
.Version
= PDFWriter::PDF_1_4
;
132 aContext
.Tagged
= true;
133 aContext
.InitialPage
= 2;
134 aContext
.DocumentInfo
.Title
= OUString( "PDF export test document" );
135 aContext
.DocumentInfo
.Producer
= OUString( "VCL" );
137 aContext
.SignPDF
= true;
138 aContext
.SignLocation
= OUString( "Burdur" );
139 aContext
.SignReason
= OUString( "Some valid reason to sign" );
140 aContext
.SignContact
= OUString( "signer@example.com" );
142 com::sun::star::uno::Reference
< com::sun::star::beans::XMaterialHolder
> xEnc
;
143 PDFWriter
aWriter( aContext
, xEnc
);
144 aWriter
.NewPage( 595, 842 );
145 aWriter
.BeginStructureElement( PDFWriter::Document
);
146 // set duration of 3 sec for first page
147 aWriter
.SetAutoAdvanceTime( 3 );
148 aWriter
.SetMapMode( MapMode( MAP_100TH_MM
) );
150 aWriter
.SetFillColor( Color( COL_LIGHTRED
) );
151 aWriter
.SetLineColor( Color( COL_LIGHTGREEN
) );
152 aWriter
.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
154 aWriter
.SetFont( Font( String( "Times" ), Size( 0, 500 ) ) );
155 aWriter
.SetTextColor( Color( COL_BLACK
) );
156 aWriter
.SetLineColor( Color( COL_BLACK
) );
157 aWriter
.SetFillColor( Color( COL_LIGHTBLUE
) );
159 Rectangle
aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
160 aWriter
.DrawRect( aRect
);
161 aWriter
.DrawText( aRect
, String( "Link annot 1" ) );
162 sal_Int32 nFirstLink
= aWriter
.CreateLink( aRect
);
164 aNote
.Title
= String( "A small test note" );
165 aNote
.Contents
= String( "There is no business like show business like no business i know. Everything about it is appealing." );
166 aWriter
.CreateNote( Rectangle( Point( aRect
.Right(), aRect
.Top() ), Size( 6000, 3000 ) ), aNote
);
168 Rectangle
aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
169 aWriter
.SetFillColor( Color( COL_LIGHTGREEN
) );
170 aWriter
.DrawRect( aTargetRect
);
171 aWriter
.DrawText( aTargetRect
, String( "Dest second link" ) );
172 sal_Int32 nSecondDest
= aWriter
.CreateDest( aTargetRect
);
174 aWriter
.BeginStructureElement( PDFWriter::Section
);
175 aWriter
.BeginStructureElement( PDFWriter::Heading
);
176 aWriter
.DrawText( Point(4500, 9000), String( "A small structure test" ) );
177 aWriter
.EndStructureElement();
178 aWriter
.BeginStructureElement( PDFWriter::Paragraph
);
179 aWriter
.SetStructureAttribute( PDFWriter::WritingMode
, PDFWriter::LrTb
);
180 aWriter
.SetStructureAttribute( PDFWriter::TextDecorationType
, PDFWriter::Underline
);
181 aWriter
.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
182 String( "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." ),
183 TEXT_DRAW_MULTILINE
| TEXT_DRAW_WORDBREAK
185 aWriter
.SetActualText( String( "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." ) );
186 aWriter
.SetAlternateText( String( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." ) );
187 aWriter
.EndStructureElement();
188 aWriter
.BeginStructureElement( PDFWriter::Paragraph
);
189 aWriter
.SetStructureAttribute( PDFWriter::WritingMode
, PDFWriter::LrTb
);
190 aWriter
.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
191 String( "This paragraph is nothing special either but ends on the next page structurewise" ),
192 TEXT_DRAW_MULTILINE
| TEXT_DRAW_WORDBREAK
195 aWriter
.NewPage( 595, 842 );
196 // test AddStream interface
197 aWriter
.AddStream( String( "text/plain" ), new PDFTestOutputStream(), true );
198 // set transitional mode
199 aWriter
.SetPageTransition( PDFWriter::WipeRightToLeft
, 1500 );
200 aWriter
.SetMapMode( MapMode( MAP_100TH_MM
) );
201 aWriter
.SetTextColor( Color( COL_BLACK
) );
202 aWriter
.SetFont( Font( String( "Times" ), Size( 0, 500 ) ) );
203 aWriter
.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
204 String( "Here's where all things come to an end ... well at least the paragaph from the last page." ),
205 TEXT_DRAW_MULTILINE
| TEXT_DRAW_WORDBREAK
207 aWriter
.EndStructureElement();
209 aWriter
.SetFillColor( Color( COL_LIGHTBLUE
) );
211 aWriter
.BeginStructureElement( PDFWriter::NonStructElement
);
212 aWriter
.DrawRect( aRect
);
213 aWriter
.BeginStructureElement( PDFWriter::Paragraph
);
214 aWriter
.DrawText( aRect
, String( "Link annot 2" ) );
215 sal_Int32 nSecondLink
= aWriter
.CreateLink( aRect
);
217 aWriter
.SetFillColor( Color( COL_LIGHTGREEN
) );
218 aWriter
.BeginStructureElement( PDFWriter::ListItem
);
219 aWriter
.DrawRect( aTargetRect
);
220 aWriter
.DrawText( aTargetRect
, String( "Dest first link" ) );
221 sal_Int32 nFirstDest
= aWriter
.CreateDest( aTargetRect
);
223 aWriter
.EndStructureElement();
225 aWriter
.EndStructureElement();
226 aWriter
.EndStructureElement();
227 aWriter
.BeginStructureElement( PDFWriter::Figure
);
228 aWriter
.BeginStructureElement( PDFWriter::Caption
);
229 aWriter
.DrawText( Point( 4500, 9000 ), String( "Some drawing stuff inside the structure" ) );
230 aWriter
.EndStructureElement();
233 basegfx::B2DPolyPolygon aClip
;
234 basegfx::B2DPolygon aClipPoly
;
235 aClipPoly
.append( basegfx::B2DPoint( 8250, 9600 ) );
236 aClipPoly
.append( basegfx::B2DPoint( 16500, 11100 ) );
237 aClipPoly
.append( basegfx::B2DPoint( 8250, 12600 ) );
238 aClipPoly
.append( basegfx::B2DPoint( 4500, 11100 ) );
239 aClipPoly
.setClosed( true );
241 aClip
.append( aClipPoly
);
243 aWriter
.Push( PUSH_CLIPREGION
| PUSH_FILLCOLOR
);
244 aWriter
.SetClipRegion( aClip
);
245 aWriter
.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
246 aWriter
.MoveClipRegion( 1000, 500 );
247 aWriter
.SetFillColor( Color( COL_RED
) );
248 aWriter
.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
252 Rectangle
aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
253 aWriter
.SetFillColor( Color( COL_LIGHTRED
) );
254 aWriter
.DrawRect( aTranspRect
);
255 aWriter
.BeginTransparencyGroup();
257 aWriter
.SetFillColor( Color( COL_LIGHTGREEN
) );
258 aWriter
.DrawEllipse( aTranspRect
);
259 aWriter
.SetTextColor( Color( COL_LIGHTBLUE
) );
260 aWriter
.DrawText( aTranspRect
,
261 String( "Some transparent text" ),
262 TEXT_DRAW_CENTER
| TEXT_DRAW_VCENTER
| TEXT_DRAW_MULTILINE
| TEXT_DRAW_WORDBREAK
);
264 aWriter
.EndTransparencyGroup( aTranspRect
, 50 );
266 // prepare an alpha mask
267 Bitmap
aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
268 BitmapWriteAccess
* pAcc
= aTransMask
.AcquireWriteAccess();
269 for( int nX
= 0; nX
< 256; nX
++ )
270 for( int nY
= 0; nY
< 256; nY
++ )
271 pAcc
->SetPixel( nX
, nY
, BitmapColor( (sal_uInt8
)((nX
+nY
)/2) ) );
272 aTransMask
.ReleaseAccess( pAcc
);
273 aTransMask
.SetPrefMapMode( MAP_MM
);
274 aTransMask
.SetPrefSize( Size( 10, 10 ) );
276 aWriter
.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask
);
278 aTranspRect
= Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
279 aWriter
.SetFillColor( Color( COL_LIGHTRED
) );
280 aWriter
.DrawRect( aTranspRect
);
281 aWriter
.SetFillColor( Color( COL_LIGHTGREEN
) );
282 aWriter
.DrawEllipse( aTranspRect
);
283 aWriter
.SetTextColor( Color( COL_LIGHTBLUE
) );
284 aWriter
.DrawText( aTranspRect
,
285 String( "Some transparent text" ),
286 TEXT_DRAW_CENTER
| TEXT_DRAW_VCENTER
| TEXT_DRAW_MULTILINE
| TEXT_DRAW_WORDBREAK
);
287 aTranspRect
= Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
288 aWriter
.SetFillColor( Color( COL_LIGHTRED
) );
289 aWriter
.DrawRect( aTranspRect
);
291 Bitmap
aImageBmp( Size( 256, 256 ), 24 );
292 pAcc
= aImageBmp
.AcquireWriteAccess();
293 pAcc
->SetFillColor( Color( 0xff, 0, 0xff ) );
294 pAcc
->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
295 aImageBmp
.ReleaseAccess( pAcc
);
296 BitmapEx
aBmpEx( aImageBmp
, AlphaMask( aTransMask
) );
297 aWriter
.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx
);
300 aWriter
.EndStructureElement();
301 aWriter
.EndStructureElement();
303 LineInfo
aLI( LINE_DASH
, 3 );
304 aLI
.SetDashCount( 2 );
305 aLI
.SetDashLen( 50 );
306 aLI
.SetDotCount( 2 );
308 aLI
.SetDistance( 15 );
309 Point aLIPoints
[] = { Point( 4000, 10000 ),
310 Point( 8000, 12000 ),
311 Point( 3000, 19000 ) };
312 Polygon
aLIPoly( 3, aLIPoints
);
313 aWriter
.SetLineColor( Color( COL_BLUE
) );
314 aWriter
.SetFillColor();
315 aWriter
.DrawPolyLine( aLIPoly
, aLI
);
317 aLI
.SetDashCount( 4 );
318 aLIPoly
.Move( 1000, 1000 );
319 aWriter
.DrawPolyLine( aLIPoly
, aLI
);
321 aWriter
.NewPage( 595, 842 );
322 aWriter
.SetMapMode( MapMode( MAP_100TH_MM
) );
323 Wallpaper
aWall( aTransMask
);
324 aWall
.SetStyle( WALLPAPER_TILE
);
325 aWriter
.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall
);
327 aWriter
.NewPage( 595, 842 );
328 aWriter
.SetMapMode( MapMode( MAP_100TH_MM
) );
329 aWriter
.SetFont( Font( String( "Times" ), Size( 0, 500 ) ) );
330 aWriter
.SetTextColor( Color( COL_BLACK
) );
331 aRect
= Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
332 aWriter
.DrawRect( aRect
);
333 aWriter
.DrawText( aRect
, String( "www.heise.de" ) );
334 sal_Int32 nURILink
= aWriter
.CreateLink( aRect
);
335 aWriter
.SetLinkURL( nURILink
, OUString( "http://www.heise.de" ) );
337 aWriter
.SetLinkDest( nFirstLink
, nFirstDest
);
338 aWriter
.SetLinkDest( nSecondLink
, nSecondDest
);
341 PDFWriter::PushButtonWidget aBtn
;
342 aBtn
.Name
= OUString( "testButton" );
343 aBtn
.Description
= OUString( "A test button" );
344 aBtn
.Text
= OUString( "hit me" );
345 aBtn
.Location
= Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
346 aBtn
.Border
= aBtn
.Background
= true;
347 aWriter
.CreateControl( aBtn
);
349 // include a uri button
350 PDFWriter::PushButtonWidget aUriBtn
;
351 aUriBtn
.Name
= OUString( "wwwButton" );
352 aUriBtn
.Description
= OUString( "A URI button" );
353 aUriBtn
.Text
= OUString( "to www" );
354 aUriBtn
.Location
= Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
355 aUriBtn
.Border
= aUriBtn
.Background
= true;
356 aUriBtn
.URL
= OUString( "http://www.heise.de" );
357 aWriter
.CreateControl( aUriBtn
);
359 // include a dest button
360 PDFWriter::PushButtonWidget aDstBtn
;
361 aDstBtn
.Name
= OUString( "destButton" );
362 aDstBtn
.Description
= OUString( "A Dest button" );
363 aDstBtn
.Text
= OUString( "to paragraph" );
364 aDstBtn
.Location
= Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
365 aDstBtn
.Border
= aDstBtn
.Background
= true;
366 aDstBtn
.Dest
= nFirstDest
;
367 aWriter
.CreateControl( aDstBtn
);
369 PDFWriter::CheckBoxWidget aCBox
;
370 aCBox
.Name
= OUString( "textCheckBox" );
371 aCBox
.Description
= OUString( "A test check box" );
372 aCBox
.Text
= OUString( "check me" );
373 aCBox
.Location
= Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
374 aCBox
.Checked
= true;
375 aCBox
.Border
= aCBox
.Background
= false;
376 aWriter
.CreateControl( aCBox
);
378 PDFWriter::CheckBoxWidget aCBox2
;
379 aCBox2
.Name
= OUString( "textCheckBox2" );
380 aCBox2
.Description
= OUString( "Another test check box" );
381 aCBox2
.Text
= OUString( "check me right" );
382 aCBox2
.Location
= Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
383 aCBox2
.Checked
= true;
384 aCBox2
.Border
= aCBox2
.Background
= false;
385 aCBox2
.ButtonIsLeft
= false;
386 aWriter
.CreateControl( aCBox2
);
388 PDFWriter::RadioButtonWidget aRB1
;
389 aRB1
.Name
= OUString( "rb1_1" );
390 aRB1
.Description
= OUString( "radio 1 button 1" );
391 aRB1
.Text
= OUString( "Despair" );
392 aRB1
.Location
= Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
393 aRB1
.Selected
= true;
395 aRB1
.Border
= aRB1
.Background
= true;
396 aRB1
.ButtonIsLeft
= false;
397 aRB1
.BorderColor
= Color( COL_LIGHTGREEN
);
398 aRB1
.BackgroundColor
= Color( COL_LIGHTBLUE
);
399 aRB1
.TextColor
= Color( COL_LIGHTRED
);
400 aRB1
.TextFont
= Font( String( "Courier" ), Size( 0, 800 ) );
401 aWriter
.CreateControl( aRB1
);
403 PDFWriter::RadioButtonWidget aRB2
;
404 aRB2
.Name
= OUString( "rb2_1" );
405 aRB2
.Description
= OUString( "radio 2 button 1" );
406 aRB2
.Text
= OUString( "Joy" );
407 aRB2
.Location
= Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
408 aRB2
.Selected
= true;
410 aWriter
.CreateControl( aRB2
);
412 PDFWriter::RadioButtonWidget aRB3
;
413 aRB3
.Name
= OUString( "rb1_2" );
414 aRB3
.Description
= OUString( "radio 1 button 2" );
415 aRB3
.Text
= OUString( "Desperation" );
416 aRB3
.Location
= Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
417 aRB3
.Selected
= true;
419 aWriter
.CreateControl( aRB3
);
421 PDFWriter::EditWidget aEditBox
;
422 aEditBox
.Name
= OUString( "testEdit" );
423 aEditBox
.Description
= OUString( "A test edit field" );
424 aEditBox
.Text
= OUString( "A little test text" );
425 aEditBox
.TextStyle
= TEXT_DRAW_LEFT
| TEXT_DRAW_VCENTER
;
426 aEditBox
.Location
= Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
427 aEditBox
.MaxLen
= 100;
428 aEditBox
.Border
= aEditBox
.Background
= true;
429 aEditBox
.BorderColor
= Color( COL_BLACK
);
430 aWriter
.CreateControl( aEditBox
);
433 PDFWriter::ListBoxWidget aLstBox
;
434 aLstBox
.Name
= OUString( "testListBox" );
435 aLstBox
.Text
= OUString( "One" );
436 aLstBox
.Description
= OUString( "select me" );
437 aLstBox
.Location
= Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
439 aLstBox
.MultiSelect
= true;
440 aLstBox
.Border
= aLstBox
.Background
= true;
441 aLstBox
.BorderColor
= Color( COL_BLACK
);
442 aLstBox
.Entries
.push_back( OUString( "One" ) );
443 aLstBox
.Entries
.push_back( OUString( "Two" ) );
444 aLstBox
.Entries
.push_back( OUString( "Three" ) );
445 aLstBox
.Entries
.push_back( OUString( "Four" ) );
446 aLstBox
.SelectedEntries
.push_back( 1 );
447 aLstBox
.SelectedEntries
.push_back( 2 );
448 aWriter
.CreateControl( aLstBox
);
451 aLstBox
.Name
= OUString( "testDropDownListBox" );
452 aLstBox
.DropDown
= true;
453 aLstBox
.Location
= Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
454 aWriter
.CreateControl( aLstBox
);
457 PDFWriter::ComboBoxWidget aComboBox
;
458 aComboBox
.Name
= OUString( "testComboBox" );
459 aComboBox
.Text
= OUString( "test a combobox" );
460 aComboBox
.Entries
.push_back( OUString( "Larry" ) );
461 aComboBox
.Entries
.push_back( OUString( "Curly" ) );
462 aComboBox
.Entries
.push_back( OUString( "Moe" ) );
463 aComboBox
.Location
= Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
464 aWriter
.CreateControl( aComboBox
);
467 sal_Int32 nPage1OL
= aWriter
.CreateOutlineItem();
468 aWriter
.SetOutlineItemText( nPage1OL
, OUString( "Page 1" ) );
469 aWriter
.SetOutlineItemDest( nPage1OL
, nSecondDest
);
470 aWriter
.CreateOutlineItem( nPage1OL
, OUString( "Dest 2" ), nSecondDest
);
471 aWriter
.CreateOutlineItem( nPage1OL
, OUString( "Dest 2 revisited" ), nSecondDest
);
472 aWriter
.CreateOutlineItem( nPage1OL
, OUString( "Dest 2 again" ), nSecondDest
);
473 sal_Int32 nPage2OL
= aWriter
.CreateOutlineItem();
474 aWriter
.SetOutlineItemText( nPage2OL
, OUString( "Page 2" ) );
475 aWriter
.CreateOutlineItem( nPage2OL
, OUString( "Dest 1" ), nFirstDest
);
477 aWriter
.EndStructureElement(); // close document
483 static const sal_Int32 nLog10Divisor
= 1;
484 static const double fDivisor
= 10.0;
486 static inline double pixelToPoint( sal_Int32 px
) { return double(px
)/fDivisor
; }
487 static inline double pixelToPoint( double px
) { return px
/fDivisor
; }
488 static inline sal_Int32
pointToPixel( double pt
) { return sal_Int32(pt
*fDivisor
); }
490 const sal_uInt8
PDFWriterImpl::s_nPadString
[32] =
492 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
493 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
496 static void appendHex( sal_Int8 nInt
, OStringBuffer
& rBuffer
)
498 static const sal_Char pHexDigits
[] = { '0', '1', '2', '3', '4', '5', '6', '7',
499 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
500 rBuffer
.append( pHexDigits
[ (nInt
>> 4) & 15 ] );
501 rBuffer
.append( pHexDigits
[ nInt
& 15 ] );
504 static void appendName( const OUString
& rStr
, OStringBuffer
& rBuffer
)
506 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
507 // I guess than when reading the #xx sequence it will count for a single character.
508 OString
aStr( OUStringToOString( rStr
, RTL_TEXTENCODING_UTF8
) );
509 const sal_Char
* pStr
= aStr
.getStr();
510 int nLen
= aStr
.getLength();
511 for( int i
= 0; i
< nLen
; i
++ )
513 /* #i16920# PDF recommendation: output UTF8, any byte
514 * outside the interval [33(=ASCII'!');126(=ASCII'~')]
515 * should be escaped hexadecimal
516 * for the sake of ghostscript which also reads PDF
517 * but has a narrower acceptance rate we only pass
518 * alphanumerics and '-' literally.
520 if( (pStr
[i
] >= 'A' && pStr
[i
] <= 'Z' ) ||
521 (pStr
[i
] >= 'a' && pStr
[i
] <= 'z' ) ||
522 (pStr
[i
] >= '0' && pStr
[i
] <= '9' ) ||
525 rBuffer
.append( pStr
[i
] );
529 rBuffer
.append( '#' );
530 appendHex( (sal_Int8
)pStr
[i
], rBuffer
);
535 static void appendName( const sal_Char
* pStr
, OStringBuffer
& rBuffer
)
537 //FIXME i59651 see above
538 while( pStr
&& *pStr
)
540 if( (*pStr
>= 'A' && *pStr
<= 'Z' ) ||
541 (*pStr
>= 'a' && *pStr
<= 'z' ) ||
542 (*pStr
>= '0' && *pStr
<= '9' ) ||
545 rBuffer
.append( *pStr
);
549 rBuffer
.append( '#' );
550 appendHex( (sal_Int8
)*pStr
, rBuffer
);
556 //used only to emit encoded passwords
557 static void appendLiteralString( const sal_Char
* pStr
, sal_Int32 nLength
, OStringBuffer
& rBuffer
)
564 rBuffer
.append( "\\n" );
567 rBuffer
.append( "\\r" );
570 rBuffer
.append( "\\t" );
573 rBuffer
.append( "\\b" );
576 rBuffer
.append( "\\f" );
581 rBuffer
.append( "\\" );
582 rBuffer
.append( (sal_Char
) *pStr
);
585 rBuffer
.append( (sal_Char
) *pStr
);
594 * Convert a string before using it.
596 * This string conversion function is needed because the destination name
597 * in a PDF file seen through an Internet browser should be
598 * specially crafted, in order to be used directly by the browser.
599 * In this way the fragment part of a hyperlink to a PDF file (e.g. something
600 * as 'test1/test2/a-file.pdf#thefragment) will be (hopefully) interpreted by the
601 * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
602 * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
603 * and go to named destination thefragment using default zoom'.
604 * The conversion is needed because in case of a fragment in the form: Slide%201
605 * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
606 * using this conversion, in both the generated named destinations, fragment and GoToR
609 * The names for destinations are name objects and so they don't need to be encrypted
610 * even though they expose the content of PDF file (e.g. guessing the PDF content from the
613 * Fhurter limitation: it is advisable to use standard ASCII characters for
616 static void appendDestinationName( const OUString
& rString
, OStringBuffer
& rBuffer
)
618 const sal_Unicode
* pStr
= rString
.getStr();
619 sal_Int32 nLen
= rString
.getLength();
620 for( int i
= 0; i
< nLen
; i
++ )
622 sal_Unicode aChar
= pStr
[i
];
623 if( (aChar
>= '0' && aChar
<= '9' ) ||
624 (aChar
>= 'a' && aChar
<= 'z' ) ||
625 (aChar
>= 'A' && aChar
<= 'Z' ) ||
628 rBuffer
.append((sal_Char
)aChar
);
632 sal_Int8 aValueHigh
= sal_Int8(aChar
>> 8);
634 appendHex( aValueHigh
, rBuffer
);
635 appendHex( (sal_Int8
)(aChar
& 255 ), rBuffer
);
641 static void appendUnicodeTextString( const OUString
& rString
, OStringBuffer
& rBuffer
)
643 rBuffer
.append( "FEFF" );
644 const sal_Unicode
* pStr
= rString
.getStr();
645 sal_Int32 nLen
= rString
.getLength();
646 for( int i
= 0; i
< nLen
; i
++ )
648 sal_Unicode aChar
= pStr
[i
];
649 appendHex( (sal_Int8
)(aChar
>> 8), rBuffer
);
650 appendHex( (sal_Int8
)(aChar
& 255 ), rBuffer
);
654 void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex
, const PDFWriter::AnyWidget
& i_rControl
)
656 /* #i80258# previously we use appendName here
657 however we need a slightly different coding scheme than the normal
658 name encoding for field names
660 const OUString
& rName
= (m_aContext
.Version
> PDFWriter::PDF_1_2
) ? i_rControl
.Name
: i_rControl
.Text
;
661 OString
aStr( OUStringToOString( rName
, RTL_TEXTENCODING_UTF8
) );
662 const sal_Char
* pStr
= aStr
.getStr();
663 int nLen
= aStr
.getLength();
665 OStringBuffer
aBuffer( rName
.getLength()+64 );
666 for( int i
= 0; i
< nLen
; i
++ )
668 /* #i16920# PDF recommendation: output UTF8, any byte
669 * outside the interval [32(=ASCII' ');126(=ASCII'~')]
670 * should be escaped hexadecimal
672 if( (pStr
[i
] >= 32 && pStr
[i
] <= 126 ) )
673 aBuffer
.append( pStr
[i
] );
676 aBuffer
.append( '#' );
677 appendHex( (sal_Int8
)pStr
[i
], aBuffer
);
681 OString
aFullName( aBuffer
.makeStringAndClear() );
683 /* #i82785# create hierarchical fields down to the for each dot in i_rName */
684 sal_Int32 nTokenIndex
= 0, nLastTokenIndex
= 0;
685 OString aPartialName
;
689 nLastTokenIndex
= nTokenIndex
;
690 aPartialName
= aFullName
.getToken( 0, '.', nTokenIndex
);
691 if( nTokenIndex
!= -1 )
693 // find or create a hierarchical field
694 // first find the fully qualified name up to this field
695 aDomain
= aFullName
.copy( 0, nTokenIndex
-1 );
696 boost::unordered_map
< OString
, sal_Int32
, OStringHash
>::const_iterator it
= m_aFieldNameMap
.find( aDomain
);
697 if( it
== m_aFieldNameMap
.end() )
699 // create new hierarchy field
700 sal_Int32 nNewWidget
= m_aWidgets
.size();
701 m_aWidgets
.push_back( PDFWidget() );
702 m_aWidgets
[nNewWidget
].m_nObject
= createObject();
703 m_aWidgets
[nNewWidget
].m_eType
= PDFWriter::Hierarchy
;
704 m_aWidgets
[nNewWidget
].m_aName
= aPartialName
;
705 m_aWidgets
[i_nWidgetIndex
].m_nParent
= m_aWidgets
[nNewWidget
].m_nObject
;
706 m_aFieldNameMap
[aDomain
] = nNewWidget
;
707 m_aWidgets
[i_nWidgetIndex
].m_nParent
= m_aWidgets
[nNewWidget
].m_nObject
;
708 if( nLastTokenIndex
> 0 )
710 // this field is not a root field and
711 // needs to be inserted to its parent
712 OString
aParentDomain( aDomain
.copy( 0, nLastTokenIndex
-1 ) );
713 it
= m_aFieldNameMap
.find( aParentDomain
);
714 OSL_ENSURE( it
!= m_aFieldNameMap
.end(), "field name not found" );
715 if( it
!= m_aFieldNameMap
.end() )
717 OSL_ENSURE( it
->second
< sal_Int32(m_aWidgets
.size()), "invalid field number entry" );
718 if( it
->second
< sal_Int32(m_aWidgets
.size()) )
720 PDFWidget
& rParentField( m_aWidgets
[it
->second
] );
721 rParentField
.m_aKids
.push_back( m_aWidgets
[nNewWidget
].m_nObject
);
722 rParentField
.m_aKidsIndex
.push_back( nNewWidget
);
723 m_aWidgets
[nNewWidget
].m_nParent
= rParentField
.m_nObject
;
728 else if( m_aWidgets
[it
->second
].m_eType
!= PDFWriter::Hierarchy
)
730 // this is invalid, someone tries to have a terminal field as parent
731 // example: a button with the name foo.bar exists and
732 // another button is named foo.bar.no
733 // workaround: put the second terminal field as much up in the hierarchy as
734 // necessary to have a non-terminal field as parent (or none at all)
735 // since it->second already is terminal, we just need to use its parent
737 aPartialName
= aFullName
.copy( aFullName
.lastIndexOf( '.' )+1 );
738 if( nLastTokenIndex
> 0 )
740 aDomain
= aFullName
.copy( 0, nLastTokenIndex
-1 );
741 OStringBuffer
aBuf( aDomain
.getLength() + 1 + aPartialName
.getLength() );
742 aBuf
.append( aDomain
);
744 aBuf
.append( aPartialName
);
745 aFullName
= aBuf
.makeStringAndClear();
748 aFullName
= aPartialName
;
752 } while( nTokenIndex
!= -1 );
754 // insert widget into its hierarchy field
755 if( !aDomain
.isEmpty() )
757 boost::unordered_map
< OString
, sal_Int32
, OStringHash
>::const_iterator it
= m_aFieldNameMap
.find( aDomain
);
758 if( it
!= m_aFieldNameMap
.end() )
760 OSL_ENSURE( it
->second
>= 0 && it
->second
< sal_Int32( m_aWidgets
.size() ), "invalid field index" );
761 if( it
->second
>= 0 && it
->second
< sal_Int32(m_aWidgets
.size()) )
763 m_aWidgets
[i_nWidgetIndex
].m_nParent
= m_aWidgets
[it
->second
].m_nObject
;
764 m_aWidgets
[it
->second
].m_aKids
.push_back( m_aWidgets
[i_nWidgetIndex
].m_nObject
);
765 m_aWidgets
[it
->second
].m_aKidsIndex
.push_back( i_nWidgetIndex
);
770 if( aPartialName
.isEmpty() )
772 // how funny, an empty field name
773 if( i_rControl
.getType() == PDFWriter::RadioButton
)
775 aPartialName
= "RadioGroup";
776 aPartialName
+= OString::valueOf( static_cast<const PDFWriter::RadioButtonWidget
&>(i_rControl
).RadioGroup
);
779 aPartialName
= OString( "Widget" );
782 if( ! m_aContext
.AllowDuplicateFieldNames
)
784 boost::unordered_map
<OString
, sal_Int32
, OStringHash
>::iterator it
= m_aFieldNameMap
.find( aFullName
);
786 if( it
!= m_aFieldNameMap
.end() ) // not unique
788 boost::unordered_map
< OString
, sal_Int32
, OStringHash
>::const_iterator check_it
;
793 OStringBuffer
aUnique( aFullName
.getLength() + 16 );
794 aUnique
.append( aFullName
);
795 aUnique
.append( '_' );
796 aUnique
.append( nTry
++ );
797 aTry
= aUnique
.makeStringAndClear();
798 check_it
= m_aFieldNameMap
.find( aTry
);
799 } while( check_it
!= m_aFieldNameMap
.end() );
801 m_aFieldNameMap
[ aFullName
] = i_nWidgetIndex
;
802 aPartialName
= aFullName
.copy( aFullName
.lastIndexOf( '.' )+1 );
805 m_aFieldNameMap
[ aFullName
] = i_nWidgetIndex
;
809 m_aWidgets
[i_nWidgetIndex
].m_aName
= aPartialName
;
812 static void appendFixedInt( sal_Int32 nValue
, OStringBuffer
& rBuffer
, sal_Int32 nPrecision
= nLog10Divisor
)
816 rBuffer
.append( '-' );
819 sal_Int32 nFactor
= 1, nDiv
= nPrecision
;
823 sal_Int32 nInt
= nValue
/ nFactor
;
824 rBuffer
.append( nInt
);
827 sal_Int32 nDecimal
= nValue
% nFactor
;
830 rBuffer
.append( '.' );
831 // omit trailing zeros
832 while( (nDecimal
% 10) == 0 )
834 rBuffer
.append( nDecimal
);
840 // appends a double. PDF does not accept exponential format, only fixed point
841 static void appendDouble( double fValue
, OStringBuffer
& rBuffer
, sal_Int32 nPrecision
= 5 )
850 sal_Int64 nInt
= (sal_Int64
)fValue
;
851 fValue
-= (double)nInt
;
852 // optimizing hardware may lead to a value of 1.0 after the subtraction
853 if( fValue
== 1.0 || log10( 1.0-fValue
) <= -nPrecision
)
861 fValue
*= pow( 10.0, (double)nPrecision
);
862 nFrac
= (sal_Int64
)fValue
;
864 if( bNeg
&& ( nInt
|| nFrac
) )
865 rBuffer
.append( '-' );
866 rBuffer
.append( nInt
);
870 rBuffer
.append( '.' );
871 sal_Int64 nBound
= (sal_Int64
)(pow( 10.0, nPrecision
- 1.0 )+0.5);
872 for ( i
= 0; ( i
< nPrecision
) && nFrac
; i
++ )
874 sal_Int64 nNumb
= nFrac
/ nBound
;
875 nFrac
-= nNumb
* nBound
;
876 rBuffer
.append( nNumb
);
883 static void appendColor( const Color
& rColor
, OStringBuffer
& rBuffer
, bool bConvertToGrey
= false )
886 if( rColor
!= Color( COL_TRANSPARENT
) )
890 sal_uInt8 cByte
= rColor
.GetLuminance();
891 appendDouble( (double)cByte
/ 255.0, rBuffer
);
895 appendDouble( (double)rColor
.GetRed() / 255.0, rBuffer
);
896 rBuffer
.append( ' ' );
897 appendDouble( (double)rColor
.GetGreen() / 255.0, rBuffer
);
898 rBuffer
.append( ' ' );
899 appendDouble( (double)rColor
.GetBlue() / 255.0, rBuffer
);
904 void PDFWriterImpl::appendStrokingColor( const Color
& rColor
, OStringBuffer
& rBuffer
)
906 if( rColor
!= Color( COL_TRANSPARENT
) )
908 bool bGrey
= m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
;
909 appendColor( rColor
, rBuffer
, bGrey
);
910 rBuffer
.append( bGrey
? " G" : " RG" );
914 void PDFWriterImpl::appendNonStrokingColor( const Color
& rColor
, OStringBuffer
& rBuffer
)
916 if( rColor
!= Color( COL_TRANSPARENT
) )
918 bool bGrey
= m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
;
919 appendColor( rColor
, rBuffer
, bGrey
);
920 rBuffer
.append( bGrey
? " g" : " rg" );
924 // matrix helper class
925 // TODO: use basegfx matrix class instead or derive from it
926 namespace vcl
// TODO: use anonymous namespace to keep this class local
928 /* for sparse matrices of the form (2D linear transformations)
937 void set( double *pn
) { for( int i
= 0 ; i
< 6; i
++ ) f
[i
] = pn
[i
]; }
942 void skew( double alpha
, double beta
);
943 void scale( double sx
, double sy
);
944 void rotate( double angle
);
945 void translate( double tx
, double ty
);
948 void append( PDFWriterImpl::PDFPage
& rPage
, OStringBuffer
& rBuffer
, Point
* pBack
= NULL
);
950 Point
transform( const Point
& rPoint
) const;
956 // initialize to unity
965 Point
Matrix3::transform( const Point
& rOrig
) const
967 double x
= (double)rOrig
.X(), y
= (double)rOrig
.Y();
968 return Point( (int)(x
*f
[0] + y
*f
[2] + f
[4]), (int)(x
*f
[1] + y
*f
[3] + f
[5]) );
971 void Matrix3::skew( double alpha
, double beta
)
974 double tb
= tan( beta
);
975 fn
[0] = f
[0] + f
[2]*tb
;
977 fn
[2] = f
[2] + f
[3]*tb
;
979 fn
[4] = f
[4] + f
[5]*tb
;
983 double ta
= tan( alpha
);
991 void Matrix3::scale( double sx
, double sy
)
1003 void Matrix3::rotate( double angle
)
1006 double fSin
= sin(angle
);
1007 double fCos
= cos(angle
);
1008 fn
[0] = f
[0]*fCos
- f
[1]*fSin
;
1009 fn
[1] = f
[0]*fSin
+ f
[1]*fCos
;
1010 fn
[2] = f
[2]*fCos
- f
[3]*fSin
;
1011 fn
[3] = f
[2]*fSin
+ f
[3]*fCos
;
1012 fn
[4] = f
[4]*fCos
- f
[5]*fSin
;
1013 fn
[5] = f
[4]*fSin
+ f
[5]*fCos
;
1017 void Matrix3::translate( double tx
, double ty
)
1023 bool Matrix3::invert()
1025 // short circuit trivial cases
1026 if( f
[1]==f
[2] && f
[1]==0.0 && f
[0]==f
[3] && f
[0]==1.0 )
1033 // check determinant
1034 const double fDet
= f
[0]*f
[3]-f
[1]*f
[2];
1038 // invert the matrix
1040 fn
[0] = +f
[3] / fDet
;
1041 fn
[1] = -f
[1] / fDet
;
1042 fn
[2] = -f
[2] / fDet
;
1043 fn
[3] = +f
[0] / fDet
;
1045 // apply inversion to translation
1046 fn
[4] = -(f
[4]*fn
[0] + f
[5]*fn
[2]);
1047 fn
[5] = -(f
[4]*fn
[1] + f
[5]*fn
[3]);
1053 void Matrix3::append( PDFWriterImpl::PDFPage
& rPage
, OStringBuffer
& rBuffer
, Point
* pBack
)
1055 appendDouble( f
[0], rBuffer
);
1056 rBuffer
.append( ' ' );
1057 appendDouble( f
[1], rBuffer
);
1058 rBuffer
.append( ' ' );
1059 appendDouble( f
[2], rBuffer
);
1060 rBuffer
.append( ' ' );
1061 appendDouble( f
[3], rBuffer
);
1062 rBuffer
.append( ' ' );
1063 rPage
.appendPoint( Point( (long)f
[4], (long)f
[5] ), rBuffer
, false, pBack
);
1066 static void appendResourceMap( OStringBuffer
& rBuf
, const char* pPrefix
, const PDFWriterImpl::ResourceMap
& rList
)
1071 rBuf
.append( pPrefix
);
1072 rBuf
.append( "<<" );
1074 for( PDFWriterImpl::ResourceMap::const_iterator it
= rList
.begin(); it
!= rList
.end(); ++it
)
1076 if( !it
->first
.isEmpty() && it
->second
> 0 )
1079 rBuf
.append( it
->first
);
1081 rBuf
.append( it
->second
);
1082 rBuf
.append( " 0 R" );
1083 if( ((++ni
) & 7) == 0 )
1084 rBuf
.append( '\n' );
1087 rBuf
.append( ">>\n" );
1090 void PDFWriterImpl::ResourceDict::append( OStringBuffer
& rBuf
, sal_Int32 nFontDictObject
)
1092 rBuf
.append( "<</Font " );
1093 rBuf
.append( nFontDictObject
);
1094 rBuf
.append( " 0 R\n" );
1095 appendResourceMap( rBuf
, "XObject", m_aXObjects
);
1096 appendResourceMap( rBuf
, "ExtGState", m_aExtGStates
);
1097 appendResourceMap( rBuf
, "Shading", m_aShadings
);
1098 appendResourceMap( rBuf
, "Pattern", m_aPatterns
);
1099 rBuf
.append( "/ProcSet[/PDF/Text" );
1100 if( !m_aXObjects
.empty() )
1101 rBuf
.append( "/ImageC/ImageI/ImageB" );
1102 rBuf
.append( "]\n>>\n" );
1105 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl
* pWriter
, sal_Int32 nPageWidth
, sal_Int32 nPageHeight
, PDFWriter::Orientation eOrientation
)
1107 m_pWriter( pWriter
),
1108 m_nPageWidth( nPageWidth
),
1109 m_nPageHeight( nPageHeight
),
1110 m_eOrientation( eOrientation
),
1111 m_nPageObject( 0 ), // invalid object number
1112 m_nPageIndex( -1 ), // invalid index
1113 m_nStreamLengthObject( 0 ),
1114 m_nBeginStreamPos( 0 ),
1115 m_eTransition( PDFWriter::Regular
),
1118 m_bHasWidgets( false )
1120 // object ref must be only ever updated in emit()
1121 m_nPageObject
= m_pWriter
->createObject();
1124 PDFWriterImpl::PDFPage::~PDFPage()
1128 void PDFWriterImpl::PDFPage::beginStream()
1130 #if OSL_DEBUG_LEVEL > 1
1132 OStringBuffer
aLine( "PDFWriterImpl::PDFPage::beginStream, +" );
1133 m_pWriter
->emitComment( aLine
.getStr() );
1136 m_aStreamObjects
.push_back(m_pWriter
->createObject());
1137 if( ! m_pWriter
->updateObject( m_aStreamObjects
.back() ) )
1140 m_nStreamLengthObject
= m_pWriter
->createObject();
1141 // write content stream header
1142 OStringBuffer aLine
;
1143 aLine
.append( m_aStreamObjects
.back() );
1144 aLine
.append( " 0 obj\n<</Length " );
1145 aLine
.append( m_nStreamLengthObject
);
1146 aLine
.append( " 0 R" );
1147 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1148 aLine
.append( "/Filter/FlateDecode" );
1150 aLine
.append( ">>\nstream\n" );
1151 if( ! m_pWriter
->writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
1153 if( osl_File_E_None
!= osl_getFilePos( m_pWriter
->m_aFile
, &m_nBeginStreamPos
) )
1155 osl_closeFile( m_pWriter
->m_aFile
);
1156 m_pWriter
->m_bOpen
= false;
1158 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1159 m_pWriter
->beginCompression();
1161 m_pWriter
->checkAndEnableStreamEncryption( m_aStreamObjects
.back() );
1164 void PDFWriterImpl::PDFPage::endStream()
1166 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1167 m_pWriter
->endCompression();
1169 sal_uInt64 nEndStreamPos
;
1170 if( osl_File_E_None
!= osl_getFilePos( m_pWriter
->m_aFile
, &nEndStreamPos
) )
1172 osl_closeFile( m_pWriter
->m_aFile
);
1173 m_pWriter
->m_bOpen
= false;
1176 m_pWriter
->disableStreamEncryption();
1177 if( ! m_pWriter
->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1179 // emit stream length object
1180 if( ! m_pWriter
->updateObject( m_nStreamLengthObject
) )
1182 OStringBuffer aLine
;
1183 aLine
.append( m_nStreamLengthObject
);
1184 aLine
.append( " 0 obj\n" );
1185 aLine
.append( (sal_Int64
)(nEndStreamPos
-m_nBeginStreamPos
) );
1186 aLine
.append( "\nendobj\n\n" );
1187 m_pWriter
->writeBuffer( aLine
.getStr(), aLine
.getLength() );
1190 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject
)
1193 if( ! m_pWriter
->updateObject( m_nPageObject
) )
1195 OStringBuffer aLine
;
1197 aLine
.append( m_nPageObject
);
1198 aLine
.append( " 0 obj\n"
1199 "<</Type/Page/Parent " );
1200 aLine
.append( nParentObject
);
1201 aLine
.append( " 0 R" );
1202 aLine
.append( "/Resources " );
1203 aLine
.append( m_pWriter
->getResourceDictObj() );
1204 aLine
.append( " 0 R" );
1205 if( m_nPageWidth
&& m_nPageHeight
)
1207 aLine
.append( "/MediaBox[0 0 " );
1208 aLine
.append( m_nPageWidth
);
1209 aLine
.append( ' ' );
1210 aLine
.append( m_nPageHeight
);
1211 aLine
.append( "]" );
1213 switch( m_eOrientation
)
1215 case PDFWriter::Landscape
: aLine
.append( "/Rotate 90\n" );break;
1216 case PDFWriter::Seascape
: aLine
.append( "/Rotate -90\n" );break;
1217 case PDFWriter::Portrait
: aLine
.append( "/Rotate 0\n" );break;
1219 case PDFWriter::Inherit
:
1223 int nAnnots
= m_aAnnotations
.size();
1226 aLine
.append( "/Annots[\n" );
1227 for( int i
= 0; i
< nAnnots
; i
++ )
1229 aLine
.append( m_aAnnotations
[i
] );
1230 aLine
.append( " 0 R" );
1231 aLine
.append( ((i
+1)%15) ? " " : "\n" );
1233 aLine
.append( "]\n" );
1235 if( m_aMCIDParents
.size() > 0 )
1237 OStringBuffer
aStructParents( 1024 );
1238 aStructParents
.append( "[ " );
1239 int nParents
= m_aMCIDParents
.size();
1240 for( int i
= 0; i
< nParents
; i
++ )
1242 aStructParents
.append( m_aMCIDParents
[i
] );
1243 aStructParents
.append( " 0 R" );
1244 aStructParents
.append( ((i
%10) == 9) ? "\n" : " " );
1246 aStructParents
.append( "]" );
1247 m_pWriter
->m_aStructParentTree
.push_back( aStructParents
.makeStringAndClear() );
1249 aLine
.append( "/StructParents " );
1250 aLine
.append( sal_Int32(m_pWriter
->m_aStructParentTree
.size()-1) );
1251 aLine
.append( "\n" );
1253 if( m_nDuration
> 0 )
1255 aLine
.append( "/Dur " );
1256 aLine
.append( (sal_Int32
)m_nDuration
);
1257 aLine
.append( "\n" );
1259 if( m_eTransition
!= PDFWriter::Regular
&& m_nTransTime
> 0 )
1261 // transition duration
1262 aLine
.append( "/Trans<</D " );
1263 appendDouble( (double)m_nTransTime
/1000.0, aLine
, 3 );
1264 aLine
.append( "\n" );
1265 const char *pStyle
= NULL
, *pDm
= NULL
, *pM
= NULL
, *pDi
= NULL
;
1266 switch( m_eTransition
)
1268 case PDFWriter::SplitHorizontalInward
:
1269 pStyle
= "Split"; pDm
= "H"; pM
= "I"; break;
1270 case PDFWriter::SplitHorizontalOutward
:
1271 pStyle
= "Split"; pDm
= "H"; pM
= "O"; break;
1272 case PDFWriter::SplitVerticalInward
:
1273 pStyle
= "Split"; pDm
= "V"; pM
= "I"; break;
1274 case PDFWriter::SplitVerticalOutward
:
1275 pStyle
= "Split"; pDm
= "V"; pM
= "O"; break;
1276 case PDFWriter::BlindsHorizontal
:
1277 pStyle
= "Blinds"; pDm
= "H"; break;
1278 case PDFWriter::BlindsVertical
:
1279 pStyle
= "Blinds"; pDm
= "V"; break;
1280 case PDFWriter::BoxInward
:
1281 pStyle
= "Box"; pM
= "I"; break;
1282 case PDFWriter::BoxOutward
:
1283 pStyle
= "Box"; pM
= "O"; break;
1284 case PDFWriter::WipeLeftToRight
:
1285 pStyle
= "Wipe"; pDi
= "0"; break;
1286 case PDFWriter::WipeBottomToTop
:
1287 pStyle
= "Wipe"; pDi
= "90"; break;
1288 case PDFWriter::WipeRightToLeft
:
1289 pStyle
= "Wipe"; pDi
= "180"; break;
1290 case PDFWriter::WipeTopToBottom
:
1291 pStyle
= "Wipe"; pDi
= "270"; break;
1292 case PDFWriter::Dissolve
:
1293 pStyle
= "Dissolve"; break;
1294 case PDFWriter::GlitterLeftToRight
:
1295 pStyle
= "Glitter"; pDi
= "0"; break;
1296 case PDFWriter::GlitterTopToBottom
:
1297 pStyle
= "Glitter"; pDi
= "270"; break;
1298 case PDFWriter::GlitterTopLeftToBottomRight
:
1299 pStyle
= "Glitter"; pDi
= "315"; break;
1300 case PDFWriter::Regular
:
1306 aLine
.append( "/S/" );
1307 aLine
.append( pStyle
);
1308 aLine
.append( "\n" );
1312 aLine
.append( "/Dm/" );
1313 aLine
.append( pDm
);
1314 aLine
.append( "\n" );
1318 aLine
.append( "/M/" );
1320 aLine
.append( "\n" );
1324 aLine
.append( "/Di " );
1325 aLine
.append( pDi
);
1326 aLine
.append( "\n" );
1328 aLine
.append( ">>\n" );
1330 if( m_pWriter
->getVersion() > PDFWriter::PDF_1_3
&& ! m_pWriter
->m_bIsPDF_A1
)
1332 aLine
.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1334 aLine
.append( "/Contents" );
1335 unsigned int nStreamObjects
= m_aStreamObjects
.size();
1336 if( nStreamObjects
> 1 )
1337 aLine
.append( '[' );
1338 for( unsigned int i
= 0; i
< m_aStreamObjects
.size(); i
++ )
1340 aLine
.append( ' ' );
1341 aLine
.append( m_aStreamObjects
[i
] );
1342 aLine
.append( " 0 R" );
1344 if( nStreamObjects
> 1 )
1345 aLine
.append( ']' );
1346 aLine
.append( ">>\nendobj\n\n" );
1347 return m_pWriter
->writeBuffer( aLine
.getStr(), aLine
.getLength() );
1352 template < class GEOMETRY
>
1353 GEOMETRY
lcl_convert( const MapMode
& _rSource
, const MapMode
& _rDest
, OutputDevice
* _pPixelConversion
, const GEOMETRY
& _rObject
)
1356 if ( MAP_PIXEL
== _rSource
.GetMapUnit() )
1358 aPoint
= _pPixelConversion
->PixelToLogic( _rObject
, _rDest
);
1362 aPoint
= OutputDevice::LogicToLogic( _rObject
, _rSource
, _rDest
);
1368 void PDFWriterImpl::PDFPage::appendPoint( const Point
& rPoint
, OStringBuffer
& rBuffer
, bool bNeg
, Point
* pOutPoint
) const
1372 Point
aPoint( lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1373 m_pWriter
->m_aMapMode
,
1374 m_pWriter
->getReferenceDevice(),
1376 *pOutPoint
= aPoint
;
1379 Point
aPoint( lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1380 m_pWriter
->m_aMapMode
,
1381 m_pWriter
->getReferenceDevice(),
1384 sal_Int32 nValue
= aPoint
.X();
1388 appendFixedInt( nValue
, rBuffer
);
1390 rBuffer
.append( ' ' );
1392 nValue
= pointToPixel(getHeight()) - aPoint
.Y();
1396 appendFixedInt( nValue
, rBuffer
);
1399 void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint
& rPoint
, OStringBuffer
& rBuffer
) const
1401 double fValue
= pixelToPoint(rPoint
.getX());
1403 appendDouble( fValue
, rBuffer
, nLog10Divisor
);
1405 rBuffer
.append( ' ' );
1407 fValue
= double(getHeight()) - pixelToPoint(rPoint
.getY());
1409 appendDouble( fValue
, rBuffer
, nLog10Divisor
);
1412 void PDFWriterImpl::PDFPage::appendRect( const Rectangle
& rRect
, OStringBuffer
& rBuffer
) const
1414 appendPoint( rRect
.BottomLeft() + Point( 0, 1 ), rBuffer
);
1415 rBuffer
.append( ' ' );
1416 appendMappedLength( (sal_Int32
)rRect
.GetWidth(), rBuffer
, false );
1417 rBuffer
.append( ' ' );
1418 appendMappedLength( (sal_Int32
)rRect
.GetHeight(), rBuffer
, true );
1419 rBuffer
.append( " re" );
1422 void PDFWriterImpl::PDFPage::convertRect( Rectangle
& rRect
) const
1424 Point aLL
= lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1425 m_pWriter
->m_aMapMode
,
1426 m_pWriter
->getReferenceDevice(),
1427 rRect
.BottomLeft() + Point( 0, 1 )
1429 Size aSize
= lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1430 m_pWriter
->m_aMapMode
,
1431 m_pWriter
->getReferenceDevice(),
1433 rRect
.Left() = aLL
.X();
1434 rRect
.Right() = aLL
.X() + aSize
.Width();
1435 rRect
.Top() = pointToPixel(getHeight()) - aLL
.Y();
1436 rRect
.Bottom() = rRect
.Top() + aSize
.Height();
1439 void PDFWriterImpl::PDFPage::appendPolygon( const Polygon
& rPoly
, OStringBuffer
& rBuffer
, bool bClose
) const
1441 sal_uInt16 nPoints
= rPoly
.GetSize();
1443 * #108582# applications do weird things
1445 sal_uInt32 nBufLen
= rBuffer
.getLength();
1448 const sal_uInt8
* pFlagArray
= rPoly
.GetConstFlagAry();
1449 appendPoint( rPoly
[0], rBuffer
);
1450 rBuffer
.append( " m\n" );
1451 for( sal_uInt16 i
= 1; i
< nPoints
; i
++ )
1453 if( pFlagArray
&& pFlagArray
[i
] == POLY_CONTROL
&& nPoints
-i
> 2 )
1456 DBG_ASSERT( pFlagArray
[i
+1] == POLY_CONTROL
&& pFlagArray
[i
+2] != POLY_CONTROL
, "unexpected sequence of control points" );
1457 appendPoint( rPoly
[i
], rBuffer
);
1458 rBuffer
.append( " " );
1459 appendPoint( rPoly
[i
+1], rBuffer
);
1460 rBuffer
.append( " " );
1461 appendPoint( rPoly
[i
+2], rBuffer
);
1462 rBuffer
.append( " c" );
1463 i
+= 2; // add additionally consumed points
1468 appendPoint( rPoly
[i
], rBuffer
);
1469 rBuffer
.append( " l" );
1471 if( (rBuffer
.getLength() - nBufLen
) > 65 )
1473 rBuffer
.append( "\n" );
1474 nBufLen
= rBuffer
.getLength();
1477 rBuffer
.append( " " );
1480 rBuffer
.append( "h\n" );
1484 void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon
& rPoly
, OStringBuffer
& rBuffer
, bool bClose
) const
1486 basegfx::B2DPolygon
aPoly( lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1487 m_pWriter
->m_aMapMode
,
1488 m_pWriter
->getReferenceDevice(),
1491 if( basegfx::tools::isRectangle( aPoly
) )
1493 basegfx::B2DRange
aRange( aPoly
.getB2DRange() );
1494 basegfx::B2DPoint
aBL( aRange
.getMinX(), aRange
.getMaxY() );
1495 appendPixelPoint( aBL
, rBuffer
);
1496 rBuffer
.append( ' ' );
1497 appendMappedLength( aRange
.getWidth(), rBuffer
, false, NULL
, nLog10Divisor
);
1498 rBuffer
.append( ' ' );
1499 appendMappedLength( aRange
.getHeight(), rBuffer
, true, NULL
, nLog10Divisor
);
1500 rBuffer
.append( " re\n" );
1503 sal_uInt32 nPoints
= aPoly
.count();
1506 sal_uInt32 nBufLen
= rBuffer
.getLength();
1507 basegfx::B2DPoint
aLastPoint( aPoly
.getB2DPoint( 0 ) );
1508 appendPixelPoint( aLastPoint
, rBuffer
);
1509 rBuffer
.append( " m\n" );
1510 for( sal_uInt32 i
= 1; i
<= nPoints
; i
++ )
1512 if( i
!= nPoints
|| aPoly
.isClosed() )
1514 sal_uInt32 nCurPoint
= i
% nPoints
;
1515 sal_uInt32 nLastPoint
= i
-1;
1516 basegfx::B2DPoint
aPoint( aPoly
.getB2DPoint( nCurPoint
) );
1517 if( aPoly
.isNextControlPointUsed( nLastPoint
) &&
1518 aPoly
.isPrevControlPointUsed( nCurPoint
) )
1520 appendPixelPoint( aPoly
.getNextControlPoint( nLastPoint
), rBuffer
);
1521 rBuffer
.append( ' ' );
1522 appendPixelPoint( aPoly
.getPrevControlPoint( nCurPoint
), rBuffer
);
1523 rBuffer
.append( ' ' );
1524 appendPixelPoint( aPoint
, rBuffer
);
1525 rBuffer
.append( " c" );
1527 else if( aPoly
.isNextControlPointUsed( nLastPoint
) )
1529 appendPixelPoint( aPoly
.getNextControlPoint( nLastPoint
), rBuffer
);
1530 rBuffer
.append( ' ' );
1531 appendPixelPoint( aPoint
, rBuffer
);
1532 rBuffer
.append( " y" );
1534 else if( aPoly
.isPrevControlPointUsed( nCurPoint
) )
1536 appendPixelPoint( aPoly
.getPrevControlPoint( nCurPoint
), rBuffer
);
1537 rBuffer
.append( ' ' );
1538 appendPixelPoint( aPoint
, rBuffer
);
1539 rBuffer
.append( " v" );
1543 appendPixelPoint( aPoint
, rBuffer
);
1544 rBuffer
.append( " l" );
1546 if( (rBuffer
.getLength() - nBufLen
) > 65 )
1548 rBuffer
.append( "\n" );
1549 nBufLen
= rBuffer
.getLength();
1552 rBuffer
.append( " " );
1556 rBuffer
.append( "h\n" );
1560 void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon
& rPolyPoly
, OStringBuffer
& rBuffer
, bool bClose
) const
1562 sal_uInt16 nPolygons
= rPolyPoly
.Count();
1563 for( sal_uInt16 n
= 0; n
< nPolygons
; n
++ )
1564 appendPolygon( rPolyPoly
[n
], rBuffer
, bClose
);
1567 void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon
& rPolyPoly
, OStringBuffer
& rBuffer
, bool bClose
) const
1569 sal_uInt32 nPolygons
= rPolyPoly
.count();
1570 for( sal_uInt32 n
= 0; n
< nPolygons
; n
++ )
1571 appendPolygon( rPolyPoly
.getB2DPolygon( n
), rBuffer
, bClose
);
1574 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength
, OStringBuffer
& rBuffer
, bool bVertical
, sal_Int32
* pOutLength
) const
1576 sal_Int32 nValue
= nLength
;
1579 rBuffer
.append( '-' );
1582 Size
aSize( lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1583 m_pWriter
->m_aMapMode
,
1584 m_pWriter
->getReferenceDevice(),
1585 Size( nValue
, nValue
) ) );
1586 nValue
= bVertical
? aSize
.Height() : aSize
.Width();
1588 *pOutLength
= ((nLength
< 0 ) ? -nValue
: nValue
);
1590 appendFixedInt( nValue
, rBuffer
, 1 );
1593 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength
, OStringBuffer
& rBuffer
, bool bVertical
, sal_Int32
* pOutLength
, sal_Int32 nPrecision
) const
1595 Size
aSize( lcl_convert( m_pWriter
->m_aGraphicsStack
.front().m_aMapMode
,
1596 m_pWriter
->m_aMapMode
,
1597 m_pWriter
->getReferenceDevice(),
1598 Size( 1000, 1000 ) ) );
1600 *pOutLength
= (sal_Int32
)(fLength
*(double)(bVertical
? aSize
.Height() : aSize
.Width())/1000.0);
1601 fLength
*= pixelToPoint((double)(bVertical
? aSize
.Height() : aSize
.Width()) / 1000.0);
1602 appendDouble( fLength
, rBuffer
, nPrecision
);
1605 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo
& rInfo
, OStringBuffer
& rBuffer
) const
1607 if(LINE_DASH
== rInfo
.GetStyle() && rInfo
.GetDashLen() != rInfo
.GetDotLen())
1609 // dashed and non-degraded case, check for implementation limits of dash array
1610 // in PDF reader apps (e.g. acroread)
1611 if(2 * (rInfo
.GetDashCount() + rInfo
.GetDotCount()) > 10)
1617 if(basegfx::B2DLINEJOIN_NONE
!= rInfo
.GetLineJoin())
1619 // LineJoin used, ExtLineInfo required
1623 if(com::sun::star::drawing::LineCap_BUTT
!= rInfo
.GetLineCap())
1625 // LineCap used, ExtLineInfo required
1629 if( rInfo
.GetStyle() == LINE_DASH
)
1631 rBuffer
.append( "[ " );
1632 if( rInfo
.GetDashLen() == rInfo
.GetDotLen() ) // degraded case
1634 appendMappedLength( (sal_Int32
)rInfo
.GetDashLen(), rBuffer
);
1635 rBuffer
.append( ' ' );
1636 appendMappedLength( (sal_Int32
)rInfo
.GetDistance(), rBuffer
);
1637 rBuffer
.append( ' ' );
1641 for( int n
= 0; n
< rInfo
.GetDashCount(); n
++ )
1643 appendMappedLength( (sal_Int32
)rInfo
.GetDashLen(), rBuffer
);
1644 rBuffer
.append( ' ' );
1645 appendMappedLength( (sal_Int32
)rInfo
.GetDistance(), rBuffer
);
1646 rBuffer
.append( ' ' );
1648 for( int m
= 0; m
< rInfo
.GetDotCount(); m
++ )
1650 appendMappedLength( (sal_Int32
)rInfo
.GetDotLen(), rBuffer
);
1651 rBuffer
.append( ' ' );
1652 appendMappedLength( (sal_Int32
)rInfo
.GetDistance(), rBuffer
);
1653 rBuffer
.append( ' ' );
1656 rBuffer
.append( "] 0 d\n" );
1659 if( rInfo
.GetWidth() > 1 )
1661 appendMappedLength( (sal_Int32
)rInfo
.GetWidth(), rBuffer
);
1662 rBuffer
.append( " w\n" );
1664 else if( rInfo
.GetWidth() == 0 )
1667 appendDouble( 72.0/double(m_pWriter
->getReferenceDevice()->ImplGetDPIX()), rBuffer
);
1668 rBuffer
.append( " w\n" );
1674 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth
, sal_Int32 nY
, sal_Int32 nDelta
, OStringBuffer
& rBuffer
) const
1681 rBuffer
.append( "0 " );
1682 appendMappedLength( nY
, rBuffer
, true );
1683 rBuffer
.append( " m\n" );
1684 for( sal_Int32 n
= 0; n
< nWidth
; )
1687 appendMappedLength( n
, rBuffer
, false );
1688 rBuffer
.append( ' ' );
1689 appendMappedLength( nDelta
+nY
, rBuffer
, true );
1690 rBuffer
.append( ' ' );
1692 appendMappedLength( n
, rBuffer
, false );
1693 rBuffer
.append( ' ' );
1694 appendMappedLength( nY
, rBuffer
, true );
1695 rBuffer
.append( " v " );
1699 appendMappedLength( n
, rBuffer
, false );
1700 rBuffer
.append( ' ' );
1701 appendMappedLength( nY
-nDelta
, rBuffer
, true );
1702 rBuffer
.append( ' ' );
1704 appendMappedLength( n
, rBuffer
, false );
1705 rBuffer
.append( ' ' );
1706 appendMappedLength( nY
, rBuffer
, true );
1707 rBuffer
.append( " v\n" );
1710 rBuffer
.append( "S\n" );
1714 * class PDFWriterImpl
1717 PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext
& rContext
,
1718 const com::sun::star::uno::Reference
< com::sun::star::beans::XMaterialHolder
>& xEnc
,
1719 PDFWriter
& i_rOuterFace
)
1721 m_pReferenceDevice( NULL
),
1722 m_aMapMode( MAP_POINT
, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ),
1723 m_nCurrentStructElement( 0 ),
1724 m_bEmitStructure( true ),
1725 m_bNewMCID( false ),
1727 m_bEmbedStandardFonts( true ),
1729 m_bEmbedStandardFonts( false ),
1732 m_nInheritedPageWidth( 595 ), // default A4
1733 m_nInheritedPageHeight( 842 ), // default A4
1734 m_eInheritedOrientation( PDFWriter::Portrait
),
1735 m_nCurrentPage( -1 ),
1736 m_nSignatureObject( -1 ),
1737 m_nSignatureContentOffset( 0 ),
1738 m_nSignatureLastByteRangeNoOffset( 0 ),
1739 m_nResourceDict( -1 ),
1740 m_nFontDictObject( -1 ),
1742 m_aDocDigest( rtl_digest_createMD5() ),
1743 m_aCipher( (rtlCipher
)NULL
),
1745 m_bEncryptThisStream( false ),
1746 m_pEncryptionBuffer( NULL
),
1747 m_nEncryptionBufferSize( 0 ),
1748 m_bIsPDF_A1( false ),
1749 m_rOuterFace( i_rOuterFace
)
1752 static bool bOnce
= true;
1759 m_aContext
= rContext
;
1760 m_aStructure
.push_back( PDFStructureElement() );
1761 m_aStructure
[0].m_nOwnElement
= 0;
1762 m_aStructure
[0].m_nParentElement
= 0;
1765 aFont
.SetName( String( "Times" ) );
1766 aFont
.SetSize( Size( 0, 12 ) );
1768 GraphicsState aState
;
1769 aState
.m_aMapMode
= m_aMapMode
;
1770 aState
.m_aFont
= aFont
;
1771 m_aGraphicsStack
.push_front( aState
);
1773 oslFileError aError
= osl_openFile( m_aContext
.URL
.pData
, &m_aFile
, osl_File_OpenFlag_Write
| osl_File_OpenFlag_Create
);
1774 if( aError
!= osl_File_E_None
)
1776 if( aError
== osl_File_E_EXIST
)
1778 aError
= osl_openFile( m_aContext
.URL
.pData
, &m_aFile
, osl_File_OpenFlag_Write
);
1779 if( aError
== osl_File_E_None
)
1780 aError
= osl_setFileSize( m_aFile
, 0 );
1783 if( aError
!= osl_File_E_None
)
1791 /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1792 m_aCipher
= rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream
);
1793 m_aDigest
= rtl_digest_createMD5();
1795 /* the size of the Codec default maximum */
1796 checkEncryptionBufferSize( 0x4000 );
1799 prepareEncryption( xEnc
);
1801 if( m_aContext
.Encryption
.Encrypt() )
1804 if( m_aContext
.Encryption
.OValue
.size() != ENCRYPTED_PWD_SIZE
||
1805 m_aContext
.Encryption
.UValue
.size() != ENCRYPTED_PWD_SIZE
||
1806 m_aContext
.Encryption
.EncryptionKey
.size() != MAXIMUM_RC4_KEY_LENGTH
1809 // the field lengths are invalid ? This was not setup by initEncryption.
1810 // do not encrypt after all
1811 m_aContext
.Encryption
.OValue
.clear();
1812 m_aContext
.Encryption
.UValue
.clear();
1813 OSL_ENSURE( 0, "encryption data failed sanity check, encryption disabled" );
1815 else // setup key lengths
1816 m_nAccessPermissions
= computeAccessPermissions( m_aContext
.Encryption
, m_nKeyLength
, m_nRC4KeyLength
);
1820 OStringBuffer
aBuffer( 20 );
1821 aBuffer
.append( "%PDF-" );
1822 switch( m_aContext
.Version
)
1824 case PDFWriter::PDF_1_2
: aBuffer
.append( "1.2" );break;
1825 case PDFWriter::PDF_1_3
: aBuffer
.append( "1.3" );break;
1826 case PDFWriter::PDF_A_1
:
1828 case PDFWriter::PDF_1_4
: aBuffer
.append( "1.4" );break;
1829 case PDFWriter::PDF_1_5
: aBuffer
.append( "1.5" );break;
1831 // append something binary as comment (suggested in PDF Reference)
1832 aBuffer
.append( "\n%äüöß\n" );
1833 if( !writeBuffer( aBuffer
.getStr(), aBuffer
.getLength() ) )
1835 osl_closeFile( m_aFile
);
1840 // insert outline root
1841 m_aOutline
.push_back( PDFOutlineEntry() );
1843 m_bIsPDF_A1
= (m_aContext
.Version
== PDFWriter::PDF_A_1
);
1845 m_aContext
.Version
= PDFWriter::PDF_1_4
; //meaning we need PDF 1.4, PDF/A flavour
1848 m_bEmbedStandardFonts
= m_aContext
.EmbedStandardFonts
;
1852 PDFWriterImpl::~PDFWriterImpl()
1855 rtl_digest_destroyMD5( m_aDocDigest
);
1856 delete static_cast<VirtualDevice
*>(m_pReferenceDevice
);
1859 rtl_cipher_destroyARCFOUR( m_aCipher
);
1861 rtl_digest_destroyMD5( m_aDigest
);
1863 rtl_freeMemory( m_pEncryptionBuffer
);
1866 void PDFWriterImpl::setupDocInfo()
1868 std::vector
< sal_uInt8
> aId
;
1869 computeDocumentIdentifier( aId
, m_aContext
.DocumentInfo
, m_aCreationDateString
, m_aCreationMetaDateString
);
1870 if( m_aContext
.Encryption
.DocumentIdentifier
.empty() )
1871 m_aContext
.Encryption
.DocumentIdentifier
= aId
;
1874 void PDFWriterImpl::computeDocumentIdentifier( std::vector
< sal_uInt8
>& o_rIdentifier
,
1875 const vcl::PDFWriter::PDFDocInfo
& i_rDocInfo
,
1876 OString
& o_rCString1
,
1877 OString
& o_rCString2
1880 o_rIdentifier
.clear();
1882 //build the document id
1883 OString aInfoValuesOut
;
1884 OStringBuffer
aID( 1024 );
1885 if( i_rDocInfo
.Title
.Len() )
1886 appendUnicodeTextString( i_rDocInfo
.Title
, aID
);
1887 if( i_rDocInfo
.Author
.Len() )
1888 appendUnicodeTextString( i_rDocInfo
.Author
, aID
);
1889 if( i_rDocInfo
.Subject
.Len() )
1890 appendUnicodeTextString( i_rDocInfo
.Subject
, aID
);
1891 if( i_rDocInfo
.Keywords
.Len() )
1892 appendUnicodeTextString( i_rDocInfo
.Keywords
, aID
);
1893 if( i_rDocInfo
.Creator
.Len() )
1894 appendUnicodeTextString( i_rDocInfo
.Creator
, aID
);
1895 if( i_rDocInfo
.Producer
.Len() )
1896 appendUnicodeTextString( i_rDocInfo
.Producer
, aID
);
1898 TimeValue aTVal
, aGMT
;
1900 osl_getSystemTime( &aGMT
);
1901 osl_getLocalTimeFromSystemTime( &aGMT
, &aTVal
);
1902 osl_getDateTimeFromTimeValue( &aTVal
, &aDT
);
1903 OStringBuffer
aCreationDateString(64), aCreationMetaDateString(64);
1904 aCreationDateString
.append( "D:" );
1905 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Year
/1000)%10)) );
1906 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Year
/100)%10)) );
1907 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Year
/10)%10)) );
1908 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Year
)%10)) );
1909 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Month
/10)%10)) );
1910 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Month
)%10)) );
1911 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Day
/10)%10)) );
1912 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Day
)%10)) );
1913 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Hours
/10)%10)) );
1914 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Hours
)%10)) );
1915 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Minutes
/10)%10)) );
1916 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Minutes
)%10)) );
1917 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Seconds
/10)%10)) );
1918 aCreationDateString
.append( (sal_Char
)('0' + ((aDT
.Seconds
)%10)) );
1920 //--> i59651, we fill the Metadata date string as well, if PDF/A is requested
1921 // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1922 // local time zone offset UTC only, whereas Acrobat 8 seems
1923 // to use the localtime notation only
1924 // according to a raccomandation in XMP Specification (Jan 2004, page 75)
1925 // the Acrobat way seems the right approach
1926 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Year
/1000)%10)) );
1927 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Year
/100)%10)) );
1928 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Year
/10)%10)) );
1929 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Year
)%10)) );
1930 aCreationMetaDateString
.append( "-" );
1931 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Month
/10)%10)) );
1932 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Month
)%10)) );
1933 aCreationMetaDateString
.append( "-" );
1934 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Day
/10)%10)) );
1935 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Day
)%10)) );
1936 aCreationMetaDateString
.append( "T" );
1937 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Hours
/10)%10)) );
1938 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Hours
)%10)) );
1939 aCreationMetaDateString
.append( ":" );
1940 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Minutes
/10)%10)) );
1941 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Minutes
)%10)) );
1942 aCreationMetaDateString
.append( ":" );
1943 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Seconds
/10)%10)) );
1944 aCreationMetaDateString
.append( (sal_Char
)('0' + ((aDT
.Seconds
)%10)) );
1946 sal_uInt32 nDelta
= 0;
1947 if( aGMT
.Seconds
> aTVal
.Seconds
)
1949 aCreationDateString
.append( "-" );
1950 nDelta
= aGMT
.Seconds
-aTVal
.Seconds
;
1951 aCreationMetaDateString
.append( "-" );
1953 else if( aGMT
.Seconds
< aTVal
.Seconds
)
1955 aCreationDateString
.append( "+" );
1956 nDelta
= aTVal
.Seconds
-aGMT
.Seconds
;
1957 aCreationMetaDateString
.append( "+" );
1961 aCreationDateString
.append( "Z" );
1962 aCreationMetaDateString
.append( "Z" );
1967 aCreationDateString
.append( (sal_Char
)('0' + ((nDelta
/36000)%10)) );
1968 aCreationDateString
.append( (sal_Char
)('0' + ((nDelta
/3600)%10)) );
1969 aCreationDateString
.append( "'" );
1970 aCreationDateString
.append( (sal_Char
)('0' + ((nDelta
/600)%6)) );
1971 aCreationDateString
.append( (sal_Char
)('0' + ((nDelta
/60)%10)) );
1973 aCreationMetaDateString
.append( (sal_Char
)('0' + ((nDelta
/36000)%10)) );
1974 aCreationMetaDateString
.append( (sal_Char
)('0' + ((nDelta
/3600)%10)) );
1975 aCreationMetaDateString
.append( ":" );
1976 aCreationMetaDateString
.append( (sal_Char
)('0' + ((nDelta
/600)%6)) );
1977 aCreationMetaDateString
.append( (sal_Char
)('0' + ((nDelta
/60)%10)) );
1979 aCreationDateString
.append( "'" );
1980 aID
.append( aCreationDateString
.getStr(), aCreationDateString
.getLength() );
1982 aInfoValuesOut
= aID
.makeStringAndClear();
1983 o_rCString1
= aCreationDateString
.makeStringAndClear();
1984 o_rCString2
= aCreationMetaDateString
.makeStringAndClear();
1986 rtlDigest aDigest
= rtl_digest_createMD5();
1987 OSL_ENSURE( aDigest
!= NULL
, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" );
1990 rtlDigestError nError
= rtl_digest_updateMD5( aDigest
, &aGMT
, sizeof( aGMT
) );
1991 if( nError
== rtl_Digest_E_None
)
1992 nError
= rtl_digest_updateMD5( aDigest
, aInfoValuesOut
.getStr(), aInfoValuesOut
.getLength() );
1993 if( nError
== rtl_Digest_E_None
)
1995 o_rIdentifier
= std::vector
< sal_uInt8
>( 16, 0 );
1996 //the binary form of the doc id is needed for encryption stuff
1997 rtl_digest_getMD5( aDigest
, &o_rIdentifier
[0], 16 );
1999 rtl_digest_destroyMD5(aDigest
);
2003 /* i12626 methods */
2005 check if the Unicode string must be encrypted or not, perform the requested task,
2006 append the string as unicode hex, encrypted if needed
2008 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const OUString
& rInString
, const sal_Int32 nInObjectNumber
, OStringBuffer
& rOutBuffer
)
2010 rOutBuffer
.append( "<" );
2011 if( m_aContext
.Encryption
.Encrypt() )
2013 const sal_Unicode
* pStr
= rInString
.getStr();
2014 sal_Int32 nLen
= rInString
.getLength();
2015 //prepare a unicode string, encrypt it
2016 if( checkEncryptionBufferSize( nLen
*2 ) )
2018 enableStringEncryption( nInObjectNumber
);
2019 register sal_uInt8
*pCopy
= m_pEncryptionBuffer
;
2020 sal_Int32 nChars
= 2;
2023 // we need to prepare a byte stream from the unicode string buffer
2024 for( register int i
= 0; i
< nLen
; i
++ )
2026 register sal_Unicode aUnChar
= pStr
[i
];
2027 *pCopy
++ = (sal_uInt8
)( aUnChar
>> 8 );
2028 *pCopy
++ = (sal_uInt8
)( aUnChar
& 255 );
2032 rtl_cipher_encodeARCFOUR( m_aCipher
, m_pEncryptionBuffer
, nChars
, m_pEncryptionBuffer
, nChars
);
2033 //now append, hexadecimal (appendHex), the encrypted result
2034 for(register int i
= 0; i
< nChars
; i
++)
2035 appendHex( m_pEncryptionBuffer
[i
], rOutBuffer
);
2039 appendUnicodeTextString( rInString
, rOutBuffer
);
2040 rOutBuffer
.append( ">" );
2043 inline void PDFWriterImpl::appendLiteralStringEncrypt( OStringBuffer
& rInString
, const sal_Int32 nInObjectNumber
, OStringBuffer
& rOutBuffer
)
2045 rOutBuffer
.append( "(" );
2046 sal_Int32 nChars
= rInString
.getLength();
2047 //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
2048 if( m_aContext
.Encryption
.Encrypt() && checkEncryptionBufferSize( nChars
) )
2050 //encrypt the string in a buffer, then append it
2051 enableStringEncryption( nInObjectNumber
);
2052 rtl_cipher_encodeARCFOUR( m_aCipher
, rInString
.getStr(), nChars
, m_pEncryptionBuffer
, nChars
);
2053 appendLiteralString( (const sal_Char
*)m_pEncryptionBuffer
, nChars
, rOutBuffer
);
2056 appendLiteralString( rInString
.getStr(), nChars
, rOutBuffer
);
2057 rOutBuffer
.append( ")" );
2060 inline void PDFWriterImpl::appendLiteralStringEncrypt( const OString
& rInString
, const sal_Int32 nInObjectNumber
, OStringBuffer
& rOutBuffer
)
2062 OStringBuffer
aBufferString( rInString
);
2063 appendLiteralStringEncrypt( aBufferString
, nInObjectNumber
, rOutBuffer
);
2066 void PDFWriterImpl::appendLiteralStringEncrypt( const OUString
& rInString
, const sal_Int32 nInObjectNumber
, OStringBuffer
& rOutBuffer
, rtl_TextEncoding nEnc
)
2068 OString
aBufferString( OUStringToOString( rInString
, nEnc
) );
2069 sal_Int32 nLen
= aBufferString
.getLength();
2070 OStringBuffer
aBuf( nLen
);
2071 const sal_Char
* pT
= aBufferString
.getStr();
2073 for( sal_Int32 i
= 0; i
< nLen
; i
++, pT
++ )
2075 if( (*pT
& 0x80) == 0 )
2080 appendHex( *pT
, aBuf
);
2084 aBufferString
= aBuf
.makeStringAndClear();
2085 appendLiteralStringEncrypt( aBufferString
, nInObjectNumber
, rOutBuffer
);
2088 /* end i12626 methods */
2090 void PDFWriterImpl::emitComment( const char* pComment
)
2092 OStringBuffer
aLine( 64 );
2093 aLine
.append( "% " );
2094 aLine
.append( (const sal_Char
*)pComment
);
2095 aLine
.append( "\n" );
2096 writeBuffer( aLine
.getStr(), aLine
.getLength() );
2099 bool PDFWriterImpl::compressStream( SvMemoryStream
* pStream
)
2101 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2102 pStream
->Seek( STREAM_SEEK_TO_END
);
2103 sal_uLong nEndPos
= pStream
->Tell();
2104 pStream
->Seek( STREAM_SEEK_TO_BEGIN
);
2105 ZCodec
* pCodec
= new ZCodec( 0x4000, 0x4000 );
2106 SvMemoryStream aStream
;
2107 pCodec
->BeginCompression();
2108 pCodec
->Write( aStream
, (const sal_uInt8
*)pStream
->GetData(), nEndPos
);
2109 pCodec
->EndCompression();
2111 nEndPos
= aStream
.Tell();
2112 pStream
->Seek( STREAM_SEEK_TO_BEGIN
);
2113 aStream
.Seek( STREAM_SEEK_TO_BEGIN
);
2114 pStream
->SetStreamSize( nEndPos
);
2115 pStream
->Write( aStream
.GetData(), nEndPos
);
2123 void PDFWriterImpl::beginCompression()
2125 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2126 m_pCodec
= new ZCodec( 0x4000, 0x4000 );
2127 m_pMemStream
= new SvMemoryStream();
2128 m_pCodec
->BeginCompression();
2132 void PDFWriterImpl::endCompression()
2134 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2137 m_pCodec
->EndCompression();
2140 sal_uInt64 nLen
= m_pMemStream
->Tell();
2141 m_pMemStream
->Seek( 0 );
2142 writeBuffer( m_pMemStream
->GetData(), nLen
);
2143 delete m_pMemStream
;
2144 m_pMemStream
= NULL
;
2149 bool PDFWriterImpl::writeBuffer( const void* pBuffer
, sal_uInt64 nBytes
)
2151 if( ! m_bOpen
) // we are already down the drain
2154 if( ! nBytes
) // huh ?
2157 if( m_aOutputStreams
.begin() != m_aOutputStreams
.end() )
2159 m_aOutputStreams
.front().m_pStream
->Seek( STREAM_SEEK_TO_END
);
2160 m_aOutputStreams
.front().m_pStream
->Write( pBuffer
, sal::static_int_cast
<sal_Size
>(nBytes
) );
2164 sal_uInt64 nWritten
;
2167 m_pCodec
->Write( *m_pMemStream
, static_cast<const sal_uInt8
*>(pBuffer
), (sal_uLong
)nBytes
);
2172 sal_Bool buffOK
= sal_True
;
2173 if( m_bEncryptThisStream
)
2175 /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
2176 if( ( buffOK
= checkEncryptionBufferSize( static_cast<sal_Int32
>(nBytes
) ) ) != sal_False
)
2177 rtl_cipher_encodeARCFOUR( m_aCipher
,
2178 (sal_uInt8
*)pBuffer
, static_cast<sal_Size
>(nBytes
),
2179 m_pEncryptionBuffer
, static_cast<sal_Size
>(nBytes
) );
2182 const void* pWriteBuffer
= ( m_bEncryptThisStream
&& buffOK
) ? m_pEncryptionBuffer
: pBuffer
;
2184 rtl_digest_updateMD5( m_aDocDigest
, pWriteBuffer
, static_cast<sal_uInt32
>(nBytes
) );
2186 if( osl_writeFile( m_aFile
,
2188 nBytes
, &nWritten
) != osl_File_E_None
)
2191 if( nWritten
!= nBytes
)
2193 osl_closeFile( m_aFile
);
2198 return nWritten
== nBytes
;
2201 OutputDevice
* PDFWriterImpl::getReferenceDevice()
2203 if( ! m_pReferenceDevice
)
2205 VirtualDevice
* pVDev
= new VirtualDevice( 0 );
2207 m_pReferenceDevice
= pVDev
;
2209 if( m_aContext
.DPIx
== 0 || m_aContext
.DPIy
== 0 )
2210 pVDev
->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1
);
2212 pVDev
->SetReferenceDevice( m_aContext
.DPIx
, m_aContext
.DPIy
);
2214 pVDev
->SetOutputSizePixel( Size( 640, 480 ) );
2215 pVDev
->SetMapMode( MAP_MM
);
2217 m_pReferenceDevice
->mpPDFWriter
= this;
2218 m_pReferenceDevice
->ImplUpdateFontData( sal_True
);
2220 return m_pReferenceDevice
;
2223 class ImplPdfBuiltinFontData
: public PhysicalFontFace
2226 const PDFWriterImpl::BuiltinFont
& mrBuiltin
;
2229 enum {PDF_FONT_MAGIC
= 0xBDFF0A1C };
2230 ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont
& );
2231 const PDFWriterImpl::BuiltinFont
* GetBuiltinFont() const { return &mrBuiltin
; }
2233 virtual PhysicalFontFace
* Clone() const { return new ImplPdfBuiltinFontData(*this); }
2234 virtual ImplFontEntry
* CreateFontInstance( FontSelectPattern
& ) const;
2235 virtual sal_IntPtr
GetFontId() const { return reinterpret_cast<sal_IntPtr
>(&mrBuiltin
); }
2238 inline const ImplPdfBuiltinFontData
* GetPdfFontData( const PhysicalFontFace
* pFontData
)
2240 const ImplPdfBuiltinFontData
* pFD
= NULL
;
2241 if( pFontData
&& pFontData
->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC
) )
2242 pFD
= static_cast<const ImplPdfBuiltinFontData
*>( pFontData
);
2246 static ImplDevFontAttributes
GetDevFontAttributes( const PDFWriterImpl::BuiltinFont
& rBuiltin
)
2248 ImplDevFontAttributes aDFA
;
2249 aDFA
.SetFamilyName( OUString::createFromAscii( rBuiltin
.m_pName
) );
2250 aDFA
.SetStyleName( OUString::createFromAscii( rBuiltin
.m_pStyleName
) );
2251 aDFA
.SetFamilyType( rBuiltin
.m_eFamily
);
2252 aDFA
.SetSymbolFlag( rBuiltin
.m_eCharSet
!= RTL_TEXTENCODING_MS_1252
);
2253 aDFA
.SetPitch( rBuiltin
.m_ePitch
);
2254 aDFA
.SetWeight( rBuiltin
.m_eWeight
);
2255 aDFA
.SetItalic( rBuiltin
.m_eItalic
);
2256 aDFA
.SetWidthType( rBuiltin
.m_eWidthType
);
2258 aDFA
.mbOrientation
= true;
2259 aDFA
.mbDevice
= true;
2260 aDFA
.mnQuality
= 50000;
2261 aDFA
.mbSubsettable
= false;
2262 aDFA
.mbEmbeddable
= false;
2266 ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont
& rBuiltin
)
2267 : PhysicalFontFace( GetDevFontAttributes(rBuiltin
), PDF_FONT_MAGIC
),
2268 mrBuiltin( rBuiltin
)
2271 ImplFontEntry
* ImplPdfBuiltinFontData::CreateFontInstance( FontSelectPattern
& rFSD
) const
2273 ImplFontEntry
* pEntry
= new ImplFontEntry( rFSD
);
2277 ImplDevFontList
* PDFWriterImpl::filterDevFontList( ImplDevFontList
* pFontList
)
2279 DBG_ASSERT( m_aSubsets
.empty(), "Fonts changing during PDF generation, document will be invalid" );
2280 ImplDevFontList
* pFiltered
= pFontList
->Clone( true, true );
2282 // append the PDF builtin fonts
2283 if( !m_bIsPDF_A1
&& !m_bEmbedStandardFonts
)
2284 for( unsigned int i
= 0; i
< SAL_N_ELEMENTS(m_aBuiltinFonts
); i
++ )
2286 PhysicalFontFace
* pNewData
= new ImplPdfBuiltinFontData( m_aBuiltinFonts
[i
] );
2287 pFiltered
->Add( pNewData
);
2292 bool PDFWriterImpl::isBuiltinFont( const PhysicalFontFace
* pFont
) const
2294 const ImplPdfBuiltinFontData
* pFD
= GetPdfFontData( pFont
);
2295 return (pFD
!= NULL
);
2298 void PDFWriterImpl::getFontMetric( FontSelectPattern
* pSelect
, ImplFontMetricData
* pMetric
) const
2300 const ImplPdfBuiltinFontData
* pFD
= GetPdfFontData( pSelect
->mpFontData
);
2303 const BuiltinFont
* pBuiltinFont
= pFD
->GetBuiltinFont();
2305 pMetric
->mnOrientation
= sal::static_int_cast
<short>(pSelect
->mnOrientation
);
2306 pMetric
->SetFamilyType( pBuiltinFont
->m_eFamily
);
2307 pMetric
->SetPitch( pBuiltinFont
->m_ePitch
);
2308 pMetric
->SetWeight( pBuiltinFont
->m_eWeight
);
2309 pMetric
->SetItalic( pBuiltinFont
->m_eItalic
);
2310 pMetric
->SetSymbolFlag( pFD
->IsSymbolFont() );
2311 pMetric
->mnWidth
= pSelect
->mnHeight
;
2312 pMetric
->mnAscent
= ( pSelect
->mnHeight
* +pBuiltinFont
->m_nAscent
+ 500 ) / 1000;
2313 pMetric
->mnDescent
= ( pSelect
->mnHeight
* -pBuiltinFont
->m_nDescent
+ 500 ) / 1000;
2314 pMetric
->mnIntLeading
= 0;
2315 pMetric
->mnExtLeading
= 0;
2316 pMetric
->mnSlant
= 0;
2317 pMetric
->mbScalableFont
= true;
2318 pMetric
->mbDevice
= true;
2321 // -----------------------------------------------------------------------
2325 class PDFSalLayout
: public GenericSalLayout
2327 PDFWriterImpl
& mrPDFWriterImpl
;
2328 const PDFWriterImpl::BuiltinFont
& mrBuiltinFont
;
2329 bool mbIsSymbolFont
;
2334 PDFSalLayout( PDFWriterImpl
&,
2335 const PDFWriterImpl::BuiltinFont
&,
2336 long nPixelPerEM
, int nOrientation
);
2338 void SetText( const OUString
& rText
) { maOrigText
= rText
; }
2339 virtual bool LayoutText( ImplLayoutArgs
& );
2340 virtual void InitFont() const;
2341 virtual void DrawText( SalGraphics
& ) const;
2346 // -----------------------------------------------------------------------
2348 PDFSalLayout::PDFSalLayout( PDFWriterImpl
& rPDFWriterImpl
,
2349 const PDFWriterImpl::BuiltinFont
& rBuiltinFont
,
2350 long nPixelPerEM
, int nOrientation
)
2351 : mrPDFWriterImpl( rPDFWriterImpl
),
2352 mrBuiltinFont( rBuiltinFont
),
2353 mnPixelPerEM( nPixelPerEM
)
2355 mbIsSymbolFont
= (rBuiltinFont
.m_eCharSet
!= RTL_TEXTENCODING_MS_1252
);
2356 SetOrientation( nOrientation
);
2359 // -----------------------------------------------------------------------
2361 bool PDFSalLayout::LayoutText( ImplLayoutArgs
& rArgs
)
2363 const OUString
aText(rArgs
.mpStr
+rArgs
.mnMinCharPos
, rArgs
.mnEndCharPos
-rArgs
.mnMinCharPos
);
2365 SetUnitsPerPixel( 1000 );
2367 rtl_UnicodeToTextConverter aConv
= rtl_createTextToUnicodeConverter( mrBuiltinFont
.m_eCharSet
);
2369 Point
aNewPos( 0, 0 );
2371 Reserve(rArgs
.mnLength
);
2372 for( int nCharPos
= -1; rArgs
.GetNextPos( &nCharPos
, &bRightToLeft
); )
2374 // TODO: handle unicode surrogates
2375 // on the other hand the PDF builtin fonts don't support them anyway
2376 sal_Unicode cChar
= rArgs
.mpStr
[ nCharPos
];
2378 cChar
= static_cast<sal_Unicode
>(GetMirroredChar( cChar
));
2382 sal_Size nSrcCvtChars
;
2384 sal_Size nConv
= rtl_convertUnicodeToText( aConv
,
2387 aBuf
, sizeof(aBuf
)/sizeof(*aBuf
),
2388 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
,
2389 &nInfo
, &nSrcCvtChars
);
2390 // check whether conversion was possible
2391 // else fallback font is needed as the standard fonts
2392 // are handled via WinAnsi encoding
2394 cChar
= ((sal_Unicode
)aBuf
[0]) & 0x00ff;
2396 if( cChar
& 0xff00 )
2398 cChar
= 0; // NotDef glyph
2399 rArgs
.NeedFallback( nCharPos
, bRightToLeft
);
2402 long nGlyphWidth
= (long)mrBuiltinFont
.m_aWidths
[cChar
] * mnPixelPerEM
;
2403 long nGlyphFlags
= 0; // builtin fonts don't have diacritic glyphs
2405 nGlyphFlags
|= GlyphItem::IS_RTL_GLYPH
;
2406 // TODO: get kerning from builtin fonts
2407 GlyphItem
aGI( nCharPos
, cChar
, aNewPos
, nGlyphFlags
, nGlyphWidth
);
2410 aNewPos
.X() += nGlyphWidth
;
2413 rtl_destroyUnicodeToTextConverter( aConv
);
2418 // -----------------------------------------------------------------------
2420 void PDFSalLayout::InitFont() const
2422 // TODO: recreate font with all its attributes
2425 // -----------------------------------------------------------------------
2427 void PDFSalLayout::DrawText( SalGraphics
& ) const
2429 mrPDFWriterImpl
.drawLayout( *const_cast<PDFSalLayout
*>(this), maOrigText
, true );
2432 // -----------------------------------------------------------------------
2434 SalLayout
* PDFWriterImpl::GetTextLayout( ImplLayoutArgs
& rArgs
, FontSelectPattern
* pSelect
)
2436 DBG_ASSERT( (pSelect
->mpFontData
!= NULL
),
2437 "PDFWriterImpl::GetTextLayout mpFontData is NULL" );
2439 const ImplPdfBuiltinFontData
* pFD
= GetPdfFontData( pSelect
->mpFontData
);
2442 const BuiltinFont
* pBuiltinFont
= pFD
->GetBuiltinFont();
2444 long nPixelPerEM
= pSelect
->mnWidth
? pSelect
->mnWidth
: pSelect
->mnHeight
;
2445 int nOrientation
= pSelect
->mnOrientation
;
2446 PDFSalLayout
* pLayout
= new PDFSalLayout( *this, *pBuiltinFont
, nPixelPerEM
, nOrientation
);
2447 pLayout
->SetText( rArgs
.mpStr
);
2451 sal_Int32
PDFWriterImpl::newPage( sal_Int32 nPageWidth
, sal_Int32 nPageHeight
, PDFWriter::Orientation eOrientation
)
2454 m_nCurrentPage
= m_aPages
.size();
2455 m_aPages
.push_back( PDFPage(this, nPageWidth
, nPageHeight
, eOrientation
) );
2456 m_aPages
.back().m_nPageIndex
= m_nCurrentPage
;
2457 m_aPages
.back().beginStream();
2459 // setup global graphics state
2460 // linewidth is "1 pixel" by default
2461 OStringBuffer
aBuf( 16 );
2462 appendDouble( 72.0/double(getReferenceDevice()->ImplGetDPIX()), aBuf
);
2463 aBuf
.append( " w\n" );
2464 writeBuffer( aBuf
.getStr(), aBuf
.getLength() );
2466 return m_nCurrentPage
;
2469 void PDFWriterImpl::endPage()
2471 if( m_aPages
.begin() != m_aPages
.end() )
2473 // close eventual MC sequence
2474 endStructureElementMCSeq();
2477 if( m_aOutputStreams
.begin() != m_aOutputStreams
.end() )
2479 OSL_FAIL( "redirection across pages !!!" );
2480 m_aOutputStreams
.clear(); // leak !
2481 m_aMapMode
.SetOrigin( Point() );
2484 m_aGraphicsStack
.clear();
2485 m_aGraphicsStack
.push_back( GraphicsState() );
2487 // this should pop the PDF graphics stack if necessary
2488 updateGraphicsState();
2490 m_aPages
.back().endStream();
2492 // reset the default font
2494 aFont
.SetName( String( "Times" ) );
2495 aFont
.SetSize( Size( 0, 12 ) );
2497 m_aCurrentPDFState
= m_aGraphicsStack
.front();
2498 m_aGraphicsStack
.front().m_aFont
= aFont
;
2500 for( std::list
<BitmapEmit
>::iterator it
= m_aBitmaps
.begin();
2501 it
!= m_aBitmaps
.end(); ++it
)
2503 if( ! it
->m_aBitmap
.IsEmpty() )
2505 writeBitmapObject( *it
);
2506 it
->m_aBitmap
= BitmapEx();
2509 for( std::list
<JPGEmit
>::iterator jpeg
= m_aJPGs
.begin(); jpeg
!= m_aJPGs
.end(); ++jpeg
)
2511 if( jpeg
->m_pStream
)
2514 delete jpeg
->m_pStream
;
2515 jpeg
->m_pStream
= NULL
;
2516 jpeg
->m_aMask
= Bitmap();
2519 for( std::list
<TransparencyEmit
>::iterator t
= m_aTransparentObjects
.begin();
2520 t
!= m_aTransparentObjects
.end(); ++t
)
2522 if( t
->m_pContentStream
)
2524 writeTransparentObject( *t
);
2525 delete t
->m_pContentStream
;
2526 t
->m_pContentStream
= NULL
;
2532 sal_Int32
PDFWriterImpl::createObject()
2534 m_aObjects
.push_back( ~0U );
2535 return m_aObjects
.size();
2538 bool PDFWriterImpl::updateObject( sal_Int32 n
)
2543 sal_uInt64 nOffset
= ~0U;
2544 oslFileError aError
= osl_getFilePos( m_aFile
, &nOffset
);
2545 DBG_ASSERT( aError
== osl_File_E_None
, "could not register object" );
2546 if( aError
!= osl_File_E_None
)
2548 osl_closeFile( m_aFile
);
2551 m_aObjects
[ n
-1 ] = nOffset
;
2552 return aError
== osl_File_E_None
;
2555 #define CHECK_RETURN( x ) if( !(x) ) return 0
2557 sal_Int32
PDFWriterImpl::emitStructParentTree( sal_Int32 nObject
)
2561 OStringBuffer
aLine( 1024 );
2563 aLine
.append( nObject
);
2564 aLine
.append( " 0 obj\n"
2566 sal_Int32 nTreeItems
= m_aStructParentTree
.size();
2567 for( sal_Int32 n
= 0; n
< nTreeItems
; n
++ )
2570 aLine
.append( ' ' );
2571 aLine
.append( m_aStructParentTree
[n
] );
2572 aLine
.append( "\n" );
2574 aLine
.append( "]>>\nendobj\n\n" );
2575 CHECK_RETURN( updateObject( nObject
) );
2576 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
2581 const sal_Char
* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr
)
2583 static std::map
< PDFWriter::StructAttribute
, const char* > aAttributeStrings
;
2585 if( aAttributeStrings
.empty() )
2587 aAttributeStrings
[ PDFWriter::Placement
] = "Placement";
2588 aAttributeStrings
[ PDFWriter::WritingMode
] = "WritingMode";
2589 aAttributeStrings
[ PDFWriter::SpaceBefore
] = "SpaceBefore";
2590 aAttributeStrings
[ PDFWriter::SpaceAfter
] = "SpaceAfter";
2591 aAttributeStrings
[ PDFWriter::StartIndent
] = "StartIndent";
2592 aAttributeStrings
[ PDFWriter::EndIndent
] = "EndIndent";
2593 aAttributeStrings
[ PDFWriter::TextIndent
] = "TextIndent";
2594 aAttributeStrings
[ PDFWriter::TextAlign
] = "TextAlign";
2595 aAttributeStrings
[ PDFWriter::Width
] = "Width";
2596 aAttributeStrings
[ PDFWriter::Height
] = "Height";
2597 aAttributeStrings
[ PDFWriter::BlockAlign
] = "BlockAlign";
2598 aAttributeStrings
[ PDFWriter::InlineAlign
] = "InlineAlign";
2599 aAttributeStrings
[ PDFWriter::LineHeight
] = "LineHeight";
2600 aAttributeStrings
[ PDFWriter::BaselineShift
] = "BaselineShift";
2601 aAttributeStrings
[ PDFWriter::TextDecorationType
] = "TextDecorationType";
2602 aAttributeStrings
[ PDFWriter::ListNumbering
] = "ListNumbering";
2603 aAttributeStrings
[ PDFWriter::RowSpan
] = "RowSpan";
2604 aAttributeStrings
[ PDFWriter::ColSpan
] = "ColSpan";
2605 aAttributeStrings
[ PDFWriter::LinkAnnotation
] = "LinkAnnotation";
2608 std::map
< PDFWriter::StructAttribute
, const char* >::const_iterator it
=
2609 aAttributeStrings
.find( eAttr
);
2611 #if OSL_DEBUG_LEVEL > 1
2612 if( it
== aAttributeStrings
.end() )
2613 fprintf( stderr
, "invalid PDFWriter::StructAttribute %d\n", eAttr
);
2616 return it
!= aAttributeStrings
.end() ? it
->second
: "";
2619 const sal_Char
* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal
)
2621 static std::map
< PDFWriter::StructAttributeValue
, const char* > aValueStrings
;
2623 if( aValueStrings
.empty() )
2625 aValueStrings
[ PDFWriter::NONE
] = "None";
2626 aValueStrings
[ PDFWriter::Block
] = "Block";
2627 aValueStrings
[ PDFWriter::Inline
] = "Inline";
2628 aValueStrings
[ PDFWriter::Before
] = "Before";
2629 aValueStrings
[ PDFWriter::After
] = "After";
2630 aValueStrings
[ PDFWriter::Start
] = "Start";
2631 aValueStrings
[ PDFWriter::End
] = "End";
2632 aValueStrings
[ PDFWriter::LrTb
] = "LrTb";
2633 aValueStrings
[ PDFWriter::RlTb
] = "RlTb";
2634 aValueStrings
[ PDFWriter::TbRl
] = "TbRl";
2635 aValueStrings
[ PDFWriter::Center
] = "Center";
2636 aValueStrings
[ PDFWriter::Justify
] = "Justify";
2637 aValueStrings
[ PDFWriter::Auto
] = "Auto";
2638 aValueStrings
[ PDFWriter::Middle
] = "Middle";
2639 aValueStrings
[ PDFWriter::Normal
] = "Normal";
2640 aValueStrings
[ PDFWriter::Underline
] = "Underline";
2641 aValueStrings
[ PDFWriter::Overline
] = "Overline";
2642 aValueStrings
[ PDFWriter::LineThrough
] = "LineThrough";
2643 aValueStrings
[ PDFWriter::Disc
] = "Disc";
2644 aValueStrings
[ PDFWriter::Circle
] = "Circle";
2645 aValueStrings
[ PDFWriter::Square
] = "Square";
2646 aValueStrings
[ PDFWriter::Decimal
] = "Decimal";
2647 aValueStrings
[ PDFWriter::UpperRoman
] = "UpperRoman";
2648 aValueStrings
[ PDFWriter::LowerRoman
] = "LowerRoman";
2649 aValueStrings
[ PDFWriter::UpperAlpha
] = "UpperAlpha";
2650 aValueStrings
[ PDFWriter::LowerAlpha
] = "LowerAlpha";
2653 std::map
< PDFWriter::StructAttributeValue
, const char* >::const_iterator it
=
2654 aValueStrings
.find( eVal
);
2656 #if OSL_DEBUG_LEVEL > 1
2657 if( it
== aValueStrings
.end() )
2658 fprintf( stderr
, "invalid PDFWriter::StructAttributeValue %d\n", eVal
);
2661 return it
!= aValueStrings
.end() ? it
->second
: "";
2664 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr
, const PDFWriterImpl::PDFStructureAttribute
& i_rVal
, OStringBuffer
& o_rLine
, bool i_bIsFixedInt
)
2666 o_rLine
.append( "/" );
2667 o_rLine
.append( PDFWriterImpl::getAttributeTag( i_eAttr
) );
2669 if( i_rVal
.eValue
!= PDFWriter::Invalid
)
2671 o_rLine
.append( "/" );
2672 o_rLine
.append( PDFWriterImpl::getAttributeValueTag( i_rVal
.eValue
) );
2677 o_rLine
.append( " " );
2679 appendFixedInt( i_rVal
.nValue
, o_rLine
);
2681 o_rLine
.append( i_rVal
.nValue
);
2683 o_rLine
.append( "\n" );
2686 OString
PDFWriterImpl::emitStructureAttributes( PDFStructureElement
& i_rEle
)
2688 // create layout, list and table attribute sets
2689 OStringBuffer
aLayout(256), aList(64), aTable(64);
2690 for( PDFStructAttributes::const_iterator it
= i_rEle
.m_aAttributes
.begin();
2691 it
!= i_rEle
.m_aAttributes
.end(); ++it
)
2693 if( it
->first
== PDFWriter::ListNumbering
)
2694 appendStructureAttributeLine( it
->first
, it
->second
, aList
, true );
2695 else if( it
->first
== PDFWriter::RowSpan
||
2696 it
->first
== PDFWriter::ColSpan
)
2697 appendStructureAttributeLine( it
->first
, it
->second
, aTable
, false );
2698 else if( it
->first
== PDFWriter::LinkAnnotation
)
2700 sal_Int32 nLink
= it
->second
.nValue
;
2701 std::map
< sal_Int32
, sal_Int32
>::const_iterator link_it
=
2702 m_aLinkPropertyMap
.find( nLink
);
2703 if( link_it
!= m_aLinkPropertyMap
.end() )
2704 nLink
= link_it
->second
;
2705 if( nLink
>= 0 && nLink
< (sal_Int32
)m_aLinks
.size() )
2707 // update struct parent of link
2708 OStringBuffer
aStructParentEntry( 32 );
2709 aStructParentEntry
.append( i_rEle
.m_nObject
);
2710 aStructParentEntry
.append( " 0 R" );
2711 m_aStructParentTree
.push_back( aStructParentEntry
.makeStringAndClear() );
2712 m_aLinks
[ nLink
].m_nStructParent
= m_aStructParentTree
.size()-1;
2714 sal_Int32 nRefObject
= createObject();
2715 OStringBuffer
aRef( 256 );
2716 aRef
.append( nRefObject
);
2717 aRef
.append( " 0 obj\n"
2718 "<</Type/OBJR/Obj " );
2719 aRef
.append( m_aLinks
[ nLink
].m_nObject
);
2720 aRef
.append( " 0 R>>\n"
2723 updateObject( nRefObject
);
2724 writeBuffer( aRef
.getStr(), aRef
.getLength() );
2726 i_rEle
.m_aKids
.push_back( PDFStructureElementKid( nRefObject
) );
2730 OSL_FAIL( "unresolved link id for Link structure" );
2731 #if OSL_DEBUG_LEVEL > 1
2732 fprintf( stderr
, "unresolved link id %" SAL_PRIdINT32
" for Link structure\n", nLink
);
2734 OStringBuffer
aLine( "unresolved link id " );
2735 aLine
.append( nLink
);
2736 aLine
.append( " for Link structure" );
2737 emitComment( aLine
.getStr() );
2743 appendStructureAttributeLine( it
->first
, it
->second
, aLayout
, true );
2745 if( ! i_rEle
.m_aBBox
.IsEmpty() )
2747 aLayout
.append( "/BBox[" );
2748 appendFixedInt( i_rEle
.m_aBBox
.Left(), aLayout
);
2749 aLayout
.append( " " );
2750 appendFixedInt( i_rEle
.m_aBBox
.Top(), aLayout
);
2751 aLayout
.append( " " );
2752 appendFixedInt( i_rEle
.m_aBBox
.Right(), aLayout
);
2753 aLayout
.append( " " );
2754 appendFixedInt( i_rEle
.m_aBBox
.Bottom(), aLayout
);
2755 aLayout
.append( "]\n" );
2758 std::vector
< sal_Int32
> aAttribObjects
;
2759 if( aLayout
.getLength() )
2761 aAttribObjects
.push_back( createObject() );
2762 updateObject( aAttribObjects
.back() );
2763 OStringBuffer
aObj( 64 );
2764 aObj
.append( aAttribObjects
.back() );
2765 aObj
.append( " 0 obj\n"
2767 aLayout
.append( ">>\nendobj\n\n" );
2768 writeBuffer( aObj
.getStr(), aObj
.getLength() );
2769 writeBuffer( aLayout
.getStr(), aLayout
.getLength() );
2771 if( aList
.getLength() )
2773 aAttribObjects
.push_back( createObject() );
2774 updateObject( aAttribObjects
.back() );
2775 OStringBuffer
aObj( 64 );
2776 aObj
.append( aAttribObjects
.back() );
2777 aObj
.append( " 0 obj\n"
2779 aList
.append( ">>\nendobj\n\n" );
2780 writeBuffer( aObj
.getStr(), aObj
.getLength() );
2781 writeBuffer( aList
.getStr(), aList
.getLength() );
2783 if( aTable
.getLength() )
2785 aAttribObjects
.push_back( createObject() );
2786 updateObject( aAttribObjects
.back() );
2787 OStringBuffer
aObj( 64 );
2788 aObj
.append( aAttribObjects
.back() );
2789 aObj
.append( " 0 obj\n"
2791 aTable
.append( ">>\nendobj\n\n" );
2792 writeBuffer( aObj
.getStr(), aObj
.getLength() );
2793 writeBuffer( aTable
.getStr(), aTable
.getLength() );
2796 OStringBuffer
aRet( 64 );
2797 if( aAttribObjects
.size() > 1 )
2798 aRet
.append( " [" );
2799 for( std::vector
< sal_Int32
>::const_iterator at_it
= aAttribObjects
.begin();
2800 at_it
!= aAttribObjects
.end(); ++at_it
)
2803 aRet
.append( *at_it
);
2804 aRet
.append( " 0 R" );
2806 if( aAttribObjects
.size() > 1 )
2807 aRet
.append( " ]" );
2808 return aRet
.makeStringAndClear();
2811 sal_Int32
PDFWriterImpl::emitStructure( PDFStructureElement
& rEle
)
2814 // do not emit NonStruct and its children
2815 rEle
.m_eType
== PDFWriter::NonStructElement
&&
2816 rEle
.m_nOwnElement
!= rEle
.m_nParentElement
// but of course emit the struct tree root
2820 for( std::list
< sal_Int32
>::const_iterator it
= rEle
.m_aChildren
.begin(); it
!= rEle
.m_aChildren
.end(); ++it
)
2822 if( *it
> 0 && *it
< sal_Int32(m_aStructure
.size()) )
2824 PDFStructureElement
& rChild
= m_aStructure
[ *it
];
2825 if( rChild
.m_eType
!= PDFWriter::NonStructElement
)
2827 if( rChild
.m_nParentElement
== rEle
.m_nOwnElement
)
2828 emitStructure( rChild
);
2831 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure element" );
2832 #if OSL_DEBUG_LEVEL > 1
2833 fprintf( stderr
, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32
"\n", *it
);
2840 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
2841 #if OSL_DEBUG_LEVEL > 1
2842 fprintf( stderr
, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32
"\n", *it
);
2847 OStringBuffer
aLine( 512 );
2848 aLine
.append( rEle
.m_nObject
);
2849 aLine
.append( " 0 obj\n"
2851 sal_Int32 nParentTree
= -1;
2852 if( rEle
.m_nOwnElement
== rEle
.m_nParentElement
)
2854 nParentTree
= createObject();
2855 CHECK_RETURN( nParentTree
);
2856 aLine
.append( "/StructTreeRoot\n" );
2857 aLine
.append( "/ParentTree " );
2858 aLine
.append( nParentTree
);
2859 aLine
.append( " 0 R\n" );
2860 if( ! m_aRoleMap
.empty() )
2862 aLine
.append( "/RoleMap<<" );
2863 for( boost::unordered_map
<OString
,OString
,OStringHash
>::const_iterator
2864 it
= m_aRoleMap
.begin(); it
!= m_aRoleMap
.end(); ++it
)
2866 aLine
.append( '/' );
2867 aLine
.append(it
->first
);
2868 aLine
.append( '/' );
2869 aLine
.append( it
->second
);
2870 aLine
.append( '\n' );
2872 aLine
.append( ">>\n" );
2877 aLine
.append( "/StructElem\n"
2879 if( !rEle
.m_aAlias
.isEmpty() )
2880 aLine
.append( rEle
.m_aAlias
);
2882 aLine
.append( getStructureTag( rEle
.m_eType
) );
2885 aLine
.append( m_aStructure
[ rEle
.m_nParentElement
].m_nObject
);
2886 aLine
.append( " 0 R\n"
2888 aLine
.append( rEle
.m_nFirstPageObject
);
2889 aLine
.append( " 0 R\n" );
2890 if( !rEle
.m_aActualText
.isEmpty() )
2892 aLine
.append( "/ActualText" );
2893 appendUnicodeTextStringEncrypt( rEle
.m_aActualText
, rEle
.m_nObject
, aLine
);
2894 aLine
.append( "\n" );
2896 if( !rEle
.m_aAltText
.isEmpty() )
2898 aLine
.append( "/Alt" );
2899 appendUnicodeTextStringEncrypt( rEle
.m_aAltText
, rEle
.m_nObject
, aLine
);
2900 aLine
.append( "\n" );
2903 if( ! rEle
.m_aBBox
.IsEmpty() || rEle
.m_aAttributes
.size() )
2905 OString aAttribs
= emitStructureAttributes( rEle
);
2906 if( !aAttribs
.isEmpty() )
2908 aLine
.append( "/A" );
2909 aLine
.append( aAttribs
);
2910 aLine
.append( "\n" );
2913 if( !rEle
.m_aLocale
.Language
.isEmpty() )
2915 OUStringBuffer
aLocBuf( 16 );
2916 aLocBuf
.append( rEle
.m_aLocale
.Language
.toAsciiLowerCase() );
2917 if( !rEle
.m_aLocale
.Country
.isEmpty() )
2919 aLocBuf
.append( sal_Unicode('-') );
2920 aLocBuf
.append( rEle
.m_aLocale
.Country
);
2922 aLine
.append( "/Lang" );
2923 appendLiteralStringEncrypt( aLocBuf
.makeStringAndClear(), rEle
.m_nObject
, aLine
);
2924 aLine
.append( "\n" );
2926 if( ! rEle
.m_aKids
.empty() )
2929 aLine
.append( "/K[" );
2930 for( std::list
< PDFStructureElementKid
>::const_iterator it
=
2931 rEle
.m_aKids
.begin(); it
!= rEle
.m_aKids
.end(); ++it
, i
++ )
2933 if( it
->nMCID
== -1 )
2935 aLine
.append( it
->nObject
);
2936 aLine
.append( " 0 R" );
2937 aLine
.append( ( (i
& 15) == 15 ) ? "\n" : " " );
2941 if( it
->nObject
== rEle
.m_nFirstPageObject
)
2943 aLine
.append( it
->nMCID
);
2944 aLine
.append( " " );
2948 aLine
.append( "<</Type/MCR/Pg " );
2949 aLine
.append( it
->nObject
);
2950 aLine
.append( " 0 R /MCID " );
2951 aLine
.append( it
->nMCID
);
2952 aLine
.append( ">>\n" );
2956 aLine
.append( "]\n" );
2958 aLine
.append( ">>\nendobj\n\n" );
2960 CHECK_RETURN( updateObject( rEle
.m_nObject
) );
2961 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
2963 CHECK_RETURN( emitStructParentTree( nParentTree
) );
2965 return rEle
.m_nObject
;
2968 bool PDFWriterImpl::emitGradients()
2970 for( std::list
<GradientEmit
>::iterator it
= m_aGradients
.begin();
2971 it
!= m_aGradients
.end(); ++it
)
2973 CHECK_RETURN( writeGradientFunction( *it
) );
2978 bool PDFWriterImpl::emitTilings()
2980 OStringBuffer
aTilingObj( 1024 );
2982 for( std::vector
<TilingEmit
>::iterator it
= m_aTilings
.begin(); it
!= m_aTilings
.end(); ++it
)
2984 DBG_ASSERT( it
->m_pTilingStream
, "tiling without stream" );
2985 if( ! it
->m_pTilingStream
)
2988 aTilingObj
.setLength( 0 );
2990 #if OSL_DEBUG_LEVEL > 1
2991 emitComment( "PDFWriterImpl::emitTilings" );
2994 sal_Int32 nX
= (sal_Int32
)it
->m_aRectangle
.Left();
2995 sal_Int32 nY
= (sal_Int32
)it
->m_aRectangle
.Top();
2996 sal_Int32 nW
= (sal_Int32
)it
->m_aRectangle
.GetWidth();
2997 sal_Int32 nH
= (sal_Int32
)it
->m_aRectangle
.GetHeight();
2998 if( it
->m_aCellSize
.Width() == 0 )
2999 it
->m_aCellSize
.Width() = nW
;
3000 if( it
->m_aCellSize
.Height() == 0 )
3001 it
->m_aCellSize
.Height() = nH
;
3003 bool bDeflate
= compressStream( it
->m_pTilingStream
);
3004 it
->m_pTilingStream
->Seek( STREAM_SEEK_TO_END
);
3005 sal_Size nTilingStreamSize
= it
->m_pTilingStream
->Tell();
3006 it
->m_pTilingStream
->Seek( STREAM_SEEK_TO_BEGIN
);
3008 // write pattern object
3009 aTilingObj
.append( it
->m_nObject
);
3010 aTilingObj
.append( " 0 obj\n" );
3011 aTilingObj
.append( "<</Type/Pattern/PatternType 1\n"
3015 appendFixedInt( nX
, aTilingObj
);
3016 aTilingObj
.append( ' ' );
3017 appendFixedInt( nY
, aTilingObj
);
3018 aTilingObj
.append( ' ' );
3019 appendFixedInt( nX
+nW
, aTilingObj
);
3020 aTilingObj
.append( ' ' );
3021 appendFixedInt( nY
+nH
, aTilingObj
);
3022 aTilingObj
.append( "]\n"
3024 appendFixedInt( it
->m_aCellSize
.Width(), aTilingObj
);
3025 aTilingObj
.append( "\n"
3027 appendFixedInt( it
->m_aCellSize
.Height(), aTilingObj
);
3028 aTilingObj
.append( "\n" );
3029 if( it
->m_aTransform
.matrix
[0] != 1.0 ||
3030 it
->m_aTransform
.matrix
[1] != 0.0 ||
3031 it
->m_aTransform
.matrix
[3] != 0.0 ||
3032 it
->m_aTransform
.matrix
[4] != 1.0 ||
3033 it
->m_aTransform
.matrix
[2] != 0.0 ||
3034 it
->m_aTransform
.matrix
[5] != 0.0 )
3036 aTilingObj
.append( "/Matrix [" );
3037 // TODO: scaling, mirroring on y, etc
3038 appendDouble( it
->m_aTransform
.matrix
[0], aTilingObj
);
3039 aTilingObj
.append( ' ' );
3040 appendDouble( it
->m_aTransform
.matrix
[1], aTilingObj
);
3041 aTilingObj
.append( ' ' );
3042 appendDouble( it
->m_aTransform
.matrix
[3], aTilingObj
);
3043 aTilingObj
.append( ' ' );
3044 appendDouble( it
->m_aTransform
.matrix
[4], aTilingObj
);
3045 aTilingObj
.append( ' ' );
3046 appendDouble( it
->m_aTransform
.matrix
[2], aTilingObj
);
3047 aTilingObj
.append( ' ' );
3048 appendDouble( it
->m_aTransform
.matrix
[5], aTilingObj
);
3049 aTilingObj
.append( "]\n" );
3051 aTilingObj
.append( "/Resources" );
3052 it
->m_aResources
.append( aTilingObj
, getFontDictObject() );
3054 aTilingObj
.append( "/Filter/FlateDecode" );
3055 aTilingObj
.append( "/Length " );
3056 aTilingObj
.append( (sal_Int32
)nTilingStreamSize
);
3057 aTilingObj
.append( ">>\nstream\n" );
3058 CHECK_RETURN( updateObject( it
->m_nObject
) );
3059 CHECK_RETURN( writeBuffer( aTilingObj
.getStr(), aTilingObj
.getLength() ) );
3060 checkAndEnableStreamEncryption( it
->m_nObject
);
3061 nTilingStreamSize
= writeBuffer( it
->m_pTilingStream
->GetData(), nTilingStreamSize
);
3062 delete it
->m_pTilingStream
;
3063 it
->m_pTilingStream
= NULL
;
3064 if( nTilingStreamSize
== 0 )
3066 disableStreamEncryption();
3067 aTilingObj
.setLength( 0 );
3068 aTilingObj
.append( "\nendstream\nendobj\n\n" );
3069 CHECK_RETURN( writeBuffer( aTilingObj
.getStr(), aTilingObj
.getLength() ) );
3074 sal_Int32
PDFWriterImpl::emitBuiltinFont( const PhysicalFontFace
* pFont
, sal_Int32 nFontObject
)
3076 const ImplPdfBuiltinFontData
* pFD
= GetPdfFontData( pFont
);
3079 const BuiltinFont
* pBuiltinFont
= pFD
->GetBuiltinFont();
3081 OStringBuffer
aLine( 1024 );
3083 if( nFontObject
<= 0 )
3084 nFontObject
= createObject();
3085 CHECK_RETURN( updateObject( nFontObject
) );
3086 aLine
.append( nFontObject
);
3087 aLine
.append( " 0 obj\n"
3088 "<</Type/Font/Subtype/Type1/BaseFont/" );
3089 appendName( pBuiltinFont
->m_pPSName
, aLine
);
3090 aLine
.append( "\n" );
3091 if( pBuiltinFont
->m_eCharSet
== RTL_TEXTENCODING_MS_1252
)
3092 aLine
.append( "/Encoding/WinAnsiEncoding\n" );
3093 aLine
.append( ">>\nendobj\n\n" );
3094 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
3098 std::map
< sal_Int32
, sal_Int32
> PDFWriterImpl::emitSystemFont( const PhysicalFontFace
* pFont
, EmbedFont
& rEmbed
)
3100 std::map
< sal_Int32
, sal_Int32
> aRet
;
3101 if( isBuiltinFont( pFont
) )
3103 aRet
[ rEmbed
.m_nNormalFontID
] = emitBuiltinFont( pFont
);
3107 sal_Int32 nFontDescriptor
= 0;
3108 OString
aSubType( "/Type1" );
3109 FontSubsetInfo aInfo
;
3110 // fill in dummy values
3111 aInfo
.m_nAscent
= 1000;
3112 aInfo
.m_nDescent
= 200;
3113 aInfo
.m_nCapHeight
= 1000;
3114 aInfo
.m_aFontBBox
= Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) );
3115 aInfo
.m_aPSName
= pFont
->GetFamilyName();
3116 sal_Int32 pWidths
[256];
3117 memset( pWidths
, 0, sizeof(pWidths
) );
3118 if( pFont
->IsEmbeddable() )
3120 const unsigned char* pFontData
= NULL
;
3122 sal_Ucs nEncodedCodes
[256];
3123 sal_Int32 pEncWidths
[256];
3124 if( (pFontData
= (const unsigned char*)m_pReferenceDevice
->mpGraphics
->GetEmbedFontData( pFont
, nEncodedCodes
, pEncWidths
, aInfo
, &nFontLen
)) != NULL
)
3126 m_pReferenceDevice
->mpGraphics
->FreeEmbedFontData( pFontData
, nFontLen
);
3127 for( int i
= 0; i
< 256; i
++ )
3129 if( nEncodedCodes
[i
] >= 32 && nEncodedCodes
[i
] < 256 )
3131 pWidths
[i
] = pEncWidths
[ i
];
3136 else if( pFont
->mbSubsettable
)
3138 aSubType
= OString( "/TrueType" );
3139 Int32Vector aGlyphWidths
;
3140 Ucs2UIntMap aUnicodeMap
;
3141 m_pReferenceDevice
->mpGraphics
->GetGlyphWidths( pFont
, false, aGlyphWidths
, aUnicodeMap
);
3144 osl_createTempFile( NULL
, NULL
, &aTmpName
.pData
);
3145 sal_Int32 pGlyphIDs
[ 256 ];
3146 sal_uInt8 pEncoding
[ 256 ];
3147 sal_Int32 pDuWidths
[ 256 ];
3149 memset( pGlyphIDs
, 0, sizeof( pGlyphIDs
) );
3150 memset( pEncoding
, 0, sizeof( pEncoding
) );
3151 memset( pDuWidths
, 0, sizeof( pDuWidths
) );
3153 for( sal_Ucs c
= 32; c
< 256; c
++ )
3157 if( aUnicodeMap
.find( c
) != aUnicodeMap
.end() )
3158 pWidths
[ c
] = aGlyphWidths
[ aUnicodeMap
[ c
] ];
3161 m_pReferenceDevice
->mpGraphics
->CreateFontSubset( aTmpName
, pFont
, pGlyphIDs
, pEncoding
, pDuWidths
, 256, aInfo
);
3162 osl_removeFile( aTmpName
.pData
);
3166 OSL_FAIL( "system font neither embeddable nor subsettable" );
3169 // write font descriptor
3170 nFontDescriptor
= emitFontDescriptor( pFont
, aInfo
, 0, 0 );
3171 if( nFontDescriptor
)
3173 // write font object
3174 sal_Int32 nObject
= createObject();
3175 if( updateObject( nObject
) )
3177 OStringBuffer
aLine( 1024 );
3178 aLine
.append( nObject
);
3179 aLine
.append( " 0 obj\n"
3180 "<</Type/Font/Subtype" );
3181 aLine
.append( aSubType
);
3182 aLine
.append( "/BaseFont/" );
3183 appendName( aInfo
.m_aPSName
, aLine
);
3184 aLine
.append( "\n" );
3185 if( !pFont
->IsSymbolFont() )
3186 aLine
.append( "/Encoding/WinAnsiEncoding\n" );
3187 aLine
.append( "/FirstChar 32 /LastChar 255\n"
3189 for( int i
= 32; i
< 256; i
++ )
3191 aLine
.append( pWidths
[i
] );
3192 aLine
.append( ((i
&15) == 15) ? "\n" : " " );
3195 "/FontDescriptor " );
3196 aLine
.append( nFontDescriptor
);
3197 aLine
.append( " 0 R>>\n"
3199 writeBuffer( aLine
.getStr(), aLine
.getLength() );
3201 aRet
[ rEmbed
.m_nNormalFontID
] = nObject
;
3208 typedef int ThreeInts
[3];
3209 static bool getPfbSegmentLengths( const unsigned char* pFontBytes
, int nByteLen
,
3210 ThreeInts
& rSegmentLengths
)
3212 if( !pFontBytes
|| (nByteLen
< 0) )
3214 const unsigned char* pPtr
= pFontBytes
;
3215 const unsigned char* pEnd
= pFontBytes
+ nByteLen
;
3217 for( int i
= 0; i
< 3; ++i
) {
3218 // read segment1 header
3219 if( pPtr
+6 >= pEnd
)
3221 if( (pPtr
[0] != 0x80) || (pPtr
[1] >= 0x03) )
3223 const int nLen
= (pPtr
[5]<<24) + (pPtr
[4]<<16) + (pPtr
[3]<<8) + pPtr
[2];
3226 rSegmentLengths
[i
] = nLen
;
3230 // read segment-end header
3231 if( pPtr
+2 >= pEnd
)
3233 if( (pPtr
[0] != 0x80) || (pPtr
[1] != 0x03) )
3239 struct FontException
: public std::exception
3243 // TODO: always subset instead of embedding the full font => this method becomes obsolete then
3244 std::map
< sal_Int32
, sal_Int32
> PDFWriterImpl::emitEmbeddedFont( const PhysicalFontFace
* pFont
, EmbedFont
& rEmbed
)
3246 std::map
< sal_Int32
, sal_Int32
> aRet
;
3247 if( isBuiltinFont( pFont
) )
3249 aRet
[ rEmbed
.m_nNormalFontID
] = emitBuiltinFont( pFont
);
3253 sal_Int32 nStreamObject
= 0;
3254 sal_Int32 nFontDescriptor
= 0;
3256 // prepare font encoding
3257 const Ucs2SIntMap
* pEncoding
= m_pReferenceDevice
->mpGraphics
->GetFontEncodingVector( pFont
, NULL
);
3258 sal_Int32 nToUnicodeStream
= 0;
3259 sal_uInt8 nEncoding
[256];
3260 sal_Ucs nEncodedCodes
[256];
3261 std::vector
<sal_Ucs
> aUnicodes
;
3262 aUnicodes
.reserve( 256 );
3263 sal_Int32 pUnicodesPerGlyph
[256];
3264 sal_Int32 pEncToUnicodeIndex
[256];
3267 memset( nEncoding
, 0, sizeof(nEncoding
) );
3268 memset( nEncodedCodes
, 0, sizeof(nEncodedCodes
) );
3269 memset( pUnicodesPerGlyph
, 0, sizeof(pUnicodesPerGlyph
) );
3270 memset( pEncToUnicodeIndex
, 0, sizeof(pEncToUnicodeIndex
) );
3271 for( Ucs2SIntMap::const_iterator it
= pEncoding
->begin(); it
!= pEncoding
->end(); ++it
)
3273 if( it
->second
!= -1 )
3275 sal_Int32 nCode
= (sal_Int32
)(it
->second
& 0x000000ff);
3276 nEncoding
[ nCode
] = static_cast<sal_uInt8
>( nCode
);
3277 nEncodedCodes
[ nCode
] = it
->first
;
3278 pEncToUnicodeIndex
[ nCode
] = static_cast<sal_Int32
>(aUnicodes
.size());
3279 aUnicodes
.push_back( it
->first
);
3280 pUnicodesPerGlyph
[ nCode
] = 1;
3285 FontSubsetInfo aInfo
;
3286 sal_Int32 pWidths
[256];
3287 const unsigned char* pFontData
= NULL
;
3289 sal_Int32 nLength1
, nLength2
;
3292 if( (pFontData
= (const unsigned char*)m_pReferenceDevice
->mpGraphics
->GetEmbedFontData( pFont
, nEncodedCodes
, pWidths
, aInfo
, &nFontLen
)) != NULL
)
3294 if( (aInfo
.m_nFontType
& FontSubsetInfo::ANY_TYPE1
) == 0 )
3295 throw FontException();
3296 // see whether it is pfb or pfa; if it is a pfb, fill ranges
3297 // of 6 bytes that are not part of the font program
3298 std::list
< int > aSections
;
3299 std::list
< int >::const_iterator it
;
3301 while( (nIndex
< nFontLen
-1) && pFontData
[nIndex
] == 0x80 )
3303 aSections
.push_back( nIndex
);
3304 if( pFontData
[nIndex
+1] == 0x03 )
3307 ((sal_Int32
)pFontData
[nIndex
+2]) |
3308 ((sal_Int32
)pFontData
[nIndex
+3]) << 8 |
3309 ((sal_Int32
)pFontData
[nIndex
+4]) << 16 |
3310 ((sal_Int32
)pFontData
[nIndex
+5]) << 24;
3315 // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below
3318 int nBeginBinaryIndex
;
3319 int nEndBinaryIndex
;
3322 while( nIndex
< nFontLen
-4 &&
3323 ( pFontData
[nIndex
] != 'e' ||
3324 pFontData
[nIndex
+1] != 'e' ||
3325 pFontData
[nIndex
+2] != 'x' ||
3326 pFontData
[nIndex
+3] != 'e' ||
3327 pFontData
[nIndex
+4] != 'c'
3333 // check whether we are in a excluded section
3334 for( it
= aSections
.begin(); it
!= aSections
.end() && (nIndex
< *it
|| nIndex
> ((*it
) + 5) ); ++it
)
3336 } while( it
!= aSections
.end() && nIndex
< nFontLen
-4 );
3337 // this should end the ascii part
3338 if( nIndex
> nFontLen
-5 )
3339 throw FontException();
3341 nEndAsciiIndex
= nIndex
+4;
3342 // now count backwards until we can account for 512 '0'
3343 // which is the endmarker of the (hopefully) binary data
3344 // do not count the pfb header sections
3346 nIndex
= nFontLen
-1;
3347 while( nIndex
> 0 && nFound
< 512 )
3349 for( it
= aSections
.begin(); it
!= aSections
.end() && (nIndex
< *it
|| nIndex
> ((*it
) + 5) ); ++it
)
3351 if( it
== aSections
.end() )
3353 // inside the 512 '0' block there may only be whitespace
3354 // according to T1 spec; probably it would be to simple
3355 // if all fonts complied
3356 if( pFontData
[nIndex
] == '0' )
3358 else if( nFound
> 0 &&
3359 pFontData
[nIndex
] != '\r' &&
3360 pFontData
[nIndex
] != '\t' &&
3361 pFontData
[nIndex
] != '\n' &&
3362 pFontData
[nIndex
] != ' ' )
3368 if( nIndex
< 1 || nIndex
<= nEndAsciiIndex
)
3369 throw FontException();
3371 // nLength3 is the rest of the file - excluding any section headers
3372 // nIndex now points before the first of the 512 '0' characters marking the
3373 // fixed content portion
3374 sal_Int32 nLength3
= nFontLen
- nIndex
- 1;
3375 for( it
= aSections
.begin(); it
!= aSections
.end(); ++it
)
3377 // special case: nIndex inside a section marker
3378 if( nIndex
>= (*it
) && (*it
)+6 > nIndex
)
3379 nLength3
-= (*it
)+6 - nIndex
;
3380 else if( *it
>= nIndex
)
3382 if( *it
< nFontLen
- 6 )
3384 else // the last section 0x8003 is only 2 bytes after all
3385 nLength3
-= (nFontLen
- *it
);
3389 // there may be whitespace to ignore before the 512 '0'
3390 while( pFontData
[nIndex
] == '\r' || pFontData
[nIndex
] == '\n' )
3393 for( it
= aSections
.begin(); it
!= aSections
.end() && (nIndex
< *it
|| nIndex
> ((*it
) + 5) ); ++it
)
3395 if( it
!= aSections
.end() )
3398 break; // this is surely a binary boundary, in ascii case it wouldn't matter
3401 nEndBinaryIndex
= nIndex
;
3403 // search for beginning of binary section
3404 nBeginBinaryIndex
= nEndAsciiIndex
;
3407 nBeginBinaryIndex
++;
3408 for( it
= aSections
.begin(); it
!= aSections
.end() && (nBeginBinaryIndex
< *it
|| nBeginBinaryIndex
> ((*it
) + 5) ); ++it
)
3410 } while( nBeginBinaryIndex
< nEndBinaryIndex
&&
3411 ( pFontData
[nBeginBinaryIndex
] == '\r' ||
3412 pFontData
[nBeginBinaryIndex
] == '\n' ||
3413 it
!= aSections
.end() ) );
3415 // it seems to be vital to copy the exact whitespace between binary data
3416 // and eexec, else a invalid font results. so make nEndAsciiIndex
3417 // always immediate in front of nBeginBinaryIndex
3418 nEndAsciiIndex
= nBeginBinaryIndex
-1;
3419 for( it
= aSections
.begin(); it
!= aSections
.end() && (nEndAsciiIndex
< *it
|| nEndAsciiIndex
> ((*it
)+5)); ++it
)
3421 if( it
!= aSections
.end() )
3422 nEndAsciiIndex
= (*it
)-1;
3424 nLength1
= nEndAsciiIndex
+1; // including the last character
3425 for( it
= aSections
.begin(); it
!= aSections
.end() && *it
< nEndAsciiIndex
; ++it
)
3426 nLength1
-= 6; // decrease by pfb section size
3428 // if the first four bytes are all ascii hex characters, then binary data
3429 // has to be converted to real binary data
3430 for( nIndex
= 0; nIndex
< 4 &&
3431 ( ( pFontData
[ nBeginBinaryIndex
+nIndex
] >= '0' && pFontData
[ nBeginBinaryIndex
+nIndex
] <= '9' ) ||
3432 ( pFontData
[ nBeginBinaryIndex
+nIndex
] >= 'a' && pFontData
[ nBeginBinaryIndex
+nIndex
] <= 'f' ) ||
3433 ( pFontData
[ nBeginBinaryIndex
+nIndex
] >= 'A' && pFontData
[ nBeginBinaryIndex
+nIndex
] <= 'F' )
3436 bool bConvertHexData
= true;
3439 bConvertHexData
= false;
3440 nLength2
= nEndBinaryIndex
- nBeginBinaryIndex
+ 1; // include the last byte
3441 for( it
= aSections
.begin(); it
!= aSections
.end(); ++it
)
3442 if( *it
> nBeginBinaryIndex
&& *it
< nEndBinaryIndex
)
3447 // count the hex ascii characters to get nLength2
3449 int nNextSectionIndex
= 0;
3450 for( it
= aSections
.begin(); it
!= aSections
.end() && *it
< nBeginBinaryIndex
; ++it
)
3452 if( it
!= aSections
.end() )
3453 nNextSectionIndex
= *it
;
3454 for( nIndex
= nBeginBinaryIndex
; nIndex
<= nEndBinaryIndex
; nIndex
++ )
3456 if( nIndex
== nNextSectionIndex
)
3460 nNextSectionIndex
= (it
== aSections
.end() ? 0 : *it
);
3462 if( ( pFontData
[ nIndex
] >= '0' && pFontData
[ nIndex
] <= '9' ) ||
3463 ( pFontData
[ nIndex
] >= 'a' && pFontData
[ nIndex
] <= 'f' ) ||
3464 ( pFontData
[ nIndex
] >= 'A' && pFontData
[ nIndex
] <= 'F' ) )
3467 DBG_ASSERT( !(nLength2
& 1), "uneven number of hex chars in binary pfa section" );
3471 // now we can actually write the font stream !
3472 #if OSL_DEBUG_LEVEL > 1
3473 emitComment( " PDFWriterImpl::emitEmbeddedFont" );
3475 OStringBuffer
aLine( 512 );
3476 nStreamObject
= createObject();
3477 if( !updateObject(nStreamObject
))
3478 throw FontException();
3479 sal_Int32 nStreamLengthObject
= createObject();
3480 aLine
.append( nStreamObject
);
3481 aLine
.append( " 0 obj\n"
3483 aLine
.append( nStreamLengthObject
);
3484 aLine
.append( " 0 R"
3485 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3486 "/Filter/FlateDecode"
3489 aLine
.append( nLength1
);
3490 aLine
.append( " /Length2 " );
3491 aLine
.append( nLength2
);
3492 aLine
.append( " /Length3 ");
3493 aLine
.append( nLength3
);
3494 aLine
.append( ">>\n"
3496 if( !writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
3497 throw FontException();
3499 sal_uInt64 nBeginStreamPos
= 0;
3500 osl_getFilePos( m_aFile
, &nBeginStreamPos
);
3503 checkAndEnableStreamEncryption( nStreamObject
);
3505 // write ascii section
3506 if( aSections
.begin() == aSections
.end() )
3508 if( ! writeBuffer( pFontData
, nEndAsciiIndex
+1 ) )
3509 throw FontException();
3513 // first section always starts at 0
3514 it
= aSections
.begin();
3517 while( *it
< nEndAsciiIndex
)
3519 if( ! writeBuffer( pFontData
+nIndex
, (*it
)-nIndex
) )
3520 throw FontException();
3524 // write partial last section
3525 if( ! writeBuffer( pFontData
+nIndex
, nEndAsciiIndex
-nIndex
+1 ) )
3526 throw FontException();
3529 // write binary section
3530 if( ! bConvertHexData
)
3532 if( aSections
.begin() == aSections
.end() )
3534 if( ! writeBuffer( pFontData
+nBeginBinaryIndex
, nFontLen
-nBeginBinaryIndex
) )
3535 throw FontException();
3539 for( it
= aSections
.begin(); *it
< nBeginBinaryIndex
; ++it
)
3541 // write first partial section
3542 if( ! writeBuffer( pFontData
+nBeginBinaryIndex
, (*it
) - nBeginBinaryIndex
) )
3543 throw FontException();
3544 // write following sections
3545 while( it
!= aSections
.end() )
3549 if( nIndex
< nFontLen
) // last section marker is usually the EOF which has only 2 bytes
3551 sal_Int32 nSectionLen
= (it
== aSections
.end()) ? nFontLen
- nIndex
: (*it
) - nIndex
;
3552 if( ! writeBuffer( pFontData
+nIndex
, nSectionLen
) )
3553 throw FontException();
3560 boost::shared_array
<unsigned char> pWriteBuffer( new unsigned char[ nLength2
] );
3561 memset( pWriteBuffer
.get(), 0, nLength2
);
3562 int nWriteIndex
= 0;
3564 int nNextSectionIndex
= 0;
3565 for( it
= aSections
.begin(); it
!= aSections
.end() && *it
< nBeginBinaryIndex
; ++it
)
3567 if( it
!= aSections
.end() )
3568 nNextSectionIndex
= *it
;
3569 for( nIndex
= nBeginBinaryIndex
; nIndex
<= nEndBinaryIndex
; nIndex
++ )
3571 if( nIndex
== nNextSectionIndex
)
3575 nNextSectionIndex
= (it
== aSections
.end() ? nFontLen
: *it
);
3577 unsigned char cNibble
= 0x80;
3578 if( pFontData
[ nIndex
] >= '0' && pFontData
[ nIndex
] <= '9' )
3579 cNibble
= pFontData
[nIndex
] - '0';
3580 else if( pFontData
[ nIndex
] >= 'a' && pFontData
[ nIndex
] <= 'f' )
3581 cNibble
= pFontData
[nIndex
] - 'a' + 10;
3582 else if( pFontData
[ nIndex
] >= 'A' && pFontData
[ nIndex
] <= 'F' )
3583 cNibble
= pFontData
[nIndex
] - 'A' + 10;
3584 if( cNibble
!= 0x80 )
3586 if( !(nWriteIndex
& 1 ) )
3588 pWriteBuffer
.get()[ nWriteIndex
/2 ] |= cNibble
;
3592 if( ! writeBuffer( pWriteBuffer
.get(), nLength2
) )
3593 throw FontException();
3594 if( aSections
.empty() )
3596 if( ! writeBuffer( pFontData
+nIndex
, nFontLen
-nIndex
) )
3597 throw FontException();
3601 // write rest of this section
3602 if( nIndex
< nNextSectionIndex
)
3604 if( ! writeBuffer( pFontData
+nIndex
, nNextSectionIndex
- nIndex
) )
3605 throw FontException();
3607 // write following sections
3608 while( it
!= aSections
.end() )
3612 if( nIndex
< nFontLen
) // last section marker is usually the EOF which has only 2 bytes
3614 sal_Int32 nSectionLen
= (it
== aSections
.end()) ? nFontLen
- nIndex
: (*it
) - nIndex
;
3615 if( ! writeBuffer( pFontData
+nIndex
, nSectionLen
) )
3616 throw FontException();
3622 disableStreamEncryption();
3625 sal_uInt64 nEndStreamPos
= 0;
3626 osl_getFilePos( m_aFile
, &nEndStreamPos
);
3628 // and finally close the stream
3629 aLine
.setLength( 0 );
3630 aLine
.append( "\nendstream\nendobj\n\n" );
3631 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
3632 throw FontException();
3634 // write stream length object
3635 aLine
.setLength( 0 );
3636 if( ! updateObject( nStreamLengthObject
) )
3637 throw FontException();
3638 aLine
.append( nStreamLengthObject
);
3639 aLine
.append( " 0 obj\n" );
3640 aLine
.append( (sal_Int64
)(nEndStreamPos
-nBeginStreamPos
) );
3641 aLine
.append( "\nendobj\n\n" );
3642 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
3643 throw FontException();
3647 OStringBuffer
aErrorComment( 256 );
3648 aErrorComment
.append( "GetEmbedFontData failed for font \"" );
3649 aErrorComment
.append( OUStringToOString( pFont
->GetFamilyName(), RTL_TEXTENCODING_UTF8
) );
3650 aErrorComment
.append( '\"' );
3651 if( pFont
->GetSlant() == ITALIC_NORMAL
)
3652 aErrorComment
.append( " italic" );
3653 else if( pFont
->GetSlant() == ITALIC_OBLIQUE
)
3654 aErrorComment
.append( " oblique" );
3655 aErrorComment
.append( " weight=" );
3656 aErrorComment
.append( sal_Int32(pFont
->GetWeight()) );
3657 emitComment( aErrorComment
.getStr() );
3662 // write font descriptor
3663 nFontDescriptor
= emitFontDescriptor( pFont
, aInfo
, 0, nStreamObject
);
3666 if( nFontDescriptor
)
3669 nToUnicodeStream
= createToUnicodeCMap( nEncoding
, &aUnicodes
[0], pUnicodesPerGlyph
, pEncToUnicodeIndex
, SAL_N_ELEMENTS(nEncoding
) );
3671 // write font object
3672 sal_Int32 nObject
= createObject();
3673 if( ! updateObject( nObject
) )
3674 throw FontException();
3676 OStringBuffer
aLine( 1024 );
3677 aLine
.append( nObject
);
3678 aLine
.append( " 0 obj\n"
3679 "<</Type/Font/Subtype/Type1/BaseFont/" );
3680 appendName( aInfo
.m_aPSName
, aLine
);
3681 aLine
.append( "\n" );
3682 if( !pFont
->IsSymbolFont() && pEncoding
== 0 )
3683 aLine
.append( "/Encoding/WinAnsiEncoding\n" );
3684 if( nToUnicodeStream
)
3686 aLine
.append( "/ToUnicode " );
3687 aLine
.append( nToUnicodeStream
);
3688 aLine
.append( " 0 R\n" );
3690 aLine
.append( "/FirstChar 0 /LastChar 255\n"
3692 for( int i
= 0; i
< 256; i
++ )
3694 aLine
.append( pWidths
[i
] );
3695 aLine
.append( ((i
&15) == 15) ? "\n" : " " );
3698 "/FontDescriptor " );
3699 aLine
.append( nFontDescriptor
);
3700 aLine
.append( " 0 R>>\n"
3702 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
3703 throw FontException();
3705 aRet
[ rEmbed
.m_nNormalFontID
] = nObject
;
3707 // write additional encodings
3708 for( std::list
< EmbedEncoding
>::iterator enc_it
= rEmbed
.m_aExtendedEncodings
.begin(); enc_it
!= rEmbed
.m_aExtendedEncodings
.end(); ++enc_it
)
3710 sal_Int32 aEncWidths
[ 256 ];
3711 // emit encoding dict
3712 sal_Int32 nEncObject
= createObject();
3713 if( ! updateObject( nEncObject
) )
3714 throw FontException();
3716 OutputDevice
* pRef
= getReferenceDevice();
3717 pRef
->Push( PUSH_FONT
| PUSH_MAPMODE
);
3718 pRef
->SetMapMode( MapMode( MAP_PIXEL
) );
3719 Font
aFont( pFont
->GetFamilyName(), pFont
->GetStyleName(), Size( 0, 1000 ) );
3720 aFont
.SetWeight( pFont
->GetWeight() );
3721 aFont
.SetItalic( pFont
->GetSlant() );
3722 aFont
.SetPitch( pFont
->GetPitch() );
3723 pRef
->SetFont( aFont
);
3724 pRef
->ImplNewFont();
3726 aLine
.setLength( 0 );
3727 aLine
.append( nEncObject
);
3728 aLine
.append( " 0 obj\n"
3729 "<</Type/Encoding/Differences[ 0\n" );
3732 for( std::vector
< EmbedCode
>::iterator str_it
= enc_it
->m_aEncVector
.begin(); str_it
!= enc_it
->m_aEncVector
.end(); ++str_it
)
3734 OUString
aStr( str_it
->m_aUnicode
);
3735 aEncWidths
[nEncoded
] = pRef
->GetTextWidth( aStr
);
3736 nEncodedCodes
[nEncoded
] = str_it
->m_aUnicode
;
3737 nEncoding
[nEncoded
] = sal::static_int_cast
<sal_uInt8
>(nEncoded
);
3738 pEncToUnicodeIndex
[nEncoded
] = static_cast<sal_Int32
>(aUnicodes
.size());
3739 aUnicodes
.push_back( nEncodedCodes
[nEncoded
] );
3740 pUnicodesPerGlyph
[nEncoded
] = 1;
3742 aLine
.append( " /" );
3743 aLine
.append( str_it
->m_aName
);
3744 if( !((++nEncoded
) & 15) )
3745 aLine
.append( "\n" );
3747 aLine
.append( "]>>\n"
3752 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
3753 throw FontException();
3755 nToUnicodeStream
= createToUnicodeCMap( nEncoding
, &aUnicodes
[0], pUnicodesPerGlyph
, pEncToUnicodeIndex
, nEncoded
);
3757 nObject
= createObject();
3758 if( ! updateObject( nObject
) )
3759 throw FontException();
3761 aLine
.setLength( 0 );
3762 aLine
.append( nObject
);
3763 aLine
.append( " 0 obj\n"
3764 "<</Type/Font/Subtype/Type1/BaseFont/" );
3765 appendName( aInfo
.m_aPSName
, aLine
);
3766 aLine
.append( "\n" );
3767 aLine
.append( "/Encoding " );
3768 aLine
.append( nEncObject
);
3769 aLine
.append( " 0 R\n" );
3770 if( nToUnicodeStream
)
3772 aLine
.append( "/ToUnicode " );
3773 aLine
.append( nToUnicodeStream
);
3774 aLine
.append( " 0 R\n" );
3776 aLine
.append( "/FirstChar 0\n"
3778 aLine
.append( (sal_Int32
)(nEncoded
-1) );
3781 for( int i
= 0; i
< nEncoded
; i
++ )
3783 aLine
.append( aEncWidths
[i
] );
3784 aLine
.append( ((i
&15) == 15) ? "\n" : " " );
3786 aLine
.append( " ]\n"
3787 "/FontDescriptor " );
3788 aLine
.append( nFontDescriptor
);
3789 aLine
.append( " 0 R>>\n"
3791 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
3792 throw FontException();
3794 aRet
[ enc_it
->m_nFontID
] = nObject
;
3798 catch( FontException
& )
3800 // these do nothing in case there was no compression or encryption ongoing
3802 disableStreamEncryption();
3806 m_pReferenceDevice
->mpGraphics
->FreeEmbedFontData( pFontData
, nFontLen
);
3811 static void appendSubsetName( int nSubsetID
, const OUString
& rPSName
, OStringBuffer
& rBuffer
)
3815 for( int i
= 0; i
< 6; i
++ )
3817 int nOffset
= (nSubsetID
% 26);
3819 rBuffer
.append( (sal_Char
)('A'+nOffset
) );
3821 rBuffer
.append( '+' );
3823 appendName( rPSName
, rBuffer
);
3826 sal_Int32
PDFWriterImpl::createToUnicodeCMap( sal_uInt8
* pEncoding
,
3828 sal_Int32
* pUnicodesPerGlyph
,
3829 sal_Int32
* pEncToUnicodeIndex
,
3832 int nMapped
= 0, n
= 0;
3833 for( n
= 0; n
< nGlyphs
; n
++ )
3834 if( pUnicodes
[pEncToUnicodeIndex
[n
]] && pUnicodesPerGlyph
[n
] )
3840 sal_Int32 nStream
= createObject();
3841 CHECK_RETURN( updateObject( nStream
) );
3843 OStringBuffer
aContents( 1024 );
3845 "/CIDInit/ProcSet findresource begin\n"
3848 "/CIDSystemInfo<<\n"
3849 "/Registry (Adobe)\n"
3853 "/CMapName/Adobe-Identity-UCS def\n"
3855 "1 begincodespacerange\n"
3857 "endcodespacerange\n"
3860 for( n
= 0; n
< nGlyphs
; n
++ )
3862 if( pUnicodes
[pEncToUnicodeIndex
[n
]] && pUnicodesPerGlyph
[n
] )
3864 if( (nCount
% 100) == 0 )
3867 aContents
.append( "endbfchar\n" );
3868 aContents
.append( (sal_Int32
)((nMapped
-nCount
> 100) ? 100 : nMapped
-nCount
) );
3869 aContents
.append( " beginbfchar\n" );
3871 aContents
.append( '<' );
3872 appendHex( (sal_Int8
)pEncoding
[n
], aContents
);
3873 aContents
.append( "> <" );
3874 // TODO: handle unicodes>U+FFFF
3875 sal_Int32 nIndex
= pEncToUnicodeIndex
[n
];
3876 for( sal_Int32 j
= 0; j
< pUnicodesPerGlyph
[n
]; j
++ )
3878 appendHex( (sal_Int8
)(pUnicodes
[nIndex
+ j
] / 256), aContents
);
3879 appendHex( (sal_Int8
)(pUnicodes
[nIndex
+ j
] & 255), aContents
);
3881 aContents
.append( ">\n" );
3885 aContents
.append( "endbfchar\n"
3887 "CMapName currentdict /CMap defineresource pop\n"
3890 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3891 ZCodec
* pCodec
= new ZCodec( 0x4000, 0x4000 );
3892 SvMemoryStream aStream
;
3893 pCodec
->BeginCompression();
3894 pCodec
->Write( aStream
, (const sal_uInt8
*)aContents
.getStr(), aContents
.getLength() );
3895 pCodec
->EndCompression();
3899 #if OSL_DEBUG_LEVEL > 1
3900 emitComment( "PDFWriterImpl::createToUnicodeCMap" );
3902 OStringBuffer
aLine( 40 );
3904 aLine
.append( nStream
);
3905 aLine
.append( " 0 obj\n<</Length " );
3906 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3907 sal_Int32 nLen
= (sal_Int32
)aStream
.Tell();
3909 aLine
.append( nLen
);
3910 aLine
.append( "/Filter/FlateDecode" );
3912 aLine
.append( aContents
.getLength() );
3914 aLine
.append( ">>\nstream\n" );
3915 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
3916 checkAndEnableStreamEncryption( nStream
);
3917 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3918 CHECK_RETURN( writeBuffer( aStream
.GetData(), nLen
) );
3920 CHECK_RETURN( writeBuffer( aContents
.getStr(), aContents
.getLength() ) );
3922 disableStreamEncryption();
3923 aLine
.setLength( 0 );
3924 aLine
.append( "\nendstream\n"
3926 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
3930 sal_Int32
PDFWriterImpl::emitFontDescriptor( const PhysicalFontFace
* pFont
, FontSubsetInfo
& rInfo
, sal_Int32 nSubsetID
, sal_Int32 nFontStream
)
3932 OStringBuffer
aLine( 1024 );
3933 // get font flags, see PDF reference 1.4 p. 358
3934 // possibly characters outside Adobe standard encoding
3935 // so set Symbolic flag
3936 sal_Int32 nFontFlags
= (1<<2);
3937 if( pFont
->GetSlant() == ITALIC_NORMAL
|| pFont
->GetSlant() == ITALIC_OBLIQUE
)
3938 nFontFlags
|= (1 << 6);
3939 if( pFont
->GetPitch() == PITCH_FIXED
)
3941 if( pFont
->GetFamilyType() == FAMILY_SCRIPT
)
3942 nFontFlags
|= (1 << 3);
3943 else if( pFont
->GetFamilyType() == FAMILY_ROMAN
)
3944 nFontFlags
|= (1 << 1);
3946 sal_Int32 nFontDescriptor
= createObject();
3947 CHECK_RETURN( updateObject( nFontDescriptor
) );
3948 aLine
.setLength( 0 );
3949 aLine
.append( nFontDescriptor
);
3950 aLine
.append( " 0 obj\n"
3951 "<</Type/FontDescriptor/FontName/" );
3952 appendSubsetName( nSubsetID
, rInfo
.m_aPSName
, aLine
);
3955 aLine
.append( nFontFlags
);
3958 // note: Top and Bottom are reversed in VCL and PDF rectangles
3959 aLine
.append( (sal_Int32
)rInfo
.m_aFontBBox
.TopLeft().X() );
3960 aLine
.append( ' ' );
3961 aLine
.append( (sal_Int32
)rInfo
.m_aFontBBox
.TopLeft().Y() );
3962 aLine
.append( ' ' );
3963 aLine
.append( (sal_Int32
)rInfo
.m_aFontBBox
.BottomRight().X() );
3964 aLine
.append( ' ' );
3965 aLine
.append( (sal_Int32
)(rInfo
.m_aFontBBox
.BottomRight().Y()+1) );
3966 aLine
.append( "]/ItalicAngle " );
3967 if( pFont
->GetSlant() == ITALIC_OBLIQUE
|| pFont
->GetSlant() == ITALIC_NORMAL
)
3968 aLine
.append( "-30" );
3970 aLine
.append( "0" );
3973 aLine
.append( (sal_Int32
)rInfo
.m_nAscent
);
3976 aLine
.append( (sal_Int32
)-rInfo
.m_nDescent
);
3979 aLine
.append( (sal_Int32
)rInfo
.m_nCapHeight
);
3980 // According to PDF reference 1.4 StemV is required
3981 // seems a tad strange to me, but well ...
3986 aLine
.append( "/FontFile" );
3987 switch( rInfo
.m_nFontType
)
3989 case FontSubsetInfo::SFNT_TTF
:
3990 aLine
.append( '2' );
3992 case FontSubsetInfo::TYPE1_PFA
:
3993 case FontSubsetInfo::TYPE1_PFB
:
3994 case FontSubsetInfo::ANY_TYPE1
:
3997 OSL_FAIL( "unknown fonttype in PDF font descriptor" );
4000 aLine
.append( ' ' );
4001 aLine
.append( nFontStream
);
4002 aLine
.append( " 0 R\n" );
4004 aLine
.append( ">>\n"
4006 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4008 return nFontDescriptor
;
4011 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer
& rDict
) const
4013 for( std::map
< sal_Int32
, sal_Int32
>::const_iterator it
=
4014 m_aBuiltinFontToObjectMap
.begin(); it
!= m_aBuiltinFontToObjectMap
.end(); ++it
)
4016 rDict
.append( m_aBuiltinFonts
[it
->first
].getNameObject() );
4017 rDict
.append( ' ' );
4018 rDict
.append( it
->second
);
4019 rDict
.append( " 0 R" );
4023 bool PDFWriterImpl::emitFonts()
4025 if( ! m_pReferenceDevice
->ImplGetGraphics() )
4028 OStringBuffer
aLine( 1024 );
4030 std::map
< sal_Int32
, sal_Int32
> aFontIDToObject
;
4033 osl_createTempFile( NULL
, NULL
, &aTmpName
.pData
);
4034 for( FontSubsetData::iterator it
= m_aSubsets
.begin(); it
!= m_aSubsets
.end(); ++it
)
4036 for( FontEmitList::iterator lit
= it
->second
.m_aSubsets
.begin(); lit
!= it
->second
.m_aSubsets
.end(); ++lit
)
4038 sal_Int32 pGlyphIDs
[ 256 ];
4039 sal_Int32 pWidths
[ 256 ];
4040 sal_uInt8 pEncoding
[ 256 ];
4041 sal_Int32 pEncToUnicodeIndex
[ 256 ];
4042 sal_Int32 pUnicodesPerGlyph
[ 256 ];
4043 std::vector
<sal_Ucs
> aUnicodes
;
4044 aUnicodes
.reserve( 256 );
4046 // fill arrays and prepare encoding index map
4047 sal_Int32 nToUnicodeStream
= 0;
4049 memset( pGlyphIDs
, 0, sizeof( pGlyphIDs
) );
4050 memset( pEncoding
, 0, sizeof( pEncoding
) );
4051 memset( pUnicodesPerGlyph
, 0, sizeof( pUnicodesPerGlyph
) );
4052 memset( pEncToUnicodeIndex
, 0, sizeof( pEncToUnicodeIndex
) );
4053 for( FontEmitMapping::iterator fit
= lit
->m_aMapping
.begin(); fit
!= lit
->m_aMapping
.end();++fit
)
4055 sal_uInt8 nEnc
= fit
->second
.getGlyphId();
4057 DBG_ASSERT( pGlyphIDs
[nEnc
] == 0 && pEncoding
[nEnc
] == 0, "duplicate glyph" );
4058 DBG_ASSERT( nEnc
<= lit
->m_aMapping
.size(), "invalid glyph encoding" );
4060 pGlyphIDs
[ nEnc
] = fit
->first
;
4061 pEncoding
[ nEnc
] = nEnc
;
4062 pEncToUnicodeIndex
[ nEnc
] = static_cast<sal_Int32
>(aUnicodes
.size());
4063 pUnicodesPerGlyph
[ nEnc
] = fit
->second
.countCodes();
4064 for( sal_Int32 n
= 0; n
< pUnicodesPerGlyph
[ nEnc
]; n
++ )
4065 aUnicodes
.push_back( fit
->second
.getCode( n
) );
4066 if( fit
->second
.getCode(0) )
4067 nToUnicodeStream
= 1;
4072 OSL_FAIL( "too many glyphs for subset" );
4075 FontSubsetInfo aSubsetInfo
;
4076 if( m_pReferenceDevice
->mpGraphics
->CreateFontSubset( aTmpName
, it
->first
, pGlyphIDs
, pEncoding
, pWidths
, nGlyphs
, aSubsetInfo
) )
4078 // create font stream
4079 oslFileHandle aFontFile
;
4080 CHECK_RETURN( (osl_File_E_None
== osl_openFile( aTmpName
.pData
, &aFontFile
, osl_File_OpenFlag_Read
) ) );
4082 sal_uInt64 nLength1
;
4083 CHECK_RETURN( (osl_File_E_None
== osl_setFilePos( aFontFile
, osl_Pos_End
, 0 ) ) );
4084 CHECK_RETURN( (osl_File_E_None
== osl_getFilePos( aFontFile
, &nLength1
) ) );
4085 CHECK_RETURN( (osl_File_E_None
== osl_setFilePos( aFontFile
, osl_Pos_Absolut
, 0 ) ) );
4087 #if OSL_DEBUG_LEVEL > 1
4088 emitComment( "PDFWriterImpl::emitFonts" );
4090 sal_Int32 nFontStream
= createObject();
4091 sal_Int32 nStreamLengthObject
= createObject();
4092 CHECK_RETURN( updateObject( nFontStream
) );
4093 aLine
.setLength( 0 );
4094 aLine
.append( nFontStream
);
4095 aLine
.append( " 0 obj\n"
4097 aLine
.append( (sal_Int32
)nStreamLengthObject
);
4098 aLine
.append( " 0 R"
4099 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
4100 "/Filter/FlateDecode"
4104 sal_uInt64 nStartPos
= 0;
4105 if( aSubsetInfo
.m_nFontType
== FontSubsetInfo::SFNT_TTF
)
4107 aLine
.append( (sal_Int32
)nLength1
);
4109 aLine
.append( ">>\n"
4111 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4112 CHECK_RETURN( (osl_File_E_None
== osl_getFilePos( m_aFile
, &nStartPos
) ) );
4116 checkAndEnableStreamEncryption( nFontStream
);
4117 sal_Bool bEOF
= sal_False
;
4122 CHECK_RETURN( (osl_File_E_None
== osl_readFile( aFontFile
, buf
, sizeof( buf
), &nRead
) ) );
4123 CHECK_RETURN( writeBuffer( buf
, nRead
) );
4124 CHECK_RETURN( (osl_File_E_None
== osl_isEndOfFile( aFontFile
, &bEOF
) ) );
4127 else if( (aSubsetInfo
.m_nFontType
& FontSubsetInfo::CFF_FONT
) != 0 )
4130 OSL_FAIL( "PDFWriterImpl does not support CFF-font subsets yet!" );
4132 else if( (aSubsetInfo
.m_nFontType
& FontSubsetInfo::TYPE1_PFB
) != 0 ) // TODO: also support PFA?
4134 boost::shared_array
<unsigned char> pBuffer( new unsigned char[ nLength1
] );
4136 sal_uInt64 nBytesRead
= 0;
4137 CHECK_RETURN( (osl_File_E_None
== osl_readFile( aFontFile
, pBuffer
.get(), nLength1
, &nBytesRead
) ) );
4138 DBG_ASSERT( nBytesRead
==nLength1
, "PDF-FontSubset read incomplete!" );
4139 CHECK_RETURN( (osl_File_E_None
== osl_setFilePos( aFontFile
, osl_Pos_Absolut
, 0 ) ) );
4140 // get the PFB-segment lengths
4141 ThreeInts aSegmentLengths
= {0,0,0};
4142 getPfbSegmentLengths( pBuffer
.get(), (int)nBytesRead
, aSegmentLengths
);
4143 // the lengths below are mandatory for PDF-exported Type1 fonts
4144 // because the PFB segment headers get stripped! WhyOhWhy.
4145 aLine
.append( (sal_Int32
)aSegmentLengths
[0] );
4146 aLine
.append( "/Length2 " );
4147 aLine
.append( (sal_Int32
)aSegmentLengths
[1] );
4148 aLine
.append( "/Length3 " );
4149 aLine
.append( (sal_Int32
)aSegmentLengths
[2] );
4151 aLine
.append( ">>\n"
4153 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4154 CHECK_RETURN( (osl_File_E_None
== osl_getFilePos( m_aFile
, &nStartPos
) ) );
4156 // emit PFB-sections without section headers
4158 checkAndEnableStreamEncryption( nFontStream
);
4159 CHECK_RETURN( writeBuffer( &pBuffer
[6], aSegmentLengths
[0] ) );
4160 CHECK_RETURN( writeBuffer( &pBuffer
[12] + aSegmentLengths
[0], aSegmentLengths
[1] ) );
4161 CHECK_RETURN( writeBuffer( &pBuffer
[18] + aSegmentLengths
[0] + aSegmentLengths
[1], aSegmentLengths
[2] ) );
4165 fprintf( stderr
, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo
.m_nFontType
);
4166 aLine
.append( "0 >>\nstream\n" );
4170 disableStreamEncryption();
4172 osl_closeFile( aFontFile
);
4174 sal_uInt64 nEndPos
= 0;
4175 CHECK_RETURN( (osl_File_E_None
== osl_getFilePos( m_aFile
, &nEndPos
) ) );
4177 aLine
.setLength( 0 );
4178 aLine
.append( "\nendstream\nendobj\n\n" );
4179 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4181 // emit stream length object
4182 CHECK_RETURN( updateObject( nStreamLengthObject
) );
4183 aLine
.setLength( 0 );
4184 aLine
.append( nStreamLengthObject
);
4185 aLine
.append( " 0 obj\n" );
4186 aLine
.append( (sal_Int64
)(nEndPos
-nStartPos
) );
4187 aLine
.append( "\nendobj\n\n" );
4188 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4190 // write font descriptor
4191 sal_Int32 nFontDescriptor
= emitFontDescriptor( it
->first
, aSubsetInfo
, lit
->m_nFontID
, nFontStream
);
4193 if( nToUnicodeStream
)
4194 nToUnicodeStream
= createToUnicodeCMap( pEncoding
, &aUnicodes
[0], pUnicodesPerGlyph
, pEncToUnicodeIndex
, nGlyphs
);
4196 sal_Int32 nFontObject
= createObject();
4197 CHECK_RETURN( updateObject( nFontObject
) );
4198 aLine
.setLength( 0 );
4199 aLine
.append( nFontObject
);
4201 aLine
.append( " 0 obj\n" );
4202 aLine
.append( ((aSubsetInfo
.m_nFontType
& FontSubsetInfo::ANY_TYPE1
) != 0) ?
4203 "<</Type/Font/Subtype/Type1/BaseFont/" :
4204 "<</Type/Font/Subtype/TrueType/BaseFont/" );
4205 appendSubsetName( lit
->m_nFontID
, aSubsetInfo
.m_aPSName
, aLine
);
4209 aLine
.append( (sal_Int32
)(nGlyphs
-1) );
4212 for( int i
= 0; i
< nGlyphs
; i
++ )
4214 aLine
.append( pWidths
[ i
] );
4215 aLine
.append( ((i
& 15) == 15) ? "\n" : " " );
4218 "/FontDescriptor " );
4219 aLine
.append( nFontDescriptor
);
4220 aLine
.append( " 0 R\n" );
4221 if( nToUnicodeStream
)
4223 aLine
.append( "/ToUnicode " );
4224 aLine
.append( nToUnicodeStream
);
4225 aLine
.append( " 0 R\n" );
4227 aLine
.append( ">>\n"
4229 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4231 aFontIDToObject
[ lit
->m_nFontID
] = nFontObject
;
4235 const PhysicalFontFace
* pFont
= it
->first
;
4236 OStringBuffer
aErrorComment( 256 );
4237 aErrorComment
.append( "CreateFontSubset failed for font \"" );
4238 aErrorComment
.append( OUStringToOString( pFont
->GetFamilyName(), RTL_TEXTENCODING_UTF8
) );
4239 aErrorComment
.append( '\"' );
4240 if( pFont
->GetSlant() == ITALIC_NORMAL
)
4241 aErrorComment
.append( " italic" );
4242 else if( pFont
->GetSlant() == ITALIC_OBLIQUE
)
4243 aErrorComment
.append( " oblique" );
4244 aErrorComment
.append( " weight=" );
4245 aErrorComment
.append( sal_Int32(pFont
->GetWeight()) );
4246 emitComment( aErrorComment
.getStr() );
4250 osl_removeFile( aTmpName
.pData
);
4252 // emit embedded fonts
4253 for( FontEmbedData::iterator eit
= m_aEmbeddedFonts
.begin(); eit
!= m_aEmbeddedFonts
.end(); ++eit
)
4255 std::map
< sal_Int32
, sal_Int32
> aObjects
= emitEmbeddedFont( eit
->first
, eit
->second
);
4256 for( std::map
< sal_Int32
, sal_Int32
>::iterator fit
= aObjects
.begin(); fit
!= aObjects
.end(); ++fit
)
4258 CHECK_RETURN( fit
->second
);
4259 aFontIDToObject
[ fit
->first
] = fit
->second
;
4263 // emit system fonts
4264 for( FontEmbedData::iterator sit
= m_aSystemFonts
.begin(); sit
!= m_aSystemFonts
.end(); ++sit
)
4266 std::map
< sal_Int32
, sal_Int32
> aObjects
= emitSystemFont( sit
->first
, sit
->second
);
4267 for( std::map
< sal_Int32
, sal_Int32
>::iterator fit
= aObjects
.begin(); fit
!= aObjects
.end(); ++fit
)
4269 CHECK_RETURN( fit
->second
);
4270 aFontIDToObject
[ fit
->first
] = fit
->second
;
4274 OStringBuffer
aFontDict( 1024 );
4275 aFontDict
.append( getFontDictObject() );
4276 aFontDict
.append( " 0 obj\n"
4279 for( std::map
< sal_Int32
, sal_Int32
>::iterator mit
= aFontIDToObject
.begin(); mit
!= aFontIDToObject
.end(); ++mit
)
4281 aFontDict
.append( "/F" );
4282 aFontDict
.append( mit
->first
);
4283 aFontDict
.append( ' ' );
4284 aFontDict
.append( mit
->second
);
4285 aFontDict
.append( " 0 R" );
4286 if( ((++ni
) & 7) == 0 )
4287 aFontDict
.append( '\n' );
4289 // emit builtin font for widget apperances / variable text
4290 for( std::map
< sal_Int32
, sal_Int32
>::iterator it
= m_aBuiltinFontToObjectMap
.begin();
4291 it
!= m_aBuiltinFontToObjectMap
.end(); ++it
)
4293 ImplPdfBuiltinFontData
aData(m_aBuiltinFonts
[it
->first
]);
4294 it
->second
= emitBuiltinFont( &aData
, it
->second
);
4296 appendBuiltinFontsToDict( aFontDict
);
4297 aFontDict
.append( "\n>>\nendobj\n\n" );
4299 CHECK_RETURN( updateObject( getFontDictObject() ) );
4300 CHECK_RETURN( writeBuffer( aFontDict
.getStr(), aFontDict
.getLength() ) );
4304 sal_Int32
PDFWriterImpl::emitResources()
4307 if( ! m_aGradients
.empty() )
4308 CHECK_RETURN( emitGradients() );
4310 if( ! m_aTilings
.empty() )
4311 CHECK_RETURN( emitTilings() );
4314 CHECK_RETURN( emitFonts() );
4316 // emit Resource dict
4317 OStringBuffer
aLine( 512 );
4318 sal_Int32 nResourceDict
= getResourceDictObj();
4319 CHECK_RETURN( updateObject( nResourceDict
) );
4320 aLine
.setLength( 0 );
4321 aLine
.append( nResourceDict
);
4322 aLine
.append( " 0 obj\n" );
4323 m_aGlobalResourceDict
.append( aLine
, getFontDictObject() );
4324 aLine
.append( "endobj\n\n" );
4325 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4326 return nResourceDict
;
4329 sal_Int32
PDFWriterImpl::updateOutlineItemCount( std::vector
< sal_Int32
>& rCounts
,
4330 sal_Int32 nItemLevel
,
4331 sal_Int32 nCurrentItemId
)
4333 /* The /Count number of an item is
4334 positive: the number of visible subitems
4335 negative: the negative number of subitems that will become visible if
4336 the item gets opened
4337 see PDF ref 1.4 p 478
4340 sal_Int32 nCount
= 0;
4342 if( m_aContext
.OpenBookmarkLevels
< 0 || // all levels arevisible
4343 m_aContext
.OpenBookmarkLevels
>= nItemLevel
// this level is visible
4346 PDFOutlineEntry
& rItem
= m_aOutline
[ nCurrentItemId
];
4347 sal_Int32 nChildren
= rItem
.m_aChildren
.size();
4348 for( sal_Int32 i
= 0; i
< nChildren
; i
++ )
4349 nCount
+= updateOutlineItemCount( rCounts
, nItemLevel
+1, rItem
.m_aChildren
[i
] );
4350 rCounts
[nCurrentItemId
] = nCount
;
4351 // return 1 (this item) + visible sub items
4358 // this bookmark level is invisible
4359 PDFOutlineEntry
& rItem
= m_aOutline
[ nCurrentItemId
];
4360 sal_Int32 nChildren
= rItem
.m_aChildren
.size();
4361 rCounts
[ nCurrentItemId
] = -sal_Int32(rItem
.m_aChildren
.size());
4362 for( sal_Int32 i
= 0; i
< nChildren
; i
++ )
4363 updateOutlineItemCount( rCounts
, nItemLevel
+1, rItem
.m_aChildren
[i
] );
4370 sal_Int32
PDFWriterImpl::emitOutline()
4372 int i
, nItems
= m_aOutline
.size();
4374 // do we have an outline at all ?
4378 // reserve object numbers for all outline items
4379 for( i
= 0; i
< nItems
; ++i
)
4380 m_aOutline
[i
].m_nObject
= createObject();
4382 // update all parent, next and prev object ids
4383 for( i
= 0; i
< nItems
; ++i
)
4385 PDFOutlineEntry
& rItem
= m_aOutline
[i
];
4386 int nChildren
= rItem
.m_aChildren
.size();
4390 for( int n
= 0; n
< nChildren
; ++n
)
4392 PDFOutlineEntry
& rChild
= m_aOutline
[ rItem
.m_aChildren
[n
] ];
4394 rChild
.m_nParentObject
= rItem
.m_nObject
;
4395 rChild
.m_nPrevObject
= (n
> 0) ? m_aOutline
[ rItem
.m_aChildren
[n
-1] ].m_nObject
: 0;
4396 rChild
.m_nNextObject
= (n
< nChildren
-1) ? m_aOutline
[ rItem
.m_aChildren
[n
+1] ].m_nObject
: 0;
4402 // calculate Count entries for all items
4403 std::vector
< sal_Int32
> aCounts( nItems
);
4404 updateOutlineItemCount( aCounts
, 0, 0 );
4407 for( i
= 0; i
< nItems
; ++i
)
4409 PDFOutlineEntry
& rItem
= m_aOutline
[i
];
4410 OStringBuffer
aLine( 1024 );
4412 CHECK_RETURN( updateObject( rItem
.m_nObject
) );
4413 aLine
.append( rItem
.m_nObject
);
4414 aLine
.append( " 0 obj\n" );
4415 aLine
.append( "<<" );
4416 // number of visible children (all levels)
4417 if( i
> 0 || aCounts
[0] > 0 )
4419 aLine
.append( "/Count " );
4420 aLine
.append( aCounts
[i
] );
4422 if( ! rItem
.m_aChildren
.empty() )
4424 // children list: First, Last
4425 aLine
.append( "/First " );
4426 aLine
.append( m_aOutline
[rItem
.m_aChildren
.front()].m_nObject
);
4427 aLine
.append( " 0 R/Last " );
4428 aLine
.append( m_aOutline
[rItem
.m_aChildren
.back()].m_nObject
);
4429 aLine
.append( " 0 R\n" );
4433 // Title, Dest, Parent, Prev, Next
4434 aLine
.append( "/Title" );
4435 appendUnicodeTextStringEncrypt( rItem
.m_aTitle
, rItem
.m_nObject
, aLine
);
4436 aLine
.append( "\n" );
4437 // Dest is not required
4438 if( rItem
.m_nDestID
>= 0 && rItem
.m_nDestID
< (sal_Int32
)m_aDests
.size() )
4440 aLine
.append( "/Dest" );
4441 appendDest( rItem
.m_nDestID
, aLine
);
4443 aLine
.append( "/Parent " );
4444 aLine
.append( rItem
.m_nParentObject
);
4445 aLine
.append( " 0 R" );
4446 if( rItem
.m_nPrevObject
)
4448 aLine
.append( "/Prev " );
4449 aLine
.append( rItem
.m_nPrevObject
);
4450 aLine
.append( " 0 R" );
4452 if( rItem
.m_nNextObject
)
4454 aLine
.append( "/Next " );
4455 aLine
.append( rItem
.m_nNextObject
);
4456 aLine
.append( " 0 R" );
4459 aLine
.append( ">>\nendobj\n\n" );
4460 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4463 return m_aOutline
[0].m_nObject
;
4467 #define CHECK_RETURN( x ) if( !x ) return false
4469 bool PDFWriterImpl::appendDest( sal_Int32 nDestID
, OStringBuffer
& rBuffer
)
4471 if( nDestID
< 0 || nDestID
>= (sal_Int32
)m_aDests
.size() )
4473 #if OSL_DEBUG_LEVEL > 1
4474 fprintf( stderr
, "ERROR: invalid dest %d requested\n", (int)nDestID
);
4480 const PDFDest
& rDest
= m_aDests
[ nDestID
];
4481 const PDFPage
& rDestPage
= m_aPages
[ rDest
.m_nPage
];
4483 rBuffer
.append( '[' );
4484 rBuffer
.append( rDestPage
.m_nPageObject
);
4485 rBuffer
.append( " 0 R" );
4487 switch( rDest
.m_eType
)
4489 case PDFWriter::XYZ
:
4491 rBuffer
.append( "/XYZ " );
4492 appendFixedInt( rDest
.m_aRect
.Left(), rBuffer
);
4493 rBuffer
.append( ' ' );
4494 appendFixedInt( rDest
.m_aRect
.Bottom(), rBuffer
);
4495 rBuffer
.append( " 0" );
4497 case PDFWriter::Fit
:
4498 rBuffer
.append( "/Fit" );
4500 case PDFWriter::FitRectangle
:
4501 rBuffer
.append( "/FitR " );
4502 appendFixedInt( rDest
.m_aRect
.Left(), rBuffer
);
4503 rBuffer
.append( ' ' );
4504 appendFixedInt( rDest
.m_aRect
.Top(), rBuffer
);
4505 rBuffer
.append( ' ' );
4506 appendFixedInt( rDest
.m_aRect
.Right(), rBuffer
);
4507 rBuffer
.append( ' ' );
4508 appendFixedInt( rDest
.m_aRect
.Bottom(), rBuffer
);
4510 case PDFWriter::FitHorizontal
:
4511 rBuffer
.append( "/FitH " );
4512 appendFixedInt( rDest
.m_aRect
.Bottom(), rBuffer
);
4514 case PDFWriter::FitVertical
:
4515 rBuffer
.append( "/FitV " );
4516 appendFixedInt( rDest
.m_aRect
.Left(), rBuffer
);
4518 case PDFWriter::FitPageBoundingBox
:
4519 rBuffer
.append( "/FitB" );
4521 case PDFWriter::FitPageBoundingBoxHorizontal
:
4522 rBuffer
.append( "/FitBH " );
4523 appendFixedInt( rDest
.m_aRect
.Bottom(), rBuffer
);
4525 case PDFWriter::FitPageBoundingBoxVertical
:
4526 rBuffer
.append( "/FitBV " );
4527 appendFixedInt( rDest
.m_aRect
.Left(), rBuffer
);
4530 rBuffer
.append( ']' );
4535 bool PDFWriterImpl::emitLinkAnnotations()
4537 int nAnnots
= m_aLinks
.size();
4538 for( int i
= 0; i
< nAnnots
; i
++ )
4540 const PDFLink
& rLink
= m_aLinks
[i
];
4541 if( ! updateObject( rLink
.m_nObject
) )
4544 OStringBuffer
aLine( 1024 );
4545 aLine
.append( rLink
.m_nObject
);
4546 aLine
.append( " 0 obj\n" );
4547 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4548 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4549 aLine
.append( "<</Type/Annot" );
4551 aLine
.append( "/F 4" );
4552 aLine
.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
4554 appendFixedInt( rLink
.m_aRect
.Left()-7, aLine
);//the +7 to have a better shape of the border rectangle
4555 aLine
.append( ' ' );
4556 appendFixedInt( rLink
.m_aRect
.Top(), aLine
);
4557 aLine
.append( ' ' );
4558 appendFixedInt( rLink
.m_aRect
.Right()+7, aLine
);//the +7 to have a better shape of the border rectangle
4559 aLine
.append( ' ' );
4560 appendFixedInt( rLink
.m_aRect
.Bottom(), aLine
);
4561 aLine
.append( "]" );
4562 if( rLink
.m_nDest
>= 0 )
4564 aLine
.append( "/Dest" );
4565 appendDest( rLink
.m_nDest
, aLine
);
4570 destination is external to the document, so
4571 we check in the following sequence:
4573 if target type is neither .pdf, nor .od[tpgs], then
4574 check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
4576 else if target is .od[tpgs]: then
4577 if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file
4580 if (new)target is .pdf : then
4581 if GotToR is requested, then
4582 convert the target in GoToR where the fragment of the URI is
4583 considered the named destination in the target file, set relative or absolute as requested
4584 else strip the fragment from URL and then set URI or 'launch application' as requested
4587 // FIXME: check if the decode mechanisms for URL processing throughout this implementation
4588 // are the correct one!!
4590 // extract target file type
4591 INetURLObject
aDocumentURL( m_aContext
.BaseURL
);
4592 INetURLObject
aTargetURL( rLink
.m_aURL
);
4593 sal_Int32 nSetGoToRMode
= 0;
4594 sal_Bool bTargetHasPDFExtension
= sal_False
;
4595 INetProtocol eTargetProtocol
= aTargetURL
.GetProtocol();
4596 sal_Bool bIsUNCPath
= sal_False
;
4597 // check if the protocol is a known one, or if there is no protocol at all (on target only)
4598 // if there is no protocol, make the target relative to the current document directory
4599 // getting the needed URL information from the current document path
4600 if( eTargetProtocol
== INET_PROT_NOT_VALID
)
4602 if( rLink
.m_aURL
.getLength() > 4 && rLink
.m_aURL
.startsWith("\\\\\\\\"))
4604 bIsUNCPath
= sal_True
;
4608 INetURLObject
aNewBase( aDocumentURL
);//duplicate document URL
4609 aNewBase
.removeSegment(); //remove last segment from it, obtaining the base URL of the
4611 aNewBase
.insertName( rLink
.m_aURL
);
4612 aTargetURL
= aNewBase
;//reassign the new target URL
4613 //recompute the target protocol, with the new URL
4614 //normal URL processing resumes
4615 eTargetProtocol
= aTargetURL
.GetProtocol();
4619 OUString aFileExtension
= aTargetURL
.GetFileExtension();
4621 // Check if the URL ends in '/': if yes it's a directory,
4622 // it will be forced to a URI link.
4623 // possibly a malformed URI, leave it as it is, force as URI
4624 if( aTargetURL
.hasFinalSlash() )
4625 m_aContext
.DefaultLinkAction
= PDFWriter::URIAction
;
4627 if( !aFileExtension
.isEmpty() )
4629 if( m_aContext
.ConvertOOoTargetToPDFTarget
)
4631 bool bChangeFileExtensionToPDF
= false;
4632 //examine the file type (.odm .odt. .odp, odg, ods)
4633 if( aFileExtension
.equalsIgnoreAsciiCase(OUString( "odm" ) ) )
4634 bChangeFileExtensionToPDF
= true;
4635 if( aFileExtension
.equalsIgnoreAsciiCase(OUString( "odt" ) ) )
4636 bChangeFileExtensionToPDF
= true;
4637 else if( aFileExtension
.equalsIgnoreAsciiCase(OUString( "odp" ) ) )
4638 bChangeFileExtensionToPDF
= true;
4639 else if( aFileExtension
.equalsIgnoreAsciiCase(OUString( "odg" ) ) )
4640 bChangeFileExtensionToPDF
= true;
4641 else if( aFileExtension
.equalsIgnoreAsciiCase(OUString( "ods" ) ) )
4642 bChangeFileExtensionToPDF
= true;
4643 if( bChangeFileExtensionToPDF
)
4644 aTargetURL
.setExtension(OUString( "pdf" ) );
4646 //check if extension is pdf, see if GoToR should be forced
4647 bTargetHasPDFExtension
= aTargetURL
.GetFileExtension().equalsIgnoreAsciiCase(OUString( "pdf" ) );
4648 if( m_aContext
.ForcePDFAction
&& bTargetHasPDFExtension
)
4651 //prepare the URL, if relative or not
4652 INetProtocol eBaseProtocol
= aDocumentURL
.GetProtocol();
4653 //queue the string common to all types of actions
4654 aLine
.append( "/A<</Type/Action/S");
4655 if( bIsUNCPath
) // handle Win UNC paths
4657 aLine
.append( "/Launch/Win<</F" );
4658 // INetURLObject is not good with UNC paths, use original path
4659 appendLiteralStringEncrypt( rLink
.m_aURL
, rLink
.m_nObject
, aLine
, osl_getThreadTextEncoding() );
4660 aLine
.append( ">>" );
4664 bool bSetRelative
= false;
4665 bool bFileSpec
= false;
4666 //check if relative file link is requested and if the protocol is 'file://'
4667 if( m_aContext
.RelFsys
&& eBaseProtocol
== eTargetProtocol
&& eTargetProtocol
== INET_PROT_FILE
)
4668 bSetRelative
= true;
4670 OUString aFragment
= aTargetURL
.GetMark( INetURLObject::NO_DECODE
/*DECODE_WITH_CHARSET*/ ); //fragment as is,
4671 if( nSetGoToRMode
== 0 )
4673 switch( m_aContext
.DefaultLinkAction
)
4676 case PDFWriter::URIAction
:
4677 case PDFWriter::URIActionDestination
:
4678 aLine
.append( "/URI/URI" );
4680 case PDFWriter::LaunchAction
:
4682 // if a launch action is requested and the hyperlink target has a fragment
4683 // and the target file does not have a pdf extension, or it's not a 'file:://' protocol
4684 // then force the uri action on it
4685 // This code will permit the correct opening of application on web pages, the one that
4686 // normally have fragments (but I may be wrong...)
4687 // and will force the use of URI when the protocol is not file://
4688 if( (!aFragment
.isEmpty() && !bTargetHasPDFExtension
) ||
4689 eTargetProtocol
!= INET_PROT_FILE
)
4691 aLine
.append( "/URI/URI" );
4695 aLine
.append( "/Launch/F" );
4701 //fragment are encoded in the same way as in the named destination processing
4705 OUString aURLNoMark
= aTargetURL
.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET
);
4706 aLine
.append("/GoToR");
4709 appendLiteralStringEncrypt( bSetRelative
? INetURLObject::GetRelURL( m_aContext
.BaseURL
, aURLNoMark
,
4710 INetURLObject::WAS_ENCODED
,
4711 INetURLObject::DECODE_WITH_CHARSET
) :
4712 aURLNoMark
, rLink
.m_nObject
, aLine
, osl_getThreadTextEncoding() );
4713 if( !aFragment
.isEmpty() )
4715 aLine
.append("/D/");
4716 appendDestinationName( aFragment
, aLine
);
4721 // change the fragment to accommodate the bookmark (only if the file extension is PDF and
4722 // the requested action is of the correct type)
4723 if(m_aContext
.DefaultLinkAction
== PDFWriter::URIActionDestination
&&
4724 bTargetHasPDFExtension
&& !aFragment
.isEmpty() )
4726 OStringBuffer
aLineLoc( 1024 );
4727 appendDestinationName( aFragment
, aLineLoc
);
4728 //substitute the fragment
4729 aTargetURL
.SetMark( aLineLoc
.getStr() );
4731 OUString aURL
= aTargetURL
.GetMainURL( bFileSpec
? INetURLObject::DECODE_WITH_CHARSET
: INetURLObject::NO_DECODE
);
4732 appendLiteralStringEncrypt(bSetRelative
? INetURLObject::GetRelURL( m_aContext
.BaseURL
, aURL
,
4733 INetURLObject::WAS_ENCODED
,
4734 bFileSpec
? INetURLObject::DECODE_WITH_CHARSET
: INetURLObject::NO_DECODE
4736 aURL
, rLink
.m_nObject
, aLine
, osl_getThreadTextEncoding() );
4740 aLine
.append( ">>\n" );
4742 if( rLink
.m_nStructParent
> 0 )
4744 aLine
.append( "/StructParent " );
4745 aLine
.append( rLink
.m_nStructParent
);
4747 aLine
.append( ">>\nendobj\n\n" );
4748 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4754 bool PDFWriterImpl::emitNoteAnnotations()
4756 // emit note annotations
4757 int nAnnots
= m_aNotes
.size();
4758 for( int i
= 0; i
< nAnnots
; i
++ )
4760 const PDFNoteEntry
& rNote
= m_aNotes
[i
];
4761 if( ! updateObject( rNote
.m_nObject
) )
4764 OStringBuffer
aLine( 1024 );
4765 aLine
.append( rNote
.m_nObject
);
4766 aLine
.append( " 0 obj\n" );
4767 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4768 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4769 aLine
.append( "<</Type/Annot" );
4771 aLine
.append( "/F 4" );
4772 aLine
.append( "/Subtype/Text/Rect[" );
4774 appendFixedInt( rNote
.m_aRect
.Left(), aLine
);
4775 aLine
.append( ' ' );
4776 appendFixedInt( rNote
.m_aRect
.Top(), aLine
);
4777 aLine
.append( ' ' );
4778 appendFixedInt( rNote
.m_aRect
.Right(), aLine
);
4779 aLine
.append( ' ' );
4780 appendFixedInt( rNote
.m_aRect
.Bottom(), aLine
);
4781 aLine
.append( "]" );
4783 // contents of the note (type text string)
4784 aLine
.append( "/Contents\n" );
4785 appendUnicodeTextStringEncrypt( rNote
.m_aContents
.Contents
, rNote
.m_nObject
, aLine
);
4786 aLine
.append( "\n" );
4789 if( rNote
.m_aContents
.Title
.Len() )
4791 aLine
.append( "/T" );
4792 appendUnicodeTextStringEncrypt( rNote
.m_aContents
.Title
, rNote
.m_nObject
, aLine
);
4793 aLine
.append( "\n" );
4796 aLine
.append( ">>\nendobj\n\n" );
4797 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
4802 Font
PDFWriterImpl::replaceFont( const Font
& rControlFont
, const Font
& rAppSetFont
)
4804 bool bAdjustSize
= false;
4806 Font
aFont( rControlFont
);
4807 if( ! aFont
.GetName().Len() )
4809 aFont
= rAppSetFont
;
4810 if( rControlFont
.GetHeight() )
4811 aFont
.SetSize( Size( 0, rControlFont
.GetHeight() ) );
4814 if( rControlFont
.GetItalic() != ITALIC_DONTKNOW
)
4815 aFont
.SetItalic( rControlFont
.GetItalic() );
4816 if( rControlFont
.GetWeight() != WEIGHT_DONTKNOW
)
4817 aFont
.SetWeight( rControlFont
.GetWeight() );
4819 else if( ! aFont
.GetHeight() )
4821 aFont
.SetSize( rAppSetFont
.GetSize() );
4826 Size aFontSize
= aFont
.GetSize();
4827 OutputDevice
* pDefDev
= Application::GetDefaultDevice();
4828 aFontSize
= OutputDevice::LogicToLogic( aFontSize
, pDefDev
->GetMapMode(), getMapMode() );
4829 aFont
.SetSize( aFontSize
);
4834 sal_Int32
PDFWriterImpl::getBestBuiltinFont( const Font
& rFont
)
4836 sal_Int32 nBest
= 4; // default to Helvetica
4837 OUString
aFontName( rFont
.GetName() );
4838 aFontName
= aFontName
.toAsciiLowerCase();
4840 if( aFontName
.indexOf( "times" ) != -1 )
4842 else if( aFontName
.indexOf( "courier" ) != -1 )
4844 else if( aFontName
.indexOf( "dingbats" ) != -1 )
4846 else if( aFontName
.indexOf( "symbol" ) != -1 )
4850 if( rFont
.GetItalic() == ITALIC_OBLIQUE
|| rFont
.GetItalic() == ITALIC_NORMAL
)
4852 if( rFont
.GetWeight() > WEIGHT_MEDIUM
)
4856 if( m_aBuiltinFontToObjectMap
.find( nBest
) == m_aBuiltinFontToObjectMap
.end() )
4857 m_aBuiltinFontToObjectMap
[ nBest
] = createObject();
4862 static inline const Color
& replaceColor( const Color
& rCol1
, const Color
& rCol2
)
4864 return (rCol1
== Color( COL_TRANSPARENT
)) ? rCol2
: rCol1
;
4867 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget
& rButton
, const PDFWriter::PushButtonWidget
& rWidget
)
4869 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
4871 // save graphics state
4872 push( sal::static_int_cast
<sal_uInt16
>(~0U) );
4874 // transform relative to control's coordinates since an
4875 // appearance stream is a form XObject
4876 // this relies on the m_aRect member of rButton NOT already being transformed
4877 // to default user space
4878 if( rWidget
.Background
|| rWidget
.Border
)
4880 setLineColor( rWidget
.Border
? replaceColor( rWidget
.BorderColor
, rSettings
.GetLightColor() ) : Color( COL_TRANSPARENT
) );
4881 setFillColor( rWidget
.Background
? replaceColor( rWidget
.BackgroundColor
, rSettings
.GetDialogColor() ) : Color( COL_TRANSPARENT
) );
4882 drawRectangle( rWidget
.Location
);
4884 // prepare font to use
4885 Font aFont
= replaceFont( rWidget
.TextFont
, rSettings
.GetPushButtonFont() );
4887 setTextColor( replaceColor( rWidget
.TextColor
, rSettings
.GetButtonTextColor() ) );
4889 drawText( rButton
.m_aRect
, rButton
.m_aText
, rButton
.m_nTextStyle
);
4891 // create DA string while local mapmode is still in place
4892 // (that is before endRedirect())
4893 OStringBuffer
aDA( 256 );
4894 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetButtonTextColor() ), aDA
);
4895 Font
aDummyFont( String( "Helvetica" ), aFont
.GetSize() );
4896 sal_Int32 nDummyBuiltin
= getBestBuiltinFont( aDummyFont
);
4898 aDA
.append( m_aBuiltinFonts
[nDummyBuiltin
].getNameObject() );
4900 m_aPages
[m_nCurrentPage
].appendMappedLength( sal_Int32( aFont
.GetHeight() ), aDA
);
4901 aDA
.append( " Tf" );
4902 rButton
.m_aDAString
= aDA
.makeStringAndClear();
4906 rButton
.m_aAppearances
[ "N" ][ "Standard" ] = new SvMemoryStream();
4908 /* seems like a bad hack but at least works in both AR5 and 6:
4909 we draw the button ourselves and tell AR
4910 the button would be totally transparent with no text
4912 One would expect that simply setting a normal appearance
4913 should suffice, but no, as soon as the user actually presses
4914 the button and an action is tied to it (gasp! a button that
4915 does something) the appearance gets replaced by some crap that AR
4916 creates on the fly even if no DA or MK is given. On AR6 at least
4917 the DA and MK work as expected, but on AR5 this creates a region
4918 filled with the background color but nor text. Urgh.
4920 rButton
.m_aMKDict
= "/BC [] /BG [] /CA";
4921 rButton
.m_aMKDictCAString
= "";
4924 Font
PDFWriterImpl::drawFieldBorder( PDFWidget
& rIntern
,
4925 const PDFWriter::AnyWidget
& rWidget
,
4926 const StyleSettings
& rSettings
)
4928 Font aFont
= replaceFont( rWidget
.TextFont
, rSettings
.GetFieldFont() );
4930 if( rWidget
.Background
|| rWidget
.Border
)
4932 if( rWidget
.Border
&& rWidget
.BorderColor
== Color( COL_TRANSPARENT
) )
4934 sal_Int32 nDelta
= getReferenceDevice()->ImplGetDPIX() / 500;
4937 setLineColor( Color( COL_TRANSPARENT
) );
4938 Rectangle aRect
= rIntern
.m_aRect
;
4939 setFillColor( rSettings
.GetLightBorderColor() );
4940 drawRectangle( aRect
);
4941 aRect
.Left() += nDelta
; aRect
.Top() += nDelta
;
4942 aRect
.Right() -= nDelta
; aRect
.Bottom() -= nDelta
;
4943 setFillColor( rSettings
.GetFieldColor() );
4944 drawRectangle( aRect
);
4945 setFillColor( rSettings
.GetLightColor() );
4946 drawRectangle( Rectangle( Point( aRect
.Left(), aRect
.Bottom()-nDelta
), aRect
.BottomRight() ) );
4947 drawRectangle( Rectangle( Point( aRect
.Right()-nDelta
, aRect
.Top() ), aRect
.BottomRight() ) );
4948 setFillColor( rSettings
.GetDarkShadowColor() );
4949 drawRectangle( Rectangle( aRect
.TopLeft(), Point( aRect
.Left()+nDelta
, aRect
.Bottom() ) ) );
4950 drawRectangle( Rectangle( aRect
.TopLeft(), Point( aRect
.Right(), aRect
.Top()+nDelta
) ) );
4954 setLineColor( rWidget
.Border
? replaceColor( rWidget
.BorderColor
, rSettings
.GetShadowColor() ) : Color( COL_TRANSPARENT
) );
4955 setFillColor( rWidget
.Background
? replaceColor( rWidget
.BackgroundColor
, rSettings
.GetFieldColor() ) : Color( COL_TRANSPARENT
) );
4956 drawRectangle( rIntern
.m_aRect
);
4959 if( rWidget
.Border
)
4961 // adjust edit area accounting for border
4962 sal_Int32 nDelta
= aFont
.GetHeight()/4;
4965 rIntern
.m_aRect
.Left() += nDelta
;
4966 rIntern
.m_aRect
.Top() += nDelta
;
4967 rIntern
.m_aRect
.Right() -= nDelta
;
4968 rIntern
.m_aRect
.Bottom()-= nDelta
;
4974 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget
& rEdit
, const PDFWriter::EditWidget
& rWidget
)
4976 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
4977 SvMemoryStream
* pEditStream
= new SvMemoryStream( 1024, 1024 );
4979 push( sal::static_int_cast
<sal_uInt16
>(~0U) );
4981 // prepare font to use, draw field border
4982 Font aFont
= drawFieldBorder( rEdit
, rWidget
, rSettings
);
4983 sal_Int32 nBest
= m_aContext
.FieldsUseSystemFonts
? getSystemFont( aFont
): getBestBuiltinFont( aFont
);
4985 // prepare DA string
4986 OStringBuffer
aDA( 32 );
4987 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetFieldTextColor() ), aDA
);
4989 if( m_aContext
.FieldsUseSystemFonts
)
4992 aDA
.append( nBest
);
4994 OStringBuffer
aDR( 32 );
4995 aDR
.append( "/Font " );
4996 aDR
.append( getFontDictObject() );
4997 aDR
.append( " 0 R" );
4998 rEdit
.m_aDRDict
= aDR
.makeStringAndClear();
5001 aDA
.append( m_aBuiltinFonts
[nBest
].getNameObject() );
5003 m_aPages
[ m_nCurrentPage
].appendMappedLength( sal_Int32( aFont
.GetHeight() ), aDA
);
5004 aDA
.append( " Tf" );
5006 /* create an empty appearance stream, let the viewer create
5007 the appearance at runtime. This is because AR5 seems to
5008 paint the widget appearance always, and a dynamically created
5009 appearance on top of it. AR6 is well behaved in that regard, so
5010 that behaviour seems to be a bug. Anyway this empty appearance
5011 relies on /NeedAppearances in the AcroForm dictionary set to "true"
5013 beginRedirect( pEditStream
, rEdit
.m_aRect
);
5014 OStringBuffer
aAppearance( 32 );
5015 aAppearance
.append( "/Tx BMC\nEMC\n" );
5016 writeBuffer( aAppearance
.getStr(), aAppearance
.getLength() );
5021 rEdit
.m_aAppearances
[ "N" ][ "Standard" ] = pEditStream
;
5023 rEdit
.m_aDAString
= aDA
.makeStringAndClear();
5026 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget
& rBox
, const PDFWriter::ListBoxWidget
& rWidget
)
5028 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
5029 SvMemoryStream
* pListBoxStream
= new SvMemoryStream( 1024, 1024 );
5031 push( sal::static_int_cast
<sal_uInt16
>(~0U) );
5033 // prepare font to use, draw field border
5034 Font aFont
= drawFieldBorder( rBox
, rWidget
, rSettings
);
5035 sal_Int32 nBest
= m_aContext
.FieldsUseSystemFonts
? getSystemFont( aFont
): getBestBuiltinFont( aFont
);
5037 beginRedirect( pListBoxStream
, rBox
.m_aRect
);
5038 OStringBuffer
aAppearance( 64 );
5040 setLineColor( Color( COL_TRANSPARENT
) );
5041 setFillColor( replaceColor( rWidget
.BackgroundColor
, rSettings
.GetFieldColor() ) );
5042 drawRectangle( rBox
.m_aRect
);
5044 // empty appearance, see createDefaultEditAppearance for reference
5045 aAppearance
.append( "/Tx BMC\nEMC\n" );
5046 writeBuffer( aAppearance
.getStr(), aAppearance
.getLength() );
5051 rBox
.m_aAppearances
[ "N" ][ "Standard" ] = pListBoxStream
;
5053 // prepare DA string
5054 OStringBuffer
aDA( 256 );
5055 // prepare DA string
5056 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetFieldTextColor() ), aDA
);
5058 if( m_aContext
.FieldsUseSystemFonts
)
5061 aDA
.append( nBest
);
5063 OStringBuffer
aDR( 32 );
5064 aDR
.append( "/Font " );
5065 aDR
.append( getFontDictObject() );
5066 aDR
.append( " 0 R" );
5067 rBox
.m_aDRDict
= aDR
.makeStringAndClear();
5070 aDA
.append( m_aBuiltinFonts
[nBest
].getNameObject() );
5072 m_aPages
[ m_nCurrentPage
].appendMappedLength( sal_Int32( aFont
.GetHeight() ), aDA
);
5073 aDA
.append( " Tf" );
5074 rBox
.m_aDAString
= aDA
.makeStringAndClear();
5077 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget
& rBox
, const PDFWriter::CheckBoxWidget
& rWidget
)
5079 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
5081 // save graphics state
5082 push( sal::static_int_cast
<sal_uInt16
>(~0U) );
5084 if( rWidget
.Background
|| rWidget
.Border
)
5086 setLineColor( rWidget
.Border
? replaceColor( rWidget
.BorderColor
, rSettings
.GetCheckedColor() ) : Color( COL_TRANSPARENT
) );
5087 setFillColor( rWidget
.Background
? replaceColor( rWidget
.BackgroundColor
, rSettings
.GetFieldColor() ) : Color( COL_TRANSPARENT
) );
5088 drawRectangle( rBox
.m_aRect
);
5091 Font aFont
= replaceFont( rWidget
.TextFont
, rSettings
.GetRadioCheckFont() );
5093 Size aFontSize
= aFont
.GetSize();
5094 if( aFontSize
.Height() > rBox
.m_aRect
.GetHeight() )
5095 aFontSize
.Height() = rBox
.m_aRect
.GetHeight();
5096 sal_Int32 nDelta
= aFontSize
.Height()/10;
5100 Rectangle aCheckRect
, aTextRect
;
5101 if( rWidget
.ButtonIsLeft
)
5103 aCheckRect
.Left() = rBox
.m_aRect
.Left() + nDelta
;
5104 aCheckRect
.Top() = rBox
.m_aRect
.Top() + (rBox
.m_aRect
.GetHeight()-aFontSize
.Height())/2;
5105 aCheckRect
.Right() = aCheckRect
.Left() + aFontSize
.Height();
5106 aCheckRect
.Bottom() = aCheckRect
.Top() + aFontSize
.Height();
5108 // #i74206# handle small controls without text area
5109 while( aCheckRect
.GetWidth() > rBox
.m_aRect
.GetWidth() && aCheckRect
.GetWidth() > nDelta
)
5111 aCheckRect
.Right() -= nDelta
;
5112 aCheckRect
.Top() += nDelta
/2;
5113 aCheckRect
.Bottom() -= nDelta
- (nDelta
/2);
5116 aTextRect
.Left() = rBox
.m_aRect
.Left() + aCheckRect
.GetWidth()+5*nDelta
;
5117 aTextRect
.Top() = rBox
.m_aRect
.Top();
5118 aTextRect
.Right() = aTextRect
.Left() + rBox
.m_aRect
.GetWidth() - aCheckRect
.GetWidth()-6*nDelta
;
5119 aTextRect
.Bottom() = rBox
.m_aRect
.Bottom();
5123 aCheckRect
.Left() = rBox
.m_aRect
.Right() - nDelta
- aFontSize
.Height();
5124 aCheckRect
.Top() = rBox
.m_aRect
.Top() + (rBox
.m_aRect
.GetHeight()-aFontSize
.Height())/2;
5125 aCheckRect
.Right() = aCheckRect
.Left() + aFontSize
.Height();
5126 aCheckRect
.Bottom() = aCheckRect
.Top() + aFontSize
.Height();
5128 // #i74206# handle small controls without text area
5129 while( aCheckRect
.GetWidth() > rBox
.m_aRect
.GetWidth() && aCheckRect
.GetWidth() > nDelta
)
5131 aCheckRect
.Left() += nDelta
;
5132 aCheckRect
.Top() += nDelta
/2;
5133 aCheckRect
.Bottom() -= nDelta
- (nDelta
/2);
5136 aTextRect
.Left() = rBox
.m_aRect
.Left();
5137 aTextRect
.Top() = rBox
.m_aRect
.Top();
5138 aTextRect
.Right() = aTextRect
.Left() + rBox
.m_aRect
.GetWidth() - aCheckRect
.GetWidth()-6*nDelta
;
5139 aTextRect
.Bottom() = rBox
.m_aRect
.Bottom();
5141 setLineColor( Color( COL_BLACK
) );
5142 setFillColor( Color( COL_TRANSPARENT
) );
5143 OStringBuffer
aLW( 32 );
5145 m_aPages
[m_nCurrentPage
].appendMappedLength( nDelta
, aLW
);
5146 aLW
.append( " w " );
5147 writeBuffer( aLW
.getStr(), aLW
.getLength() );
5148 drawRectangle( aCheckRect
);
5149 writeBuffer( " Q\n", 3 );
5150 setTextColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ) );
5151 drawText( aTextRect
, rBox
.m_aText
, rBox
.m_nTextStyle
);
5155 OStringBuffer
aDA( 256 );
5156 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ), aDA
);
5157 sal_Int32 nBest
= getBestBuiltinFont( Font( String( "ZapfDingbats" ), aFont
.GetSize() ) );
5159 aDA
.append( m_aBuiltinFonts
[nBest
].getNameObject() );
5160 aDA
.append( " 0 Tf" );
5161 rBox
.m_aDAString
= aDA
.makeStringAndClear();
5162 rBox
.m_aMKDict
= "/CA";
5163 rBox
.m_aMKDictCAString
= "8";
5164 rBox
.m_aRect
= aCheckRect
;
5166 // create appearance streams
5167 sal_Char cMark
= '8';
5168 sal_Int32 nCharXOffset
= 1000-m_aBuiltinFonts
[13].m_aWidths
[sal_Int32(cMark
)];
5169 nCharXOffset
*= aCheckRect
.GetHeight();
5170 nCharXOffset
/= 2000;
5171 sal_Int32 nCharYOffset
= 1000-
5172 (m_aBuiltinFonts
[13].m_nAscent
+m_aBuiltinFonts
[13].m_nDescent
); // descent is negative
5173 nCharYOffset
*= aCheckRect
.GetHeight();
5174 nCharYOffset
/= 2000;
5176 SvMemoryStream
* pCheckStream
= new SvMemoryStream( 256, 256 );
5177 beginRedirect( pCheckStream
, aCheckRect
);
5178 aDA
.append( "/Tx BMC\nq BT\n" );
5179 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ), aDA
);
5181 aDA
.append( m_aBuiltinFonts
[nBest
].getNameObject() );
5183 m_aPages
[ m_nCurrentPage
].appendMappedLength( sal_Int32( aCheckRect
.GetHeight() ), aDA
);
5184 aDA
.append( " Tf\n" );
5185 m_aPages
[ m_nCurrentPage
].appendMappedLength( nCharXOffset
, aDA
);
5187 m_aPages
[ m_nCurrentPage
].appendMappedLength( nCharYOffset
, aDA
);
5188 aDA
.append( " Td (" );
5189 aDA
.append( cMark
);
5190 aDA
.append( ") Tj\nET\nQ\nEMC\n" );
5191 writeBuffer( aDA
.getStr(), aDA
.getLength() );
5193 rBox
.m_aAppearances
[ "N" ][ "Yes" ] = pCheckStream
;
5195 SvMemoryStream
* pUncheckStream
= new SvMemoryStream( 256, 256 );
5196 beginRedirect( pUncheckStream
, aCheckRect
);
5197 writeBuffer( "/Tx BMC\nEMC\n", 12 );
5199 rBox
.m_aAppearances
[ "N" ][ "Off" ] = pUncheckStream
;
5202 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget
& rBox
, const PDFWriter::RadioButtonWidget
& rWidget
)
5204 const StyleSettings
& rSettings
= Application::GetSettings().GetStyleSettings();
5206 // save graphics state
5207 push( sal::static_int_cast
<sal_uInt16
>(~0U) );
5209 if( rWidget
.Background
|| rWidget
.Border
)
5211 setLineColor( rWidget
.Border
? replaceColor( rWidget
.BorderColor
, rSettings
.GetCheckedColor() ) : Color( COL_TRANSPARENT
) );
5212 setFillColor( rWidget
.Background
? replaceColor( rWidget
.BackgroundColor
, rSettings
.GetFieldColor() ) : Color( COL_TRANSPARENT
) );
5213 drawRectangle( rBox
.m_aRect
);
5216 Font aFont
= replaceFont( rWidget
.TextFont
, rSettings
.GetRadioCheckFont() );
5218 Size aFontSize
= aFont
.GetSize();
5219 if( aFontSize
.Height() > rBox
.m_aRect
.GetHeight() )
5220 aFontSize
.Height() = rBox
.m_aRect
.GetHeight();
5221 sal_Int32 nDelta
= aFontSize
.Height()/10;
5225 Rectangle aCheckRect
, aTextRect
;
5226 if( rWidget
.ButtonIsLeft
)
5228 aCheckRect
.Left() = rBox
.m_aRect
.Left() + nDelta
;
5229 aCheckRect
.Top() = rBox
.m_aRect
.Top() + (rBox
.m_aRect
.GetHeight()-aFontSize
.Height())/2;
5230 aCheckRect
.Right() = aCheckRect
.Left() + aFontSize
.Height();
5231 aCheckRect
.Bottom() = aCheckRect
.Top() + aFontSize
.Height();
5233 // #i74206# handle small controls without text area
5234 while( aCheckRect
.GetWidth() > rBox
.m_aRect
.GetWidth() && aCheckRect
.GetWidth() > nDelta
)
5236 aCheckRect
.Right() -= nDelta
;
5237 aCheckRect
.Top() += nDelta
/2;
5238 aCheckRect
.Bottom() -= nDelta
- (nDelta
/2);
5241 aTextRect
.Left() = rBox
.m_aRect
.Left() + aCheckRect
.GetWidth()+5*nDelta
;
5242 aTextRect
.Top() = rBox
.m_aRect
.Top();
5243 aTextRect
.Right() = aTextRect
.Left() + rBox
.m_aRect
.GetWidth() - aCheckRect
.GetWidth()-6*nDelta
;
5244 aTextRect
.Bottom() = rBox
.m_aRect
.Bottom();
5248 aCheckRect
.Left() = rBox
.m_aRect
.Right() - nDelta
- aFontSize
.Height();
5249 aCheckRect
.Top() = rBox
.m_aRect
.Top() + (rBox
.m_aRect
.GetHeight()-aFontSize
.Height())/2;
5250 aCheckRect
.Right() = aCheckRect
.Left() + aFontSize
.Height();
5251 aCheckRect
.Bottom() = aCheckRect
.Top() + aFontSize
.Height();
5253 // #i74206# handle small controls without text area
5254 while( aCheckRect
.GetWidth() > rBox
.m_aRect
.GetWidth() && aCheckRect
.GetWidth() > nDelta
)
5256 aCheckRect
.Left() += nDelta
;
5257 aCheckRect
.Top() += nDelta
/2;
5258 aCheckRect
.Bottom() -= nDelta
- (nDelta
/2);
5261 aTextRect
.Left() = rBox
.m_aRect
.Left();
5262 aTextRect
.Top() = rBox
.m_aRect
.Top();
5263 aTextRect
.Right() = aTextRect
.Left() + rBox
.m_aRect
.GetWidth() - aCheckRect
.GetWidth()-6*nDelta
;
5264 aTextRect
.Bottom() = rBox
.m_aRect
.Bottom();
5266 setLineColor( Color( COL_BLACK
) );
5267 setFillColor( Color( COL_TRANSPARENT
) );
5268 OStringBuffer
aLW( 32 );
5270 m_aPages
[ m_nCurrentPage
].appendMappedLength( nDelta
, aLW
);
5271 aLW
.append( " w " );
5272 writeBuffer( aLW
.getStr(), aLW
.getLength() );
5273 drawEllipse( aCheckRect
);
5274 writeBuffer( " Q\n", 3 );
5275 setTextColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ) );
5276 drawText( aTextRect
, rBox
.m_aText
, rBox
.m_nTextStyle
);
5280 OStringBuffer
aDA( 256 );
5281 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ), aDA
);
5282 sal_Int32 nBest
= getBestBuiltinFont( Font( String( "ZapfDingbats" ), aFont
.GetSize() ) );
5284 aDA
.append( m_aBuiltinFonts
[nBest
].getNameObject() );
5285 aDA
.append( " 0 Tf" );
5286 rBox
.m_aDAString
= aDA
.makeStringAndClear();
5287 //to encrypt this (el)
5288 rBox
.m_aMKDict
= "/CA";
5289 //after this assignement, to m_aMKDic cannot be added anything
5290 rBox
.m_aMKDictCAString
= "l";
5292 rBox
.m_aRect
= aCheckRect
;
5294 // create appearance streams
5295 push( sal::static_int_cast
<sal_uInt16
>(~0U) );
5296 SvMemoryStream
* pCheckStream
= new SvMemoryStream( 256, 256 );
5298 beginRedirect( pCheckStream
, aCheckRect
);
5299 aDA
.append( "/Tx BMC\nq BT\n" );
5300 appendNonStrokingColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ), aDA
);
5302 aDA
.append( m_aBuiltinFonts
[nBest
].getNameObject() );
5304 m_aPages
[m_nCurrentPage
].appendMappedLength( sal_Int32( aCheckRect
.GetHeight() ), aDA
);
5305 aDA
.append( " Tf\n0 0 Td\nET\nQ\n" );
5306 writeBuffer( aDA
.getStr(), aDA
.getLength() );
5307 setFillColor( replaceColor( rWidget
.TextColor
, rSettings
.GetRadioCheckTextColor() ) );
5308 setLineColor( Color( COL_TRANSPARENT
) );
5309 aCheckRect
.Left() += 3*nDelta
;
5310 aCheckRect
.Top() += 3*nDelta
;
5311 aCheckRect
.Bottom() -= 3*nDelta
;
5312 aCheckRect
.Right() -= 3*nDelta
;
5313 drawEllipse( aCheckRect
);
5314 writeBuffer( "\nEMC\n", 5 );
5318 rBox
.m_aAppearances
[ "N" ][ "Yes" ] = pCheckStream
;
5320 SvMemoryStream
* pUncheckStream
= new SvMemoryStream( 256, 256 );
5321 beginRedirect( pUncheckStream
, aCheckRect
);
5322 writeBuffer( "/Tx BMC\nEMC\n", 12 );
5324 rBox
.m_aAppearances
[ "N" ][ "Off" ] = pUncheckStream
;
5327 bool PDFWriterImpl::emitAppearances( PDFWidget
& rWidget
, OStringBuffer
& rAnnotDict
)
5330 // TODO: check and insert default streams
5331 OString aStandardAppearance
;
5332 switch( rWidget
.m_eType
)
5334 case PDFWriter::CheckBox
:
5335 aStandardAppearance
= OUStringToOString( rWidget
.m_aValue
, RTL_TEXTENCODING_ASCII_US
);
5341 if( rWidget
.m_aAppearances
.size() )
5343 rAnnotDict
.append( "/AP<<\n" );
5344 for( PDFAppearanceMap::iterator dict_it
= rWidget
.m_aAppearances
.begin(); dict_it
!= rWidget
.m_aAppearances
.end(); ++dict_it
)
5346 rAnnotDict
.append( "/" );
5347 rAnnotDict
.append( dict_it
->first
);
5348 bool bUseSubDict
= (dict_it
->second
.size() > 1);
5349 rAnnotDict
.append( bUseSubDict
? "<<" : " " );
5351 for( PDFAppearanceStreams::const_iterator stream_it
= dict_it
->second
.begin();
5352 stream_it
!= dict_it
->second
.end(); ++stream_it
)
5354 SvMemoryStream
* pApppearanceStream
= stream_it
->second
;
5355 dict_it
->second
[ stream_it
->first
] = NULL
;
5357 bool bDeflate
= compressStream( pApppearanceStream
);
5359 pApppearanceStream
->Seek( STREAM_SEEK_TO_END
);
5360 sal_Int64 nStreamLen
= pApppearanceStream
->Tell();
5361 pApppearanceStream
->Seek( STREAM_SEEK_TO_BEGIN
);
5362 sal_Int32 nObject
= createObject();
5363 CHECK_RETURN( updateObject( nObject
) );
5364 #if OSL_DEBUG_LEVEL > 1
5365 emitComment( "PDFWriterImpl::emitAppearances" );
5367 OStringBuffer aLine
;
5368 aLine
.append( nObject
);
5370 aLine
.append( " 0 obj\n"
5374 appendFixedInt( rWidget
.m_aRect
.GetWidth()-1, aLine
);
5375 aLine
.append( " " );
5376 appendFixedInt( rWidget
.m_aRect
.GetHeight()-1, aLine
);
5379 aLine
.append( getResourceDictObj() );
5380 aLine
.append( " 0 R\n"
5382 aLine
.append( nStreamLen
);
5383 aLine
.append( "\n" );
5385 aLine
.append( "/Filter/FlateDecode\n" );
5386 aLine
.append( ">>\nstream\n" );
5387 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
5388 checkAndEnableStreamEncryption( nObject
);
5389 CHECK_RETURN( writeBuffer( pApppearanceStream
->GetData(), nStreamLen
) );
5390 disableStreamEncryption();
5391 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
5395 rAnnotDict
.append( " /" );
5396 rAnnotDict
.append( stream_it
->first
);
5397 rAnnotDict
.append( " " );
5399 rAnnotDict
.append( nObject
);
5400 rAnnotDict
.append( " 0 R" );
5402 delete pApppearanceStream
;
5405 rAnnotDict
.append( bUseSubDict
? ">>\n" : "\n" );
5407 rAnnotDict
.append( ">>\n" );
5408 if( !aStandardAppearance
.isEmpty() )
5410 rAnnotDict
.append( "/AS /" );
5411 rAnnotDict
.append( aStandardAppearance
);
5412 rAnnotDict
.append( "\n" );
5419 bool PDFWriterImpl::emitWidgetAnnotations()
5421 ensureUniqueRadioOnValues();
5423 int nAnnots
= m_aWidgets
.size();
5424 for( int a
= 0; a
< nAnnots
; a
++ )
5426 PDFWidget
& rWidget
= m_aWidgets
[a
];
5428 OStringBuffer
aLine( 1024 );
5429 OStringBuffer
aValue( 256 );
5430 aLine
.append( rWidget
.m_nObject
);
5431 aLine
.append( " 0 obj\n"
5433 if( rWidget
.m_eType
!= PDFWriter::Hierarchy
)
5435 // emit widget annotation only for terminal fields
5436 if( rWidget
.m_aKids
.empty() )
5440 aLine
.append( "/Type/Annot/Subtype/Widget/F " );
5442 if (rWidget
.m_eType
== PDFWriter::Signature
)
5444 aLine
.append( "132\n" ); // Print & Locked
5449 aLine
.append( "4\n" );
5453 aLine
.append("/Rect[" );
5454 appendFixedInt( rWidget
.m_aRect
.Left()-iRectMargin
, aLine
);
5455 aLine
.append( ' ' );
5456 appendFixedInt( rWidget
.m_aRect
.Top()+iRectMargin
, aLine
);
5457 aLine
.append( ' ' );
5458 appendFixedInt( rWidget
.m_aRect
.Right()+iRectMargin
, aLine
);
5459 aLine
.append( ' ' );
5460 appendFixedInt( rWidget
.m_aRect
.Bottom()-iRectMargin
, aLine
);
5461 aLine
.append( "]\n" );
5463 aLine
.append( "/FT/" );
5464 switch( rWidget
.m_eType
)
5466 case PDFWriter::RadioButton
:
5467 case PDFWriter::CheckBox
:
5468 // for radio buttons only the RadioButton field, not the
5469 // CheckBox children should have a value, else acrobat reader
5470 // does not always check the right button
5471 // of course real check boxes (not belonging to a radio group)
5472 // need their values, too
5473 if( rWidget
.m_eType
== PDFWriter::RadioButton
|| rWidget
.m_nRadioGroup
< 0 )
5475 aValue
.append( "/" );
5476 // check for radio group with all buttons unpressed
5477 if( rWidget
.m_aValue
.isEmpty() )
5478 aValue
.append( "Off" );
5480 appendName( rWidget
.m_aValue
, aValue
);
5482 case PDFWriter::PushButton
:
5483 aLine
.append( "Btn" );
5485 case PDFWriter::ListBox
:
5486 if( rWidget
.m_nFlags
& 0x200000 ) // multiselect
5488 aValue
.append( "[" );
5489 for( unsigned int i
= 0; i
< rWidget
.m_aSelectedEntries
.size(); i
++ )
5491 sal_Int32 nEntry
= rWidget
.m_aSelectedEntries
[i
];
5492 if( nEntry
>= 0 && nEntry
< sal_Int32(rWidget
.m_aListEntries
.size()) )
5493 appendUnicodeTextStringEncrypt( rWidget
.m_aListEntries
[ nEntry
], rWidget
.m_nObject
, aValue
);
5495 aValue
.append( "]" );
5497 else if( rWidget
.m_aSelectedEntries
.size() > 0 &&
5498 rWidget
.m_aSelectedEntries
[0] >= 0 &&
5499 rWidget
.m_aSelectedEntries
[0] < sal_Int32(rWidget
.m_aListEntries
.size()) )
5501 appendUnicodeTextStringEncrypt( rWidget
.m_aListEntries
[ rWidget
.m_aSelectedEntries
[0] ], rWidget
.m_nObject
, aValue
);
5504 appendUnicodeTextStringEncrypt( OUString(), rWidget
.m_nObject
, aValue
);
5505 aLine
.append( "Ch" );
5507 case PDFWriter::ComboBox
:
5508 appendUnicodeTextStringEncrypt( rWidget
.m_aValue
, rWidget
.m_nObject
, aValue
);
5509 aLine
.append( "Ch" );
5511 case PDFWriter::Edit
:
5512 aLine
.append( "Tx" );
5513 appendUnicodeTextStringEncrypt( rWidget
.m_aValue
, rWidget
.m_nObject
, aValue
);
5515 case PDFWriter::Signature
:
5516 aLine
.append( "Sig" );
5517 aValue
.append(OUStringToOString(rWidget
.m_aValue
, RTL_TEXTENCODING_ASCII_US
));
5519 case PDFWriter::Hierarchy
: // make the compiler happy
5522 aLine
.append( "\n" );
5523 aLine
.append( "/P " );
5524 aLine
.append( m_aPages
[ rWidget
.m_nPage
].m_nPageObject
);
5525 aLine
.append( " 0 R\n" );
5527 if( rWidget
.m_nParent
)
5529 aLine
.append( "/Parent " );
5530 aLine
.append( rWidget
.m_nParent
);
5531 aLine
.append( " 0 R\n" );
5533 if( rWidget
.m_aKids
.size() )
5535 aLine
.append( "/Kids[" );
5536 for( unsigned int i
= 0; i
< rWidget
.m_aKids
.size(); i
++ )
5538 aLine
.append( rWidget
.m_aKids
[i
] );
5539 aLine
.append( " 0 R" );
5540 aLine
.append( ( (i
&15) == 15 ) ? "\n" : " " );
5542 aLine
.append( "]\n" );
5544 if( !rWidget
.m_aName
.isEmpty() )
5546 aLine
.append( "/T" );
5547 appendLiteralStringEncrypt( rWidget
.m_aName
, rWidget
.m_nObject
, aLine
);
5548 aLine
.append( "\n" );
5550 if( m_aContext
.Version
> PDFWriter::PDF_1_2
&& !rWidget
.m_aDescription
.isEmpty() )
5552 // the alternate field name should be unicode able since it is
5553 // supposed to be used in UI
5554 aLine
.append( "/TU" );
5555 appendUnicodeTextStringEncrypt( rWidget
.m_aDescription
, rWidget
.m_nObject
, aLine
);
5556 aLine
.append( "\n" );
5559 if( rWidget
.m_nFlags
)
5561 aLine
.append( "/Ff " );
5562 aLine
.append( rWidget
.m_nFlags
);
5563 aLine
.append( "\n" );
5565 if( aValue
.getLength() )
5567 OString aVal
= aValue
.makeStringAndClear();
5568 aLine
.append( "/V " );
5569 aLine
.append( aVal
);
5572 aLine
.append( aVal
);
5573 aLine
.append( "\n" );
5575 if( rWidget
.m_eType
== PDFWriter::ListBox
|| rWidget
.m_eType
== PDFWriter::ComboBox
)
5578 aLine
.append( "/Opt[\n" );
5580 for( std::vector
< OUString
>::const_iterator it
= rWidget
.m_aListEntries
.begin(); it
!= rWidget
.m_aListEntries
.end(); ++it
, ++i
)
5582 appendUnicodeTextStringEncrypt( *it
, rWidget
.m_nObject
, aLine
);
5583 aLine
.append( "\n" );
5584 if( *it
== rWidget
.m_aValue
)
5587 aLine
.append( "]\n" );
5590 aLine
.append( "/TI " );
5591 aLine
.append( nTI
);
5592 aLine
.append( "\n" );
5593 if( rWidget
.m_nFlags
& 0x200000 ) // Multiselect
5595 aLine
.append( "/I [" );
5596 aLine
.append( nTI
);
5597 aLine
.append( "]\n" );
5601 if( rWidget
.m_eType
== PDFWriter::Edit
&& rWidget
.m_nMaxLen
> 0 )
5603 aLine
.append( "/MaxLen " );
5604 aLine
.append( rWidget
.m_nMaxLen
);
5605 aLine
.append( "\n" );
5607 if( rWidget
.m_eType
== PDFWriter::PushButton
)
5611 OStringBuffer aDest
;
5612 if( rWidget
.m_nDest
!= -1 && appendDest( m_aDestinationIdTranslation
[ rWidget
.m_nDest
], aDest
) )
5614 aLine
.append( "/AA<</D<</Type/Action/S/GoTo/D " );
5615 aLine
.append( aDest
.makeStringAndClear() );
5616 aLine
.append( ">>>>\n" );
5618 else if( rWidget
.m_aListEntries
.empty() )
5620 // create a reset form action
5621 aLine
.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
5623 else if( rWidget
.m_bSubmit
)
5625 // create a submit form action
5626 aLine
.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
5627 appendLiteralStringEncrypt( rWidget
.m_aListEntries
.front(), rWidget
.m_nObject
, aLine
, osl_getThreadTextEncoding() );
5628 aLine
.append( "/Flags " );
5630 sal_Int32 nFlags
= 0;
5631 switch( m_aContext
.SubmitFormat
)
5633 case PDFWriter::HTML
:
5636 case PDFWriter::XML
:
5637 if( m_aContext
.Version
> PDFWriter::PDF_1_3
)
5640 case PDFWriter::PDF
:
5641 if( m_aContext
.Version
> PDFWriter::PDF_1_3
)
5644 case PDFWriter::FDF
:
5648 if( rWidget
.m_bSubmitGet
)
5650 aLine
.append( nFlags
);
5651 aLine
.append( ">>>>\n" );
5655 // create a URI action
5656 aLine
.append( "/AA<</D<</Type/Action/S/URI/URI(" );
5657 aLine
.append( OUStringToOString( rWidget
.m_aListEntries
.front(), RTL_TEXTENCODING_ASCII_US
) );
5658 aLine
.append( ")>>>>\n" );
5662 m_aErrors
.insert( PDFWriter::Warning_FormAction_Omitted_PDFA
);
5664 if( !rWidget
.m_aDAString
.isEmpty() )
5666 if( !rWidget
.m_aDRDict
.isEmpty() )
5668 aLine
.append( "/DR<<" );
5669 aLine
.append( rWidget
.m_aDRDict
);
5670 aLine
.append( ">>\n" );
5674 aLine
.append( "/DR<</Font<<" );
5675 appendBuiltinFontsToDict( aLine
);
5676 aLine
.append( ">>>>\n" );
5678 aLine
.append( "/DA" );
5679 appendLiteralStringEncrypt( rWidget
.m_aDAString
, rWidget
.m_nObject
, aLine
);
5680 aLine
.append( "\n" );
5681 if( rWidget
.m_nTextStyle
& TEXT_DRAW_CENTER
)
5682 aLine
.append( "/Q 1\n" );
5683 else if( rWidget
.m_nTextStyle
& TEXT_DRAW_RIGHT
)
5684 aLine
.append( "/Q 2\n" );
5686 // appearance charactristics for terminal fields
5687 // which are supposed to have an appearance constructed
5688 // by the viewer application
5689 if( !rWidget
.m_aMKDict
.isEmpty() )
5691 aLine
.append( "/MK<<" );
5692 aLine
.append( rWidget
.m_aMKDict
);
5693 //add the CA string, encrypting it
5694 appendLiteralStringEncrypt(rWidget
.m_aMKDictCAString
, rWidget
.m_nObject
, aLine
);
5695 aLine
.append( ">>\n" );
5698 CHECK_RETURN( emitAppearances( rWidget
, aLine
) );
5700 aLine
.append( ">>\n"
5702 CHECK_RETURN( updateObject( rWidget
.m_nObject
) );
5703 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
5708 bool PDFWriterImpl::emitAnnotations()
5710 if( m_aPages
.size() < 1 )
5713 CHECK_RETURN( emitLinkAnnotations() );
5715 CHECK_RETURN( emitNoteAnnotations() );
5717 CHECK_RETURN( emitWidgetAnnotations() );
5723 #define CHECK_RETURN( x ) if( !x ) return false
5725 bool PDFWriterImpl::emitCatalog()
5728 // currently there is only one node that contains all leaves
5730 // first create a page tree node id
5731 sal_Int32 nTreeNode
= createObject();
5733 // emit global resource dictionary (page emit needs it)
5734 CHECK_RETURN( emitResources() );
5737 for( std::vector
<PDFPage
>::iterator it
= m_aPages
.begin(); it
!= m_aPages
.end(); ++it
)
5738 if( ! it
->emit( nTreeNode
) )
5741 sal_Int32 nNamedDestinationsDictionary
= emitNamedDestinations();
5743 sal_Int32 nOutlineDict
= emitOutline();
5745 //emit Output intent i59651
5746 sal_Int32 nOutputIntentObject
= emitOutputIntent();
5749 sal_Int32 nMetadataObject
= emitDocumentMetadata();
5751 sal_Int32 nStructureDict
= 0;
5752 if(m_aStructure
.size() > 1)
5754 ///check if dummy structure containers are needed
5755 addInternalStructureContainer(m_aStructure
[0]);
5756 nStructureDict
= m_aStructure
[0].m_nObject
= createObject();
5757 emitStructure( m_aStructure
[ 0 ] );
5760 // adjust tree node file offset
5761 if( ! updateObject( nTreeNode
) )
5765 OStringBuffer
aLine( 2048 );
5766 aLine
.append( nTreeNode
);
5767 aLine
.append( " 0 obj\n" );
5768 aLine
.append( "<</Type/Pages\n" );
5769 aLine
.append( "/Resources " );
5770 aLine
.append( getResourceDictObj() );
5771 aLine
.append( " 0 R\n" );
5773 switch( m_eInheritedOrientation
)
5775 case PDFWriter::Landscape
: aLine
.append( "/Rotate 90\n" );break;
5776 case PDFWriter::Seascape
: aLine
.append( "/Rotate -90\n" );break;
5778 case PDFWriter::Inherit
: // actually Inherit would be a bug, but insignificant
5779 case PDFWriter::Portrait
:
5783 sal_Int32 nMediaBoxWidth
= 0;
5784 sal_Int32 nMediaBoxHeight
= 0;
5785 if( m_aPages
.empty() ) // sanity check, this should not happen
5787 nMediaBoxWidth
= m_nInheritedPageWidth
;
5788 nMediaBoxHeight
= m_nInheritedPageHeight
;
5792 for( std::vector
<PDFPage
>::const_iterator iter
= m_aPages
.begin(); iter
!= m_aPages
.end(); ++iter
)
5794 if( iter
->m_nPageWidth
> nMediaBoxWidth
)
5795 nMediaBoxWidth
= iter
->m_nPageWidth
;
5796 if( iter
->m_nPageHeight
> nMediaBoxHeight
)
5797 nMediaBoxHeight
= iter
->m_nPageHeight
;
5800 aLine
.append( "/MediaBox[ 0 0 " );
5801 aLine
.append( nMediaBoxWidth
);
5802 aLine
.append( ' ' );
5803 aLine
.append( nMediaBoxHeight
);
5804 aLine
.append( " ]\n"
5807 for( std::vector
<PDFPage
>::const_iterator iter
= m_aPages
.begin(); iter
!= m_aPages
.end(); ++iter
, i
++ )
5809 aLine
.append( iter
->m_nPageObject
);
5810 aLine
.append( " 0 R" );
5811 aLine
.append( ( (i
&15) == 15 ) ? "\n" : " " );
5815 aLine
.append( (sal_Int32
)m_aPages
.size() );
5816 aLine
.append( ">>\n"
5818 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
5820 // emit annotation objects
5821 CHECK_RETURN( emitAnnotations() );
5824 m_nCatalogObject
= createObject();
5825 if( ! updateObject( m_nCatalogObject
) )
5827 aLine
.setLength( 0 );
5828 aLine
.append( m_nCatalogObject
);
5829 aLine
.append( " 0 obj\n"
5830 "<</Type/Catalog/Pages " );
5831 aLine
.append( nTreeNode
);
5832 aLine
.append( " 0 R\n" );
5834 //check if there are named destinations to emit (root must be inside the catalog)
5835 if( nNamedDestinationsDictionary
)
5837 aLine
.append("/Dests ");
5838 aLine
.append( nNamedDestinationsDictionary
);
5839 aLine
.append( " 0 R\n" );
5842 if( m_aContext
.PageLayout
!= PDFWriter::DefaultLayout
)
5843 switch( m_aContext
.PageLayout
)
5846 case PDFWriter::SinglePage
:
5847 aLine
.append( "/PageLayout/SinglePage\n" );
5849 case PDFWriter::Continuous
:
5850 aLine
.append( "/PageLayout/OneColumn\n" );
5852 case PDFWriter::ContinuousFacing
:
5853 //the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5854 aLine
.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5857 if( m_aContext
.PDFDocumentMode
!= PDFWriter::ModeDefault
&& !m_aContext
.OpenInFullScreenMode
)
5858 switch( m_aContext
.PDFDocumentMode
)
5861 aLine
.append( "/PageMode/UseNone\n" );
5863 case PDFWriter::UseOutlines
:
5864 aLine
.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5866 case PDFWriter::UseThumbs
:
5867 aLine
.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5870 else if( m_aContext
.OpenInFullScreenMode
)
5871 aLine
.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5873 OStringBuffer aInitPageRef
;
5874 if( m_aContext
.InitialPage
>= 0 && m_aContext
.InitialPage
< (sal_Int32
)m_aPages
.size() )
5876 aInitPageRef
.append( m_aPages
[m_aContext
.InitialPage
].m_nPageObject
);
5877 aInitPageRef
.append( " 0 R" );
5880 aInitPageRef
.append( "0" );
5882 switch( m_aContext
.PDFDocumentAction
)
5884 case PDFWriter::ActionDefault
: //do nothing, this is the Acrobat default
5886 if( aInitPageRef
.getLength() > 1 )
5888 aLine
.append( "/OpenAction[" );
5889 aLine
.append( aInitPageRef
.makeStringAndClear() );
5890 aLine
.append( " /XYZ null null 0]\n" );
5893 case PDFWriter::FitInWindow
:
5894 aLine
.append( "/OpenAction[" );
5895 aLine
.append( aInitPageRef
.makeStringAndClear() );
5896 aLine
.append( " /Fit]\n" ); //Open fit page
5898 case PDFWriter::FitWidth
:
5899 aLine
.append( "/OpenAction[" );
5900 aLine
.append( aInitPageRef
.makeStringAndClear() );
5901 aLine
.append( " /FitH " );
5902 aLine
.append( m_nInheritedPageHeight
);//Open fit width
5903 aLine
.append( "]\n" );
5905 case PDFWriter::FitVisible
:
5906 aLine
.append( "/OpenAction[" );
5907 aLine
.append( aInitPageRef
.makeStringAndClear() );
5908 aLine
.append( " /FitBH " );
5909 aLine
.append( m_nInheritedPageHeight
);//Open fit visible
5910 aLine
.append( "]\n" );
5912 case PDFWriter::ActionZoom
:
5913 aLine
.append( "/OpenAction[" );
5914 aLine
.append( aInitPageRef
.makeStringAndClear() );
5915 aLine
.append( " /XYZ null null " );
5916 if( m_aContext
.Zoom
>= 50 && m_aContext
.Zoom
<= 1600 )
5917 aLine
.append( (double)m_aContext
.Zoom
/100.0 );
5919 aLine
.append( "0" );
5920 aLine
.append( "]\n" );
5923 // viewer preferences, if we had some, then emit
5924 if( m_aContext
.HideViewerToolbar
||
5925 ( m_aContext
.Version
> PDFWriter::PDF_1_3
&& m_aContext
.DocumentInfo
.Title
.Len() && m_aContext
.DisplayPDFDocumentTitle
) ||
5926 m_aContext
.HideViewerMenubar
||
5927 m_aContext
.HideViewerWindowControls
|| m_aContext
.FitWindow
||
5928 m_aContext
.CenterWindow
|| (m_aContext
.FirstPageLeft
&& m_aContext
.PageLayout
== PDFWriter::ContinuousFacing
) ||
5929 m_aContext
.OpenInFullScreenMode
)
5931 aLine
.append( "/ViewerPreferences<<" );
5932 if( m_aContext
.HideViewerToolbar
)
5933 aLine
.append( "/HideToolbar true\n" );
5934 if( m_aContext
.HideViewerMenubar
)
5935 aLine
.append( "/HideMenubar true\n" );
5936 if( m_aContext
.HideViewerWindowControls
)
5937 aLine
.append( "/HideWindowUI true\n" );
5938 if( m_aContext
.FitWindow
)
5939 aLine
.append( "/FitWindow true\n" );
5940 if( m_aContext
.CenterWindow
)
5941 aLine
.append( "/CenterWindow true\n" );
5942 if( m_aContext
.Version
> PDFWriter::PDF_1_3
&& m_aContext
.DocumentInfo
.Title
.Len() && m_aContext
.DisplayPDFDocumentTitle
)
5943 aLine
.append( "/DisplayDocTitle true\n" );
5944 if( m_aContext
.FirstPageLeft
&& m_aContext
.PageLayout
== PDFWriter::ContinuousFacing
)
5945 aLine
.append( "/Direction/R2L\n" );
5946 if( m_aContext
.OpenInFullScreenMode
)
5947 switch( m_aContext
.PDFDocumentMode
)
5950 case PDFWriter::ModeDefault
:
5951 aLine
.append( "/NonFullScreenPageMode/UseNone\n" );
5953 case PDFWriter::UseOutlines
:
5954 aLine
.append( "/NonFullScreenPageMode/UseOutlines\n" );
5956 case PDFWriter::UseThumbs
:
5957 aLine
.append( "/NonFullScreenPageMode/UseThumbs\n" );
5960 aLine
.append( ">>\n" );
5965 aLine
.append( "/Outlines " );
5966 aLine
.append( nOutlineDict
);
5967 aLine
.append( " 0 R\n" );
5969 if( nStructureDict
)
5971 aLine
.append( "/StructTreeRoot " );
5972 aLine
.append( nStructureDict
);
5973 aLine
.append( " 0 R\n" );
5975 if( !m_aContext
.DocumentLocale
.Language
.isEmpty() )
5977 OUStringBuffer
aLocBuf( 16 );
5978 aLocBuf
.append( m_aContext
.DocumentLocale
.Language
.toAsciiLowerCase() );
5979 if( !m_aContext
.DocumentLocale
.Country
.isEmpty() )
5981 aLocBuf
.append( sal_Unicode('-') );
5982 aLocBuf
.append( m_aContext
.DocumentLocale
.Country
);
5984 aLine
.append( "/Lang" );
5985 appendLiteralStringEncrypt( aLocBuf
.makeStringAndClear(), m_nCatalogObject
, aLine
);
5986 aLine
.append( "\n" );
5988 if( m_aContext
.Tagged
&& m_aContext
.Version
> PDFWriter::PDF_1_3
)
5990 aLine
.append( "/MarkInfo<</Marked true>>\n" );
5992 if( m_aWidgets
.size() > 0 )
5994 aLine
.append( "/AcroForm<</Fields[\n" );
5995 int nWidgets
= m_aWidgets
.size();
5997 for( int j
= 0; j
< nWidgets
; j
++ )
5999 // output only root fields
6000 if( m_aWidgets
[j
].m_nParent
< 1 )
6002 aLine
.append( m_aWidgets
[j
].m_nObject
);
6003 aLine
.append( (nOut
++ % 5)==4 ? " 0 R\n" : " 0 R " );
6006 aLine
.append( "\n]" );
6008 #if !defined(ANDROID) && !defined(IOS)
6009 if (m_nSignatureObject
!= -1)
6010 aLine
.append( "/SigFlags 3");
6013 aLine
.append( "/DR " );
6014 aLine
.append( getResourceDictObj() );
6015 aLine
.append( " 0 R" );
6016 // /NeedAppearances must not be used if PDF is signed
6018 #if !defined(ANDROID) && !defined(IOS)
6019 || ( m_nSignatureObject
!= -1 )
6022 aLine
.append( ">>\n" );
6024 aLine
.append( "/NeedAppearances true>>\n" );
6027 //check if there is a Metadata object
6028 if( nOutputIntentObject
)
6030 aLine
.append("/OutputIntents[");
6031 aLine
.append( nOutputIntentObject
);
6032 aLine
.append( " 0 R]" );
6034 if( nMetadataObject
)
6036 aLine
.append("/Metadata ");
6037 aLine
.append( nMetadataObject
);
6038 aLine
.append( " 0 R" );
6041 aLine
.append( ">>\n"
6043 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
6048 #if !defined(ANDROID) && !defined(IOS)
6050 bool PDFWriterImpl::emitSignature()
6052 if( !updateObject( m_nSignatureObject
) )
6055 OStringBuffer
aLine( 0x5000 );
6056 aLine
.append( m_nSignatureObject
);
6057 aLine
.append( " 0 obj\n" );
6058 aLine
.append("<</Contents <" );
6060 sal_uInt64 nOffset
= ~0U;
6061 CHECK_RETURN( (osl_File_E_None
== osl_getFilePos( m_aFile
, &nOffset
) ) );
6063 m_nSignatureContentOffset
= nOffset
+ aLine
.getLength();
6065 // reserve some space for the PKCS#7 object
6066 OStringBuffer
aContentFiller( MAX_SIGNATURE_CONTENT_LENGTH
);
6067 comphelper::string::padToLength(aContentFiller
, MAX_SIGNATURE_CONTENT_LENGTH
, '0');
6068 aLine
.append( aContentFiller
.makeStringAndClear() );
6069 aLine
.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached");
6071 if( m_aContext
.DocumentInfo
.Author
.Len() )
6073 aLine
.append( "/Name" );
6074 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Author
, m_nSignatureObject
, aLine
);
6077 aLine
.append( " /M ");
6078 appendLiteralStringEncrypt( m_aCreationDateString
, m_nSignatureObject
, aLine
);
6080 aLine
.append( " /ByteRange [ 0 ");
6081 aLine
.append( m_nSignatureContentOffset
- 1, 10 );
6082 aLine
.append( " " );
6083 aLine
.append( m_nSignatureContentOffset
+ MAX_SIGNATURE_CONTENT_LENGTH
+ 1, 10 );
6084 aLine
.append( " " );
6086 m_nSignatureLastByteRangeNoOffset
= nOffset
+ aLine
.getLength();
6088 // mark the last ByteRange no and add some space. Now, we don't know
6089 // how many bytes we need for this ByteRange value
6090 // The real value will be overwritten in the finalizeSignature method
6091 OStringBuffer
aByteRangeFiller( 100 );
6092 comphelper::string::padToLength(aByteRangeFiller
, 100, ' ');
6093 aLine
.append( aByteRangeFiller
.makeStringAndClear() );
6094 aLine
.append(" /Filter/Adobe.PPKMS");
6096 //emit reason, location and contactinfo
6097 if ( !m_aContext
.SignReason
.isEmpty() )
6099 aLine
.append("/Reason");
6100 appendUnicodeTextStringEncrypt( m_aContext
.SignReason
, m_nSignatureObject
, aLine
);
6103 if ( !m_aContext
.SignLocation
.isEmpty() )
6105 aLine
.append("/Location");
6106 appendUnicodeTextStringEncrypt( m_aContext
.SignLocation
, m_nSignatureObject
, aLine
);
6109 if ( !m_aContext
.SignContact
.isEmpty() )
6111 aLine
.append("/ContactInfo");
6112 appendUnicodeTextStringEncrypt( m_aContext
.SignContact
, m_nSignatureObject
, aLine
);
6115 aLine
.append(" >>\nendobj\n\n" );
6117 if (!writeBuffer( aLine
.getStr(), aLine
.getLength() ))
6123 char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo
* /*slot*/, PRBool
/*retry*/, void *arg
)
6128 bool PDFWriterImpl::finalizeSignature()
6131 if (!m_aContext
.SignCertificate
.is())
6134 // 1- calculate last ByteRange value
6135 sal_uInt64 nOffset
= ~0U;
6136 CHECK_RETURN( (osl_File_E_None
== osl_getFilePos( m_aFile
, &nOffset
) ) );
6138 sal_Int64 nLastByteRangeNo
= nOffset
- (m_nSignatureContentOffset
+ MAX_SIGNATURE_CONTENT_LENGTH
+ 1);
6140 // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position
6141 sal_uInt64 nWritten
= 0;
6142 CHECK_RETURN( (osl_File_E_None
== osl_setFilePos( m_aFile
, osl_Pos_Absolut
, m_nSignatureLastByteRangeNoOffset
) ) );
6143 OStringBuffer
aByteRangeNo( 256 );
6144 aByteRangeNo
.append( nLastByteRangeNo
, 10);
6145 aByteRangeNo
.append( " ]" );
6147 if( osl_writeFile( m_aFile
, aByteRangeNo
.getStr(), aByteRangeNo
.getLength(), &nWritten
) != osl_File_E_None
)
6149 CHECK_RETURN( (osl_File_E_None
== osl_setFilePos( m_aFile
, osl_Pos_Absolut
, nOffset
) ) );
6153 // 3- create the PKCS#7 object using NSS
6154 com::sun::star::uno::Sequence
< sal_Int8
> derEncoded
= m_aContext
.SignCertificate
->getEncoded();
6156 if (!derEncoded
.hasElements())
6159 sal_Int8
* n_derArray
= derEncoded
.getArray();
6160 sal_Int32 n_derLength
= derEncoded
.getLength();
6164 CERTCertificate
*cert
= CERT_DecodeCertFromPackage(reinterpret_cast<char *>(n_derArray
), n_derLength
);
6168 SAL_WARN("vcl.gdi", "PDF Signing: Error occured, certificate cannot be reconstructed.");
6172 SAL_WARN("vcl.gdi", "PDF Signing: Certificate Subject: " << cert
->subjectName
<< "\n\tCertificate Issuer: " << cert
->issuerName
);
6174 // Prepare buffer and calculate PDF file digest
6175 CHECK_RETURN( (osl_File_E_None
== osl_setFilePos( m_aFile
, osl_Pos_Absolut
, 0) ) );
6177 HASHContext
*hc
= HASH_Create(HASH_AlgSHA1
);
6181 SAL_WARN("vcl.gdi", "PDF Signing: SHA1 HASH_Create failed!");
6187 char *buffer
= new char[m_nSignatureContentOffset
+ 1];
6188 sal_uInt64 bytesRead
;
6190 //FIXME: Check if SHA1 is calculated from the correct byterange
6192 CHECK_RETURN( (osl_File_E_None
== osl_readFile( m_aFile
, buffer
, m_nSignatureContentOffset
- 1 , &bytesRead
) ) );
6193 if (bytesRead
!= (sal_uInt64
)m_nSignatureContentOffset
- 1)
6194 SAL_WARN("vcl.gdi", "PDF Signing: First buffer read failed!");
6196 HASH_Update(hc
, reinterpret_cast<const unsigned char*>(buffer
), bytesRead
);
6199 CHECK_RETURN( (osl_File_E_None
== osl_setFilePos( m_aFile
, osl_Pos_Absolut
, m_nSignatureContentOffset
+ MAX_SIGNATURE_CONTENT_LENGTH
+ 1) ) );
6200 buffer
= new char[nLastByteRangeNo
+ 1];
6201 CHECK_RETURN( (osl_File_E_None
== osl_readFile( m_aFile
, buffer
, nLastByteRangeNo
, &bytesRead
) ) );
6202 if (bytesRead
!= (sal_uInt64
) nLastByteRangeNo
)
6203 SAL_WARN("vcl.gdi", "PDF Signing: Second buffer read failed!");
6205 HASH_Update(hc
, reinterpret_cast<const unsigned char*>(buffer
), bytesRead
);
6209 unsigned char hash
[SHA1_LENGTH
];
6211 HASH_End(hc
, digest
.data
, &digest
.len
, SHA1_LENGTH
);
6214 const char *pass
= OUStringToOString( m_aContext
.SignPassword
, RTL_TEXTENCODING_UTF8
).getStr();
6216 NSSCMSMessage
*cms_msg
= NSS_CMSMessage_Create(NULL
);
6219 SAL_WARN("vcl.gdi", "PDF signing: can't create new CMS message.");
6223 NSSCMSSignedData
*cms_sd
= NSS_CMSSignedData_Create(cms_msg
);
6226 SAL_WARN("vcl.gdi", "PDF signing: can't create CMS SignedData.");
6230 NSSCMSContentInfo
*cms_cinfo
= NSS_CMSMessage_GetContentInfo(cms_msg
);
6231 if (NSS_CMSContentInfo_SetContent_SignedData(cms_msg
, cms_cinfo
, cms_sd
) != SECSuccess
)
6233 SAL_WARN("vcl.gdi", "PDF signing: Can't set CMS content signed data.");
6237 cms_cinfo
= NSS_CMSSignedData_GetContentInfo(cms_sd
);
6238 //attach NULL data as detached data
6239 if (NSS_CMSContentInfo_SetContent_Data(cms_msg
, cms_cinfo
, NULL
, PR_TRUE
) != SECSuccess
)
6241 SAL_WARN("vcl.gdi", "PDF signing: Can't set CMS content data.");
6245 NSSCMSSignerInfo
*cms_signer
= NSS_CMSSignerInfo_Create(cms_msg
, cert
, SEC_OID_SHA1
);
6248 SAL_WARN("vcl.gdi", "PDF signing: can't create CMS SignerInfo.");
6252 if (NSS_CMSSignerInfo_IncludeCerts(cms_signer
, NSSCMSCM_CertChain
, certUsageEmailSigner
) != SECSuccess
)
6254 SAL_WARN("vcl.gdi", "PDF signing: can't include cert chain.");
6258 if (NSS_CMSSignerInfo_AddSigningTime(cms_signer
, PR_Now()) != SECSuccess
)
6260 SAL_WARN("vcl.gdi", "PDF signing: can't add signing time.");
6264 if (NSS_CMSSignedData_AddCertificate(cms_sd
, cert
) != SECSuccess
)
6266 SAL_WARN("vcl.gdi", "PDF signing: can't add signer certificate.");
6270 if (NSS_CMSSignedData_AddSignerInfo(cms_sd
, cms_signer
) != SECSuccess
)
6272 SAL_WARN("vcl.gdi", "PDF signing: can't add signer info.");
6276 if (NSS_CMSSignedData_SetDigestValue(cms_sd
, SEC_OID_SHA1
, &digest
) != SECSuccess
)
6278 SAL_WARN("vcl.gdi", "PDF signing: can't set PDF digest value.");
6282 SAL_WARN("vcl.gdi","PKCS7 Object created successfully!");
6285 cms_output
.data
= 0;
6287 PLArenaPool
*arena
= PORT_NewArena(MAX_SIGNATURE_CONTENT_LENGTH
);
6288 NSSCMSEncoderContext
*cms_ecx
;
6290 //FIXME: Check if password is passed correctly to SEC_PKCS7CreateSignedData function
6291 cms_ecx
= NSS_CMSEncoder_Start(cms_msg
, NULL
, NULL
, &cms_output
, arena
, (PK11PasswordFunc
)::PDFSigningPKCS7PasswordCallback
, (void *)pass
, NULL
, NULL
, NULL
, NULL
);
6295 SAL_WARN("vcl.gdi", "PDF Signing: can't start DER encoder.");
6298 SAL_WARN("vcl.gdi", "PDF Signing: Started DER encoding.");
6300 if (NSS_CMSEncoder_Finish(cms_ecx
) != SECSuccess
)
6302 SAL_WARN("vcl.gdi", "PDF Signing: can't finish DER encoder.");
6305 SAL_WARN("vcl.gdi", "PDF Signing: Finished DER encoding.");
6307 OStringBuffer cms_hexbuffer
;
6309 for (unsigned int i
= 0; i
< cms_output
.len
; i
++)
6310 appendHex(cms_output
.data
[i
], cms_hexbuffer
);
6312 SAL_WARN("vcl.gdi","PKCS7 object encoded successfully!");
6314 // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
6316 CHECK_RETURN( (osl_File_E_None
== osl_setFilePos( m_aFile
, osl_Pos_Absolut
, m_nSignatureContentOffset
) ) );
6317 osl_writeFile(m_aFile
, cms_hexbuffer
.getStr(), cms_hexbuffer
.getLength(), &nWritten
);
6319 NSS_CMSMessage_Destroy(cms_msg
);
6321 CHECK_RETURN( (osl_File_E_None
== osl_setFilePos( m_aFile
, osl_Pos_Absolut
, nOffset
) ) );
6327 sal_Int32
PDFWriterImpl::emitInfoDict( )
6329 sal_Int32 nObject
= createObject();
6331 if( updateObject( nObject
) )
6333 OStringBuffer
aLine( 1024 );
6334 aLine
.append( nObject
);
6335 aLine
.append( " 0 obj\n"
6337 if( m_aContext
.DocumentInfo
.Title
.Len() )
6339 aLine
.append( "/Title" );
6340 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Title
, nObject
, aLine
);
6341 aLine
.append( "\n" );
6343 if( m_aContext
.DocumentInfo
.Author
.Len() )
6345 aLine
.append( "/Author" );
6346 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Author
, nObject
, aLine
);
6347 aLine
.append( "\n" );
6349 if( m_aContext
.DocumentInfo
.Subject
.Len() )
6351 aLine
.append( "/Subject" );
6352 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Subject
, nObject
, aLine
);
6353 aLine
.append( "\n" );
6355 if( m_aContext
.DocumentInfo
.Keywords
.Len() )
6357 aLine
.append( "/Keywords" );
6358 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Keywords
, nObject
, aLine
);
6359 aLine
.append( "\n" );
6361 if( m_aContext
.DocumentInfo
.Creator
.Len() )
6363 aLine
.append( "/Creator" );
6364 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Creator
, nObject
, aLine
);
6365 aLine
.append( "\n" );
6367 if( m_aContext
.DocumentInfo
.Producer
.Len() )
6369 aLine
.append( "/Producer" );
6370 appendUnicodeTextStringEncrypt( m_aContext
.DocumentInfo
.Producer
, nObject
, aLine
);
6371 aLine
.append( "\n" );
6374 aLine
.append( "/CreationDate" );
6375 appendLiteralStringEncrypt( m_aCreationDateString
, nObject
, aLine
);
6376 aLine
.append( ">>\nendobj\n\n" );
6377 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
6387 // Part of this function may be shared with method appendDest.
6389 sal_Int32
PDFWriterImpl::emitNamedDestinations()
6391 sal_Int32 nCount
= m_aNamedDests
.size();
6393 return 0;//define internal error
6395 //get the object number for all the destinations
6396 sal_Int32 nObject
= createObject();
6398 if( updateObject( nObject
) )
6400 //emit the dictionary
6401 OStringBuffer
aLine( 1024 );
6402 aLine
.append( nObject
);
6403 aLine
.append( " 0 obj\n"
6407 for( nDestID
= 0; nDestID
< nCount
; nDestID
++ )
6409 const PDFNamedDest
& rDest
= m_aNamedDests
[ nDestID
];
6410 // In order to correctly function both under an Internet browser and
6411 // directly with a reader (provided the reader has the feature) we
6412 // need to set the name of the destination the same way it will be encoded
6413 // in an Internet link
6414 INetURLObject
aLocalURL(
6415 OUString( "http://ahost.ax" ) ); //dummy location, won't be used
6416 aLocalURL
.SetMark( rDest
.m_aDestName
);
6418 const OUString aName
= aLocalURL
.GetMark( INetURLObject::NO_DECODE
); //same coding as
6419 // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
6420 const PDFPage
& rDestPage
= m_aPages
[ rDest
.m_nPage
];
6422 aLine
.append( '/' );
6423 appendDestinationName( aName
, aLine
); // this conversion must be done when forming the link to target ( see in emitCatalog )
6424 aLine
.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
6425 //maps the preceeding character properly
6426 aLine
.append( rDestPage
.m_nPageObject
);
6427 aLine
.append( " 0 R" );
6429 switch( rDest
.m_eType
)
6431 case PDFWriter::XYZ
:
6433 aLine
.append( "/XYZ " );
6434 appendFixedInt( rDest
.m_aRect
.Left(), aLine
);
6435 aLine
.append( ' ' );
6436 appendFixedInt( rDest
.m_aRect
.Bottom(), aLine
);
6437 aLine
.append( " 0" );
6439 case PDFWriter::Fit
:
6440 aLine
.append( "/Fit" );
6442 case PDFWriter::FitRectangle
:
6443 aLine
.append( "/FitR " );
6444 appendFixedInt( rDest
.m_aRect
.Left(), aLine
);
6445 aLine
.append( ' ' );
6446 appendFixedInt( rDest
.m_aRect
.Top(), aLine
);
6447 aLine
.append( ' ' );
6448 appendFixedInt( rDest
.m_aRect
.Right(), aLine
);
6449 aLine
.append( ' ' );
6450 appendFixedInt( rDest
.m_aRect
.Bottom(), aLine
);
6452 case PDFWriter::FitHorizontal
:
6453 aLine
.append( "/FitH " );
6454 appendFixedInt( rDest
.m_aRect
.Bottom(), aLine
);
6456 case PDFWriter::FitVertical
:
6457 aLine
.append( "/FitV " );
6458 appendFixedInt( rDest
.m_aRect
.Left(), aLine
);
6460 case PDFWriter::FitPageBoundingBox
:
6461 aLine
.append( "/FitB" );
6463 case PDFWriter::FitPageBoundingBoxHorizontal
:
6464 aLine
.append( "/FitBH " );
6465 appendFixedInt( rDest
.m_aRect
.Bottom(), aLine
);
6467 case PDFWriter::FitPageBoundingBoxVertical
:
6468 aLine
.append( "/FitBV " );
6469 appendFixedInt( rDest
.m_aRect
.Left(), aLine
);
6472 aLine
.append( "]\n" );
6476 aLine
.append( ">>\nendobj\n\n" );
6477 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
6488 // emits the output intent dictionary
6490 sal_Int32
PDFWriterImpl::emitOutputIntent()
6495 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
6497 OStringBuffer
aLine( 1024 );
6498 sal_Int32 nICCObject
= createObject();
6499 sal_Int32 nStreamLengthObject
= createObject();
6501 aLine
.append( nICCObject
);
6502 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
6503 aLine
.append( " 0 obj\n<</N 3/Length " );
6504 aLine
.append( nStreamLengthObject
);
6505 aLine
.append( " 0 R" );
6506 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
6507 aLine
.append( "/Filter/FlateDecode" );
6509 aLine
.append( ">>\nstream\n" );
6510 CHECK_RETURN( updateObject( nICCObject
) );
6511 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
6513 sal_uInt64 nBeginStreamPos
= 0;
6514 osl_getFilePos( m_aFile
, &nBeginStreamPos
);
6516 checkAndEnableStreamEncryption( nICCObject
);
6517 cmsHPROFILE hProfile
= cmsCreate_sRGBProfile();
6518 //force ICC profile version 2.1
6519 cmsSetProfileVersion(hProfile
, 2.1);
6520 cmsUInt32Number nBytesNeeded
= 0;
6521 cmsSaveProfileToMem(hProfile
, NULL
, &nBytesNeeded
);
6524 std::vector
<unsigned char> xBuffer(nBytesNeeded
);
6525 cmsSaveProfileToMem(hProfile
, &xBuffer
[0], &nBytesNeeded
);
6526 cmsCloseProfile(hProfile
);
6527 sal_Int32 nStreamSize
= writeBuffer( &xBuffer
[0], (sal_Int32
) xBuffer
.size() );
6528 disableStreamEncryption();
6530 sal_uInt64 nEndStreamPos
= 0;
6531 osl_getFilePos( m_aFile
, &nEndStreamPos
);
6533 if( nStreamSize
== 0 )
6535 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6537 aLine
.setLength( 0 );
6539 //emit the stream length object
6540 CHECK_RETURN( updateObject( nStreamLengthObject
) );
6541 aLine
.setLength( 0 );
6542 aLine
.append( nStreamLengthObject
);
6543 aLine
.append( " 0 obj\n" );
6544 aLine
.append( (sal_Int64
)(nEndStreamPos
-nBeginStreamPos
) );
6545 aLine
.append( "\nendobj\n\n" );
6546 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
6547 aLine
.setLength( 0 );
6549 //emit the OutputIntent dictionary
6550 sal_Int32 nOIObject
= createObject();
6551 CHECK_RETURN( updateObject( nOIObject
) );
6552 aLine
.append( nOIObject
);
6553 aLine
.append( " 0 obj\n"
6554 "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
6556 OUString
aComment( "sRGB IEC61966-2.1" );
6557 appendLiteralStringEncrypt( aComment
,nOIObject
, aLine
);
6558 aLine
.append("/DestOutputProfile ");
6559 aLine
.append( nICCObject
);
6560 aLine
.append( " 0 R>>\nendobj\n\n" );;
6561 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
6566 // formats the string for the XML stream
6567 static void escapeStringXML( const OUString
& rStr
, OUString
&rValue
)
6569 const sal_Unicode
* pUni
= rStr
.getStr();
6570 int nLen
= rStr
.getLength();
6571 for( ; nLen
; nLen
--, pUni
++ )
6575 case sal_Unicode('&'):
6576 rValue
+= OUString( "&" );
6578 case sal_Unicode('<'):
6579 rValue
+= OUString( "<" );
6581 case sal_Unicode('>'):
6582 rValue
+= OUString( ">" );
6584 case sal_Unicode('\''):
6585 rValue
+= OUString( "'" );
6587 case sal_Unicode('"'):
6588 rValue
+= OUString( """ );
6591 rValue
+= OUString( *pUni
);
6597 // emits the document metadata
6599 sal_Int32
PDFWriterImpl::emitDocumentMetadata()
6604 //get the object number for all the destinations
6605 sal_Int32 nObject
= createObject();
6607 if( updateObject( nObject
) )
6609 // the following string are written in UTF-8 unicode
6610 OStringBuffer
aMetadataStream( 8192 );
6612 aMetadataStream
.append( "<?xpacket begin=\"" );
6613 // this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used
6614 // as a byte-order marker.
6615 aMetadataStream
.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8
) );
6616 aMetadataStream
.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
6617 aMetadataStream
.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
6618 aMetadataStream
.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
6619 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
6620 aMetadataStream
.append( " <rdf:Description rdf:about=\"\"\n" );
6621 aMetadataStream
.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
6622 aMetadataStream
.append( " <pdfaid:part>1</pdfaid:part>\n" );
6623 aMetadataStream
.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" );
6624 aMetadataStream
.append( " </rdf:Description>\n" );
6625 //... Dublin Core properties go here
6626 if( m_aContext
.DocumentInfo
.Title
.Len() ||
6627 m_aContext
.DocumentInfo
.Author
.Len() ||
6628 m_aContext
.DocumentInfo
.Subject
.Len() )
6630 aMetadataStream
.append( " <rdf:Description rdf:about=\"\"\n" );
6631 aMetadataStream
.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
6632 if( m_aContext
.DocumentInfo
.Title
.Len() )
6634 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6635 aMetadataStream
.append( " <dc:title>\n" );
6636 aMetadataStream
.append( " <rdf:Alt>\n" );
6637 aMetadataStream
.append( " <rdf:li xml:lang=\"x-default\">" );
6639 escapeStringXML( m_aContext
.DocumentInfo
.Title
, aTitle
);
6640 aMetadataStream
.append( OUStringToOString( aTitle
, RTL_TEXTENCODING_UTF8
) );
6641 aMetadataStream
.append( "</rdf:li>\n" );
6642 aMetadataStream
.append( " </rdf:Alt>\n" );
6643 aMetadataStream
.append( " </dc:title>\n" );
6645 if( m_aContext
.DocumentInfo
.Author
.Len() )
6647 aMetadataStream
.append( " <dc:creator>\n" );
6648 aMetadataStream
.append( " <rdf:Seq>\n" );
6649 aMetadataStream
.append( " <rdf:li>" );
6651 escapeStringXML( m_aContext
.DocumentInfo
.Author
, aAuthor
);
6652 aMetadataStream
.append( OUStringToOString( aAuthor
, RTL_TEXTENCODING_UTF8
) );
6653 aMetadataStream
.append( "</rdf:li>\n" );
6654 aMetadataStream
.append( " </rdf:Seq>\n" );
6655 aMetadataStream
.append( " </dc:creator>\n" );
6657 if( m_aContext
.DocumentInfo
.Subject
.Len() )
6659 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6660 aMetadataStream
.append( " <dc:description>\n" );
6661 aMetadataStream
.append( " <rdf:Alt>\n" );
6662 aMetadataStream
.append( " <rdf:li xml:lang=\"x-default\">" );
6664 escapeStringXML( m_aContext
.DocumentInfo
.Subject
, aSubject
);
6665 aMetadataStream
.append( OUStringToOString( aSubject
, RTL_TEXTENCODING_UTF8
) );
6666 aMetadataStream
.append( "</rdf:li>\n" );
6667 aMetadataStream
.append( " </rdf:Alt>\n" );
6668 aMetadataStream
.append( " </dc:description>\n" );
6670 aMetadataStream
.append( " </rdf:Description>\n" );
6673 //... PDF properties go here
6674 if( m_aContext
.DocumentInfo
.Producer
.Len() ||
6675 m_aContext
.DocumentInfo
.Keywords
.Len() )
6677 aMetadataStream
.append( " <rdf:Description rdf:about=\"\"\n" );
6678 aMetadataStream
.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
6679 if( m_aContext
.DocumentInfo
.Producer
.Len() )
6681 aMetadataStream
.append( " <pdf:Producer>" );
6683 escapeStringXML( m_aContext
.DocumentInfo
.Producer
, aProducer
);
6684 aMetadataStream
.append( OUStringToOString( aProducer
, RTL_TEXTENCODING_UTF8
) );
6685 aMetadataStream
.append( "</pdf:Producer>\n" );
6687 if( m_aContext
.DocumentInfo
.Keywords
.Len() )
6689 aMetadataStream
.append( " <pdf:Keywords>" );
6691 escapeStringXML( m_aContext
.DocumentInfo
.Keywords
, aKeywords
);
6692 aMetadataStream
.append( OUStringToOString( aKeywords
, RTL_TEXTENCODING_UTF8
) );
6693 aMetadataStream
.append( "</pdf:Keywords>\n" );
6695 aMetadataStream
.append( " </rdf:Description>\n" );
6698 aMetadataStream
.append( " <rdf:Description rdf:about=\"\"\n" );
6699 aMetadataStream
.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
6700 if( m_aContext
.DocumentInfo
.Creator
.Len() )
6702 aMetadataStream
.append( " <xmp:CreatorTool>" );
6704 escapeStringXML( m_aContext
.DocumentInfo
.Creator
, aCreator
);
6705 aMetadataStream
.append( OUStringToOString( aCreator
, RTL_TEXTENCODING_UTF8
) );
6706 aMetadataStream
.append( "</xmp:CreatorTool>\n" );
6709 aMetadataStream
.append( " <xmp:CreateDate>" );
6710 aMetadataStream
.append( m_aCreationMetaDateString
);
6711 aMetadataStream
.append( "</xmp:CreateDate>\n" );
6713 aMetadataStream
.append( " </rdf:Description>\n" );
6714 aMetadataStream
.append( " </rdf:RDF>\n" );
6715 aMetadataStream
.append( "</x:xmpmeta>\n" );
6718 for( sal_Int32 nSpaces
= 1; nSpaces
<= 2100; nSpaces
++ )
6720 aMetadataStream
.append( " " );
6721 if( nSpaces
% 100 == 0 )
6722 aMetadataStream
.append( "\n" );
6725 aMetadataStream
.append( "<?xpacket end=\"w\"?>\n" );
6727 OStringBuffer
aMetadataObj( 1024 );
6729 aMetadataObj
.append( nObject
);
6730 aMetadataObj
.append( " 0 obj\n" );
6732 aMetadataObj
.append( "<</Type/Metadata/Subtype/XML/Length " );
6734 aMetadataObj
.append( (sal_Int32
) aMetadataStream
.getLength() );
6735 aMetadataObj
.append( ">>\nstream\n" );
6736 CHECK_RETURN( writeBuffer( aMetadataObj
.getStr(), aMetadataObj
.getLength() ) );
6738 CHECK_RETURN( writeBuffer( aMetadataStream
.getStr(), aMetadataStream
.getLength() ) );
6740 aMetadataObj
.setLength( 0 );
6741 aMetadataObj
.append( "\nendstream\nendobj\n\n" );
6742 if( ! writeBuffer( aMetadataObj
.getStr(), aMetadataObj
.getLength() ) )
6752 bool PDFWriterImpl::emitTrailer()
6755 sal_Int32 nDocInfoObject
= emitInfoDict( );
6757 sal_Int32 nSecObject
= 0;
6759 if( m_aContext
.Encryption
.Encrypt() )
6761 //emit the security information
6762 //must be emitted as indirect dictionary object, since
6763 //Acrobat Reader 5 works only with this kind of implementation
6764 nSecObject
= createObject();
6766 if( updateObject( nSecObject
) )
6768 OStringBuffer
aLineS( 1024 );
6769 aLineS
.append( nSecObject
);
6770 aLineS
.append( " 0 obj\n"
6771 "<</Filter/Standard/V " );
6772 // check the version
6773 if( m_aContext
.Encryption
.Security128bit
)
6774 aLineS
.append( "2/Length 128/R 3" );
6776 aLineS
.append( "1/R 2" );
6778 // emit the owner password, must not be encrypted
6779 aLineS
.append( "/O(" );
6780 appendLiteralString( (const sal_Char
*)&m_aContext
.Encryption
.OValue
[0], sal_Int32(m_aContext
.Encryption
.OValue
.size()), aLineS
);
6781 aLineS
.append( ")/U(" );
6782 appendLiteralString( (const sal_Char
*)&m_aContext
.Encryption
.UValue
[0], sal_Int32(m_aContext
.Encryption
.UValue
.size()), aLineS
);
6783 aLineS
.append( ")/P " );// the permission set
6784 aLineS
.append( m_nAccessPermissions
);
6785 aLineS
.append( ">>\nendobj\n\n" );
6786 if( !writeBuffer( aLineS
.getStr(), aLineS
.getLength() ) )
6794 sal_uInt64 nXRefOffset
= 0;
6795 CHECK_RETURN( (osl_File_E_None
== osl_getFilePos( m_aFile
, &nXRefOffset
)) );
6796 CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
6798 sal_Int32 nObjects
= m_aObjects
.size();
6799 OStringBuffer aLine
;
6800 aLine
.append( "0 " );
6801 aLine
.append( (sal_Int32
)(nObjects
+1) );
6802 aLine
.append( "\n" );
6803 aLine
.append( "0000000000 65535 f \n" );
6804 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
6806 for( sal_Int32 i
= 0; i
< nObjects
; i
++ )
6808 aLine
.setLength( 0 );
6809 OString aOffset
= OString::valueOf( (sal_Int64
)m_aObjects
[i
] );
6810 for( sal_Int32 j
= 0; j
< (10-aOffset
.getLength()); j
++ )
6811 aLine
.append( '0' );
6812 aLine
.append( aOffset
);
6813 aLine
.append( " 00000 n \n" );
6814 DBG_ASSERT( aLine
.getLength() == 20, "invalid xref entry" );
6815 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
6818 // prepare document checksum
6819 OStringBuffer
aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5
+1 );
6822 sal_uInt8 nMD5Sum
[ RTL_DIGEST_LENGTH_MD5
];
6823 rtl_digest_getMD5( m_aDocDigest
, nMD5Sum
, sizeof(nMD5Sum
) );
6824 for( unsigned int i
= 0; i
< RTL_DIGEST_LENGTH_MD5
; i
++ )
6825 appendHex( nMD5Sum
[i
], aDocChecksum
);
6827 // document id set in setDocInfo method
6829 aLine
.setLength( 0 );
6830 aLine
.append( "trailer\n"
6832 aLine
.append( (sal_Int32
)(nObjects
+1) );
6833 aLine
.append( "/Root " );
6834 aLine
.append( m_nCatalogObject
);
6835 aLine
.append( " 0 R\n" );
6838 aLine
.append( "/Encrypt ");
6839 aLine
.append( nSecObject
);
6840 aLine
.append( " 0 R\n" );
6842 if( nDocInfoObject
)
6844 aLine
.append( "/Info " );
6845 aLine
.append( nDocInfoObject
);
6846 aLine
.append( " 0 R\n" );
6848 if( ! m_aContext
.Encryption
.DocumentIdentifier
.empty() )
6850 aLine
.append( "/ID [ <" );
6851 for( std::vector
< sal_uInt8
>::const_iterator it
= m_aContext
.Encryption
.DocumentIdentifier
.begin();
6852 it
!= m_aContext
.Encryption
.DocumentIdentifier
.end(); ++it
)
6854 appendHex( sal_Int8(*it
), aLine
);
6858 for( std::vector
< sal_uInt8
>::const_iterator it
= m_aContext
.Encryption
.DocumentIdentifier
.begin();
6859 it
!= m_aContext
.Encryption
.DocumentIdentifier
.end(); ++it
)
6861 appendHex( sal_Int8(*it
), aLine
);
6863 aLine
.append( "> ]\n" );
6865 if( aDocChecksum
.getLength() )
6867 aLine
.append( "/DocChecksum /" );
6868 aLine
.append( aDocChecksum
.makeStringAndClear() );
6869 aLine
.append( "\n" );
6871 if( m_aAdditionalStreams
.size() > 0 )
6873 aLine
.append( "/AdditionalStreams [" );
6874 for( unsigned int i
= 0; i
< m_aAdditionalStreams
.size(); i
++ )
6876 aLine
.append( "/" );
6877 appendName( m_aAdditionalStreams
[i
].m_aMimeType
, aLine
);
6878 aLine
.append( " " );
6879 aLine
.append( m_aAdditionalStreams
[i
].m_nStreamObject
);
6880 aLine
.append( " 0 R\n" );
6882 aLine
.append( "]\n" );
6884 aLine
.append( ">>\n"
6886 aLine
.append( (sal_Int64
)nXRefOffset
);
6889 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
6894 struct AnnotationSortEntry
6896 sal_Int32 nTabOrder
;
6898 sal_Int32 nWidgetIndex
;
6900 AnnotationSortEntry( sal_Int32 nTab
, sal_Int32 nObj
, sal_Int32 nI
) :
6907 struct AnnotSortContainer
6909 std::set
< sal_Int32
> aObjects
;
6910 std::vector
< AnnotationSortEntry
> aSortedAnnots
;
6913 struct AnnotSorterLess
6915 std::vector
< PDFWriterImpl::PDFWidget
>& m_rWidgets
;
6917 AnnotSorterLess( std::vector
< PDFWriterImpl::PDFWidget
>& rWidgets
) : m_rWidgets( rWidgets
) {}
6919 bool operator()( const AnnotationSortEntry
& rLeft
, const AnnotationSortEntry
& rRight
)
6921 if( rLeft
.nTabOrder
< rRight
.nTabOrder
)
6923 if( rRight
.nTabOrder
< rLeft
.nTabOrder
)
6925 if( rLeft
.nWidgetIndex
< 0 && rRight
.nWidgetIndex
< 0 )
6927 if( rRight
.nWidgetIndex
< 0 )
6929 if( rLeft
.nWidgetIndex
< 0 )
6931 // remember: widget rects are in PDF coordinates, so they are ordered down up
6932 if( m_rWidgets
[ rLeft
.nWidgetIndex
].m_aRect
.Top() >
6933 m_rWidgets
[ rRight
.nWidgetIndex
].m_aRect
.Top() )
6935 if( m_rWidgets
[ rRight
.nWidgetIndex
].m_aRect
.Top() >
6936 m_rWidgets
[ rLeft
.nWidgetIndex
].m_aRect
.Top() )
6938 if( m_rWidgets
[ rLeft
.nWidgetIndex
].m_aRect
.Left() <
6939 m_rWidgets
[ rRight
.nWidgetIndex
].m_aRect
.Left() )
6945 void PDFWriterImpl::sortWidgets()
6947 // sort widget annotations on each page as per their
6948 // TabOrder attribute
6949 boost::unordered_map
< sal_Int32
, AnnotSortContainer
> sorted
;
6950 int nWidgets
= m_aWidgets
.size();
6951 for( int nW
= 0; nW
< nWidgets
; nW
++ )
6953 const PDFWidget
& rWidget
= m_aWidgets
[nW
];
6954 if( rWidget
.m_nPage
>= 0 )
6956 AnnotSortContainer
& rCont
= sorted
[ rWidget
.m_nPage
];
6957 // optimize vector allocation
6958 if( rCont
.aSortedAnnots
.empty() )
6959 rCont
.aSortedAnnots
.reserve( m_aPages
[ rWidget
.m_nPage
].m_aAnnotations
.size() );
6960 // insert widget to tab sorter
6961 // RadioButtons are not page annotations, only their individual check boxes are
6962 if( rWidget
.m_eType
!= PDFWriter::RadioButton
)
6964 rCont
.aObjects
.insert( rWidget
.m_nObject
);
6965 rCont
.aSortedAnnots
.push_back( AnnotationSortEntry( rWidget
.m_nTabOrder
, rWidget
.m_nObject
, nW
) );
6969 for( boost::unordered_map
< sal_Int32
, AnnotSortContainer
>::iterator it
= sorted
.begin(); it
!= sorted
.end(); ++it
)
6971 // append entries for non widget annotations
6972 PDFPage
& rPage
= m_aPages
[ it
->first
];
6973 unsigned int nAnnots
= rPage
.m_aAnnotations
.size();
6974 for( unsigned int nA
= 0; nA
< nAnnots
; nA
++ )
6975 if( it
->second
.aObjects
.find( rPage
.m_aAnnotations
[nA
] ) == it
->second
.aObjects
.end())
6976 it
->second
.aSortedAnnots
.push_back( AnnotationSortEntry( 10000, rPage
.m_aAnnotations
[nA
], -1 ) );
6978 AnnotSorterLess
aLess( m_aWidgets
);
6979 std::stable_sort( it
->second
.aSortedAnnots
.begin(), it
->second
.aSortedAnnots
.end(), aLess
);
6981 if( it
->second
.aSortedAnnots
.size() == nAnnots
)
6983 for( unsigned int nA
= 0; nA
< nAnnots
; nA
++ )
6984 rPage
.m_aAnnotations
[nA
] = it
->second
.aSortedAnnots
[nA
].nObject
;
6988 DBG_ASSERT( 0, "wrong number of sorted annotations" );
6989 #if OSL_DEBUG_LEVEL > 0
6990 fprintf( stderr
, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n"
6991 " %ld sorted and %ld unsorted\n", (long int)it
->first
, (long int)it
->second
.aSortedAnnots
.size(), (long int)nAnnots
);
6996 // FIXME: implement tab order in structure tree for PDF 1.5
7001 public cppu::WeakImplHelper1
< com::sun::star::io::XOutputStream
>
7003 PDFWriterImpl
* m_pWriter
;
7006 PDFStreamIf( PDFWriterImpl
* pWriter
) : m_pWriter( pWriter
), m_bWrite( true ) {}
7007 virtual ~PDFStreamIf();
7009 virtual void SAL_CALL
writeBytes( const com::sun::star::uno::Sequence
< sal_Int8
>& aData
) throw();
7010 virtual void SAL_CALL
flush() throw();
7011 virtual void SAL_CALL
closeOutput() throw();
7015 PDFStreamIf::~PDFStreamIf()
7019 void SAL_CALL
PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence
< sal_Int8
>& aData
) throw()
7021 if( m_bWrite
&& aData
.getLength() )
7023 sal_Int32 nBytes
= aData
.getLength();
7024 m_pWriter
->writeBuffer( aData
.getConstArray(), nBytes
);
7028 void SAL_CALL
PDFStreamIf::flush() throw()
7032 void SAL_CALL
PDFStreamIf::closeOutput() throw()
7037 bool PDFWriterImpl::emitAdditionalStreams()
7039 unsigned int nStreams
= m_aAdditionalStreams
.size();
7040 for( unsigned int i
= 0; i
< nStreams
; i
++ )
7042 PDFAddStream
& rStream
= m_aAdditionalStreams
[i
];
7043 rStream
.m_nStreamObject
= createObject();
7044 sal_Int32 nSizeObject
= createObject();
7046 if( ! updateObject( rStream
.m_nStreamObject
) )
7049 OStringBuffer aLine
;
7050 aLine
.append( rStream
.m_nStreamObject
);
7051 aLine
.append( " 0 obj\n<</Length " );
7052 aLine
.append( nSizeObject
);
7053 aLine
.append( " 0 R" );
7054 if( rStream
.m_bCompress
)
7055 aLine
.append( "/Filter/FlateDecode" );
7056 aLine
.append( ">>\nstream\n" );
7057 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
7059 sal_uInt64 nBeginStreamPos
= 0, nEndStreamPos
= 0;
7060 if( osl_File_E_None
!= osl_getFilePos( m_aFile
, &nBeginStreamPos
) )
7062 osl_closeFile( m_aFile
);
7065 if( rStream
.m_bCompress
)
7068 checkAndEnableStreamEncryption( rStream
.m_nStreamObject
);
7069 com::sun::star::uno::Reference
< com::sun::star::io::XOutputStream
> xStream( new PDFStreamIf( this ) );
7070 rStream
.m_pStream
->write( xStream
);
7072 delete rStream
.m_pStream
;
7073 rStream
.m_pStream
= NULL
;
7074 disableStreamEncryption();
7076 if( rStream
.m_bCompress
)
7079 if( osl_File_E_None
!= osl_getFilePos( m_aFile
, &nEndStreamPos
) )
7081 osl_closeFile( m_aFile
);
7085 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
7087 // emit stream length object
7088 if( ! updateObject( nSizeObject
) )
7090 aLine
.setLength( 0 );
7091 aLine
.append( nSizeObject
);
7092 aLine
.append( " 0 obj\n" );
7093 aLine
.append( (sal_Int64
)(nEndStreamPos
-nBeginStreamPos
) );
7094 aLine
.append( "\nendobj\n\n" );
7095 if( ! writeBuffer( aLine
.getStr(), aLine
.getLength() ) )
7101 bool PDFWriterImpl::emit()
7105 // resort structure tree and annotations if necessary
7106 // needed for widget tab order
7109 #if !defined(ANDROID) && !defined(IOS)
7110 if( m_aContext
.SignPDF
)
7112 // sign the document
7113 PDFWriter::SignatureWidget aSignature
;
7114 aSignature
.Name
= OUString("Signature1");
7115 createControl( aSignature
, 0 );
7119 // emit additional streams
7120 CHECK_RETURN( emitAdditionalStreams() );
7123 CHECK_RETURN( emitCatalog() );
7125 #if !defined(ANDROID) && !defined(IOS)
7126 if (m_nSignatureObject
!= -1) // if document is signed, emit sigdict
7127 CHECK_RETURN( emitSignature() );
7131 CHECK_RETURN( emitTrailer() );
7133 #if !defined(ANDROID) && !defined(IOS)
7134 if (m_nSignatureObject
!= -1) // finalize the signature
7135 CHECK_RETURN( finalizeSignature() );
7138 osl_closeFile( m_aFile
);
7144 std::set
< PDFWriter::ErrorCode
> PDFWriterImpl::getErrors()
7149 sal_Int32
PDFWriterImpl::getSystemFont( const Font
& i_rFont
)
7151 getReferenceDevice()->Push();
7152 getReferenceDevice()->SetFont( i_rFont
);
7153 getReferenceDevice()->ImplNewFont();
7155 const PhysicalFontFace
* pDevFont
= m_pReferenceDevice
->mpFontEntry
->maFontSelData
.mpFontData
;
7156 sal_Int32 nFontID
= 0;
7157 FontEmbedData::iterator it
= m_aSystemFonts
.find( pDevFont
);
7158 if( it
!= m_aSystemFonts
.end() )
7159 nFontID
= it
->second
.m_nNormalFontID
;
7162 nFontID
= m_nNextFID
++;
7163 m_aSystemFonts
[ pDevFont
] = EmbedFont();
7164 m_aSystemFonts
[ pDevFont
].m_nNormalFontID
= nFontID
;
7167 getReferenceDevice()->Pop();
7168 getReferenceDevice()->ImplNewFont();
7173 void PDFWriterImpl::registerGlyphs( int nGlyphs
,
7174 sal_GlyphId
* pGlyphs
,
7175 sal_Int32
* pGlyphWidths
,
7177 sal_Int32
* pUnicodesPerGlyph
,
7178 sal_uInt8
* pMappedGlyphs
,
7179 sal_Int32
* pMappedFontObjects
,
7180 const PhysicalFontFace
* pFallbackFonts
[] )
7182 const PhysicalFontFace
* pDevFont
= m_pReferenceDevice
->mpFontEntry
->maFontSelData
.mpFontData
;
7183 sal_Ucs
* pCurUnicode
= pUnicodes
;
7184 for( int i
= 0; i
< nGlyphs
; pCurUnicode
+= pUnicodesPerGlyph
[i
] , i
++ )
7186 const int nFontGlyphId
= pGlyphs
[i
] & (GF_IDXMASK
| GF_ISCHAR
| GF_GSUB
);
7187 const PhysicalFontFace
* pCurrentFont
= pFallbackFonts
[i
] ? pFallbackFonts
[i
] : pDevFont
;
7189 if( isBuiltinFont( pCurrentFont
) )
7191 sal_Int32 nFontID
= 0;
7192 FontEmbedData::iterator it
= m_aEmbeddedFonts
.find( pCurrentFont
);
7193 if( it
!= m_aEmbeddedFonts
.end() )
7194 nFontID
= it
->second
.m_nNormalFontID
;
7197 nFontID
= m_nNextFID
++;
7198 m_aEmbeddedFonts
[ pCurrentFont
] = EmbedFont();
7199 m_aEmbeddedFonts
[ pCurrentFont
].m_nNormalFontID
= nFontID
;
7202 pGlyphWidths
[ i
] = 0;
7203 pMappedGlyphs
[ i
] = sal::static_int_cast
<sal_Int8
>( nFontGlyphId
);
7204 pMappedFontObjects
[ i
] = nFontID
;
7205 const ImplPdfBuiltinFontData
* pFD
= GetPdfFontData( pCurrentFont
);
7208 const BuiltinFont
* pBuiltinFont
= pFD
->GetBuiltinFont();
7209 pGlyphWidths
[i
] = pBuiltinFont
->m_aWidths
[ nFontGlyphId
& 0x00ff ];
7212 else if( pCurrentFont
->mbSubsettable
)
7214 FontSubset
& rSubset
= m_aSubsets
[ pCurrentFont
];
7215 // search for font specific glyphID
7216 FontMapping::iterator it
= rSubset
.m_aMapping
.find( nFontGlyphId
);
7217 if( it
!= rSubset
.m_aMapping
.end() )
7219 pMappedFontObjects
[i
] = it
->second
.m_nFontID
;
7220 pMappedGlyphs
[i
] = it
->second
.m_nSubsetGlyphID
;
7224 // create new subset if necessary
7225 if( rSubset
.m_aSubsets
.empty()
7226 || (rSubset
.m_aSubsets
.back().m_aMapping
.size() > 254) )
7228 rSubset
.m_aSubsets
.push_back( FontEmit( m_nNextFID
++ ) );
7232 pMappedFontObjects
[i
] = rSubset
.m_aSubsets
.back().m_nFontID
;
7233 // create new glyph in subset
7234 sal_uInt8 nNewId
= sal::static_int_cast
<sal_uInt8
>(rSubset
.m_aSubsets
.back().m_aMapping
.size()+1);
7235 pMappedGlyphs
[i
] = nNewId
;
7237 // add new glyph to emitted font subset
7238 GlyphEmit
& rNewGlyphEmit
= rSubset
.m_aSubsets
.back().m_aMapping
[ nFontGlyphId
];
7239 rNewGlyphEmit
.setGlyphId( nNewId
);
7240 for( sal_Int32 n
= 0; n
< pUnicodesPerGlyph
[i
]; n
++ )
7241 rNewGlyphEmit
.addCode( pCurUnicode
[n
] );
7243 // add new glyph to font mapping
7244 Glyph
& rNewGlyph
= rSubset
.m_aMapping
[ nFontGlyphId
];
7245 rNewGlyph
.m_nFontID
= pMappedFontObjects
[i
];
7246 rNewGlyph
.m_nSubsetGlyphID
= nNewId
;
7248 getReferenceDevice()->ImplGetGraphics();
7249 const bool bVertical
= ((pGlyphs
[i
] & GF_ROTMASK
) != 0);
7250 pGlyphWidths
[i
] = m_aFontCache
.getGlyphWidth( pCurrentFont
,
7253 m_pReferenceDevice
->mpGraphics
);
7255 else if( pCurrentFont
->IsEmbeddable() )
7257 sal_Int32 nFontID
= 0;
7258 FontEmbedData::iterator it
= m_aEmbeddedFonts
.find( pCurrentFont
);
7259 if( it
!= m_aEmbeddedFonts
.end() )
7260 nFontID
= it
->second
.m_nNormalFontID
;
7263 nFontID
= m_nNextFID
++;
7264 m_aEmbeddedFonts
[ pCurrentFont
] = EmbedFont();
7265 m_aEmbeddedFonts
[ pCurrentFont
].m_nNormalFontID
= nFontID
;
7267 EmbedFont
& rEmbedFont
= m_aEmbeddedFonts
[pCurrentFont
];
7269 const Ucs2SIntMap
* pEncoding
= NULL
;
7270 const Ucs2OStrMap
* pNonEncoded
= NULL
;
7271 getReferenceDevice()->ImplGetGraphics();
7272 pEncoding
= m_pReferenceDevice
->mpGraphics
->GetFontEncodingVector( pCurrentFont
, &pNonEncoded
);
7274 Ucs2SIntMap::const_iterator enc_it
;
7275 Ucs2OStrMap::const_iterator nonenc_it
;
7277 sal_Int32 nCurFontID
= nFontID
;
7278 sal_Ucs cChar
= *pCurUnicode
;
7281 enc_it
= pEncoding
->find( cChar
);
7282 if( enc_it
!= pEncoding
->end() && enc_it
->second
> 0 )
7284 DBG_ASSERT( (enc_it
->second
& 0xffffff00) == 0, "Invalid character code" );
7285 cChar
= (sal_Ucs
)enc_it
->second
;
7287 else if( (enc_it
== pEncoding
->end() || enc_it
->second
== -1) &&
7289 (nonenc_it
= pNonEncoded
->find( cChar
)) != pNonEncoded
->end() )
7292 // find non encoded glyph
7293 for( std::list
< EmbedEncoding
>::iterator nec_it
= rEmbedFont
.m_aExtendedEncodings
.begin(); nec_it
!= rEmbedFont
.m_aExtendedEncodings
.end(); ++nec_it
)
7295 if( nec_it
->m_aCMap
.find( cChar
) != nec_it
->m_aCMap
.end() )
7297 nCurFontID
= nec_it
->m_nFontID
;
7298 cChar
= (sal_Ucs
)nec_it
->m_aCMap
[ cChar
];
7302 if( nCurFontID
== 0 ) // new nonencoded glyph
7304 if( rEmbedFont
.m_aExtendedEncodings
.empty() || rEmbedFont
.m_aExtendedEncodings
.back().m_aEncVector
.size() == 255 )
7306 rEmbedFont
.m_aExtendedEncodings
.push_back( EmbedEncoding() );
7307 rEmbedFont
.m_aExtendedEncodings
.back().m_nFontID
= m_nNextFID
++;
7309 EmbedEncoding
& rEncoding
= rEmbedFont
.m_aExtendedEncodings
.back();
7310 rEncoding
.m_aEncVector
.push_back( EmbedCode() );
7311 rEncoding
.m_aEncVector
.back().m_aUnicode
= cChar
;
7312 rEncoding
.m_aEncVector
.back().m_aName
= nonenc_it
->second
;
7313 rEncoding
.m_aCMap
[ cChar
] = (sal_Int8
)(rEncoding
.m_aEncVector
.size()-1);
7314 nCurFontID
= rEncoding
.m_nFontID
;
7315 cChar
= (sal_Ucs
)rEncoding
.m_aCMap
[ cChar
];
7323 if( cChar
& 0xff00 )
7325 // some characters can be used by conversion
7326 if( cChar
>= 0xf000 && cChar
<= 0xf0ff ) // symbol encoding in private use area
7330 OString
aChar(&cChar
, 1, RTL_TEXTENCODING_MS_1252
);
7331 cChar
= ((sal_Ucs
)aChar
[0]) & 0x00ff;
7336 pMappedGlyphs
[ i
] = (sal_Int8
)cChar
;
7337 pMappedFontObjects
[ i
] = nCurFontID
;
7338 pGlyphWidths
[ i
] = m_aFontCache
.getGlyphWidth( pCurrentFont
,
7339 (pEncoding
? *pCurUnicode
: cChar
) | GF_ISCHAR
,
7341 m_pReferenceDevice
->mpGraphics
);
7346 void PDFWriterImpl::drawRelief( SalLayout
& rLayout
, const String
& rText
, bool bTextLines
)
7350 FontRelief eRelief
= m_aCurrentPDFState
.m_aFont
.GetRelief();
7352 Color aTextColor
= m_aCurrentPDFState
.m_aFont
.GetColor();
7353 Color aTextLineColor
= m_aCurrentPDFState
.m_aTextLineColor
;
7354 Color aOverlineColor
= m_aCurrentPDFState
.m_aOverlineColor
;
7355 Color
aReliefColor( COL_LIGHTGRAY
);
7356 if( aTextColor
== COL_BLACK
)
7357 aTextColor
= Color( COL_WHITE
);
7358 if( aTextLineColor
== COL_BLACK
)
7359 aTextLineColor
= Color( COL_WHITE
);
7360 if( aOverlineColor
== COL_BLACK
)
7361 aOverlineColor
= Color( COL_WHITE
);
7362 if( aTextColor
== COL_WHITE
)
7363 aReliefColor
= Color( COL_BLACK
);
7365 Font aSetFont
= m_aCurrentPDFState
.m_aFont
;
7366 aSetFont
.SetRelief( RELIEF_NONE
);
7367 aSetFont
.SetShadow( sal_False
);
7369 aSetFont
.SetColor( aReliefColor
);
7370 setTextLineColor( aReliefColor
);
7371 setOverlineColor( aReliefColor
);
7372 setFont( aSetFont
);
7373 long nOff
= 1 + getReferenceDevice()->mnDPIX
/300;
7374 if( eRelief
== RELIEF_ENGRAVED
)
7377 rLayout
.DrawOffset() += Point( nOff
, nOff
);
7378 updateGraphicsState();
7379 drawLayout( rLayout
, rText
, bTextLines
);
7381 rLayout
.DrawOffset() -= Point( nOff
, nOff
);
7382 setTextLineColor( aTextLineColor
);
7383 setOverlineColor( aOverlineColor
);
7384 aSetFont
.SetColor( aTextColor
);
7385 setFont( aSetFont
);
7386 updateGraphicsState();
7387 drawLayout( rLayout
, rText
, bTextLines
);
7389 // clean up the mess
7393 void PDFWriterImpl::drawShadow( SalLayout
& rLayout
, const String
& rText
, bool bTextLines
)
7395 Font aSaveFont
= m_aCurrentPDFState
.m_aFont
;
7396 Color aSaveTextLineColor
= m_aCurrentPDFState
.m_aTextLineColor
;
7397 Color aSaveOverlineColor
= m_aCurrentPDFState
.m_aOverlineColor
;
7399 Font
& rFont
= m_aCurrentPDFState
.m_aFont
;
7400 if( rFont
.GetColor() == Color( COL_BLACK
) || rFont
.GetColor().GetLuminance() < 8 )
7401 rFont
.SetColor( Color( COL_LIGHTGRAY
) );
7403 rFont
.SetColor( Color( COL_BLACK
) );
7404 rFont
.SetShadow( sal_False
);
7405 rFont
.SetOutline( sal_False
);
7407 setTextLineColor( rFont
.GetColor() );
7408 setOverlineColor( rFont
.GetColor() );
7409 updateGraphicsState();
7411 long nOff
= 1 + ((m_pReferenceDevice
->mpFontEntry
->mnLineHeight
-24)/24);
7412 if( rFont
.IsOutline() )
7414 rLayout
.DrawBase() += Point( nOff
, nOff
);
7415 drawLayout( rLayout
, rText
, bTextLines
);
7416 rLayout
.DrawBase() -= Point( nOff
, nOff
);
7418 setFont( aSaveFont
);
7419 setTextLineColor( aSaveTextLineColor
);
7420 setOverlineColor( aSaveOverlineColor
);
7421 updateGraphicsState();
7424 void PDFWriterImpl::drawVerticalGlyphs(
7425 const std::vector
<PDFWriterImpl::PDFGlyph
>& rGlyphs
,
7426 OStringBuffer
& rLine
,
7427 const Point
& rAlignOffset
,
7428 const Matrix3
& rRotScale
,
7432 sal_Int32 nFontHeight
)
7435 Point
aCurPos( rGlyphs
[0].m_aPos
);
7436 aCurPos
= m_pReferenceDevice
->PixelToLogic( aCurPos
);
7437 aCurPos
+= rAlignOffset
;
7438 for( size_t i
= 0; i
< rGlyphs
.size(); i
++ )
7440 // have to emit each glyph on its own
7441 double fDeltaAngle
= 0.0;
7442 double fYScale
= 1.0;
7443 double fTempXScale
= fXScale
;
7444 double fSkewB
= fSkew
;
7445 double fSkewA
= 0.0;
7448 if( ( rGlyphs
[i
].m_nGlyphId
& GF_ROTMASK
) == GF_ROTL
)
7450 fDeltaAngle
= M_PI
/2.0;
7451 aDeltaPos
.X() = m_pReferenceDevice
->GetFontMetric().GetAscent();
7452 aDeltaPos
.Y() = (int)((double)m_pReferenceDevice
->GetFontMetric().GetDescent() * fXScale
);
7458 else if( ( rGlyphs
[i
].m_nGlyphId
& GF_ROTMASK
) == GF_ROTR
)
7460 fDeltaAngle
= -M_PI
/2.0;
7461 aDeltaPos
.X() = (int)((double)m_pReferenceDevice
->GetFontMetric().GetDescent()*fXScale
);
7462 aDeltaPos
.Y() = -m_pReferenceDevice
->GetFontMetric().GetAscent();
7468 aDeltaPos
+= (m_pReferenceDevice
->PixelToLogic( Point( (int)((double)nXOffset
/fXScale
), 0 ) ) - m_pReferenceDevice
->PixelToLogic( Point() ) );
7469 if( i
< rGlyphs
.size()-1 )
7470 // [Bug 120627] the text on the Y axis is reversed when export ppt file to PDF format
7472 long nOffsetX
= rGlyphs
[i
+1].m_aPos
.X() - rGlyphs
[i
].m_aPos
.X();
7473 long nOffsetY
= rGlyphs
[i
+1].m_aPos
.Y() - rGlyphs
[i
].m_aPos
.Y();
7474 nXOffset
+= (int)sqrt(double(nOffsetX
*nOffsetX
+ nOffsetY
*nOffsetY
));
7476 if( ! rGlyphs
[i
].m_nGlyphId
)
7479 aDeltaPos
= rRotScale
.transform( aDeltaPos
);
7482 if( fSkewB
!= 0.0 || fSkewA
!= 0.0 )
7483 aMat
.skew( fSkewA
, fSkewB
);
7484 aMat
.scale( fTempXScale
, fYScale
);
7485 aMat
.rotate( fAngle
+fDeltaAngle
);
7486 aMat
.translate( aCurPos
.X()+aDeltaPos
.X(), aCurPos
.Y()+aDeltaPos
.Y() );
7487 aMat
.append( m_aPages
.back(), rLine
);
7488 rLine
.append( " Tm" );
7489 if( i
== 0 || rGlyphs
[i
-1].m_nMappedFontId
!= rGlyphs
[i
].m_nMappedFontId
)
7491 rLine
.append( " /F" );
7492 rLine
.append( rGlyphs
[i
].m_nMappedFontId
);
7493 rLine
.append( ' ' );
7494 m_aPages
.back().appendMappedLength( nFontHeight
, rLine
, true );
7495 rLine
.append( " Tf" );
7497 rLine
.append( "<" );
7498 appendHex( rGlyphs
[i
].m_nMappedGlyphId
, rLine
);
7499 rLine
.append( ">Tj\n" );
7503 void PDFWriterImpl::drawHorizontalGlyphs(
7504 const std::vector
<PDFWriterImpl::PDFGlyph
>& rGlyphs
,
7505 OStringBuffer
& rLine
,
7506 const Point
& rAlignOffset
,
7510 sal_Int32 nFontHeight
,
7511 sal_Int32 nPixelFontHeight
7514 // horizontal (= normal) case
7516 // fill in run end indices
7517 // end is marked by index of the first glyph of the next run
7518 // a run is marked by same mapped font id and same Y position
7519 std::vector
< sal_uInt32
> aRunEnds
;
7520 aRunEnds
.reserve( rGlyphs
.size() );
7521 for( size_t i
= 1; i
< rGlyphs
.size(); i
++ )
7523 if( rGlyphs
[i
].m_nMappedFontId
!= rGlyphs
[i
-1].m_nMappedFontId
||
7524 rGlyphs
[i
].m_aPos
.Y() != rGlyphs
[i
-1].m_aPos
.Y() )
7526 aRunEnds
.push_back(i
);
7529 // last run ends at last glyph
7530 aRunEnds
.push_back( rGlyphs
.size() );
7532 // loop over runs of the same font
7533 sal_uInt32 nBeginRun
= 0;
7534 for( size_t nRun
= 0; nRun
< aRunEnds
.size(); nRun
++ )
7536 // setup text matrix
7537 Point aCurPos
= rGlyphs
[nBeginRun
].m_aPos
;
7538 // back transformation to current coordinate system
7539 aCurPos
= m_pReferenceDevice
->PixelToLogic( aCurPos
);
7540 aCurPos
+= rAlignOffset
;
7541 // the first run can be set with "Td" operator
7542 // subsequent use of that operator would move
7543 // the texline matrix relative to what was set before
7544 // making use of that would drive us into rounding issues
7546 if( nRun
== 0 && fAngle
== 0.0 && fXScale
== 1.0 && fSkew
== 0.0 )
7548 m_aPages
.back().appendPoint( aCurPos
, rLine
, false );
7549 rLine
.append( " Td " );
7554 aMat
.skew( 0.0, fSkew
);
7555 aMat
.scale( fXScale
, 1.0 );
7556 aMat
.rotate( fAngle
);
7557 aMat
.translate( aCurPos
.X(), aCurPos
.Y() );
7558 aMat
.append( m_aPages
.back(), rLine
);
7559 rLine
.append( " Tm\n" );
7561 // set up correct font
7562 rLine
.append( "/F" );
7563 rLine
.append( rGlyphs
[nBeginRun
].m_nMappedFontId
);
7564 rLine
.append( ' ' );
7565 m_aPages
.back().appendMappedLength( nFontHeight
, rLine
, true );
7566 rLine
.append( " Tf" );
7568 // output glyphs using Tj or TJ
7569 OStringBuffer
aKernedLine( 256 ), aUnkernedLine( 256 );
7570 aKernedLine
.append( "[<" );
7571 aUnkernedLine
.append( '<' );
7572 appendHex( rGlyphs
[nBeginRun
].m_nMappedGlyphId
, aKernedLine
);
7573 appendHex( rGlyphs
[nBeginRun
].m_nMappedGlyphId
, aUnkernedLine
);
7576 bool bNeedKern
= false;
7577 for( sal_uInt32 nPos
= nBeginRun
+1; nPos
< aRunEnds
[nRun
]; nPos
++ )
7579 appendHex( rGlyphs
[nPos
].m_nMappedGlyphId
, aUnkernedLine
);
7580 // check if default glyph positioning is sufficient
7581 const Point aThisPos
= aMat
.transform( rGlyphs
[nPos
].m_aPos
);
7582 const Point aPrevPos
= aMat
.transform( rGlyphs
[nPos
-1].m_aPos
);
7583 double fAdvance
= aThisPos
.X() - aPrevPos
.X();
7584 fAdvance
*= 1000.0 / nPixelFontHeight
;
7585 const sal_Int32 nAdjustment
= (sal_Int32
)(rGlyphs
[nPos
-1].m_nNativeWidth
- fAdvance
+ 0.5);
7586 if( nAdjustment
!= 0 )
7588 // apply individual glyph positioning
7590 aKernedLine
.append( ">" );
7591 aKernedLine
.append( nAdjustment
);
7592 aKernedLine
.append( "<" );
7594 appendHex( rGlyphs
[nPos
].m_nMappedGlyphId
, aKernedLine
);
7596 aKernedLine
.append( ">]TJ\n" );
7597 aUnkernedLine
.append( ">Tj\n" );
7599 (bNeedKern
? aKernedLine
: aUnkernedLine
).makeStringAndClear() );
7601 // set beginning of next run
7602 nBeginRun
= aRunEnds
[nRun
];
7606 void PDFWriterImpl::drawLayout( SalLayout
& rLayout
, const String
& rText
, bool bTextLines
)
7608 // relief takes precedence over shadow (see outdev3.cxx)
7609 if( m_aCurrentPDFState
.m_aFont
.GetRelief() != RELIEF_NONE
)
7611 drawRelief( rLayout
, rText
, bTextLines
);
7614 else if( m_aCurrentPDFState
.m_aFont
.IsShadow() )
7615 drawShadow( rLayout
, rText
, bTextLines
);
7617 OStringBuffer
aLine( 512 );
7619 const int nMaxGlyphs
= 256;
7621 sal_GlyphId pGlyphs
[nMaxGlyphs
];
7622 sal_Int32 pGlyphWidths
[nMaxGlyphs
];
7623 sal_uInt8 pMappedGlyphs
[nMaxGlyphs
];
7624 sal_Int32 pMappedFontObjects
[nMaxGlyphs
];
7625 std::vector
<sal_Ucs
> aUnicodes
;
7626 aUnicodes
.reserve( nMaxGlyphs
);
7627 sal_Int32 pUnicodesPerGlyph
[nMaxGlyphs
];
7628 int pCharPosAry
[nMaxGlyphs
];
7629 sal_Int32 nAdvanceWidths
[nMaxGlyphs
];
7630 const PhysicalFontFace
* pFallbackFonts
[nMaxGlyphs
] = { NULL
};
7631 bool bVertical
= m_aCurrentPDFState
.m_aFont
.IsVertical();
7634 int nMinCharPos
= 0, nMaxCharPos
= rText
.Len()-1;
7635 double fXScale
= 1.0;
7637 sal_Int32 nPixelFontHeight
= m_pReferenceDevice
->mpFontEntry
->maFontSelData
.mnHeight
;
7638 TextAlign eAlign
= m_aCurrentPDFState
.m_aFont
.GetAlign();
7640 // transform font height back to current units
7641 // note: the layout calculates in outdevs device pixel !!
7642 sal_Int32 nFontHeight
= m_pReferenceDevice
->ImplDevicePixelToLogicHeight( nPixelFontHeight
);
7643 if( m_aCurrentPDFState
.m_aFont
.GetWidth() )
7645 Font
aFont( m_aCurrentPDFState
.m_aFont
);
7646 aFont
.SetWidth( 0 );
7647 FontMetric aMetric
= m_pReferenceDevice
->GetFontMetric( aFont
);
7648 if( aMetric
.GetWidth() != m_aCurrentPDFState
.m_aFont
.GetWidth() )
7651 (double)m_aCurrentPDFState
.m_aFont
.GetWidth() /
7652 (double)aMetric
.GetWidth();
7654 // force state before GetFontMetric
7655 m_pReferenceDevice
->ImplNewFont();
7658 // perform artificial italics if necessary
7659 if( ( m_aCurrentPDFState
.m_aFont
.GetItalic() == ITALIC_NORMAL
||
7660 m_aCurrentPDFState
.m_aFont
.GetItalic() == ITALIC_OBLIQUE
) &&
7661 !( m_pReferenceDevice
->mpFontEntry
->maFontSelData
.mpFontData
->GetSlant() == ITALIC_NORMAL
||
7662 m_pReferenceDevice
->mpFontEntry
->maFontSelData
.mpFontData
->GetSlant() == ITALIC_OBLIQUE
)
7668 // if the mapmode is distorted we need to adjust for that also
7669 if( m_aCurrentPDFState
.m_aMapMode
.GetScaleX() != m_aCurrentPDFState
.m_aMapMode
.GetScaleY() )
7671 fXScale
*= double(m_aCurrentPDFState
.m_aMapMode
.GetScaleX()) / double(m_aCurrentPDFState
.m_aMapMode
.GetScaleY());
7674 int nAngle
= m_aCurrentPDFState
.m_aFont
.GetOrientation();
7678 nAngle
= nAngle
% 3600;
7679 double fAngle
= (double)nAngle
* M_PI
/ 1800.0;
7682 aRotScale
.scale( fXScale
, 1.0 );
7684 aRotScale
.rotate( -fAngle
);
7687 bool bABold
= false;
7688 // artificial bold necessary ?
7689 if( m_pReferenceDevice
->mpFontEntry
->maFontSelData
.mpFontData
->GetWeight() <= WEIGHT_MEDIUM
&&
7690 m_pReferenceDevice
->mpFontEntry
->maFontSelData
.GetWeight() > WEIGHT_MEDIUM
)
7693 aLine
.append( "q " );
7697 // setup text colors (if necessary)
7698 Color
aStrokeColor( COL_TRANSPARENT
);
7699 Color
aNonStrokeColor( COL_TRANSPARENT
);
7701 if( m_aCurrentPDFState
.m_aFont
.IsOutline() )
7703 aStrokeColor
= m_aCurrentPDFState
.m_aFont
.GetColor();
7704 aNonStrokeColor
= Color( COL_WHITE
);
7707 aNonStrokeColor
= m_aCurrentPDFState
.m_aFont
.GetColor();
7709 aStrokeColor
= m_aCurrentPDFState
.m_aFont
.GetColor();
7711 if( aStrokeColor
!= Color( COL_TRANSPARENT
) && aStrokeColor
!= m_aCurrentPDFState
.m_aLineColor
)
7714 aLine
.append( "q " );
7716 appendStrokingColor( aStrokeColor
, aLine
);
7717 aLine
.append( "\n" );
7719 if( aNonStrokeColor
!= Color( COL_TRANSPARENT
) && aNonStrokeColor
!= m_aCurrentPDFState
.m_aFillColor
)
7722 aLine
.append( "q " );
7724 appendNonStrokingColor( aNonStrokeColor
, aLine
);
7725 aLine
.append( "\n" );
7728 // begin text object
7729 aLine
.append( "BT\n" );
7730 // outline attribute ?
7731 if( m_aCurrentPDFState
.m_aFont
.IsOutline() || bABold
)
7733 // set correct text mode, set stroke width
7734 aLine
.append( "2 Tr " ); // fill, then stroke
7736 if( m_aCurrentPDFState
.m_aFont
.IsOutline() )
7738 // unclear what to do in case of outline and artificial bold
7739 // for the time being outline wins
7740 aLine
.append( "0.25 w \n" );
7744 double fW
= (double)m_aCurrentPDFState
.m_aFont
.GetHeight() / 30.0;
7745 m_aPages
.back().appendMappedLength( fW
, aLine
);
7746 aLine
.append ( " w\n" );
7750 FontMetric aRefDevFontMetric
= m_pReferenceDevice
->GetFontMetric();
7752 // collect the glyphs into a single array
7753 const int nTmpMaxGlyphs
= rLayout
.GetOrientation() ? 1 : nMaxGlyphs
; // #i97991# temporary workaround for #i87686#
7754 std::vector
< PDFGlyph
> aGlyphs
;
7755 aGlyphs
.reserve( nTmpMaxGlyphs
);
7756 // first get all the glyphs and register them; coordinates still in Pixel
7758 while( (nGlyphs
= rLayout
.GetNextGlyphs( nTmpMaxGlyphs
, pGlyphs
, aGNGlyphPos
, nIndex
, nAdvanceWidths
, pCharPosAry
, pFallbackFonts
)) != 0 )
7761 for( int i
= 0; i
< nGlyphs
; i
++ )
7763 // default case: 1 glyph is one unicode
7764 pUnicodesPerGlyph
[i
] = 1;
7765 if( (pGlyphs
[i
] & GF_ISCHAR
) )
7767 aUnicodes
.push_back( static_cast<sal_Ucs
>(pGlyphs
[i
] & GF_IDXMASK
) );
7769 else if( pCharPosAry
[i
] >= nMinCharPos
&& pCharPosAry
[i
] <= nMaxCharPos
)
7772 aUnicodes
.push_back( rText
.GetChar( sal::static_int_cast
<xub_StrLen
>(pCharPosAry
[i
]) ) );
7773 pUnicodesPerGlyph
[i
] = 1;
7774 // try to handle ligatures and such
7777 nChars
= pCharPosAry
[i
+1] - pCharPosAry
[i
];
7778 // #i115618# fix for simple RTL+CTL cases
7779 // TODO: sanitize for RTL ligatures, more complex CTL, etc.
7782 else if( nChars
== 0 )
7784 pUnicodesPerGlyph
[i
] = nChars
;
7785 for( int n
= 1; n
< nChars
; n
++ )
7786 aUnicodes
.push_back( rText
.GetChar( sal::static_int_cast
<xub_StrLen
>(pCharPosAry
[i
]+n
) ) );
7788 // #i36691# hack that is needed because currently the pGlyphs[]
7789 // argument is ignored for embeddable fonts and so the layout
7790 // engine's glyph work is ignored (i.e. char mirroring)
7791 // TODO: a real solution would be to map the layout engine's
7792 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
7793 // back to unicode and then to embeddable font's encoding
7794 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL
)
7796 size_t nI
= aUnicodes
.size()-1;
7797 for( int n
= 0; n
< nChars
; n
++, nI
-- )
7798 aUnicodes
[nI
] = static_cast<sal_Ucs
>(GetMirroredChar(aUnicodes
[nI
]));
7802 aUnicodes
.push_back( 0 );
7803 // note: in case of ctl one character may result
7804 // in multiple glyphs. The current SalLayout
7805 // implementations set -1 then to indicate that no direct
7806 // mapping is possible
7809 registerGlyphs( nGlyphs
, pGlyphs
, pGlyphWidths
, &aUnicodes
[0], pUnicodesPerGlyph
, pMappedGlyphs
, pMappedFontObjects
, pFallbackFonts
);
7811 for( int i
= 0; i
< nGlyphs
; i
++ )
7813 aGlyphs
.push_back( PDFGlyph( aGNGlyphPos
,
7816 pMappedFontObjects
[i
],
7817 pMappedGlyphs
[i
] ) );
7819 aGNGlyphPos
.Y() += nAdvanceWidths
[i
]/rLayout
.GetUnitsPerPixel();
7821 aGNGlyphPos
.X() += nAdvanceWidths
[i
]/rLayout
.GetUnitsPerPixel();
7826 if ( eAlign
== ALIGN_BOTTOM
)
7827 aAlignOffset
.Y() -= aRefDevFontMetric
.GetDescent();
7828 else if ( eAlign
== ALIGN_TOP
)
7829 aAlignOffset
.Y() += aRefDevFontMetric
.GetAscent();
7830 if( aAlignOffset
.X() || aAlignOffset
.Y() )
7831 aAlignOffset
= aRotScale
.transform( aAlignOffset
);
7833 /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
7834 string contained only on of the UTF16 BOMs
7836 if( ! aGlyphs
.empty() )
7839 drawVerticalGlyphs( aGlyphs
, aLine
, aAlignOffset
, aRotScale
, fAngle
, fXScale
, fSkew
, nFontHeight
);
7841 drawHorizontalGlyphs( aGlyphs
, aLine
, aAlignOffset
, fAngle
, fXScale
, fSkew
, nFontHeight
, nPixelFontHeight
);
7845 aLine
.append( "ET\n" );
7847 aLine
.append( "Q\n" );
7849 writeBuffer( aLine
.getStr(), aLine
.getLength() );
7851 // draw eventual textlines
7852 FontStrikeout eStrikeout
= m_aCurrentPDFState
.m_aFont
.GetStrikeout();
7853 FontUnderline eUnderline
= m_aCurrentPDFState
.m_aFont
.GetUnderline();
7854 FontUnderline eOverline
= m_aCurrentPDFState
.m_aFont
.GetOverline();
7857 ( eUnderline
!= UNDERLINE_NONE
&& eUnderline
!= UNDERLINE_DONTKNOW
) ||
7858 ( eOverline
!= UNDERLINE_NONE
&& eOverline
!= UNDERLINE_DONTKNOW
) ||
7859 ( eStrikeout
!= STRIKEOUT_NONE
&& eStrikeout
!= STRIKEOUT_DONTKNOW
)
7863 sal_Bool bUnderlineAbove
= OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState
.m_aFont
);
7864 if( m_aCurrentPDFState
.m_aFont
.IsWordLineMode() )
7866 Point aPos
, aStartPt
;
7867 sal_Int32 nWidth
= 0, nAdvance
=0;
7868 for( int nStart
= 0;;)
7870 sal_GlyphId nGlyphIndex
;
7871 if( !rLayout
.GetNextGlyphs( 1, &nGlyphIndex
, aPos
, nStart
, &nAdvance
) )
7874 if( !rLayout
.IsSpacingGlyph( nGlyphIndex
) )
7881 else if( nWidth
> 0 )
7883 drawTextLine( m_pReferenceDevice
->PixelToLogic( aStartPt
),
7884 m_pReferenceDevice
->ImplDevicePixelToLogicWidth( nWidth
),
7885 eStrikeout
, eUnderline
, eOverline
, bUnderlineAbove
);
7892 drawTextLine( m_pReferenceDevice
->PixelToLogic( aStartPt
),
7893 m_pReferenceDevice
->ImplDevicePixelToLogicWidth( nWidth
),
7894 eStrikeout
, eUnderline
, eOverline
, bUnderlineAbove
);
7899 Point aStartPt
= rLayout
.GetDrawPosition();
7900 int nWidth
= rLayout
.GetTextWidth() / rLayout
.GetUnitsPerPixel();
7901 drawTextLine( m_pReferenceDevice
->PixelToLogic( aStartPt
),
7902 m_pReferenceDevice
->ImplDevicePixelToLogicWidth( nWidth
),
7903 eStrikeout
, eUnderline
, eOverline
, bUnderlineAbove
);
7907 // write eventual emphasis marks
7908 if( m_aCurrentPDFState
.m_aFont
.GetEmphasisMark() & EMPHASISMARK_STYLE
)
7910 PolyPolygon aEmphPoly
;
7911 Rectangle aEmphRect1
;
7912 Rectangle aEmphRect2
;
7916 sal_Bool bEmphPolyLine
;
7917 FontEmphasisMark nEmphMark
;
7921 aLine
.setLength( 0 );
7922 aLine
.append( "q\n" );
7924 nEmphMark
= m_pReferenceDevice
->ImplGetEmphasisMarkStyle( m_aCurrentPDFState
.m_aFont
);
7925 if ( nEmphMark
& EMPHASISMARK_POS_BELOW
)
7926 nEmphHeight
= m_pReferenceDevice
->mnEmphasisDescent
;
7928 nEmphHeight
= m_pReferenceDevice
->mnEmphasisAscent
;
7929 m_pReferenceDevice
->ImplGetEmphasisMark( aEmphPoly
,
7936 m_pReferenceDevice
->ImplDevicePixelToLogicWidth(nEmphHeight
),
7937 m_pReferenceDevice
->mpFontEntry
->mnOrientation
);
7938 if ( bEmphPolyLine
)
7940 setLineColor( m_aCurrentPDFState
.m_aFont
.GetColor() );
7941 setFillColor( Color( COL_TRANSPARENT
) );
7945 setFillColor( m_aCurrentPDFState
.m_aFont
.GetColor() );
7946 setLineColor( Color( COL_TRANSPARENT
) );
7948 writeBuffer( aLine
.getStr(), aLine
.getLength() );
7950 Point aOffset
= Point(0,0);
7952 if ( nEmphMark
& EMPHASISMARK_POS_BELOW
)
7953 aOffset
.Y() += m_pReferenceDevice
->mpFontEntry
->maMetric
.mnDescent
+ nEmphYOff
;
7955 aOffset
.Y() -= m_pReferenceDevice
->mpFontEntry
->maMetric
.mnAscent
+ nEmphYOff
;
7957 long nEmphWidth2
= nEmphWidth
/ 2;
7958 long nEmphHeight2
= nEmphHeight
/ 2;
7959 aOffset
+= Point( nEmphWidth2
, nEmphHeight2
);
7961 if ( eAlign
== ALIGN_BOTTOM
)
7962 aOffset
.Y() -= m_pReferenceDevice
->mpFontEntry
->maMetric
.mnDescent
;
7963 else if ( eAlign
== ALIGN_TOP
)
7964 aOffset
.Y() += m_pReferenceDevice
->mpFontEntry
->maMetric
.mnAscent
;
7966 for( int nStart
= 0;;)
7969 sal_GlyphId nGlyphIndex
;
7971 if( !rLayout
.GetNextGlyphs( 1, &nGlyphIndex
, aPos
, nStart
, &nAdvance
) )
7974 if( !rLayout
.IsSpacingGlyph( nGlyphIndex
) )
7976 Point aAdjOffset
= aOffset
;
7977 aAdjOffset
.X() += (nAdvance
- nEmphWidth
) / 2;
7978 aAdjOffset
= aRotScale
.transform( aAdjOffset
);
7980 aAdjOffset
-= Point( nEmphWidth2
, nEmphHeight2
);
7983 aPos
= m_pReferenceDevice
->PixelToLogic( aPos
);
7984 drawEmphasisMark( aPos
.X(), aPos
.Y(),
7985 aEmphPoly
, bEmphPolyLine
,
7986 aEmphRect1
, aEmphRect2
);
7990 writeBuffer( "Q\n", 2 );
7996 void PDFWriterImpl::drawEmphasisMark( long nX
, long nY
,
7997 const PolyPolygon
& rPolyPoly
, sal_Bool bPolyLine
,
7998 const Rectangle
& rRect1
, const Rectangle
& rRect2
)
8000 // TODO: pass nWidth as width of this mark
8003 if ( rPolyPoly
.Count() )
8007 Polygon aPoly
= rPolyPoly
.GetObject( 0 );
8008 aPoly
.Move( nX
, nY
);
8009 drawPolyLine( aPoly
);
8013 PolyPolygon aPolyPoly
= rPolyPoly
;
8014 aPolyPoly
.Move( nX
, nY
);
8015 drawPolyPolygon( aPolyPoly
);
8019 if ( !rRect1
.IsEmpty() )
8021 Rectangle
aRect( Point( nX
+rRect1
.Left(),
8022 nY
+rRect1
.Top() ), rRect1
.GetSize() );
8023 drawRectangle( aRect
);
8026 if ( !rRect2
.IsEmpty() )
8028 Rectangle
aRect( Point( nX
+rRect2
.Left(),
8029 nY
+rRect2
.Top() ), rRect2
.GetSize() );
8031 drawRectangle( aRect
);
8035 void PDFWriterImpl::drawText( const Point
& rPos
, const String
& rText
, xub_StrLen nIndex
, xub_StrLen nLen
, bool bTextLines
)
8039 updateGraphicsState();
8041 // get a layout from the OuputDevice's SalGraphics
8042 // this also enforces font substitution and sets the font on SalGraphics
8043 SalLayout
* pLayout
= m_pReferenceDevice
->ImplLayout( rText
, nIndex
, nLen
, rPos
);
8046 drawLayout( *pLayout
, rText
, bTextLines
);
8051 void PDFWriterImpl::drawTextArray( const Point
& rPos
, const String
& rText
, const sal_Int32
* pDXArray
, xub_StrLen nIndex
, xub_StrLen nLen
, bool bTextLines
)
8053 MARK( "drawText with array" );
8055 updateGraphicsState();
8057 // get a layout from the OuputDevice's SalGraphics
8058 // this also enforces font substitution and sets the font on SalGraphics
8059 SalLayout
* pLayout
= m_pReferenceDevice
->ImplLayout( rText
, nIndex
, nLen
, rPos
, 0, pDXArray
);
8062 drawLayout( *pLayout
, rText
, bTextLines
);
8067 void PDFWriterImpl::drawStretchText( const Point
& rPos
, sal_uLong nWidth
, const String
& rText
, xub_StrLen nIndex
, xub_StrLen nLen
, bool bTextLines
)
8069 MARK( "drawStretchText" );
8071 updateGraphicsState();
8073 // get a layout from the OuputDevice's SalGraphics
8074 // this also enforces font substitution and sets the font on SalGraphics
8075 SalLayout
* pLayout
= m_pReferenceDevice
->ImplLayout( rText
, nIndex
, nLen
, rPos
, nWidth
);
8078 drawLayout( *pLayout
, rText
, bTextLines
);
8083 void PDFWriterImpl::drawText( const Rectangle
& rRect
, const String
& rOrigStr
, sal_uInt16 nStyle
, bool bTextLines
)
8085 long nWidth
= rRect
.GetWidth();
8086 long nHeight
= rRect
.GetHeight();
8088 if ( nWidth
<= 0 || nHeight
<= 0 )
8091 MARK( "drawText with rectangle" );
8093 updateGraphicsState();
8095 // clip with rectangle
8096 OStringBuffer aLine
;
8097 aLine
.append( "q " );
8098 m_aPages
.back().appendRect( rRect
, aLine
);
8099 aLine
.append( " W* n\n" );
8100 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8102 // if disabled text is needed, put in here
8104 Point aPos
= rRect
.TopLeft();
8106 long nTextHeight
= m_pReferenceDevice
->GetTextHeight();
8107 xub_StrLen nMnemonicPos
= STRING_NOTFOUND
;
8109 String aStr
= rOrigStr
;
8110 if ( nStyle
& TEXT_DRAW_MNEMONIC
)
8111 aStr
= m_pReferenceDevice
->GetNonMnemonicString( aStr
, nMnemonicPos
);
8114 if ( nStyle
& TEXT_DRAW_MULTILINE
)
8117 ImplMultiTextLineInfo aMultiLineInfo
;
8118 ImplTextLineInfo
* pLineInfo
;
8121 xub_StrLen nFormatLines
;
8125 ::vcl::DefaultTextLayout
aLayout( *m_pReferenceDevice
);
8126 OutputDevice::ImplGetTextLines( aMultiLineInfo
, nWidth
, aStr
, nStyle
, aLayout
);
8127 nLines
= (xub_StrLen
)(nHeight
/nTextHeight
);
8128 nFormatLines
= aMultiLineInfo
.Count();
8131 if ( nFormatLines
> nLines
)
8133 if ( nStyle
& TEXT_DRAW_ENDELLIPSIS
)
8136 nFormatLines
= nLines
-1;
8138 pLineInfo
= aMultiLineInfo
.GetLine( nFormatLines
);
8139 aLastLine
= convertLineEnd(aStr
.Copy(pLineInfo
->GetIndex()), LINEEND_LF
);
8140 // replace line feed by space
8141 aLastLine
= aLastLine
.replace('\n', ' ');
8142 aLastLine
= m_pReferenceDevice
->GetEllipsisString( aLastLine
, nWidth
, nStyle
);
8143 nStyle
&= ~(TEXT_DRAW_VCENTER
| TEXT_DRAW_BOTTOM
);
8144 nStyle
|= TEXT_DRAW_TOP
;
8148 // vertical alignment
8149 if ( nStyle
& TEXT_DRAW_BOTTOM
)
8150 aPos
.Y() += nHeight
-(nFormatLines
*nTextHeight
);
8151 else if ( nStyle
& TEXT_DRAW_VCENTER
)
8152 aPos
.Y() += (nHeight
-(nFormatLines
*nTextHeight
))/2;
8154 // draw all lines excluding the last
8155 for ( i
= 0; i
< nFormatLines
; i
++ )
8157 pLineInfo
= aMultiLineInfo
.GetLine( i
);
8158 if ( nStyle
& TEXT_DRAW_RIGHT
)
8159 aPos
.X() += nWidth
-pLineInfo
->GetWidth();
8160 else if ( nStyle
& TEXT_DRAW_CENTER
)
8161 aPos
.X() += (nWidth
-pLineInfo
->GetWidth())/2;
8162 xub_StrLen nIndex
= pLineInfo
->GetIndex();
8163 xub_StrLen nLineLen
= pLineInfo
->GetLen();
8164 drawText( aPos
, aStr
, nIndex
, nLineLen
, bTextLines
);
8165 // mnemonics should not appear in documents,
8166 // if the need arises, put them in here
8167 aPos
.Y() += nTextHeight
;
8168 aPos
.X() = rRect
.Left();
8172 // output last line left adjusted since it was shortened
8173 if (!aLastLine
.isEmpty())
8174 drawText( aPos
, aLastLine
, 0, STRING_LEN
, bTextLines
);
8179 long nTextWidth
= m_pReferenceDevice
->GetTextWidth( aStr
);
8181 // Evt. Text kuerzen
8182 if ( nTextWidth
> nWidth
)
8184 if ( nStyle
& (TEXT_DRAW_ENDELLIPSIS
| TEXT_DRAW_PATHELLIPSIS
| TEXT_DRAW_NEWSELLIPSIS
) )
8186 aStr
= m_pReferenceDevice
->GetEllipsisString( aStr
, nWidth
, nStyle
);
8187 nStyle
&= ~(TEXT_DRAW_CENTER
| TEXT_DRAW_RIGHT
);
8188 nStyle
|= TEXT_DRAW_LEFT
;
8189 nTextWidth
= m_pReferenceDevice
->GetTextWidth( aStr
);
8193 // vertical alignment
8194 if ( nStyle
& TEXT_DRAW_RIGHT
)
8195 aPos
.X() += nWidth
-nTextWidth
;
8196 else if ( nStyle
& TEXT_DRAW_CENTER
)
8197 aPos
.X() += (nWidth
-nTextWidth
)/2;
8199 if ( nStyle
& TEXT_DRAW_BOTTOM
)
8200 aPos
.Y() += nHeight
-nTextHeight
;
8201 else if ( nStyle
& TEXT_DRAW_VCENTER
)
8202 aPos
.Y() += (nHeight
-nTextHeight
)/2;
8204 // mnemonics should be inserted here if the need arises
8206 // draw the actual text
8207 drawText( aPos
, aStr
, 0, STRING_LEN
, bTextLines
);
8210 // reset clip region to original value
8211 aLine
.setLength( 0 );
8212 aLine
.append( "Q\n" );
8213 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8216 void PDFWriterImpl::drawLine( const Point
& rStart
, const Point
& rStop
)
8220 updateGraphicsState();
8222 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) )
8225 OStringBuffer aLine
;
8226 m_aPages
.back().appendPoint( rStart
, aLine
);
8227 aLine
.append( " m " );
8228 m_aPages
.back().appendPoint( rStop
, aLine
);
8229 aLine
.append( " l S\n" );
8231 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8234 void PDFWriterImpl::drawLine( const Point
& rStart
, const Point
& rStop
, const LineInfo
& rInfo
)
8236 MARK( "drawLine with LineInfo" );
8237 updateGraphicsState();
8239 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) )
8242 if( rInfo
.GetStyle() == LINE_SOLID
&& rInfo
.GetWidth() < 2 )
8244 drawLine( rStart
, rStop
);
8248 OStringBuffer aLine
;
8250 aLine
.append( "q " );
8251 if( m_aPages
.back().appendLineInfo( rInfo
, aLine
) )
8253 m_aPages
.back().appendPoint( rStart
, aLine
);
8254 aLine
.append( " m " );
8255 m_aPages
.back().appendPoint( rStop
, aLine
);
8256 aLine
.append( " l S Q\n" );
8258 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8262 PDFWriter::ExtLineInfo aInfo
;
8263 convertLineInfoToExtLineInfo( rInfo
, aInfo
);
8264 Point aPolyPoints
[2] = { rStart
, rStop
};
8265 Polygon
aPoly( 2, aPolyPoints
);
8266 drawPolyLine( aPoly
, aInfo
);
8270 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
8272 void PDFWriterImpl::drawWaveTextLine( OStringBuffer
& aLine
, long nWidth
, FontUnderline eTextLine
, Color aColor
, bool bIsAbove
)
8274 // note: units in pFontEntry are ref device pixel
8275 ImplFontEntry
* pFontEntry
= m_pReferenceDevice
->mpFontEntry
;
8276 long nLineHeight
= 0;
8279 appendStrokingColor( aColor
, aLine
);
8280 aLine
.append( "\n" );
8284 if ( !pFontEntry
->maMetric
.mnAboveWUnderlineSize
)
8285 m_pReferenceDevice
->ImplInitAboveTextLineSize();
8286 nLineHeight
= HCONV( pFontEntry
->maMetric
.mnAboveWUnderlineSize
);
8287 nLinePos
= HCONV( pFontEntry
->maMetric
.mnAboveWUnderlineOffset
);
8291 if ( !pFontEntry
->maMetric
.mnWUnderlineSize
)
8292 m_pReferenceDevice
->ImplInitTextLineSize();
8293 nLineHeight
= HCONV( pFontEntry
->maMetric
.mnWUnderlineSize
);
8294 nLinePos
= HCONV( pFontEntry
->maMetric
.mnWUnderlineOffset
);
8296 if ( (eTextLine
== UNDERLINE_SMALLWAVE
) && (nLineHeight
> 3) )
8299 long nLineWidth
= getReferenceDevice()->mnDPIX
/450;
8303 if ( eTextLine
== UNDERLINE_BOLDWAVE
)
8304 nLineWidth
= 3*nLineWidth
;
8306 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineWidth
, aLine
);
8307 aLine
.append( " w " );
8309 if ( eTextLine
== UNDERLINE_DOUBLEWAVE
)
8311 long nOrgLineHeight
= nLineHeight
;
8313 if ( nLineHeight
< 2 )
8315 if ( nOrgLineHeight
> 1 )
8320 long nLineDY
= nOrgLineHeight
-(nLineHeight
*2);
8321 if ( nLineDY
< nLineWidth
)
8322 nLineDY
= nLineWidth
;
8323 long nLineDY2
= nLineDY
/2;
8327 nLinePos
-= nLineWidth
-nLineDY2
;
8329 m_aPages
.back().appendWaveLine( nWidth
, -nLinePos
, 2*nLineHeight
, aLine
);
8331 nLinePos
+= nLineWidth
+nLineDY
;
8332 m_aPages
.back().appendWaveLine( nWidth
, -nLinePos
, 2*nLineHeight
, aLine
);
8336 if ( eTextLine
!= UNDERLINE_BOLDWAVE
)
8337 nLinePos
-= nLineWidth
/2;
8338 m_aPages
.back().appendWaveLine( nWidth
, -nLinePos
, nLineHeight
, aLine
);
8342 void PDFWriterImpl::drawStraightTextLine( OStringBuffer
& aLine
, long nWidth
, FontUnderline eTextLine
, Color aColor
, bool bIsAbove
)
8344 // note: units in pFontEntry are ref device pixel
8345 ImplFontEntry
* pFontEntry
= m_pReferenceDevice
->mpFontEntry
;
8346 long nLineHeight
= 0;
8350 if ( eTextLine
> UNDERLINE_BOLDWAVE
)
8351 eTextLine
= UNDERLINE_SINGLE
;
8353 switch ( eTextLine
)
8355 case UNDERLINE_SINGLE
:
8356 case UNDERLINE_DOTTED
:
8357 case UNDERLINE_DASH
:
8358 case UNDERLINE_LONGDASH
:
8359 case UNDERLINE_DASHDOT
:
8360 case UNDERLINE_DASHDOTDOT
:
8363 if ( !pFontEntry
->maMetric
.mnAboveUnderlineSize
)
8364 m_pReferenceDevice
->ImplInitAboveTextLineSize();
8365 nLineHeight
= HCONV( pFontEntry
->maMetric
.mnAboveUnderlineSize
);
8366 nLinePos
= HCONV( pFontEntry
->maMetric
.mnAboveUnderlineOffset
);
8370 if ( !pFontEntry
->maMetric
.mnUnderlineSize
)
8371 m_pReferenceDevice
->ImplInitTextLineSize();
8372 nLineHeight
= HCONV( pFontEntry
->maMetric
.mnUnderlineSize
);
8373 nLinePos
= HCONV( pFontEntry
->maMetric
.mnUnderlineOffset
);
8376 case UNDERLINE_BOLD
:
8377 case UNDERLINE_BOLDDOTTED
:
8378 case UNDERLINE_BOLDDASH
:
8379 case UNDERLINE_BOLDLONGDASH
:
8380 case UNDERLINE_BOLDDASHDOT
:
8381 case UNDERLINE_BOLDDASHDOTDOT
:
8384 if ( !pFontEntry
->maMetric
.mnAboveBUnderlineSize
)
8385 m_pReferenceDevice
->ImplInitAboveTextLineSize();
8386 nLineHeight
= HCONV( pFontEntry
->maMetric
.mnAboveBUnderlineSize
);
8387 nLinePos
= HCONV( pFontEntry
->maMetric
.mnAboveBUnderlineOffset
);
8391 if ( !pFontEntry
->maMetric
.mnBUnderlineSize
)
8392 m_pReferenceDevice
->ImplInitTextLineSize();
8393 nLineHeight
= HCONV( pFontEntry
->maMetric
.mnBUnderlineSize
);
8394 nLinePos
= HCONV( pFontEntry
->maMetric
.mnBUnderlineOffset
);
8395 nLinePos
+= nLineHeight
/2;
8398 case UNDERLINE_DOUBLE
:
8401 if ( !pFontEntry
->maMetric
.mnAboveDUnderlineSize
)
8402 m_pReferenceDevice
->ImplInitAboveTextLineSize();
8403 nLineHeight
= HCONV( pFontEntry
->maMetric
.mnAboveDUnderlineSize
);
8404 nLinePos
= HCONV( pFontEntry
->maMetric
.mnAboveDUnderlineOffset1
);
8405 nLinePos2
= HCONV( pFontEntry
->maMetric
.mnAboveDUnderlineOffset2
);
8409 if ( !pFontEntry
->maMetric
.mnDUnderlineSize
)
8410 m_pReferenceDevice
->ImplInitTextLineSize();
8411 nLineHeight
= HCONV( pFontEntry
->maMetric
.mnDUnderlineSize
);
8412 nLinePos
= HCONV( pFontEntry
->maMetric
.mnDUnderlineOffset1
);
8413 nLinePos2
= HCONV( pFontEntry
->maMetric
.mnDUnderlineOffset2
);
8421 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
, true );
8422 aLine
.append( " w " );
8423 appendStrokingColor( aColor
, aLine
);
8424 aLine
.append( "\n" );
8426 switch ( eTextLine
)
8428 case UNDERLINE_DOTTED
:
8429 case UNDERLINE_BOLDDOTTED
:
8430 aLine
.append( "[ " );
8431 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
, false );
8432 aLine
.append( " ] 0 d\n" );
8434 case UNDERLINE_DASH
:
8435 case UNDERLINE_LONGDASH
:
8436 case UNDERLINE_BOLDDASH
:
8437 case UNDERLINE_BOLDLONGDASH
:
8439 sal_Int32 nDashLength
= 4*nLineHeight
;
8440 sal_Int32 nVoidLength
= 2*nLineHeight
;
8441 if ( ( eTextLine
== UNDERLINE_LONGDASH
) || ( eTextLine
== UNDERLINE_BOLDLONGDASH
) )
8442 nDashLength
= 8*nLineHeight
;
8444 aLine
.append( "[ " );
8445 m_aPages
.back().appendMappedLength( nDashLength
, aLine
, false );
8446 aLine
.append( ' ' );
8447 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
8448 aLine
.append( " ] 0 d\n" );
8451 case UNDERLINE_DASHDOT
:
8452 case UNDERLINE_BOLDDASHDOT
:
8454 sal_Int32 nDashLength
= 4*nLineHeight
;
8455 sal_Int32 nVoidLength
= 2*nLineHeight
;
8456 aLine
.append( "[ " );
8457 m_aPages
.back().appendMappedLength( nDashLength
, aLine
, false );
8458 aLine
.append( ' ' );
8459 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
8460 aLine
.append( ' ' );
8461 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
, false );
8462 aLine
.append( ' ' );
8463 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
8464 aLine
.append( " ] 0 d\n" );
8467 case UNDERLINE_DASHDOTDOT
:
8468 case UNDERLINE_BOLDDASHDOTDOT
:
8470 sal_Int32 nDashLength
= 4*nLineHeight
;
8471 sal_Int32 nVoidLength
= 2*nLineHeight
;
8472 aLine
.append( "[ " );
8473 m_aPages
.back().appendMappedLength( nDashLength
, aLine
, false );
8474 aLine
.append( ' ' );
8475 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
8476 aLine
.append( ' ' );
8477 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
, false );
8478 aLine
.append( ' ' );
8479 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
8480 aLine
.append( ' ' );
8481 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
, false );
8482 aLine
.append( ' ' );
8483 m_aPages
.back().appendMappedLength( nVoidLength
, aLine
, false );
8484 aLine
.append( " ] 0 d\n" );
8491 aLine
.append( "0 " );
8492 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos
), aLine
, true );
8493 aLine
.append( " m " );
8494 m_aPages
.back().appendMappedLength( (sal_Int32
)nWidth
, aLine
, false );
8495 aLine
.append( ' ' );
8496 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos
), aLine
, true );
8497 aLine
.append( " l S\n" );
8498 if ( eTextLine
== UNDERLINE_DOUBLE
)
8500 aLine
.append( "0 " );
8501 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos2
-nLineHeight
), aLine
, true );
8502 aLine
.append( " m " );
8503 m_aPages
.back().appendMappedLength( (sal_Int32
)nWidth
, aLine
, false );
8504 aLine
.append( ' ' );
8505 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos2
-nLineHeight
), aLine
, true );
8506 aLine
.append( " l S\n" );
8511 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer
& aLine
, long nWidth
, FontStrikeout eStrikeout
, Color aColor
)
8513 // note: units in pFontEntry are ref device pixel
8514 ImplFontEntry
* pFontEntry
= m_pReferenceDevice
->mpFontEntry
;
8515 long nLineHeight
= 0;
8519 if ( eStrikeout
> STRIKEOUT_X
)
8520 eStrikeout
= STRIKEOUT_SINGLE
;
8522 switch ( eStrikeout
)
8524 case STRIKEOUT_SINGLE
:
8525 if ( !pFontEntry
->maMetric
.mnStrikeoutSize
)
8526 m_pReferenceDevice
->ImplInitTextLineSize();
8527 nLineHeight
= HCONV( pFontEntry
->maMetric
.mnStrikeoutSize
);
8528 nLinePos
= HCONV( pFontEntry
->maMetric
.mnStrikeoutOffset
);
8530 case STRIKEOUT_BOLD
:
8531 if ( !pFontEntry
->maMetric
.mnBStrikeoutSize
)
8532 m_pReferenceDevice
->ImplInitTextLineSize();
8533 nLineHeight
= HCONV( pFontEntry
->maMetric
.mnBStrikeoutSize
);
8534 nLinePos
= HCONV( pFontEntry
->maMetric
.mnBStrikeoutOffset
);
8536 case STRIKEOUT_DOUBLE
:
8537 if ( !pFontEntry
->maMetric
.mnDStrikeoutSize
)
8538 m_pReferenceDevice
->ImplInitTextLineSize();
8539 nLineHeight
= HCONV( pFontEntry
->maMetric
.mnDStrikeoutSize
);
8540 nLinePos
= HCONV( pFontEntry
->maMetric
.mnDStrikeoutOffset1
);
8541 nLinePos2
= HCONV( pFontEntry
->maMetric
.mnDStrikeoutOffset2
);
8549 m_aPages
.back().appendMappedLength( (sal_Int32
)nLineHeight
, aLine
, true );
8550 aLine
.append( " w " );
8551 appendStrokingColor( aColor
, aLine
);
8552 aLine
.append( "\n" );
8554 aLine
.append( "0 " );
8555 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos
), aLine
, true );
8556 aLine
.append( " m " );
8557 m_aPages
.back().appendMappedLength( (sal_Int32
)nWidth
, aLine
, true );
8558 aLine
.append( ' ' );
8559 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos
), aLine
, true );
8560 aLine
.append( " l S\n" );
8562 if ( eStrikeout
== STRIKEOUT_DOUBLE
)
8564 aLine
.append( "0 " );
8565 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos2
-nLineHeight
), aLine
, true );
8566 aLine
.append( " m " );
8567 m_aPages
.back().appendMappedLength( (sal_Int32
)nWidth
, aLine
, true );
8568 aLine
.append( ' ' );
8569 m_aPages
.back().appendMappedLength( (sal_Int32
)(-nLinePos2
-nLineHeight
), aLine
, true );
8570 aLine
.append( " l S\n" );
8575 void PDFWriterImpl::drawStrikeoutChar( const Point
& rPos
, long nWidth
, FontStrikeout eStrikeout
)
8577 //See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
8580 OUString aStrikeoutChar
= eStrikeout
== STRIKEOUT_SLASH
? OUString("/") : OUString("X");
8581 String aStrikeout
= aStrikeoutChar
;
8582 while( m_pReferenceDevice
->GetTextWidth( aStrikeout
) < nWidth
)
8583 aStrikeout
.Append( aStrikeout
);
8585 // do not get broader than nWidth modulo 1 character
8586 while( m_pReferenceDevice
->GetTextWidth( aStrikeout
) >= nWidth
)
8587 aStrikeout
.Erase( 0, 1 );
8588 aStrikeout
.Append( aStrikeoutChar
);
8589 sal_Bool bShadow
= m_aCurrentPDFState
.m_aFont
.IsShadow();
8592 Font aFont
= m_aCurrentPDFState
.m_aFont
;
8593 aFont
.SetShadow( sal_False
);
8595 updateGraphicsState();
8598 // strikeout string is left aligned non-CTL text
8599 sal_uLong nOrigTLM
= m_pReferenceDevice
->GetLayoutMode();
8600 m_pReferenceDevice
->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG
|TEXT_LAYOUT_COMPLEX_DISABLED
);
8602 push( PUSH_CLIPREGION
);
8603 FontMetric aRefDevFontMetric
= m_pReferenceDevice
->GetFontMetric();
8605 aRect
.Left() = rPos
.X();
8606 aRect
.Right() = aRect
.Left()+nWidth
;
8607 aRect
.Bottom() = rPos
.Y()+aRefDevFontMetric
.GetDescent();
8608 aRect
.Top() = rPos
.Y()-aRefDevFontMetric
.GetAscent();
8610 ImplFontEntry
* pFontEntry
= m_pReferenceDevice
->mpFontEntry
;
8611 if (pFontEntry
->mnOrientation
)
8613 Polygon
aPoly( aRect
);
8614 aPoly
.Rotate( rPos
, pFontEntry
->mnOrientation
);
8615 aRect
= aPoly
.GetBoundRect();
8618 intersectClipRegion( aRect
);
8619 drawText( rPos
, aStrikeout
, 0, aStrikeout
.Len(), false );
8622 m_pReferenceDevice
->SetLayoutMode( nOrigTLM
);
8626 Font aFont
= m_aCurrentPDFState
.m_aFont
;
8627 aFont
.SetShadow( sal_True
);
8629 updateGraphicsState();
8633 void PDFWriterImpl::drawTextLine( const Point
& rPos
, long nWidth
, FontStrikeout eStrikeout
, FontUnderline eUnderline
, FontUnderline eOverline
, bool bUnderlineAbove
)
8636 ( ((eStrikeout
== STRIKEOUT_NONE
)||(eStrikeout
== STRIKEOUT_DONTKNOW
)) &&
8637 ((eUnderline
== UNDERLINE_NONE
)||(eUnderline
== UNDERLINE_DONTKNOW
)) &&
8638 ((eOverline
== UNDERLINE_NONE
)||(eOverline
== UNDERLINE_DONTKNOW
)) ) )
8641 MARK( "drawTextLine" );
8642 updateGraphicsState();
8644 // note: units in pFontEntry are ref device pixel
8645 ImplFontEntry
* pFontEntry
= m_pReferenceDevice
->mpFontEntry
;
8646 Color aUnderlineColor
= m_aCurrentPDFState
.m_aTextLineColor
;
8647 Color aOverlineColor
= m_aCurrentPDFState
.m_aOverlineColor
;
8648 Color aStrikeoutColor
= m_aCurrentPDFState
.m_aFont
.GetColor();
8649 bool bStrikeoutDone
= false;
8650 bool bUnderlineDone
= false;
8651 bool bOverlineDone
= false;
8653 if ( (eStrikeout
== STRIKEOUT_SLASH
) || (eStrikeout
== STRIKEOUT_X
) )
8655 drawStrikeoutChar( rPos
, nWidth
, eStrikeout
);
8656 bStrikeoutDone
= true;
8660 TextAlign eAlign
= m_aCurrentPDFState
.m_aFont
.GetAlign();
8661 if( eAlign
== ALIGN_TOP
)
8662 aPos
.Y() += HCONV( pFontEntry
->maMetric
.mnAscent
);
8663 else if( eAlign
== ALIGN_BOTTOM
)
8664 aPos
.Y() -= HCONV( pFontEntry
->maMetric
.mnDescent
);
8666 OStringBuffer
aLine( 512 );
8668 aLine
.append( "q " );
8670 // rotate and translate matrix
8671 double fAngle
= (double)m_aCurrentPDFState
.m_aFont
.GetOrientation() * M_PI
/ 1800.0;
8673 aMat
.rotate( fAngle
);
8674 aMat
.translate( aPos
.X(), aPos
.Y() );
8675 aMat
.append( m_aPages
.back(), aLine
);
8676 aLine
.append( " cm\n" );
8678 if ( aUnderlineColor
.GetTransparency() != 0 )
8679 aUnderlineColor
= aStrikeoutColor
;
8681 if ( (eUnderline
== UNDERLINE_SMALLWAVE
) ||
8682 (eUnderline
== UNDERLINE_WAVE
) ||
8683 (eUnderline
== UNDERLINE_DOUBLEWAVE
) ||
8684 (eUnderline
== UNDERLINE_BOLDWAVE
) )
8686 drawWaveTextLine( aLine
, nWidth
, eUnderline
, aUnderlineColor
, bUnderlineAbove
);
8687 bUnderlineDone
= true;
8690 if ( (eOverline
== UNDERLINE_SMALLWAVE
) ||
8691 (eOverline
== UNDERLINE_WAVE
) ||
8692 (eOverline
== UNDERLINE_DOUBLEWAVE
) ||
8693 (eOverline
== UNDERLINE_BOLDWAVE
) )
8695 drawWaveTextLine( aLine
, nWidth
, eOverline
, aOverlineColor
, true );
8696 bOverlineDone
= true;
8699 if ( !bUnderlineDone
)
8701 drawStraightTextLine( aLine
, nWidth
, eUnderline
, aUnderlineColor
, bUnderlineAbove
);
8704 if ( !bOverlineDone
)
8706 drawStraightTextLine( aLine
, nWidth
, eOverline
, aOverlineColor
, true );
8709 if ( !bStrikeoutDone
)
8711 drawStrikeoutLine( aLine
, nWidth
, eStrikeout
, aStrikeoutColor
);
8714 aLine
.append( "Q\n" );
8715 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8718 void PDFWriterImpl::drawPolygon( const Polygon
& rPoly
)
8720 MARK( "drawPolygon" );
8722 updateGraphicsState();
8724 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
8725 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
8728 int nPoints
= rPoly
.GetSize();
8729 OStringBuffer
aLine( 20 * nPoints
);
8730 m_aPages
.back().appendPolygon( rPoly
, aLine
);
8731 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
8732 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
8733 aLine
.append( "B*\n" );
8734 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
8735 aLine
.append( "S\n" );
8737 aLine
.append( "f*\n" );
8739 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8742 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon
& rPolyPoly
)
8744 MARK( "drawPolyPolygon" );
8746 updateGraphicsState();
8748 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
8749 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
8752 int nPolygons
= rPolyPoly
.Count();
8754 OStringBuffer
aLine( 40 * nPolygons
);
8755 m_aPages
.back().appendPolyPolygon( rPolyPoly
, aLine
);
8756 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
8757 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
8758 aLine
.append( "B*\n" );
8759 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
8760 aLine
.append( "S\n" );
8762 aLine
.append( "f*\n" );
8764 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8767 void PDFWriterImpl::drawTransparent( const PolyPolygon
& rPolyPoly
, sal_uInt32 nTransparentPercent
)
8769 DBG_ASSERT( nTransparentPercent
<= 100, "invalid alpha value" );
8770 nTransparentPercent
= nTransparentPercent
% 100;
8772 MARK( "drawTransparent" );
8774 updateGraphicsState();
8776 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
8777 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
8780 if( m_bIsPDF_A1
|| m_aContext
.Version
< PDFWriter::PDF_1_4
)
8782 m_aErrors
.insert( m_bIsPDF_A1
?
8783 PDFWriter::Warning_Transparency_Omitted_PDFA
:
8784 PDFWriter::Warning_Transparency_Omitted_PDF13
);
8786 drawPolyPolygon( rPolyPoly
);
8791 m_aTransparentObjects
.push_back( TransparencyEmit() );
8792 // FIXME: polygons with beziers may yield incorrect bound rect
8793 m_aTransparentObjects
.back().m_aBoundRect
= rPolyPoly
.GetBoundRect();
8794 // convert rectangle to default user space
8795 m_aPages
.back().convertRect( m_aTransparentObjects
.back().m_aBoundRect
);
8796 m_aTransparentObjects
.back().m_nObject
= createObject();
8797 m_aTransparentObjects
.back().m_nExtGStateObject
= createObject();
8798 m_aTransparentObjects
.back().m_fAlpha
= (double)(100-nTransparentPercent
) / 100.0;
8799 m_aTransparentObjects
.back().m_pContentStream
= new SvMemoryStream( 256, 256 );
8800 // create XObject's content stream
8801 OStringBuffer
aContent( 256 );
8802 m_aPages
.back().appendPolyPolygon( rPolyPoly
, aContent
);
8803 if( m_aCurrentPDFState
.m_aLineColor
!= Color( COL_TRANSPARENT
) &&
8804 m_aCurrentPDFState
.m_aFillColor
!= Color( COL_TRANSPARENT
) )
8805 aContent
.append( " B*\n" );
8806 else if( m_aCurrentPDFState
.m_aLineColor
!= Color( COL_TRANSPARENT
) )
8807 aContent
.append( " S\n" );
8809 aContent
.append( " f*\n" );
8810 m_aTransparentObjects
.back().m_pContentStream
->Write( aContent
.getStr(), aContent
.getLength() );
8812 OStringBuffer
aObjName( 16 );
8813 aObjName
.append( "Tr" );
8814 aObjName
.append( m_aTransparentObjects
.back().m_nObject
);
8815 OString
aTrName( aObjName
.makeStringAndClear() );
8816 aObjName
.append( "EGS" );
8817 aObjName
.append( m_aTransparentObjects
.back().m_nExtGStateObject
);
8818 OString
aExtName( aObjName
.makeStringAndClear() );
8820 OStringBuffer
aLine( 80 );
8822 aLine
.append( "q /" );
8823 aLine
.append( aExtName
);
8824 aLine
.append( " gs /" );
8825 aLine
.append( aTrName
);
8826 aLine
.append( " Do Q\n" );
8827 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8829 pushResource( ResXObject
, aTrName
, m_aTransparentObjects
.back().m_nObject
);
8830 pushResource( ResExtGState
, aExtName
, m_aTransparentObjects
.back().m_nExtGStateObject
);
8833 void PDFWriterImpl::pushResource( ResourceKind eKind
, const OString
& rResource
, sal_Int32 nObject
)
8840 m_aGlobalResourceDict
.m_aXObjects
[ rResource
] = nObject
;
8841 if( ! m_aOutputStreams
.empty() )
8842 m_aOutputStreams
.front().m_aResourceDict
.m_aXObjects
[ rResource
] = nObject
;
8845 m_aGlobalResourceDict
.m_aExtGStates
[ rResource
] = nObject
;
8846 if( ! m_aOutputStreams
.empty() )
8847 m_aOutputStreams
.front().m_aResourceDict
.m_aExtGStates
[ rResource
] = nObject
;
8850 m_aGlobalResourceDict
.m_aShadings
[ rResource
] = nObject
;
8851 if( ! m_aOutputStreams
.empty() )
8852 m_aOutputStreams
.front().m_aResourceDict
.m_aShadings
[ rResource
] = nObject
;
8855 m_aGlobalResourceDict
.m_aPatterns
[ rResource
] = nObject
;
8856 if( ! m_aOutputStreams
.empty() )
8857 m_aOutputStreams
.front().m_aResourceDict
.m_aPatterns
[ rResource
] = nObject
;
8863 void PDFWriterImpl::beginRedirect( SvStream
* pStream
, const Rectangle
& rTargetRect
)
8867 // force reemitting clip region
8869 updateGraphicsState();
8871 m_aOutputStreams
.push_front( StreamRedirect() );
8872 m_aOutputStreams
.front().m_pStream
= pStream
;
8873 m_aOutputStreams
.front().m_aMapMode
= m_aMapMode
;
8875 if( !rTargetRect
.IsEmpty() )
8877 m_aOutputStreams
.front().m_aTargetRect
=
8878 lcl_convert( m_aGraphicsStack
.front().m_aMapMode
,
8880 getReferenceDevice(),
8882 Point aDelta
= m_aOutputStreams
.front().m_aTargetRect
.BottomLeft();
8883 long nPageHeight
= pointToPixel(m_aPages
[m_nCurrentPage
].getHeight());
8884 aDelta
.Y() = -(nPageHeight
- m_aOutputStreams
.front().m_aTargetRect
.Bottom());
8885 m_aMapMode
.SetOrigin( m_aMapMode
.GetOrigin() + aDelta
);
8888 // setup graphics state for independent object stream
8890 // force reemitting colors
8891 m_aCurrentPDFState
.m_aLineColor
= Color( COL_TRANSPARENT
);
8892 m_aCurrentPDFState
.m_aFillColor
= Color( COL_TRANSPARENT
);
8895 SvStream
* PDFWriterImpl::endRedirect()
8897 SvStream
* pStream
= NULL
;
8898 if( ! m_aOutputStreams
.empty() )
8900 pStream
= m_aOutputStreams
.front().m_pStream
;
8901 m_aMapMode
= m_aOutputStreams
.front().m_aMapMode
;
8902 m_aOutputStreams
.pop_front();
8906 // force reemitting colors and clip region
8908 m_aCurrentPDFState
.m_bClipRegion
= m_aGraphicsStack
.front().m_bClipRegion
;
8909 m_aCurrentPDFState
.m_aClipRegion
= m_aGraphicsStack
.front().m_aClipRegion
;
8910 m_aCurrentPDFState
.m_aLineColor
= Color( COL_TRANSPARENT
);
8911 m_aCurrentPDFState
.m_aFillColor
= Color( COL_TRANSPARENT
);
8913 updateGraphicsState();
8918 void PDFWriterImpl::beginTransparencyGroup()
8920 updateGraphicsState();
8921 if( m_aContext
.Version
>= PDFWriter::PDF_1_4
)
8922 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8925 void PDFWriterImpl::endTransparencyGroup( const Rectangle
& rBoundingBox
, sal_uInt32 nTransparentPercent
)
8927 DBG_ASSERT( nTransparentPercent
<= 100, "invalid alpha value" );
8928 nTransparentPercent
= nTransparentPercent
% 100;
8930 if( m_aContext
.Version
>= PDFWriter::PDF_1_4
)
8933 m_aTransparentObjects
.push_back( TransparencyEmit() );
8934 m_aTransparentObjects
.back().m_aBoundRect
= rBoundingBox
;
8935 // convert rectangle to default user space
8936 m_aPages
.back().convertRect( m_aTransparentObjects
.back().m_aBoundRect
);
8937 m_aTransparentObjects
.back().m_nObject
= createObject();
8938 m_aTransparentObjects
.back().m_fAlpha
= (double)(100-nTransparentPercent
) / 100.0;
8939 // get XObject's content stream
8940 m_aTransparentObjects
.back().m_pContentStream
= static_cast<SvMemoryStream
*>(endRedirect());
8941 m_aTransparentObjects
.back().m_nExtGStateObject
= createObject();
8943 OStringBuffer
aObjName( 16 );
8944 aObjName
.append( "Tr" );
8945 aObjName
.append( m_aTransparentObjects
.back().m_nObject
);
8946 OString
aTrName( aObjName
.makeStringAndClear() );
8947 aObjName
.append( "EGS" );
8948 aObjName
.append( m_aTransparentObjects
.back().m_nExtGStateObject
);
8949 OString
aExtName( aObjName
.makeStringAndClear() );
8951 OStringBuffer
aLine( 80 );
8953 aLine
.append( "q /" );
8954 aLine
.append( aExtName
);
8955 aLine
.append( " gs /" );
8956 aLine
.append( aTrName
);
8957 aLine
.append( " Do Q\n" );
8958 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8960 pushResource( ResXObject
, aTrName
, m_aTransparentObjects
.back().m_nObject
);
8961 pushResource( ResExtGState
, aExtName
, m_aTransparentObjects
.back().m_nExtGStateObject
);
8965 void PDFWriterImpl::drawRectangle( const Rectangle
& rRect
)
8967 MARK( "drawRectangle" );
8969 updateGraphicsState();
8971 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
8972 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
8975 OStringBuffer
aLine( 40 );
8976 m_aPages
.back().appendRect( rRect
, aLine
);
8978 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
8979 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
8980 aLine
.append( " B*\n" );
8981 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
8982 aLine
.append( " S\n" );
8984 aLine
.append( " f*\n" );
8986 writeBuffer( aLine
.getStr(), aLine
.getLength() );
8989 void PDFWriterImpl::drawRectangle( const Rectangle
& rRect
, sal_uInt32 nHorzRound
, sal_uInt32 nVertRound
)
8991 MARK( "drawRectangle with rounded edges" );
8993 if( !nHorzRound
&& !nVertRound
)
8994 drawRectangle( rRect
);
8996 updateGraphicsState();
8998 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
8999 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
9002 if( nHorzRound
> (sal_uInt32
)rRect
.GetWidth()/2 )
9003 nHorzRound
= rRect
.GetWidth()/2;
9004 if( nVertRound
> (sal_uInt32
)rRect
.GetHeight()/2 )
9005 nVertRound
= rRect
.GetHeight()/2;
9008 const double kappa
= 0.5522847498;
9009 const sal_uInt32 kx
= (sal_uInt32
)((kappa
*(double)nHorzRound
)+0.5);
9010 const sal_uInt32 ky
= (sal_uInt32
)((kappa
*(double)nVertRound
)+0.5);
9012 aPoints
[1] = Point( rRect
.TopLeft().X() + nHorzRound
, rRect
.TopLeft().Y() );
9013 aPoints
[0] = Point( aPoints
[1].X() - kx
, aPoints
[1].Y() );
9014 aPoints
[2] = Point( rRect
.TopRight().X()+1 - nHorzRound
, aPoints
[1].Y() );
9015 aPoints
[3] = Point( aPoints
[2].X()+kx
, aPoints
[2].Y() );
9017 aPoints
[5] = Point( rRect
.TopRight().X()+1, rRect
.TopRight().Y()+nVertRound
);
9018 aPoints
[4] = Point( aPoints
[5].X(), aPoints
[5].Y()-ky
);
9019 aPoints
[6] = Point( aPoints
[5].X(), rRect
.BottomRight().Y()+1 - nVertRound
);
9020 aPoints
[7] = Point( aPoints
[6].X(), aPoints
[6].Y()+ky
);
9022 aPoints
[9] = Point( rRect
.BottomRight().X()+1-nHorzRound
, rRect
.BottomRight().Y()+1 );
9023 aPoints
[8] = Point( aPoints
[9].X()+kx
, aPoints
[9].Y() );
9024 aPoints
[10] = Point( rRect
.BottomLeft().X() + nHorzRound
, aPoints
[9].Y() );
9025 aPoints
[11] = Point( aPoints
[10].X()-kx
, aPoints
[10].Y() );
9027 aPoints
[13] = Point( rRect
.BottomLeft().X(), rRect
.BottomLeft().Y()+1-nVertRound
);
9028 aPoints
[12] = Point( aPoints
[13].X(), aPoints
[13].Y()+ky
);
9029 aPoints
[14] = Point( rRect
.TopLeft().X(), rRect
.TopLeft().Y()+nVertRound
);
9030 aPoints
[15] = Point( aPoints
[14].X(), aPoints
[14].Y()-ky
);
9033 OStringBuffer
aLine( 80 );
9034 m_aPages
.back().appendPoint( aPoints
[1], aLine
);
9035 aLine
.append( " m " );
9036 m_aPages
.back().appendPoint( aPoints
[2], aLine
);
9037 aLine
.append( " l " );
9038 m_aPages
.back().appendPoint( aPoints
[3], aLine
);
9039 aLine
.append( ' ' );
9040 m_aPages
.back().appendPoint( aPoints
[4], aLine
);
9041 aLine
.append( ' ' );
9042 m_aPages
.back().appendPoint( aPoints
[5], aLine
);
9043 aLine
.append( " c\n" );
9044 m_aPages
.back().appendPoint( aPoints
[6], aLine
);
9045 aLine
.append( " l " );
9046 m_aPages
.back().appendPoint( aPoints
[7], aLine
);
9047 aLine
.append( ' ' );
9048 m_aPages
.back().appendPoint( aPoints
[8], aLine
);
9049 aLine
.append( ' ' );
9050 m_aPages
.back().appendPoint( aPoints
[9], aLine
);
9051 aLine
.append( " c\n" );
9052 m_aPages
.back().appendPoint( aPoints
[10], aLine
);
9053 aLine
.append( " l " );
9054 m_aPages
.back().appendPoint( aPoints
[11], aLine
);
9055 aLine
.append( ' ' );
9056 m_aPages
.back().appendPoint( aPoints
[12], aLine
);
9057 aLine
.append( ' ' );
9058 m_aPages
.back().appendPoint( aPoints
[13], aLine
);
9059 aLine
.append( " c\n" );
9060 m_aPages
.back().appendPoint( aPoints
[14], aLine
);
9061 aLine
.append( " l " );
9062 m_aPages
.back().appendPoint( aPoints
[15], aLine
);
9063 aLine
.append( ' ' );
9064 m_aPages
.back().appendPoint( aPoints
[0], aLine
);
9065 aLine
.append( ' ' );
9066 m_aPages
.back().appendPoint( aPoints
[1], aLine
);
9067 aLine
.append( " c " );
9069 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
9070 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
9071 aLine
.append( "b*\n" );
9072 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
9073 aLine
.append( "s\n" );
9075 aLine
.append( "f*\n" );
9077 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9080 void PDFWriterImpl::drawEllipse( const Rectangle
& rRect
)
9082 MARK( "drawEllipse" );
9084 updateGraphicsState();
9086 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
9087 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
9091 const double kappa
= 0.5522847498;
9092 const sal_uInt32 kx
= (sal_uInt32
)((kappa
*(double)rRect
.GetWidth()/2.0)+0.5);
9093 const sal_uInt32 ky
= (sal_uInt32
)((kappa
*(double)rRect
.GetHeight()/2.0)+0.5);
9095 aPoints
[1] = Point( rRect
.TopLeft().X() + rRect
.GetWidth()/2, rRect
.TopLeft().Y() );
9096 aPoints
[0] = Point( aPoints
[1].X() - kx
, aPoints
[1].Y() );
9097 aPoints
[2] = Point( aPoints
[1].X() + kx
, aPoints
[1].Y() );
9099 aPoints
[4] = Point( rRect
.TopRight().X()+1, rRect
.TopRight().Y() + rRect
.GetHeight()/2 );
9100 aPoints
[3] = Point( aPoints
[4].X(), aPoints
[4].Y() - ky
);
9101 aPoints
[5] = Point( aPoints
[4].X(), aPoints
[4].Y() + ky
);
9103 aPoints
[7] = Point( rRect
.BottomLeft().X() + rRect
.GetWidth()/2, rRect
.BottomLeft().Y()+1 );
9104 aPoints
[6] = Point( aPoints
[7].X() + kx
, aPoints
[7].Y() );
9105 aPoints
[8] = Point( aPoints
[7].X() - kx
, aPoints
[7].Y() );
9107 aPoints
[10] = Point( rRect
.TopLeft().X(), rRect
.TopLeft().Y() + rRect
.GetHeight()/2 );
9108 aPoints
[9] = Point( aPoints
[10].X(), aPoints
[10].Y() + ky
);
9109 aPoints
[11] = Point( aPoints
[10].X(), aPoints
[10].Y() - ky
);
9111 OStringBuffer
aLine( 80 );
9112 m_aPages
.back().appendPoint( aPoints
[1], aLine
);
9113 aLine
.append( " m " );
9114 m_aPages
.back().appendPoint( aPoints
[2], aLine
);
9115 aLine
.append( ' ' );
9116 m_aPages
.back().appendPoint( aPoints
[3], aLine
);
9117 aLine
.append( ' ' );
9118 m_aPages
.back().appendPoint( aPoints
[4], aLine
);
9119 aLine
.append( " c\n" );
9120 m_aPages
.back().appendPoint( aPoints
[5], aLine
);
9121 aLine
.append( ' ' );
9122 m_aPages
.back().appendPoint( aPoints
[6], aLine
);
9123 aLine
.append( ' ' );
9124 m_aPages
.back().appendPoint( aPoints
[7], aLine
);
9125 aLine
.append( " c\n" );
9126 m_aPages
.back().appendPoint( aPoints
[8], aLine
);
9127 aLine
.append( ' ' );
9128 m_aPages
.back().appendPoint( aPoints
[9], aLine
);
9129 aLine
.append( ' ' );
9130 m_aPages
.back().appendPoint( aPoints
[10], aLine
);
9131 aLine
.append( " c\n" );
9132 m_aPages
.back().appendPoint( aPoints
[11], aLine
);
9133 aLine
.append( ' ' );
9134 m_aPages
.back().appendPoint( aPoints
[0], aLine
);
9135 aLine
.append( ' ' );
9136 m_aPages
.back().appendPoint( aPoints
[1], aLine
);
9137 aLine
.append( " c " );
9139 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
9140 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
9141 aLine
.append( "b*\n" );
9142 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
9143 aLine
.append( "s\n" );
9145 aLine
.append( "f*\n" );
9147 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9150 static double calcAngle( const Rectangle
& rRect
, const Point
& rPoint
)
9152 Point
aOrigin((rRect
.Left()+rRect
.Right()+1)/2,
9153 (rRect
.Top()+rRect
.Bottom()+1)/2);
9154 Point aPoint
= rPoint
- aOrigin
;
9156 double fX
= (double)aPoint
.X();
9157 double fY
= (double)-aPoint
.Y();
9159 if( rRect
.GetWidth() > rRect
.GetHeight() )
9160 fY
= fY
*((double)rRect
.GetWidth()/(double)rRect
.GetHeight());
9161 else if( rRect
.GetHeight() > rRect
.GetWidth() )
9162 fX
= fX
*((double)rRect
.GetHeight()/(double)rRect
.GetWidth());
9163 return atan2( fY
, fX
);
9166 void PDFWriterImpl::drawArc( const Rectangle
& rRect
, const Point
& rStart
, const Point
& rStop
, bool bWithPie
, bool bWithChord
)
9170 updateGraphicsState();
9172 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) &&
9173 m_aGraphicsStack
.front().m_aFillColor
== Color( COL_TRANSPARENT
) )
9176 // calculate start and stop angles
9177 const double fStartAngle
= calcAngle( rRect
, rStart
);
9178 double fStopAngle
= calcAngle( rRect
, rStop
);
9179 while( fStopAngle
< fStartAngle
)
9180 fStopAngle
+= 2.0*M_PI
;
9181 const int nFragments
= (int)((fStopAngle
-fStartAngle
)/(M_PI
/2.0))+1;
9182 const double fFragmentDelta
= (fStopAngle
-fStartAngle
)/(double)nFragments
;
9183 const double kappa
= fabs( 4.0 * (1.0-cos(fFragmentDelta
/2.0))/sin(fFragmentDelta
/2.0) / 3.0);
9184 const double halfWidth
= (double)rRect
.GetWidth()/2.0;
9185 const double halfHeight
= (double)rRect
.GetHeight()/2.0;
9187 const Point
aCenter( (rRect
.Left()+rRect
.Right()+1)/2,
9188 (rRect
.Top()+rRect
.Bottom()+1)/2 );
9190 OStringBuffer
aLine( 30*nFragments
);
9191 Point
aPoint( (int)(halfWidth
* cos(fStartAngle
) ),
9192 -(int)(halfHeight
* sin(fStartAngle
) ) );
9194 m_aPages
.back().appendPoint( aPoint
, aLine
);
9195 aLine
.append( " m " );
9196 if( !basegfx::fTools::equal(fStartAngle
, fStopAngle
) )
9198 for( int i
= 0; i
< nFragments
; i
++ )
9200 const double fStartFragment
= fStartAngle
+ (double)i
*fFragmentDelta
;
9201 const double fStopFragment
= fStartFragment
+ fFragmentDelta
;
9202 aPoint
= Point( (int)(halfWidth
* (cos(fStartFragment
) - kappa
*sin(fStartFragment
) ) ),
9203 -(int)(halfHeight
* (sin(fStartFragment
) + kappa
*cos(fStartFragment
) ) ) );
9205 m_aPages
.back().appendPoint( aPoint
, aLine
);
9206 aLine
.append( ' ' );
9208 aPoint
= Point( (int)(halfWidth
* (cos(fStopFragment
) + kappa
*sin(fStopFragment
) ) ),
9209 -(int)(halfHeight
* (sin(fStopFragment
) - kappa
*cos(fStopFragment
) ) ) );
9211 m_aPages
.back().appendPoint( aPoint
, aLine
);
9212 aLine
.append( ' ' );
9214 aPoint
= Point( (int)(halfWidth
* cos(fStopFragment
) ),
9215 -(int)(halfHeight
* sin(fStopFragment
) ) );
9217 m_aPages
.back().appendPoint( aPoint
, aLine
);
9218 aLine
.append( " c\n" );
9221 if( bWithChord
|| bWithPie
)
9225 m_aPages
.back().appendPoint( aCenter
, aLine
);
9226 aLine
.append( " l " );
9228 aLine
.append( "h " );
9230 if( ! bWithChord
&& ! bWithPie
)
9231 aLine
.append( "S\n" );
9232 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) &&
9233 m_aGraphicsStack
.front().m_aFillColor
!= Color( COL_TRANSPARENT
) )
9234 aLine
.append( "B*\n" );
9235 else if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
9236 aLine
.append( "S\n" );
9238 aLine
.append( "f*\n" );
9240 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9243 void PDFWriterImpl::drawPolyLine( const Polygon
& rPoly
)
9245 MARK( "drawPolyLine" );
9247 sal_uInt16 nPoints
= rPoly
.GetSize();
9251 updateGraphicsState();
9253 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) )
9256 OStringBuffer
aLine( 20 * nPoints
);
9257 m_aPages
.back().appendPolygon( rPoly
, aLine
, rPoly
[0] == rPoly
[nPoints
-1] );
9258 aLine
.append( "S\n" );
9260 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9263 void PDFWriterImpl::drawPolyLine( const Polygon
& rPoly
, const LineInfo
& rInfo
)
9265 MARK( "drawPolyLine with LineInfo" );
9267 updateGraphicsState();
9269 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) )
9272 OStringBuffer aLine
;
9273 aLine
.append( "q " );
9274 if( m_aPages
.back().appendLineInfo( rInfo
, aLine
) )
9276 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9277 drawPolyLine( rPoly
);
9278 writeBuffer( "Q\n", 2 );
9282 PDFWriter::ExtLineInfo aInfo
;
9283 convertLineInfoToExtLineInfo( rInfo
, aInfo
);
9284 drawPolyLine( rPoly
, aInfo
);
9288 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo
& rIn
, PDFWriter::ExtLineInfo
& rOut
)
9290 DBG_ASSERT( rIn
.GetStyle() == LINE_DASH
, "invalid conversion" );
9291 rOut
.m_fLineWidth
= rIn
.GetWidth();
9292 rOut
.m_fTransparency
= 0.0;
9293 rOut
.m_eCap
= PDFWriter::capButt
;
9294 rOut
.m_eJoin
= PDFWriter::joinMiter
;
9295 rOut
.m_fMiterLimit
= 10;
9296 rOut
.m_aDashArray
.clear();
9298 // add DashDot to DashArray
9299 const int nDashes
= rIn
.GetDashCount();
9300 const int nDashLen
= rIn
.GetDashLen();
9301 const int nDistance
= rIn
.GetDistance();
9303 for( int n
= 0; n
< nDashes
; n
++ )
9305 rOut
.m_aDashArray
.push_back( nDashLen
);
9306 rOut
.m_aDashArray
.push_back( nDistance
);
9308 const int nDots
= rIn
.GetDotCount();
9309 const int nDotLen
= rIn
.GetDotLen();
9311 for( int n
= 0; n
< nDots
; n
++ )
9313 rOut
.m_aDashArray
.push_back( nDotLen
);
9314 rOut
.m_aDashArray
.push_back( nDistance
);
9318 switch(rIn
.GetLineJoin())
9320 case basegfx::B2DLINEJOIN_BEVEL
:
9322 rOut
.m_eJoin
= PDFWriter::joinBevel
;
9325 default : // basegfx::B2DLINEJOIN_NONE :
9326 // Pdf has no 'none' lineJoin, default is miter
9327 case basegfx::B2DLINEJOIN_MIDDLE
:
9328 case basegfx::B2DLINEJOIN_MITER
:
9330 rOut
.m_eJoin
= PDFWriter::joinMiter
;
9333 case basegfx::B2DLINEJOIN_ROUND
:
9335 rOut
.m_eJoin
= PDFWriter::joinRound
;
9341 switch(rIn
.GetLineCap())
9343 default: /* com::sun::star::drawing::LineCap_BUTT */
9345 rOut
.m_eCap
= PDFWriter::capButt
;
9348 case com::sun::star::drawing::LineCap_ROUND
:
9350 rOut
.m_eCap
= PDFWriter::capRound
;
9353 case com::sun::star::drawing::LineCap_SQUARE
:
9355 rOut
.m_eCap
= PDFWriter::capSquare
;
9361 void PDFWriterImpl::drawPolyLine( const Polygon
& rPoly
, const PDFWriter::ExtLineInfo
& rInfo
)
9363 MARK( "drawPolyLine with ExtLineInfo" );
9365 updateGraphicsState();
9367 if( m_aGraphicsStack
.front().m_aLineColor
== Color( COL_TRANSPARENT
) )
9370 if( rInfo
.m_fTransparency
>= 1.0 )
9373 if( rInfo
.m_fTransparency
!= 0.0 )
9374 beginTransparencyGroup();
9376 OStringBuffer aLine
;
9377 aLine
.append( "q " );
9378 m_aPages
.back().appendMappedLength( rInfo
.m_fLineWidth
, aLine
);
9379 aLine
.append( " w" );
9380 if( rInfo
.m_aDashArray
.size() < 10 ) // implmentation limit of acrobat reader
9382 switch( rInfo
.m_eCap
)
9385 case PDFWriter::capButt
: aLine
.append( " 0 J" );break;
9386 case PDFWriter::capRound
: aLine
.append( " 1 J" );break;
9387 case PDFWriter::capSquare
: aLine
.append( " 2 J" );break;
9389 switch( rInfo
.m_eJoin
)
9392 case PDFWriter::joinMiter
:
9394 double fLimit
= rInfo
.m_fMiterLimit
;
9395 if( rInfo
.m_fLineWidth
< rInfo
.m_fMiterLimit
)
9396 fLimit
= fLimit
/ rInfo
.m_fLineWidth
;
9399 aLine
.append( " 0 j " );
9400 appendDouble( fLimit
, aLine
);
9401 aLine
.append( " M" );
9404 case PDFWriter::joinRound
: aLine
.append( " 1 j" );break;
9405 case PDFWriter::joinBevel
: aLine
.append( " 2 j" );break;
9407 if( rInfo
.m_aDashArray
.size() > 0 )
9409 aLine
.append( " [ " );
9410 for( std::vector
<double>::const_iterator it
= rInfo
.m_aDashArray
.begin();
9411 it
!= rInfo
.m_aDashArray
.end(); ++it
)
9413 m_aPages
.back().appendMappedLength( *it
, aLine
);
9414 aLine
.append( ' ' );
9416 aLine
.append( "] 0 d" );
9418 aLine
.append( "\n" );
9419 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9420 drawPolyLine( rPoly
);
9424 basegfx::B2DPolygon
aPoly(rPoly
.getB2DPolygon());
9425 basegfx::B2DPolyPolygon aPolyPoly
;
9427 basegfx::tools::applyLineDashing(aPoly
, rInfo
.m_aDashArray
, &aPolyPoly
);
9429 // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
9430 // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
9431 // this line needs to be removed and the loop below adapted accordingly
9432 aPolyPoly
= basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly
);
9434 const sal_uInt32
nPolygonCount(aPolyPoly
.count());
9436 for( sal_uInt32 nPoly
= 0; nPoly
< nPolygonCount
; nPoly
++ )
9438 aLine
.append( (nPoly
!= 0 && (nPoly
& 7) == 0) ? "\n" : " " );
9439 aPoly
= aPolyPoly
.getB2DPolygon( nPoly
);
9440 const sal_uInt32
nPointCount(aPoly
.count());
9444 const sal_uInt32
nEdgeCount(aPoly
.isClosed() ? nPointCount
: nPointCount
- 1);
9445 basegfx::B2DPoint
aCurrent(aPoly
.getB2DPoint(0));
9447 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
9450 aLine
.append( " " );
9451 const sal_uInt32
nNextIndex((a
+ 1) % nPointCount
);
9452 const basegfx::B2DPoint
aNext(aPoly
.getB2DPoint(nNextIndex
));
9454 m_aPages
.back().appendPoint( Point( FRound(aCurrent
.getX()),
9455 FRound(aCurrent
.getY()) ),
9457 aLine
.append( " m " );
9458 m_aPages
.back().appendPoint( Point( FRound(aNext
.getX()),
9459 FRound(aNext
.getY()) ),
9461 aLine
.append( " l" );
9463 // prepare next edge
9468 aLine
.append( " S " );
9469 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9471 writeBuffer( "Q\n", 2 );
9473 if( rInfo
.m_fTransparency
!= 0.0 )
9475 // FIXME: actually this may be incorrect with bezier polygons
9476 Rectangle
aBoundRect( rPoly
.GetBoundRect() );
9477 // avoid clipping with thick lines
9478 if( rInfo
.m_fLineWidth
> 0.0 )
9480 sal_Int32 nLW
= sal_Int32(rInfo
.m_fLineWidth
);
9481 aBoundRect
.Top() -= nLW
;
9482 aBoundRect
.Left() -= nLW
;
9483 aBoundRect
.Right() += nLW
;
9484 aBoundRect
.Bottom() += nLW
;
9486 endTransparencyGroup( aBoundRect
, (sal_uInt16
)(100.0*rInfo
.m_fTransparency
) );
9490 void PDFWriterImpl::drawPixel( const Point
& rPoint
, const Color
& rColor
)
9492 MARK( "drawPixel" );
9494 Color aColor
= ( rColor
== Color( COL_TRANSPARENT
) ? m_aGraphicsStack
.front().m_aLineColor
: rColor
);
9496 if( aColor
== Color( COL_TRANSPARENT
) )
9499 // pixels are drawn in line color, so have to set
9500 // the nonstroking color to line color
9501 Color aOldFillColor
= m_aGraphicsStack
.front().m_aFillColor
;
9502 setFillColor( aColor
);
9504 updateGraphicsState();
9506 OStringBuffer
aLine( 20 );
9507 m_aPages
.back().appendPoint( rPoint
, aLine
);
9508 aLine
.append( ' ' );
9509 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine
);
9510 aLine
.append( ' ' );
9511 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine
);
9512 aLine
.append( " re f\n" );
9513 writeBuffer( aLine
.getStr(), aLine
.getLength() );
9515 setFillColor( aOldFillColor
);
9518 class AccessReleaser
9520 BitmapReadAccess
* m_pAccess
;
9522 AccessReleaser( BitmapReadAccess
* pAccess
) : m_pAccess( pAccess
){}
9523 ~AccessReleaser() { delete m_pAccess
; }
9526 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit
& rObject
)
9528 CHECK_RETURN( updateObject( rObject
.m_nObject
) );
9530 bool bFlateFilter
= compressStream( rObject
.m_pContentStream
);
9531 rObject
.m_pContentStream
->Seek( STREAM_SEEK_TO_END
);
9532 sal_uLong nSize
= rObject
.m_pContentStream
->Tell();
9533 rObject
.m_pContentStream
->Seek( STREAM_SEEK_TO_BEGIN
);
9534 #if OSL_DEBUG_LEVEL > 1
9535 emitComment( "PDFWriterImpl::writeTransparentObject" );
9537 OStringBuffer
aLine( 512 );
9538 CHECK_RETURN( updateObject( rObject
.m_nObject
) );
9539 aLine
.append( rObject
.m_nObject
);
9540 aLine
.append( " 0 obj\n"
9544 appendFixedInt( rObject
.m_aBoundRect
.Left(), aLine
);
9545 aLine
.append( ' ' );
9546 appendFixedInt( rObject
.m_aBoundRect
.Top(), aLine
);
9547 aLine
.append( ' ' );
9548 appendFixedInt( rObject
.m_aBoundRect
.Right(), aLine
);
9549 aLine
.append( ' ' );
9550 appendFixedInt( rObject
.m_aBoundRect
.Bottom()+1, aLine
);
9551 aLine
.append( " ]\n" );
9552 if( ! rObject
.m_pSoftMaskStream
)
9556 aLine
.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
9559 /* #i42884# the PDF reference recommends that each Form XObject
9560 * should have a resource dict; alas if that is the same object
9561 * as the one of the page it triggers an endless recursion in
9562 * acroread 5 (6 and up have that fixed). Since we have only one
9563 * resource dict anyway, let's use the one from the page by NOT
9564 * emitting a Resources entry.
9567 aLine
.append( "/Length " );
9568 aLine
.append( (sal_Int32
)(nSize
) );
9569 aLine
.append( "\n" );
9571 aLine
.append( "/Filter/FlateDecode\n" );
9572 aLine
.append( ">>\n"
9574 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
9575 checkAndEnableStreamEncryption( rObject
.m_nObject
);
9576 CHECK_RETURN( writeBuffer( rObject
.m_pContentStream
->GetData(), nSize
) );
9577 disableStreamEncryption();
9578 aLine
.setLength( 0 );
9582 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
9584 // write ExtGState dict for this XObject
9585 aLine
.setLength( 0 );
9586 aLine
.append( rObject
.m_nExtGStateObject
);
9587 aLine
.append( " 0 obj\n"
9589 if( ! rObject
.m_pSoftMaskStream
)
9594 aLine
.append( "/CA 1.0/ca 1.0" );
9595 m_aErrors
.insert( PDFWriter::Warning_Transparency_Omitted_PDFA
);
9599 aLine
.append( "/CA " );
9600 appendDouble( rObject
.m_fAlpha
, aLine
);
9603 appendDouble( rObject
.m_fAlpha
, aLine
);
9605 aLine
.append( "\n" );
9611 aLine
.append( "/SMask/None" );
9612 m_aErrors
.insert( PDFWriter::Warning_Transparency_Omitted_PDFA
);
9616 rObject
.m_pSoftMaskStream
->Seek( STREAM_SEEK_TO_END
);
9617 sal_Int32 nMaskSize
= (sal_Int32
)rObject
.m_pSoftMaskStream
->Tell();
9618 rObject
.m_pSoftMaskStream
->Seek( STREAM_SEEK_TO_BEGIN
);
9619 sal_Int32 nMaskObject
= createObject();
9620 aLine
.append( "/SMask<</Type/Mask/S/Luminosity/G " );
9621 aLine
.append( nMaskObject
);
9622 aLine
.append( " 0 R>>\n" );
9624 OStringBuffer aMask
;
9625 aMask
.append( nMaskObject
);
9626 aMask
.append( " 0 obj\n"
9630 appendFixedInt( rObject
.m_aBoundRect
.Left(), aMask
);
9631 aMask
.append( ' ' );
9632 appendFixedInt( rObject
.m_aBoundRect
.Top(), aMask
);
9633 aMask
.append( ' ' );
9634 appendFixedInt( rObject
.m_aBoundRect
.Right(), aMask
);
9635 aMask
.append( ' ' );
9636 appendFixedInt( rObject
.m_aBoundRect
.Bottom()+1, aMask
);
9637 aMask
.append( "]\n" );
9639 /* #i42884# see above */
9640 aMask
.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
9641 aMask
.append( "/Length " );
9642 aMask
.append( nMaskSize
);
9643 aMask
.append( ">>\n"
9645 CHECK_RETURN( updateObject( nMaskObject
) );
9646 checkAndEnableStreamEncryption( nMaskObject
);
9647 CHECK_RETURN( writeBuffer( aMask
.getStr(), aMask
.getLength() ) );
9648 CHECK_RETURN( writeBuffer( rObject
.m_pSoftMaskStream
->GetData(), nMaskSize
) );
9649 disableStreamEncryption();
9650 aMask
.setLength( 0 );
9651 aMask
.append( "\nendstream\n"
9653 CHECK_RETURN( writeBuffer( aMask
.getStr(), aMask
.getLength() ) );
9656 aLine
.append( ">>\n"
9658 CHECK_RETURN( updateObject( rObject
.m_nExtGStateObject
) );
9659 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
9664 bool PDFWriterImpl::writeGradientFunction( GradientEmit
& rObject
)
9666 sal_Int32 nFunctionObject
= createObject();
9667 CHECK_RETURN( updateObject( nFunctionObject
) );
9670 aDev
.SetOutputSizePixel( rObject
.m_aSize
);
9671 aDev
.SetMapMode( MapMode( MAP_PIXEL
) );
9672 if( m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
)
9673 aDev
.SetDrawMode( aDev
.GetDrawMode() |
9674 ( DRAWMODE_GRAYLINE
| DRAWMODE_GRAYFILL
| DRAWMODE_GRAYTEXT
|
9675 DRAWMODE_GRAYBITMAP
| DRAWMODE_GRAYGRADIENT
) );
9676 aDev
.DrawGradient( Rectangle( Point( 0, 0 ), rObject
.m_aSize
), rObject
.m_aGradient
);
9678 Bitmap aSample
= aDev
.GetBitmap( Point( 0, 0 ), rObject
.m_aSize
);
9679 BitmapReadAccess
* pAccess
= aSample
.AcquireReadAccess();
9680 AccessReleaser
aReleaser( pAccess
);
9682 Size aSize
= aSample
.GetSizePixel();
9684 sal_Int32 nStreamLengthObject
= createObject();
9685 #if OSL_DEBUG_LEVEL > 1
9686 emitComment( "PDFWriterImpl::writeGradientFunction" );
9688 OStringBuffer
aLine( 120 );
9689 aLine
.append( nFunctionObject
);
9690 aLine
.append( " 0 obj\n"
9691 "<</FunctionType 0\n"
9692 "/Domain[ 0 1 0 1 ]\n"
9694 aLine
.append( (sal_Int32
)aSize
.Width() );
9695 aLine
.append( ' ' );
9696 aLine
.append( (sal_Int32
)aSize
.Height() );
9697 aLine
.append( " ]\n"
9698 "/BitsPerSample 8\n"
9699 "/Range[ 0 1 0 1 0 1 ]\n"
9702 aLine
.append( nStreamLengthObject
);
9703 aLine
.append( " 0 R\n"
9704 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9705 "/Filter/FlateDecode"
9709 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
9711 sal_uInt64 nStartStreamPos
= 0;
9712 CHECK_RETURN( (osl_File_E_None
== osl_getFilePos( m_aFile
, &nStartStreamPos
)) );
9714 checkAndEnableStreamEncryption( nFunctionObject
);
9716 for( int y
= aSize
.Height()-1; y
>= 0; y
-- )
9718 for( int x
= 0; x
< aSize
.Width(); x
++ )
9721 BitmapColor aColor
= pAccess
->GetColor( y
, x
);
9722 aCol
[0] = aColor
.GetRed();
9723 aCol
[1] = aColor
.GetGreen();
9724 aCol
[2] = aColor
.GetBlue();
9725 CHECK_RETURN( writeBuffer( aCol
, 3 ) );
9729 disableStreamEncryption();
9731 sal_uInt64 nEndStreamPos
= 0;
9732 CHECK_RETURN( (osl_File_E_None
== osl_getFilePos( m_aFile
, &nEndStreamPos
)) );
9734 aLine
.setLength( 0 );
9735 aLine
.append( "\nendstream\nendobj\n\n" );
9736 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
9738 // write stream length
9739 CHECK_RETURN( updateObject( nStreamLengthObject
) );
9740 aLine
.setLength( 0 );
9741 aLine
.append( nStreamLengthObject
);
9742 aLine
.append( " 0 obj\n" );
9743 aLine
.append( (sal_Int64
)(nEndStreamPos
-nStartStreamPos
) );
9744 aLine
.append( "\nendobj\n\n" );
9745 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
9747 CHECK_RETURN( updateObject( rObject
.m_nObject
) );
9748 aLine
.setLength( 0 );
9749 aLine
.append( rObject
.m_nObject
);
9750 aLine
.append( " 0 obj\n"
9751 "<</ShadingType 1\n"
9752 "/ColorSpace/DeviceRGB\n"
9754 "/Domain[ 0 1 0 1 ]\n"
9756 aLine
.append( (sal_Int32
)aSize
.Width() );
9757 aLine
.append( " 0 0 " );
9758 aLine
.append( (sal_Int32
)aSize
.Height() );
9759 aLine
.append( " 0 0 ]\n"
9761 aLine
.append( nFunctionObject
);
9762 aLine
.append( " 0 R\n"
9765 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
9770 bool PDFWriterImpl::writeJPG( JPGEmit
& rObject
)
9772 CHECK_RETURN( rObject
.m_pStream
);
9773 CHECK_RETURN( updateObject( rObject
.m_nObject
) );
9775 sal_Int32 nLength
= 0;
9776 rObject
.m_pStream
->Seek( STREAM_SEEK_TO_END
);
9777 nLength
= rObject
.m_pStream
->Tell();
9778 rObject
.m_pStream
->Seek( STREAM_SEEK_TO_BEGIN
);
9780 sal_Int32 nMaskObject
= 0;
9781 if( !!rObject
.m_aMask
)
9783 if( rObject
.m_aMask
.GetBitCount() == 1 ||
9784 ( rObject
.m_aMask
.GetBitCount() == 8 && m_aContext
.Version
>= PDFWriter::PDF_1_4
&& !m_bIsPDF_A1
)//i59651
9787 nMaskObject
= createObject();
9789 else if( m_bIsPDF_A1
)
9790 m_aErrors
.insert( PDFWriter::Warning_Transparency_Omitted_PDFA
);
9791 else if( m_aContext
.Version
< PDFWriter::PDF_1_4
)
9792 m_aErrors
.insert( PDFWriter::Warning_Transparency_Omitted_PDF13
);
9795 #if OSL_DEBUG_LEVEL > 1
9796 emitComment( "PDFWriterImpl::writeJPG" );
9799 OStringBuffer
aLine(200);
9800 aLine
.append( rObject
.m_nObject
);
9801 aLine
.append( " 0 obj\n"
9802 "<</Type/XObject/Subtype/Image/Width " );
9803 aLine
.append( (sal_Int32
)rObject
.m_aID
.m_aPixelSize
.Width() );
9804 aLine
.append( " /Height " );
9805 aLine
.append( (sal_Int32
)rObject
.m_aID
.m_aPixelSize
.Height() );
9806 aLine
.append( " /BitsPerComponent 8 " );
9807 if( rObject
.m_bTrueColor
)
9808 aLine
.append( "/ColorSpace/DeviceRGB" );
9810 aLine
.append( "/ColorSpace/DeviceGray" );
9811 aLine
.append( "/Filter/DCTDecode/Length " );
9812 aLine
.append( nLength
);
9815 aLine
.append( rObject
.m_aMask
.GetBitCount() == 1 ? " /Mask " : " /SMask " );
9816 aLine
.append( nMaskObject
);
9817 aLine
.append( " 0 R " );
9819 aLine
.append( ">>\nstream\n" );
9820 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
9822 checkAndEnableStreamEncryption( rObject
.m_nObject
);
9823 CHECK_RETURN( writeBuffer( rObject
.m_pStream
->GetData(), nLength
) );
9824 disableStreamEncryption();
9826 aLine
.setLength( 0 );
9827 aLine
.append( "\nendstream\nendobj\n\n" );
9828 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
9833 aEmit
.m_nObject
= nMaskObject
;
9834 if( rObject
.m_aMask
.GetBitCount() == 1 )
9835 aEmit
.m_aBitmap
= BitmapEx( rObject
.m_aMask
, rObject
.m_aMask
);
9836 else if( rObject
.m_aMask
.GetBitCount() == 8 )
9837 aEmit
.m_aBitmap
= BitmapEx( rObject
.m_aMask
, AlphaMask( rObject
.m_aMask
) );
9838 writeBitmapObject( aEmit
, true );
9844 bool PDFWriterImpl::writeBitmapObject( BitmapEmit
& rObject
, bool bMask
)
9846 CHECK_RETURN( updateObject( rObject
.m_nObject
) );
9849 Color
aTransparentColor( COL_TRANSPARENT
);
9850 bool bWriteMask
= false;
9853 aBitmap
= rObject
.m_aBitmap
.GetBitmap();
9854 if( rObject
.m_aBitmap
.IsAlpha() )
9856 if( m_aContext
.Version
>= PDFWriter::PDF_1_4
)
9858 // else draw without alpha channel
9862 switch( rObject
.m_aBitmap
.GetTransparentType() )
9864 case TRANSPARENT_NONE
:
9865 // comes from drawMask function
9866 if( aBitmap
.GetBitCount() == 1 && rObject
.m_bDrawMask
)
9869 case TRANSPARENT_COLOR
:
9870 aTransparentColor
= rObject
.m_aBitmap
.GetTransparentColor();
9872 case TRANSPARENT_BITMAP
:
9880 if( m_aContext
.Version
< PDFWriter::PDF_1_4
|| ! rObject
.m_aBitmap
.IsAlpha() )
9882 aBitmap
= rObject
.m_aBitmap
.GetMask();
9883 aBitmap
.Convert( BMP_CONVERSION_1BIT_THRESHOLD
);
9884 DBG_ASSERT( aBitmap
.GetBitCount() == 1, "mask conversion failed" );
9886 else if( aBitmap
.GetBitCount() != 8 )
9888 aBitmap
= rObject
.m_aBitmap
.GetAlpha().GetBitmap();
9889 aBitmap
.Convert( BMP_CONVERSION_8BIT_GREYS
);
9890 DBG_ASSERT( aBitmap
.GetBitCount() == 8, "alpha mask conversion failed" );
9894 BitmapReadAccess
* pAccess
= aBitmap
.AcquireReadAccess();
9895 AccessReleaser
aReleaser( pAccess
);
9898 sal_Int32 nBitsPerComponent
;
9899 switch( aBitmap
.GetBitCount() )
9906 nBitsPerComponent
= aBitmap
.GetBitCount();
9910 nBitsPerComponent
= 8;
9914 sal_Int32 nStreamLengthObject
= createObject();
9915 sal_Int32 nMaskObject
= 0;
9917 #if OSL_DEBUG_LEVEL > 1
9918 emitComment( "PDFWriterImpl::writeBitmapObject" );
9920 OStringBuffer
aLine(1024);
9921 aLine
.append( rObject
.m_nObject
);
9922 aLine
.append( " 0 obj\n"
9923 "<</Type/XObject/Subtype/Image/Width " );
9924 aLine
.append( (sal_Int32
)aBitmap
.GetSizePixel().Width() );
9925 aLine
.append( "/Height " );
9926 aLine
.append( (sal_Int32
)aBitmap
.GetSizePixel().Height() );
9927 aLine
.append( "/BitsPerComponent " );
9928 aLine
.append( nBitsPerComponent
);
9929 aLine
.append( "/Length " );
9930 aLine
.append( nStreamLengthObject
);
9931 aLine
.append( " 0 R\n" );
9932 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9933 if( nBitsPerComponent
!= 1 )
9935 aLine
.append( "/Filter/FlateDecode" );
9939 aLine
.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
9940 aLine
.append( (sal_Int32
)aBitmap
.GetSizePixel().Width() );
9941 aLine
.append( ">>\n" );
9946 aLine
.append( "/ColorSpace" );
9948 aLine
.append( "/DeviceRGB\n" );
9949 else if( aBitmap
.HasGreyPalette() )
9951 aLine
.append( "/DeviceGray\n" );
9952 if( aBitmap
.GetBitCount() == 1 )
9954 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
9955 sal_Int32 nBlackIndex
= pAccess
->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK
) ) );
9956 DBG_ASSERT( nBlackIndex
== 0 || nBlackIndex
== 1, "wrong black index" );
9957 if( nBlackIndex
== 1 )
9958 aLine
.append( "/Decode[1 0]\n" );
9963 aLine
.append( "[ /Indexed/DeviceRGB " );
9964 aLine
.append( (sal_Int32
)(pAccess
->GetPaletteEntryCount()-1) );
9965 aLine
.append( "\n<" );
9966 if( m_aContext
.Encryption
.Encrypt() )
9968 enableStringEncryption( rObject
.m_nObject
);
9969 //check encryption buffer size
9970 if( checkEncryptionBufferSize( pAccess
->GetPaletteEntryCount()*3 ) )
9973 //fill the encryption buffer
9974 for( sal_uInt16 i
= 0; i
< pAccess
->GetPaletteEntryCount(); i
++ )
9976 const BitmapColor
& rColor
= pAccess
->GetPaletteColor( i
);
9977 m_pEncryptionBuffer
[nChar
++] = rColor
.GetRed();
9978 m_pEncryptionBuffer
[nChar
++] = rColor
.GetGreen();
9979 m_pEncryptionBuffer
[nChar
++] = rColor
.GetBlue();
9981 //encrypt the colorspace lookup table
9982 rtl_cipher_encodeARCFOUR( m_aCipher
, m_pEncryptionBuffer
, nChar
, m_pEncryptionBuffer
, nChar
);
9983 //now queue the data for output
9985 for( sal_uInt16 i
= 0; i
< pAccess
->GetPaletteEntryCount(); i
++ )
9987 appendHex(m_pEncryptionBuffer
[nChar
++], aLine
);
9988 appendHex(m_pEncryptionBuffer
[nChar
++], aLine
);
9989 appendHex(m_pEncryptionBuffer
[nChar
++], aLine
);
9993 else //no encryption requested (PDF/A-1a program flow drops here)
9995 for( sal_uInt16 i
= 0; i
< pAccess
->GetPaletteEntryCount(); i
++ )
9997 const BitmapColor
& rColor
= pAccess
->GetPaletteColor( i
);
9998 appendHex( rColor
.GetRed(), aLine
);
9999 appendHex( rColor
.GetGreen(), aLine
);
10000 appendHex( rColor
.GetBlue(), aLine
);
10003 aLine
.append( ">\n]\n" );
10008 if( aBitmap
.GetBitCount() == 1 )
10010 aLine
.append( "/ImageMask true\n" );
10011 sal_Int32 nBlackIndex
= pAccess
->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK
) ) );
10012 DBG_ASSERT( nBlackIndex
== 0 || nBlackIndex
== 1, "wrong black index" );
10014 aLine
.append( "/Decode[ 1 0 ]\n" );
10016 aLine
.append( "/Decode[ 0 1 ]\n" );
10018 else if( aBitmap
.GetBitCount() == 8 )
10020 aLine
.append( "/ColorSpace/DeviceGray\n"
10021 "/Decode [ 1 0 ]\n" );
10025 if( ! bMask
&& m_aContext
.Version
> PDFWriter::PDF_1_2
&& !m_bIsPDF_A1
)//i59651
10029 nMaskObject
= createObject();
10030 if( rObject
.m_aBitmap
.IsAlpha() && m_aContext
.Version
> PDFWriter::PDF_1_3
)
10031 aLine
.append( "/SMask " );
10033 aLine
.append( "/Mask " );
10034 aLine
.append( nMaskObject
);
10035 aLine
.append( " 0 R\n" );
10037 else if( aTransparentColor
!= Color( COL_TRANSPARENT
) )
10039 aLine
.append( "/Mask[ " );
10042 aLine
.append( (sal_Int32
)aTransparentColor
.GetRed() );
10043 aLine
.append( ' ' );
10044 aLine
.append( (sal_Int32
)aTransparentColor
.GetRed() );
10045 aLine
.append( ' ' );
10046 aLine
.append( (sal_Int32
)aTransparentColor
.GetGreen() );
10047 aLine
.append( ' ' );
10048 aLine
.append( (sal_Int32
)aTransparentColor
.GetGreen() );
10049 aLine
.append( ' ' );
10050 aLine
.append( (sal_Int32
)aTransparentColor
.GetBlue() );
10051 aLine
.append( ' ' );
10052 aLine
.append( (sal_Int32
)aTransparentColor
.GetBlue() );
10056 sal_Int32 nIndex
= pAccess
->GetBestPaletteIndex( BitmapColor( aTransparentColor
) );
10057 aLine
.append( nIndex
);
10059 aLine
.append( " ]\n" );
10062 else if( m_bIsPDF_A1
&& (bWriteMask
|| aTransparentColor
!= Color( COL_TRANSPARENT
)) )
10063 m_aErrors
.insert( PDFWriter::Warning_Transparency_Omitted_PDFA
);
10065 aLine
.append( ">>\n"
10067 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10068 sal_uInt64 nStartPos
= 0;
10069 CHECK_RETURN( (osl_File_E_None
== osl_getFilePos( m_aFile
, &nStartPos
)) );
10071 checkAndEnableStreamEncryption( rObject
.m_nObject
);
10072 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
10073 if( nBitsPerComponent
== 1 )
10075 writeG4Stream( pAccess
);
10080 beginCompression();
10081 if( ! bTrueColor
|| pAccess
->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB
)
10083 const int nScanLineBytes
= 1 + ( pAccess
->GetBitCount() * ( pAccess
->Width() - 1 ) / 8U );
10085 for( int i
= 0; i
< pAccess
->Height(); i
++ )
10087 CHECK_RETURN( writeBuffer( pAccess
->GetScanline( i
), nScanLineBytes
) );
10092 const int nScanLineBytes
= pAccess
->Width()*3;
10093 boost::shared_array
<sal_uInt8
> pCol( new sal_uInt8
[ nScanLineBytes
] );
10094 for( int y
= 0; y
< pAccess
->Height(); y
++ )
10096 for( int x
= 0; x
< pAccess
->Width(); x
++ )
10098 BitmapColor aColor
= pAccess
->GetColor( y
, x
);
10099 pCol
[3*x
+0] = aColor
.GetRed();
10100 pCol
[3*x
+1] = aColor
.GetGreen();
10101 pCol
[3*x
+2] = aColor
.GetBlue();
10103 CHECK_RETURN( writeBuffer( pCol
.get(), nScanLineBytes
) );
10108 disableStreamEncryption();
10110 sal_uInt64 nEndPos
= 0;
10111 CHECK_RETURN( (osl_File_E_None
== osl_getFilePos( m_aFile
, &nEndPos
)) );
10112 aLine
.setLength( 0 );
10113 aLine
.append( "\nendstream\nendobj\n\n" );
10114 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10115 CHECK_RETURN( updateObject( nStreamLengthObject
) );
10116 aLine
.setLength( 0 );
10117 aLine
.append( nStreamLengthObject
);
10118 aLine
.append( " 0 obj\n" );
10119 aLine
.append( (sal_Int64
)(nEndPos
-nStartPos
) );
10120 aLine
.append( "\nendobj\n\n" );
10121 CHECK_RETURN( writeBuffer( aLine
.getStr(), aLine
.getLength() ) );
10126 aEmit
.m_nObject
= nMaskObject
;
10127 aEmit
.m_aBitmap
= rObject
.m_aBitmap
;
10128 return writeBitmapObject( aEmit
, true );
10134 void PDFWriterImpl::drawJPGBitmap( SvStream
& rDCTData
, bool bIsTrueColor
, const Size
& rSizePixel
, const Rectangle
& rTargetArea
, const Bitmap
& rMask
)
10136 MARK( "drawJPGBitmap" );
10138 OStringBuffer
aLine( 80 );
10139 updateGraphicsState();
10141 // #i40055# sanity check
10142 if( ! (rTargetArea
.GetWidth() && rTargetArea
.GetHeight() ) )
10144 if( ! (rSizePixel
.Width() && rSizePixel
.Height()) )
10147 rDCTData
.Seek( 0 );
10148 if( bIsTrueColor
&& m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
)
10150 // need to convert to grayscale;
10151 // load stream to bitmap and draw the bitmap instead
10153 GraphicConverter::Import( rDCTData
, aGraphic
, CVT_JPG
);
10154 Bitmap
aBmp( aGraphic
.GetBitmap() );
10155 if( !!rMask
&& rMask
.GetSizePixel() == aBmp
.GetSizePixel() )
10157 BitmapEx
aBmpEx( aBmp
, rMask
);
10158 drawBitmap( rTargetArea
.TopLeft(), rTargetArea
.GetSize(), aBmpEx
);
10161 drawBitmap( rTargetArea
.TopLeft(), rTargetArea
.GetSize(), aBmp
);
10165 SvMemoryStream
* pStream
= new SvMemoryStream
;
10166 *pStream
<< rDCTData
;
10167 pStream
->Seek( STREAM_SEEK_TO_END
);
10170 aID
.m_aPixelSize
= rSizePixel
;
10171 aID
.m_nSize
= pStream
->Tell();
10172 pStream
->Seek( STREAM_SEEK_TO_BEGIN
);
10173 aID
.m_nChecksum
= rtl_crc32( 0, pStream
->GetData(), aID
.m_nSize
);
10174 if( ! rMask
.IsEmpty() )
10175 aID
.m_nMaskChecksum
= rMask
.GetChecksum();
10177 std::list
< JPGEmit
>::const_iterator it
;
10178 for( it
= m_aJPGs
.begin(); it
!= m_aJPGs
.end() && ! (aID
== it
->m_aID
); ++it
)
10180 if( it
== m_aJPGs
.end() )
10182 m_aJPGs
.push_front( JPGEmit() );
10183 JPGEmit
& rEmit
= m_aJPGs
.front();
10184 rEmit
.m_nObject
= createObject();
10186 rEmit
.m_pStream
= pStream
;
10187 rEmit
.m_bTrueColor
= bIsTrueColor
;
10188 if( !! rMask
&& rMask
.GetSizePixel() == rSizePixel
)
10189 rEmit
.m_aMask
= rMask
;
10191 it
= m_aJPGs
.begin();
10196 aLine
.append( "q " );
10197 sal_Int32 nCheckWidth
= 0;
10198 m_aPages
.back().appendMappedLength( (sal_Int32
)rTargetArea
.GetWidth(), aLine
, false, &nCheckWidth
);
10199 aLine
.append( " 0 0 " );
10200 sal_Int32 nCheckHeight
= 0;
10201 m_aPages
.back().appendMappedLength( (sal_Int32
)rTargetArea
.GetHeight(), aLine
, true, &nCheckHeight
);
10202 aLine
.append( ' ' );
10203 m_aPages
.back().appendPoint( rTargetArea
.BottomLeft(), aLine
);
10204 aLine
.append( " cm\n/Im" );
10205 aLine
.append( it
->m_nObject
);
10206 aLine
.append( " Do Q\n" );
10207 if( nCheckWidth
== 0 || nCheckHeight
== 0 )
10209 // #i97512# avoid invalid current matrix
10210 aLine
.setLength( 0 );
10211 aLine
.append( "\n%jpeg image /Im" );
10212 aLine
.append( it
->m_nObject
);
10213 aLine
.append( " scaled to zero size, omitted\n" );
10215 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10217 OStringBuffer
aObjName( 16 );
10218 aObjName
.append( "Im" );
10219 aObjName
.append( it
->m_nObject
);
10220 pushResource( ResXObject
, aObjName
.makeStringAndClear(), it
->m_nObject
);
10224 void PDFWriterImpl::drawBitmap( const Point
& rDestPoint
, const Size
& rDestSize
, const BitmapEmit
& rBitmap
, const Color
& rFillColor
)
10226 OStringBuffer
aLine( 80 );
10227 updateGraphicsState();
10229 aLine
.append( "q " );
10230 if( rFillColor
!= Color( COL_TRANSPARENT
) )
10232 appendNonStrokingColor( rFillColor
, aLine
);
10233 aLine
.append( ' ' );
10235 sal_Int32 nCheckWidth
= 0;
10236 m_aPages
.back().appendMappedLength( (sal_Int32
)rDestSize
.Width(), aLine
, false, &nCheckWidth
);
10237 aLine
.append( " 0 0 " );
10238 sal_Int32 nCheckHeight
= 0;
10239 m_aPages
.back().appendMappedLength( (sal_Int32
)rDestSize
.Height(), aLine
, true, &nCheckHeight
);
10240 aLine
.append( ' ' );
10241 m_aPages
.back().appendPoint( rDestPoint
+ Point( 0, rDestSize
.Height()-1 ), aLine
);
10242 aLine
.append( " cm\n/Im" );
10243 aLine
.append( rBitmap
.m_nObject
);
10244 aLine
.append( " Do Q\n" );
10245 if( nCheckWidth
== 0 || nCheckHeight
== 0 )
10247 // #i97512# avoid invalid current matrix
10248 aLine
.setLength( 0 );
10249 aLine
.append( "\n%bitmap image /Im" );
10250 aLine
.append( rBitmap
.m_nObject
);
10251 aLine
.append( " scaled to zero size, omitted\n" );
10253 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10256 const PDFWriterImpl::BitmapEmit
& PDFWriterImpl::createBitmapEmit( const BitmapEx
& i_rBitmap
, bool bDrawMask
)
10258 BitmapEx
aBitmap( i_rBitmap
);
10259 if( m_aContext
.ColorMode
== PDFWriter::DrawGreyscale
)
10261 BmpConversion eConv
= BMP_CONVERSION_8BIT_GREYS
;
10262 int nDepth
= aBitmap
.GetBitmap().GetBitCount();
10264 eConv
= BMP_CONVERSION_4BIT_GREYS
;
10266 aBitmap
.Convert( eConv
);
10269 aID
.m_aPixelSize
= aBitmap
.GetSizePixel();
10270 aID
.m_nSize
= aBitmap
.GetBitCount();
10271 aID
.m_nChecksum
= aBitmap
.GetBitmap().GetChecksum();
10272 aID
.m_nMaskChecksum
= 0;
10273 if( aBitmap
.IsAlpha() )
10274 aID
.m_nMaskChecksum
= aBitmap
.GetAlpha().GetChecksum();
10277 Bitmap aMask
= aBitmap
.GetMask();
10278 if( ! aMask
.IsEmpty() )
10279 aID
.m_nMaskChecksum
= aMask
.GetChecksum();
10281 std::list
< BitmapEmit
>::const_iterator it
;
10282 for( it
= m_aBitmaps
.begin(); it
!= m_aBitmaps
.end(); ++it
)
10284 if( aID
== it
->m_aID
)
10287 if( it
== m_aBitmaps
.end() )
10289 m_aBitmaps
.push_front( BitmapEmit() );
10290 m_aBitmaps
.front().m_aID
= aID
;
10291 m_aBitmaps
.front().m_aBitmap
= aBitmap
;
10292 m_aBitmaps
.front().m_nObject
= createObject();
10293 m_aBitmaps
.front().m_bDrawMask
= bDrawMask
;
10294 it
= m_aBitmaps
.begin();
10297 OStringBuffer
aObjName( 16 );
10298 aObjName
.append( "Im" );
10299 aObjName
.append( it
->m_nObject
);
10300 pushResource( ResXObject
, aObjName
.makeStringAndClear(), it
->m_nObject
);
10305 void PDFWriterImpl::drawBitmap( const Point
& rDestPoint
, const Size
& rDestSize
, const Bitmap
& rBitmap
)
10307 MARK( "drawBitmap (Bitmap)" );
10309 // #i40055# sanity check
10310 if( ! (rDestSize
.Width() && rDestSize
.Height()) )
10313 const BitmapEmit
& rEmit
= createBitmapEmit( BitmapEx( rBitmap
) );
10314 drawBitmap( rDestPoint
, rDestSize
, rEmit
, Color( COL_TRANSPARENT
) );
10317 void PDFWriterImpl::drawBitmap( const Point
& rDestPoint
, const Size
& rDestSize
, const BitmapEx
& rBitmap
)
10319 MARK( "drawBitmap (BitmapEx)" );
10321 // #i40055# sanity check
10322 if( ! (rDestSize
.Width() && rDestSize
.Height()) )
10325 const BitmapEmit
& rEmit
= createBitmapEmit( rBitmap
);
10326 drawBitmap( rDestPoint
, rDestSize
, rEmit
, Color( COL_TRANSPARENT
) );
10329 sal_Int32
PDFWriterImpl::createGradient( const Gradient
& rGradient
, const Size
& rSize
)
10331 Size
aPtSize( lcl_convert( m_aGraphicsStack
.front().m_aMapMode
,
10332 MapMode( MAP_POINT
),
10333 getReferenceDevice(),
10335 // check if we already have this gradient
10336 std::list
<GradientEmit
>::iterator it
;
10337 // rounding to point will generally lose some pixels
10338 // round up to point boundary
10340 aPtSize
.Height()++;
10341 for( it
= m_aGradients
.begin(); it
!= m_aGradients
.end(); ++it
)
10343 if( it
->m_aGradient
== rGradient
)
10345 if( it
->m_aSize
== aPtSize
)
10349 if( it
== m_aGradients
.end() )
10351 m_aGradients
.push_front( GradientEmit() );
10352 m_aGradients
.front().m_aGradient
= rGradient
;
10353 m_aGradients
.front().m_nObject
= createObject();
10354 m_aGradients
.front().m_aSize
= aPtSize
;
10355 it
= m_aGradients
.begin();
10358 OStringBuffer
aObjName( 16 );
10359 aObjName
.append( 'P' );
10360 aObjName
.append( it
->m_nObject
);
10361 pushResource( ResShading
, aObjName
.makeStringAndClear(), it
->m_nObject
);
10363 return it
->m_nObject
;
10366 void PDFWriterImpl::drawGradient( const Rectangle
& rRect
, const Gradient
& rGradient
)
10368 MARK( "drawGradient (Rectangle)" );
10370 if( m_aContext
.Version
== PDFWriter::PDF_1_2
)
10372 drawRectangle( rRect
);
10376 sal_Int32 nGradient
= createGradient( rGradient
, rRect
.GetSize() );
10378 Point
aTranslate( rRect
.BottomLeft() );
10379 aTranslate
+= Point( 0, 1 );
10381 updateGraphicsState();
10383 OStringBuffer
aLine( 80 );
10384 aLine
.append( "q 1 0 0 1 " );
10385 m_aPages
.back().appendPoint( aTranslate
, aLine
);
10386 aLine
.append( " cm " );
10387 // if a stroke is appended reset the clip region before stroke
10388 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
10389 aLine
.append( "q " );
10390 aLine
.append( "0 0 " );
10391 m_aPages
.back().appendMappedLength( (sal_Int32
)rRect
.GetWidth(), aLine
, false );
10392 aLine
.append( ' ' );
10393 m_aPages
.back().appendMappedLength( (sal_Int32
)rRect
.GetHeight(), aLine
, true );
10394 aLine
.append( " re W n\n" );
10396 aLine
.append( "/P" );
10397 aLine
.append( nGradient
);
10398 aLine
.append( " sh " );
10399 if( m_aGraphicsStack
.front().m_aLineColor
!= Color( COL_TRANSPARENT
) )
10401 aLine
.append( "Q 0 0 " );
10402 m_aPages
.back().appendMappedLength( (sal_Int32
)rRect
.GetWidth(), aLine
, false );
10403 aLine
.append( ' ' );
10404 m_aPages
.back().appendMappedLength( (sal_Int32
)rRect
.GetHeight(), aLine
, true );
10405 aLine
.append( " re S " );
10407 aLine
.append( "Q\n" );
10408 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10411 void PDFWriterImpl::drawHatch( const PolyPolygon
& rPolyPoly
, const Hatch
& rHatch
)
10413 MARK( "drawHatch" );
10415 updateGraphicsState();
10417 if( rPolyPoly
.Count() )
10419 PolyPolygon
aPolyPoly( rPolyPoly
);
10421 aPolyPoly
.Optimize( POLY_OPTIMIZE_NO_SAME
);
10422 push( PUSH_LINECOLOR
);
10423 setLineColor( rHatch
.GetColor() );
10424 getReferenceDevice()->ImplDrawHatch( aPolyPoly
, rHatch
, sal_False
);
10429 void PDFWriterImpl::drawWallpaper( const Rectangle
& rRect
, const Wallpaper
& rWall
)
10431 MARK( "drawWallpaper" );
10433 bool bDrawColor
= false;
10434 bool bDrawGradient
= false;
10435 bool bDrawBitmap
= false;
10438 Point aBmpPos
= rRect
.TopLeft();
10440 if( rWall
.IsBitmap() )
10442 aBitmap
= rWall
.GetBitmap();
10443 aBmpSize
= lcl_convert( aBitmap
.GetPrefMapMode(),
10445 getReferenceDevice(),
10446 aBitmap
.GetPrefSize() );
10447 Rectangle
aRect( rRect
);
10448 if( rWall
.IsRect() )
10450 aRect
= rWall
.GetRect();
10451 aBmpPos
= aRect
.TopLeft();
10452 aBmpSize
= aRect
.GetSize();
10454 if( rWall
.GetStyle() != WALLPAPER_SCALE
)
10456 if( rWall
.GetStyle() != WALLPAPER_TILE
)
10458 bDrawBitmap
= true;
10459 if( rWall
.IsGradient() )
10460 bDrawGradient
= true;
10463 switch( rWall
.GetStyle() )
10465 case WALLPAPER_TOPLEFT
:
10467 case WALLPAPER_TOP
:
10468 aBmpPos
.X() += (aRect
.GetWidth()-aBmpSize
.Width())/2;
10470 case WALLPAPER_LEFT
:
10471 aBmpPos
.Y() += (aRect
.GetHeight()-aBmpSize
.Height())/2;
10473 case WALLPAPER_TOPRIGHT
:
10474 aBmpPos
.X() += aRect
.GetWidth()-aBmpSize
.Width();
10476 case WALLPAPER_CENTER
:
10477 aBmpPos
.X() += (aRect
.GetWidth()-aBmpSize
.Width())/2;
10478 aBmpPos
.Y() += (aRect
.GetHeight()-aBmpSize
.Height())/2;
10480 case WALLPAPER_RIGHT
:
10481 aBmpPos
.X() += aRect
.GetWidth()-aBmpSize
.Width();
10482 aBmpPos
.Y() += (aRect
.GetHeight()-aBmpSize
.Height())/2;
10484 case WALLPAPER_BOTTOMLEFT
:
10485 aBmpPos
.Y() += aRect
.GetHeight()-aBmpSize
.Height();
10487 case WALLPAPER_BOTTOM
:
10488 aBmpPos
.X() += (aRect
.GetWidth()-aBmpSize
.Width())/2;
10489 aBmpPos
.Y() += aRect
.GetHeight()-aBmpSize
.Height();
10491 case WALLPAPER_BOTTOMRIGHT
:
10492 aBmpPos
.X() += aRect
.GetWidth()-aBmpSize
.Width();
10493 aBmpPos
.Y() += aRect
.GetHeight()-aBmpSize
.Height();
10501 const BitmapEmit
& rEmit
= createBitmapEmit( BitmapEx( aBitmap
) );
10503 // convert to page coordinates; this needs to be done here
10504 // since the emit does not know the page anymore
10505 Rectangle
aConvertRect( aBmpPos
, aBmpSize
);
10506 m_aPages
.back().convertRect( aConvertRect
);
10508 OStringBuffer
aNameBuf(16);
10509 aNameBuf
.append( "Im" );
10510 aNameBuf
.append( rEmit
.m_nObject
);
10511 OString
aImageName( aNameBuf
.makeStringAndClear() );
10513 // push the pattern
10514 OStringBuffer
aTilingStream( 32 );
10515 appendFixedInt( aConvertRect
.GetWidth(), aTilingStream
);
10516 aTilingStream
.append( " 0 0 " );
10517 appendFixedInt( aConvertRect
.GetHeight(), aTilingStream
);
10518 aTilingStream
.append( " 0 0 cm\n/" );
10519 aTilingStream
.append( aImageName
);
10520 aTilingStream
.append( " Do\n" );
10522 m_aTilings
.push_back( TilingEmit() );
10523 m_aTilings
.back().m_nObject
= createObject();
10524 m_aTilings
.back().m_aRectangle
= Rectangle( Point( 0, 0 ), aConvertRect
.GetSize() );
10525 m_aTilings
.back().m_pTilingStream
= new SvMemoryStream();
10526 m_aTilings
.back().m_pTilingStream
->Write( aTilingStream
.getStr(), aTilingStream
.getLength() );
10527 // phase the tiling so wallpaper begins on upper left
10528 m_aTilings
.back().m_aTransform
.matrix
[2] = double(aConvertRect
.Left() % aConvertRect
.GetWidth()) / fDivisor
;
10529 m_aTilings
.back().m_aTransform
.matrix
[5] = double(aConvertRect
.Top() % aConvertRect
.GetHeight()) / fDivisor
;
10530 m_aTilings
.back().m_aResources
.m_aXObjects
[aImageName
] = rEmit
.m_nObject
;
10532 updateGraphicsState();
10534 OStringBuffer
aObjName( 16 );
10535 aObjName
.append( 'P' );
10536 aObjName
.append( m_aTilings
.back().m_nObject
);
10537 OString
aPatternName( aObjName
.makeStringAndClear() );
10538 pushResource( ResPattern
, aPatternName
, m_aTilings
.back().m_nObject
);
10540 // fill a rRect with the pattern
10541 OStringBuffer
aLine( 100 );
10542 aLine
.append( "q /Pattern cs /" );
10543 aLine
.append( aPatternName
);
10544 aLine
.append( " scn " );
10545 m_aPages
.back().appendRect( rRect
, aLine
);
10546 aLine
.append( " f Q\n" );
10547 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10552 aBmpPos
= aRect
.TopLeft();
10553 aBmpSize
= aRect
.GetSize();
10554 bDrawBitmap
= true;
10557 if( aBitmap
.IsTransparent() )
10559 if( rWall
.IsGradient() )
10560 bDrawGradient
= true;
10565 else if( rWall
.IsGradient() )
10566 bDrawGradient
= true;
10570 if( bDrawGradient
)
10572 drawGradient( rRect
, rWall
.GetGradient() );
10576 Color aOldLineColor
= m_aGraphicsStack
.front().m_aLineColor
;
10577 Color aOldFillColor
= m_aGraphicsStack
.front().m_aFillColor
;
10578 setLineColor( Color( COL_TRANSPARENT
) );
10579 setFillColor( rWall
.GetColor() );
10580 drawRectangle( rRect
);
10581 setLineColor( aOldLineColor
);
10582 setFillColor( aOldFillColor
);
10586 // set temporary clip region since aBmpPos and aBmpSize
10587 // may be outside rRect
10588 OStringBuffer
aLine( 20 );
10589 aLine
.append( "q " );
10590 m_aPages
.back().appendRect( rRect
, aLine
);
10591 aLine
.append( " W n\n" );
10592 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10593 drawBitmap( aBmpPos
, aBmpSize
, aBitmap
);
10594 writeBuffer( "Q\n", 2 );
10598 void PDFWriterImpl::updateGraphicsState()
10600 OStringBuffer
aLine( 256 );
10601 GraphicsState
& rNewState
= m_aGraphicsStack
.front();
10602 // first set clip region since it might invalidate everything else
10604 if( (rNewState
.m_nUpdateFlags
& GraphicsState::updateClipRegion
) )
10606 rNewState
.m_nUpdateFlags
&= ~GraphicsState::updateClipRegion
;
10608 if( m_aCurrentPDFState
.m_bClipRegion
!= rNewState
.m_bClipRegion
||
10609 ( rNewState
.m_bClipRegion
&& m_aCurrentPDFState
.m_aClipRegion
!= rNewState
.m_aClipRegion
) )
10611 if( m_aCurrentPDFState
.m_bClipRegion
&& m_aCurrentPDFState
.m_aClipRegion
.count() )
10613 aLine
.append( "Q " );
10614 // invalidate everything but the clip region
10615 m_aCurrentPDFState
= GraphicsState();
10616 rNewState
.m_nUpdateFlags
= sal::static_int_cast
<sal_uInt16
>(~GraphicsState::updateClipRegion
);
10618 if( rNewState
.m_bClipRegion
&& rNewState
.m_aClipRegion
.count() )
10620 // clip region is always stored in private PDF mapmode
10621 MapMode aNewMapMode
= rNewState
.m_aMapMode
;
10622 rNewState
.m_aMapMode
= m_aMapMode
;
10623 getReferenceDevice()->SetMapMode( rNewState
.m_aMapMode
);
10624 m_aCurrentPDFState
.m_aMapMode
= rNewState
.m_aMapMode
;
10626 aLine
.append( "q " );
10627 m_aPages
.back().appendPolyPolygon( rNewState
.m_aClipRegion
, aLine
);
10628 aLine
.append( "W* n\n" );
10629 rNewState
.m_aMapMode
= aNewMapMode
;
10630 getReferenceDevice()->SetMapMode( rNewState
.m_aMapMode
);
10631 m_aCurrentPDFState
.m_aMapMode
= rNewState
.m_aMapMode
;
10636 if( (rNewState
.m_nUpdateFlags
& GraphicsState::updateMapMode
) )
10638 rNewState
.m_nUpdateFlags
&= ~GraphicsState::updateMapMode
;
10639 getReferenceDevice()->SetMapMode( rNewState
.m_aMapMode
);
10642 if( (rNewState
.m_nUpdateFlags
& GraphicsState::updateFont
) )
10644 rNewState
.m_nUpdateFlags
&= ~GraphicsState::updateFont
;
10645 getReferenceDevice()->SetFont( rNewState
.m_aFont
);
10646 getReferenceDevice()->ImplNewFont();
10649 if( (rNewState
.m_nUpdateFlags
& GraphicsState::updateLayoutMode
) )
10651 rNewState
.m_nUpdateFlags
&= ~GraphicsState::updateLayoutMode
;
10652 getReferenceDevice()->SetLayoutMode( rNewState
.m_nLayoutMode
);
10655 if( (rNewState
.m_nUpdateFlags
& GraphicsState::updateDigitLanguage
) )
10657 rNewState
.m_nUpdateFlags
&= ~GraphicsState::updateDigitLanguage
;
10658 getReferenceDevice()->SetDigitLanguage( rNewState
.m_aDigitLanguage
);
10661 if( (rNewState
.m_nUpdateFlags
& GraphicsState::updateLineColor
) )
10663 rNewState
.m_nUpdateFlags
&= ~GraphicsState::updateLineColor
;
10664 if( m_aCurrentPDFState
.m_aLineColor
!= rNewState
.m_aLineColor
&&
10665 rNewState
.m_aLineColor
!= Color( COL_TRANSPARENT
) )
10667 appendStrokingColor( rNewState
.m_aLineColor
, aLine
);
10668 aLine
.append( "\n" );
10672 if( (rNewState
.m_nUpdateFlags
& GraphicsState::updateFillColor
) )
10674 rNewState
.m_nUpdateFlags
&= ~GraphicsState::updateFillColor
;
10675 if( m_aCurrentPDFState
.m_aFillColor
!= rNewState
.m_aFillColor
&&
10676 rNewState
.m_aFillColor
!= Color( COL_TRANSPARENT
) )
10678 appendNonStrokingColor( rNewState
.m_aFillColor
, aLine
);
10679 aLine
.append( "\n" );
10683 if( (rNewState
.m_nUpdateFlags
& GraphicsState::updateTransparentPercent
) )
10685 rNewState
.m_nUpdateFlags
&= ~GraphicsState::updateTransparentPercent
;
10686 if( m_aContext
.Version
>= PDFWriter::PDF_1_4
&& m_aCurrentPDFState
.m_nTransparentPercent
!= rNewState
.m_nTransparentPercent
)
10688 // TODO: switch extended graphicsstate
10692 // everything is up to date now
10693 m_aCurrentPDFState
= m_aGraphicsStack
.front();
10694 if( aLine
.getLength() )
10695 writeBuffer( aLine
.getStr(), aLine
.getLength() );
10698 /* #i47544# imitate OutputDevice behaviour:
10699 * if a font with a nontransparent color is set, it overwrites the current
10700 * text color. OTOH setting the text color will overwrite the color of the font.
10702 void PDFWriterImpl::setFont( const Font
& rFont
)
10704 Color aColor
= rFont
.GetColor();
10705 if( aColor
== Color( COL_TRANSPARENT
) )
10706 aColor
= m_aGraphicsStack
.front().m_aFont
.GetColor();
10707 m_aGraphicsStack
.front().m_aFont
= rFont
;
10708 m_aGraphicsStack
.front().m_aFont
.SetColor( aColor
);
10709 m_aGraphicsStack
.front().m_nUpdateFlags
|= GraphicsState::updateFont
;
10712 void PDFWriterImpl::push( sal_uInt16 nFlags
)
10714 OSL_ENSURE( m_aGraphicsStack
.size() > 0, "invalid graphics stack" );
10715 m_aGraphicsStack
.push_front( m_aGraphicsStack
.front() );
10716 m_aGraphicsStack
.front().m_nFlags
= nFlags
;
10719 void PDFWriterImpl::pop()
10721 OSL_ENSURE( m_aGraphicsStack
.size() > 1, "pop without push" );
10722 if( m_aGraphicsStack
.size() < 2 )
10725 GraphicsState aState
= m_aGraphicsStack
.front();
10726 m_aGraphicsStack
.pop_front();
10727 GraphicsState
& rOld
= m_aGraphicsStack
.front();
10729 // move those parameters back that were not pushed
10730 // in the first place
10731 if( ! (aState
.m_nFlags
& PUSH_LINECOLOR
) )
10732 setLineColor( aState
.m_aLineColor
);
10733 if( ! (aState
.m_nFlags
& PUSH_FILLCOLOR
) )
10734 setFillColor( aState
.m_aFillColor
);
10735 if( ! (aState
.m_nFlags
& PUSH_FONT
) )
10736 setFont( aState
.m_aFont
);
10737 if( ! (aState
.m_nFlags
& PUSH_TEXTCOLOR
) )
10738 setTextColor( aState
.m_aFont
.GetColor() );
10739 if( ! (aState
.m_nFlags
& PUSH_MAPMODE
) )
10740 setMapMode( aState
.m_aMapMode
);
10741 if( ! (aState
.m_nFlags
& PUSH_CLIPREGION
) )
10743 // do not use setClipRegion here
10744 // it would convert again assuming the current mapmode
10745 rOld
.m_aClipRegion
= aState
.m_aClipRegion
;
10746 rOld
.m_bClipRegion
= aState
.m_bClipRegion
;
10748 if( ! (aState
.m_nFlags
& PUSH_TEXTLINECOLOR
) )
10749 setTextLineColor( aState
.m_aTextLineColor
);
10750 if( ! (aState
.m_nFlags
& PUSH_OVERLINECOLOR
) )
10751 setOverlineColor( aState
.m_aOverlineColor
);
10752 if( ! (aState
.m_nFlags
& PUSH_TEXTALIGN
) )
10753 setTextAlign( aState
.m_aFont
.GetAlign() );
10754 if( ! (aState
.m_nFlags
& PUSH_TEXTFILLCOLOR
) )
10755 setTextFillColor( aState
.m_aFont
.GetFillColor() );
10756 if( ! (aState
.m_nFlags
& PUSH_REFPOINT
) )
10760 // invalidate graphics state
10761 m_aGraphicsStack
.front().m_nUpdateFlags
= sal::static_int_cast
<sal_uInt16
>(~0U);
10764 void PDFWriterImpl::setMapMode( const MapMode
& rMapMode
)
10766 m_aGraphicsStack
.front().m_aMapMode
= rMapMode
;
10767 getReferenceDevice()->SetMapMode( rMapMode
);
10768 m_aCurrentPDFState
.m_aMapMode
= rMapMode
;
10771 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon
& rRegion
)
10773 basegfx::B2DPolyPolygon aRegion
= getReferenceDevice()->LogicToPixel( rRegion
, m_aGraphicsStack
.front().m_aMapMode
);
10774 aRegion
= getReferenceDevice()->PixelToLogic( aRegion
, m_aMapMode
);
10775 m_aGraphicsStack
.front().m_aClipRegion
= aRegion
;
10776 m_aGraphicsStack
.front().m_bClipRegion
= true;
10777 m_aGraphicsStack
.front().m_nUpdateFlags
|= GraphicsState::updateClipRegion
;
10780 void PDFWriterImpl::moveClipRegion( sal_Int32 nX
, sal_Int32 nY
)
10782 if( m_aGraphicsStack
.front().m_bClipRegion
&& m_aGraphicsStack
.front().m_aClipRegion
.count() )
10784 Point
aPoint( lcl_convert( m_aGraphicsStack
.front().m_aMapMode
,
10786 getReferenceDevice(),
10787 Point( nX
, nY
) ) );
10788 aPoint
-= lcl_convert( m_aGraphicsStack
.front().m_aMapMode
,
10790 getReferenceDevice(),
10792 basegfx::B2DHomMatrix aMat
;
10793 aMat
.translate( aPoint
.X(), aPoint
.Y() );
10794 m_aGraphicsStack
.front().m_aClipRegion
.transform( aMat
);
10795 m_aGraphicsStack
.front().m_nUpdateFlags
|= GraphicsState::updateClipRegion
;
10799 bool PDFWriterImpl::intersectClipRegion( const Rectangle
& rRect
)
10801 basegfx::B2DPolyPolygon
aRect( basegfx::tools::createPolygonFromRect(
10802 basegfx::B2DRectangle( rRect
.Left(), rRect
.Top(), rRect
.Right(), rRect
.Bottom() ) ) );
10803 return intersectClipRegion( aRect
);
10807 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon
& rRegion
)
10809 basegfx::B2DPolyPolygon
aRegion( getReferenceDevice()->LogicToPixel( rRegion
, m_aGraphicsStack
.front().m_aMapMode
) );
10810 aRegion
= getReferenceDevice()->PixelToLogic( aRegion
, m_aMapMode
);
10811 m_aGraphicsStack
.front().m_nUpdateFlags
|= GraphicsState::updateClipRegion
;
10812 if( m_aGraphicsStack
.front().m_bClipRegion
)
10814 basegfx::B2DPolyPolygon
aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack
.front().m_aClipRegion
) );
10815 aRegion
= basegfx::tools::prepareForPolygonOperation( aRegion
);
10816 m_aGraphicsStack
.front().m_aClipRegion
= basegfx::tools::solvePolygonOperationAnd( aOld
, aRegion
);
10820 m_aGraphicsStack
.front().m_aClipRegion
= aRegion
;
10821 m_aGraphicsStack
.front().m_bClipRegion
= true;
10826 void PDFWriterImpl::createNote( const Rectangle
& rRect
, const PDFNote
& rNote
, sal_Int32 nPageNr
)
10829 nPageNr
= m_nCurrentPage
;
10831 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
10834 m_aNotes
.push_back( PDFNoteEntry() );
10835 m_aNotes
.back().m_nObject
= createObject();
10836 m_aNotes
.back().m_aContents
= rNote
;
10837 m_aNotes
.back().m_aRect
= rRect
;
10838 // convert to default user space now, since the mapmode may change
10839 m_aPages
[nPageNr
].convertRect( m_aNotes
.back().m_aRect
);
10841 // insert note to page's annotation list
10842 m_aPages
[ nPageNr
].m_aAnnotations
.push_back( m_aNotes
.back().m_nObject
);
10845 sal_Int32
PDFWriterImpl::createLink( const Rectangle
& rRect
, sal_Int32 nPageNr
)
10848 nPageNr
= m_nCurrentPage
;
10850 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
10853 sal_Int32 nRet
= m_aLinks
.size();
10855 m_aLinks
.push_back( PDFLink() );
10856 m_aLinks
.back().m_nObject
= createObject();
10857 m_aLinks
.back().m_nPage
= nPageNr
;
10858 m_aLinks
.back().m_aRect
= rRect
;
10859 // convert to default user space now, since the mapmode may change
10860 m_aPages
[nPageNr
].convertRect( m_aLinks
.back().m_aRect
);
10862 // insert link to page's annotation list
10863 m_aPages
[ nPageNr
].m_aAnnotations
.push_back( m_aLinks
.back().m_nObject
);
10869 sal_Int32
PDFWriterImpl::createNamedDest( const OUString
& sDestName
, const Rectangle
& rRect
, sal_Int32 nPageNr
, PDFWriter::DestAreaType eType
)
10872 nPageNr
= m_nCurrentPage
;
10874 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
10877 sal_Int32 nRet
= m_aNamedDests
.size();
10879 m_aNamedDests
.push_back( PDFNamedDest() );
10880 m_aNamedDests
.back().m_aDestName
= sDestName
;
10881 m_aNamedDests
.back().m_nPage
= nPageNr
;
10882 m_aNamedDests
.back().m_eType
= eType
;
10883 m_aNamedDests
.back().m_aRect
= rRect
;
10884 // convert to default user space now, since the mapmode may change
10885 m_aPages
[nPageNr
].convertRect( m_aNamedDests
.back().m_aRect
);
10891 sal_Int32
PDFWriterImpl::createDest( const Rectangle
& rRect
, sal_Int32 nPageNr
, PDFWriter::DestAreaType eType
)
10894 nPageNr
= m_nCurrentPage
;
10896 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
10899 sal_Int32 nRet
= m_aDests
.size();
10901 m_aDests
.push_back( PDFDest() );
10902 m_aDests
.back().m_nPage
= nPageNr
;
10903 m_aDests
.back().m_eType
= eType
;
10904 m_aDests
.back().m_aRect
= rRect
;
10905 // convert to default user space now, since the mapmode may change
10906 m_aPages
[nPageNr
].convertRect( m_aDests
.back().m_aRect
);
10911 sal_Int32
PDFWriterImpl::registerDestReference( sal_Int32 nDestId
, const Rectangle
& rRect
, sal_Int32 nPageNr
, PDFWriter::DestAreaType eType
)
10913 return m_aDestinationIdTranslation
[ nDestId
] = createDest( rRect
, nPageNr
, eType
);
10916 sal_Int32
PDFWriterImpl::setLinkDest( sal_Int32 nLinkId
, sal_Int32 nDestId
)
10918 if( nLinkId
< 0 || nLinkId
>= (sal_Int32
)m_aLinks
.size() )
10920 if( nDestId
< 0 || nDestId
>= (sal_Int32
)m_aDests
.size() )
10923 m_aLinks
[ nLinkId
].m_nDest
= nDestId
;
10928 sal_Int32
PDFWriterImpl::setLinkURL( sal_Int32 nLinkId
, const OUString
& rURL
)
10930 if( nLinkId
< 0 || nLinkId
>= (sal_Int32
)m_aLinks
.size() )
10933 m_aLinks
[ nLinkId
].m_nDest
= -1;
10935 using namespace ::com::sun::star
;
10937 if (!m_xTrans
.is())
10939 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getProcessComponentContext() );
10940 m_xTrans
= util::URLTransformer::create(xContext
);;
10944 aURL
.Complete
= rURL
;
10946 m_xTrans
->parseStrict( aURL
);
10948 m_aLinks
[ nLinkId
].m_aURL
= aURL
.Complete
;
10953 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId
, sal_Int32 nPropertyId
)
10955 m_aLinkPropertyMap
[ nPropertyId
] = nLinkId
;
10958 sal_Int32
PDFWriterImpl::createOutlineItem( sal_Int32 nParent
, const OUString
& rText
, sal_Int32 nDestID
)
10961 sal_Int32 nNewItem
= m_aOutline
.size();
10962 m_aOutline
.push_back( PDFOutlineEntry() );
10964 // set item attributes
10965 setOutlineItemParent( nNewItem
, nParent
);
10966 setOutlineItemText( nNewItem
, rText
);
10967 setOutlineItemDest( nNewItem
, nDestID
);
10972 sal_Int32
PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem
, sal_Int32 nNewParent
)
10974 if( nItem
< 1 || nItem
>= (sal_Int32
)m_aOutline
.size() )
10979 if( nNewParent
< 0 || nNewParent
>= (sal_Int32
)m_aOutline
.size() || nNewParent
== nItem
)
10984 // remove item from previous parent
10985 sal_Int32 nParentID
= m_aOutline
[ nItem
].m_nParentID
;
10986 if( nParentID
>= 0 && nParentID
< (sal_Int32
)m_aOutline
.size() )
10988 PDFOutlineEntry
& rParent
= m_aOutline
[ nParentID
];
10990 for( std::vector
<sal_Int32
>::iterator it
= rParent
.m_aChildren
.begin();
10991 it
!= rParent
.m_aChildren
.end(); ++it
)
10995 rParent
.m_aChildren
.erase( it
);
11001 // insert item to new parent's list of children
11002 m_aOutline
[ nNewParent
].m_aChildren
.push_back( nItem
);
11007 sal_Int32
PDFWriterImpl::setOutlineItemText( sal_Int32 nItem
, const OUString
& rText
)
11009 if( nItem
< 1 || nItem
>= (sal_Int32
)m_aOutline
.size() )
11012 m_aOutline
[ nItem
].m_aTitle
= psp::WhitespaceToSpace( rText
);
11016 sal_Int32
PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem
, sal_Int32 nDestID
)
11018 if( nItem
< 1 || nItem
>= (sal_Int32
)m_aOutline
.size() ) // item does not exist
11020 if( nDestID
< 0 || nDestID
>= (sal_Int32
)m_aDests
.size() ) // dest does not exist
11022 m_aOutline
[nItem
].m_nDestID
= nDestID
;
11026 const sal_Char
* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType
)
11028 static std::map
< PDFWriter::StructElement
, const char* > aTagStrings
;
11029 if( aTagStrings
.empty() )
11031 aTagStrings
[ PDFWriter::NonStructElement
] = "NonStruct";
11032 aTagStrings
[ PDFWriter::Document
] = "Document";
11033 aTagStrings
[ PDFWriter::Part
] = "Part";
11034 aTagStrings
[ PDFWriter::Article
] = "Art";
11035 aTagStrings
[ PDFWriter::Section
] = "Sect";
11036 aTagStrings
[ PDFWriter::Division
] = "Div";
11037 aTagStrings
[ PDFWriter::BlockQuote
] = "BlockQuote";
11038 aTagStrings
[ PDFWriter::Caption
] = "Caption";
11039 aTagStrings
[ PDFWriter::TOC
] = "TOC";
11040 aTagStrings
[ PDFWriter::TOCI
] = "TOCI";
11041 aTagStrings
[ PDFWriter::Index
] = "Index";
11042 aTagStrings
[ PDFWriter::Paragraph
] = "P";
11043 aTagStrings
[ PDFWriter::Heading
] = "H";
11044 aTagStrings
[ PDFWriter::H1
] = "H1";
11045 aTagStrings
[ PDFWriter::H2
] = "H2";
11046 aTagStrings
[ PDFWriter::H3
] = "H3";
11047 aTagStrings
[ PDFWriter::H4
] = "H4";
11048 aTagStrings
[ PDFWriter::H5
] = "H5";
11049 aTagStrings
[ PDFWriter::H6
] = "H6";
11050 aTagStrings
[ PDFWriter::List
] = "L";
11051 aTagStrings
[ PDFWriter::ListItem
] = "LI";
11052 aTagStrings
[ PDFWriter::LILabel
] = "Lbl";
11053 aTagStrings
[ PDFWriter::LIBody
] = "LBody";
11054 aTagStrings
[ PDFWriter::Table
] = "Table";
11055 aTagStrings
[ PDFWriter::TableRow
] = "TR";
11056 aTagStrings
[ PDFWriter::TableHeader
] = "TH";
11057 aTagStrings
[ PDFWriter::TableData
] = "TD";
11058 aTagStrings
[ PDFWriter::Span
] = "Span";
11059 aTagStrings
[ PDFWriter::Quote
] = "Quote";
11060 aTagStrings
[ PDFWriter::Note
] = "Note";
11061 aTagStrings
[ PDFWriter::Reference
] = "Reference";
11062 aTagStrings
[ PDFWriter::BibEntry
] = "BibEntry";
11063 aTagStrings
[ PDFWriter::Code
] = "Code";
11064 aTagStrings
[ PDFWriter::Link
] = "Link";
11065 aTagStrings
[ PDFWriter::Figure
] = "Figure";
11066 aTagStrings
[ PDFWriter::Formula
] = "Formula";
11067 aTagStrings
[ PDFWriter::Form
] = "Form";
11070 std::map
< PDFWriter::StructElement
, const char* >::const_iterator it
= aTagStrings
.find( eType
);
11072 return it
!= aTagStrings
.end() ? it
->second
: "Div";
11075 void PDFWriterImpl::beginStructureElementMCSeq()
11077 if( m_bEmitStructure
&&
11078 m_nCurrentStructElement
> 0 && // StructTreeRoot
11079 ! m_aStructure
[ m_nCurrentStructElement
].m_bOpenMCSeq
// already opened sequence
11082 PDFStructureElement
& rEle
= m_aStructure
[ m_nCurrentStructElement
];
11083 OStringBuffer
aLine( 128 );
11084 sal_Int32 nMCID
= m_aPages
[ m_nCurrentPage
].m_aMCIDParents
.size();
11085 aLine
.append( "/" );
11086 if( !rEle
.m_aAlias
.isEmpty() )
11087 aLine
.append( rEle
.m_aAlias
);
11089 aLine
.append( getStructureTag( rEle
.m_eType
) );
11090 aLine
.append( "<</MCID " );
11091 aLine
.append( nMCID
);
11092 aLine
.append( ">>BDC\n" );
11093 writeBuffer( aLine
.getStr(), aLine
.getLength() );
11095 // update the element's content list
11096 #if OSL_DEBUG_LEVEL > 1
11097 fprintf( stderr
, "beginning marked content id %" SAL_PRIdINT32
" on page object %" SAL_PRIdINT32
", structure first page = %" SAL_PRIdINT32
"\n",
11099 m_aPages
[ m_nCurrentPage
].m_nPageObject
,
11100 rEle
.m_nFirstPageObject
);
11102 rEle
.m_aKids
.push_back( PDFStructureElementKid( nMCID
, m_aPages
[m_nCurrentPage
].m_nPageObject
) );
11103 // update the page's mcid parent list
11104 m_aPages
[ m_nCurrentPage
].m_aMCIDParents
.push_back( rEle
.m_nObject
);
11105 // mark element MC sequence as open
11106 rEle
.m_bOpenMCSeq
= true;
11108 // handle artifacts
11109 else if( ! m_bEmitStructure
&& m_aContext
.Tagged
&&
11110 m_nCurrentStructElement
> 0 &&
11111 m_aStructure
[ m_nCurrentStructElement
].m_eType
== PDFWriter::NonStructElement
&&
11112 ! m_aStructure
[ m_nCurrentStructElement
].m_bOpenMCSeq
// already opened sequence
11115 OStringBuffer
aLine( 128 );
11116 aLine
.append( "/Artifact BMC\n" );
11117 writeBuffer( aLine
.getStr(), aLine
.getLength() );
11118 // mark element MC sequence as open
11119 m_aStructure
[ m_nCurrentStructElement
].m_bOpenMCSeq
= true;
11123 void PDFWriterImpl::endStructureElementMCSeq()
11125 if( m_nCurrentStructElement
> 0 && // StructTreeRoot
11126 ( m_bEmitStructure
|| m_aStructure
[ m_nCurrentStructElement
].m_eType
== PDFWriter::NonStructElement
) &&
11127 m_aStructure
[ m_nCurrentStructElement
].m_bOpenMCSeq
// must have an opened MC sequence
11130 writeBuffer( "EMC\n", 4 );
11131 m_aStructure
[ m_nCurrentStructElement
].m_bOpenMCSeq
= false;
11135 bool PDFWriterImpl::checkEmitStructure()
11137 bool bEmit
= false;
11138 if( m_aContext
.Tagged
)
11141 sal_Int32 nEle
= m_nCurrentStructElement
;
11142 while( nEle
> 0 && nEle
< sal_Int32(m_aStructure
.size()) )
11144 if( m_aStructure
[ nEle
].m_eType
== PDFWriter::NonStructElement
)
11149 nEle
= m_aStructure
[ nEle
].m_nParentElement
;
11155 sal_Int32
PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType
, const OUString
& rAlias
)
11157 if( m_nCurrentPage
< 0 )
11160 if( ! m_aContext
.Tagged
)
11163 // close eventual current MC sequence
11164 endStructureElementMCSeq();
11166 if( m_nCurrentStructElement
== 0 &&
11167 eType
!= PDFWriter::Document
&& eType
!= PDFWriter::NonStructElement
)
11169 // struct tree root hit, but not beginning document
11170 // this might happen with setCurrentStructureElement
11171 // silently insert structure into document again if one properly exists
11172 if( ! m_aStructure
[ 0 ].m_aChildren
.empty() )
11174 PDFWriter::StructElement childType
= PDFWriter::NonStructElement
;
11175 sal_Int32 nNewCurElement
= 0;
11176 const std::list
< sal_Int32
>& rRootChildren
= m_aStructure
[0].m_aChildren
;
11177 for( std::list
< sal_Int32
>::const_iterator it
= rRootChildren
.begin();
11178 childType
!= PDFWriter::Document
&& it
!= rRootChildren
.end(); ++it
)
11180 nNewCurElement
= *it
;
11181 childType
= m_aStructure
[ nNewCurElement
].m_eType
;
11183 if( childType
== PDFWriter::Document
)
11185 m_nCurrentStructElement
= nNewCurElement
;
11186 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" );
11189 OSL_FAIL( "document structure in disorder !" );
11193 OSL_FAIL( "PDF document structure MUST be contained in a Document element" );
11197 sal_Int32 nNewId
= sal_Int32(m_aStructure
.size());
11198 m_aStructure
.push_back( PDFStructureElement() );
11199 PDFStructureElement
& rEle
= m_aStructure
.back();
11200 rEle
.m_eType
= eType
;
11201 rEle
.m_nOwnElement
= nNewId
;
11202 rEle
.m_nParentElement
= m_nCurrentStructElement
;
11203 rEle
.m_nFirstPageObject
= m_aPages
[ m_nCurrentPage
].m_nPageObject
;
11204 m_aStructure
[ m_nCurrentStructElement
].m_aChildren
.push_back( nNewId
);
11205 m_nCurrentStructElement
= nNewId
;
11207 // handle alias names
11208 if( !rAlias
.isEmpty() && eType
!= PDFWriter::NonStructElement
)
11210 OStringBuffer
aNameBuf( rAlias
.getLength() );
11211 appendName( rAlias
, aNameBuf
);
11212 OString
aAliasName( aNameBuf
.makeStringAndClear() );
11213 rEle
.m_aAlias
= aAliasName
;
11214 m_aRoleMap
[ aAliasName
] = getStructureTag( eType
);
11217 #if OSL_DEBUG_LEVEL > 1
11218 OStringBuffer
aLine( "beginStructureElement " );
11219 aLine
.append( m_nCurrentStructElement
);
11220 aLine
.append( ": " );
11221 aLine
.append( getStructureTag( eType
) );
11222 if( !rEle
.m_aAlias
.isEmpty() )
11224 aLine
.append( " aliased as \"" );
11225 aLine
.append( rEle
.m_aAlias
);
11226 aLine
.append( '\"' );
11228 emitComment( aLine
.getStr() );
11231 // check whether to emit structure henceforth
11232 m_bEmitStructure
= checkEmitStructure();
11234 if( m_bEmitStructure
) // don't create nonexistant objects
11236 rEle
.m_nObject
= createObject();
11237 // update parent's kids list
11238 m_aStructure
[ rEle
.m_nParentElement
].m_aKids
.push_back( rEle
.m_nObject
);
11243 void PDFWriterImpl::endStructureElement()
11245 if( m_nCurrentPage
< 0 )
11248 if( ! m_aContext
.Tagged
)
11251 if( m_nCurrentStructElement
== 0 )
11253 // hit the struct tree root, that means there is an endStructureElement
11254 // without corresponding beginStructureElement
11258 // end the marked content sequence
11259 endStructureElementMCSeq();
11261 #if OSL_DEBUG_LEVEL > 1
11262 OStringBuffer
aLine( "endStructureElement " );
11263 aLine
.append( m_nCurrentStructElement
);
11264 aLine
.append( ": " );
11265 aLine
.append( getStructureTag( m_aStructure
[ m_nCurrentStructElement
].m_eType
) );
11266 if( !m_aStructure
[ m_nCurrentStructElement
].m_aAlias
.isEmpty() )
11268 aLine
.append( " aliased as \"" );
11269 aLine
.append( m_aStructure
[ m_nCurrentStructElement
].m_aAlias
);
11270 aLine
.append( '\"' );
11274 // "end" the structure element, the parent becomes current element
11275 m_nCurrentStructElement
= m_aStructure
[ m_nCurrentStructElement
].m_nParentElement
;
11277 // check whether to emit structure henceforth
11278 m_bEmitStructure
= checkEmitStructure();
11280 #if OSL_DEBUG_LEVEL > 1
11281 if( m_bEmitStructure
)
11282 emitComment( aLine
.getStr() );
11288 * This function adds an internal structure list container to overcome the 8191 elements array limitation
11289 * in kids element emission.
11290 * Recursive function
11293 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement
& rEle
)
11295 if( rEle
.m_eType
== PDFWriter::NonStructElement
&&
11296 rEle
.m_nOwnElement
!= rEle
.m_nParentElement
)
11299 for( std::list
< sal_Int32
>::const_iterator it
= rEle
.m_aChildren
.begin(); it
!= rEle
.m_aChildren
.end(); ++it
)
11301 if( *it
> 0 && *it
< sal_Int32(m_aStructure
.size()) )
11303 PDFStructureElement
& rChild
= m_aStructure
[ *it
];
11304 if( rChild
.m_eType
!= PDFWriter::NonStructElement
)
11306 //triggered when a child of the rEle element is found
11307 if( rChild
.m_nParentElement
== rEle
.m_nOwnElement
)
11308 addInternalStructureContainer( rChild
);//examine the child
11311 OSL_FAIL( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
11312 #if OSL_DEBUG_LEVEL > 1
11313 fprintf( stderr
, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32
"\n", *it
);
11320 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
11321 #if OSL_DEBUG_LEVEL > 1
11322 fprintf( stderr
, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32
"\n", *it
);
11327 if( rEle
.m_nOwnElement
!= rEle
.m_nParentElement
)
11329 if( !rEle
.m_aKids
.empty() )
11331 if( rEle
.m_aKids
.size() > ncMaxPDFArraySize
) {
11332 //then we need to add the containers for the kids elements
11333 // a list to be used for the new kid element
11334 std::list
< PDFStructureElementKid
> aNewKids
;
11335 std::list
< sal_Int32
> aNewChildren
;
11337 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
11338 OStringBuffer
aNameBuf( "Div" );
11339 OString
aAliasName( aNameBuf
.makeStringAndClear() );
11340 m_aRoleMap
[ aAliasName
] = getStructureTag( PDFWriter::Division
);
11342 while( rEle
.m_aKids
.size() > ncMaxPDFArraySize
)
11344 sal_Int32 nCurrentStructElement
= rEle
.m_nOwnElement
;
11345 sal_Int32 nNewId
= sal_Int32(m_aStructure
.size());
11346 m_aStructure
.push_back( PDFStructureElement() );
11347 PDFStructureElement
& rEleNew
= m_aStructure
.back();
11348 rEleNew
.m_aAlias
= aAliasName
;
11349 rEleNew
.m_eType
= PDFWriter::Division
; // a new Div type container
11350 rEleNew
.m_nOwnElement
= nNewId
;
11351 rEleNew
.m_nParentElement
= nCurrentStructElement
;
11352 //inherit the same page as the first child to be reparented
11353 rEleNew
.m_nFirstPageObject
= m_aStructure
[ rEle
.m_aChildren
.front() ].m_nFirstPageObject
;
11354 rEleNew
.m_nObject
= createObject();//assign a PDF object number
11355 //add the object to the kid list of the parent
11356 aNewKids
.push_back( PDFStructureElementKid( rEleNew
.m_nObject
) );
11357 aNewChildren
.push_back( nNewId
);
11359 std::list
< sal_Int32
>::iterator
aChildEndIt( rEle
.m_aChildren
.begin() );
11360 std::list
< PDFStructureElementKid
>::iterator
aKidEndIt( rEle
.m_aKids
.begin() );
11361 advance( aChildEndIt
, ncMaxPDFArraySize
);
11362 advance( aKidEndIt
, ncMaxPDFArraySize
);
11364 rEleNew
.m_aKids
.splice( rEleNew
.m_aKids
.begin(),
11366 rEle
.m_aKids
.begin(),
11368 rEleNew
.m_aChildren
.splice( rEleNew
.m_aChildren
.begin(),
11370 rEle
.m_aChildren
.begin(),
11372 // set the kid's new parent
11373 for( std::list
< sal_Int32
>::const_iterator it
= rEleNew
.m_aChildren
.begin();
11374 it
!= rEleNew
.m_aChildren
.end(); ++it
)
11376 m_aStructure
[ *it
].m_nParentElement
= nNewId
;
11379 //finally add the new kids resulting from the container added
11380 rEle
.m_aKids
.insert( rEle
.m_aKids
.begin(), aNewKids
.begin(), aNewKids
.end() );
11381 rEle
.m_aChildren
.insert( rEle
.m_aChildren
.begin(), aNewChildren
.begin(), aNewChildren
.end() );
11388 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle
)
11390 bool bSuccess
= false;
11392 if( m_aContext
.Tagged
&& nEle
>= 0 && nEle
< sal_Int32(m_aStructure
.size()) )
11394 // end eventual previous marked content sequence
11395 endStructureElementMCSeq();
11397 m_nCurrentStructElement
= nEle
;
11398 m_bEmitStructure
= checkEmitStructure();
11399 #if OSL_DEBUG_LEVEL > 1
11400 OStringBuffer
aLine( "setCurrentStructureElement " );
11401 aLine
.append( m_nCurrentStructElement
);
11402 aLine
.append( ": " );
11403 aLine
.append( getStructureTag( m_aStructure
[ m_nCurrentStructElement
].m_eType
) );
11404 if( !m_aStructure
[ m_nCurrentStructElement
].m_aAlias
.isEmpty() )
11406 aLine
.append( " aliased as \"" );
11407 aLine
.append( m_aStructure
[ m_nCurrentStructElement
].m_aAlias
);
11408 aLine
.append( '\"' );
11410 if( ! m_bEmitStructure
)
11411 aLine
.append( " (inside NonStruct)" );
11412 emitComment( aLine
.getStr() );
11420 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr
, enum PDFWriter::StructAttributeValue eVal
)
11422 if( !m_aContext
.Tagged
)
11425 bool bInsert
= false;
11426 if( m_nCurrentStructElement
> 0 && m_bEmitStructure
)
11428 PDFWriter::StructElement eType
= m_aStructure
[ m_nCurrentStructElement
].m_eType
;
11431 case PDFWriter::Placement
:
11432 if( eVal
== PDFWriter::Block
||
11433 eVal
== PDFWriter::Inline
||
11434 eVal
== PDFWriter::Before
||
11435 eVal
== PDFWriter::Start
||
11436 eVal
== PDFWriter::End
)
11439 case PDFWriter::WritingMode
:
11440 if( eVal
== PDFWriter::LrTb
||
11441 eVal
== PDFWriter::RlTb
||
11442 eVal
== PDFWriter::TbRl
)
11447 case PDFWriter::TextAlign
:
11448 if( eVal
== PDFWriter::Start
||
11449 eVal
== PDFWriter::Center
||
11450 eVal
== PDFWriter::End
||
11451 eVal
== PDFWriter::Justify
)
11453 if( eType
== PDFWriter::Paragraph
||
11454 eType
== PDFWriter::Heading
||
11455 eType
== PDFWriter::H1
||
11456 eType
== PDFWriter::H2
||
11457 eType
== PDFWriter::H3
||
11458 eType
== PDFWriter::H4
||
11459 eType
== PDFWriter::H5
||
11460 eType
== PDFWriter::H6
||
11461 eType
== PDFWriter::List
||
11462 eType
== PDFWriter::ListItem
||
11463 eType
== PDFWriter::LILabel
||
11464 eType
== PDFWriter::LIBody
||
11465 eType
== PDFWriter::Table
||
11466 eType
== PDFWriter::TableRow
||
11467 eType
== PDFWriter::TableHeader
||
11468 eType
== PDFWriter::TableData
)
11474 case PDFWriter::Width
:
11475 case PDFWriter::Height
:
11476 if( eVal
== PDFWriter::Auto
)
11478 if( eType
== PDFWriter::Figure
||
11479 eType
== PDFWriter::Formula
||
11480 eType
== PDFWriter::Form
||
11481 eType
== PDFWriter::Table
||
11482 eType
== PDFWriter::TableHeader
||
11483 eType
== PDFWriter::TableData
)
11489 case PDFWriter::BlockAlign
:
11490 if( eVal
== PDFWriter::Before
||
11491 eVal
== PDFWriter::Middle
||
11492 eVal
== PDFWriter::After
||
11493 eVal
== PDFWriter::Justify
)
11495 if( eType
== PDFWriter::TableHeader
||
11496 eType
== PDFWriter::TableData
)
11502 case PDFWriter::InlineAlign
:
11503 if( eVal
== PDFWriter::Start
||
11504 eVal
== PDFWriter::Center
||
11505 eVal
== PDFWriter::End
)
11507 if( eType
== PDFWriter::TableHeader
||
11508 eType
== PDFWriter::TableData
)
11514 case PDFWriter::LineHeight
:
11515 if( eVal
== PDFWriter::Normal
||
11516 eVal
== PDFWriter::Auto
)
11518 // only for ILSE and BLSE
11519 if( eType
== PDFWriter::Paragraph
||
11520 eType
== PDFWriter::Heading
||
11521 eType
== PDFWriter::H1
||
11522 eType
== PDFWriter::H2
||
11523 eType
== PDFWriter::H3
||
11524 eType
== PDFWriter::H4
||
11525 eType
== PDFWriter::H5
||
11526 eType
== PDFWriter::H6
||
11527 eType
== PDFWriter::List
||
11528 eType
== PDFWriter::ListItem
||
11529 eType
== PDFWriter::LILabel
||
11530 eType
== PDFWriter::LIBody
||
11531 eType
== PDFWriter::Table
||
11532 eType
== PDFWriter::TableRow
||
11533 eType
== PDFWriter::TableHeader
||
11534 eType
== PDFWriter::TableData
||
11535 eType
== PDFWriter::Span
||
11536 eType
== PDFWriter::Quote
||
11537 eType
== PDFWriter::Note
||
11538 eType
== PDFWriter::Reference
||
11539 eType
== PDFWriter::BibEntry
||
11540 eType
== PDFWriter::Code
||
11541 eType
== PDFWriter::Link
)
11547 case PDFWriter::TextDecorationType
:
11548 if( eVal
== PDFWriter::NONE
||
11549 eVal
== PDFWriter::Underline
||
11550 eVal
== PDFWriter::Overline
||
11551 eVal
== PDFWriter::LineThrough
)
11553 // only for ILSE and BLSE
11554 if( eType
== PDFWriter::Paragraph
||
11555 eType
== PDFWriter::Heading
||
11556 eType
== PDFWriter::H1
||
11557 eType
== PDFWriter::H2
||
11558 eType
== PDFWriter::H3
||
11559 eType
== PDFWriter::H4
||
11560 eType
== PDFWriter::H5
||
11561 eType
== PDFWriter::H6
||
11562 eType
== PDFWriter::List
||
11563 eType
== PDFWriter::ListItem
||
11564 eType
== PDFWriter::LILabel
||
11565 eType
== PDFWriter::LIBody
||
11566 eType
== PDFWriter::Table
||
11567 eType
== PDFWriter::TableRow
||
11568 eType
== PDFWriter::TableHeader
||
11569 eType
== PDFWriter::TableData
||
11570 eType
== PDFWriter::Span
||
11571 eType
== PDFWriter::Quote
||
11572 eType
== PDFWriter::Note
||
11573 eType
== PDFWriter::Reference
||
11574 eType
== PDFWriter::BibEntry
||
11575 eType
== PDFWriter::Code
||
11576 eType
== PDFWriter::Link
)
11582 case PDFWriter::ListNumbering
:
11583 if( eVal
== PDFWriter::NONE
||
11584 eVal
== PDFWriter::Disc
||
11585 eVal
== PDFWriter::Circle
||
11586 eVal
== PDFWriter::Square
||
11587 eVal
== PDFWriter::Decimal
||
11588 eVal
== PDFWriter::UpperRoman
||
11589 eVal
== PDFWriter::LowerRoman
||
11590 eVal
== PDFWriter::UpperAlpha
||
11591 eVal
== PDFWriter::LowerAlpha
)
11593 if( eType
== PDFWriter::List
)
11602 m_aStructure
[ m_nCurrentStructElement
].m_aAttributes
[ eAttr
] = PDFStructureAttribute( eVal
);
11603 #if OSL_DEBUG_LEVEL > 1
11604 else if( m_nCurrentStructElement
> 0 && m_bEmitStructure
)
11605 fprintf( stderr
, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
11606 getAttributeTag( eAttr
),
11607 getAttributeValueTag( eVal
),
11608 getStructureTag( m_aStructure
[ m_nCurrentStructElement
].m_eType
),
11609 m_aStructure
[ m_nCurrentStructElement
].m_aAlias
.getStr()
11616 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr
, sal_Int32 nValue
)
11618 if( ! m_aContext
.Tagged
)
11621 bool bInsert
= false;
11622 if( m_nCurrentStructElement
> 0 && m_bEmitStructure
)
11624 if( eAttr
== PDFWriter::Language
)
11626 m_aStructure
[ m_nCurrentStructElement
].m_aLocale
= LanguageTag( (LanguageType
)nValue
).getLocale();
11630 PDFWriter::StructElement eType
= m_aStructure
[ m_nCurrentStructElement
].m_eType
;
11633 case PDFWriter::SpaceBefore
:
11634 case PDFWriter::SpaceAfter
:
11635 case PDFWriter::StartIndent
:
11636 case PDFWriter::EndIndent
:
11638 if( eType
== PDFWriter::Paragraph
||
11639 eType
== PDFWriter::Heading
||
11640 eType
== PDFWriter::H1
||
11641 eType
== PDFWriter::H2
||
11642 eType
== PDFWriter::H3
||
11643 eType
== PDFWriter::H4
||
11644 eType
== PDFWriter::H5
||
11645 eType
== PDFWriter::H6
||
11646 eType
== PDFWriter::List
||
11647 eType
== PDFWriter::ListItem
||
11648 eType
== PDFWriter::LILabel
||
11649 eType
== PDFWriter::LIBody
||
11650 eType
== PDFWriter::Table
||
11651 eType
== PDFWriter::TableRow
||
11652 eType
== PDFWriter::TableHeader
||
11653 eType
== PDFWriter::TableData
)
11658 case PDFWriter::TextIndent
:
11659 // paragraph like BLSE and additional elements
11660 if( eType
== PDFWriter::Paragraph
||
11661 eType
== PDFWriter::Heading
||
11662 eType
== PDFWriter::H1
||
11663 eType
== PDFWriter::H2
||
11664 eType
== PDFWriter::H3
||
11665 eType
== PDFWriter::H4
||
11666 eType
== PDFWriter::H5
||
11667 eType
== PDFWriter::H6
||
11668 eType
== PDFWriter::LILabel
||
11669 eType
== PDFWriter::LIBody
||
11670 eType
== PDFWriter::TableHeader
||
11671 eType
== PDFWriter::TableData
)
11676 case PDFWriter::Width
:
11677 case PDFWriter::Height
:
11678 if( eType
== PDFWriter::Figure
||
11679 eType
== PDFWriter::Formula
||
11680 eType
== PDFWriter::Form
||
11681 eType
== PDFWriter::Table
||
11682 eType
== PDFWriter::TableHeader
||
11683 eType
== PDFWriter::TableData
)
11688 case PDFWriter::LineHeight
:
11689 case PDFWriter::BaselineShift
:
11690 // only for ILSE and BLSE
11691 if( eType
== PDFWriter::Paragraph
||
11692 eType
== PDFWriter::Heading
||
11693 eType
== PDFWriter::H1
||
11694 eType
== PDFWriter::H2
||
11695 eType
== PDFWriter::H3
||
11696 eType
== PDFWriter::H4
||
11697 eType
== PDFWriter::H5
||
11698 eType
== PDFWriter::H6
||
11699 eType
== PDFWriter::List
||
11700 eType
== PDFWriter::ListItem
||
11701 eType
== PDFWriter::LILabel
||
11702 eType
== PDFWriter::LIBody
||
11703 eType
== PDFWriter::Table
||
11704 eType
== PDFWriter::TableRow
||
11705 eType
== PDFWriter::TableHeader
||
11706 eType
== PDFWriter::TableData
||
11707 eType
== PDFWriter::Span
||
11708 eType
== PDFWriter::Quote
||
11709 eType
== PDFWriter::Note
||
11710 eType
== PDFWriter::Reference
||
11711 eType
== PDFWriter::BibEntry
||
11712 eType
== PDFWriter::Code
||
11713 eType
== PDFWriter::Link
)
11718 case PDFWriter::RowSpan
:
11719 case PDFWriter::ColSpan
:
11720 // only for table cells
11721 if( eType
== PDFWriter::TableHeader
||
11722 eType
== PDFWriter::TableData
)
11727 case PDFWriter::LinkAnnotation
:
11728 if( eType
== PDFWriter::Link
)
11736 m_aStructure
[ m_nCurrentStructElement
].m_aAttributes
[ eAttr
] = PDFStructureAttribute( nValue
);
11737 #if OSL_DEBUG_LEVEL > 1
11738 else if( m_nCurrentStructElement
> 0 && m_bEmitStructure
)
11739 fprintf( stderr
, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
11740 getAttributeTag( eAttr
),
11742 getStructureTag( m_aStructure
[ m_nCurrentStructElement
].m_eType
),
11743 m_aStructure
[ m_nCurrentStructElement
].m_aAlias
.getStr() );
11749 void PDFWriterImpl::setStructureBoundingBox( const Rectangle
& rRect
)
11751 sal_Int32 nPageNr
= m_nCurrentPage
;
11752 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() || !m_aContext
.Tagged
)
11756 if( m_nCurrentStructElement
> 0 && m_bEmitStructure
)
11758 PDFWriter::StructElement eType
= m_aStructure
[ m_nCurrentStructElement
].m_eType
;
11759 if( eType
== PDFWriter::Figure
||
11760 eType
== PDFWriter::Formula
||
11761 eType
== PDFWriter::Form
||
11762 eType
== PDFWriter::Table
)
11764 m_aStructure
[ m_nCurrentStructElement
].m_aBBox
= rRect
;
11765 // convert to default user space now, since the mapmode may change
11766 m_aPages
[nPageNr
].convertRect( m_aStructure
[ m_nCurrentStructElement
].m_aBBox
);
11771 void PDFWriterImpl::setActualText( const String
& rText
)
11773 if( m_aContext
.Tagged
&& m_nCurrentStructElement
> 0 && m_bEmitStructure
)
11775 m_aStructure
[ m_nCurrentStructElement
].m_aActualText
= rText
;
11779 void PDFWriterImpl::setAlternateText( const String
& rText
)
11781 if( m_aContext
.Tagged
&& m_nCurrentStructElement
> 0 && m_bEmitStructure
)
11783 m_aStructure
[ m_nCurrentStructElement
].m_aAltText
= rText
;
11787 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds
, sal_Int32 nPageNr
)
11790 nPageNr
= m_nCurrentPage
;
11792 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
11795 m_aPages
[ nPageNr
].m_nDuration
= nSeconds
;
11798 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType
, sal_uInt32 nMilliSec
, sal_Int32 nPageNr
)
11801 nPageNr
= m_nCurrentPage
;
11803 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
11806 m_aPages
[ nPageNr
].m_eTransition
= eType
;
11807 m_aPages
[ nPageNr
].m_nTransTime
= nMilliSec
;
11810 void PDFWriterImpl::ensureUniqueRadioOnValues()
11812 // loop over radio groups
11813 for( std::map
<sal_Int32
,sal_Int32
>::const_iterator group
= m_aRadioGroupWidgets
.begin();
11814 group
!= m_aRadioGroupWidgets
.end(); ++group
)
11816 PDFWidget
& rGroupWidget
= m_aWidgets
[ group
->second
];
11817 // check whether all kids have a unique OnValue
11818 boost::unordered_map
< OUString
, sal_Int32
, OUStringHash
> aOnValues
;
11819 int nChildren
= rGroupWidget
.m_aKidsIndex
.size();
11820 bool bIsUnique
= true;
11821 for( int nKid
= 0; nKid
< nChildren
&& bIsUnique
; nKid
++ )
11823 int nKidIndex
= rGroupWidget
.m_aKidsIndex
[nKid
];
11824 const OUString
& rVal
= m_aWidgets
[nKidIndex
].m_aOnValue
;
11825 #if OSL_DEBUG_LEVEL > 1
11826 fprintf( stderr
, "OnValue: %s\n", OUStringToOString( rVal
, RTL_TEXTENCODING_UTF8
).getStr() );
11828 if( aOnValues
.find( rVal
) == aOnValues
.end() )
11830 aOnValues
[ rVal
] = 1;
11839 #if OSL_DEBUG_LEVEL > 1
11840 fprintf( stderr
, "enforcing unique OnValues\n" );
11842 // make unique by using ascending OnValues
11843 for( int nKid
= 0; nKid
< nChildren
; nKid
++ )
11845 int nKidIndex
= rGroupWidget
.m_aKidsIndex
[nKid
];
11846 PDFWidget
& rKid
= m_aWidgets
[nKidIndex
];
11847 rKid
.m_aOnValue
= OUString::valueOf( sal_Int32(nKid
+1) );
11848 if( rKid
.m_aValue
!= "Off" )
11849 rKid
.m_aValue
= rKid
.m_aOnValue
;
11852 // finally move the "Yes" appearance to the OnValue appearance
11853 for( int nKid
= 0; nKid
< nChildren
; nKid
++ )
11855 int nKidIndex
= rGroupWidget
.m_aKidsIndex
[nKid
];
11856 PDFWidget
& rKid
= m_aWidgets
[nKidIndex
];
11857 PDFAppearanceMap::iterator app_it
= rKid
.m_aAppearances
.find( "N" );
11858 if( app_it
!= rKid
.m_aAppearances
.end() )
11860 PDFAppearanceStreams::iterator stream_it
= app_it
->second
.find( "Yes" );
11861 if( stream_it
!= app_it
->second
.end() )
11863 SvMemoryStream
* pStream
= stream_it
->second
;
11864 app_it
->second
.erase( stream_it
);
11865 OStringBuffer
aBuf( rKid
.m_aOnValue
.getLength()*2 );
11866 appendName( rKid
.m_aOnValue
, aBuf
);
11867 (app_it
->second
)[ aBuf
.makeStringAndClear() ] = pStream
;
11869 #if OSL_DEBUG_LEVEL > 1
11871 fprintf( stderr
, "error: RadioButton without \"Yes\" stream\n" );
11874 // update selected radio button
11875 if( rKid
.m_aValue
!= "Off" )
11877 rGroupWidget
.m_aValue
= rKid
.m_aValue
;
11883 sal_Int32
PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget
& rBtn
)
11885 sal_Int32 nRadioGroupWidget
= -1;
11887 std::map
< sal_Int32
, sal_Int32
>::const_iterator it
= m_aRadioGroupWidgets
.find( rBtn
.RadioGroup
);
11889 if( it
== m_aRadioGroupWidgets
.end() )
11891 m_aRadioGroupWidgets
[ rBtn
.RadioGroup
] = nRadioGroupWidget
=
11892 sal_Int32(m_aWidgets
.size());
11894 // new group, insert the radiobutton
11895 m_aWidgets
.push_back( PDFWidget() );
11896 m_aWidgets
.back().m_nObject
= createObject();
11897 m_aWidgets
.back().m_nPage
= m_nCurrentPage
;
11898 m_aWidgets
.back().m_eType
= PDFWriter::RadioButton
;
11899 m_aWidgets
.back().m_nRadioGroup
= rBtn
.RadioGroup
;
11900 m_aWidgets
.back().m_nFlags
|= 0x0000C000; // NoToggleToOff and Radio bits
11902 createWidgetFieldName( sal_Int32(m_aWidgets
.size()-1), rBtn
);
11905 nRadioGroupWidget
= it
->second
;
11907 return nRadioGroupWidget
;
11910 sal_Int32
PDFWriterImpl::createControl( const PDFWriter::AnyWidget
& rControl
, sal_Int32 nPageNr
)
11913 nPageNr
= m_nCurrentPage
;
11915 if( nPageNr
< 0 || nPageNr
>= (sal_Int32
)m_aPages
.size() )
11918 bool sigHidden(true);
11919 sal_Int32 nNewWidget
= m_aWidgets
.size();
11920 m_aWidgets
.push_back( PDFWidget() );
11922 m_aWidgets
.back().m_nObject
= createObject();
11923 m_aWidgets
.back().m_aRect
= rControl
.Location
;
11924 m_aWidgets
.back().m_nPage
= nPageNr
;
11925 m_aWidgets
.back().m_eType
= rControl
.getType();
11927 sal_Int32 nRadioGroupWidget
= -1;
11928 // for unknown reasons the radio buttons of a radio group must not have a
11929 // field name, else the buttons are in fact check boxes -
11930 // that is multiple buttons of the radio group can be selected
11931 if( rControl
.getType() == PDFWriter::RadioButton
)
11932 nRadioGroupWidget
= findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget
&>(rControl
) );
11935 createWidgetFieldName( nNewWidget
, rControl
);
11938 // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
11939 PDFWidget
& rNewWidget
= m_aWidgets
[nNewWidget
];
11940 rNewWidget
.m_aDescription
= rControl
.Description
;
11941 rNewWidget
.m_aText
= rControl
.Text
;
11942 rNewWidget
.m_nTextStyle
= rControl
.TextStyle
&
11943 ( TEXT_DRAW_LEFT
| TEXT_DRAW_CENTER
| TEXT_DRAW_RIGHT
| TEXT_DRAW_TOP
|
11944 TEXT_DRAW_VCENTER
| TEXT_DRAW_BOTTOM
|
11945 TEXT_DRAW_MULTILINE
| TEXT_DRAW_WORDBREAK
);
11946 rNewWidget
.m_nTabOrder
= rControl
.TabOrder
;
11948 // various properties are set via the flags (/Ff) property of the field dict
11949 if( rControl
.ReadOnly
)
11950 rNewWidget
.m_nFlags
|= 1;
11951 if( rControl
.getType() == PDFWriter::PushButton
)
11953 const PDFWriter::PushButtonWidget
& rBtn
= static_cast<const PDFWriter::PushButtonWidget
&>(rControl
);
11954 if( rNewWidget
.m_nTextStyle
== 0 )
11955 rNewWidget
.m_nTextStyle
=
11956 TEXT_DRAW_CENTER
| TEXT_DRAW_VCENTER
|
11957 TEXT_DRAW_MULTILINE
| TEXT_DRAW_WORDBREAK
;
11959 rNewWidget
.m_nFlags
|= 0x00010000;
11960 if( !rBtn
.URL
.isEmpty() )
11961 rNewWidget
.m_aListEntries
.push_back( rBtn
.URL
);
11962 rNewWidget
.m_bSubmit
= rBtn
.Submit
;
11963 rNewWidget
.m_bSubmitGet
= rBtn
.SubmitGet
;
11964 rNewWidget
.m_nDest
= rBtn
.Dest
;
11965 createDefaultPushButtonAppearance( rNewWidget
, rBtn
);
11967 else if( rControl
.getType() == PDFWriter::RadioButton
)
11969 const PDFWriter::RadioButtonWidget
& rBtn
= static_cast<const PDFWriter::RadioButtonWidget
&>(rControl
);
11970 if( rNewWidget
.m_nTextStyle
== 0 )
11971 rNewWidget
.m_nTextStyle
=
11972 TEXT_DRAW_VCENTER
| TEXT_DRAW_MULTILINE
| TEXT_DRAW_WORDBREAK
;
11973 /* PDF sees a RadioButton group as one radio button with
11974 * children which are in turn check boxes
11976 * so we need to create a radio button on demand for a new group
11977 * and insert a checkbox for each RadioButtonWidget as its child
11979 rNewWidget
.m_eType
= PDFWriter::CheckBox
;
11980 rNewWidget
.m_nRadioGroup
= rBtn
.RadioGroup
;
11982 DBG_ASSERT( nRadioGroupWidget
>= 0 && nRadioGroupWidget
< (sal_Int32
)m_aWidgets
.size(), "no radio group parent" );
11984 PDFWidget
& rRadioButton
= m_aWidgets
[nRadioGroupWidget
];
11985 rRadioButton
.m_aKids
.push_back( rNewWidget
.m_nObject
);
11986 rRadioButton
.m_aKidsIndex
.push_back( nNewWidget
);
11987 rNewWidget
.m_nParent
= rRadioButton
.m_nObject
;
11989 rNewWidget
.m_aValue
= OUString( "Off" );
11990 rNewWidget
.m_aOnValue
= rBtn
.OnValue
;
11991 if( rRadioButton
.m_aValue
.isEmpty() && rBtn
.Selected
)
11993 rNewWidget
.m_aValue
= rNewWidget
.m_aOnValue
;
11994 rRadioButton
.m_aValue
= rNewWidget
.m_aOnValue
;
11996 createDefaultRadioButtonAppearance( rNewWidget
, rBtn
);
11998 // union rect of radio group
11999 Rectangle aRect
= rNewWidget
.m_aRect
;
12000 m_aPages
[ nPageNr
].convertRect( aRect
);
12001 rRadioButton
.m_aRect
.Union( aRect
);
12003 else if( rControl
.getType() == PDFWriter::CheckBox
)
12005 const PDFWriter::CheckBoxWidget
& rBox
= static_cast<const PDFWriter::CheckBoxWidget
&>(rControl
);
12006 if( rNewWidget
.m_nTextStyle
== 0 )
12007 rNewWidget
.m_nTextStyle
=
12008 TEXT_DRAW_VCENTER
| TEXT_DRAW_MULTILINE
| TEXT_DRAW_WORDBREAK
;
12010 rNewWidget
.m_aValue
= rBox
.Checked
? OUString("Yes") : OUString("Off" );
12011 // create default appearance before m_aRect gets transformed
12012 createDefaultCheckBoxAppearance( rNewWidget
, rBox
);
12014 else if( rControl
.getType() == PDFWriter::ListBox
)
12016 if( rNewWidget
.m_nTextStyle
== 0 )
12017 rNewWidget
.m_nTextStyle
= TEXT_DRAW_VCENTER
;
12019 const PDFWriter::ListBoxWidget
& rLstBox
= static_cast<const PDFWriter::ListBoxWidget
&>(rControl
);
12020 rNewWidget
.m_aListEntries
= rLstBox
.Entries
;
12021 rNewWidget
.m_aSelectedEntries
= rLstBox
.SelectedEntries
;
12022 rNewWidget
.m_aValue
= rLstBox
.Text
;
12023 if( rLstBox
.DropDown
)
12024 rNewWidget
.m_nFlags
|= 0x00020000;
12026 rNewWidget
.m_nFlags
|= 0x00080000;
12027 if( rLstBox
.MultiSelect
&& !rLstBox
.DropDown
&& (int)m_aContext
.Version
> (int)PDFWriter::PDF_1_3
)
12028 rNewWidget
.m_nFlags
|= 0x00200000;
12030 createDefaultListBoxAppearance( rNewWidget
, rLstBox
);
12032 else if( rControl
.getType() == PDFWriter::ComboBox
)
12034 if( rNewWidget
.m_nTextStyle
== 0 )
12035 rNewWidget
.m_nTextStyle
= TEXT_DRAW_VCENTER
;
12037 const PDFWriter::ComboBoxWidget
& rBox
= static_cast<const PDFWriter::ComboBoxWidget
&>(rControl
);
12038 rNewWidget
.m_aValue
= rBox
.Text
;
12039 rNewWidget
.m_aListEntries
= rBox
.Entries
;
12040 rNewWidget
.m_nFlags
|= 0x00060000; // combo and edit flag
12042 rNewWidget
.m_nFlags
|= 0x00080000;
12044 PDFWriter::ListBoxWidget aLBox
;
12045 aLBox
.Name
= rBox
.Name
;
12046 aLBox
.Description
= rBox
.Description
;
12047 aLBox
.Text
= rBox
.Text
;
12048 aLBox
.TextStyle
= rBox
.TextStyle
;
12049 aLBox
.ReadOnly
= rBox
.ReadOnly
;
12050 aLBox
.Border
= rBox
.Border
;
12051 aLBox
.BorderColor
= rBox
.BorderColor
;
12052 aLBox
.Background
= rBox
.Background
;
12053 aLBox
.BackgroundColor
= rBox
.BackgroundColor
;
12054 aLBox
.TextFont
= rBox
.TextFont
;
12055 aLBox
.TextColor
= rBox
.TextColor
;
12056 aLBox
.DropDown
= true;
12057 aLBox
.Sort
= rBox
.Sort
;
12058 aLBox
.MultiSelect
= false;
12059 aLBox
.Entries
= rBox
.Entries
;
12061 createDefaultListBoxAppearance( rNewWidget
, aLBox
);
12063 else if( rControl
.getType() == PDFWriter::Edit
)
12065 if( rNewWidget
.m_nTextStyle
== 0 )
12066 rNewWidget
.m_nTextStyle
= TEXT_DRAW_LEFT
| TEXT_DRAW_VCENTER
;
12068 const PDFWriter::EditWidget
& rEdit
= static_cast<const PDFWriter::EditWidget
&>(rControl
);
12069 if( rEdit
.MultiLine
)
12071 rNewWidget
.m_nFlags
|= 0x00001000;
12072 rNewWidget
.m_nTextStyle
|= TEXT_DRAW_MULTILINE
| TEXT_DRAW_WORDBREAK
;
12074 if( rEdit
.Password
)
12075 rNewWidget
.m_nFlags
|= 0x00002000;
12076 if( rEdit
.FileSelect
&& m_aContext
.Version
> PDFWriter::PDF_1_3
)
12077 rNewWidget
.m_nFlags
|= 0x00100000;
12078 rNewWidget
.m_nMaxLen
= rEdit
.MaxLen
;
12079 rNewWidget
.m_aValue
= rEdit
.Text
;
12081 createDefaultEditAppearance( rNewWidget
, rEdit
);
12083 #if !defined(ANDROID) && !defined(IOS)
12084 else if( rControl
.getType() == PDFWriter::Signature
)
12086 const PDFWriter::SignatureWidget
& rSig
= static_cast<const PDFWriter::SignatureWidget
&>(rControl
);
12087 sigHidden
= rSig
.SigHidden
;
12090 rNewWidget
.m_aRect
= Rectangle(0, 0, 0, 0);
12092 m_nSignatureObject
= createObject();
12093 rNewWidget
.m_aValue
= OUString::valueOf( m_nSignatureObject
);
12094 rNewWidget
.m_aValue
+= " 0 R";
12095 //createDefaultSignatureAppearance( rNewWidget, rSig );
12096 // let's add a fake appearance
12097 rNewWidget
.m_aAppearances
[ "N" ][ "Standard" ] = new SvMemoryStream();
12101 // if control is a hidden signature, do not convert coordinates since we
12102 // need /Rect [ 0 0 0 0 ]
12103 if ( ! ( ( rControl
.getType() == PDFWriter::Signature
) && ( sigHidden
) ) )
12105 // convert to default user space now, since the mapmode may change
12106 // note: create default appearances before m_aRect gets transformed
12107 m_aPages
[ nPageNr
].convertRect( rNewWidget
.m_aRect
);
12110 // insert widget to page's annotation list
12111 m_aPages
[ nPageNr
].m_aAnnotations
.push_back( rNewWidget
.m_nObject
);
12113 // mark page as having widgets
12114 m_aPages
[ nPageNr
].m_bHasWidgets
= true;
12119 void PDFWriterImpl::addStream( const String
& rMimeType
, PDFOutputStream
* pStream
, bool bCompress
)
12123 m_aAdditionalStreams
.push_back( PDFAddStream() );
12124 PDFAddStream
& rStream
= m_aAdditionalStreams
.back();
12125 rStream
.m_aMimeType
= rMimeType
.Len()
12126 ? OUString( rMimeType
)
12127 : OUString( "application/octet-stream" );
12128 rStream
.m_pStream
= pStream
;
12129 rStream
.m_bCompress
= bCompress
;
12135 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */