update ooo310-m15
[ooovba.git] / vcl / source / gdi / pdfwriter_impl.cxx
blob88ac192592648f17a8892bd0a266de103d62fc0b
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: pdfwriter_impl.cxx,v $
10 * $Revision: 1.132.72.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #define _USE_MATH_DEFINES
35 #include <math.h>
36 #include <algorithm>
38 #include <pdfwriter_impl.hxx>
39 #include <basegfx/polygon/b2dpolygon.hxx>
40 #include <basegfx/polygon/b2dpolypolygon.hxx>
41 #include <basegfx/polygon/b2dpolygontools.hxx>
42 #include <basegfx/polygon/b2dpolypolygontools.hxx>
43 #include <rtl/ustrbuf.hxx>
44 #include <tools/debug.hxx>
45 #include <tools/zcodec.hxx>
46 #include <tools/stream.hxx>
47 #include <tools/urlobj.hxx> //for relative url
48 #include <i18npool/mslangid.hxx>
49 #include <vcl/virdev.hxx>
50 #include <vcl/bmpacc.hxx>
51 #include <vcl/bitmapex.hxx>
52 #include <vcl/image.hxx>
53 #include <vcl/outdev.h>
54 #include <vcl/sallayout.hxx>
55 #include <vcl/metric.hxx>
56 #include <svsys.h>
57 #include <vcl/salgdi.hxx>
58 #include <vcl/svapp.hxx>
59 #include <osl/thread.h>
60 #include <osl/file.h>
61 #include <rtl/crc.h>
62 #include <rtl/digest.h>
63 #include <comphelper/processfactory.hxx>
64 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
65 #include <com/sun/star/util/URL.hpp>
67 #include "implncvt.hxx"
69 #include "cppuhelper/implbase1.hxx"
70 #include <icc/sRGB-IEC61966-2.1.hxx>
72 using namespace vcl;
73 using namespace rtl;
75 #if (OSL_DEBUG_LEVEL < 2)
76 #define COMPRESS_PAGES
77 #else
78 #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams
79 #endif
81 #ifdef DO_TEST_PDF
82 class PDFTestOutputStream : public PDFOutputStream
84 public:
85 virtual ~PDFTestOutputStream();
86 virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream );
89 PDFTestOutputStream::~PDFTestOutputStream()
93 void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream )
95 OString aStr( "lalala\ntest\ntest\ntest" );
96 com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() );
97 rtl_copyMemory( aData.getArray(), aStr.getStr(), aStr.getLength() );
98 xStream->writeBytes( aData );
101 // this test code cannot be used to test PDF/A-1 because it forces
102 // control item (widgets) to bypass the structure controlling
103 // the embedding of such elements in actual run
104 void doTestCode()
106 static const char* pHome = getenv( "HOME" );
107 rtl::OUString aTestFile( RTL_CONSTASCII_USTRINGPARAM( "file://" ) );
108 aTestFile += rtl::OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 );
109 aTestFile += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/pdf_export_test.pdf" ) );
111 PDFWriter::PDFWriterContext aContext;
112 aContext.URL = aTestFile;
113 aContext.Version = PDFWriter::PDF_1_4;
114 aContext.Tagged = true;
115 aContext.InitialPage = 2;
117 PDFWriter aWriter( aContext );
118 PDFDocInfo aDocInfo;
119 aDocInfo.Title = OUString( RTL_CONSTASCII_USTRINGPARAM( "PDF export test document" ) );
120 aDocInfo.Producer = OUString( RTL_CONSTASCII_USTRINGPARAM( "VCL" ) );
121 aWriter.SetDocInfo( aDocInfo );
122 aWriter.NewPage();
123 aWriter.BeginStructureElement( PDFWriter::Document );
124 // set duration of 3 sec for first page
125 aWriter.SetAutoAdvanceTime( 3 );
126 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
128 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
129 aWriter.SetLineColor( Color( COL_LIGHTGREEN ) );
130 aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
132 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
133 aWriter.SetTextColor( Color( COL_BLACK ) );
134 aWriter.SetLineColor( Color( COL_BLACK ) );
135 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
137 Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
138 aWriter.DrawRect( aRect );
139 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 1" ) ) );
140 sal_Int32 nFirstLink = aWriter.CreateLink( aRect );
141 PDFNote aNote;
142 aNote.Title = String( RTL_CONSTASCII_USTRINGPARAM( "A small test note" ) );
143 aNote.Contents = String( RTL_CONSTASCII_USTRINGPARAM( "There is no business like show business like no business i know. Everything about it is appealing." ) );
144 aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote );
146 Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
147 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
148 aWriter.DrawRect( aTargetRect );
149 aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest second link" ) ) );
150 sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect );
152 aWriter.BeginStructureElement( PDFWriter::Section );
153 aWriter.BeginStructureElement( PDFWriter::Heading );
154 aWriter.DrawText( Point(4500, 9000), String( RTL_CONSTASCII_USTRINGPARAM( "A small structure test" ) ) );
155 aWriter.EndStructureElement();
156 aWriter.BeginStructureElement( PDFWriter::Paragraph );
157 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
158 aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline );
159 aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
160 String( RTL_CONSTASCII_USTRINGPARAM( "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." ) ),
161 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
163 aWriter.SetActualText( String( RTL_CONSTASCII_USTRINGPARAM( "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." ) ) );
164 aWriter.SetAlternateText( String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." ) ) );
165 aWriter.EndStructureElement();
166 sal_Int32 nLongPara = aWriter.BeginStructureElement( PDFWriter::Paragraph );
167 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
168 aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
169 String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph is nothing special either but ends on the next page structurewise" ) ),
170 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
173 aWriter.NewPage();
174 // test AddStream interface
175 aWriter.AddStream( String( RTL_CONSTASCII_USTRINGPARAM( "text/plain" ) ), new PDFTestOutputStream(), true );
176 // set transitional mode
177 aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 );
178 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
179 aWriter.SetTextColor( Color( COL_BLACK ) );
180 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
181 aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
182 String( RTL_CONSTASCII_USTRINGPARAM( "Here's where all things come to an end ... well at least the paragaph from the last page." ) ),
183 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
185 aWriter.EndStructureElement();
187 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
188 // disable structure
189 aWriter.BeginStructureElement( PDFWriter::NonStructElement );
190 aWriter.DrawRect( aRect );
191 aWriter.BeginStructureElement( PDFWriter::Paragraph );
192 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 2" ) ) );
193 sal_Int32 nSecondLink = aWriter.CreateLink( aRect );
195 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
196 aWriter.BeginStructureElement( PDFWriter::ListItem );
197 aWriter.DrawRect( aTargetRect );
198 aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest first link" ) ) );
199 sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect );
200 // enable structure
201 aWriter.EndStructureElement();
202 // add something to the long paragraph as an afterthought
203 sal_Int32 nSaveStruct = aWriter.GetCurrentStructureElement();
204 aWriter.SetCurrentStructureElement( nLongPara );
205 aWriter.DrawText( Rectangle( Point( 4500,4500 ), Size( 12000, 1000 ) ),
206 String( RTL_CONSTASCII_USTRINGPARAM( "Add something to the longish paragraph above." ) ),
207 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
208 aWriter.SetCurrentStructureElement( nSaveStruct );
209 aWriter.EndStructureElement();
210 aWriter.EndStructureElement();
211 aWriter.BeginStructureElement( PDFWriter::Figure );
212 aWriter.BeginStructureElement( PDFWriter::Caption );
213 aWriter.DrawText( Point( 4500, 9000 ), String( RTL_CONSTASCII_USTRINGPARAM( "Some drawing stuff inside the structure" ) ) );
214 aWriter.EndStructureElement();
215 aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
216 // test transparency
217 // draw background
218 Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
219 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
220 aWriter.DrawRect( aTranspRect );
221 aWriter.BeginTransparencyGroup();
223 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
224 aWriter.DrawEllipse( aTranspRect );
225 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
226 aWriter.DrawText( aTranspRect,
227 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
228 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
230 aWriter.EndTransparencyGroup( aTranspRect, 50 );
232 // prepare an alpha mask
233 Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
234 BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess();
235 for( int nX = 0; nX < 256; nX++ )
236 for( int nY = 0; nY < 256; nY++ )
237 pAcc->SetPixel( nX, nY, BitmapColor( (BYTE)((nX+nY)/2) ) );
238 aTransMask.ReleaseAccess( pAcc );
239 aTransMask.SetPrefMapMode( MAP_MM );
240 aTransMask.SetPrefSize( Size( 10, 10 ) );
242 aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask );
244 aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
245 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
246 aWriter.DrawRect( aTranspRect );
247 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
248 aWriter.DrawEllipse( aTranspRect );
249 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
250 aWriter.DrawText( aTranspRect,
251 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
252 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
253 aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
254 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
255 aWriter.DrawRect( aTranspRect );
256 aWriter.BeginTransparencyGroup();
257 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
258 aWriter.DrawEllipse( aTranspRect );
259 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
260 aWriter.DrawText( aTranspRect,
261 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
262 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
263 aWriter.EndTransparencyGroup( aTranspRect, aTransMask );
265 Bitmap aImageBmp( Size( 256, 256 ), 24 );
266 pAcc = aImageBmp.AcquireWriteAccess();
267 pAcc->SetFillColor( Color( 0xff, 0, 0xff ) );
268 pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
269 aImageBmp.ReleaseAccess( pAcc );
270 BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) );
271 aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx );
274 aWriter.EndStructureElement();
275 aWriter.EndStructureElement();
277 LineInfo aLI( LINE_DASH, 3 );
278 aLI.SetDashCount( 2 );
279 aLI.SetDashLen( 50 );
280 aLI.SetDotCount( 2 );
281 aLI.SetDotLen( 25 );
282 aLI.SetDistance( 15 );
283 Point aLIPoints[] = { Point( 4000, 10000 ),
284 Point( 8000, 12000 ),
285 Point( 3000, 19000 ) };
286 Polygon aLIPoly( 3, aLIPoints );
287 aWriter.SetLineColor( Color( COL_BLUE ) );
288 aWriter.SetFillColor();
289 aWriter.DrawPolyLine( aLIPoly, aLI );
291 aLI.SetDashCount( 4 );
292 aLIPoly.Move( 1000, 1000 );
293 aWriter.DrawPolyLine( aLIPoly, aLI );
295 aWriter.NewPage();
296 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
297 Wallpaper aWall( aTransMask );
298 aWall.SetStyle( WALLPAPER_TILE );
299 aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall );
301 aWriter.Push( PUSH_ALL );
302 aWriter.BeginPattern(Rectangle(Point(0,0),Size(2000,1000)));
303 aWriter.SetFillColor( Color( COL_RED ) );
304 aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
305 Point aFillPoints[] = { Point( 1000, 0 ),
306 Point( 0, 1000 ),
307 Point( 2000, 1000 ) };
308 aWriter.DrawPolygon( Polygon( 3, aFillPoints ) );
309 aWriter.DrawBitmap( Point( 200, 200 ), Size( 1600, 600 ), aTransMask );
310 aWriter.DrawText( Rectangle( Point( 200, 200 ), Size( 1600, 600 ) ), String( RTL_CONSTASCII_USTRINGPARAM( "Pattern" ) ) );
311 sal_Int32 nPattern = aWriter.EndPattern( SvtGraphicFill::Transform() );
312 aWriter.Pop();
313 Rectangle aPolyRect( Point( 3800, 11200 ), Size( 10200, 6300 ) );
314 aWriter.DrawPolyPolygon( PolyPolygon( Polygon( aPolyRect ) ), nPattern, true );
315 aWriter.SetFillColor();
316 aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
317 aWriter.DrawRect( aPolyRect );
319 aWriter.NewPage();
320 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
321 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
322 aWriter.SetTextColor( Color( COL_BLACK ) );
323 aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
324 aWriter.DrawRect( aRect );
325 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "www.heise.de" ) ) );
326 sal_Int32 nURILink = aWriter.CreateLink( aRect );
327 aWriter.SetLinkURL( nURILink, OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) ) );
329 aWriter.SetLinkDest( nFirstLink, nFirstDest );
330 aWriter.SetLinkDest( nSecondLink, nSecondDest );
332 // include a button
333 PDFWriter::PushButtonWidget aBtn;
334 aBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testButton" ) );
335 aBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test button" ) );
336 aBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "hit me" ) );
337 aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
338 aBtn.Border = aBtn.Background = true;
339 aWriter.CreateControl( aBtn );
341 // include a uri button
342 PDFWriter::PushButtonWidget aUriBtn;
343 aUriBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "wwwButton" ) );
344 aUriBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A URI button" ) );
345 aUriBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to www" ) );
346 aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
347 aUriBtn.Border = aUriBtn.Background = true;
348 aUriBtn.URL = OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) );
349 aWriter.CreateControl( aUriBtn );
351 // include a dest button
352 PDFWriter::PushButtonWidget aDstBtn;
353 aDstBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "destButton" ) );
354 aDstBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A Dest button" ) );
355 aDstBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to paragraph" ) );
356 aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
357 aDstBtn.Border = aDstBtn.Background = true;
358 aDstBtn.Dest = nFirstDest;
359 aWriter.CreateControl( aDstBtn );
361 PDFWriter::CheckBoxWidget aCBox;
362 aCBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox" ) );
363 aCBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test check box" ) );
364 aCBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me" ) );
365 aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
366 aCBox.Checked = true;
367 aCBox.Border = aCBox.Background = false;
368 aWriter.CreateControl( aCBox );
370 PDFWriter::CheckBoxWidget aCBox2;
371 aCBox2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox2" ) );
372 aCBox2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "Another test check box" ) );
373 aCBox2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me right" ) );
374 aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
375 aCBox2.Checked = true;
376 aCBox2.Border = aCBox2.Background = false;
377 aCBox2.ButtonIsLeft = false;
378 aWriter.CreateControl( aCBox2 );
380 PDFWriter::RadioButtonWidget aRB1;
381 aRB1.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_1" ) );
382 aRB1.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 1" ) );
383 aRB1.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Despair" ) );
384 aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
385 aRB1.Selected = true;
386 aRB1.RadioGroup = 1;
387 aRB1.Border = aRB1.Background = true;
388 aRB1.ButtonIsLeft = false;
389 aRB1.BorderColor = Color( COL_LIGHTGREEN );
390 aRB1.BackgroundColor = Color( COL_LIGHTBLUE );
391 aRB1.TextColor = Color( COL_LIGHTRED );
392 aRB1.TextFont = Font( String( RTL_CONSTASCII_USTRINGPARAM( "Courier" ) ), Size( 0, 800 ) );
393 aWriter.CreateControl( aRB1 );
395 PDFWriter::RadioButtonWidget aRB2;
396 aRB2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb2_1" ) );
397 aRB2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 2 button 1" ) );
398 aRB2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Joy" ) );
399 aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
400 aRB2.Selected = true;
401 aRB2.RadioGroup = 2;
402 aWriter.CreateControl( aRB2 );
404 PDFWriter::RadioButtonWidget aRB3;
405 aRB3.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_2" ) );
406 aRB3.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 2" ) );
407 aRB3.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Desperation" ) );
408 aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
409 aRB3.Selected = true;
410 aRB3.RadioGroup = 1;
411 aWriter.CreateControl( aRB3 );
413 PDFWriter::EditWidget aEditBox;
414 aEditBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testEdit" ) );
415 aEditBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test edit field" ) );
416 aEditBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "A little test text" ) );
417 aEditBox.TextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
418 aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
419 aEditBox.MaxLen = 100;
420 aEditBox.Border = aEditBox.Background = true;
421 aEditBox.BorderColor = Color( COL_BLACK );
422 aWriter.CreateControl( aEditBox );
424 // normal list box
425 PDFWriter::ListBoxWidget aLstBox;
426 aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testListBox" ) );
427 aLstBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) );
428 aLstBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "select me" ) );
429 aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
430 aLstBox.Sort = true;
431 aLstBox.MultiSelect = true;
432 aLstBox.Border = aLstBox.Background = true;
433 aLstBox.BorderColor = Color( COL_BLACK );
434 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) ) );
435 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Two" ) ) );
436 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Three" ) ) );
437 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Four" ) ) );
438 aLstBox.SelectedEntries.push_back( 1 );
439 aLstBox.SelectedEntries.push_back( 2 );
440 aWriter.CreateControl( aLstBox );
442 // dropdown list box
443 aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testDropDownListBox" ) );
444 aLstBox.DropDown = true;
445 aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
446 aWriter.CreateControl( aLstBox );
448 // combo box
449 PDFWriter::ComboBoxWidget aComboBox;
450 aComboBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testComboBox" ) );
451 aComboBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "test a combobox" ) );
452 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Larry" ) ) );
453 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Curly" ) ) );
454 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Moe" ) ) );
455 aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
456 aWriter.CreateControl( aComboBox );
458 // test outlines
459 sal_Int32 nPage1OL = aWriter.CreateOutlineItem();
460 aWriter.SetOutlineItemText( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 1" ) ) );
461 aWriter.SetOutlineItemDest( nPage1OL, nSecondDest );
462 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2" ) ), nSecondDest );
463 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 revisited" ) ), nSecondDest );
464 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 again" ) ), nSecondDest );
465 sal_Int32 nPage2OL = aWriter.CreateOutlineItem();
466 aWriter.SetOutlineItemText( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 2" ) ) );
467 aWriter.CreateOutlineItem( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 1" ) ), nFirstDest );
469 aWriter.EndStructureElement(); // close document
470 aWriter.Emit();
472 #endif
474 static const sal_Int32 nLog10Divisor = 1;
475 static const double fDivisor = 10.0;
477 static inline double pixelToPoint( sal_Int32 px ) { return double(px)/fDivisor; }
478 static inline double pixelToPoint( double px ) { return px/fDivisor; }
479 static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); }
481 static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
483 static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
484 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
485 rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
486 rBuffer.append( pHexDigits[ nInt & 15 ] );
489 static void appendName( const OUString& rStr, OStringBuffer& rBuffer )
491 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
492 // I guess than when reading the #xx sequence it will count for a single character.
493 OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) );
494 const sal_Char* pStr = aStr.getStr();
495 int nLen = aStr.getLength();
496 for( int i = 0; i < nLen; i++ )
498 /* #i16920# PDF recommendation: output UTF8, any byte
499 * outside the interval [33(=ASCII'!');126(=ASCII'~')]
500 * should be escaped hexadecimal
501 * for the sake of ghostscript which also reads PDF
502 * but has a narrower acceptance rate we only pass
503 * alphanumerics and '-' literally.
505 if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) ||
506 (pStr[i] >= 'a' && pStr[i] <= 'z' ) ||
507 (pStr[i] >= '0' && pStr[i] <= '9' ) ||
508 pStr[i] == '-' )
510 rBuffer.append( pStr[i] );
512 else
514 rBuffer.append( '#' );
515 appendHex( (sal_Int8)pStr[i], rBuffer );
520 static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer )
522 //FIXME i59651 see above
523 while( pStr && *pStr )
525 if( (*pStr >= 'A' && *pStr <= 'Z' ) ||
526 (*pStr >= 'a' && *pStr <= 'z' ) ||
527 (*pStr >= '0' && *pStr <= '9' ) ||
528 *pStr == '-' )
530 rBuffer.append( *pStr );
532 else
534 rBuffer.append( '#' );
535 appendHex( (sal_Int8)*pStr, rBuffer );
537 pStr++;
541 //used only to emit encoded passwords
542 static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer )
544 while( nLength )
546 switch( *pStr )
548 case '\n' :
549 rBuffer.append( "\\n" );
550 break;
551 case '\r' :
552 rBuffer.append( "\\r" );
553 break;
554 case '\t' :
555 rBuffer.append( "\\t" );
556 break;
557 case '\b' :
558 rBuffer.append( "\\b" );
559 break;
560 case '\f' :
561 rBuffer.append( "\\f" );
562 break;
563 case '(' :
564 case ')' :
565 case '\\' :
566 rBuffer.append( "\\" );
567 rBuffer.append( (sal_Char) *pStr );
568 break;
569 default:
570 rBuffer.append( (sal_Char) *pStr );
571 break;
573 pStr++;
574 nLength--;
578 /**--->i56629
579 * Convert a string before using it.
581 * This string conversion function is needed because the destination name
582 * in a PDF file seen through an Internet browser should be
583 * specially crafted, in order to be used directly by the browser.
584 * In this way the fragment part of a hyperlink to a PDF file (e.g. something
585 * as 'test1/test2/a-file.pdf#thefragment) will be (hopefully) interpreted by the
586 * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
587 * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
588 * and go to named destination thefragment using default zoom'.
589 * The conversion is needed because in case of a fragment in the form: Slide%201
590 * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
591 * using this conversion, in both the generated named destinations, fragment and GoToR
592 * destination.
594 * The names for destinations are name objects and so they don't need to be encrypted
595 * even though they expose the content of PDF file (e.g. guessing the PDF content from the
596 * destination name).
598 * Fhurter limitation: it is advisable to use standard ASCII characters for
599 * OOo bookmarks.
601 static void appendDestinationName( const rtl::OUString& rString, OStringBuffer& rBuffer )
603 const sal_Unicode* pStr = rString.getStr();
604 sal_Int32 nLen = rString.getLength();
605 for( int i = 0; i < nLen; i++ )
607 sal_Unicode aChar = pStr[i];
608 if( (aChar >= '0' && aChar <= '9' ) ||
609 (aChar >= 'a' && aChar <= 'z' ) ||
610 (aChar >= 'A' && aChar <= 'Z' ) ||
611 aChar == '-' )
613 rBuffer.append((sal_Char)aChar);
615 else
617 sal_Int8 aValueHigh = sal_Int8(aChar >> 8);
618 if(aValueHigh > 0)
619 appendHex( aValueHigh, rBuffer );
620 appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
624 //<--- i56629
626 static void appendUnicodeTextString( const rtl::OUString& rString, OStringBuffer& rBuffer )
628 rBuffer.append( "FEFF" );
629 const sal_Unicode* pStr = rString.getStr();
630 sal_Int32 nLen = rString.getLength();
631 for( int i = 0; i < nLen; i++ )
633 sal_Unicode aChar = pStr[i];
634 appendHex( (sal_Int8)(aChar >> 8), rBuffer );
635 appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
639 OString PDFWriterImpl::convertWidgetFieldName( const rtl::OUString& rString )
641 OStringBuffer aBuffer( rString.getLength()+64 );
643 /* #i80258# previously we use appendName here
644 however we need a slightly different coding scheme than the normal
645 name encoding for field names
647 also replace all '.' by '_' as '.' indicates a hierarchy level which
648 we do not have here
651 OString aStr( OUStringToOString( rString, RTL_TEXTENCODING_UTF8 ) );
652 const sal_Char* pStr = aStr.getStr();
653 int nLen = aStr.getLength();
654 for( int i = 0; i < nLen; i++ )
656 /* #i16920# PDF recommendation: output UTF8, any byte
657 * outside the interval [33(=ASCII'!');126(=ASCII'~')]
658 * should be escaped hexadecimal
660 if( pStr[i] == '.' )
661 aBuffer.append( '_' );
662 else if( (pStr[i] >= 33 && pStr[i] <= 126 ) )
663 aBuffer.append( pStr[i] );
664 else
666 aBuffer.append( '#' );
667 appendHex( (sal_Int8)pStr[i], aBuffer );
671 OString aRet = aBuffer.makeStringAndClear();
672 std::hash_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aRet );
674 if( it != m_aFieldNameMap.end() ) // not unique
676 std::hash_map< OString, sal_Int32, OStringHash >::const_iterator check_it;
677 OString aTry;
680 OStringBuffer aUnique( aRet.getLength() + 16 );
681 aUnique.append( aRet );
682 aUnique.append( '_' );
683 aUnique.append( it->second );
684 it->second++;
685 aTry = aUnique.makeStringAndClear();
686 check_it = m_aFieldNameMap.find( aTry );
687 } while( check_it != m_aFieldNameMap.end() );
688 aRet = aTry;
690 else
691 m_aFieldNameMap[ aRet ] = 2;
692 return aRet;
695 static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor )
697 if( nValue < 0 )
699 rBuffer.append( '-' );
700 nValue = -nValue;
702 sal_Int32 nFactor = 1, nDiv = nPrecision;
703 while( nDiv-- )
704 nFactor *= 10;
706 sal_Int32 nInt = nValue / nFactor;
707 rBuffer.append( nInt );
708 if( nFactor > 1 )
710 sal_Int32 nDecimal = nValue % nFactor;
711 if( nDecimal )
713 rBuffer.append( '.' );
714 // omit trailing zeros
715 while( (nDecimal % 10) == 0 )
716 nDecimal /= 10;
717 rBuffer.append( nDecimal );
723 // appends a double. PDF does not accept exponential format, only fixed point
724 static void appendDouble( double fValue, OStringBuffer& rBuffer, int nPrecision = 5 )
726 bool bNeg = false;
727 if( fValue < 0.0 )
729 bNeg = true;
730 fValue=-fValue;
733 sal_Int64 nInt = (sal_Int64)fValue;
734 fValue -= (double)nInt;
735 // optimizing hardware may lead to a value of 1.0 after the subtraction
736 if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision )
738 nInt++;
739 fValue = 0.0;
741 sal_Int64 nFrac = 0;
742 if( fValue )
744 fValue *= pow( 10.0, (double)nPrecision );
745 nFrac = (sal_Int64)fValue;
747 if( bNeg && ( nInt || nFrac ) )
748 rBuffer.append( '-' );
749 rBuffer.append( nInt );
750 if( nFrac )
752 int i;
753 rBuffer.append( '.' );
754 sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5);
755 for ( i = 0; ( i < nPrecision ) && nFrac; i++ )
757 sal_Int64 nNumb = nFrac / nBound;
758 nFrac -= nNumb * nBound;
759 rBuffer.append( nNumb );
760 nBound /= 10;
766 static void appendColor( const Color& rColor, OStringBuffer& rBuffer )
769 if( rColor != Color( COL_TRANSPARENT ) )
771 appendDouble( (double)rColor.GetRed() / 255.0, rBuffer );
772 rBuffer.append( ' ' );
773 appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer );
774 rBuffer.append( ' ' );
775 appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer );
779 static void appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
781 if( rColor != Color( COL_TRANSPARENT ) )
783 appendColor( rColor, rBuffer );
784 rBuffer.append( " RG" );
788 static void appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
790 if( rColor != Color( COL_TRANSPARENT ) )
792 appendColor( rColor, rBuffer );
793 rBuffer.append( " rg" );
797 // matrix helper class
798 // TODO: use basegfx matrix class instead or derive from it
799 namespace vcl // TODO: use anonymous namespace to keep this class local
801 /* for sparse matrices of the form (2D linear transformations)
802 * f[0] f[1] 0
803 * f[2] f[3] 0
804 * f[4] f[5] 1
806 class Matrix3
808 double f[6];
810 void set( double *pn ) { for( int i = 0 ; i < 5; i++ ) f[i] = pn[i]; }
811 public:
812 Matrix3();
813 ~Matrix3() {}
815 void skew( double alpha, double beta );
816 void scale( double sx, double sy );
817 void rotate( double angle );
818 void translate( double tx, double ty );
819 bool invert();
821 void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL );
823 Point transform( const Point& rPoint ) const;
827 Matrix3::Matrix3()
829 // initialize to unity
830 f[0] = 1.0;
831 f[1] = 0.0;
832 f[2] = 0.0;
833 f[3] = 1.0;
834 f[4] = 0.0;
835 f[5] = 0.0;
838 Point Matrix3::transform( const Point& rOrig ) const
840 double x = (double)rOrig.X(), y = (double)rOrig.Y();
841 return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) );
844 void Matrix3::skew( double alpha, double beta )
846 double fn[6];
847 double tb = tan( beta );
848 fn[0] = f[0] + f[2]*tb;
849 fn[1] = f[1];
850 fn[2] = f[2] + f[3]*tb;
851 fn[3] = f[3];
852 fn[4] = f[4] + f[5]*tb;
853 fn[5] = f[5];
854 if( alpha != 0.0 )
856 double ta = tan( alpha );
857 fn[1] += f[0]*ta;
858 fn[3] += f[2]*ta;
859 fn[5] += f[4]*ta;
861 set( fn );
864 void Matrix3::scale( double sx, double sy )
866 double fn[6];
867 fn[0] = sx*f[0];
868 fn[1] = sy*f[1];
869 fn[2] = sx*f[2];
870 fn[3] = sy*f[3];
871 fn[4] = sx*f[4];
872 fn[5] = sy*f[5];
873 set( fn );
876 void Matrix3::rotate( double angle )
878 double fn[6];
879 double fSin = sin(angle);
880 double fCos = cos(angle);
881 fn[0] = f[0]*fCos - f[1]*fSin;
882 fn[1] = f[0]*fSin + f[1]*fCos;
883 fn[2] = f[2]*fCos - f[3]*fSin;
884 fn[3] = f[2]*fSin + f[3]*fCos;
885 fn[4] = f[4]*fCos - f[5]*fSin;
886 fn[5] = f[4]*fSin + f[5]*fCos;
887 set( fn );
890 void Matrix3::translate( double tx, double ty )
892 f[4] += tx;
893 f[5] += ty;
896 bool Matrix3::invert()
898 // short circuit trivial cases
899 if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 )
901 f[4] = -f[4];
902 f[5] = -f[5];
903 return true;
906 // check determinant
907 const double fDet = f[0]*f[3]-f[1]*f[2];
908 if( fDet == 0.0 )
909 return false;
911 // invert the matrix
912 double fn[6];
913 fn[0] = +f[3] / fDet;
914 fn[1] = -f[1] / fDet;
915 fn[2] = -f[2] / fDet;
916 fn[3] = +f[0] / fDet;
918 // apply inversion to translation
919 fn[4] = -(f[4]*fn[0] + f[5]*fn[2]);
920 fn[5] = -(f[4]*fn[1] + f[5]*fn[3]);
922 set( fn );
923 return true;
926 void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack )
928 appendDouble( f[0], rBuffer );
929 rBuffer.append( ' ' );
930 appendDouble( f[1], rBuffer );
931 rBuffer.append( ' ' );
932 appendDouble( f[2], rBuffer );
933 rBuffer.append( ' ' );
934 appendDouble( f[3], rBuffer );
935 rBuffer.append( ' ' );
936 rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack );
939 static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList )
941 if( rList.empty() )
942 return;
943 rBuf.append( '/' );
944 rBuf.append( pPrefix );
945 rBuf.append( "<<" );
946 int ni = 0;
947 for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it )
949 if( it->first.getLength() && it->second > 0 )
951 rBuf.append( '/' );
952 rBuf.append( it->first );
953 rBuf.append( ' ' );
954 rBuf.append( it->second );
955 rBuf.append( " 0 R" );
956 if( ((++ni) & 7) == 0 )
957 rBuf.append( '\n' );
960 rBuf.append( ">>\n" );
963 void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject )
965 rBuf.append( "<</Font " );
966 rBuf.append( nFontDictObject );
967 rBuf.append( " 0 R\n" );
968 appendResourceMap( rBuf, "XObject", m_aXObjects );
969 appendResourceMap( rBuf, "ExtGState", m_aExtGStates );
970 appendResourceMap( rBuf, "Shading", m_aShadings );
971 appendResourceMap( rBuf, "Pattern", m_aPatterns );
972 rBuf.append( "/ProcSet[/PDF/Text" );
973 if( !m_aXObjects.empty() )
974 rBuf.append( "/ImageC/ImageI/ImageB" );
975 rBuf.append( "]\n>>\n" );
978 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
980 m_pWriter( pWriter ),
981 m_nPageWidth( nPageWidth ),
982 m_nPageHeight( nPageHeight ),
983 m_eOrientation( eOrientation ),
984 m_nPageObject( 0 ), // invalid object number
985 m_nPageIndex( -1 ), // invalid index
986 m_nStreamLengthObject( 0 ),
987 m_nBeginStreamPos( 0 ),
988 m_eTransition( PDFWriter::Regular ),
989 m_nTransTime( 0 ),
990 m_nDuration( 0 ),
991 m_bHasWidgets( false )
993 // object ref must be only ever updated in emit()
994 m_nPageObject = m_pWriter->createObject();
997 PDFWriterImpl::PDFPage::~PDFPage()
1001 void PDFWriterImpl::PDFPage::beginStream()
1003 #if OSL_DEBUG_LEVEL > 1
1005 OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" );
1006 m_pWriter->emitComment( aLine.getStr() );
1008 #endif
1009 m_aStreamObjects.push_back(m_pWriter->createObject());
1010 if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) )
1011 return;
1013 m_nStreamLengthObject = m_pWriter->createObject();
1014 // write content stream header
1015 OStringBuffer aLine;
1016 aLine.append( m_aStreamObjects.back() );
1017 aLine.append( " 0 obj\n<</Length " );
1018 aLine.append( m_nStreamLengthObject );
1019 aLine.append( " 0 R" );
1020 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1021 aLine.append( "/Filter/FlateDecode" );
1022 #endif
1023 aLine.append( ">>\nstream\n" );
1024 if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) )
1025 return;
1026 if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &m_nBeginStreamPos ) )
1028 osl_closeFile( m_pWriter->m_aFile );
1029 m_pWriter->m_bOpen = false;
1031 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1032 m_pWriter->beginCompression();
1033 #endif
1034 m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() );
1037 void PDFWriterImpl::PDFPage::endStream()
1039 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1040 m_pWriter->endCompression();
1041 #endif
1042 sal_uInt64 nEndStreamPos;
1043 if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &nEndStreamPos ) )
1045 osl_closeFile( m_pWriter->m_aFile );
1046 m_pWriter->m_bOpen = false;
1047 return;
1049 m_pWriter->disableStreamEncryption();
1050 if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1051 return;
1052 // emit stream length object
1053 if( ! m_pWriter->updateObject( m_nStreamLengthObject ) )
1054 return;
1055 OStringBuffer aLine;
1056 aLine.append( m_nStreamLengthObject );
1057 aLine.append( " 0 obj\n" );
1058 aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) );
1059 aLine.append( "\nendobj\n\n" );
1060 m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1063 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject )
1065 // emit page object
1066 if( ! m_pWriter->updateObject( m_nPageObject ) )
1067 return false;
1068 OStringBuffer aLine;
1070 aLine.append( m_nPageObject );
1071 aLine.append( " 0 obj\n"
1072 "<</Type/Page/Parent " );
1073 aLine.append( nParentObject );
1074 aLine.append( " 0 R" );
1075 aLine.append( "/Resources " );
1076 aLine.append( m_pWriter->getResourceDictObj() );
1077 aLine.append( " 0 R" );
1078 if( m_nPageWidth && m_nPageHeight )
1080 aLine.append( "/MediaBox[0 0 " );
1081 aLine.append( m_nPageWidth );
1082 aLine.append( ' ' );
1083 aLine.append( m_nPageHeight );
1084 aLine.append( "]" );
1086 switch( m_eOrientation )
1088 case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
1089 case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
1090 case PDFWriter::Portrait: aLine.append( "/Rotate 0\n" );break;
1092 case PDFWriter::Inherit:
1093 default:
1094 break;
1096 int nAnnots = m_aAnnotations.size();
1097 if( nAnnots > 0 )
1099 aLine.append( "/Annots[\n" );
1100 for( int i = 0; i < nAnnots; i++ )
1102 aLine.append( m_aAnnotations[i] );
1103 aLine.append( " 0 R" );
1104 aLine.append( ((i+1)%15) ? " " : "\n" );
1106 aLine.append( "]\n" );
1108 #if 0
1109 // FIXME: implement tab order as Structure Tree
1110 if( m_bHasWidgets && m_pWriter->getVersion() >= PDFWriter::PDF_1_5 )
1111 aLine.append( " /Tabs /S\n" );
1112 #endif
1113 if( m_aMCIDParents.size() > 0 )
1115 OStringBuffer aStructParents( 1024 );
1116 aStructParents.append( "[ " );
1117 int nParents = m_aMCIDParents.size();
1118 for( int i = 0; i < nParents; i++ )
1120 aStructParents.append( m_aMCIDParents[i] );
1121 aStructParents.append( " 0 R" );
1122 aStructParents.append( ((i%10) == 9) ? "\n" : " " );
1124 aStructParents.append( "]" );
1125 m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() );
1127 aLine.append( "/StructParents " );
1128 aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) );
1129 aLine.append( "\n" );
1131 if( m_nDuration > 0 )
1133 aLine.append( "/Dur " );
1134 aLine.append( (sal_Int32)m_nDuration );
1135 aLine.append( "\n" );
1137 if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 )
1139 // transition duration
1140 aLine.append( "/Trans<</D " );
1141 appendDouble( (double)m_nTransTime/1000.0, aLine, 3 );
1142 aLine.append( "\n" );
1143 const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL;
1144 switch( m_eTransition )
1146 case PDFWriter::SplitHorizontalInward:
1147 pStyle = "Split"; pDm = "H"; pM = "I"; break;
1148 case PDFWriter::SplitHorizontalOutward:
1149 pStyle = "Split"; pDm = "H"; pM = "O"; break;
1150 case PDFWriter::SplitVerticalInward:
1151 pStyle = "Split"; pDm = "V"; pM = "I"; break;
1152 case PDFWriter::SplitVerticalOutward:
1153 pStyle = "Split"; pDm = "V"; pM = "O"; break;
1154 case PDFWriter::BlindsHorizontal:
1155 pStyle = "Blinds"; pDm = "H"; break;
1156 case PDFWriter::BlindsVertical:
1157 pStyle = "Blinds"; pDm = "V"; break;
1158 case PDFWriter::BoxInward:
1159 pStyle = "Box"; pM = "I"; break;
1160 case PDFWriter::BoxOutward:
1161 pStyle = "Box"; pM = "O"; break;
1162 case PDFWriter::WipeLeftToRight:
1163 pStyle = "Wipe"; pDi = "0"; break;
1164 case PDFWriter::WipeBottomToTop:
1165 pStyle = "Wipe"; pDi = "90"; break;
1166 case PDFWriter::WipeRightToLeft:
1167 pStyle = "Wipe"; pDi = "180"; break;
1168 case PDFWriter::WipeTopToBottom:
1169 pStyle = "Wipe"; pDi = "270"; break;
1170 case PDFWriter::Dissolve:
1171 pStyle = "Dissolve"; break;
1172 case PDFWriter::GlitterLeftToRight:
1173 pStyle = "Glitter"; pDi = "0"; break;
1174 case PDFWriter::GlitterTopToBottom:
1175 pStyle = "Glitter"; pDi = "270"; break;
1176 case PDFWriter::GlitterTopLeftToBottomRight:
1177 pStyle = "Glitter"; pDi = "315"; break;
1178 case PDFWriter::Regular:
1179 break;
1181 // transition style
1182 if( pStyle )
1184 aLine.append( "/S/" );
1185 aLine.append( pStyle );
1186 aLine.append( "\n" );
1188 if( pDm )
1190 aLine.append( "/Dm/" );
1191 aLine.append( pDm );
1192 aLine.append( "\n" );
1194 if( pM )
1196 aLine.append( "/M/" );
1197 aLine.append( pM );
1198 aLine.append( "\n" );
1200 if( pDi )
1202 aLine.append( "/Di " );
1203 aLine.append( pDi );
1204 aLine.append( "\n" );
1206 aLine.append( ">>\n" );
1208 if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 )
1210 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1212 aLine.append( "/Contents" );
1213 unsigned int nStreamObjects = m_aStreamObjects.size();
1214 if( nStreamObjects > 1 )
1215 aLine.append( '[' );
1216 for( unsigned int i = 0; i < m_aStreamObjects.size(); i++ )
1218 aLine.append( ' ' );
1219 aLine.append( m_aStreamObjects[i] );
1220 aLine.append( " 0 R" );
1222 if( nStreamObjects > 1 )
1223 aLine.append( ']' );
1224 aLine.append( ">>\nendobj\n\n" );
1225 return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1228 namespace vcl
1230 template < class GEOMETRY >
1231 GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
1233 GEOMETRY aPoint;
1234 if ( MAP_PIXEL == _rSource.GetMapUnit() )
1236 aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
1238 else
1240 aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
1242 return aPoint;
1246 void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const
1248 if( pOutPoint )
1250 Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1251 m_pWriter->m_aMapMode,
1252 m_pWriter->getReferenceDevice(),
1253 rPoint ) );
1254 *pOutPoint = aPoint;
1257 Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1258 m_pWriter->m_aMapMode,
1259 m_pWriter->getReferenceDevice(),
1260 rPoint ) );
1262 sal_Int32 nValue = aPoint.X();
1263 if( bNeg )
1264 nValue = -nValue;
1266 appendFixedInt( nValue, rBuffer );
1268 rBuffer.append( ' ' );
1270 nValue = pointToPixel(getHeight()) - aPoint.Y();
1271 if( bNeg )
1272 nValue = -nValue;
1274 appendFixedInt( nValue, rBuffer );
1277 void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const
1279 appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer );
1280 rBuffer.append( ' ' );
1281 appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false );
1282 rBuffer.append( ' ' );
1283 appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true );
1284 rBuffer.append( " re" );
1287 void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const
1289 Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1290 m_pWriter->m_aMapMode,
1291 m_pWriter->getReferenceDevice(),
1292 rRect.BottomLeft() + Point( 0, 1 )
1294 Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1295 m_pWriter->m_aMapMode,
1296 m_pWriter->getReferenceDevice(),
1297 rRect.GetSize() );
1298 rRect.Left() = aLL.X();
1299 rRect.Right() = aLL.X() + aSize.Width();
1300 rRect.Top() = pointToPixel(getHeight()) - aLL.Y();
1301 rRect.Bottom() = rRect.Top() + aSize.Height();
1304 void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1306 USHORT nPoints = rPoly.GetSize();
1308 * #108582# applications do weird things
1310 sal_uInt32 nBufLen = rBuffer.getLength();
1311 if( nPoints > 0 )
1313 const BYTE* pFlagArray = rPoly.GetConstFlagAry();
1314 appendPoint( rPoly[0], rBuffer );
1315 rBuffer.append( " m\n" );
1316 for( USHORT i = 1; i < nPoints; i++ )
1318 if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 )
1320 // bezier
1321 DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" );
1322 appendPoint( rPoly[i], rBuffer );
1323 rBuffer.append( " " );
1324 appendPoint( rPoly[i+1], rBuffer );
1325 rBuffer.append( " " );
1326 appendPoint( rPoly[i+2], rBuffer );
1327 rBuffer.append( " c" );
1328 i += 2; // add additionally consumed points
1330 else
1332 // line
1333 appendPoint( rPoly[i], rBuffer );
1334 rBuffer.append( " l" );
1336 if( (rBuffer.getLength() - nBufLen) > 65 )
1338 rBuffer.append( "\n" );
1339 nBufLen = rBuffer.getLength();
1341 else
1342 rBuffer.append( " " );
1344 if( bClose )
1345 rBuffer.append( "h\n" );
1349 void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1351 USHORT nPolygons = rPolyPoly.Count();
1352 for( USHORT n = 0; n < nPolygons; n++ )
1353 appendPolygon( rPolyPoly[n], rBuffer, bClose );
1356 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1358 sal_Int32 nValue = nLength;
1359 if ( nLength < 0 )
1361 rBuffer.append( '-' );
1362 nValue = -nLength;
1364 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1365 m_pWriter->m_aMapMode,
1366 m_pWriter->getReferenceDevice(),
1367 Size( nValue, nValue ) ) );
1368 nValue = bVertical ? aSize.Height() : aSize.Width();
1369 if( pOutLength )
1370 *pOutLength = ((nLength < 0 ) ? -nValue : nValue);
1372 appendFixedInt( nValue, rBuffer, 1 );
1375 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1377 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1378 m_pWriter->m_aMapMode,
1379 m_pWriter->getReferenceDevice(),
1380 Size( 1000, 1000 ) ) );
1381 if( pOutLength )
1382 *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0);
1383 fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0);
1384 appendDouble( fLength, rBuffer );
1387 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const
1389 bool bRet = true;
1390 if( rInfo.GetStyle() == LINE_DASH )
1392 rBuffer.append( "[ " );
1393 if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case
1395 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1396 rBuffer.append( ' ' );
1397 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1398 rBuffer.append( ' ' );
1400 else
1402 // check for implementation limits of dash array
1403 // in PDF reader apps (e.g. acroread)
1404 if( 2*(rInfo.GetDashCount() + rInfo.GetDotCount()) > 10 )
1405 bRet = false;
1406 for( int n = 0; n < rInfo.GetDashCount(); n++ )
1408 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1409 rBuffer.append( ' ' );
1410 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1411 rBuffer.append( ' ' );
1413 for( int m = 0; m < rInfo.GetDotCount(); m++ )
1415 appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer );
1416 rBuffer.append( ' ' );
1417 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1418 rBuffer.append( ' ' );
1421 rBuffer.append( "] 0 d\n" );
1423 if( rInfo.GetWidth() > 1 )
1425 appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer );
1426 rBuffer.append( " w\n" );
1428 else if( rInfo.GetWidth() == 0 )
1430 // "pixel" line
1431 appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->ImplGetDPIX()), rBuffer );
1432 rBuffer.append( " w\n" );
1434 return bRet;
1437 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const
1439 if( nWidth <= 0 )
1440 return;
1441 if( nDelta < 1 )
1442 nDelta = 1;
1444 rBuffer.append( "0 " );
1445 appendMappedLength( nY, rBuffer, true );
1446 rBuffer.append( " m\n" );
1447 for( sal_Int32 n = 0; n < nWidth; )
1449 n += nDelta;
1450 appendMappedLength( n, rBuffer, false );
1451 rBuffer.append( ' ' );
1452 appendMappedLength( nDelta+nY, rBuffer, true );
1453 rBuffer.append( ' ' );
1454 n += nDelta;
1455 appendMappedLength( n, rBuffer, false );
1456 rBuffer.append( ' ' );
1457 appendMappedLength( nY, rBuffer, true );
1458 rBuffer.append( " v " );
1459 if( n < nWidth )
1461 n += nDelta;
1462 appendMappedLength( n, rBuffer, false );
1463 rBuffer.append( ' ' );
1464 appendMappedLength( nY-nDelta, rBuffer, true );
1465 rBuffer.append( ' ' );
1466 n += nDelta;
1467 appendMappedLength( n, rBuffer, false );
1468 rBuffer.append( ' ' );
1469 appendMappedLength( nY, rBuffer, true );
1470 rBuffer.append( " v\n" );
1473 rBuffer.append( "S\n" );
1477 * class PDFWriterImpl
1480 PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext )
1482 m_pReferenceDevice( NULL ),
1483 m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ),
1484 m_nCurrentStructElement( 0 ),
1485 m_bEmitStructure( true ),
1486 m_bNewMCID( false ),
1487 m_nCurrentControl( -1 ),
1488 m_bEmbedStandardFonts( false ),
1489 m_nNextFID( 1 ),
1490 m_nInheritedPageWidth( 595 ), // default A4
1491 m_nInheritedPageHeight( 842 ), // default A4
1492 m_eInheritedOrientation( PDFWriter::Portrait ),
1493 m_nCurrentPage( -1 ),
1494 m_nResourceDict( -1 ),
1495 m_nFontDictObject( -1 ),
1496 m_pCodec( NULL ),
1497 m_aDocDigest( rtl_digest_createMD5() ),
1498 m_aCipher( (rtlCipher)NULL ),
1499 m_aDigest( NULL ),
1500 m_bEncryptThisStream( false ),
1501 m_aDocID( 32 ),
1502 m_aCreationDateString( 64 ),
1503 m_aCreationMetaDateString( 64 ),
1504 m_pEncryptionBuffer( NULL ),
1505 m_nEncryptionBufferSize( 0 ),
1506 m_bIsPDF_A1( false )
1508 #ifdef DO_TEST_PDF
1509 static bool bOnce = true;
1510 if( bOnce )
1512 bOnce = false;
1513 doTestCode();
1515 #endif
1516 m_aContext = rContext;
1517 m_aStructure.push_back( PDFStructureElement() );
1518 m_aStructure[0].m_nOwnElement = 0;
1519 m_aStructure[0].m_nParentElement = 0;
1521 Font aFont;
1522 aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
1523 aFont.SetSize( Size( 0, 12 ) );
1525 GraphicsState aState;
1526 aState.m_aMapMode = m_aMapMode;
1527 aState.m_aFont = aFont;
1528 m_aGraphicsStack.push_front( aState );
1530 oslFileError aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
1531 if( aError != osl_File_E_None )
1533 if( aError == osl_File_E_EXIST )
1535 aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write );
1536 if( aError == osl_File_E_None )
1537 aError = osl_setFileSize( m_aFile, 0 );
1540 if( aError != osl_File_E_None )
1541 return;
1543 m_bOpen = true;
1545 /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1547 m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1548 m_aDigest = rtl_digest_createMD5();
1550 /* the size of the Codec default maximum */
1551 checkEncryptionBufferSize( 0x4000 );
1553 // write header
1554 OStringBuffer aBuffer( 20 );
1555 aBuffer.append( "%PDF-" );
1556 switch( m_aContext.Version )
1558 case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break;
1559 case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break;
1560 case PDFWriter::PDF_A_1:
1561 default:
1562 case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break;
1563 case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break;
1565 // append something binary as comment (suggested in PDF Reference)
1566 aBuffer.append( "\n%äüöß\n" );
1567 if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) )
1569 osl_closeFile( m_aFile );
1570 m_bOpen = false;
1571 return;
1574 // insert outline root
1575 m_aOutline.push_back( PDFOutlineEntry() );
1577 m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1);
1578 if( m_bIsPDF_A1 )
1579 m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour
1581 m_bEmbedStandardFonts = m_aContext.EmbedStandardFonts;
1584 PDFWriterImpl::~PDFWriterImpl()
1586 if( m_aDocDigest )
1587 rtl_digest_destroyMD5( m_aDocDigest );
1588 delete static_cast<VirtualDevice*>(m_pReferenceDevice);
1590 if( m_aCipher )
1591 rtl_cipher_destroyARCFOUR( m_aCipher );
1592 if( m_aDigest )
1593 rtl_digest_destroyMD5( m_aDigest );
1595 rtl_freeMemory( m_pEncryptionBuffer );
1598 void PDFWriterImpl::setDocInfo( const PDFDocInfo& rInfo )
1600 m_aDocInfo.Title = rInfo.Title;
1601 m_aDocInfo.Author = rInfo.Author;
1602 m_aDocInfo.Subject = rInfo.Subject;
1603 m_aDocInfo.Keywords = rInfo.Keywords;
1604 m_aDocInfo.Creator = rInfo.Creator;
1605 m_aDocInfo.Producer = rInfo.Producer;
1607 //build the document id
1608 rtl::OString aInfoValuesOut;
1609 OStringBuffer aID( 1024 );
1610 if( m_aDocInfo.Title.Len() )
1611 appendUnicodeTextString( m_aDocInfo.Title, aID );
1612 if( m_aDocInfo.Author.Len() )
1613 appendUnicodeTextString( m_aDocInfo.Author, aID );
1614 if( m_aDocInfo.Subject.Len() )
1615 appendUnicodeTextString( m_aDocInfo.Subject, aID );
1616 if( m_aDocInfo.Keywords.Len() )
1617 appendUnicodeTextString( m_aDocInfo.Keywords, aID );
1618 if( m_aDocInfo.Creator.Len() )
1619 appendUnicodeTextString( m_aDocInfo.Creator, aID );
1620 if( m_aDocInfo.Producer.Len() )
1621 appendUnicodeTextString( m_aDocInfo.Producer, aID );
1623 TimeValue aTVal, aGMT;
1624 oslDateTime aDT;
1625 osl_getSystemTime( &aGMT );
1626 osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
1627 osl_getDateTimeFromTimeValue( &aTVal, &aDT );
1628 m_aCreationDateString.append( "D:" );
1629 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1630 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1631 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1632 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1633 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1634 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1635 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1636 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1637 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1638 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1639 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1640 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1641 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1642 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1643 //--> i59651, we fill the Metadata date string as well, if PDF/A is requested
1644 if( m_bIsPDF_A1 )
1646 // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1647 // local time zone offset UTC only, whereas Acrobat 8 seems
1648 // to use the localtime notation only
1649 // according to a raccomandation in XMP Specification (Jan 2004, page 75)
1650 // the Acrobat way seems the right approach
1651 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1652 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1653 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1654 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1655 m_aCreationMetaDateString.append( "-" );
1656 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1657 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1658 m_aCreationMetaDateString.append( "-" );
1659 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1660 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1661 m_aCreationMetaDateString.append( "T" );
1662 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1663 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1664 m_aCreationMetaDateString.append( ":" );
1665 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1666 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1667 m_aCreationMetaDateString.append( ":" );
1668 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1669 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1671 sal_uInt32 nDelta = 0;
1672 if( aGMT.Seconds > aTVal.Seconds )
1674 m_aCreationDateString.append( "-" );
1675 nDelta = aGMT.Seconds-aTVal.Seconds;
1676 if( m_bIsPDF_A1 )
1677 m_aCreationMetaDateString.append( "-" );
1679 else if( aGMT.Seconds < aTVal.Seconds )
1681 m_aCreationDateString.append( "+" );
1682 nDelta = aTVal.Seconds-aGMT.Seconds;
1683 if( m_bIsPDF_A1 )
1684 m_aCreationMetaDateString.append( "+" );
1686 else
1688 m_aCreationDateString.append( "Z" );
1689 if( m_bIsPDF_A1 )
1690 m_aCreationMetaDateString.append( "Z" );
1693 if( nDelta )
1695 m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1696 m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1697 m_aCreationDateString.append( "'" );
1698 m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1699 m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1700 if( m_bIsPDF_A1 )
1702 m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1703 m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1704 m_aCreationMetaDateString.append( ":" );
1705 m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1706 m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1709 m_aCreationDateString.append( "'" );
1710 aID.append( m_aCreationDateString.getStr(), m_aCreationDateString.getLength() );
1712 aInfoValuesOut = aID.makeStringAndClear();
1714 DBG_ASSERT( m_aDigest != NULL, "PDFWrite_Impl::setDocInfo: cannot obtain a digest object !" );
1716 m_aDocID.setLength( 0 );
1717 if( m_aDigest )
1719 osl_getSystemTime( &aGMT );
1720 rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, &aGMT, sizeof( aGMT ) );
1721 if( nError == rtl_Digest_E_None )
1722 nError = rtl_digest_updateMD5( m_aDigest, m_aContext.URL.getStr(), m_aContext.URL.getLength()*sizeof(sal_Unicode) );
1723 if( nError == rtl_Digest_E_None )
1724 nError = rtl_digest_updateMD5( m_aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() );
1725 if( nError == rtl_Digest_E_None )
1727 //the binary form of the doc id is needed for encryption stuff
1728 rtl_digest_getMD5( m_aDigest, m_nDocID, 16 );
1729 for( unsigned int i = 0; i < 16; i++ )
1730 appendHex( m_nDocID[i], m_aDocID );
1735 /* i12626 methods */
1737 check if the Unicode string must be encrypted or not, perform the requested task,
1738 append the string as unicode hex, encrypted if needed
1740 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
1742 rOutBuffer.append( "<" );
1743 if( m_aContext.Encrypt )
1745 const sal_Unicode* pStr = rInString.getStr();
1746 sal_Int32 nLen = rInString.getLength();
1747 //prepare a unicode string, encrypt it
1748 if( checkEncryptionBufferSize( nLen*2 ) )
1750 enableStringEncryption( nInObjectNumber );
1751 register sal_uInt8 *pCopy = m_pEncryptionBuffer;
1752 sal_Int32 nChars = 2;
1753 *pCopy++ = 0xFE;
1754 *pCopy++ = 0xFF;
1755 // we need to prepare a byte stream from the unicode string buffer
1756 for( register int i = 0; i < nLen; i++ )
1758 register sal_Unicode aUnChar = pStr[i];
1759 *pCopy++ = (sal_uInt8)( aUnChar >> 8 );
1760 *pCopy++ = (sal_uInt8)( aUnChar & 255 );
1761 nChars += 2;
1763 //encrypt in place
1764 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars );
1765 //now append, hexadecimal (appendHex), the encrypted result
1766 for(register int i = 0; i < nChars; i++)
1767 appendHex( m_pEncryptionBuffer[i], rOutBuffer );
1770 else
1771 appendUnicodeTextString( rInString, rOutBuffer );
1772 rOutBuffer.append( ">" );
1775 inline void PDFWriterImpl::appendLiteralStringEncrypt( rtl::OStringBuffer& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
1777 rOutBuffer.append( "(" );
1778 sal_Int32 nChars = rInString.getLength();
1779 //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
1780 if( m_aContext.Encrypt && checkEncryptionBufferSize( nChars ) )
1782 //encrypt the string in a buffer, then append it
1783 enableStringEncryption( nInObjectNumber );
1784 rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars );
1785 appendLiteralString( (const sal_Char*)m_pEncryptionBuffer, nChars, rOutBuffer );
1787 else
1788 appendLiteralString( rInString.getStr(), nChars , rOutBuffer );
1789 rOutBuffer.append( ")" );
1792 inline void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
1794 rtl::OStringBuffer aBufferString( rInString );
1795 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
1798 inline void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
1800 rtl::OString aBufferString( rtl::OUStringToOString( rInString, RTL_TEXTENCODING_ASCII_US ) );
1801 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
1804 /* end i12626 methods */
1806 void PDFWriterImpl::emitComment( const char* pComment )
1808 OStringBuffer aLine( 64 );
1809 aLine.append( "% " );
1810 aLine.append( (const sal_Char*)pComment );
1811 aLine.append( "\n" );
1812 writeBuffer( aLine.getStr(), aLine.getLength() );
1815 bool PDFWriterImpl::compressStream( SvMemoryStream* pStream )
1817 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
1818 pStream->Seek( STREAM_SEEK_TO_END );
1819 ULONG nEndPos = pStream->Tell();
1820 pStream->Seek( STREAM_SEEK_TO_BEGIN );
1821 ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
1822 SvMemoryStream aStream;
1823 pCodec->BeginCompression();
1824 pCodec->Write( aStream, (const BYTE*)pStream->GetData(), nEndPos );
1825 pCodec->EndCompression();
1826 delete pCodec;
1827 nEndPos = aStream.Tell();
1828 pStream->Seek( STREAM_SEEK_TO_BEGIN );
1829 aStream.Seek( STREAM_SEEK_TO_BEGIN );
1830 pStream->SetStreamSize( nEndPos );
1831 pStream->Write( aStream.GetData(), nEndPos );
1832 return true;
1833 #else
1834 (void)pStream;
1835 return false;
1836 #endif
1839 void PDFWriterImpl::beginCompression()
1841 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
1842 m_pCodec = new ZCodec( 0x4000, 0x4000 );
1843 m_pMemStream = new SvMemoryStream();
1844 m_pCodec->BeginCompression();
1845 #endif
1848 void PDFWriterImpl::endCompression()
1850 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
1851 if( m_pCodec )
1853 m_pCodec->EndCompression();
1854 delete m_pCodec;
1855 m_pCodec = NULL;
1856 sal_uInt64 nLen = m_pMemStream->Tell();
1857 m_pMemStream->Seek( 0 );
1858 writeBuffer( m_pMemStream->GetData(), nLen );
1859 delete m_pMemStream;
1860 m_pMemStream = NULL;
1862 #endif
1865 bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes )
1867 if( ! m_bOpen ) // we are already down the drain
1868 return false;
1870 if( ! nBytes ) // huh ?
1871 return true;
1873 if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
1875 m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END );
1876 m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) );
1877 return true;
1880 sal_uInt64 nWritten;
1881 if( m_pCodec )
1883 m_pCodec->Write( *m_pMemStream, static_cast<const BYTE*>(pBuffer), (ULONG)nBytes );
1884 nWritten = nBytes;
1886 else
1888 sal_Bool buffOK = sal_True;
1889 if( m_bEncryptThisStream )
1891 /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
1892 if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) != sal_False )
1893 rtl_cipher_encodeARCFOUR( m_aCipher,
1894 (sal_uInt8*)pBuffer, static_cast<sal_Size>(nBytes),
1895 m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) );
1898 const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer : pBuffer;
1899 if( m_aDocDigest )
1900 rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) );
1902 if( osl_writeFile( m_aFile,
1903 pWriteBuffer,
1904 nBytes, &nWritten ) != osl_File_E_None )
1905 nWritten = 0;
1907 if( nWritten != nBytes )
1909 osl_closeFile( m_aFile );
1910 m_bOpen = false;
1914 return nWritten == nBytes;
1917 OutputDevice* PDFWriterImpl::getReferenceDevice()
1919 if( ! m_pReferenceDevice )
1921 VirtualDevice* pVDev = new VirtualDevice( 0 );
1923 m_pReferenceDevice = pVDev;
1925 pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 );
1927 pVDev->SetOutputSizePixel( Size( 640, 480 ) );
1928 pVDev->SetMapMode( MAP_MM );
1930 m_pReferenceDevice->mpPDFWriter = this;
1931 m_pReferenceDevice->ImplUpdateFontData( TRUE );
1933 return m_pReferenceDevice;
1936 class ImplPdfBuiltinFontData : public ImplFontData
1938 private:
1939 const PDFWriterImpl::BuiltinFont& mrBuiltin;
1941 public:
1942 enum {PDF_FONT_MAGIC = 0xBDFF0A1C };
1943 ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& );
1944 const PDFWriterImpl::BuiltinFont* GetBuiltinFont() const { return &mrBuiltin; }
1946 virtual ImplFontData* Clone() const { return new ImplPdfBuiltinFontData(*this); }
1947 virtual ImplFontEntry* CreateFontInstance( ImplFontSelectData& ) const;
1948 virtual sal_IntPtr GetFontId() const { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); }
1951 inline const ImplPdfBuiltinFontData* GetPdfFontData( const ImplFontData* pFontData )
1953 const ImplPdfBuiltinFontData* pFD = NULL;
1954 if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) )
1955 pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData );
1956 return pFD;
1959 static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin )
1961 ImplDevFontAttributes aDFA;
1962 aDFA.maName = String::CreateFromAscii( rBuiltin.m_pName );
1963 aDFA.maStyleName = String::CreateFromAscii( rBuiltin.m_pStyleName );
1964 aDFA.meFamily = rBuiltin.m_eFamily;
1965 aDFA.mbSymbolFlag = (rBuiltin.m_eCharSet == RTL_TEXTENCODING_SYMBOL);
1966 aDFA.mePitch = rBuiltin.m_ePitch;
1967 aDFA.meWeight = rBuiltin.m_eWeight;
1968 aDFA.meItalic = rBuiltin.m_eItalic;
1969 aDFA.meWidthType = rBuiltin.m_eWidthType;
1971 aDFA.mbOrientation = true;
1972 aDFA.mbDevice = true;
1973 aDFA.mnQuality = 50000;
1974 aDFA.mbSubsettable = false;
1975 aDFA.mbEmbeddable = false;
1976 return aDFA;
1979 ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin )
1980 : ImplFontData( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ),
1981 mrBuiltin( rBuiltin )
1984 ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
1986 ImplFontEntry* pEntry = new ImplFontEntry( rFSD );
1987 return pEntry;
1990 ImplDevFontList* PDFWriterImpl::filterDevFontList( ImplDevFontList* pFontList )
1992 DBG_ASSERT( m_aSubsets.size() == 0, "Fonts changing during PDF generation, document will be invalid" );
1993 ImplDevFontList* pFiltered = pFontList->Clone( true, true );
1995 // append the PDF builtin fonts
1996 if( !m_bIsPDF_A1 && !m_bEmbedStandardFonts)
1997 for( unsigned int i = 0; i < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); i++ )
1999 ImplFontData* pNewData = new ImplPdfBuiltinFontData( m_aBuiltinFonts[i] );
2000 pFiltered->Add( pNewData );
2002 return pFiltered;
2005 bool PDFWriterImpl::isBuiltinFont( const ImplFontData* pFont ) const
2007 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
2008 return (pFD != NULL);
2011 void PDFWriterImpl::getFontMetric( ImplFontSelectData* pSelect, ImplFontMetricData* pMetric ) const
2013 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2014 if( !pFD )
2015 return;
2016 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2018 pMetric->mnOrientation = sal::static_int_cast<short>(pSelect->mnOrientation);
2019 pMetric->meFamily = pBuiltinFont->m_eFamily;
2020 pMetric->mePitch = pBuiltinFont->m_ePitch;
2021 pMetric->meWeight = pBuiltinFont->m_eWeight;
2022 pMetric->meItalic = pBuiltinFont->m_eItalic;
2023 pMetric->mbSymbolFlag = pFD->IsSymbolFont();
2024 pMetric->mnWidth = pSelect->mnHeight;
2025 pMetric->mnAscent = ( pSelect->mnHeight * +pBuiltinFont->m_nAscent + 500 ) / 1000;
2026 pMetric->mnDescent = ( pSelect->mnHeight * -pBuiltinFont->m_nDescent + 500 ) / 1000;
2027 pMetric->mnIntLeading = 0;
2028 pMetric->mnExtLeading = 0;
2029 pMetric->mnSlant = 0;
2030 pMetric->mbScalableFont = true;
2031 pMetric->mbDevice = true;
2034 // -----------------------------------------------------------------------
2036 namespace vcl {
2038 class PDFSalLayout : public GenericSalLayout
2040 PDFWriterImpl& mrPDFWriterImpl;
2041 const PDFWriterImpl::BuiltinFont& mrBuiltinFont;
2042 bool mbIsSymbolFont;
2043 long mnPixelPerEM;
2044 String maOrigText;
2046 public:
2047 PDFSalLayout( PDFWriterImpl&,
2048 const PDFWriterImpl::BuiltinFont&,
2049 long nPixelPerEM, int nOrientation );
2051 void SetText( const String& rText ) { maOrigText = rText; }
2052 virtual bool LayoutText( ImplLayoutArgs& );
2053 virtual void InitFont() const;
2054 virtual void DrawText( SalGraphics& ) const;
2059 // -----------------------------------------------------------------------
2061 PDFSalLayout::PDFSalLayout( PDFWriterImpl& rPDFWriterImpl,
2062 const PDFWriterImpl::BuiltinFont& rBuiltinFont,
2063 long nPixelPerEM, int nOrientation )
2064 : mrPDFWriterImpl( rPDFWriterImpl ),
2065 mrBuiltinFont( rBuiltinFont ),
2066 mnPixelPerEM( nPixelPerEM )
2068 mbIsSymbolFont = (rBuiltinFont.m_eCharSet == RTL_TEXTENCODING_SYMBOL);
2069 SetOrientation( nOrientation );
2072 // -----------------------------------------------------------------------
2074 bool PDFSalLayout::LayoutText( ImplLayoutArgs& rArgs )
2076 const String aText( rArgs.mpStr+rArgs.mnMinCharPos, sal::static_int_cast<xub_StrLen>(rArgs.mnEndCharPos-rArgs.mnMinCharPos) );
2077 SetText( aText );
2078 SetUnitsPerPixel( 1000 );
2080 rtl_UnicodeToTextConverter aConv = rtl_createTextToUnicodeConverter( RTL_TEXTENCODING_MS_1252 );
2082 Point aNewPos( 0, 0 );
2083 bool bRightToLeft;
2084 for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); )
2086 // TODO: handle unicode surrogates
2087 // on the other hand builtin fonts don't support them anyway
2088 sal_Unicode cChar = rArgs.mpStr[ nCharPos ];
2089 if( bRightToLeft )
2090 cChar = static_cast<sal_Unicode>(GetMirroredChar( cChar ));
2092 if( cChar & 0xff00 )
2094 // some characters can be used by conversion
2095 if( (cChar >= 0xf000) && mbIsSymbolFont )
2096 cChar -= 0xf000;
2097 else
2099 sal_Char aBuf[4];
2100 sal_uInt32 nInfo;
2101 sal_Size nSrcCvtChars;
2103 sal_Size nConv = rtl_convertUnicodeToText( aConv,
2104 NULL,
2105 &cChar, 1,
2106 aBuf, 1,
2107 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR,
2108 &nInfo, &nSrcCvtChars );
2109 // check whether conversion was possible
2110 // else fallback font is needed as the standard fonts
2111 // are handled via WinAnsi encoding
2112 if( nConv > 0 )
2113 cChar = ((sal_Unicode)aBuf[0]) & 0x00ff;
2116 if( cChar & 0xff00 )
2118 cChar = 0; // NotDef glyph
2119 rArgs.NeedFallback( nCharPos, bRightToLeft );
2122 long nGlyphWidth = (long)mrBuiltinFont.m_aWidths[cChar] * mnPixelPerEM;
2123 long nGlyphFlags = (nGlyphWidth > 0) ? 0 : GlyphItem::IS_IN_CLUSTER;
2124 if( bRightToLeft )
2125 nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
2126 // TODO: get kerning from builtin fonts
2127 GlyphItem aGI( nCharPos, cChar, aNewPos, nGlyphFlags, nGlyphWidth );
2128 AppendGlyph( aGI );
2130 aNewPos.X() += nGlyphWidth;
2133 rtl_destroyUnicodeToTextConverter( aConv );
2135 return true;
2138 // -----------------------------------------------------------------------
2140 void PDFSalLayout::InitFont() const
2142 // TODO: recreate font with all its attributes
2145 // -----------------------------------------------------------------------
2147 void PDFSalLayout::DrawText( SalGraphics& ) const
2149 mrPDFWriterImpl.drawLayout( *const_cast<PDFSalLayout*>(this), maOrigText, true );
2152 // -----------------------------------------------------------------------
2154 SalLayout* PDFWriterImpl::GetTextLayout( ImplLayoutArgs& rArgs, ImplFontSelectData* pSelect )
2156 DBG_ASSERT( (pSelect->mpFontData != NULL),
2157 "PDFWriterImpl::GetTextLayout mpFontData is NULL" );
2159 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2160 if( !pFD )
2161 return NULL;
2162 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2164 long nPixelPerEM = pSelect->mnWidth ? pSelect->mnWidth : pSelect->mnHeight;
2165 int nOrientation = pSelect->mnOrientation;
2166 PDFSalLayout* pLayout = new PDFSalLayout( *this, *pBuiltinFont, nPixelPerEM, nOrientation );
2167 pLayout->SetText( rArgs.mpStr );
2168 return pLayout;
2171 sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
2173 if( m_aContext.Encrypt && m_aPages.empty() )
2174 initEncryption();
2176 endPage();
2177 m_nCurrentPage = m_aPages.size();
2178 m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) );
2179 m_aPages.back().m_nPageIndex = m_nCurrentPage;
2180 m_aPages.back().beginStream();
2182 // setup global graphics state
2183 // linewidth is "1 pixel" by default
2184 OStringBuffer aBuf( 16 );
2185 appendDouble( 72.0/double(getReferenceDevice()->ImplGetDPIX()), aBuf );
2186 aBuf.append( " w\n" );
2187 writeBuffer( aBuf.getStr(), aBuf.getLength() );
2189 return m_nCurrentPage;
2192 void PDFWriterImpl::endPage()
2194 if( m_aPages.begin() != m_aPages.end() )
2196 // close eventual MC sequence
2197 endStructureElementMCSeq();
2199 // sanity check
2200 if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2202 DBG_ERROR( "redirection across pages !!!" );
2203 m_aOutputStreams.clear(); // leak !
2204 m_aMapMode.SetOrigin( Point() );
2207 m_aGraphicsStack.clear();
2208 m_aGraphicsStack.push_back( GraphicsState() );
2210 // this should pop the PDF graphics stack if necessary
2211 updateGraphicsState();
2213 m_aPages.back().endStream();
2215 // reset the default font
2216 Font aFont;
2217 aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
2218 aFont.SetSize( Size( 0, 12 ) );
2220 m_aCurrentPDFState = m_aGraphicsStack.front();
2221 m_aGraphicsStack.front().m_aFont = aFont;
2223 for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin();
2224 it != m_aBitmaps.end(); ++it )
2226 if( ! it->m_aBitmap.IsEmpty() )
2228 writeBitmapObject( *it );
2229 it->m_aBitmap = BitmapEx();
2232 for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg )
2234 if( jpeg->m_pStream )
2236 writeJPG( *jpeg );
2237 delete jpeg->m_pStream;
2238 jpeg->m_pStream = NULL;
2239 jpeg->m_aMask = Bitmap();
2242 for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin();
2243 t != m_aTransparentObjects.end(); ++t )
2245 if( t->m_pContentStream )
2247 writeTransparentObject( *t );
2248 delete t->m_pContentStream;
2249 t->m_pContentStream = NULL;
2255 sal_Int32 PDFWriterImpl::createObject()
2257 m_aObjects.push_back( ~0U );
2258 return m_aObjects.size();
2261 bool PDFWriterImpl::updateObject( sal_Int32 n )
2263 if( ! m_bOpen )
2264 return false;
2266 sal_uInt64 nOffset = ~0U;
2267 oslFileError aError = osl_getFilePos( m_aFile, &nOffset );
2268 DBG_ASSERT( aError == osl_File_E_None, "could not register object" );
2269 if( aError != osl_File_E_None )
2271 osl_closeFile( m_aFile );
2272 m_bOpen = false;
2274 m_aObjects[ n-1 ] = nOffset;
2275 return aError == osl_File_E_None;
2278 #define CHECK_RETURN( x ) if( !(x) ) return 0
2280 sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject )
2282 if( nObject > 0 )
2284 OStringBuffer aLine( 1024 );
2286 aLine.append( nObject );
2287 aLine.append( " 0 obj\n"
2288 "<</Nums[\n" );
2289 sal_Int32 nTreeItems = m_aStructParentTree.size();
2290 for( sal_Int32 n = 0; n < nTreeItems; n++ )
2292 aLine.append( n );
2293 aLine.append( ' ' );
2294 aLine.append( m_aStructParentTree[n] );
2295 aLine.append( "\n" );
2297 aLine.append( "]>>\nendobj\n\n" );
2298 CHECK_RETURN( updateObject( nObject ) );
2299 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2301 return nObject;
2304 const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr )
2306 static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings;
2307 // fill maps once
2308 if( aAttributeStrings.empty() )
2310 aAttributeStrings[ PDFWriter::Placement ] = "Placement";
2311 aAttributeStrings[ PDFWriter::WritingMode ] = "WritingMode";
2312 aAttributeStrings[ PDFWriter::SpaceBefore ] = "SpaceBefore";
2313 aAttributeStrings[ PDFWriter::SpaceAfter ] = "SpaceAfter";
2314 aAttributeStrings[ PDFWriter::StartIndent ] = "StartIndent";
2315 aAttributeStrings[ PDFWriter::EndIndent ] = "EndIndent";
2316 aAttributeStrings[ PDFWriter::TextIndent ] = "TextIndent";
2317 aAttributeStrings[ PDFWriter::TextAlign ] = "TextAlign";
2318 aAttributeStrings[ PDFWriter::Width ] = "Width";
2319 aAttributeStrings[ PDFWriter::Height ] = "Height";
2320 aAttributeStrings[ PDFWriter::BlockAlign ] = "BlockAlign";
2321 aAttributeStrings[ PDFWriter::InlineAlign ] = "InlineAlign";
2322 aAttributeStrings[ PDFWriter::LineHeight ] = "LineHeight";
2323 aAttributeStrings[ PDFWriter::BaselineShift ] = "BaselineShift";
2324 aAttributeStrings[ PDFWriter::TextDecorationType ] = "TextDecorationType";
2325 aAttributeStrings[ PDFWriter::ListNumbering ] = "ListNumbering";
2326 aAttributeStrings[ PDFWriter::RowSpan ] = "RowSpan";
2327 aAttributeStrings[ PDFWriter::ColSpan ] = "ColSpan";
2328 aAttributeStrings[ PDFWriter::LinkAnnotation ] = "LinkAnnotation";
2331 std::map< PDFWriter::StructAttribute, const char* >::const_iterator it =
2332 aAttributeStrings.find( eAttr );
2334 #if OSL_DEBUG_LEVEL > 1
2335 if( it == aAttributeStrings.end() )
2336 fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr );
2337 #endif
2339 return it != aAttributeStrings.end() ? it->second : "";
2342 const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal )
2344 static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings;
2346 if( aValueStrings.empty() )
2348 aValueStrings[ PDFWriter::NONE ] = "None";
2349 aValueStrings[ PDFWriter::Block ] = "Block";
2350 aValueStrings[ PDFWriter::Inline ] = "Inline";
2351 aValueStrings[ PDFWriter::Before ] = "Before";
2352 aValueStrings[ PDFWriter::After ] = "After";
2353 aValueStrings[ PDFWriter::Start ] = "Start";
2354 aValueStrings[ PDFWriter::End ] = "End";
2355 aValueStrings[ PDFWriter::LrTb ] = "LrTb";
2356 aValueStrings[ PDFWriter::RlTb ] = "RlTb";
2357 aValueStrings[ PDFWriter::TbRl ] = "TbRl";
2358 aValueStrings[ PDFWriter::Center ] = "Center";
2359 aValueStrings[ PDFWriter::Justify ] = "Justify";
2360 aValueStrings[ PDFWriter::Auto ] = "Auto";
2361 aValueStrings[ PDFWriter::Middle ] = "Middle";
2362 aValueStrings[ PDFWriter::Normal ] = "Normal";
2363 aValueStrings[ PDFWriter::Underline ] = "Underline";
2364 aValueStrings[ PDFWriter::Overline ] = "Overline";
2365 aValueStrings[ PDFWriter::LineThrough ] = "LineThrough";
2366 aValueStrings[ PDFWriter::Disc ] = "Disc";
2367 aValueStrings[ PDFWriter::Circle ] = "Circle";
2368 aValueStrings[ PDFWriter::Square ] = "Square";
2369 aValueStrings[ PDFWriter::Decimal ] = "Decimal";
2370 aValueStrings[ PDFWriter::UpperRoman ] = "UpperRoman";
2371 aValueStrings[ PDFWriter::LowerRoman ] = "LowerRoman";
2372 aValueStrings[ PDFWriter::UpperAlpha ] = "UpperAlpha";
2373 aValueStrings[ PDFWriter::LowerAlpha ] = "LowerAlpha";
2376 std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it =
2377 aValueStrings.find( eVal );
2379 #if OSL_DEBUG_LEVEL > 1
2380 if( it == aValueStrings.end() )
2381 fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal );
2382 #endif
2384 return it != aValueStrings.end() ? it->second : "";
2387 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt )
2389 o_rLine.append( "/" );
2390 o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) );
2392 if( i_rVal.eValue != PDFWriter::Invalid )
2394 o_rLine.append( "/" );
2395 o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) );
2397 else
2399 // numerical value
2400 o_rLine.append( " " );
2401 if( i_bIsFixedInt )
2402 appendFixedInt( i_rVal.nValue, o_rLine );
2403 else
2404 o_rLine.append( i_rVal.nValue );
2406 o_rLine.append( "\n" );
2409 OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
2411 // create layout, list and table attribute sets
2412 OStringBuffer aLayout(256), aList(64), aTable(64);
2413 for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin();
2414 it != i_rEle.m_aAttributes.end(); ++it )
2416 if( it->first == PDFWriter::ListNumbering )
2417 appendStructureAttributeLine( it->first, it->second, aList, true );
2418 else if( it->first == PDFWriter::RowSpan ||
2419 it->first == PDFWriter::ColSpan )
2420 appendStructureAttributeLine( it->first, it->second, aTable, false );
2421 else if( it->first == PDFWriter::LinkAnnotation )
2423 sal_Int32 nLink = it->second.nValue;
2424 std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
2425 m_aLinkPropertyMap.find( nLink );
2426 if( link_it != m_aLinkPropertyMap.end() )
2427 nLink = link_it->second;
2428 if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() )
2430 // update struct parent of link
2431 OStringBuffer aStructParentEntry( 32 );
2432 aStructParentEntry.append( i_rEle.m_nObject );
2433 aStructParentEntry.append( " 0 R" );
2434 m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() );
2435 m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1;
2437 sal_Int32 nRefObject = createObject();
2438 OStringBuffer aRef( 256 );
2439 aRef.append( nRefObject );
2440 aRef.append( " 0 obj\n"
2441 "<</Type/OBJR/Obj " );
2442 aRef.append( m_aLinks[ nLink ].m_nObject );
2443 aRef.append( " 0 R>>\n"
2444 "endobj\n\n"
2446 updateObject( nRefObject );
2447 writeBuffer( aRef.getStr(), aRef.getLength() );
2449 i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) );
2451 else
2453 DBG_ERROR( "unresolved link id for Link structure" );
2454 #if OSL_DEBUG_LEVEL > 1
2455 fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink );
2457 OStringBuffer aLine( "unresolved link id " );
2458 aLine.append( nLink );
2459 aLine.append( " for Link structure" );
2460 emitComment( aLine.getStr() );
2462 #endif
2465 else
2466 appendStructureAttributeLine( it->first, it->second, aLayout, true );
2468 if( ! i_rEle.m_aBBox.IsEmpty() )
2470 aLayout.append( "/BBox[" );
2471 appendFixedInt( i_rEle.m_aBBox.Left(), aLayout );
2472 aLayout.append( " " );
2473 appendFixedInt( i_rEle.m_aBBox.Top(), aLayout );
2474 aLayout.append( " " );
2475 appendFixedInt( i_rEle.m_aBBox.Right(), aLayout );
2476 aLayout.append( " " );
2477 appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout );
2478 aLayout.append( "]\n" );
2481 std::vector< sal_Int32 > aAttribObjects;
2482 if( aLayout.getLength() )
2484 aAttribObjects.push_back( createObject() );
2485 updateObject( aAttribObjects.back() );
2486 OStringBuffer aObj( 64 );
2487 aObj.append( aAttribObjects.back() );
2488 aObj.append( " 0 obj\n"
2489 "<</O/Layout\n" );
2490 aLayout.append( ">>\nendobj\n\n" );
2491 writeBuffer( aObj.getStr(), aObj.getLength() );
2492 writeBuffer( aLayout.getStr(), aLayout.getLength() );
2494 if( aList.getLength() )
2496 aAttribObjects.push_back( createObject() );
2497 updateObject( aAttribObjects.back() );
2498 OStringBuffer aObj( 64 );
2499 aObj.append( aAttribObjects.back() );
2500 aObj.append( " 0 obj\n"
2501 "<</O/List\n" );
2502 aList.append( ">>\nendobj\n\n" );
2503 writeBuffer( aObj.getStr(), aObj.getLength() );
2504 writeBuffer( aList.getStr(), aList.getLength() );
2506 if( aTable.getLength() )
2508 aAttribObjects.push_back( createObject() );
2509 updateObject( aAttribObjects.back() );
2510 OStringBuffer aObj( 64 );
2511 aObj.append( aAttribObjects.back() );
2512 aObj.append( " 0 obj\n"
2513 "<</O/Table\n" );
2514 aTable.append( ">>\nendobj\n\n" );
2515 writeBuffer( aObj.getStr(), aObj.getLength() );
2516 writeBuffer( aTable.getStr(), aTable.getLength() );
2519 OStringBuffer aRet( 64 );
2520 if( aAttribObjects.size() > 1 )
2521 aRet.append( " [" );
2522 for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin();
2523 at_it != aAttribObjects.end(); ++at_it )
2525 aRet.append( " " );
2526 aRet.append( *at_it );
2527 aRet.append( " 0 R" );
2529 if( aAttribObjects.size() > 1 )
2530 aRet.append( " ]" );
2531 return aRet.makeStringAndClear();
2534 sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
2537 // do not emit NonStruct and its children
2538 rEle.m_eType == PDFWriter::NonStructElement &&
2539 rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root
2541 return 0;
2543 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
2545 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
2547 PDFStructureElement& rChild = m_aStructure[ *it ];
2548 if( rChild.m_eType != PDFWriter::NonStructElement )
2550 if( rChild.m_nParentElement == rEle.m_nOwnElement )
2551 emitStructure( rChild );
2552 else
2554 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure element" );
2555 #if OSL_DEBUG_LEVEL > 1
2556 fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
2557 #endif
2561 else
2563 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
2564 #if OSL_DEBUG_LEVEL > 1
2565 fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
2566 #endif
2570 OStringBuffer aLine( 512 );
2571 aLine.append( rEle.m_nObject );
2572 aLine.append( " 0 obj\n"
2573 "<</Type" );
2574 sal_Int32 nParentTree = -1;
2575 if( rEle.m_nOwnElement == rEle.m_nParentElement )
2577 nParentTree = createObject();
2578 CHECK_RETURN( nParentTree );
2579 aLine.append( "/StructTreeRoot\n" );
2580 aLine.append( "/ParentTree " );
2581 aLine.append( nParentTree );
2582 aLine.append( " 0 R\n" );
2583 if( ! m_aRoleMap.empty() )
2585 aLine.append( "/RoleMap<<" );
2586 for( std::hash_map<OString,OString,OStringHash>::const_iterator
2587 it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it )
2589 aLine.append( '/' );
2590 aLine.append(it->first);
2591 aLine.append( '/' );
2592 aLine.append( it->second );
2593 aLine.append( '\n' );
2595 aLine.append( ">>\n" );
2598 else
2600 aLine.append( "/StructElem\n"
2601 "/S/" );
2602 if( rEle.m_aAlias.getLength() > 0 )
2603 aLine.append( rEle.m_aAlias );
2604 else
2605 aLine.append( getStructureTag( rEle.m_eType ) );
2606 aLine.append( "\n"
2607 "/P " );
2608 aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject );
2609 aLine.append( " 0 R\n"
2610 "/Pg " );
2611 aLine.append( rEle.m_nFirstPageObject );
2612 aLine.append( " 0 R\n" );
2613 if( rEle.m_aActualText.getLength() )
2615 aLine.append( "/ActualText" );
2616 appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine );
2617 aLine.append( "\n" );
2619 if( rEle.m_aAltText.getLength() )
2621 aLine.append( "/Alt" );
2622 appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine );
2623 aLine.append( "\n" );
2626 if( ! rEle.m_aBBox.IsEmpty() || rEle.m_aAttributes.size() )
2628 OString aAttribs = emitStructureAttributes( rEle );
2629 if( aAttribs.getLength() )
2631 aLine.append( "/A" );
2632 aLine.append( aAttribs );
2633 aLine.append( "\n" );
2636 if( rEle.m_aLocale.Language.getLength() > 0 )
2638 OUStringBuffer aLocBuf( 16 );
2639 aLocBuf.append( rEle.m_aLocale.Language.toAsciiLowerCase() );
2640 if( rEle.m_aLocale.Country.getLength() > 0 )
2642 aLocBuf.append( sal_Unicode('-') );
2643 aLocBuf.append( rEle.m_aLocale.Country );
2645 aLine.append( "/Lang" );
2646 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine );
2647 aLine.append( "\n" );
2649 if( ! rEle.m_aKids.empty() )
2651 unsigned int i = 0;
2652 aLine.append( "/K[" );
2653 for( std::list< PDFStructureElementKid >::const_iterator it =
2654 rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ )
2656 if( it->nMCID == -1 )
2658 aLine.append( it->nObject );
2659 aLine.append( " 0 R" );
2660 aLine.append( ( (i & 15) == 15 ) ? "\n" : " " );
2662 else
2664 if( it->nObject == rEle.m_nFirstPageObject )
2666 aLine.append( it->nMCID );
2667 aLine.append( " " );
2669 else
2671 aLine.append( "<</Type/MCR/Pg " );
2672 aLine.append( it->nObject );
2673 aLine.append( " 0 R /MCID " );
2674 aLine.append( it->nMCID );
2675 aLine.append( ">>\n" );
2679 aLine.append( "]\n" );
2681 aLine.append( ">>\nendobj\n\n" );
2683 CHECK_RETURN( updateObject( rEle.m_nObject ) );
2684 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2686 CHECK_RETURN( emitStructParentTree( nParentTree ) );
2688 return rEle.m_nObject;
2691 bool PDFWriterImpl::emitGradients()
2693 for( std::list<GradientEmit>::iterator it = m_aGradients.begin();
2694 it != m_aGradients.end(); ++it )
2696 CHECK_RETURN( writeGradientFunction( *it ) );
2698 return true;
2701 bool PDFWriterImpl::emitTilings()
2703 OStringBuffer aTilingObj( 1024 );
2705 for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it )
2707 DBG_ASSERT( it->m_pTilingStream, "tiling without stream" );
2708 if( ! it->m_pTilingStream )
2709 continue;
2711 aTilingObj.setLength( 0 );
2713 #if OSL_DEBUG_LEVEL > 1
2715 OStringBuffer aLine( "PDFWriterImpl::emitTilings" );
2716 emitComment( aLine.getStr() );
2718 #endif
2720 sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left();
2721 sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top();
2722 sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth();
2723 sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight();
2724 if( it->m_aCellSize.Width() == 0 )
2725 it->m_aCellSize.Width() = nW;
2726 if( it->m_aCellSize.Height() == 0 )
2727 it->m_aCellSize.Height() = nH;
2729 bool bDeflate = compressStream( it->m_pTilingStream );
2730 it->m_pTilingStream->Seek( STREAM_SEEK_TO_END );
2731 sal_Size nTilingStreamSize = it->m_pTilingStream->Tell();
2732 it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN );
2734 // write pattern object
2735 aTilingObj.append( it->m_nObject );
2736 aTilingObj.append( " 0 obj\n" );
2737 aTilingObj.append( "<</Type/Pattern/PatternType 1\n"
2738 "/PaintType 1\n"
2739 "/TilingType 2\n"
2740 "/BBox[" );
2741 appendFixedInt( nX, aTilingObj );
2742 aTilingObj.append( ' ' );
2743 appendFixedInt( nY, aTilingObj );
2744 aTilingObj.append( ' ' );
2745 appendFixedInt( nX+nW, aTilingObj );
2746 aTilingObj.append( ' ' );
2747 appendFixedInt( nY+nH, aTilingObj );
2748 aTilingObj.append( "]\n"
2749 "/XStep " );
2750 appendFixedInt( it->m_aCellSize.Width(), aTilingObj );
2751 aTilingObj.append( "\n"
2752 "/YStep " );
2753 appendFixedInt( it->m_aCellSize.Height(), aTilingObj );
2754 aTilingObj.append( "\n" );
2755 if( it->m_aTransform.matrix[0] != 1.0 ||
2756 it->m_aTransform.matrix[1] != 0.0 ||
2757 it->m_aTransform.matrix[3] != 0.0 ||
2758 it->m_aTransform.matrix[4] != 1.0 ||
2759 it->m_aTransform.matrix[2] != 0.0 ||
2760 it->m_aTransform.matrix[5] != 0.0 )
2762 aTilingObj.append( "/Matrix [" );
2763 // TODO: scaling, mirroring on y, etc
2764 appendDouble( it->m_aTransform.matrix[0], aTilingObj );
2765 aTilingObj.append( ' ' );
2766 appendDouble( it->m_aTransform.matrix[1], aTilingObj );
2767 aTilingObj.append( ' ' );
2768 appendDouble( it->m_aTransform.matrix[3], aTilingObj );
2769 aTilingObj.append( ' ' );
2770 appendDouble( it->m_aTransform.matrix[4], aTilingObj );
2771 aTilingObj.append( ' ' );
2772 appendDouble( it->m_aTransform.matrix[2], aTilingObj );
2773 aTilingObj.append( ' ' );
2774 appendDouble( it->m_aTransform.matrix[5], aTilingObj );
2775 aTilingObj.append( "]\n" );
2777 aTilingObj.append( "/Resources" );
2778 it->m_aResources.append( aTilingObj, getFontDictObject() );
2779 if( bDeflate )
2780 aTilingObj.append( "/Filter/FlateDecode" );
2781 aTilingObj.append( "/Length " );
2782 aTilingObj.append( (sal_Int32)nTilingStreamSize );
2783 aTilingObj.append( ">>\nstream\n" );
2784 CHECK_RETURN( updateObject( it->m_nObject ) );
2785 CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
2786 checkAndEnableStreamEncryption( it->m_nObject );
2787 nTilingStreamSize = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize );
2788 delete it->m_pTilingStream;
2789 it->m_pTilingStream = NULL;
2790 if( nTilingStreamSize == 0 )
2791 return false;
2792 disableStreamEncryption();
2793 aTilingObj.setLength( 0 );
2794 aTilingObj.append( "\nendstream\nendobj\n\n" );
2795 CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
2797 return true;
2800 sal_Int32 PDFWriterImpl::emitBuiltinFont( const ImplFontData* pFont, sal_Int32 nFontObject )
2802 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
2803 if( !pFD )
2804 return 0;
2805 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2807 OStringBuffer aLine( 1024 );
2809 if( nFontObject <= 0 )
2810 nFontObject = createObject();
2811 CHECK_RETURN( updateObject( nFontObject ) );
2812 aLine.append( nFontObject );
2813 aLine.append( " 0 obj\n"
2814 "<</Type/Font/Subtype/Type1/BaseFont/" );
2815 appendName( pBuiltinFont->m_pPSName, aLine );
2816 aLine.append( "\n" );
2817 if( pBuiltinFont->m_eCharSet != RTL_TEXTENCODING_SYMBOL )
2818 aLine.append( "/Encoding/WinAnsiEncoding\n" );
2819 aLine.append( ">>\nendobj\n\n" );
2820 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2821 return nFontObject;
2824 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFontData* pFont, EmbedFont& rEmbed )
2826 std::map< sal_Int32, sal_Int32 > aRet;
2827 if( isBuiltinFont( pFont ) )
2829 aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont );
2830 return aRet;
2833 sal_Int32 nFontObject = 0;
2834 sal_Int32 nStreamObject = 0;
2835 sal_Int32 nFontDescriptor = 0;
2837 // prepare font encoding
2838 const Ucs2SIntMap* pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pFont, NULL );
2839 sal_Int32 nToUnicodeStream = 0;
2840 sal_uInt8 nEncoding[256];
2841 sal_Ucs nEncodedCodes[256];
2842 if( pEncoding )
2844 memset( nEncodedCodes, 0, sizeof(nEncodedCodes) );
2845 memset( nEncoding, 0, sizeof(nEncoding) );
2846 for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it )
2848 if( it->second != -1 )
2850 sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff);
2851 nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode );
2852 nEncodedCodes[ nCode ] = it->first;
2857 FontSubsetInfo aInfo;
2858 sal_Int32 pWidths[256];
2859 const unsigned char* pFontData = NULL;
2860 long nFontLen = 0;
2861 sal_Int32 nLength1, nLength2;
2862 if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL )
2864 if( aInfo.m_nFontType != SAL_FONTSUBSETINFO_TYPE_TYPE1 )
2865 goto streamend;
2866 // see whether it is pfb or pfa; if it is a pfb, fill ranges
2867 // of 6 bytes that are not part of the font program
2868 std::list< int > aSections;
2869 std::list< int >::const_iterator it;
2870 int nIndex = 0;
2871 while( pFontData[nIndex] == 0x80 && nIndex < nFontLen-1 )
2873 aSections.push_back( nIndex );
2874 if( pFontData[nIndex+1] == 0x03 )
2875 break;
2876 sal_Int32 nBytes =
2877 ((sal_Int32)pFontData[nIndex+2]) |
2878 ((sal_Int32)pFontData[nIndex+3]) << 8 |
2879 ((sal_Int32)pFontData[nIndex+4]) << 16 |
2880 ((sal_Int32)pFontData[nIndex+5]) << 24;
2881 nIndex += nBytes+6;
2884 // search for eexec
2885 nIndex = 0;
2886 int nEndAsciiIndex;
2887 int nBeginBinaryIndex;
2888 int nEndBinaryIndex;
2891 while( nIndex < nFontLen-4 &&
2892 ( pFontData[nIndex] != 'e' ||
2893 pFontData[nIndex+1] != 'e' ||
2894 pFontData[nIndex+2] != 'x' ||
2895 pFontData[nIndex+3] != 'e' ||
2896 pFontData[nIndex+4] != 'c'
2899 nIndex++;
2900 // check whether we are in a excluded section
2901 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
2903 } while( it != aSections.end() && nIndex < nFontLen-4 );
2904 // this should end the ascii part
2905 if( nIndex > nFontLen-5 )
2906 goto streamend;
2908 nEndAsciiIndex = nIndex+4;
2909 // now count backwards until we can account for 512 '0'
2910 // which is the endmarker of the (hopefully) binary data
2911 // do not count the pfb header sections
2912 int nFound = 0;
2913 nIndex = nFontLen-1;
2914 while( nIndex > 0 && nFound < 512 )
2916 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
2918 if( it == aSections.end() )
2920 // inside the 512 '0' block there may only be whitespace
2921 // according to T1 spec; probably it would be to simple
2922 // if all fonts complied
2923 if( pFontData[nIndex] == '0' )
2924 nFound++;
2925 else if( nFound > 0 &&
2926 pFontData[nIndex] != '\r' &&
2927 pFontData[nIndex] != '\t' &&
2928 pFontData[nIndex] != '\n' &&
2929 pFontData[nIndex] != ' ' )
2930 break;
2932 nIndex--;
2935 if( nIndex < 1 || nIndex <= nEndAsciiIndex )
2936 goto streamend;
2937 // there may be whitespace to ignore before the 512 '0'
2938 while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' )
2940 nIndex--;
2941 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
2943 if( it != aSections.end() )
2945 nIndex = (*it)-1;
2946 break; // this is surely a binary boundary, in ascii case it wouldn't matter
2949 nEndBinaryIndex = nIndex;
2951 // and count forward again to the point where we have nFound '0'
2952 // to get the corect value for nLength3
2953 sal_Int32 nLength3 = 0;
2954 sal_Int32 nL3Index = nIndex;
2955 while( nFound && nL3Index < nFontLen )
2957 for( it = aSections.begin(); it != aSections.end() && (nL3Index < *it || nL3Index > ((*it) + 5) ); ++it )
2959 if( it == aSections.end() )
2961 // inside the 512 '0' block there may only be whitespace
2962 // according to T1 spec; probably it would be to simple
2963 // if all fonts complied
2964 if( pFontData[nL3Index] == '0' )
2965 nFound--;
2966 nLength3++;
2968 nL3Index++;
2971 // search for beginning of binary section
2972 nBeginBinaryIndex = nEndAsciiIndex;
2975 nBeginBinaryIndex++;
2976 for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it )
2978 } while( nBeginBinaryIndex < nEndBinaryIndex &&
2979 ( pFontData[nBeginBinaryIndex] == '\r' ||
2980 pFontData[nBeginBinaryIndex] == '\n' ||
2981 it != aSections.end() ) );
2983 // it seems to be vital to copy the exact whitespace between binary data
2984 // and eexec, else a invalid font results. so make nEndAsciiIndex
2985 // always immediate in front of nBeginBinaryIndex
2986 nEndAsciiIndex = nBeginBinaryIndex-1;
2987 for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it )
2989 if( it != aSections.end() )
2990 nEndAsciiIndex = (*it)-1;
2992 nLength1 = nEndAsciiIndex+1; // including the last character
2993 for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it )
2994 nLength1 -= 6; // decrease by pfb section size
2996 // if the first four bytes are all ascii hex characters, then binary data
2997 // has to be converted to real binary data
2998 for( nIndex = 0; nIndex < 4 &&
2999 ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) ||
3000 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) ||
3001 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' )
3002 ); ++nIndex )
3004 bool bConvertHexData = true;
3005 if( nIndex < 4 )
3007 bConvertHexData = false;
3008 nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte
3009 for( it = aSections.begin(); it != aSections.end(); ++it )
3010 if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex )
3011 nLength2 -= 6;
3013 else
3015 // count the hex ascii characters to get nLength2
3016 nLength2 = 0;
3017 int nNextSectionIndex = 0;
3018 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3020 if( it != aSections.end() )
3021 nNextSectionIndex = *it;
3022 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3024 if( nIndex == nNextSectionIndex )
3026 nIndex += 6;
3027 ++it;
3028 nNextSectionIndex = (it == aSections.end() ? 0 : *it );
3030 if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) ||
3031 ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) ||
3032 ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) )
3033 nLength2++;
3035 DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" );
3036 nLength2 /= 2;
3039 // now we can actually write the font stream !
3040 #if OSL_DEBUG_LEVEL > 1
3042 OStringBuffer aLine( " PDFWriterImpl::emitEmbeddedFont" );
3043 emitComment( aLine.getStr() );
3045 #endif
3046 OStringBuffer aLine( 512 );
3047 nStreamObject = createObject();
3048 if( !updateObject(nStreamObject))
3049 goto streamend;
3050 sal_Int32 nStreamLengthObject = createObject();
3051 aLine.append( nStreamObject );
3052 aLine.append( " 0 obj\n"
3053 "<</Length " );
3054 aLine.append( nStreamLengthObject );
3055 aLine.append( " 0 R"
3056 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3057 "/Filter/FlateDecode"
3058 #endif
3059 "/Length1 " );
3060 aLine.append( nLength1 );
3061 aLine.append( " /Length2 " );
3062 aLine.append( nLength2 );
3063 aLine.append( " /Length3 ");
3064 aLine.append( nLength3 );
3065 aLine.append( ">>\n"
3066 "stream\n" );
3067 if( !writeBuffer( aLine.getStr(), aLine.getLength() ) )
3068 goto streamend;
3070 sal_uInt64 nBeginStreamPos = 0;
3071 osl_getFilePos( m_aFile, &nBeginStreamPos );
3073 beginCompression();
3074 checkAndEnableStreamEncryption( nStreamObject );
3076 // write ascii section
3077 if( aSections.begin() == aSections.end() )
3079 if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) )
3081 endCompression();
3082 disableStreamEncryption();
3083 goto streamend;
3086 else
3088 // first section always starts at 0
3089 it = aSections.begin();
3090 nIndex = (*it)+6;
3091 ++it;
3092 while( *it < nEndAsciiIndex )
3094 if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) )
3096 endCompression();
3097 disableStreamEncryption();
3098 goto streamend;
3100 nIndex = (*it)+6;
3101 ++it;
3103 // write partial last section
3104 if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) )
3106 endCompression();
3107 disableStreamEncryption();
3108 goto streamend;
3112 // write binary section
3113 if( ! bConvertHexData )
3115 if( aSections.begin() == aSections.end() )
3117 if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) )
3119 endCompression();
3120 disableStreamEncryption();
3121 goto streamend;
3124 else
3126 for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it )
3128 // write first partial section
3129 if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) )
3131 endCompression();
3132 disableStreamEncryption();
3133 goto streamend;
3135 // write following sections
3136 while( it != aSections.end() )
3138 nIndex = (*it)+6;
3139 ++it;
3140 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3142 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3143 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3145 endCompression();
3146 disableStreamEncryption();
3147 goto streamend;
3153 else
3155 unsigned char* pWriteBuffer = (unsigned char*)rtl_allocateMemory( nLength2 );
3156 memset( pWriteBuffer, 0, nLength2 );
3157 int nWriteIndex = 0;
3159 int nNextSectionIndex = 0;
3160 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3162 if( it != aSections.end() )
3163 nNextSectionIndex = *it;
3164 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3166 if( nIndex == nNextSectionIndex )
3168 nIndex += 6;
3169 ++it;
3170 nNextSectionIndex = (it == aSections.end() ? nFontLen : *it );
3172 unsigned char cNibble = 0x80;
3173 if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' )
3174 cNibble = pFontData[nIndex] - '0';
3175 else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' )
3176 cNibble = pFontData[nIndex] - 'a' + 10;
3177 else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' )
3178 cNibble = pFontData[nIndex] - 'A' + 10;
3179 if( cNibble != 0x80 )
3181 if( !(nWriteIndex & 1 ) )
3182 cNibble <<= 4;
3183 pWriteBuffer[ nWriteIndex/2 ] |= cNibble;
3184 nWriteIndex++;
3187 if( ! writeBuffer( pWriteBuffer, nLength2 ) )
3189 endCompression();
3190 disableStreamEncryption();
3191 goto streamend;
3193 rtl_freeMemory( pWriteBuffer );
3195 if( aSections.empty() )
3197 if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) )
3199 endCompression();
3200 disableStreamEncryption();
3201 goto streamend;
3204 else
3206 // write rest of this section
3207 if( nIndex < nNextSectionIndex )
3209 if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) )
3211 endCompression();
3212 disableStreamEncryption();
3213 goto streamend;
3216 // write following sections
3217 while( it != aSections.end() )
3219 nIndex = (*it)+6;
3220 ++it;
3221 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3223 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3224 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3226 endCompression();
3227 disableStreamEncryption();
3228 goto streamend;
3234 endCompression();
3235 disableStreamEncryption();
3238 sal_uInt64 nEndStreamPos = 0;
3239 osl_getFilePos( m_aFile, &nEndStreamPos );
3241 // and finally close the stream
3242 aLine.setLength( 0 );
3243 aLine.append( "\nendstream\nendobj\n\n" );
3244 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3245 goto streamend;
3247 // write stream length object
3248 aLine.setLength( 0 );
3249 if( ! updateObject( nStreamLengthObject ) )
3250 goto streamend;
3251 aLine.append( nStreamLengthObject );
3252 aLine.append( " 0 obj\n" );
3253 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) );
3254 aLine.append( "\nendobj\n\n" );
3255 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3256 goto streamend;
3258 else
3260 rtl::OStringBuffer aErrorComment( 256 );
3261 aErrorComment.append( "GetEmbedFontData failed for font \"" );
3262 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3263 aErrorComment.append( '\"' );
3264 if( pFont->GetSlant() == ITALIC_NORMAL )
3265 aErrorComment.append( " italic" );
3266 else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3267 aErrorComment.append( " oblique" );
3268 aErrorComment.append( " weight=" );
3269 aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3270 emitComment( aErrorComment.getStr() );
3273 if( nStreamObject )
3274 // write font descriptor
3275 nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject );
3277 if( nFontDescriptor )
3279 if( pEncoding )
3280 nToUnicodeStream = createToUnicodeCMap( nEncoding, nEncodedCodes, sizeof(nEncoding)/sizeof(nEncoding[0]) );
3282 // write font object
3283 sal_Int32 nObject = createObject();
3284 if( ! updateObject( nObject ) )
3285 goto streamend;
3287 OStringBuffer aLine( 1024 );
3288 aLine.append( nObject );
3289 aLine.append( " 0 obj\n"
3290 "<</Type/Font/Subtype/Type1/BaseFont/" );
3291 appendName( aInfo.m_aPSName, aLine );
3292 aLine.append( "\n" );
3293 if( !pFont->mbSymbolFlag && pEncoding == 0 )
3294 aLine.append( "/Encoding/WinAnsiEncoding\n" );
3295 if( nToUnicodeStream )
3297 aLine.append( "/ToUnicode " );
3298 aLine.append( nToUnicodeStream );
3299 aLine.append( " 0 R\n" );
3301 aLine.append( "/FirstChar 0 /LastChar 255\n"
3302 "/Widths[" );
3303 for( int i = 0; i < 256; i++ )
3305 aLine.append( pWidths[i] );
3306 aLine.append( ((i&15) == 15) ? "\n" : " " );
3308 aLine.append( "]\n"
3309 "/FontDescriptor " );
3310 aLine.append( nFontDescriptor );
3311 aLine.append( " 0 R>>\n"
3312 "endobj\n\n" );
3313 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3314 goto streamend;
3316 nFontObject = nObject;
3318 aRet[ rEmbed.m_nNormalFontID ] = nObject;
3320 // write additional encodings
3321 for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it )
3323 sal_Int32 aEncWidths[ 256 ];
3324 // emit encoding dict
3325 sal_Int32 nEncObject = createObject();
3326 if( ! updateObject( nEncObject ) )
3327 goto streamend;
3329 OutputDevice* pRef = getReferenceDevice();
3330 pRef->Push( PUSH_FONT | PUSH_MAPMODE );
3331 pRef->SetMapMode( MapMode( MAP_PIXEL ) );
3332 Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) );
3333 aFont.SetWeight( pFont->GetWeight() );
3334 aFont.SetItalic( pFont->GetSlant() );
3335 aFont.SetPitch( pFont->GetPitch() );
3336 pRef->SetFont( aFont );
3337 pRef->ImplNewFont();
3339 aLine.setLength( 0 );
3340 aLine.append( nEncObject );
3341 aLine.append( " 0 obj\n"
3342 "<</Type/Encoding/Differences[ 0\n" );
3343 int nEncoded = 0;
3344 for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it )
3346 String aStr( str_it->m_aUnicode );
3347 aEncWidths[nEncoded] = pRef->GetTextWidth( aStr );
3348 nEncodedCodes[nEncoded] = str_it->m_aUnicode;
3349 nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded);
3351 aLine.append( " /" );
3352 aLine.append( str_it->m_aName );
3353 if( !((++nEncoded) & 15) )
3354 aLine.append( "\n" );
3356 aLine.append( "]>>\n"
3357 "endobj\n\n" );
3359 pRef->Pop();
3361 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3362 goto streamend;
3364 nToUnicodeStream = createToUnicodeCMap( nEncoding, nEncodedCodes, nEncoded );
3366 nObject = createObject();
3367 if( ! updateObject( nObject ) )
3368 goto streamend;
3370 aLine.setLength( 0 );
3371 aLine.append( nObject );
3372 aLine.append( " 0 obj\n"
3373 "<</Type/Font/Subtype/Type1/BaseFont/" );
3374 appendName( aInfo.m_aPSName, aLine );
3375 aLine.append( "\n" );
3376 aLine.append( "/Encoding " );
3377 aLine.append( nEncObject );
3378 aLine.append( " 0 R\n" );
3379 if( nToUnicodeStream )
3381 aLine.append( "/ToUnicode " );
3382 aLine.append( nToUnicodeStream );
3383 aLine.append( " 0 R\n" );
3385 aLine.append( "/FirstChar 0\n"
3386 "/LastChar " );
3387 aLine.append( (sal_Int32)(nEncoded-1) );
3388 aLine.append( "\n"
3389 "/Widths[" );
3390 for( int i = 0; i < nEncoded; i++ )
3392 aLine.append( aEncWidths[i] );
3393 aLine.append( ((i&15) == 15) ? "\n" : " " );
3395 aLine.append( " ]\n"
3396 "/FontDescriptor " );
3397 aLine.append( nFontDescriptor );
3398 aLine.append( " 0 R>>\n"
3399 "endobj\n\n" );
3400 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3401 goto streamend;
3403 aRet[ enc_it->m_nFontID ] = nObject;
3407 streamend:
3408 if( pFontData )
3409 m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3411 return aRet;
3414 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
3416 if( nSubsetID )
3418 for( int i = 0; i < 6; i++ )
3420 int nOffset = (nSubsetID % 26);
3421 nSubsetID /= 26;
3422 rBuffer.append( (sal_Char)('A'+nOffset) );
3424 rBuffer.append( '+' );
3426 appendName( rPSName, rBuffer );
3429 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, sal_Ucs* pUnicodes, int nGlyphs )
3431 int nMapped = 0, n = 0;
3432 for( n = 0; n < nGlyphs; n++ )
3433 if( pUnicodes[n] )
3434 nMapped++;
3436 if( nMapped == 0 )
3437 return 0;
3439 sal_Int32 nStream = createObject();
3440 CHECK_RETURN( updateObject( nStream ) );
3442 OStringBuffer aContents( 1024 );
3443 aContents.append(
3444 "/CIDInit/ProcSet findresource begin\n"
3445 "12 dict begin\n"
3446 "begincmap\n"
3447 "/CIDSystemInfo<<\n"
3448 "/Registry (Adobe)\n"
3449 "/Ordering (UCS)\n"
3450 "/Supplement 0\n"
3451 ">> def\n"
3452 "/CMapName/Adobe-Identity-UCS def\n"
3453 "/CMapType 2 def\n"
3454 "1 begincodespacerange\n"
3455 "<00> <FF>\n"
3456 "endcodespacerange\n"
3458 int nCount = 0;
3459 for( n = 0; n < nGlyphs; n++ )
3461 if( pUnicodes[n] )
3463 if( (nCount % 100) == 0 )
3465 if( nCount )
3466 aContents.append( "endbfchar\n" );
3467 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) );
3468 aContents.append( " beginbfchar\n" );
3470 aContents.append( '<' );
3471 appendHex( (sal_Int8)pEncoding[n], aContents );
3472 aContents.append( "> <" );
3473 // TODO: handle unicodes>U+FFFF
3474 appendHex( (sal_Int8)(pUnicodes[n] / 256), aContents );
3475 appendHex( (sal_Int8)(pUnicodes[n] & 255), aContents );
3476 aContents.append( ">\n" );
3477 nCount++;
3480 aContents.append( "endbfchar\n"
3481 "endcmap\n"
3482 "CMapName currentdict /CMap defineresource pop\n"
3483 "end\n"
3484 "end\n" );
3485 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3486 ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
3487 SvMemoryStream aStream;
3488 pCodec->BeginCompression();
3489 pCodec->Write( aStream, (const BYTE*)aContents.getStr(), aContents.getLength() );
3490 pCodec->EndCompression();
3491 delete pCodec;
3492 #endif
3494 #if OSL_DEBUG_LEVEL > 1
3496 OStringBuffer aLine( " PDFWriterImpl::createToUnicodeCMap" );
3497 emitComment( aLine.getStr() );
3499 #endif
3500 OStringBuffer aLine( 40 );
3502 aLine.append( nStream );
3503 aLine.append( " 0 obj\n<</Length " );
3504 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3505 sal_Int32 nLen = (sal_Int32)aStream.Tell();
3506 aStream.Seek( 0 );
3507 aLine.append( nLen );
3508 aLine.append( "/Filter/FlateDecode" );
3509 #else
3510 aLine.append( aContents.getLength() );
3511 #endif
3512 aLine.append( ">>\nstream\n" );
3513 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3514 checkAndEnableStreamEncryption( nStream );
3515 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3516 CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
3517 #else
3518 CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
3519 #endif
3520 disableStreamEncryption();
3521 aLine.setLength( 0 );
3522 aLine.append( "\nendstream\n"
3523 "endobj\n\n" );
3524 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3525 return nStream;
3528 sal_Int32 PDFWriterImpl::emitFontDescriptor( const ImplFontData* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
3530 OStringBuffer aLine( 1024 );
3531 // get font flags, see PDF reference 1.4 p. 358
3532 // possibly characters outside Adobe standard encoding
3533 // so set Symbolic flag
3534 sal_Int32 nFontFlags = (1<<2);
3535 if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE )
3536 nFontFlags |= (1 << 6);
3537 if( pFont->GetPitch() == PITCH_FIXED )
3538 nFontFlags |= 1;
3539 if( pFont->GetFamilyType() == FAMILY_SCRIPT )
3540 nFontFlags |= (1 << 3);
3541 else if( pFont->GetFamilyType() == FAMILY_ROMAN )
3542 nFontFlags |= (1 << 1);
3544 sal_Int32 nFontDescriptor = createObject();
3545 CHECK_RETURN( updateObject( nFontDescriptor ) );
3546 aLine.setLength( 0 );
3547 aLine.append( nFontDescriptor );
3548 aLine.append( " 0 obj\n"
3549 "<</Type/FontDescriptor/FontName/" );
3550 appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
3551 aLine.append( "\n"
3552 "/Flags " );
3553 aLine.append( nFontFlags );
3554 aLine.append( "\n"
3555 "/FontBBox[" );
3556 // note: Top and Bottom are reversed in VCL and PDF rectangles
3557 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() );
3558 aLine.append( ' ' );
3559 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() );
3560 aLine.append( ' ' );
3561 aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() );
3562 aLine.append( ' ' );
3563 aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) );
3564 aLine.append( "]/ItalicAngle " );
3565 if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL )
3566 aLine.append( "-30" );
3567 else
3568 aLine.append( "0" );
3569 aLine.append( "\n"
3570 "/Ascent " );
3571 aLine.append( (sal_Int32)rInfo.m_nAscent );
3572 aLine.append( "\n"
3573 "/Descent " );
3574 aLine.append( (sal_Int32)-rInfo.m_nDescent );
3575 aLine.append( "\n"
3576 "/CapHeight " );
3577 aLine.append( (sal_Int32)rInfo.m_nCapHeight );
3578 // According to PDF reference 1.4 StemV is required
3579 // seems a tad strange to me, but well ...
3580 aLine.append( "\n"
3581 "/StemV 80\n"
3582 "/FontFile" );
3583 switch( rInfo.m_nFontType )
3585 case SAL_FONTSUBSETINFO_TYPE_TRUETYPE:
3586 aLine.append( '2' );
3587 break;
3588 case SAL_FONTSUBSETINFO_TYPE_TYPE1:
3589 break;
3590 default:
3591 DBG_ERROR( "unknown fonttype in PDF font descriptor" );
3592 return 0;
3594 aLine.append( ' ' );
3595 aLine.append( nFontStream );
3596 aLine.append( " 0 R>>\n"
3597 "endobj\n\n" );
3598 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3600 return nFontDescriptor;
3603 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const
3605 for( std::map< sal_Int32, sal_Int32 >::const_iterator it =
3606 m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it )
3608 rDict.append( m_aBuiltinFonts[it->first].getNameObject() );
3609 rDict.append( ' ' );
3610 rDict.append( it->second );
3611 rDict.append( " 0 R" );
3615 bool PDFWriterImpl::emitFonts()
3617 if( ! m_pReferenceDevice->ImplGetGraphics() )
3618 return false;
3620 OStringBuffer aLine( 1024 );
3621 char buf[8192];
3623 std::map< sal_Int32, sal_Int32 > aFontIDToObject;
3625 OUString aTmpName;
3626 osl_createTempFile( NULL, NULL, &aTmpName.pData );
3627 for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it )
3629 for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit )
3631 sal_Int32 pGlyphIDs[ 256 ];
3632 sal_Int32 pWidths[ 256 ];
3633 sal_uInt8 pEncoding[ 256 ];
3634 sal_Ucs pUnicodes[ 256 ];
3635 int nGlyphs = 1;
3636 // fill arrays and prepare encoding index map
3637 sal_Int32 nToUnicodeStream = 0;
3639 memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) );
3640 memset( pEncoding, 0, sizeof( pEncoding ) );
3641 memset( pUnicodes, 0, sizeof( pUnicodes ) );
3642 for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit )
3644 sal_uInt8 nEnc = fit->second.m_nSubsetGlyphID;
3646 DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" );
3647 DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" );
3649 pGlyphIDs[ nEnc ] = fit->first;
3650 pEncoding[ nEnc ] = nEnc;
3651 pUnicodes[ nEnc ] = fit->second.m_aUnicode;
3652 if( pUnicodes[ nEnc ] )
3653 nToUnicodeStream = 1;
3654 if( nGlyphs < 256 )
3655 nGlyphs++;
3656 else
3658 DBG_ERROR( "too many glyphs for subset" );
3661 FontSubsetInfo aSubsetInfo;
3662 if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
3664 DBG_ASSERT( aSubsetInfo.m_nFontType == SAL_FONTSUBSETINFO_TYPE_TRUETYPE, "wrong font type in font subset" );
3665 // create font stream
3666 oslFileHandle aFontFile;
3667 CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) );
3668 // get file size
3669 sal_uInt64 nLength;
3670 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) );
3671 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength ) ) );
3672 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
3674 #if OSL_DEBUG_LEVEL > 1
3676 OStringBuffer aLine1( " PDFWriterImpl::emitFonts" );
3677 emitComment( aLine1.getStr() );
3679 #endif
3680 sal_Int32 nFontStream = createObject();
3681 sal_Int32 nStreamLengthObject = createObject();
3682 CHECK_RETURN( updateObject( nFontStream ) );
3683 aLine.setLength( 0 );
3684 aLine.append( nFontStream );
3685 aLine.append( " 0 obj\n"
3686 "<</Length " );
3687 aLine.append( (sal_Int32)nStreamLengthObject );
3688 aLine.append( " 0 R"
3689 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3690 "/Filter/FlateDecode"
3691 #endif
3692 "/Length1 " );
3693 aLine.append( (sal_Int32)nLength );
3694 aLine.append( ">>\n"
3695 "stream\n" );
3696 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3698 sal_uInt64 nStartPos = 0;
3699 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
3701 // copy font file
3702 beginCompression();
3703 checkAndEnableStreamEncryption( nFontStream );
3704 sal_uInt64 nRead;
3705 sal_Bool bEOF = sal_False;
3708 CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) );
3709 CHECK_RETURN( writeBuffer( buf, nRead ) );
3710 CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) );
3711 } while( ! bEOF );
3712 endCompression();
3713 disableStreamEncryption();
3714 // close the file
3715 osl_closeFile( aFontFile );
3717 sal_uInt64 nEndPos = 0;
3718 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) );
3719 // end the stream
3720 aLine.setLength( 0 );
3721 aLine.append( "\nendstream\nendobj\n\n" );
3722 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3724 // emit stream length object
3725 CHECK_RETURN( updateObject( nStreamLengthObject ) );
3726 aLine.setLength( 0 );
3727 aLine.append( nStreamLengthObject );
3728 aLine.append( " 0 obj\n" );
3729 aLine.append( (sal_Int64)(nEndPos-nStartPos) );
3730 aLine.append( "\nendobj\n\n" );
3731 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3733 // write font descriptor
3734 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream );
3736 if( nToUnicodeStream )
3737 nToUnicodeStream = createToUnicodeCMap( pEncoding, pUnicodes, nGlyphs );
3739 sal_Int32 nFontObject = createObject();
3740 CHECK_RETURN( updateObject( nFontObject ) );
3741 aLine.setLength( 0 );
3742 aLine.append( nFontObject );
3743 aLine.append( " 0 obj\n"
3744 "<</Type/Font/Subtype/TrueType/BaseFont/" );
3745 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine );
3746 aLine.append( "\n"
3747 "/FirstChar 0\n"
3748 "/LastChar " );
3749 aLine.append( (sal_Int32)(nGlyphs-1) );
3750 aLine.append( "\n"
3751 "/Widths[" );
3752 for( int i = 0; i < nGlyphs; i++ )
3754 aLine.append( pWidths[ i ] );
3755 aLine.append( ((i & 15) == 15) ? "\n" : " " );
3757 aLine.append( "]\n"
3758 "/FontDescriptor " );
3759 aLine.append( nFontDescriptor );
3760 aLine.append( " 0 R\n" );
3761 if( nToUnicodeStream )
3763 aLine.append( "/ToUnicode " );
3764 aLine.append( nToUnicodeStream );
3765 aLine.append( " 0 R\n" );
3767 aLine.append( ">>\n"
3768 "endobj\n\n" );
3769 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3771 aFontIDToObject[ lit->m_nFontID ] = nFontObject;
3773 else
3775 const ImplFontData* pFont = it->first;
3776 rtl::OStringBuffer aErrorComment( 256 );
3777 aErrorComment.append( "CreateFontSubset failed for font \"" );
3778 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3779 aErrorComment.append( '\"' );
3780 if( pFont->GetSlant() == ITALIC_NORMAL )
3781 aErrorComment.append( " italic" );
3782 else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3783 aErrorComment.append( " oblique" );
3784 aErrorComment.append( " weight=" );
3785 aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3786 emitComment( aErrorComment.getStr() );
3790 osl_removeFile( aTmpName.pData );
3792 // emit embedded fonts
3793 for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit )
3795 std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second );
3796 for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
3798 CHECK_RETURN( fit->second );
3799 aFontIDToObject[ fit->first ] = fit->second;
3803 OStringBuffer aFontDict( 1024 );
3804 aFontDict.append( getFontDictObject() );
3805 aFontDict.append( " 0 obj\n"
3806 "<<" );
3807 int ni = 0;
3808 for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit )
3810 aFontDict.append( "/F" );
3811 aFontDict.append( mit->first );
3812 aFontDict.append( ' ' );
3813 aFontDict.append( mit->second );
3814 aFontDict.append( " 0 R" );
3815 if( ((++ni) & 7) == 0 )
3816 aFontDict.append( '\n' );
3818 // emit builtin font for widget apperances / variable text
3819 for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin();
3820 it != m_aBuiltinFontToObjectMap.end(); ++it )
3822 ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]);
3823 it->second = emitBuiltinFont( &aData, it->second );
3825 appendBuiltinFontsToDict( aFontDict );
3826 aFontDict.append( "\n>>\nendobj\n\n" );
3828 CHECK_RETURN( updateObject( getFontDictObject() ) );
3829 CHECK_RETURN( writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) );
3830 return true;
3833 sal_Int32 PDFWriterImpl::emitResources()
3835 // emit shadings
3836 if( ! m_aGradients.empty() )
3837 CHECK_RETURN( emitGradients() );
3838 // emit tilings
3839 if( ! m_aTilings.empty() )
3840 CHECK_RETURN( emitTilings() );
3842 // emit font dict
3843 CHECK_RETURN( emitFonts() );
3845 // emit Resource dict
3846 OStringBuffer aLine( 512 );
3847 sal_Int32 nResourceDict = getResourceDictObj();
3848 CHECK_RETURN( updateObject( nResourceDict ) );
3849 aLine.setLength( 0 );
3850 aLine.append( nResourceDict );
3851 aLine.append( " 0 obj\n" );
3852 m_aGlobalResourceDict.append( aLine, getFontDictObject() );
3853 aLine.append( "endobj\n\n" );
3854 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3855 return nResourceDict;
3858 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
3859 sal_Int32 nItemLevel,
3860 sal_Int32 nCurrentItemId )
3862 /* The /Count number of an item is
3863 positive: the number of visible subitems
3864 negative: the negative number of subitems that will become visible if
3865 the item gets opened
3866 see PDF ref 1.4 p 478
3869 sal_Int32 nCount = 0;
3871 if( m_aContext.OpenBookmarkLevels < 0 || // all levels arevisible
3872 m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible
3875 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
3876 sal_Int32 nChildren = rItem.m_aChildren.size();
3877 for( sal_Int32 i = 0; i < nChildren; i++ )
3878 nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
3879 rCounts[nCurrentItemId] = nCount;
3880 // return 1 (this item) + visible sub items
3881 if( nCount < 0 )
3882 nCount = 0;
3883 nCount++;
3885 else
3887 // this bookmark level is invisible
3888 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
3889 sal_Int32 nChildren = rItem.m_aChildren.size();
3890 rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
3891 for( sal_Int32 i = 0; i < nChildren; i++ )
3892 updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
3893 nCount = -1;
3896 return nCount;
3899 sal_Int32 PDFWriterImpl::emitOutline()
3901 int i, nItems = m_aOutline.size();
3903 // do we have an outline at all ?
3904 if( nItems < 2 )
3905 return 0;
3907 // reserve object numbers for all outline items
3908 for( i = 0; i < nItems; ++i )
3909 m_aOutline[i].m_nObject = createObject();
3911 // update all parent, next and prev object ids
3912 for( i = 0; i < nItems; ++i )
3914 PDFOutlineEntry& rItem = m_aOutline[i];
3915 int nChildren = rItem.m_aChildren.size();
3917 if( nChildren )
3919 for( int n = 0; n < nChildren; ++n )
3921 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
3923 rChild.m_nParentObject = rItem.m_nObject;
3924 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
3925 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
3931 // calculate Count entries for all items
3932 std::vector< sal_Int32 > aCounts( nItems );
3933 updateOutlineItemCount( aCounts, 0, 0 );
3935 // emit hierarchy
3936 for( i = 0; i < nItems; ++i )
3938 PDFOutlineEntry& rItem = m_aOutline[i];
3939 OStringBuffer aLine( 1024 );
3941 CHECK_RETURN( updateObject( rItem.m_nObject ) );
3942 aLine.append( rItem.m_nObject );
3943 aLine.append( " 0 obj\n" );
3944 aLine.append( "<<" );
3945 // number of visible children (all levels)
3946 if( i > 0 || aCounts[0] > 0 )
3948 aLine.append( "/Count " );
3949 aLine.append( aCounts[i] );
3951 if( ! rItem.m_aChildren.empty() )
3953 // children list: First, Last
3954 aLine.append( "/First " );
3955 aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
3956 aLine.append( " 0 R/Last " );
3957 aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
3958 aLine.append( " 0 R\n" );
3960 if( i > 0 )
3962 // Title, Dest, Parent, Prev, Next
3963 aLine.append( "/Title" );
3964 appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
3965 aLine.append( "\n" );
3966 // Dest is not required
3967 if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() )
3969 aLine.append( "/Dest" );
3970 appendDest( rItem.m_nDestID, aLine );
3972 aLine.append( "/Parent " );
3973 aLine.append( rItem.m_nParentObject );
3974 aLine.append( " 0 R" );
3975 if( rItem.m_nPrevObject )
3977 aLine.append( "/Prev " );
3978 aLine.append( rItem.m_nPrevObject );
3979 aLine.append( " 0 R" );
3981 if( rItem.m_nNextObject )
3983 aLine.append( "/Next " );
3984 aLine.append( rItem.m_nNextObject );
3985 aLine.append( " 0 R" );
3988 aLine.append( ">>\nendobj\n\n" );
3989 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3992 return m_aOutline[0].m_nObject;
3995 #undef CHECK_RETURN
3996 #define CHECK_RETURN( x ) if( !x ) return false
3998 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
4000 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() )
4002 #if OSL_DEBUG_LEVEL > 1
4003 fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID );
4004 #endif
4005 return false;
4009 const PDFDest& rDest = m_aDests[ nDestID ];
4010 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
4012 rBuffer.append( '[' );
4013 rBuffer.append( rDestPage.m_nPageObject );
4014 rBuffer.append( " 0 R" );
4016 switch( rDest.m_eType )
4018 case PDFWriter::XYZ:
4019 default:
4020 rBuffer.append( "/XYZ " );
4021 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4022 rBuffer.append( ' ' );
4023 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4024 rBuffer.append( " 0" );
4025 break;
4026 case PDFWriter::Fit:
4027 rBuffer.append( "/Fit" );
4028 break;
4029 case PDFWriter::FitRectangle:
4030 rBuffer.append( "/FitR " );
4031 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4032 rBuffer.append( ' ' );
4033 appendFixedInt( rDest.m_aRect.Top(), rBuffer );
4034 rBuffer.append( ' ' );
4035 appendFixedInt( rDest.m_aRect.Right(), rBuffer );
4036 rBuffer.append( ' ' );
4037 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4038 break;
4039 case PDFWriter::FitHorizontal:
4040 rBuffer.append( "/FitH " );
4041 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4042 break;
4043 case PDFWriter::FitVertical:
4044 rBuffer.append( "/FitV " );
4045 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4046 break;
4047 case PDFWriter::FitPageBoundingBox:
4048 rBuffer.append( "/FitB" );
4049 break;
4050 case PDFWriter::FitPageBoundingBoxHorizontal:
4051 rBuffer.append( "/FitBH " );
4052 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4053 break;
4054 case PDFWriter::FitPageBoundingBoxVertical:
4055 rBuffer.append( "/FitBV " );
4056 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4057 break;
4059 rBuffer.append( ']' );
4061 return true;
4064 bool PDFWriterImpl::emitLinkAnnotations()
4066 int nAnnots = m_aLinks.size();
4067 for( int i = 0; i < nAnnots; i++ )
4069 const PDFLink& rLink = m_aLinks[i];
4070 if( ! updateObject( rLink.m_nObject ) )
4071 continue;
4073 OStringBuffer aLine( 1024 );
4074 aLine.append( rLink.m_nObject );
4075 aLine.append( " 0 obj\n" );
4076 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4077 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4078 aLine.append( "<</Type/Annot" );
4079 if( m_bIsPDF_A1 )
4080 aLine.append( "/F 4" );
4081 aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
4083 appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
4084 aLine.append( ' ' );
4085 appendFixedInt( rLink.m_aRect.Top(), aLine );
4086 aLine.append( ' ' );
4087 appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
4088 aLine.append( ' ' );
4089 appendFixedInt( rLink.m_aRect.Bottom(), aLine );
4090 aLine.append( "]" );
4091 if( rLink.m_nDest >= 0 )
4093 aLine.append( "/Dest" );
4094 appendDest( rLink.m_nDest, aLine );
4096 else
4098 /*--->i56629
4099 destination is external to the document, so
4100 we check in the following sequence:
4102 if target type is neither .pdf, nor .od[tpgs], then
4103 check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
4104 end processing
4105 else if target is .od[tpgs]: then
4106 if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file
4107 processing continue
4109 if (new)target is .pdf : then
4110 if GotToR is requested, then
4111 convert the target in GoToR where the fragment of the URI is
4112 considered the named destination in the target file, set relative or absolute as requested
4113 else strip the fragment from URL and then set URI or 'launch application' as requested
4116 // FIXME: check if the decode mechanisms for URL processing throughout this implementation
4117 // are the correct one!!
4119 // extract target file type
4120 INetURLObject aDocumentURL( m_aContext.BaseURL );
4121 INetURLObject aTargetURL( rLink.m_aURL );
4122 sal_Int32 nChangeFileExtensionToPDF = 0;
4123 sal_Int32 nSetGoToRMode = 0;
4124 sal_Bool bTargetHasPDFExtension = sal_False;
4125 INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
4126 sal_Bool bIsUNCPath = sal_False;
4127 // check if the protocol is a known one, or if there is no protocol at all (on target only)
4128 // if there is no protocol, make the target relative to the current document directory
4129 // getting the needed URL information from the current document path
4130 if( eTargetProtocol == INET_PROT_NOT_VALID )
4132 if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.compareToAscii( "\\\\\\\\", 4 ) == 0)
4134 bIsUNCPath = sal_True;
4136 else
4138 INetURLObject aNewBase( aDocumentURL );//duplicate document URL
4139 aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
4140 //target document
4141 aNewBase.insertName( rLink.m_aURL );
4142 aTargetURL = aNewBase;//reassign the new target URL
4143 //recompute the target protocol, with the new URL
4144 //normal URL processing resumes
4145 eTargetProtocol = aTargetURL.GetProtocol();
4149 rtl::OUString aFileExtension = aTargetURL.GetFileExtension();
4151 // Check if the URL ends in '/': if yes it's a directory,
4152 // it will be forced to a URI link.
4153 // possibly a malformed URI, leave it as it is, force as URI
4154 if( aTargetURL.hasFinalSlash() )
4155 m_aContext.DefaultLinkAction = PDFWriter::URIAction;
4157 if( aFileExtension.getLength() > 0 )
4159 if( m_aContext.ConvertOOoTargetToPDFTarget )
4161 //examine the file type (.odm .odt. .odp, odg, ods)
4162 if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odm" ) ) ) )
4163 nChangeFileExtensionToPDF++;
4164 if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odt" ) ) ) )
4165 nChangeFileExtensionToPDF++;
4166 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odp" ) ) ) )
4167 nChangeFileExtensionToPDF++;
4168 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odg" ) ) ) )
4169 nChangeFileExtensionToPDF++;
4170 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ods" ) ) ) )
4171 nChangeFileExtensionToPDF++;
4172 if( nChangeFileExtensionToPDF )
4173 aTargetURL.setExtension(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4175 //check if extension is pdf, see if GoToR should be forced
4176 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4177 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
4178 nSetGoToRMode++;
4180 //prepare the URL, if relative or not
4181 INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
4182 //queue the string common to all types of actions
4183 aLine.append( "/A<</Type/Action/S");
4184 if( bIsUNCPath ) // handle Win UNC paths
4186 aLine.append( "/Launch/Win<</F" );
4187 // INetURLObject is not good with UNC paths, use original path
4188 appendLiteralStringEncrypt( rLink.m_aURL, rLink.m_nObject, aLine );
4189 aLine.append( ">>" );
4191 else
4193 sal_Int32 nSetRelative = 0;
4194 //check if relative file link is requested and if the protocol is 'file://'
4195 if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE )
4196 nSetRelative++;
4198 rtl::OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is,
4199 if( nSetGoToRMode == 0 )
4200 switch( m_aContext.DefaultLinkAction )
4202 default:
4203 case PDFWriter::URIAction :
4204 case PDFWriter::URIActionDestination :
4205 aLine.append( "/URI/URI" );
4206 break;
4207 case PDFWriter::LaunchAction:
4208 // now:
4209 // if a launch action is requested and the hyperlink target has a fragment
4210 // and the target file does not have a pdf extension, or it's not a 'file:://' protocol
4211 // then force the uri action on it
4212 // This code will permit the correct opening of application on web pages, the one that
4213 // normally have fragments (but I may be wrong...)
4214 // and will force the use of URI when the protocol is not file://
4215 if( (aFragment.getLength() > 0 && !bTargetHasPDFExtension) ||
4216 eTargetProtocol != INET_PROT_FILE )
4217 aLine.append( "/URI/URI" );
4218 else
4219 aLine.append( "/Launch/F" );
4220 break;
4222 //fragment are encoded in the same way as in the named destination processing
4223 rtl::OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET );
4224 if( nSetGoToRMode )
4225 {//add the fragment
4226 aLine.append("/GoToR");
4227 aLine.append("/F");
4228 appendLiteralStringEncrypt( nSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark,
4229 INetURLObject::WAS_ENCODED,
4230 INetURLObject::DECODE_WITH_CHARSET ) :
4231 aURLNoMark, rLink.m_nObject, aLine );
4232 if( aFragment.getLength() > 0 )
4234 aLine.append("/D/");
4235 appendDestinationName( aFragment , aLine );
4238 else
4240 // change the fragment to accomodate the bookmark (only if the file extension is PDF and
4241 // the requested action is of the correct type)
4242 if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination &&
4243 bTargetHasPDFExtension && aFragment.getLength() > 0 )
4245 OStringBuffer aLineLoc( 1024 );
4246 appendDestinationName( aFragment , aLineLoc );
4247 //substitute the fragment
4248 aTargetURL.SetMark( aLineLoc.getStr() );
4250 rtl::OUString aURL = aTargetURL.GetMainURL( (nSetRelative || eTargetProtocol == INET_PROT_FILE) ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE );
4251 // check if we have a URL available, if the string is empty, set it as the original one
4252 // if( aURL.getLength() == 0 )
4253 // appendLiteralStringEncrypt( rLink.m_aURL , rLink.m_nObject, aLine );
4254 // else
4255 appendLiteralStringEncrypt( nSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL ) :
4256 aURL , rLink.m_nObject, aLine );
4258 //<--- i56629
4260 aLine.append( ">>\n" );
4262 if( rLink.m_nStructParent > 0 )
4264 aLine.append( "/StructParent " );
4265 aLine.append( rLink.m_nStructParent );
4267 aLine.append( ">>\nendobj\n\n" );
4268 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4271 return true;
4274 bool PDFWriterImpl::emitNoteAnnotations()
4276 // emit note annotations
4277 int nAnnots = m_aNotes.size();
4278 for( int i = 0; i < nAnnots; i++ )
4280 const PDFNoteEntry& rNote = m_aNotes[i];
4281 if( ! updateObject( rNote.m_nObject ) )
4282 return false;
4284 OStringBuffer aLine( 1024 );
4285 aLine.append( rNote.m_nObject );
4286 aLine.append( " 0 obj\n" );
4287 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4288 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4289 aLine.append( "<</Type/Annot" );
4290 if( m_bIsPDF_A1 )
4291 aLine.append( "/F 4" );
4292 aLine.append( "/Subtype/Text/Rect[" );
4294 appendFixedInt( rNote.m_aRect.Left(), aLine );
4295 aLine.append( ' ' );
4296 appendFixedInt( rNote.m_aRect.Top(), aLine );
4297 aLine.append( ' ' );
4298 appendFixedInt( rNote.m_aRect.Right(), aLine );
4299 aLine.append( ' ' );
4300 appendFixedInt( rNote.m_aRect.Bottom(), aLine );
4301 aLine.append( "]" );
4303 // contents of the note (type text string)
4304 aLine.append( "/Contents\n" );
4305 appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
4306 aLine.append( "\n" );
4308 // optional title
4309 if( rNote.m_aContents.Title.Len() )
4311 aLine.append( "/T" );
4312 appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
4313 aLine.append( "\n" );
4316 aLine.append( ">>\nendobj\n\n" );
4317 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4319 return true;
4322 Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font& rAppSetFont )
4324 bool bAdjustSize = false;
4326 Font aFont( rControlFont );
4327 if( ! aFont.GetName().Len() )
4329 aFont = rAppSetFont;
4330 if( rControlFont.GetHeight() )
4331 aFont.SetSize( Size( 0, rControlFont.GetHeight() ) );
4332 else
4333 bAdjustSize = true;
4334 if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
4335 aFont.SetItalic( rControlFont.GetItalic() );
4336 if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
4337 aFont.SetWeight( rControlFont.GetWeight() );
4339 else if( ! aFont.GetHeight() )
4341 aFont.SetSize( rAppSetFont.GetSize() );
4342 bAdjustSize = true;
4344 if( bAdjustSize )
4346 Size aFontSize = aFont.GetSize();
4347 OutputDevice* pDefDev = Application::GetDefaultDevice();
4348 aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
4349 aFont.SetSize( aFontSize );
4351 return aFont;
4354 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont )
4356 sal_Int32 nBest = 4; // default to Helvetica
4357 OUString aFontName( rFont.GetName() );
4358 aFontName = aFontName.toAsciiLowerCase();
4360 if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "times" ) ) ) != -1 )
4361 nBest = 8;
4362 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "courier" ) ) ) != -1 )
4363 nBest = 0;
4364 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "dingbats" ) ) ) != -1 )
4365 nBest = 13;
4366 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "symbol" ) ) ) != -1 )
4367 nBest = 12;
4368 if( nBest < 12 )
4370 if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
4371 nBest += 1;
4372 if( rFont.GetWeight() > WEIGHT_MEDIUM )
4373 nBest += 2;
4376 if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() )
4377 m_aBuiltinFontToObjectMap[ nBest ] = createObject();
4379 return nBest;
4382 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
4384 return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1;
4387 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget )
4389 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4391 // save graphics state
4392 push( sal::static_int_cast<sal_uInt16>(~0U) );
4394 // transform relative to control's coordinates since an
4395 // appearance stream is a form XObject
4396 // this relies on the m_aRect member of rButton NOT already being transformed
4397 // to default user space
4398 if( rWidget.Background || rWidget.Border )
4400 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) );
4401 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) );
4402 drawRectangle( rWidget.Location );
4404 // prepare font to use
4405 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
4406 setFont( aFont );
4407 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
4409 drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
4411 // create DA string while local mapmode is still in place
4412 // (that is before endRedirect())
4413 OStringBuffer aDA( 256 );
4414 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
4415 Font aDummyFont( String( RTL_CONSTASCII_USTRINGPARAM( "Helvetica" ) ), aFont.GetSize() );
4416 sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont );
4417 aDA.append( ' ' );
4418 aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() );
4419 aDA.append( ' ' );
4420 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4421 aDA.append( " Tf" );
4422 rButton.m_aDAString = aDA.makeStringAndClear();
4424 pop();
4426 rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
4428 /* seems like a bad hack but at least works in both AR5 and 6:
4429 we draw the button ourselves and tell AR
4430 the button would be totally transparent with no text
4432 One would expect that simply setting a normal appearance
4433 should suffice, but no, as soon as the user actually presses
4434 the button and an action is tied to it (gasp! a button that
4435 does something) the appearance gets replaced by some crap that AR
4436 creates on the fly even if no DA or MK is given. On AR6 at least
4437 the DA and MK work as expected, but on AR5 this creates a region
4438 filled with the background color but nor text. Urgh.
4440 rButton.m_aMKDict = "/BC [] /BG [] /CA";
4441 rButton.m_aMKDictCAString = "";
4444 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern,
4445 const PDFWriter::AnyWidget& rWidget,
4446 const StyleSettings& rSettings )
4448 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
4450 if( rWidget.Background || rWidget.Border )
4452 if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) )
4454 sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500;
4455 if( nDelta < 1 )
4456 nDelta = 1;
4457 setLineColor( Color( COL_TRANSPARENT ) );
4458 Rectangle aRect = rIntern.m_aRect;
4459 setFillColor( rSettings.GetLightBorderColor() );
4460 drawRectangle( aRect );
4461 aRect.Left() += nDelta; aRect.Top() += nDelta;
4462 aRect.Right() -= nDelta; aRect.Bottom() -= nDelta;
4463 setFillColor( rSettings.GetFieldColor() );
4464 drawRectangle( aRect );
4465 setFillColor( rSettings.GetLightColor() );
4466 drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
4467 drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
4468 setFillColor( rSettings.GetDarkShadowColor() );
4469 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
4470 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
4472 else
4474 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) );
4475 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4476 drawRectangle( rIntern.m_aRect );
4479 if( rWidget.Border )
4481 // adjust edit area accounting for border
4482 sal_Int32 nDelta = aFont.GetHeight()/4;
4483 if( nDelta < 1 )
4484 nDelta = 1;
4485 rIntern.m_aRect.Left() += nDelta;
4486 rIntern.m_aRect.Top() += nDelta;
4487 rIntern.m_aRect.Right() -= nDelta;
4488 rIntern.m_aRect.Bottom()-= nDelta;
4491 return aFont;
4494 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget )
4496 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4497 SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
4499 push( sal::static_int_cast<sal_uInt16>(~0U) );
4501 // prepare font to use, draw field border
4502 Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
4503 sal_Int32 nBest = getBestBuiltinFont( aFont );
4505 // prepare DA string
4506 OStringBuffer aDA( 32 );
4507 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4508 aDA.append( ' ' );
4509 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4510 aDA.append( ' ' );
4511 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4512 aDA.append( " Tf" );
4514 /* create an empty appearance stream, let the viewer create
4515 the appearance at runtime. This is because AR5 seems to
4516 paint the widget appearance always, and a dynamically created
4517 appearance on top of it. AR6 is well behaved in that regard, so
4518 that behaviour seems to be a bug. Anyway this empty appearance
4519 relies on /NeedAppearances in the AcroForm dictionary set to "true"
4521 beginRedirect( pEditStream, rEdit.m_aRect );
4522 OStringBuffer aAppearance( 32 );
4523 aAppearance.append( "/Tx BMC\nEMC\n" );
4524 writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4526 endRedirect();
4527 pop();
4529 rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
4531 rEdit.m_aDAString = aDA.makeStringAndClear();
4534 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget )
4536 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4537 SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
4539 push( sal::static_int_cast<sal_uInt16>(~0U) );
4541 // prepare font to use, draw field border
4542 Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
4543 sal_Int32 nBest = getBestBuiltinFont( aFont );
4545 beginRedirect( pListBoxStream, rBox.m_aRect );
4546 OStringBuffer aAppearance( 64 );
4548 #if 0
4549 if( ! rWidget.DropDown )
4551 // prepare linewidth for DA string hack, see below
4552 Size aFontSize = lcl_convert( m_aGraphicsStack.front().m_aMapMode,
4553 m_aMapMode,
4554 getReferenceDevice(),
4555 Size( 0, aFont.GetHeight() ) );
4556 sal_Int32 nLW = aFontSize.Height() / 40;
4557 appendFixedInt( nLW > 0 ? nLW : 1, aAppearance );
4558 aAppearance.append( " w\n" );
4559 writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4560 aAppearance.setLength( 0 );
4562 #endif
4564 setLineColor( Color( COL_TRANSPARENT ) );
4565 setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
4566 drawRectangle( rBox.m_aRect );
4568 // empty appearance, see createDefaultEditAppearance for reference
4569 aAppearance.append( "/Tx BMC\nEMC\n" );
4570 writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4572 endRedirect();
4573 pop();
4575 rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
4577 // prepare DA string
4578 OStringBuffer aDA( 256 );
4579 #if 0
4580 if( !rWidget.DropDown )
4582 /* another of AR5's peculiarities: the selected item of a choice
4583 field is highlighted using the non stroking color - same as the
4584 text color. so workaround that by using text rendering mode 2
4585 (fill, then stroke) and set the stroking color
4587 appendStrokingColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ), aDA );
4588 aDA.append( " 2 Tr " );
4590 #endif
4591 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4592 aDA.append( ' ' );
4593 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4594 aDA.append( ' ' );
4595 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4596 aDA.append( " Tf" );
4597 rBox.m_aDAString = aDA.makeStringAndClear();
4600 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget )
4602 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4604 // save graphics state
4605 push( sal::static_int_cast<sal_uInt16>(~0U) );
4607 if( rWidget.Background || rWidget.Border )
4609 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
4610 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4611 drawRectangle( rBox.m_aRect );
4614 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
4615 setFont( aFont );
4616 Size aFontSize = aFont.GetSize();
4617 if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
4618 aFontSize.Height() = rBox.m_aRect.GetHeight();
4619 sal_Int32 nDelta = aFontSize.Height()/10;
4620 if( nDelta < 1 )
4621 nDelta = 1;
4623 Rectangle aCheckRect, aTextRect;
4624 if( rWidget.ButtonIsLeft )
4626 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
4627 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4628 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
4629 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4631 // #i74206# handle small controls without text area
4632 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4634 aCheckRect.Right() -= nDelta;
4635 aCheckRect.Top() += nDelta/2;
4636 aCheckRect.Bottom() -= nDelta - (nDelta/2);
4639 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
4640 aTextRect.Top() = rBox.m_aRect.Top();
4641 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4642 aTextRect.Bottom() = rBox.m_aRect.Bottom();
4644 else
4646 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
4647 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4648 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
4649 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4651 // #i74206# handle small controls without text area
4652 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4654 aCheckRect.Left() += nDelta;
4655 aCheckRect.Top() += nDelta/2;
4656 aCheckRect.Bottom() -= nDelta - (nDelta/2);
4659 aTextRect.Left() = rBox.m_aRect.Left();
4660 aTextRect.Top() = rBox.m_aRect.Top();
4661 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4662 aTextRect.Bottom() = rBox.m_aRect.Bottom();
4664 setLineColor( Color( COL_BLACK ) );
4665 setFillColor( Color( COL_TRANSPARENT ) );
4666 OStringBuffer aLW( 32 );
4667 aLW.append( "q " );
4668 m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
4669 aLW.append( " w " );
4670 writeBuffer( aLW.getStr(), aLW.getLength() );
4671 drawRectangle( aCheckRect );
4672 writeBuffer( " Q\n", 3 );
4673 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4674 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
4676 pop();
4678 OStringBuffer aDA( 256 );
4679 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4680 sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
4681 aDA.append( ' ' );
4682 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4683 aDA.append( " 0 Tf" );
4684 rBox.m_aDAString = aDA.makeStringAndClear();
4685 rBox.m_aMKDict = "/CA";
4686 rBox.m_aMKDictCAString = "8";
4687 rBox.m_aRect = aCheckRect;
4689 // create appearance streams
4690 sal_Char cMark = '8';
4691 sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)];
4692 nCharXOffset *= aCheckRect.GetHeight();
4693 nCharXOffset /= 2000;
4694 sal_Int32 nCharYOffset = 1000-
4695 (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative
4696 nCharYOffset *= aCheckRect.GetHeight();
4697 nCharYOffset /= 2000;
4699 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
4700 beginRedirect( pCheckStream, aCheckRect );
4701 aDA.append( "/Tx BMC\nq BT\n" );
4702 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4703 aDA.append( ' ' );
4704 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4705 aDA.append( ' ' );
4706 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
4707 aDA.append( " Tf\n" );
4708 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
4709 aDA.append( " " );
4710 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
4711 aDA.append( " Td (" );
4712 aDA.append( cMark );
4713 aDA.append( ") Tj\nET\nQ\nEMC\n" );
4714 writeBuffer( aDA.getStr(), aDA.getLength() );
4715 endRedirect();
4716 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
4718 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
4719 beginRedirect( pUncheckStream, aCheckRect );
4720 writeBuffer( "/Tx BMC\nEMC\n", 12 );
4721 endRedirect();
4722 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
4725 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget )
4727 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4729 // save graphics state
4730 push( sal::static_int_cast<sal_uInt16>(~0U) );
4732 if( rWidget.Background || rWidget.Border )
4734 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
4735 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4736 drawRectangle( rBox.m_aRect );
4739 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
4740 setFont( aFont );
4741 Size aFontSize = aFont.GetSize();
4742 if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
4743 aFontSize.Height() = rBox.m_aRect.GetHeight();
4744 sal_Int32 nDelta = aFontSize.Height()/10;
4745 if( nDelta < 1 )
4746 nDelta = 1;
4748 Rectangle aCheckRect, aTextRect;
4749 if( rWidget.ButtonIsLeft )
4751 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
4752 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4753 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
4754 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4756 // #i74206# handle small controls without text area
4757 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4759 aCheckRect.Right() -= nDelta;
4760 aCheckRect.Top() += nDelta/2;
4761 aCheckRect.Bottom() -= nDelta - (nDelta/2);
4764 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
4765 aTextRect.Top() = rBox.m_aRect.Top();
4766 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4767 aTextRect.Bottom() = rBox.m_aRect.Bottom();
4769 else
4771 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
4772 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4773 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
4774 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4776 // #i74206# handle small controls without text area
4777 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4779 aCheckRect.Left() += nDelta;
4780 aCheckRect.Top() += nDelta/2;
4781 aCheckRect.Bottom() -= nDelta - (nDelta/2);
4784 aTextRect.Left() = rBox.m_aRect.Left();
4785 aTextRect.Top() = rBox.m_aRect.Top();
4786 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4787 aTextRect.Bottom() = rBox.m_aRect.Bottom();
4789 setLineColor( Color( COL_BLACK ) );
4790 setFillColor( Color( COL_TRANSPARENT ) );
4791 OStringBuffer aLW( 32 );
4792 aLW.append( "q " );
4793 m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
4794 aLW.append( " w " );
4795 writeBuffer( aLW.getStr(), aLW.getLength() );
4796 drawEllipse( aCheckRect );
4797 writeBuffer( " Q\n", 3 );
4798 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4799 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
4801 pop();
4803 OStringBuffer aDA( 256 );
4804 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4805 sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
4806 aDA.append( ' ' );
4807 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4808 aDA.append( " 0 Tf" );
4809 rBox.m_aDAString = aDA.makeStringAndClear();
4810 //to encrypt this (el)
4811 rBox.m_aMKDict = "/CA";
4812 //after this assignement, to m_aMKDic cannot be added anything
4813 rBox.m_aMKDictCAString = "l";
4815 rBox.m_aRect = aCheckRect;
4817 // create appearance streams
4818 push( sal::static_int_cast<sal_uInt16>(~0U) );
4819 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
4821 beginRedirect( pCheckStream, aCheckRect );
4822 aDA.append( "/Tx BMC\nq BT\n" );
4823 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4824 aDA.append( ' ' );
4825 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4826 aDA.append( ' ' );
4827 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
4828 aDA.append( " Tf\n0 0 Td\nET\nQ\n" );
4829 writeBuffer( aDA.getStr(), aDA.getLength() );
4830 setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4831 setLineColor( Color( COL_TRANSPARENT ) );
4832 aCheckRect.Left() += 3*nDelta;
4833 aCheckRect.Top() += 3*nDelta;
4834 aCheckRect.Bottom() -= 3*nDelta;
4835 aCheckRect.Right() -= 3*nDelta;
4836 drawEllipse( aCheckRect );
4837 writeBuffer( "\nEMC\n", 5 );
4838 endRedirect();
4840 pop();
4841 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
4843 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
4844 beginRedirect( pUncheckStream, aCheckRect );
4845 writeBuffer( "/Tx BMC\nEMC\n", 12 );
4846 endRedirect();
4847 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
4850 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
4853 // TODO: check and insert default streams
4854 rtl::OString aStandardAppearance;
4855 switch( rWidget.m_eType )
4857 case PDFWriter::CheckBox:
4858 aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
4859 break;
4860 default:
4861 break;
4864 if( rWidget.m_aAppearances.size() )
4866 rAnnotDict.append( "/AP<<\n" );
4867 for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it )
4869 rAnnotDict.append( "/" );
4870 rAnnotDict.append( dict_it->first );
4871 bool bUseSubDict = (dict_it->second.size() > 1);
4872 rAnnotDict.append( bUseSubDict ? "<<" : " " );
4874 for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin();
4875 stream_it != dict_it->second.end(); ++stream_it )
4877 SvMemoryStream* pApppearanceStream = stream_it->second;
4878 dict_it->second[ stream_it->first ] = NULL;
4880 bool bDeflate = compressStream( pApppearanceStream );
4882 pApppearanceStream->Seek( STREAM_SEEK_TO_END );
4883 sal_Int64 nStreamLen = pApppearanceStream->Tell();
4884 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
4885 sal_Int32 nObject = createObject();
4886 CHECK_RETURN( updateObject( nObject ) );
4887 #if OSL_DEBUG_LEVEL > 1
4889 OStringBuffer aLine( " PDFWriterImpl::emitAppearances" );
4890 emitComment( aLine.getStr() );
4892 #endif
4893 OStringBuffer aLine;
4894 aLine.append( nObject );
4896 aLine.append( " 0 obj\n"
4897 "<</Type/XObject\n"
4898 "/Subtype/Form\n"
4899 "/BBox[0 0 " );
4900 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
4901 aLine.append( " " );
4902 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
4903 aLine.append( "]\n"
4904 "/Resources " );
4905 aLine.append( getResourceDictObj() );
4906 aLine.append( " 0 R\n"
4907 "/Length " );
4908 aLine.append( nStreamLen );
4909 aLine.append( "\n" );
4910 if( bDeflate )
4911 aLine.append( "/Filter/FlateDecode\n" );
4912 aLine.append( ">>\nstream\n" );
4913 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4914 checkAndEnableStreamEncryption( nObject );
4915 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
4916 disableStreamEncryption();
4917 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
4919 if( bUseSubDict )
4921 rAnnotDict.append( " /" );
4922 rAnnotDict.append( stream_it->first );
4923 rAnnotDict.append( " " );
4925 rAnnotDict.append( nObject );
4926 rAnnotDict.append( " 0 R" );
4928 delete pApppearanceStream;
4931 rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
4933 rAnnotDict.append( ">>\n" );
4934 if( aStandardAppearance.getLength() )
4936 rAnnotDict.append( "/AS /" );
4937 rAnnotDict.append( aStandardAppearance );
4938 rAnnotDict.append( "\n" );
4942 return true;
4945 bool PDFWriterImpl::emitWidgetAnnotations()
4947 ensureUniqueRadioOnValues();
4949 int nAnnots = m_aWidgets.size();
4950 for( int a = 0; a < nAnnots; a++ )
4952 PDFWidget& rWidget = m_aWidgets[a];
4954 OStringBuffer aLine( 1024 );
4955 OStringBuffer aValue( 256 );
4956 aLine.append( rWidget.m_nObject );
4957 aLine.append( " 0 obj\n"
4958 "<<" );
4959 // emit widget annotation only for terminal fields
4960 if( rWidget.m_aKids.empty() )
4962 aLine.append( "/Type/Annot/Subtype/Widget/F 4\n"
4963 "/Rect[" );
4964 appendFixedInt( rWidget.m_aRect.Left()-1, aLine );
4965 aLine.append( ' ' );
4966 appendFixedInt( rWidget.m_aRect.Top()+1, aLine );
4967 aLine.append( ' ' );
4968 appendFixedInt( rWidget.m_aRect.Right()+1, aLine );
4969 aLine.append( ' ' );
4970 appendFixedInt( rWidget.m_aRect.Bottom()-1, aLine );
4971 aLine.append( "]\n" );
4973 aLine.append( "/FT/" );
4974 switch( rWidget.m_eType )
4976 case PDFWriter::RadioButton:
4977 case PDFWriter::CheckBox:
4978 // for radio buttons only the RadioButton field, not the
4979 // CheckBox children should have a value, else acrobat reader
4980 // does not always check the right button
4981 // of course real check boxes (not belonging to a readio group)
4982 // need their values, too
4983 if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
4985 aValue.append( "/" );
4986 // check for radio group with all buttons unpressed
4987 if( rWidget.m_aValue.getLength() == 0 )
4988 aValue.append( "Off" );
4989 else
4990 appendName( rWidget.m_aValue, aValue );
4992 case PDFWriter::PushButton:
4993 aLine.append( "Btn" );
4994 break;
4995 case PDFWriter::ListBox:
4996 if( rWidget.m_nFlags & 0x200000 ) // multiselect
4998 aValue.append( "[" );
4999 for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
5001 sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
5002 if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
5003 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
5005 aValue.append( "]" );
5007 else if( rWidget.m_aSelectedEntries.size() > 0 &&
5008 rWidget.m_aSelectedEntries[0] >= 0 &&
5009 rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
5011 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
5013 else
5014 appendUnicodeTextStringEncrypt( rtl::OUString(), rWidget.m_nObject, aValue );
5015 aLine.append( "Ch" );
5016 break;
5017 case PDFWriter::ComboBox:
5018 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5019 aLine.append( "Ch" );
5020 break;
5021 case PDFWriter::Edit:
5022 aLine.append( "Tx" );
5023 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5024 break;
5026 aLine.append( "\n" );
5027 aLine.append( "/P " );
5028 aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
5029 aLine.append( " 0 R\n" );
5031 if( rWidget.m_nParent )
5033 aLine.append( "/Parent " );
5034 aLine.append( rWidget.m_nParent );
5035 aLine.append( " 0 R\n" );
5037 if( rWidget.m_aKids.size() )
5039 aLine.append( "/Kids[" );
5040 for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ )
5042 aLine.append( rWidget.m_aKids[i] );
5043 aLine.append( " 0 R" );
5044 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5046 aLine.append( "]\n" );
5048 if( rWidget.m_aName.getLength() )
5050 aLine.append( "/T" );
5051 appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
5052 aLine.append( "\n" );
5054 if( m_aContext.Version > PDFWriter::PDF_1_2 )
5056 // the alternate field name should be unicode able since it is
5057 // supposed to be used in UI
5058 aLine.append( "/TU" );
5059 appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
5060 aLine.append( "\n" );
5063 if( rWidget.m_nFlags )
5065 aLine.append( "/Ff " );
5066 aLine.append( rWidget.m_nFlags );
5067 aLine.append( "\n" );
5069 if( aValue.getLength() )
5071 OString aVal = aValue.makeStringAndClear();
5072 aLine.append( "/V " );
5073 aLine.append( aVal );
5074 aLine.append( "\n"
5075 "/DV " );
5076 aLine.append( aVal );
5077 aLine.append( "\n" );
5079 if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
5081 sal_Int32 nTI = -1;
5082 aLine.append( "/Opt[\n" );
5083 sal_Int32 i = 0;
5084 for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i )
5086 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine );
5087 aLine.append( "\n" );
5088 if( *it == rWidget.m_aValue )
5089 nTI = i;
5091 aLine.append( "]\n" );
5092 if( nTI > 0 )
5094 aLine.append( "/TI " );
5095 aLine.append( nTI );
5096 aLine.append( "\n" );
5097 if( rWidget.m_nFlags & 0x200000 ) // Multiselect
5099 aLine.append( "/I [" );
5100 aLine.append( nTI );
5101 aLine.append( "]\n" );
5105 if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
5107 aLine.append( "/MaxLen " );
5108 aLine.append( rWidget.m_nMaxLen );
5109 aLine.append( "\n" );
5111 if( rWidget.m_eType == PDFWriter::PushButton )
5113 if(!m_bIsPDF_A1)
5115 OStringBuffer aDest;
5116 if( appendDest( rWidget.m_nDest, aDest ) )
5118 aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
5119 aLine.append( aDest.makeStringAndClear() );
5120 aLine.append( ">>>>\n" );
5122 else if( rWidget.m_aListEntries.empty() )
5124 // create a reset form action
5125 aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
5127 else if( rWidget.m_bSubmit )
5129 // create a submit form action
5130 aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
5131 appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine );
5132 aLine.append( "/Flags " );
5134 sal_Int32 nFlags = 0;
5135 switch( m_aContext.SubmitFormat )
5137 case PDFWriter::HTML:
5138 nFlags |= 4;
5139 break;
5140 case PDFWriter::XML:
5141 if( m_aContext.Version > PDFWriter::PDF_1_3 )
5142 nFlags |= 32;
5143 break;
5144 case PDFWriter::PDF:
5145 if( m_aContext.Version > PDFWriter::PDF_1_3 )
5146 nFlags |= 256;
5147 break;
5148 case PDFWriter::FDF:
5149 default:
5150 break;
5152 if( rWidget.m_bSubmitGet )
5153 nFlags |= 8;
5154 aLine.append( nFlags );
5155 aLine.append( ">>>>\n" );
5157 else
5159 // create a URI action
5160 aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
5161 aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
5162 aLine.append( ")>>>>\n" );
5165 else
5166 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA );
5168 if( rWidget.m_aDAString.getLength() )
5170 aLine.append( "/DR<</Font<<" );
5171 appendBuiltinFontsToDict( aLine );
5172 aLine.append( ">>>>\n" );
5173 aLine.append( "/DA" );
5174 appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
5175 aLine.append( "\n" );
5176 if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER )
5177 aLine.append( "/Q 1\n" );
5178 else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT )
5179 aLine.append( "/Q 2\n" );
5181 // appearance charactristics for terminal fields
5182 // which are supposed to have an appearance constructed
5183 // by the viewer application
5184 if( rWidget.m_aMKDict.getLength() )
5186 aLine.append( "/MK<<" );
5187 aLine.append( rWidget.m_aMKDict );
5188 //add the CA string, encrypting it
5189 appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
5190 aLine.append( ">>\n" );
5193 CHECK_RETURN( emitAppearances( rWidget, aLine ) );
5195 aLine.append( ">>\n"
5196 "endobj\n\n" );
5197 CHECK_RETURN( updateObject( rWidget.m_nObject ) );
5198 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5200 return true;
5203 bool PDFWriterImpl::emitAnnotations()
5205 if( m_aPages.size() < 1 )
5206 return false;
5208 CHECK_RETURN( emitLinkAnnotations() );
5210 CHECK_RETURN( emitNoteAnnotations() );
5212 CHECK_RETURN( emitWidgetAnnotations() );
5214 return true;
5217 #undef CHECK_RETURN
5218 #define CHECK_RETURN( x ) if( !x ) return false
5220 bool PDFWriterImpl::emitCatalog()
5222 // build page tree
5223 // currently there is only one node that contains all leaves
5225 // first create a page tree node id
5226 sal_Int32 nTreeNode = createObject();
5228 // emit global resource dictionary (page emit needs it)
5229 CHECK_RETURN( emitResources() );
5231 // emit all pages
5232 for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it )
5233 if( ! it->emit( nTreeNode ) )
5234 return false;
5236 sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
5238 sal_Int32 nOutlineDict = emitOutline();
5240 //emit Output intent i59651
5241 sal_Int32 nOutputIntentObject = emitOutputIntent();
5243 //emit metadata
5244 sal_Int32 nMetadataObject = emitDocumentMetadata();
5246 sal_Int32 nStructureDict = 0;
5247 if(m_aStructure.size() > 1)
5249 ///check if dummy structure containers are needed
5250 addInternalStructureContainer(m_aStructure[0]);
5251 nStructureDict = m_aStructure[0].m_nObject = createObject();
5252 emitStructure( m_aStructure[ 0 ] );
5255 // adjust tree node file offset
5256 if( ! updateObject( nTreeNode ) )
5257 return false;
5259 // emit tree node
5260 OStringBuffer aLine( 2048 );
5261 aLine.append( nTreeNode );
5262 aLine.append( " 0 obj\n" );
5263 aLine.append( "<</Type/Pages\n" );
5264 aLine.append( "/Resources " );
5265 aLine.append( getResourceDictObj() );
5266 aLine.append( " 0 R\n" );
5268 switch( m_eInheritedOrientation )
5270 case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
5271 case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
5273 case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant
5274 case PDFWriter::Portrait:
5275 default:
5276 break;
5278 sal_Int32 nMediaBoxWidth = 0;
5279 sal_Int32 nMediaBoxHeight = 0;
5280 if( m_aPages.empty() ) // sanity check, this should not happen
5282 nMediaBoxWidth = m_nInheritedPageWidth;
5283 nMediaBoxHeight = m_nInheritedPageHeight;
5285 else
5287 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter )
5289 if( iter->m_nPageWidth > nMediaBoxWidth )
5290 nMediaBoxWidth = iter->m_nPageWidth;
5291 if( iter->m_nPageHeight > nMediaBoxHeight )
5292 nMediaBoxHeight = iter->m_nPageHeight;
5295 aLine.append( "/MediaBox[ 0 0 " );
5296 aLine.append( nMediaBoxWidth );
5297 aLine.append( ' ' );
5298 aLine.append( nMediaBoxHeight );
5299 aLine.append( " ]\n"
5300 "/Kids[ " );
5301 unsigned int i = 0;
5302 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ )
5304 aLine.append( iter->m_nPageObject );
5305 aLine.append( " 0 R" );
5306 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5308 aLine.append( "]\n"
5309 "/Count " );
5310 aLine.append( (sal_Int32)m_aPages.size() );
5311 aLine.append( ">>\n"
5312 "endobj\n\n" );
5313 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5315 // emit annotation objects
5316 CHECK_RETURN( emitAnnotations() );
5318 // emit Catalog
5319 m_nCatalogObject = createObject();
5320 if( ! updateObject( m_nCatalogObject ) )
5321 return false;
5322 aLine.setLength( 0 );
5323 aLine.append( m_nCatalogObject );
5324 aLine.append( " 0 obj\n"
5325 "<</Type/Catalog/Pages " );
5326 aLine.append( nTreeNode );
5327 aLine.append( " 0 R\n" );
5328 //--->i56629
5329 //check if there are named destinations to emit (root must be inside the catalog)
5330 if( nNamedDestinationsDictionary )
5332 aLine.append("/Dests ");
5333 aLine.append( nNamedDestinationsDictionary );
5334 aLine.append( " 0 R\n" );
5336 //<----
5337 if( m_aContext.PageLayout != PDFWriter::DefaultLayout )
5338 switch( m_aContext.PageLayout )
5340 default :
5341 case PDFWriter::SinglePage :
5342 aLine.append( "/PageLayout/SinglePage\n" );
5343 break;
5344 case PDFWriter::Continuous :
5345 aLine.append( "/PageLayout/OneColumn\n" );
5346 break;
5347 case PDFWriter::ContinuousFacing :
5348 //the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5349 aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5350 break;
5352 if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode )
5353 switch( m_aContext.PDFDocumentMode )
5355 default :
5356 aLine.append( "/PageMode/UseNone\n" );
5357 break;
5358 case PDFWriter::UseOutlines :
5359 aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5360 break;
5361 case PDFWriter::UseThumbs :
5362 aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5363 break;
5365 else if( m_aContext.OpenInFullScreenMode )
5366 aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5368 OStringBuffer aInitPageRef;
5369 if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() )
5371 aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5372 aInitPageRef.append( " 0 R" );
5374 else
5375 aInitPageRef.append( "0" );
5376 switch( m_aContext.PDFDocumentAction )
5378 case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default
5379 default:
5380 if( aInitPageRef.getLength() > 1 )
5382 aLine.append( "/OpenAction[" );
5383 aLine.append( aInitPageRef );
5384 aLine.append( " /XYZ null null 0]\n" );
5386 break;
5387 case PDFWriter::FitInWindow :
5388 aLine.append( "/OpenAction[" );
5389 aLine.append( aInitPageRef );
5390 aLine.append( " /Fit]\n" ); //Open fit page
5391 break;
5392 case PDFWriter::FitWidth :
5393 aLine.append( "/OpenAction[" );
5394 aLine.append( aInitPageRef );
5395 aLine.append( " /FitH " );
5396 aLine.append( m_nInheritedPageHeight );//Open fit width
5397 aLine.append( "]\n" );
5398 break;
5399 case PDFWriter::FitVisible :
5400 aLine.append( "/OpenAction[" );
5401 aLine.append( aInitPageRef );
5402 aLine.append( " /FitBH " );
5403 aLine.append( m_nInheritedPageHeight );//Open fit visible
5404 aLine.append( "]\n" );
5405 break;
5406 case PDFWriter::ActionZoom :
5407 aLine.append( "/OpenAction[" );
5408 aLine.append( aInitPageRef );
5409 aLine.append( " /XYZ null null " );
5410 if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5411 aLine.append( (double)m_aContext.Zoom/100.0 );
5412 else
5413 aLine.append( "0" );
5414 aLine.append( "]\n" );
5415 break;
5417 // viewer preferences, if we had some, then emit
5418 if( m_aContext.HideViewerToolbar ||
5419 ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aDocInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) ||
5420 m_aContext.HideViewerMenubar ||
5421 m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
5422 m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
5423 m_aContext.OpenInFullScreenMode )
5425 aLine.append( "/ViewerPreferences<<" );
5426 if( m_aContext.HideViewerToolbar )
5427 aLine.append( "/HideToolbar true\n" );
5428 if( m_aContext.HideViewerMenubar )
5429 aLine.append( "/HideMenubar true\n" );
5430 if( m_aContext.HideViewerWindowControls )
5431 aLine.append( "/HideWindowUI true\n" );
5432 if( m_aContext.FitWindow )
5433 aLine.append( "/FitWindow true\n" );
5434 if( m_aContext.CenterWindow )
5435 aLine.append( "/CenterWindow true\n" );
5436 if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aDocInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle )
5437 aLine.append( "/DisplayDocTitle true\n" );
5438 if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing )
5439 aLine.append( "/Direction/R2L\n" );
5440 if( m_aContext.OpenInFullScreenMode )
5441 switch( m_aContext.PDFDocumentMode )
5443 default :
5444 case PDFWriter::ModeDefault :
5445 aLine.append( "/NonFullScreenPageMode/UseNone\n" );
5446 break;
5447 case PDFWriter::UseOutlines :
5448 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
5449 break;
5450 case PDFWriter::UseThumbs :
5451 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
5452 break;
5454 aLine.append( ">>\n" );
5457 if( nOutlineDict )
5459 aLine.append( "/Outlines " );
5460 aLine.append( nOutlineDict );
5461 aLine.append( " 0 R\n" );
5463 if( nStructureDict )
5465 aLine.append( "/StructTreeRoot " );
5466 aLine.append( nStructureDict );
5467 aLine.append( " 0 R\n" );
5469 if( m_aContext.DocumentLocale.Language.getLength() > 0 )
5471 OUStringBuffer aLocBuf( 16 );
5472 aLocBuf.append( m_aContext.DocumentLocale.Language.toAsciiLowerCase() );
5473 if( m_aContext.DocumentLocale.Country.getLength() > 0 )
5475 aLocBuf.append( sal_Unicode('-') );
5476 aLocBuf.append( m_aContext.DocumentLocale.Country );
5478 aLine.append( "/Lang" );
5479 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
5480 aLine.append( "\n" );
5482 if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 )
5484 aLine.append( "/MarkInfo<</Marked true>>\n" );
5486 if( m_aWidgets.size() > 0 )
5488 aLine.append( "/AcroForm<</Fields[\n" );
5489 int nWidgets = m_aWidgets.size();
5490 int nOut = 0;
5491 for( int j = 0; j < nWidgets; j++ )
5493 // output only root fields
5494 if( m_aWidgets[j].m_nParent < 1 )
5496 aLine.append( m_aWidgets[j].m_nObject );
5497 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
5500 aLine.append( "\n]/DR " );
5501 aLine.append( getResourceDictObj() );
5502 aLine.append( " 0 R" );
5503 if( m_bIsPDF_A1 )
5504 aLine.append( ">>\n" );
5505 else
5506 aLine.append( "/NeedAppearances true>>\n" );
5508 //--->i59651
5509 //check if there is a Metadata object
5510 if( nOutputIntentObject )
5512 aLine.append("/OutputIntents[");
5513 aLine.append( nOutputIntentObject );
5514 aLine.append( " 0 R]" );
5516 if( nMetadataObject )
5518 aLine.append("/Metadata ");
5519 aLine.append( nMetadataObject );
5520 aLine.append( " 0 R" );
5522 //<----
5523 aLine.append( ">>\n"
5524 "endobj\n\n" );
5525 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5527 return true;
5530 sal_Int32 PDFWriterImpl::emitInfoDict( )
5532 sal_Int32 nObject = createObject();
5534 if( updateObject( nObject ) )
5536 OStringBuffer aLine( 1024 );
5537 aLine.append( nObject );
5538 aLine.append( " 0 obj\n"
5539 "<<" );
5540 if( m_aDocInfo.Title.Len() )
5542 aLine.append( "/Title" );
5543 appendUnicodeTextStringEncrypt( m_aDocInfo.Title, nObject, aLine );
5544 aLine.append( "\n" );
5546 if( m_aDocInfo.Author.Len() )
5548 aLine.append( "/Author" );
5549 appendUnicodeTextStringEncrypt( m_aDocInfo.Author, nObject, aLine );
5550 aLine.append( "\n" );
5552 if( m_aDocInfo.Subject.Len() )
5554 aLine.append( "/Subject" );
5555 appendUnicodeTextStringEncrypt( m_aDocInfo.Subject, nObject, aLine );
5556 aLine.append( "\n" );
5558 if( m_aDocInfo.Keywords.Len() )
5560 aLine.append( "/Keywords" );
5561 appendUnicodeTextStringEncrypt( m_aDocInfo.Keywords, nObject, aLine );
5562 aLine.append( "\n" );
5564 if( m_aDocInfo.Creator.Len() )
5566 aLine.append( "/Creator" );
5567 appendUnicodeTextStringEncrypt( m_aDocInfo.Creator, nObject, aLine );
5568 aLine.append( "\n" );
5570 if( m_aDocInfo.Producer.Len() )
5572 aLine.append( "/Producer" );
5573 appendUnicodeTextStringEncrypt( m_aDocInfo.Producer, nObject, aLine );
5574 aLine.append( "\n" );
5577 aLine.append( "/CreationDate" );
5578 appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine );
5579 aLine.append( ">>\nendobj\n\n" );
5580 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
5581 nObject = 0;
5583 else
5584 nObject = 0;
5586 return nObject;
5589 //--->i56629
5590 // Part of this function may be shared with method appendDest.
5592 sal_Int32 PDFWriterImpl::emitNamedDestinations()
5594 sal_Int32 nCount = m_aNamedDests.size();
5595 if( nCount <= 0 )
5596 return 0;//define internal error
5598 //get the object number for all the destinations
5599 sal_Int32 nObject = createObject();
5601 if( updateObject( nObject ) )
5603 //emit the dictionary
5604 OStringBuffer aLine( 1024 );
5605 aLine.append( nObject );
5606 aLine.append( " 0 obj\n"
5607 "<<" );
5609 sal_Int32 nDestID;
5610 for( nDestID = 0; nDestID < nCount; nDestID++ )
5612 const PDFNamedDest& rDest = m_aNamedDests[ nDestID ];
5613 // In order to correctly function both under an Internet browser and
5614 // directly with a reader (provided the reader has the feature) we
5615 // need to set the name of the destination the same way it will be encoded
5616 // in an Internet link
5617 INetURLObject aLocalURL(
5618 OUString( RTL_CONSTASCII_USTRINGPARAM( "http://ahost.ax" ) ) ); //dummy location, won't be used
5619 aLocalURL.SetMark( rDest.m_aDestName );
5621 const rtl::OUString aName = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as
5622 // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
5623 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
5625 aLine.append( '/' );
5626 appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
5627 aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
5628 //maps the preceeding character properly
5629 aLine.append( rDestPage.m_nPageObject );
5630 aLine.append( " 0 R" );
5632 switch( rDest.m_eType )
5634 case PDFWriter::XYZ:
5635 default:
5636 aLine.append( "/XYZ " );
5637 appendFixedInt( rDest.m_aRect.Left(), aLine );
5638 aLine.append( ' ' );
5639 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5640 aLine.append( " 0" );
5641 break;
5642 case PDFWriter::Fit:
5643 aLine.append( "/Fit" );
5644 break;
5645 case PDFWriter::FitRectangle:
5646 aLine.append( "/FitR " );
5647 appendFixedInt( rDest.m_aRect.Left(), aLine );
5648 aLine.append( ' ' );
5649 appendFixedInt( rDest.m_aRect.Top(), aLine );
5650 aLine.append( ' ' );
5651 appendFixedInt( rDest.m_aRect.Right(), aLine );
5652 aLine.append( ' ' );
5653 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5654 break;
5655 case PDFWriter::FitHorizontal:
5656 aLine.append( "/FitH " );
5657 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5658 break;
5659 case PDFWriter::FitVertical:
5660 aLine.append( "/FitV " );
5661 appendFixedInt( rDest.m_aRect.Left(), aLine );
5662 break;
5663 case PDFWriter::FitPageBoundingBox:
5664 aLine.append( "/FitB" );
5665 break;
5666 case PDFWriter::FitPageBoundingBoxHorizontal:
5667 aLine.append( "/FitBH " );
5668 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5669 break;
5670 case PDFWriter::FitPageBoundingBoxVertical:
5671 aLine.append( "/FitBV " );
5672 appendFixedInt( rDest.m_aRect.Left(), aLine );
5673 break;
5675 aLine.append( "]\n" );
5677 //close
5679 aLine.append( ">>\nendobj\n\n" );
5680 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
5681 nObject = 0;
5683 else
5684 nObject = 0;
5686 return nObject;
5688 //<--- i56629
5690 //--->i59651
5691 // emits the output intent dictionary
5693 sal_Int32 PDFWriterImpl::emitOutputIntent()
5695 if( !m_bIsPDF_A1 )
5696 return 0;
5698 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
5700 OStringBuffer aLine( 1024 );
5701 sal_Int32 nICCObject = createObject();
5702 sal_Int32 nStreamLengthObject = createObject();
5704 aLine.append( nICCObject );
5705 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
5706 aLine.append( " 0 obj\n<</N 3/Length " );
5707 aLine.append( nStreamLengthObject );
5708 aLine.append( " 0 R" );
5709 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
5710 aLine.append( "/Filter/FlateDecode" );
5711 #endif
5712 aLine.append( ">>\nstream\n" );
5713 CHECK_RETURN( updateObject( nICCObject ) );
5714 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5715 //get file position
5716 sal_uInt64 nBeginStreamPos = 0;
5717 osl_getFilePos( m_aFile, &nBeginStreamPos );
5718 beginCompression();
5719 checkAndEnableStreamEncryption( nICCObject );
5720 sal_Int32 nStreamSize = writeBuffer( nsRGB_ICC_profile, (sal_Int32) sizeof( nsRGB_ICC_profile ) );
5721 disableStreamEncryption();
5722 endCompression();
5723 sal_uInt64 nEndStreamPos = 0;
5724 osl_getFilePos( m_aFile, &nEndStreamPos );
5726 if( nStreamSize == 0 )
5727 return 0;
5728 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
5729 return 0 ;
5730 aLine.setLength( 0 );
5732 //emit the stream length object
5733 CHECK_RETURN( updateObject( nStreamLengthObject ) );
5734 aLine.setLength( 0 );
5735 aLine.append( nStreamLengthObject );
5736 aLine.append( " 0 obj\n" );
5737 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
5738 aLine.append( "\nendobj\n\n" );
5739 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5740 aLine.setLength( 0 );
5742 //emit the OutputIntent dictionary
5743 sal_Int32 nOIObject = createObject();
5744 CHECK_RETURN( updateObject( nOIObject ) );
5745 aLine.append( nOIObject );
5746 aLine.append( " 0 obj\n"
5747 "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
5749 rtl::OUString aComment( RTL_CONSTASCII_USTRINGPARAM( "sRGB IEC61966-2.1" ) );
5750 appendLiteralStringEncrypt( aComment ,nOIObject, aLine );
5751 aLine.append("/DestOutputProfile ");
5752 aLine.append( nICCObject );
5753 aLine.append( " 0 R>>\nendobj\n\n" );;
5754 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5756 return nOIObject;
5759 // formats the string for the XML stream
5760 static void escapeStringXML( const rtl::OUString& rStr, rtl::OUString &rValue)
5762 const sal_Unicode* pUni = rStr.getStr();
5763 int nLen = rStr.getLength();
5764 for( ; nLen; nLen--, pUni++ )
5766 switch( *pUni )
5768 case sal_Unicode('&'):
5769 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&amp;" ) );
5770 break;
5771 case sal_Unicode('<'):
5772 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&lt;" ) );
5773 break;
5774 case sal_Unicode('>'):
5775 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&gt;" ) );
5776 break;
5777 case sal_Unicode('\''):
5778 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&apos;" ) );
5779 break;
5780 case sal_Unicode('"'):
5781 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&quot;" ) );
5782 break;
5783 default:
5784 rValue += rtl::OUString( *pUni );
5785 break;
5790 // emits the document metadata
5792 sal_Int32 PDFWriterImpl::emitDocumentMetadata()
5794 if( !m_bIsPDF_A1 )
5795 return 0;
5797 //get the object number for all the destinations
5798 sal_Int32 nObject = createObject();
5800 if( updateObject( nObject ) )
5802 // the following string are written in UTF-8 unicode
5803 OStringBuffer aMetadataStream( 8192 );
5805 aMetadataStream.append( "<?xpacket begin=\"" );
5806 // this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used
5807 // as a byte-order marker.
5808 aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) );
5809 aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
5810 aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
5811 aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
5812 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
5813 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
5814 aMetadataStream.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
5815 aMetadataStream.append( " <pdfaid:part>1</pdfaid:part>\n" );
5816 aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" );
5817 aMetadataStream.append( " </rdf:Description>\n" );
5818 //... Dublin Core properties go here
5819 if( m_aDocInfo.Title.Len() ||
5820 m_aDocInfo.Author.Len() ||
5821 m_aDocInfo.Subject.Len() )
5823 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
5824 aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
5825 if( m_aDocInfo.Title.Len() )
5827 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
5828 aMetadataStream.append( " <dc:title>\n" );
5829 aMetadataStream.append( " <rdf:Alt>\n" );
5830 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
5831 rtl::OUString aTitle;
5832 escapeStringXML( m_aDocInfo.Title, aTitle );
5833 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) );
5834 aMetadataStream.append( "</rdf:li>\n" );
5835 aMetadataStream.append( " </rdf:Alt>\n" );
5836 aMetadataStream.append( " </dc:title>\n" );
5838 if( m_aDocInfo.Author.Len() )
5840 aMetadataStream.append( " <dc:creator>\n" );
5841 aMetadataStream.append( " <rdf:Seq>\n" );
5842 aMetadataStream.append( " <rdf:li>" );
5843 rtl::OUString aAuthor;
5844 escapeStringXML( m_aDocInfo.Author, aAuthor );
5845 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) );
5846 aMetadataStream.append( "</rdf:li>\n" );
5847 aMetadataStream.append( " </rdf:Seq>\n" );
5848 aMetadataStream.append( " </dc:creator>\n" );
5850 if( m_aDocInfo.Subject.Len() )
5852 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
5853 aMetadataStream.append( " <dc:description>\n" );
5854 aMetadataStream.append( " <rdf:Alt>\n" );
5855 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
5856 rtl::OUString aSubject;
5857 escapeStringXML( m_aDocInfo.Subject, aSubject );
5858 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) );
5859 aMetadataStream.append( "</rdf:li>\n" );
5860 aMetadataStream.append( " </rdf:Alt>\n" );
5861 aMetadataStream.append( " </dc:description>\n" );
5863 aMetadataStream.append( " </rdf:Description>\n" );
5866 //... PDF properties go here
5867 if( m_aDocInfo.Producer.Len() ||
5868 m_aDocInfo.Keywords.Len() )
5870 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
5871 aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
5872 if( m_aDocInfo.Producer.Len() )
5874 aMetadataStream.append( " <pdf:Producer>" );
5875 rtl::OUString aProducer;
5876 escapeStringXML( m_aDocInfo.Producer, aProducer );
5877 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) );
5878 aMetadataStream.append( "</pdf:Producer>\n" );
5880 if( m_aDocInfo.Keywords.Len() )
5882 aMetadataStream.append( " <pdf:Keywords>" );
5883 rtl::OUString aKeywords;
5884 escapeStringXML( m_aDocInfo.Keywords, aKeywords );
5885 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) );
5886 aMetadataStream.append( "</pdf:Keywords>\n" );
5888 aMetadataStream.append( " </rdf:Description>\n" );
5891 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
5892 aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
5893 if( m_aDocInfo.Creator.Len() )
5895 aMetadataStream.append( " <xmp:CreatorTool>" );
5896 rtl::OUString aCreator;
5897 escapeStringXML( m_aDocInfo.Creator, aCreator );
5898 aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) );
5899 aMetadataStream.append( "</xmp:CreatorTool>\n" );
5901 //creation date
5902 aMetadataStream.append( " <xmp:CreateDate>" );
5903 aMetadataStream.append( m_aCreationMetaDateString );
5904 aMetadataStream.append( "</xmp:CreateDate>\n" );
5906 aMetadataStream.append( " </rdf:Description>\n" );
5907 aMetadataStream.append( " </rdf:RDF>\n" );
5908 aMetadataStream.append( "</x:xmpmeta>\n" );
5910 //add the padding
5911 for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ )
5913 aMetadataStream.append( " " );
5914 if( nSpaces % 100 == 0 )
5915 aMetadataStream.append( "\n" );
5918 aMetadataStream.append( "<?xpacket end=\"w\"?>\n" );
5920 OStringBuffer aMetadataObj( 1024 );
5922 aMetadataObj.append( nObject );
5923 aMetadataObj.append( " 0 obj\n" );
5925 aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
5927 aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() );
5928 aMetadataObj.append( ">>\nstream\n" );
5929 CHECK_RETURN( writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) );
5930 //emit the stream
5931 CHECK_RETURN( writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) );
5933 aMetadataObj.setLength( 0 );
5934 aMetadataObj.append( "\nendstream\nendobj\n\n" );
5935 if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
5936 nObject = 0;
5938 else
5939 nObject = 0;
5941 return nObject;
5943 //<---i59651
5945 bool PDFWriterImpl::emitTrailer()
5947 // emit doc info
5948 OString aInfoValuesOut;
5949 sal_Int32 nDocInfoObject = emitInfoDict( );
5951 sal_Int32 nSecObject = 0;
5953 if( m_aContext.Encrypt == true )
5955 //emit the security information
5956 //must be emitted as indirect dictionary object, since
5957 //Acrobat Reader 5 works only with this kind of implementation
5958 nSecObject = createObject();
5960 if( updateObject( nSecObject ) )
5962 OStringBuffer aLineS( 1024 );
5963 aLineS.append( nSecObject );
5964 aLineS.append( " 0 obj\n"
5965 "<</Filter/Standard/V " );
5966 // check the version
5967 if( m_aContext.Security128bit == true )
5968 aLineS.append( "2/Length 128/R 3" );
5969 else
5970 aLineS.append( "1/R 2" );
5972 // emit the owner password, must not be encrypted
5973 aLineS.append( "/O(" );
5974 appendLiteralString( (const sal_Char*)m_nEncryptedOwnerPassword, 32, aLineS );
5975 aLineS.append( ")/U(" );
5976 appendLiteralString( (const sal_Char*)m_nEncryptedUserPassword, 32, aLineS );
5977 aLineS.append( ")/P " );// the permission set
5978 aLineS.append( m_nAccessPermissions );
5979 aLineS.append( ">>\nendobj\n\n" );
5980 if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) )
5981 nSecObject = 0;
5983 else
5984 nSecObject = 0;
5986 // emit xref table
5987 // remember start
5988 sal_uInt64 nXRefOffset = 0;
5989 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) );
5990 CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
5992 sal_Int32 nObjects = m_aObjects.size();
5993 OStringBuffer aLine;
5994 aLine.append( "0 " );
5995 aLine.append( (sal_Int32)(nObjects+1) );
5996 aLine.append( "\n" );
5997 aLine.append( "0000000000 65535 f \n" );
5998 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6000 for( sal_Int32 i = 0; i < nObjects; i++ )
6002 aLine.setLength( 0 );
6003 OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] );
6004 for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
6005 aLine.append( '0' );
6006 aLine.append( aOffset );
6007 aLine.append( " 00000 n \n" );
6008 DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" );
6009 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6012 // prepare document checksum
6013 OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
6014 if( m_aDocDigest )
6016 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
6017 rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) );
6018 for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ )
6019 appendHex( nMD5Sum[i], aDocChecksum );
6021 // document id set in setDocInfo method
6022 // emit trailer
6023 aLine.setLength( 0 );
6024 aLine.append( "trailer\n"
6025 "<</Size " );
6026 aLine.append( (sal_Int32)(nObjects+1) );
6027 aLine.append( "/Root " );
6028 aLine.append( m_nCatalogObject );
6029 aLine.append( " 0 R\n" );
6030 if( nSecObject |= 0 )
6032 aLine.append( "/Encrypt ");
6033 aLine.append( nSecObject );
6034 aLine.append( " 0 R\n" );
6036 if( nDocInfoObject )
6038 aLine.append( "/Info " );
6039 aLine.append( nDocInfoObject );
6040 aLine.append( " 0 R\n" );
6042 if( m_aDocID.getLength() )
6044 aLine.append( "/ID [ <" );
6045 aLine.append( m_aDocID.getStr(), m_aDocID.getLength() );
6046 aLine.append( ">\n"
6047 "<" );
6048 aLine.append( m_aDocID.getStr(), m_aDocID.getLength() );
6049 aLine.append( "> ]\n" );
6051 if( aDocChecksum.getLength() )
6053 aLine.append( "/DocChecksum /" );
6054 aLine.append( aDocChecksum );
6055 aLine.append( "\n" );
6057 if( m_aAdditionalStreams.size() > 0 )
6059 aLine.append( "/AdditionalStreams [" );
6060 for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ )
6062 aLine.append( "/" );
6063 appendName( m_aAdditionalStreams[i].m_aMimeType, aLine );
6064 aLine.append( " " );
6065 aLine.append( m_aAdditionalStreams[i].m_nStreamObject );
6066 aLine.append( " 0 R\n" );
6068 aLine.append( "]\n" );
6070 aLine.append( ">>\n"
6071 "startxref\n" );
6072 aLine.append( (sal_Int64)nXRefOffset );
6073 aLine.append( "\n"
6074 "%%EOF\n" );
6075 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6077 return true;
6080 struct AnnotationSortEntry
6082 sal_Int32 nTabOrder;
6083 sal_Int32 nObject;
6084 sal_Int32 nWidgetIndex;
6086 AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
6087 nTabOrder( nTab ),
6088 nObject( nObj ),
6089 nWidgetIndex( nI )
6093 struct AnnotSortContainer
6095 std::set< sal_Int32 > aObjects;
6096 std::vector< AnnotationSortEntry > aSortedAnnots;
6099 struct AnnotSorterLess
6101 std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets;
6103 AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {}
6105 bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
6107 if( rLeft.nTabOrder < rRight.nTabOrder )
6108 return true;
6109 if( rRight.nTabOrder < rLeft.nTabOrder )
6110 return false;
6111 if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
6112 return false;
6113 if( rRight.nWidgetIndex < 0 )
6114 return true;
6115 if( rLeft.nWidgetIndex < 0 )
6116 return false;
6117 // remember: widget rects are in PDF coordinates, so they are ordered down up
6118 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
6119 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
6120 return true;
6121 if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
6122 m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
6123 return false;
6124 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
6125 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
6126 return true;
6127 return false;
6131 void PDFWriterImpl::sortWidgets()
6133 // sort widget annotations on each page as per their
6134 // TabOrder attribute
6135 std::hash_map< sal_Int32, AnnotSortContainer > sorted;
6136 int nWidgets = m_aWidgets.size();
6137 for( int nW = 0; nW < nWidgets; nW++ )
6139 const PDFWidget& rWidget = m_aWidgets[nW];
6140 AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
6141 // optimize vector allocation
6142 if( rCont.aSortedAnnots.empty() )
6143 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
6144 // insert widget to tab sorter
6145 // RadioButtons are not page annotations, only their individual check boxes are
6146 if( rWidget.m_eType != PDFWriter::RadioButton )
6148 rCont.aObjects.insert( rWidget.m_nObject );
6149 rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) );
6152 for( std::hash_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it )
6154 // append entries for non widget annotations
6155 PDFPage& rPage = m_aPages[ it->first ];
6156 unsigned int nAnnots = rPage.m_aAnnotations.size();
6157 for( unsigned int nA = 0; nA < nAnnots; nA++ )
6158 if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end())
6159 it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) );
6161 AnnotSorterLess aLess( m_aWidgets );
6162 std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess );
6163 // sanity check
6164 if( it->second.aSortedAnnots.size() == nAnnots)
6166 for( unsigned int nA = 0; nA < nAnnots; nA++ )
6167 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject;
6169 else
6171 DBG_ASSERT( 0, "wrong number of sorted annotations" );
6172 #if OSL_DEBUG_LEVEL > 0
6173 fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n"
6174 " %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots );
6175 #endif
6179 // FIXME: implement tab order in structure tree for PDF 1.5
6182 namespace vcl {
6183 class PDFStreamIf :
6184 public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream >
6186 PDFWriterImpl* m_pWriter;
6187 bool m_bWrite;
6188 public:
6189 PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
6190 virtual ~PDFStreamIf();
6192 virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw();
6193 virtual void SAL_CALL flush() throw();
6194 virtual void SAL_CALL closeOutput() throw();
6198 PDFStreamIf::~PDFStreamIf()
6202 void SAL_CALL PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw()
6204 if( m_bWrite )
6206 sal_Int32 nBytes = aData.getLength();
6207 if( nBytes > 0 )
6208 m_pWriter->writeBuffer( aData.getConstArray(), nBytes );
6212 void SAL_CALL PDFStreamIf::flush() throw()
6216 void SAL_CALL PDFStreamIf::closeOutput() throw()
6218 m_bWrite = false;
6221 bool PDFWriterImpl::emitAdditionalStreams()
6223 unsigned int nStreams = m_aAdditionalStreams.size();
6224 for( unsigned int i = 0; i < nStreams; i++ )
6226 PDFAddStream& rStream = m_aAdditionalStreams[i];
6227 rStream.m_nStreamObject = createObject();
6228 sal_Int32 nSizeObject = createObject();
6230 if( ! updateObject( rStream.m_nStreamObject ) )
6231 return false;
6233 OStringBuffer aLine;
6234 aLine.append( rStream.m_nStreamObject );
6235 aLine.append( " 0 obj\n<</Length " );
6236 aLine.append( nSizeObject );
6237 aLine.append( " 0 R" );
6238 if( rStream.m_bCompress )
6239 aLine.append( "/Filter/FlateDecode" );
6240 aLine.append( ">>\nstream\n" );
6241 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6242 return false;
6243 sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0;
6244 if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) )
6246 osl_closeFile( m_aFile );
6247 m_bOpen = false;
6249 if( rStream.m_bCompress )
6250 beginCompression();
6252 checkAndEnableStreamEncryption( rStream.m_nStreamObject );
6253 com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) );
6254 rStream.m_pStream->write( xStream );
6255 xStream.clear();
6256 delete rStream.m_pStream;
6257 rStream.m_pStream = NULL;
6258 disableStreamEncryption();
6260 if( rStream.m_bCompress )
6261 endCompression();
6263 if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) )
6265 osl_closeFile( m_aFile );
6266 m_bOpen = false;
6267 return false;
6269 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6270 return false ;
6271 // emit stream length object
6272 if( ! updateObject( nSizeObject ) )
6273 return false;
6274 aLine.setLength( 0 );
6275 aLine.append( nSizeObject );
6276 aLine.append( " 0 obj\n" );
6277 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6278 aLine.append( "\nendobj\n\n" );
6279 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6280 return false;
6282 return true;
6285 bool PDFWriterImpl::emit()
6287 endPage();
6289 // resort structure tree and annotations if necessary
6290 // needed for widget tab order
6291 sortWidgets();
6293 // emit additional streams
6294 CHECK_RETURN( emitAdditionalStreams() );
6296 // emit catalog
6297 CHECK_RETURN( emitCatalog() );
6299 // emit trailer
6300 CHECK_RETURN( emitTrailer() );
6302 osl_closeFile( m_aFile );
6303 m_bOpen = false;
6305 return true;
6308 std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors()
6310 return m_aErrors;
6314 void PDFWriterImpl::registerGlyphs( int nGlyphs,
6315 sal_GlyphId* pGlyphs,
6316 sal_Int32* pGlyphWidths,
6317 sal_Ucs* pUnicodes,
6318 sal_uInt8* pMappedGlyphs,
6319 sal_Int32* pMappedFontObjects,
6320 const ImplFontData* pFallbackFonts[] )
6322 const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6323 for( int i = 0; i < nGlyphs; i++ )
6325 const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB);
6326 const ImplFontData* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont;
6328 if( isBuiltinFont( pCurrentFont ) )
6330 sal_Int32 nFontID = 0;
6331 FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6332 if( it != m_aEmbeddedFonts.end() )
6333 nFontID = it->second.m_nNormalFontID;
6334 else
6336 nFontID = m_nNextFID++;
6337 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6338 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6341 pGlyphWidths[ i ] = 0;
6342 pMappedGlyphs[ i ] = sal::static_int_cast<sal_Int8>( nFontGlyphId );
6343 pMappedFontObjects[ i ] = nFontID;
6344 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pCurrentFont );
6345 if( pFD )
6347 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
6348 pGlyphWidths[i] = pBuiltinFont->m_aWidths[ nFontGlyphId & 0x00ff ];
6351 else if( pCurrentFont->mbSubsettable )
6353 FontSubset& rSubset = m_aSubsets[ pCurrentFont ];
6354 // search for font specific glyphID
6355 FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId );
6356 if( it != rSubset.m_aMapping.end() )
6358 pMappedFontObjects[i] = it->second.m_nFontID;
6359 pMappedGlyphs[i] = it->second.m_nSubsetGlyphID;
6361 else
6363 // create new subset if necessary
6364 if( rSubset.m_aSubsets.empty()
6365 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
6367 rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) );
6370 // copy font id
6371 pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID;
6372 // create new glyph in subset
6373 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
6374 pMappedGlyphs[i] = nNewId;
6376 // add new glyph to emitted font subset
6377 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
6378 rNewGlyphEmit.m_nSubsetGlyphID = nNewId;
6379 rNewGlyphEmit.m_aUnicode = (pUnicodes ? pUnicodes[i] : 0);
6381 // add new glyph to font mapping
6382 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
6383 rNewGlyph.m_nFontID = pMappedFontObjects[i];
6384 rNewGlyph.m_nSubsetGlyphID = nNewId;
6386 getReferenceDevice()->ImplGetGraphics();
6387 const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0);
6388 pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont,
6389 nFontGlyphId,
6390 bVertical,
6391 m_pReferenceDevice->mpGraphics );
6393 else if( pCurrentFont->IsEmbeddable() )
6395 sal_Int32 nFontID = 0;
6396 FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6397 if( it != m_aEmbeddedFonts.end() )
6398 nFontID = it->second.m_nNormalFontID;
6399 else
6401 nFontID = m_nNextFID++;
6402 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6403 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6405 EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont];
6407 const Ucs2SIntMap* pEncoding = NULL;
6408 const Ucs2OStrMap* pNonEncoded = NULL;
6409 getReferenceDevice()->ImplGetGraphics();
6410 pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded );
6412 Ucs2SIntMap::const_iterator enc_it;
6413 Ucs2OStrMap::const_iterator nonenc_it;
6415 sal_Int32 nCurFontID = nFontID;
6416 sal_Ucs cChar = pUnicodes[i];
6417 if( pEncoding )
6419 enc_it = pEncoding->find( cChar );
6420 if( enc_it != pEncoding->end() && enc_it->second > 0 )
6422 DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" );
6423 cChar = (sal_Ucs)enc_it->second;
6425 else if( (enc_it == pEncoding->end() || enc_it->second == -1) &&
6426 pNonEncoded &&
6427 (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() )
6429 nCurFontID = 0;
6430 // find non encoded glyph
6431 for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it )
6433 if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() )
6435 nCurFontID = nec_it->m_nFontID;
6436 cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ];
6437 break;
6440 if( nCurFontID == 0 ) // new nonencoded glyph
6442 if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 )
6444 rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() );
6445 rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++;
6447 EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back();
6448 rEncoding.m_aEncVector.push_back( EmbedCode() );
6449 rEncoding.m_aEncVector.back().m_aUnicode = cChar;
6450 rEncoding.m_aEncVector.back().m_aName = nonenc_it->second;
6451 rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1);
6452 nCurFontID = rEncoding.m_nFontID;
6453 cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ];
6456 else
6457 pEncoding = NULL;
6459 if( ! pEncoding )
6461 if( cChar & 0xff00 )
6463 // some characters can be used by conversion
6464 if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area
6465 cChar -= 0xf000;
6466 else
6468 String aString(cChar);
6469 ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 );
6470 cChar = ((sal_Ucs)aChar.GetChar( 0 )) & 0x00ff;
6475 pMappedGlyphs[ i ] = (sal_Int8)cChar;
6476 pMappedFontObjects[ i ] = nCurFontID;
6477 pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont,
6478 (pEncoding ? pUnicodes[i] : cChar) | GF_ISCHAR,
6479 false,
6480 m_pReferenceDevice->mpGraphics );
6485 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines )
6487 push( PUSH_ALL );
6489 FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief();
6491 Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor();
6492 Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
6493 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
6494 Color aReliefColor( COL_LIGHTGRAY );
6495 if( aTextColor == COL_BLACK )
6496 aTextColor = Color( COL_WHITE );
6497 if( aTextLineColor == COL_BLACK )
6498 aTextLineColor = Color( COL_WHITE );
6499 if( aOverlineColor == COL_BLACK )
6500 aOverlineColor = Color( COL_WHITE );
6501 if( aTextColor == COL_WHITE )
6502 aReliefColor = Color( COL_BLACK );
6504 Font aSetFont = m_aCurrentPDFState.m_aFont;
6505 aSetFont.SetRelief( RELIEF_NONE );
6506 aSetFont.SetShadow( FALSE );
6508 aSetFont.SetColor( aReliefColor );
6509 setTextLineColor( aReliefColor );
6510 setOverlineColor( aReliefColor );
6511 setFont( aSetFont );
6512 long nOff = 1 + getReferenceDevice()->mnDPIX/300;
6513 if( eRelief == RELIEF_ENGRAVED )
6514 nOff = -nOff;
6516 rLayout.DrawOffset() += Point( nOff, nOff );
6517 updateGraphicsState();
6518 drawLayout( rLayout, rText, bTextLines );
6520 rLayout.DrawOffset() -= Point( nOff, nOff );
6521 setTextLineColor( aTextLineColor );
6522 setOverlineColor( aOverlineColor );
6523 aSetFont.SetColor( aTextColor );
6524 setFont( aSetFont );
6525 updateGraphicsState();
6526 drawLayout( rLayout, rText, bTextLines );
6528 // clean up the mess
6529 pop();
6532 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines )
6534 Font aSaveFont = m_aCurrentPDFState.m_aFont;
6535 Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
6536 Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
6538 Font& rFont = m_aCurrentPDFState.m_aFont;
6539 if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 )
6540 rFont.SetColor( Color( COL_LIGHTGRAY ) );
6541 else
6542 rFont.SetColor( Color( COL_BLACK ) );
6543 rFont.SetShadow( FALSE );
6544 rFont.SetOutline( FALSE );
6545 setFont( rFont );
6546 setTextLineColor( rFont.GetColor() );
6547 setOverlineColor( rFont.GetColor() );
6548 updateGraphicsState();
6550 long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24);
6551 if( rFont.IsOutline() )
6552 nOff++;
6553 rLayout.DrawBase() += Point( nOff, nOff );
6554 drawLayout( rLayout, rText, bTextLines );
6555 rLayout.DrawBase() -= Point( nOff, nOff );
6557 setFont( aSaveFont );
6558 setTextLineColor( aSaveTextLineColor );
6559 setOverlineColor( aSaveOverlineColor );
6560 updateGraphicsState();
6563 void PDFWriterImpl::drawVerticalGlyphs(
6564 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
6565 OStringBuffer& rLine,
6566 const Point& rAlignOffset,
6567 const Matrix3& rRotScale,
6568 double fAngle,
6569 double fXScale,
6570 double fSkew,
6571 sal_Int32 nFontHeight )
6573 long nXOffset = 0;
6574 Point aCurPos( rGlyphs[0].m_aPos );
6575 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
6576 aCurPos += rAlignOffset;
6577 for( size_t i = 0; i < rGlyphs.size(); i++ )
6579 // have to emit each glyph on its own
6580 double fDeltaAngle = 0.0;
6581 double fYScale = 1.0;
6582 double fTempXScale = fXScale;
6583 double fSkewB = fSkew;
6584 double fSkewA = 0.0;
6586 Point aDeltaPos;
6587 if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL )
6589 fDeltaAngle = M_PI/2.0;
6590 aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent();
6591 aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale);
6592 fYScale = fXScale;
6593 fTempXScale = 1.0;
6594 fSkewA = -fSkewB;
6595 fSkewB = 0.0;
6597 else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR )
6599 fDeltaAngle = -M_PI/2.0;
6600 aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale);
6601 aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent();
6602 fYScale = fXScale;
6603 fTempXScale = 1.0;
6604 fSkewA = fSkewB;
6605 fSkewB = 0.0;
6607 aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) );
6608 if( i < rGlyphs.size()-1 )
6609 nXOffset += rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
6610 if( ! rGlyphs[i].m_nGlyphId )
6611 continue;
6613 aDeltaPos = rRotScale.transform( aDeltaPos );
6615 Matrix3 aMat;
6616 if( fSkewB != 0.0 || fSkewA != 0.0 )
6617 aMat.skew( fSkewA, fSkewB );
6618 aMat.scale( fTempXScale, fYScale );
6619 aMat.rotate( fAngle+fDeltaAngle );
6620 aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
6621 aMat.append( m_aPages.back(), rLine );
6622 rLine.append( " Tm" );
6623 if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
6625 rLine.append( " /F" );
6626 rLine.append( rGlyphs[i].m_nMappedFontId );
6627 rLine.append( ' ' );
6628 m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
6629 rLine.append( " Tf" );
6631 rLine.append( "<" );
6632 appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
6633 rLine.append( ">Tj\n" );
6637 void PDFWriterImpl::drawHorizontalGlyphs(
6638 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
6639 OStringBuffer& rLine,
6640 const Point& rAlignOffset,
6641 double fAngle,
6642 double fXScale,
6643 double fSkew,
6644 sal_Int32 nFontHeight,
6645 sal_Int32 nPixelFontHeight
6648 // horizontal (= normal) case
6650 // fill in run end indices
6651 // end is marked by index of the first glyph of the next run
6652 // a run is marked by same mapped font id and same Y position
6653 std::vector< sal_uInt32 > aRunEnds;
6654 aRunEnds.reserve( rGlyphs.size() );
6655 for( size_t i = 1; i < rGlyphs.size(); i++ )
6657 if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
6658 rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
6660 aRunEnds.push_back(i);
6663 // last run ends at last glyph
6664 aRunEnds.push_back( rGlyphs.size() );
6666 // loop over runs of the same font
6667 sal_uInt32 nBeginRun = 0;
6668 for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
6670 // setup text matrix
6671 Point aCurPos = rGlyphs[nBeginRun].m_aPos;
6672 // back transformation to current coordinate system
6673 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
6674 aCurPos += rAlignOffset;
6675 // the first run can be set with "Td" operator
6676 // subsequent use of that operator would move
6677 // the texline matrix relative to what was set before
6678 // making use of that would drive us into rounding issues
6679 Matrix3 aMat;
6680 if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
6682 m_aPages.back().appendPoint( aCurPos, rLine, false );
6683 rLine.append( " Td " );
6685 else
6687 if( fSkew != 0.0 )
6688 aMat.skew( 0.0, fSkew );
6689 aMat.scale( fXScale, 1.0 );
6690 aMat.rotate( fAngle );
6691 aMat.translate( aCurPos.X(), aCurPos.Y() );
6692 aMat.append( m_aPages.back(), rLine );
6693 rLine.append( " Tm\n" );
6695 // set up correct font
6696 rLine.append( "/F" );
6697 rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
6698 rLine.append( ' ' );
6699 m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
6700 rLine.append( " Tf" );
6702 // output glyphs using Tj or TJ
6703 OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
6704 aKernedLine.append( "[<" );
6705 aUnkernedLine.append( '<' );
6706 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
6707 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
6709 aMat.invert();
6710 bool bNeedKern = false;
6711 for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
6713 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
6714 // check if glyph advance matches with the width of the previous glyph, else adjust
6715 const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
6716 const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
6717 double fAdvance = aThisPos.X() - aPrevPos.X();
6718 fAdvance *= 1000.0 / (fXScale * nPixelFontHeight);
6719 const sal_Int32 nAdjustment = rGlyphs[nPos-1].m_nNativeWidth - sal_Int32(fAdvance+0.5);
6720 if( nAdjustment != 0 )
6722 bNeedKern = true;
6723 aKernedLine.append( ">" );
6724 aKernedLine.append( nAdjustment );
6725 aKernedLine.append( "<" );
6727 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
6729 aKernedLine.append( ">]TJ\n" );
6730 aUnkernedLine.append( ">Tj\n" );
6731 rLine.append( bNeedKern ? aKernedLine : aUnkernedLine );
6733 // set beginning of next run
6734 nBeginRun = aRunEnds[nRun];
6738 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines )
6740 // relief takes precedence over shadow (see outdev3.cxx)
6741 if( m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE )
6743 drawRelief( rLayout, rText, bTextLines );
6744 return;
6746 else if( m_aCurrentPDFState.m_aFont.IsShadow() )
6747 drawShadow( rLayout, rText, bTextLines );
6749 OStringBuffer aLine( 512 );
6751 const int nMaxGlyphs = 256;
6753 sal_GlyphId pGlyphs[nMaxGlyphs];
6754 sal_Int32 pGlyphWidths[nMaxGlyphs];
6755 sal_uInt8 pMappedGlyphs[nMaxGlyphs];
6756 sal_Int32 pMappedFontObjects[nMaxGlyphs];
6757 sal_Ucs pUnicodes[nMaxGlyphs];
6758 int pCharPosAry[nMaxGlyphs];
6759 sal_Int32 nAdvanceWidths[nMaxGlyphs];
6760 const ImplFontData* pFallbackFonts[nMaxGlyphs];
6761 bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
6762 int nGlyphs;
6763 int nIndex = 0;
6764 int nMinCharPos = 0, nMaxCharPos = rText.Len()-1;
6765 double fXScale = 1.0;
6766 double fSkew = 0.0;
6767 sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight;
6768 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
6770 // transform font height back to current units
6771 // note: the layout calculates in outdevs device pixel !!
6772 sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
6773 if( m_aCurrentPDFState.m_aFont.GetWidth() )
6775 Font aFont( m_aCurrentPDFState.m_aFont );
6776 aFont.SetWidth( 0 );
6777 FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
6778 if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() )
6780 fXScale =
6781 (double)m_aCurrentPDFState.m_aFont.GetWidth() /
6782 (double)aMetric.GetWidth();
6784 // force state before GetFontMetric
6785 m_pReferenceDevice->ImplNewFont();
6788 // perform artificial italics if necessary
6789 if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
6790 m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
6791 !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL ||
6792 m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE )
6795 fSkew = M_PI/12.0;
6798 // if the mapmode is distorted we need to adjust for that also
6799 if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
6801 fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
6804 int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
6805 // normalize angles
6806 while( nAngle < 0 )
6807 nAngle += 3600;
6808 nAngle = nAngle % 3600;
6809 double fAngle = (double)nAngle * M_PI / 1800.0;
6811 Matrix3 aRotScale;
6812 aRotScale.scale( fXScale, 1.0 );
6813 if( fAngle != 0.0 )
6814 aRotScale.rotate( -fAngle );
6816 bool bPop = false;
6817 bool bABold = false;
6818 // artificial bold necessary ?
6819 if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
6820 m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
6822 if( ! bPop )
6823 aLine.append( "q " );
6824 bPop = true;
6825 bABold = true;
6827 // setup text colors (if necessary)
6828 Color aStrokeColor( COL_TRANSPARENT );
6829 Color aNonStrokeColor( COL_TRANSPARENT );
6831 if( m_aCurrentPDFState.m_aFont.IsOutline() )
6833 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6834 aNonStrokeColor = Color( COL_WHITE );
6836 else
6837 aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6838 if( bABold )
6839 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6841 if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
6843 if( ! bPop )
6844 aLine.append( "q " );
6845 bPop = true;
6846 appendStrokingColor( aStrokeColor, aLine );
6847 aLine.append( "\n" );
6849 if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
6851 if( ! bPop )
6852 aLine.append( "q " );
6853 bPop = true;
6854 appendNonStrokingColor( aNonStrokeColor, aLine );
6855 aLine.append( "\n" );
6858 // begin text object
6859 aLine.append( "BT\n" );
6860 // outline attribute ?
6861 if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
6863 // set correct text mode, set stroke width
6864 aLine.append( "2 Tr " ); // fill, then stroke
6866 if( m_aCurrentPDFState.m_aFont.IsOutline() )
6868 // unclear what to do in case of outline and artificial bold
6869 // for the time being outline wins
6870 aLine.append( "0.25 w \n" );
6872 else
6874 double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0;
6875 m_aPages.back().appendMappedLength( fW, aLine );
6876 aLine.append ( " w\n" );
6880 FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
6882 // collect the glyphs into a single array
6883 const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
6884 std::vector< PDFGlyph > aGlyphs;
6885 aGlyphs.reserve( nTmpMaxGlyphs );
6886 // first get all the glyphs and register them; coordinates still in Pixel
6887 Point aGNGlyphPos;
6888 while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 )
6890 for( int i = 0; i < nGlyphs; i++ )
6892 pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] );
6894 if( (pGlyphs[i] & GF_ISCHAR) )
6895 pUnicodes[i] = static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK);
6896 else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
6898 pUnicodes[i] = rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) );
6899 // #i36691# hack that is needed because currently the pGlyphs[]
6900 // argument is ignored for embeddable fonts and so the layout
6901 // engine's glyph work is ignored (i.e. char mirroring)
6902 // TODO: a real solution would be to map the layout engine's
6903 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
6904 // back to unicode and then to embeddable font's encoding
6905 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL )
6906 pUnicodes[i] = static_cast<sal_Ucs>(GetMirroredChar(pUnicodes[i]));
6908 else
6909 pUnicodes[i] = 0;
6910 // note: in case of ctl one character may result
6911 // in multiple glyphs. The current SalLayout
6912 // implementations set -1 then to indicate that no direct
6913 // mapping is possible
6916 registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, pUnicodes, pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
6918 for( int i = 0; i < nGlyphs; i++ )
6920 aGlyphs.push_back( PDFGlyph( aGNGlyphPos,
6921 pGlyphWidths[i],
6922 pGlyphs[i],
6923 pMappedFontObjects[i],
6924 pMappedGlyphs[i] ) );
6925 if( bVertical )
6926 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
6927 else
6928 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
6932 Point aAlignOffset;
6933 if ( eAlign == ALIGN_BOTTOM )
6934 aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
6935 else if ( eAlign == ALIGN_TOP )
6936 aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
6937 if( aAlignOffset.X() || aAlignOffset.Y() )
6938 aAlignOffset = aRotScale.transform( aAlignOffset );
6940 if( bVertical )
6941 drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
6942 else
6943 drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
6945 // end textobject
6946 aLine.append( "ET\n" );
6947 if( bPop )
6948 aLine.append( "Q\n" );
6950 writeBuffer( aLine.getStr(), aLine.getLength() );
6952 // draw eventual textlines
6953 FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
6954 FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
6955 FontUnderline eOverline = m_aCurrentPDFState.m_aFont.GetOverline();
6956 if( bTextLines &&
6958 ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) ||
6959 ( eOverline != UNDERLINE_NONE && eOverline != UNDERLINE_DONTKNOW ) ||
6960 ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
6964 BOOL bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
6965 if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
6967 Point aPos, aStartPt;
6968 sal_Int32 nWidth = 0, nAdvance=0;
6969 for( int nStart = 0;;)
6971 sal_GlyphId nGlyphIndex;
6972 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
6973 break;
6975 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
6977 if( !nWidth )
6978 aStartPt = aPos;
6980 nWidth += nAdvance;
6982 else if( nWidth > 0 )
6984 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
6985 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
6986 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
6987 nWidth = 0;
6991 if( nWidth > 0 )
6993 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
6994 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
6995 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
6998 else
7000 Point aStartPt = rLayout.GetDrawPosition();
7001 int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
7002 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7003 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7004 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7008 // write eventual emphasis marks
7009 if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
7011 PolyPolygon aEmphPoly;
7012 Rectangle aEmphRect1;
7013 Rectangle aEmphRect2;
7014 long nEmphYOff;
7015 long nEmphWidth;
7016 long nEmphHeight;
7017 BOOL bEmphPolyLine;
7018 FontEmphasisMark nEmphMark;
7020 push( PUSH_ALL );
7022 aLine.setLength( 0 );
7023 aLine.append( "q\n" );
7025 nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
7026 if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7027 nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
7028 else
7029 nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
7030 m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
7031 bEmphPolyLine,
7032 aEmphRect1,
7033 aEmphRect2,
7034 nEmphYOff,
7035 nEmphWidth,
7036 nEmphMark,
7037 m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight),
7038 m_pReferenceDevice->mpFontEntry->mnOrientation );
7039 if ( bEmphPolyLine )
7041 setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
7042 setFillColor( Color( COL_TRANSPARENT ) );
7044 else
7046 setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
7047 setLineColor( Color( COL_TRANSPARENT ) );
7049 writeBuffer( aLine.getStr(), aLine.getLength() );
7051 Point aOffset = Point(0,0);
7053 if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7054 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff;
7055 else
7056 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff;
7058 long nEmphWidth2 = nEmphWidth / 2;
7059 long nEmphHeight2 = nEmphHeight / 2;
7060 aOffset += Point( nEmphWidth2, nEmphHeight2 );
7062 if ( eAlign == ALIGN_BOTTOM )
7063 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent;
7064 else if ( eAlign == ALIGN_TOP )
7065 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent;
7067 for( int nStart = 0;;)
7069 Point aPos;
7070 sal_GlyphId nGlyphIndex;
7071 sal_Int32 nAdvance;
7072 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7073 break;
7075 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7077 Point aAdjOffset = aOffset;
7078 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2;
7079 aAdjOffset = aRotScale.transform( aAdjOffset );
7081 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
7083 aPos += aAdjOffset;
7084 aPos = m_pReferenceDevice->PixelToLogic( aPos );
7085 drawEmphasisMark( aPos.X(), aPos.Y(),
7086 aEmphPoly, bEmphPolyLine,
7087 aEmphRect1, aEmphRect2 );
7091 writeBuffer( "Q\n", 2 );
7092 pop();
7097 void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
7098 const PolyPolygon& rPolyPoly, BOOL bPolyLine,
7099 const Rectangle& rRect1, const Rectangle& rRect2 )
7101 // TODO: pass nWidth as width of this mark
7102 // long nWidth = 0;
7104 if ( rPolyPoly.Count() )
7106 if ( bPolyLine )
7108 Polygon aPoly = rPolyPoly.GetObject( 0 );
7109 aPoly.Move( nX, nY );
7110 drawPolyLine( aPoly );
7112 else
7114 PolyPolygon aPolyPoly = rPolyPoly;
7115 aPolyPoly.Move( nX, nY );
7116 drawPolyPolygon( aPolyPoly );
7120 if ( !rRect1.IsEmpty() )
7122 Rectangle aRect( Point( nX+rRect1.Left(),
7123 nY+rRect1.Top() ), rRect1.GetSize() );
7124 drawRectangle( aRect );
7127 if ( !rRect2.IsEmpty() )
7129 Rectangle aRect( Point( nX+rRect2.Left(),
7130 nY+rRect2.Top() ), rRect2.GetSize() );
7132 drawRectangle( aRect );
7136 void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7138 MARK( "drawText" );
7140 updateGraphicsState();
7142 // get a layout from the OuputDevice's SalGraphics
7143 // this also enforces font substitution and sets the font on SalGraphics
7144 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
7145 if( pLayout )
7147 drawLayout( *pLayout, rText, bTextLines );
7148 pLayout->Release();
7152 void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7154 MARK( "drawText with array" );
7156 updateGraphicsState();
7158 // get a layout from the OuputDevice's SalGraphics
7159 // this also enforces font substitution and sets the font on SalGraphics
7160 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
7161 if( pLayout )
7163 drawLayout( *pLayout, rText, bTextLines );
7164 pLayout->Release();
7168 void PDFWriterImpl::drawStretchText( const Point& rPos, ULONG nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7170 MARK( "drawStretchText" );
7172 updateGraphicsState();
7174 // get a layout from the OuputDevice's SalGraphics
7175 // this also enforces font substitution and sets the font on SalGraphics
7176 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
7177 if( pLayout )
7179 drawLayout( *pLayout, rText, bTextLines );
7180 pLayout->Release();
7184 void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, USHORT nStyle, bool bTextLines )
7186 long nWidth = rRect.GetWidth();
7187 long nHeight = rRect.GetHeight();
7189 if ( nWidth <= 0 || nHeight <= 0 )
7190 return;
7192 MARK( "drawText with rectangle" );
7194 updateGraphicsState();
7196 // clip with rectangle
7197 OStringBuffer aLine;
7198 aLine.append( "q " );
7199 m_aPages.back().appendRect( rRect, aLine );
7200 aLine.append( " W* n\n" );
7201 writeBuffer( aLine.getStr(), aLine.getLength() );
7203 // if disabled text is needed, put in here
7205 Point aPos = rRect.TopLeft();
7207 long nTextHeight = m_pReferenceDevice->GetTextHeight();
7208 xub_StrLen nMnemonicPos = STRING_NOTFOUND;
7210 String aStr = rOrigStr;
7211 if ( nStyle & TEXT_DRAW_MNEMONIC )
7212 aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos );
7214 // multiline text
7215 if ( nStyle & TEXT_DRAW_MULTILINE )
7217 XubString aLastLine;
7218 ImplMultiTextLineInfo aMultiLineInfo;
7219 ImplTextLineInfo* pLineInfo;
7220 long nMaxTextWidth;
7221 xub_StrLen i;
7222 xub_StrLen nLines;
7223 xub_StrLen nFormatLines;
7225 if ( nTextHeight )
7227 nMaxTextWidth = m_pReferenceDevice->ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle );
7228 nLines = (xub_StrLen)(nHeight/nTextHeight);
7229 nFormatLines = aMultiLineInfo.Count();
7230 if ( !nLines )
7231 nLines = 1;
7232 if ( nFormatLines > nLines )
7234 if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
7236 // handle last line
7237 nFormatLines = nLines-1;
7239 pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
7240 aLastLine = aStr.Copy( pLineInfo->GetIndex() );
7241 aLastLine.ConvertLineEnd( LINEEND_LF );
7242 // replace line feed by space
7243 xub_StrLen nLastLineLen = aLastLine.Len();
7244 for ( i = 0; i < nLastLineLen; i++ )
7246 if ( aLastLine.GetChar( i ) == _LF )
7247 aLastLine.SetChar( i, ' ' );
7249 aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
7250 nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
7251 nStyle |= TEXT_DRAW_TOP;
7255 // vertical alignment
7256 if ( nStyle & TEXT_DRAW_BOTTOM )
7257 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
7258 else if ( nStyle & TEXT_DRAW_VCENTER )
7259 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
7261 // draw all lines excluding the last
7262 for ( i = 0; i < nFormatLines; i++ )
7264 pLineInfo = aMultiLineInfo.GetLine( i );
7265 if ( nStyle & TEXT_DRAW_RIGHT )
7266 aPos.X() += nWidth-pLineInfo->GetWidth();
7267 else if ( nStyle & TEXT_DRAW_CENTER )
7268 aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
7269 xub_StrLen nIndex = pLineInfo->GetIndex();
7270 xub_StrLen nLineLen = pLineInfo->GetLen();
7271 drawText( aPos, aStr, nIndex, nLineLen, bTextLines );
7272 // mnemonics should not appear in documents,
7273 // if the need arises, put them in here
7274 aPos.Y() += nTextHeight;
7275 aPos.X() = rRect.Left();
7279 // output last line left adjusted since it was shortened
7280 if ( aLastLine.Len() )
7281 drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines );
7284 else
7286 long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7288 // Evt. Text kuerzen
7289 if ( nTextWidth > nWidth )
7291 if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) )
7293 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
7294 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
7295 nStyle |= TEXT_DRAW_LEFT;
7296 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7300 // vertical alignment
7301 if ( nStyle & TEXT_DRAW_RIGHT )
7302 aPos.X() += nWidth-nTextWidth;
7303 else if ( nStyle & TEXT_DRAW_CENTER )
7304 aPos.X() += (nWidth-nTextWidth)/2;
7306 if ( nStyle & TEXT_DRAW_BOTTOM )
7307 aPos.Y() += nHeight-nTextHeight;
7308 else if ( nStyle & TEXT_DRAW_VCENTER )
7309 aPos.Y() += (nHeight-nTextHeight)/2;
7311 // mnemonics should be inserted here if the need arises
7313 // draw the actual text
7314 drawText( aPos, aStr, 0, STRING_LEN, bTextLines );
7317 // reset clip region to original value
7318 aLine.setLength( 0 );
7319 aLine.append( "Q\n" );
7320 writeBuffer( aLine.getStr(), aLine.getLength() );
7323 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
7325 MARK( "drawLine" );
7327 updateGraphicsState();
7329 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7330 return;
7332 OStringBuffer aLine;
7333 m_aPages.back().appendPoint( rStart, aLine );
7334 aLine.append( " m " );
7335 m_aPages.back().appendPoint( rStop, aLine );
7336 aLine.append( " l S\n" );
7338 writeBuffer( aLine.getStr(), aLine.getLength() );
7341 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
7343 MARK( "drawLine with LineInfo" );
7344 updateGraphicsState();
7346 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7347 return;
7349 if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 )
7351 drawLine( rStart, rStop );
7352 return;
7355 OStringBuffer aLine;
7357 aLine.append( "q " );
7358 if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
7360 m_aPages.back().appendPoint( rStart, aLine );
7361 aLine.append( " m " );
7362 m_aPages.back().appendPoint( rStop, aLine );
7363 aLine.append( " l S Q\n" );
7365 writeBuffer( aLine.getStr(), aLine.getLength() );
7367 else
7369 PDFWriter::ExtLineInfo aInfo;
7370 convertLineInfoToExtLineInfo( rInfo, aInfo );
7371 Point aPolyPoints[2] = { rStart, rStop };
7372 Polygon aPoly( 2, aPolyPoints );
7373 drawPolyLine( aPoly, aInfo );
7377 void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth )
7379 Point aDiff( rStop-rStart );
7380 double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) );
7381 if( fLen < 1.0 )
7382 return;
7384 MARK( "drawWaveLine" );
7385 updateGraphicsState();
7387 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7388 return;
7390 OStringBuffer aLine( 512 );
7391 aLine.append( "q " );
7392 m_aPages.back().appendMappedLength( nLineWidth, aLine, true );
7393 aLine.append( " w " );
7395 appendDouble( (double)aDiff.X()/fLen, aLine );
7396 aLine.append( ' ' );
7397 appendDouble( -(double)aDiff.Y()/fLen, aLine );
7398 aLine.append( ' ' );
7399 appendDouble( (double)aDiff.Y()/fLen, aLine );
7400 aLine.append( ' ' );
7401 appendDouble( (double)aDiff.X()/fLen, aLine );
7402 aLine.append( ' ' );
7403 m_aPages.back().appendPoint( rStart, aLine );
7404 aLine.append( " cm " );
7405 m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine );
7406 aLine.append( "Q\n" );
7407 writeBuffer( aLine.getStr(), aLine.getLength() );
7410 #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x )
7411 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
7413 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
7415 // note: units in pFontEntry are ref device pixel
7416 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
7417 long nLineHeight = 0;
7418 long nLinePos = 0;
7420 appendStrokingColor( aColor, aLine );
7421 aLine.append( "\n" );
7423 if ( bIsAbove )
7425 if ( !pFontEntry->maMetric.mnAboveWUnderlineSize )
7426 m_pReferenceDevice->ImplInitAboveTextLineSize();
7427 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize );
7428 nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset );
7430 else
7432 if ( !pFontEntry->maMetric.mnWUnderlineSize )
7433 m_pReferenceDevice->ImplInitTextLineSize();
7434 nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize );
7435 nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset );
7437 if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
7438 nLineHeight = 3;
7440 long nLineWidth = getReferenceDevice()->mnDPIX/450;
7441 if ( ! nLineWidth )
7442 nLineWidth = 1;
7444 if ( eTextLine == UNDERLINE_BOLDWAVE )
7445 nLineWidth = 3*nLineWidth;
7447 m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
7448 aLine.append( " w " );
7450 if ( eTextLine == UNDERLINE_DOUBLEWAVE )
7452 long nOrgLineHeight = nLineHeight;
7453 nLineHeight /= 3;
7454 if ( nLineHeight < 2 )
7456 if ( nOrgLineHeight > 1 )
7457 nLineHeight = 2;
7458 else
7459 nLineHeight = 1;
7461 long nLineDY = nOrgLineHeight-(nLineHeight*2);
7462 if ( nLineDY < nLineWidth )
7463 nLineDY = nLineWidth;
7464 long nLineDY2 = nLineDY/2;
7465 if ( !nLineDY2 )
7466 nLineDY2 = 1;
7468 nLinePos -= nLineWidth-nLineDY2;
7470 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
7472 nLinePos += nLineWidth+nLineDY;
7473 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
7475 else
7477 if ( eTextLine != UNDERLINE_BOLDWAVE )
7478 nLinePos -= nLineWidth/2;
7479 m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
7483 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
7485 // note: units in pFontEntry are ref device pixel
7486 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
7487 long nLineHeight = 0;
7488 long nLinePos = 0;
7489 long nLinePos2 = 0;
7491 if ( eTextLine > UNDERLINE_BOLDWAVE )
7492 eTextLine = UNDERLINE_SINGLE;
7494 switch ( eTextLine )
7496 case UNDERLINE_SINGLE:
7497 case UNDERLINE_DOTTED:
7498 case UNDERLINE_DASH:
7499 case UNDERLINE_LONGDASH:
7500 case UNDERLINE_DASHDOT:
7501 case UNDERLINE_DASHDOTDOT:
7502 if ( bIsAbove )
7504 if ( !pFontEntry->maMetric.mnAboveUnderlineSize )
7505 m_pReferenceDevice->ImplInitAboveTextLineSize();
7506 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize );
7507 nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset );
7509 else
7511 if ( !pFontEntry->maMetric.mnUnderlineSize )
7512 m_pReferenceDevice->ImplInitTextLineSize();
7513 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize );
7514 nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset );
7516 break;
7517 case UNDERLINE_BOLD:
7518 case UNDERLINE_BOLDDOTTED:
7519 case UNDERLINE_BOLDDASH:
7520 case UNDERLINE_BOLDLONGDASH:
7521 case UNDERLINE_BOLDDASHDOT:
7522 case UNDERLINE_BOLDDASHDOTDOT:
7523 if ( bIsAbove )
7525 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize )
7526 m_pReferenceDevice->ImplInitAboveTextLineSize();
7527 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize );
7528 nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset );
7530 else
7532 if ( !pFontEntry->maMetric.mnBUnderlineSize )
7533 m_pReferenceDevice->ImplInitTextLineSize();
7534 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize );
7535 nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset );
7536 nLinePos += nLineHeight/2;
7538 break;
7539 case UNDERLINE_DOUBLE:
7540 if ( bIsAbove )
7542 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize )
7543 m_pReferenceDevice->ImplInitAboveTextLineSize();
7544 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize );
7545 nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 );
7546 nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 );
7548 else
7550 if ( !pFontEntry->maMetric.mnDUnderlineSize )
7551 m_pReferenceDevice->ImplInitTextLineSize();
7552 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize );
7553 nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 );
7554 nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 );
7556 default:
7557 break;
7560 if ( nLineHeight )
7562 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
7563 aLine.append( " w " );
7564 appendStrokingColor( aColor, aLine );
7565 aLine.append( "\n" );
7567 switch ( eTextLine )
7569 case UNDERLINE_DOTTED:
7570 case UNDERLINE_BOLDDOTTED:
7571 aLine.append( "[ " );
7572 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7573 aLine.append( " ] 0 d\n" );
7574 break;
7575 case UNDERLINE_DASH:
7576 case UNDERLINE_LONGDASH:
7577 case UNDERLINE_BOLDDASH:
7578 case UNDERLINE_BOLDLONGDASH:
7580 sal_Int32 nDashLength = 4*nLineHeight;
7581 sal_Int32 nVoidLength = 2*nLineHeight;
7582 if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) )
7583 nDashLength = 8*nLineHeight;
7585 aLine.append( "[ " );
7586 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7587 aLine.append( ' ' );
7588 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7589 aLine.append( " ] 0 d\n" );
7591 break;
7592 case UNDERLINE_DASHDOT:
7593 case UNDERLINE_BOLDDASHDOT:
7595 sal_Int32 nDashLength = 4*nLineHeight;
7596 sal_Int32 nVoidLength = 2*nLineHeight;
7597 aLine.append( "[ " );
7598 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7599 aLine.append( ' ' );
7600 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7601 aLine.append( ' ' );
7602 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7603 aLine.append( ' ' );
7604 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7605 aLine.append( " ] 0 d\n" );
7607 break;
7608 case UNDERLINE_DASHDOTDOT:
7609 case UNDERLINE_BOLDDASHDOTDOT:
7611 sal_Int32 nDashLength = 4*nLineHeight;
7612 sal_Int32 nVoidLength = 2*nLineHeight;
7613 aLine.append( "[ " );
7614 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7615 aLine.append( ' ' );
7616 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7617 aLine.append( ' ' );
7618 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7619 aLine.append( ' ' );
7620 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7621 aLine.append( ' ' );
7622 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7623 aLine.append( ' ' );
7624 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7625 aLine.append( " ] 0 d\n" );
7627 break;
7628 default:
7629 break;
7632 aLine.append( "0 " );
7633 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
7634 aLine.append( " m " );
7635 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
7636 aLine.append( ' ' );
7637 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
7638 aLine.append( " l S\n" );
7639 if ( eTextLine == UNDERLINE_DOUBLE )
7641 aLine.append( "0 " );
7642 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
7643 aLine.append( " m " );
7644 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
7645 aLine.append( ' ' );
7646 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
7647 aLine.append( " l S\n" );
7652 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
7654 // note: units in pFontEntry are ref device pixel
7655 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
7656 long nLineHeight = 0;
7657 long nLinePos = 0;
7658 long nLinePos2 = 0;
7660 if ( eStrikeout > STRIKEOUT_X )
7661 eStrikeout = STRIKEOUT_SINGLE;
7663 switch ( eStrikeout )
7665 case STRIKEOUT_SINGLE:
7666 if ( !pFontEntry->maMetric.mnStrikeoutSize )
7667 m_pReferenceDevice->ImplInitTextLineSize();
7668 nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize );
7669 nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset );
7670 break;
7671 case STRIKEOUT_BOLD:
7672 if ( !pFontEntry->maMetric.mnBStrikeoutSize )
7673 m_pReferenceDevice->ImplInitTextLineSize();
7674 nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize );
7675 nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset );
7676 break;
7677 case STRIKEOUT_DOUBLE:
7678 if ( !pFontEntry->maMetric.mnDStrikeoutSize )
7679 m_pReferenceDevice->ImplInitTextLineSize();
7680 nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize );
7681 nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 );
7682 nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 );
7683 break;
7684 default:
7685 break;
7688 if ( nLineHeight )
7690 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
7691 aLine.append( " w " );
7692 appendStrokingColor( aColor, aLine );
7693 aLine.append( "\n" );
7695 aLine.append( "0 " );
7696 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
7697 aLine.append( " m " );
7698 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
7699 aLine.append( ' ' );
7700 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
7701 aLine.append( " l S\n" );
7703 if ( eStrikeout == STRIKEOUT_DOUBLE )
7705 aLine.append( "0 " );
7706 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
7707 aLine.append( " m " );
7708 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
7709 aLine.append( ' ' );
7710 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
7711 aLine.append( " l S\n" );
7716 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
7718 String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" );
7719 String aStrikeout = aStrikeoutChar;
7720 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
7721 aStrikeout.Append( aStrikeout );
7723 // do not get broader than nWidth modulo 1 character
7724 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
7725 aStrikeout.Erase( 0, 1 );
7726 aStrikeout.Append( aStrikeoutChar );
7727 BOOL bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
7728 if ( bShadow )
7730 Font aFont = m_aCurrentPDFState.m_aFont;
7731 aFont.SetShadow( FALSE );
7732 setFont( aFont );
7733 updateGraphicsState();
7736 // strikeout string is left aligned non-CTL text
7737 ULONG nOrigTLM = m_pReferenceDevice->GetLayoutMode();
7738 m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED );
7739 drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false );
7740 m_pReferenceDevice->SetLayoutMode( nOrigTLM );
7742 if ( bShadow )
7744 Font aFont = m_aCurrentPDFState.m_aFont;
7745 aFont.SetShadow( TRUE );
7746 setFont( aFont );
7747 updateGraphicsState();
7751 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove )
7753 if ( !nWidth ||
7754 ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
7755 ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) &&
7756 ((eOverline == UNDERLINE_NONE)||(eOverline == UNDERLINE_DONTKNOW)) ) )
7757 return;
7759 MARK( "drawTextLine" );
7760 updateGraphicsState();
7762 // note: units in pFontEntry are ref device pixel
7763 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
7764 Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
7765 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7766 Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
7767 bool bStrikeoutDone = false;
7768 bool bUnderlineDone = false;
7769 bool bOverlineDone = false;
7771 if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
7773 drawStrikeoutChar( rPos, nWidth, eStrikeout );
7774 bStrikeoutDone = true;
7777 Point aPos( rPos );
7778 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
7779 if( eAlign == ALIGN_TOP )
7780 aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent );
7781 else if( eAlign == ALIGN_BOTTOM )
7782 aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent );
7784 OStringBuffer aLine( 512 );
7785 // save GS
7786 aLine.append( "q " );
7788 // rotate and translate matrix
7789 double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
7790 Matrix3 aMat;
7791 aMat.rotate( fAngle );
7792 aMat.translate( aPos.X(), aPos.Y() );
7793 aMat.append( m_aPages.back(), aLine );
7794 aLine.append( " cm\n" );
7796 if ( aUnderlineColor.GetTransparency() != 0 )
7797 aUnderlineColor = aStrikeoutColor;
7799 if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
7800 (eUnderline == UNDERLINE_WAVE) ||
7801 (eUnderline == UNDERLINE_DOUBLEWAVE) ||
7802 (eUnderline == UNDERLINE_BOLDWAVE) )
7804 drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
7805 bUnderlineDone = true;
7808 if ( (eOverline == UNDERLINE_SMALLWAVE) ||
7809 (eOverline == UNDERLINE_WAVE) ||
7810 (eOverline == UNDERLINE_DOUBLEWAVE) ||
7811 (eOverline == UNDERLINE_BOLDWAVE) )
7813 drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
7814 bOverlineDone = true;
7817 if ( !bUnderlineDone )
7819 drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
7822 if ( !bOverlineDone )
7824 drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
7827 if ( !bStrikeoutDone )
7829 drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
7832 aLine.append( "Q\n" );
7833 writeBuffer( aLine.getStr(), aLine.getLength() );
7836 void PDFWriterImpl::drawPolygon( const Polygon& rPoly )
7838 MARK( "drawPolygon" );
7840 updateGraphicsState();
7842 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
7843 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
7844 return;
7846 int nPoints = rPoly.GetSize();
7847 OStringBuffer aLine( 20 * nPoints );
7848 m_aPages.back().appendPolygon( rPoly, aLine );
7849 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
7850 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
7851 aLine.append( "B*\n" );
7852 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
7853 aLine.append( "S\n" );
7854 else
7855 aLine.append( "f*\n" );
7857 writeBuffer( aLine.getStr(), aLine.getLength() );
7860 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly )
7862 MARK( "drawPolyPolygon" );
7864 updateGraphicsState();
7866 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
7867 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
7868 return;
7870 int nPolygons = rPolyPoly.Count();
7872 OStringBuffer aLine( 40 * nPolygons );
7873 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
7874 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
7875 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
7876 aLine.append( "B*\n" );
7877 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
7878 aLine.append( "S\n" );
7879 else
7880 aLine.append( "f*\n" );
7882 writeBuffer( aLine.getStr(), aLine.getLength() );
7885 void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
7887 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
7888 nTransparentPercent = nTransparentPercent % 100;
7890 MARK( "drawTransparent" );
7892 updateGraphicsState();
7894 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
7895 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
7896 return;
7898 if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 )
7900 m_aErrors.insert( m_bIsPDF_A1 ?
7901 PDFWriter::Warning_Transparency_Omitted_PDFA :
7902 PDFWriter::Warning_Transparency_Omitted_PDF13 );
7904 drawPolyPolygon( rPolyPoly );
7905 return;
7908 // create XObject
7909 m_aTransparentObjects.push_back( TransparencyEmit() );
7910 // FIXME: polygons with beziers may yield incorrect bound rect
7911 m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect();
7912 // convert rectangle to default user space
7913 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
7914 m_aTransparentObjects.back().m_nObject = createObject();
7915 m_aTransparentObjects.back().m_nExtGStateObject = createObject();
7916 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
7917 m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 );
7918 // create XObject's content stream
7919 OStringBuffer aContent( 256 );
7920 m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
7921 if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
7922 m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
7923 aContent.append( " B*\n" );
7924 else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
7925 aContent.append( " S\n" );
7926 else
7927 aContent.append( " f*\n" );
7928 m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() );
7930 OStringBuffer aObjName( 16 );
7931 aObjName.append( "Tr" );
7932 aObjName.append( m_aTransparentObjects.back().m_nObject );
7933 OString aTrName( aObjName.makeStringAndClear() );
7934 aObjName.append( "EGS" );
7935 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
7936 OString aExtName( aObjName.makeStringAndClear() );
7938 OStringBuffer aLine( 80 );
7939 // insert XObject
7940 aLine.append( "q /" );
7941 aLine.append( aExtName );
7942 aLine.append( " gs /" );
7943 aLine.append( aTrName );
7944 aLine.append( " Do Q\n" );
7945 writeBuffer( aLine.getStr(), aLine.getLength() );
7947 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
7948 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
7951 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
7953 if( nObject >= 0 )
7955 switch( eKind )
7957 case ResXObject:
7958 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
7959 if( ! m_aOutputStreams.empty() )
7960 m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
7961 break;
7962 case ResExtGState:
7963 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
7964 if( ! m_aOutputStreams.empty() )
7965 m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
7966 break;
7967 case ResShading:
7968 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
7969 if( ! m_aOutputStreams.empty() )
7970 m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
7971 break;
7972 case ResPattern:
7973 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
7974 if( ! m_aOutputStreams.empty() )
7975 m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
7976 break;
7981 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect )
7983 push( PUSH_ALL );
7985 setClipRegion( Region() );
7986 updateGraphicsState();
7988 m_aOutputStreams.push_front( StreamRedirect() );
7989 m_aOutputStreams.front().m_pStream = pStream;
7990 m_aOutputStreams.front().m_aMapMode = m_aMapMode;
7992 if( !rTargetRect.IsEmpty() )
7994 m_aOutputStreams.front().m_aTargetRect =
7995 lcl_convert( m_aGraphicsStack.front().m_aMapMode,
7996 m_aMapMode,
7997 getReferenceDevice(),
7998 rTargetRect );
7999 Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
8000 long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
8001 aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
8002 m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
8005 // setup graphics state for independent object stream
8007 // force reemitting colors
8008 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8009 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8012 Rectangle PDFWriterImpl::getRedirectTargetRect() const
8014 return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect;
8017 SvStream* PDFWriterImpl::endRedirect()
8019 SvStream* pStream = NULL;
8020 if( ! m_aOutputStreams.empty() )
8022 pStream = m_aOutputStreams.front().m_pStream;
8023 m_aMapMode = m_aOutputStreams.front().m_aMapMode;
8024 m_aOutputStreams.pop_front();
8027 pop();
8028 // force reemitting colors
8029 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8030 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8032 updateGraphicsState();
8034 return pStream;
8037 void PDFWriterImpl::beginTransparencyGroup()
8039 updateGraphicsState();
8040 if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8041 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8044 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
8046 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8047 nTransparentPercent = nTransparentPercent % 100;
8049 if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8051 // create XObject
8052 m_aTransparentObjects.push_back( TransparencyEmit() );
8053 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox;
8054 // convert rectangle to default user space
8055 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8056 m_aTransparentObjects.back().m_nObject = createObject();
8057 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
8058 // get XObject's content stream
8059 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8060 m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8062 OStringBuffer aObjName( 16 );
8063 aObjName.append( "Tr" );
8064 aObjName.append( m_aTransparentObjects.back().m_nObject );
8065 OString aTrName( aObjName.makeStringAndClear() );
8066 aObjName.append( "EGS" );
8067 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8068 OString aExtName( aObjName.makeStringAndClear() );
8070 OStringBuffer aLine( 80 );
8071 // insert XObject
8072 aLine.append( "q /" );
8073 aLine.append( aExtName );
8074 aLine.append( " gs /" );
8075 aLine.append( aTrName );
8076 aLine.append( " Do Q\n" );
8077 writeBuffer( aLine.getStr(), aLine.getLength() );
8079 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8080 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8084 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask )
8086 if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8088 // create XObject
8089 m_aTransparentObjects.push_back( TransparencyEmit() );
8090 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox;
8091 // convert rectangle to default user space
8092 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8093 m_aTransparentObjects.back().m_nObject = createObject();
8094 m_aTransparentObjects.back().m_fAlpha = 0.0;
8095 // get XObject's content stream
8096 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8097 m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8099 // draw soft mask
8100 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8101 drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask );
8102 m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect());
8104 OStringBuffer aObjName( 16 );
8105 aObjName.append( "Tr" );
8106 aObjName.append( m_aTransparentObjects.back().m_nObject );
8107 OString aTrName( aObjName.makeStringAndClear() );
8108 aObjName.append( "EGS" );
8109 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8110 OString aExtName( aObjName.makeStringAndClear() );
8112 OStringBuffer aLine( 80 );
8113 // insert XObject
8114 aLine.append( "q /" );
8115 aLine.append( aExtName );
8116 aLine.append( " gs /" );
8117 aLine.append( aTrName );
8118 aLine.append( " Do Q\n" );
8119 writeBuffer( aLine.getStr(), aLine.getLength() );
8121 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8122 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8126 void PDFWriterImpl::drawRectangle( const Rectangle& rRect )
8128 MARK( "drawRectangle" );
8130 updateGraphicsState();
8132 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8133 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8134 return;
8136 OStringBuffer aLine( 40 );
8137 m_aPages.back().appendRect( rRect, aLine );
8139 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8140 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8141 aLine.append( " B*\n" );
8142 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8143 aLine.append( " S\n" );
8144 else
8145 aLine.append( " f*\n" );
8147 writeBuffer( aLine.getStr(), aLine.getLength() );
8150 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
8152 MARK( "drawRectangle with rounded edges" );
8154 if( !nHorzRound && !nVertRound )
8155 drawRectangle( rRect );
8157 updateGraphicsState();
8159 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8160 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8161 return;
8163 if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
8164 nHorzRound = rRect.GetWidth()/2;
8165 if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
8166 nVertRound = rRect.GetHeight()/2;
8168 Point aPoints[16];
8169 const double kappa = 0.5522847498;
8170 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
8171 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
8173 aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
8174 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8175 aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
8176 aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() );
8178 aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
8179 aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky );
8180 aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
8181 aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky );
8183 aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
8184 aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() );
8185 aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
8186 aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
8188 aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
8189 aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
8190 aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
8191 aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
8194 OStringBuffer aLine( 80 );
8195 m_aPages.back().appendPoint( aPoints[1], aLine );
8196 aLine.append( " m " );
8197 m_aPages.back().appendPoint( aPoints[2], aLine );
8198 aLine.append( " l " );
8199 m_aPages.back().appendPoint( aPoints[3], aLine );
8200 aLine.append( ' ' );
8201 m_aPages.back().appendPoint( aPoints[4], aLine );
8202 aLine.append( ' ' );
8203 m_aPages.back().appendPoint( aPoints[5], aLine );
8204 aLine.append( " c\n" );
8205 m_aPages.back().appendPoint( aPoints[6], aLine );
8206 aLine.append( " l " );
8207 m_aPages.back().appendPoint( aPoints[7], aLine );
8208 aLine.append( ' ' );
8209 m_aPages.back().appendPoint( aPoints[8], aLine );
8210 aLine.append( ' ' );
8211 m_aPages.back().appendPoint( aPoints[9], aLine );
8212 aLine.append( " c\n" );
8213 m_aPages.back().appendPoint( aPoints[10], aLine );
8214 aLine.append( " l " );
8215 m_aPages.back().appendPoint( aPoints[11], aLine );
8216 aLine.append( ' ' );
8217 m_aPages.back().appendPoint( aPoints[12], aLine );
8218 aLine.append( ' ' );
8219 m_aPages.back().appendPoint( aPoints[13], aLine );
8220 aLine.append( " c\n" );
8221 m_aPages.back().appendPoint( aPoints[14], aLine );
8222 aLine.append( " l " );
8223 m_aPages.back().appendPoint( aPoints[15], aLine );
8224 aLine.append( ' ' );
8225 m_aPages.back().appendPoint( aPoints[0], aLine );
8226 aLine.append( ' ' );
8227 m_aPages.back().appendPoint( aPoints[1], aLine );
8228 aLine.append( " c " );
8230 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8231 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8232 aLine.append( "b*\n" );
8233 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8234 aLine.append( "s\n" );
8235 else
8236 aLine.append( "f*\n" );
8238 writeBuffer( aLine.getStr(), aLine.getLength() );
8241 void PDFWriterImpl::drawEllipse( const Rectangle& rRect )
8243 MARK( "drawEllipse" );
8245 updateGraphicsState();
8247 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8248 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8249 return;
8251 Point aPoints[12];
8252 const double kappa = 0.5522847498;
8253 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
8254 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
8256 aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
8257 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8258 aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() );
8260 aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
8261 aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky );
8262 aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky );
8264 aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
8265 aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() );
8266 aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() );
8268 aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
8269 aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky );
8270 aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
8272 OStringBuffer aLine( 80 );
8273 m_aPages.back().appendPoint( aPoints[1], aLine );
8274 aLine.append( " m " );
8275 m_aPages.back().appendPoint( aPoints[2], aLine );
8276 aLine.append( ' ' );
8277 m_aPages.back().appendPoint( aPoints[3], aLine );
8278 aLine.append( ' ' );
8279 m_aPages.back().appendPoint( aPoints[4], aLine );
8280 aLine.append( " c\n" );
8281 m_aPages.back().appendPoint( aPoints[5], aLine );
8282 aLine.append( ' ' );
8283 m_aPages.back().appendPoint( aPoints[6], aLine );
8284 aLine.append( ' ' );
8285 m_aPages.back().appendPoint( aPoints[7], aLine );
8286 aLine.append( " c\n" );
8287 m_aPages.back().appendPoint( aPoints[8], aLine );
8288 aLine.append( ' ' );
8289 m_aPages.back().appendPoint( aPoints[9], aLine );
8290 aLine.append( ' ' );
8291 m_aPages.back().appendPoint( aPoints[10], aLine );
8292 aLine.append( " c\n" );
8293 m_aPages.back().appendPoint( aPoints[11], aLine );
8294 aLine.append( ' ' );
8295 m_aPages.back().appendPoint( aPoints[0], aLine );
8296 aLine.append( ' ' );
8297 m_aPages.back().appendPoint( aPoints[1], aLine );
8298 aLine.append( " c " );
8300 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8301 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8302 aLine.append( "b*\n" );
8303 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8304 aLine.append( "s\n" );
8305 else
8306 aLine.append( "f*\n" );
8308 writeBuffer( aLine.getStr(), aLine.getLength() );
8311 static double calcAngle( const Rectangle& rRect, const Point& rPoint )
8313 Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
8314 (rRect.Top()+rRect.Bottom()+1)/2);
8315 Point aPoint = rPoint - aOrigin;
8317 double fX = (double)aPoint.X();
8318 double fY = (double)-aPoint.Y();
8320 if( rRect.GetWidth() > rRect.GetHeight() )
8321 fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
8322 else if( rRect.GetHeight() > rRect.GetWidth() )
8323 fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
8324 return atan2( fY, fX );
8327 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
8329 MARK( "drawArc" );
8331 updateGraphicsState();
8333 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8334 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8335 return;
8337 // calculate start and stop angles
8338 const double fStartAngle = calcAngle( rRect, rStart );
8339 double fStopAngle = calcAngle( rRect, rStop );
8340 while( fStopAngle < fStartAngle )
8341 fStopAngle += 2.0*M_PI;
8342 const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
8343 const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
8344 const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
8345 const double halfWidth = (double)rRect.GetWidth()/2.0;
8346 const double halfHeight = (double)rRect.GetHeight()/2.0;
8348 const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
8349 (rRect.Top()+rRect.Bottom()+1)/2 );
8351 OStringBuffer aLine( 30*nFragments );
8352 Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
8353 -(int)(halfHeight * sin(fStartAngle) ) );
8354 aPoint += aCenter;
8355 m_aPages.back().appendPoint( aPoint, aLine );
8356 aLine.append( " m " );
8357 if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
8359 for( int i = 0; i < nFragments; i++ )
8361 const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
8362 const double fStopFragment = fStartFragment + fFragmentDelta;
8363 aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
8364 -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
8365 aPoint += aCenter;
8366 m_aPages.back().appendPoint( aPoint, aLine );
8367 aLine.append( ' ' );
8369 aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
8370 -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
8371 aPoint += aCenter;
8372 m_aPages.back().appendPoint( aPoint, aLine );
8373 aLine.append( ' ' );
8375 aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
8376 -(int)(halfHeight * sin(fStopFragment) ) );
8377 aPoint += aCenter;
8378 m_aPages.back().appendPoint( aPoint, aLine );
8379 aLine.append( " c\n" );
8382 if( bWithChord || bWithPie )
8384 if( bWithPie )
8386 m_aPages.back().appendPoint( aCenter, aLine );
8387 aLine.append( " l " );
8389 aLine.append( "h " );
8391 if( ! bWithChord && ! bWithPie )
8392 aLine.append( "S\n" );
8393 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8394 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8395 aLine.append( "B*\n" );
8396 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8397 aLine.append( "S\n" );
8398 else
8399 aLine.append( "f*\n" );
8401 writeBuffer( aLine.getStr(), aLine.getLength() );
8404 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly )
8406 MARK( "drawPolyLine" );
8408 USHORT nPoints = rPoly.GetSize();
8409 if( nPoints < 2 )
8410 return;
8412 updateGraphicsState();
8414 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8415 return;
8417 OStringBuffer aLine( 20 * nPoints );
8418 m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
8419 aLine.append( "S\n" );
8421 writeBuffer( aLine.getStr(), aLine.getLength() );
8424 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo )
8426 MARK( "drawPolyLine with LineInfo" );
8428 updateGraphicsState();
8430 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8431 return;
8433 OStringBuffer aLine;
8434 aLine.append( "q " );
8435 if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
8437 writeBuffer( aLine.getStr(), aLine.getLength() );
8438 drawPolyLine( rPoly );
8439 writeBuffer( "Q\n", 2 );
8441 else
8443 PDFWriter::ExtLineInfo aInfo;
8444 convertLineInfoToExtLineInfo( rInfo, aInfo );
8445 drawPolyLine( rPoly, aInfo );
8449 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
8451 DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" );
8452 rOut.m_fLineWidth = rIn.GetWidth();
8453 rOut.m_fTransparency = 0.0;
8454 rOut.m_eCap = PDFWriter::capButt;
8455 rOut.m_eJoin = PDFWriter::joinMiter;
8456 rOut.m_fMiterLimit = 10;
8457 rOut.m_aDashArray.clear();
8459 int nDashes = rIn.GetDashCount();
8460 int nDashLen = rIn.GetDashLen();
8461 int nDistance = rIn.GetDistance();
8462 for( int n = 0; n < nDashes; n++ )
8464 rOut.m_aDashArray.push_back( nDashLen );
8465 rOut.m_aDashArray.push_back( nDistance );
8467 int nDots = rIn.GetDotCount();
8468 int nDotLen = rIn.GetDotLen();
8469 for( int n = 0; n < nDots; n++ )
8471 rOut.m_aDashArray.push_back( nDotLen );
8472 rOut.m_aDashArray.push_back( nDistance );
8476 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
8478 MARK( "drawPolyLine with ExtLineInfo" );
8480 updateGraphicsState();
8482 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8483 return;
8485 if( rInfo.m_fTransparency >= 1.0 )
8486 return;
8488 if( rInfo.m_fTransparency != 0.0 )
8489 beginTransparencyGroup();
8491 OStringBuffer aLine;
8492 aLine.append( "q " );
8493 m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
8494 aLine.append( " w" );
8495 if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader
8497 switch( rInfo.m_eCap )
8499 default:
8500 case PDFWriter::capButt: aLine.append( " 0 J" );break;
8501 case PDFWriter::capRound: aLine.append( " 1 J" );break;
8502 case PDFWriter::capSquare: aLine.append( " 2 J" );break;
8504 switch( rInfo.m_eJoin )
8506 default:
8507 case PDFWriter::joinMiter:
8509 double fLimit = rInfo.m_fMiterLimit;
8510 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
8511 fLimit = fLimit / rInfo.m_fLineWidth;
8512 if( fLimit < 1.0 )
8513 fLimit = 1.0;
8514 aLine.append( " 0 j " );
8515 appendDouble( fLimit, aLine );
8516 aLine.append( " M" );
8518 break;
8519 case PDFWriter::joinRound: aLine.append( " 1 j" );break;
8520 case PDFWriter::joinBevel: aLine.append( " 2 j" );break;
8522 if( rInfo.m_aDashArray.size() > 0 )
8524 aLine.append( " [ " );
8525 for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
8526 it != rInfo.m_aDashArray.end(); ++it )
8528 m_aPages.back().appendMappedLength( *it, aLine );
8529 aLine.append( ' ' );
8531 aLine.append( "] 0 d" );
8533 aLine.append( "\n" );
8534 writeBuffer( aLine.getStr(), aLine.getLength() );
8535 drawPolyLine( rPoly );
8537 else
8539 basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
8540 basegfx::B2DPolyPolygon aPolyPoly;
8542 basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
8544 // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
8545 // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
8546 // this line needs to be removed and the loop below adapted accordingly
8547 aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
8549 const sal_uInt32 nPolygonCount(aPolyPoly.count());
8551 for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
8553 aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
8554 aPoly = aPolyPoly.getB2DPolygon( nPoly );
8555 const sal_uInt32 nPointCount(aPoly.count());
8557 if(nPointCount)
8559 const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
8560 basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
8562 for(sal_uInt32 a(0); a < nEdgeCount; a++)
8564 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
8565 const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
8567 m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
8568 FRound(aCurrent.getY()) ),
8569 aLine );
8570 aLine.append( " m " );
8571 m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
8572 FRound(aNext.getY()) ),
8573 aLine );
8574 aLine.append( " l" );
8576 // prepare next edge
8577 aCurrent = aNext;
8581 aLine.append( " S " );
8582 writeBuffer( aLine.getStr(), aLine.getLength() );
8584 writeBuffer( "Q\n", 2 );
8586 if( rInfo.m_fTransparency != 0.0 )
8588 // FIXME: actually this may be incorrect with bezier polygons
8589 Rectangle aBoundRect( rPoly.GetBoundRect() );
8590 // avoid clipping with thick lines
8591 if( rInfo.m_fLineWidth > 0.0 )
8593 sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
8594 aBoundRect.Top() -= nLW;
8595 aBoundRect.Left() -= nLW;
8596 aBoundRect.Right() += nLW;
8597 aBoundRect.Bottom() += nLW;
8599 endTransparencyGroup( aBoundRect, (USHORT)(100.0*rInfo.m_fTransparency) );
8603 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
8605 MARK( "drawPixel" );
8607 Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
8609 if( aColor == Color( COL_TRANSPARENT ) )
8610 return;
8612 // pixels are drawn in line color, so have to set
8613 // the nonstroking color to line color
8614 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
8615 setFillColor( aColor );
8617 updateGraphicsState();
8619 OStringBuffer aLine( 20 );
8620 m_aPages.back().appendPoint( rPoint, aLine );
8621 aLine.append( ' ' );
8622 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine );
8623 aLine.append( ' ' );
8624 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine );
8625 aLine.append( " re f\n" );
8626 writeBuffer( aLine.getStr(), aLine.getLength() );
8628 setFillColor( aOldFillColor );
8631 void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors )
8633 MARK( "drawPixel with Polygon" );
8635 updateGraphicsState();
8637 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors )
8638 return;
8640 USHORT nPoints = rPoints.GetSize();
8641 OStringBuffer aLine( nPoints*40 );
8642 aLine.append( "q " );
8643 if( ! pColors )
8645 appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine );
8646 aLine.append( ' ' );
8649 OStringBuffer aPixel(32);
8650 aPixel.append( ' ' );
8651 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel );
8652 aPixel.append( ' ' );
8653 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel );
8654 OString aPixelStr = aPixel.makeStringAndClear();
8655 for( USHORT i = 0; i < nPoints; i++ )
8657 if( pColors )
8659 if( pColors[i] == Color( COL_TRANSPARENT ) )
8660 continue;
8662 appendNonStrokingColor( pColors[i], aLine );
8663 aLine.append( ' ' );
8665 m_aPages.back().appendPoint( rPoints[i], aLine );
8666 aLine.append( aPixelStr );
8667 aLine.append( " re f\n" );
8669 aLine.append( "Q\n" );
8670 writeBuffer( aLine.getStr(), aLine.getLength() );
8673 class AccessReleaser
8675 BitmapReadAccess* m_pAccess;
8676 public:
8677 AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){}
8678 ~AccessReleaser() { delete m_pAccess; }
8681 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
8683 CHECK_RETURN( updateObject( rObject.m_nObject ) );
8685 bool bFlateFilter = compressStream( rObject.m_pContentStream );
8686 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
8687 ULONG nSize = rObject.m_pContentStream->Tell();
8688 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
8689 #if OSL_DEBUG_LEVEL > 1
8691 OStringBuffer aLine( " PDFWriterImpl::writeTransparentObject" );
8692 emitComment( aLine.getStr() );
8694 #endif
8695 OStringBuffer aLine( 512 );
8696 CHECK_RETURN( updateObject( rObject.m_nObject ) );
8697 aLine.append( rObject.m_nObject );
8698 aLine.append( " 0 obj\n"
8699 "<</Type/XObject\n"
8700 "/Subtype/Form\n"
8701 "/BBox[ " );
8702 appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
8703 aLine.append( ' ' );
8704 appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
8705 aLine.append( ' ' );
8706 appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
8707 aLine.append( ' ' );
8708 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
8709 aLine.append( " ]\n" );
8710 /* #i42884# the PDF reference recommends that each Form XObject
8711 * should have a resource dict; alas if that is the same object
8712 * as the one of the page it triggers an endless recursion in
8713 * acroread 5 (6 and up have that fixed). Since we have only one
8714 * resource dict anyway, let's use the one from the page by NOT
8715 * emitting a Resources entry.
8717 #if 0
8718 aLine.append( " /Resources " );
8719 aLine.append( getResourceDictObj() );
8720 aLine.append( " 0 R\n" );
8721 #endif
8723 aLine.append( "/Length " );
8724 aLine.append( (sal_Int32)(nSize) );
8725 aLine.append( "\n" );
8726 if( bFlateFilter )
8727 aLine.append( "/Filter/FlateDecode\n" );
8728 aLine.append( ">>\n"
8729 "stream\n" );
8730 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8731 checkAndEnableStreamEncryption( rObject.m_nObject );
8732 CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
8733 disableStreamEncryption();
8734 aLine.setLength( 0 );
8735 aLine.append( "\n"
8736 "endstream\n"
8737 "endobj\n\n" );
8738 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8740 // write ExtGState dict for this XObject
8741 aLine.setLength( 0 );
8742 aLine.append( rObject.m_nExtGStateObject );
8743 aLine.append( " 0 obj\n"
8744 "<<" );
8745 if( ! rObject.m_pSoftMaskStream )
8747 //i59651
8748 if( m_bIsPDF_A1 )
8750 aLine.append( "/CA 1.0/ca 1.0" );
8751 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
8753 else
8755 aLine.append( "/CA " );
8756 appendDouble( rObject.m_fAlpha, aLine );
8757 aLine.append( "\n"
8758 " /ca " );
8759 appendDouble( rObject.m_fAlpha, aLine );
8761 aLine.append( "\n" );
8763 else
8765 if( m_bIsPDF_A1 )
8767 aLine.append( "/SMask/None" );
8768 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
8770 else
8772 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
8773 sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
8774 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
8775 sal_Int32 nMaskObject = createObject();
8776 aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
8777 aLine.append( nMaskObject );
8778 aLine.append( " 0 R>>\n" );
8780 OStringBuffer aMask;
8781 aMask.append( nMaskObject );
8782 aMask.append( " 0 obj\n"
8783 "<</Type/XObject\n"
8784 "/Subtype/Form\n"
8785 "/BBox[" );
8786 appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
8787 aMask.append( ' ' );
8788 appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
8789 aMask.append( ' ' );
8790 appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
8791 aMask.append( ' ' );
8792 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
8793 aMask.append( "]\n" );
8795 /* #i42884# see above */
8796 #if 0
8797 aLine.append( "/Resources " );
8798 aMask.append( getResourceDictObj() );
8799 aMask.append( " 0 R\n" );
8800 #endif
8802 aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
8803 aMask.append( "/Length " );
8804 aMask.append( nMaskSize );
8805 aMask.append( ">>\n"
8806 "stream\n" );
8807 CHECK_RETURN( updateObject( nMaskObject ) );
8808 checkAndEnableStreamEncryption( nMaskObject );
8809 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
8810 CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
8811 disableStreamEncryption();
8812 aMask.setLength( 0 );
8813 aMask.append( "\nendstream\n"
8814 "endobj\n\n" );
8815 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
8818 aLine.append( ">>\n"
8819 "endobj\n\n" );
8820 CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) );
8821 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8823 return true;
8826 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
8828 sal_Int32 nFunctionObject = createObject();
8829 CHECK_RETURN( updateObject( nFunctionObject ) );
8831 OutputDevice* pRefDevice = getReferenceDevice();
8832 pRefDevice->Push( PUSH_ALL );
8833 if( rObject.m_aSize.Width() > pRefDevice->GetOutputSizePixel().Width() )
8834 rObject.m_aSize.Width() = pRefDevice->GetOutputSizePixel().Width();
8835 if( rObject.m_aSize.Height() > pRefDevice->GetOutputSizePixel().Height() )
8836 rObject.m_aSize.Height() = pRefDevice->GetOutputSizePixel().Height();
8837 pRefDevice->SetMapMode( MapMode( MAP_PIXEL ) );
8838 pRefDevice->DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
8840 Bitmap aSample = pRefDevice->GetBitmap( Point( 0, 0 ), rObject.m_aSize );
8841 BitmapReadAccess* pAccess = aSample.AcquireReadAccess();
8842 AccessReleaser aReleaser( pAccess );
8844 Size aSize = aSample.GetSizePixel();
8846 sal_Int32 nStreamLengthObject = createObject();
8847 #if OSL_DEBUG_LEVEL > 1
8849 OStringBuffer aLine( " PDFWriterImpl::writeGradientFunction" );
8850 emitComment( aLine.getStr() );
8852 #endif
8853 OStringBuffer aLine( 120 );
8854 aLine.append( nFunctionObject );
8855 aLine.append( " 0 obj\n"
8856 "<</FunctionType 0\n"
8857 "/Domain[ 0 1 0 1 ]\n"
8858 "/Size[ " );
8859 aLine.append( (sal_Int32)aSize.Width() );
8860 aLine.append( ' ' );
8861 aLine.append( (sal_Int32)aSize.Height() );
8862 aLine.append( " ]\n"
8863 "/BitsPerSample 8\n"
8864 "/Range[ 0 1 0 1 0 1 ]\n"
8865 "/Length " );
8866 aLine.append( nStreamLengthObject );
8867 aLine.append( " 0 R\n"
8868 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
8869 "/Filter/FlateDecode"
8870 #endif
8871 ">>\n"
8872 "stream\n" );
8873 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8875 sal_uInt64 nStartStreamPos = 0;
8876 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) );
8878 checkAndEnableStreamEncryption( nFunctionObject );
8879 beginCompression();
8880 for( int y = 0; y < aSize.Height(); y++ )
8882 for( int x = 0; x < aSize.Width(); x++ )
8884 sal_uInt8 aCol[3];
8885 BitmapColor aColor = pAccess->GetColor( y, x );
8886 aCol[0] = aColor.GetRed();
8887 aCol[1] = aColor.GetGreen();
8888 aCol[2] = aColor.GetBlue();
8889 CHECK_RETURN( writeBuffer( aCol, 3 ) );
8892 endCompression();
8893 disableStreamEncryption();
8895 sal_uInt64 nEndStreamPos = 0;
8896 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) );
8898 aLine.setLength( 0 );
8899 aLine.append( "\nendstream\nendobj\n\n" );
8900 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8902 // write stream length
8903 CHECK_RETURN( updateObject( nStreamLengthObject ) );
8904 aLine.setLength( 0 );
8905 aLine.append( nStreamLengthObject );
8906 aLine.append( " 0 obj\n" );
8907 aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
8908 aLine.append( "\nendobj\n\n" );
8909 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8911 CHECK_RETURN( updateObject( rObject.m_nObject ) );
8912 aLine.setLength( 0 );
8913 aLine.append( rObject.m_nObject );
8914 aLine.append( " 0 obj\n"
8915 "<</ShadingType 1\n"
8916 "/ColorSpace/DeviceRGB\n"
8917 "/AntiAlias true\n"
8918 "/Domain[ 0 1 0 1 ]\n"
8919 "/Matrix[ " );
8920 aLine.append( (sal_Int32)aSize.Width() );
8921 aLine.append( " 0 0 " );
8922 aLine.append( (sal_Int32)aSize.Height() );
8923 aLine.append( " 0 0 ]\n"
8924 "/Function " );
8925 aLine.append( nFunctionObject );
8926 aLine.append( " 0 R\n"
8927 ">>\n"
8928 "endobj\n\n" );
8929 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8931 pRefDevice->Pop();
8933 return true;
8936 bool PDFWriterImpl::writeJPG( JPGEmit& rObject )
8938 CHECK_RETURN( rObject.m_pStream );
8939 CHECK_RETURN( updateObject( rObject.m_nObject ) );
8941 sal_Int32 nLength = 0;
8942 rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
8943 nLength = rObject.m_pStream->Tell();
8944 rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
8946 sal_Int32 nMaskObject = 0;
8947 if( !!rObject.m_aMask )
8949 if( rObject.m_aMask.GetBitCount() == 1 ||
8950 ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651
8953 nMaskObject = createObject();
8955 else if( m_bIsPDF_A1 )
8956 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
8957 else if( m_aContext.Version < PDFWriter::PDF_1_4 )
8958 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
8961 #if OSL_DEBUG_LEVEL > 1
8963 OStringBuffer aLine( " PDFWriterImpl::writeJPG" );
8964 emitComment( aLine.getStr() );
8966 #endif
8968 OStringBuffer aLine(200);
8969 aLine.append( rObject.m_nObject );
8970 aLine.append( " 0 obj\n"
8971 "<</Type/XObject/Subtype/Image/Width " );
8972 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
8973 aLine.append( " /Height " );
8974 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
8975 aLine.append( " /BitsPerComponent 8 " );
8976 if( rObject.m_bTrueColor )
8977 aLine.append( "/ColorSpace/DeviceRGB" );
8978 else
8979 aLine.append( "/ColorSpace/DeviceGray" );
8980 aLine.append( "/Filter/DCTDecode/Length " );
8981 aLine.append( nLength );
8982 if( nMaskObject )
8984 aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
8985 aLine.append( nMaskObject );
8986 aLine.append( " 0 R " );
8988 aLine.append( ">>\nstream\n" );
8989 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8991 checkAndEnableStreamEncryption( rObject.m_nObject );
8992 CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
8993 disableStreamEncryption();
8995 aLine.setLength( 0 );
8996 aLine.append( "\nendstream\nendobj\n\n" );
8997 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8999 if( nMaskObject )
9001 BitmapEmit aEmit;
9002 aEmit.m_nObject = nMaskObject;
9003 if( rObject.m_aMask.GetBitCount() == 1 )
9004 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
9005 else if( rObject.m_aMask.GetBitCount() == 8 )
9006 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
9007 writeBitmapObject( aEmit, true );
9010 return true;
9013 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
9015 CHECK_RETURN( updateObject( rObject.m_nObject ) );
9017 Bitmap aBitmap;
9018 Color aTransparentColor( COL_TRANSPARENT );
9019 bool bWriteMask = false;
9020 if( ! bMask )
9022 aBitmap = rObject.m_aBitmap.GetBitmap();
9023 if( rObject.m_aBitmap.IsAlpha() )
9025 if( m_aContext.Version >= PDFWriter::PDF_1_4 )
9026 bWriteMask = true;
9027 // else draw without alpha channel
9029 else
9031 switch( rObject.m_aBitmap.GetTransparentType() )
9033 case TRANSPARENT_NONE:
9034 // comes from drawMask function
9035 if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask )
9036 bMask = true;
9037 break;
9038 case TRANSPARENT_COLOR:
9039 aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
9040 break;
9041 case TRANSPARENT_BITMAP:
9042 bWriteMask = true;
9043 break;
9047 else
9049 if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
9051 aBitmap = rObject.m_aBitmap.GetMask();
9052 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
9053 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
9055 else if( aBitmap.GetBitCount() != 8 )
9057 aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap();
9058 aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
9059 DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" );
9063 BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();
9064 AccessReleaser aReleaser( pAccess );
9066 bool bTrueColor;
9067 sal_Int32 nBitsPerComponent;
9068 switch( aBitmap.GetBitCount() )
9070 case 1:
9071 case 2:
9072 case 4:
9073 case 8:
9074 bTrueColor = false;
9075 nBitsPerComponent = aBitmap.GetBitCount();
9076 break;
9077 default:
9078 bTrueColor = true;
9079 nBitsPerComponent = 8;
9080 break;
9083 sal_Int32 nStreamLengthObject = createObject();
9084 sal_Int32 nMaskObject = 0;
9086 #if OSL_DEBUG_LEVEL > 1
9088 OStringBuffer aLine( " PDFWriterImpl::writeBitmapObject" );
9089 emitComment( aLine.getStr() );
9091 #endif
9092 OStringBuffer aLine(1024);
9093 aLine.append( rObject.m_nObject );
9094 aLine.append( " 0 obj\n"
9095 "<</Type/XObject/Subtype/Image/Width " );
9096 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9097 aLine.append( " /Height " );
9098 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
9099 aLine.append( " /BitsPerComponent " );
9100 aLine.append( nBitsPerComponent );
9101 aLine.append( " /Length " );
9102 aLine.append( nStreamLengthObject );
9103 aLine.append( " 0 R\n" );
9104 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9105 aLine.append( "/Filter/FlateDecode" );
9106 #endif
9107 if( ! bMask )
9109 aLine.append( "/ColorSpace" );
9110 if( bTrueColor )
9111 aLine.append( "/DeviceRGB\n" );
9112 else if( aBitmap.HasGreyPalette() )
9114 aLine.append( "/DeviceGray\n" );
9115 if( aBitmap.GetBitCount() == 1 )
9117 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
9118 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9119 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9120 if( nBlackIndex == 1 )
9121 aLine.append( "/Decode[1 0]\n" );
9124 else
9126 aLine.append( "[ /Indexed/DeviceRGB " );
9127 aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
9128 aLine.append( "\n<" );
9129 if( m_aContext.Encrypt )
9131 enableStringEncryption( rObject.m_nObject );
9132 //check encryption buffer size
9133 if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
9135 int nChar = 0;
9136 //fill the encryption buffer
9137 for( USHORT i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9139 const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9140 m_pEncryptionBuffer[nChar++] = rColor.GetRed();
9141 m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
9142 m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
9144 //encrypt the colorspace lookup table
9145 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
9146 //now queue the data for output
9147 nChar = 0;
9148 for( USHORT i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9150 appendHex(m_pEncryptionBuffer[nChar++], aLine );
9151 appendHex(m_pEncryptionBuffer[nChar++], aLine );
9152 appendHex(m_pEncryptionBuffer[nChar++], aLine );
9156 else //no encryption requested (PDF/A-1a program flow drops here)
9158 for( USHORT i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9160 const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9161 appendHex( rColor.GetRed(), aLine );
9162 appendHex( rColor.GetGreen(), aLine );
9163 appendHex( rColor.GetBlue(), aLine );
9166 aLine.append( ">\n]\n" );
9169 else
9171 if( aBitmap.GetBitCount() == 1 )
9173 aLine.append( " /ImageMask true\n" );
9174 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9175 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9176 if( nBlackIndex )
9177 aLine.append( "/Decode[ 1 0 ]\n" );
9178 else
9179 aLine.append( "/Decode[ 0 1 ]\n" );
9181 else if( aBitmap.GetBitCount() == 8 )
9183 aLine.append( "/ColorSpace/DeviceGray\n"
9184 "/Decode [ 1 0 ]\n" );
9188 if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651
9190 if( bWriteMask )
9192 nMaskObject = createObject();
9193 if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 )
9194 aLine.append( "/SMask " );
9195 else
9196 aLine.append( "/Mask " );
9197 aLine.append( nMaskObject );
9198 aLine.append( " 0 R\n" );
9200 else if( aTransparentColor != Color( COL_TRANSPARENT ) )
9202 aLine.append( "/Mask[ " );
9203 if( bTrueColor )
9205 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9206 aLine.append( ' ' );
9207 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9208 aLine.append( ' ' );
9209 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9210 aLine.append( ' ' );
9211 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9212 aLine.append( ' ' );
9213 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9214 aLine.append( ' ' );
9215 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9217 else
9219 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
9220 aLine.append( nIndex );
9222 aLine.append( " ]\n" );
9225 else if( m_bIsPDF_A1 )
9226 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9228 aLine.append( ">>\n"
9229 "stream\n" );
9230 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9231 sal_uInt64 nStartPos = 0;
9232 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) );
9234 checkAndEnableStreamEncryption( rObject.m_nObject );
9235 beginCompression();
9236 if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
9238 const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U );
9240 for( int i = 0; i < pAccess->Height(); i++ )
9242 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
9245 else
9247 const int nScanLineBytes = pAccess->Width()*3;
9248 sal_uInt8 *pCol = (sal_uInt8*)rtl_allocateMemory( nScanLineBytes );
9249 for( int y = 0; y < pAccess->Height(); y++ )
9251 for( int x = 0; x < pAccess->Width(); x++ )
9253 BitmapColor aColor = pAccess->GetColor( y, x );
9254 pCol[3*x+0] = aColor.GetRed();
9255 pCol[3*x+1] = aColor.GetGreen();
9256 pCol[3*x+2] = aColor.GetBlue();
9258 CHECK_RETURN( writeBuffer( pCol, nScanLineBytes ) );
9260 rtl_freeMemory( pCol );
9262 endCompression();
9263 disableStreamEncryption();
9265 sal_uInt64 nEndPos = 0;
9266 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) );
9267 aLine.setLength( 0 );
9268 aLine.append( "\nendstream\nendobj\n\n" );
9269 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9270 CHECK_RETURN( updateObject( nStreamLengthObject ) );
9271 aLine.setLength( 0 );
9272 aLine.append( nStreamLengthObject );
9273 aLine.append( " 0 obj\n" );
9274 aLine.append( (sal_Int64)(nEndPos-nStartPos) );
9275 aLine.append( "\nendobj\n\n" );
9276 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9278 if( nMaskObject )
9280 BitmapEmit aEmit;
9281 aEmit.m_nObject = nMaskObject;
9282 aEmit.m_aBitmap = rObject.m_aBitmap;
9283 return writeBitmapObject( aEmit, true );
9286 return true;
9289 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask )
9291 MARK( "drawJPGBitmap" );
9293 OStringBuffer aLine( 80 );
9294 updateGraphicsState();
9296 // #i40055# sanity check
9297 if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
9298 return;
9299 if( ! (rSizePixel.Width() && rSizePixel.Height()) )
9300 return;
9302 SvMemoryStream* pStream = new SvMemoryStream;
9303 rDCTData.Seek( 0 );
9304 *pStream << rDCTData;
9305 pStream->Seek( STREAM_SEEK_TO_END );
9307 BitmapID aID;
9308 aID.m_aPixelSize = rSizePixel;
9309 aID.m_nSize = pStream->Tell();
9310 pStream->Seek( STREAM_SEEK_TO_BEGIN );
9311 aID.m_nChecksum = rtl_crc32( 0, pStream->GetData(), aID.m_nSize );
9312 if( ! rMask.IsEmpty() )
9313 aID.m_nMaskChecksum = rMask.GetChecksum();
9315 std::list< JPGEmit >::const_iterator it;
9316 for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
9318 if( it == m_aJPGs.end() )
9320 m_aJPGs.push_front( JPGEmit() );
9321 JPGEmit& rEmit = m_aJPGs.front();
9322 rEmit.m_nObject = createObject();
9323 rEmit.m_aID = aID;
9324 rEmit.m_pStream = pStream;
9325 rEmit.m_bTrueColor = bIsTrueColor;
9326 if( !! rMask && rMask.GetSizePixel() == rSizePixel )
9327 rEmit.m_aMask = rMask;
9329 it = m_aJPGs.begin();
9331 else
9332 delete pStream;
9334 aLine.append( "q " );
9335 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false );
9336 aLine.append( " 0 0 " );
9337 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true );
9338 aLine.append( ' ' );
9339 m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
9340 aLine.append( " cm\n/Im" );
9341 aLine.append( it->m_nObject );
9342 aLine.append( " Do Q\n" );
9343 writeBuffer( aLine.getStr(), aLine.getLength() );
9345 OStringBuffer aObjName( 16 );
9346 aObjName.append( "Im" );
9347 aObjName.append( it->m_nObject );
9348 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
9352 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
9354 OStringBuffer aLine( 80 );
9355 updateGraphicsState();
9357 aLine.append( "q " );
9358 if( rFillColor != Color( COL_TRANSPARENT ) )
9360 appendNonStrokingColor( rFillColor, aLine );
9361 aLine.append( ' ' );
9363 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false );
9364 aLine.append( " 0 0 " );
9365 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true );
9366 aLine.append( ' ' );
9367 m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
9368 aLine.append( " cm\n/Im" );
9369 aLine.append( rBitmap.m_nObject );
9370 aLine.append( " Do Q\n" );
9371 writeBuffer( aLine.getStr(), aLine.getLength() );
9374 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& rBitmap, bool bDrawMask )
9376 BitmapID aID;
9377 aID.m_aPixelSize = rBitmap.GetSizePixel();
9378 aID.m_nSize = rBitmap.GetBitCount();
9379 aID.m_nChecksum = rBitmap.GetBitmap().GetChecksum();
9380 aID.m_nMaskChecksum = 0;
9381 if( rBitmap.IsAlpha() )
9382 aID.m_nMaskChecksum = rBitmap.GetAlpha().GetChecksum();
9383 else
9385 Bitmap aMask = rBitmap.GetMask();
9386 if( ! aMask.IsEmpty() )
9387 aID.m_nMaskChecksum = aMask.GetChecksum();
9389 std::list< BitmapEmit >::const_iterator it;
9390 for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
9392 if( aID == it->m_aID )
9393 break;
9395 if( it == m_aBitmaps.end() )
9397 m_aBitmaps.push_front( BitmapEmit() );
9398 m_aBitmaps.front().m_aID = aID;
9399 m_aBitmaps.front().m_aBitmap = rBitmap;
9400 m_aBitmaps.front().m_nObject = createObject();
9401 m_aBitmaps.front().m_bDrawMask = bDrawMask;
9402 it = m_aBitmaps.begin();
9405 OStringBuffer aObjName( 16 );
9406 aObjName.append( "Im" );
9407 aObjName.append( it->m_nObject );
9408 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
9410 return *it;
9413 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap )
9415 MARK( "drawBitmap (Bitmap)" );
9417 // #i40055# sanity check
9418 if( ! (rDestSize.Width() && rDestSize.Height()) )
9419 return;
9421 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) );
9422 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
9425 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
9427 MARK( "drawBitmap (BitmapEx)" );
9429 // #i40055# sanity check
9430 if( ! (rDestSize.Width() && rDestSize.Height()) )
9431 return;
9433 const BitmapEmit& rEmit = createBitmapEmit( rBitmap );
9434 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
9437 void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor )
9439 MARK( "drawMask" );
9441 // #i40055# sanity check
9442 if( ! (rDestSize.Width() && rDestSize.Height()) )
9443 return;
9445 Bitmap aBitmap( rBitmap );
9446 if( aBitmap.GetBitCount() > 1 )
9447 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
9448 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
9450 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true );
9451 drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor );
9454 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
9456 Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
9457 MapMode( MAP_POINT ),
9458 getReferenceDevice(),
9459 rSize ) );
9460 // check if we already have this gradient
9461 std::list<GradientEmit>::iterator it;
9462 for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
9464 if( it->m_aGradient == rGradient )
9466 if( it->m_aSize.Width() < aPtSize.Width() )
9467 it->m_aSize.Width() = aPtSize.Width();
9468 if( it->m_aSize.Height() <= aPtSize.Height() )
9469 it->m_aSize.Height() = aPtSize.Height();
9470 break;
9473 if( it == m_aGradients.end() )
9475 m_aGradients.push_front( GradientEmit() );
9476 m_aGradients.front().m_aGradient = rGradient;
9477 m_aGradients.front().m_nObject = createObject();
9478 m_aGradients.front().m_aSize = aPtSize;
9479 it = m_aGradients.begin();
9482 OStringBuffer aObjName( 16 );
9483 aObjName.append( 'P' );
9484 aObjName.append( it->m_nObject );
9485 pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
9487 return it->m_nObject;
9490 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient )
9492 MARK( "drawGradient (Rectangle)" );
9494 if( m_aContext.Version == PDFWriter::PDF_1_2 )
9496 drawRectangle( rRect );
9497 return;
9500 sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
9502 Point aTranslate( rRect.BottomLeft() );
9503 aTranslate += Point( 0, 1 );
9505 updateGraphicsState();
9507 OStringBuffer aLine( 80 );
9508 aLine.append( "q 1 0 0 1 " );
9509 m_aPages.back().appendPoint( aTranslate, aLine );
9510 aLine.append( " cm " );
9511 // if a stroke is appended reset the clip region before stroke
9512 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9513 aLine.append( "q " );
9514 aLine.append( "0 0 " );
9515 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
9516 aLine.append( ' ' );
9517 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
9518 aLine.append( " re W n\n" );
9520 aLine.append( "/P" );
9521 aLine.append( nGradient );
9522 aLine.append( " sh " );
9523 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9525 aLine.append( "Q 0 0 " );
9526 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
9527 aLine.append( ' ' );
9528 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
9529 aLine.append( " re S " );
9531 aLine.append( "Q\n" );
9532 writeBuffer( aLine.getStr(), aLine.getLength() );
9535 void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
9537 MARK( "drawGradient (PolyPolygon)" );
9539 if( m_aContext.Version == PDFWriter::PDF_1_2 )
9541 drawPolyPolygon( rPolyPoly );
9542 return;
9545 sal_Int32 nGradient = createGradient( rGradient, rPolyPoly.GetBoundRect().GetSize() );
9547 updateGraphicsState();
9549 Rectangle aBoundRect = rPolyPoly.GetBoundRect();
9550 Point aTranslate = aBoundRect.BottomLeft() + Point( 0, 1 );
9551 int nPolygons = rPolyPoly.Count();
9553 OStringBuffer aLine( 80*nPolygons );
9554 aLine.append( "q " );
9555 // set PolyPolygon as clip path
9556 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
9557 aLine.append( "W* n\n" );
9558 aLine.append( "1 0 0 1 " );
9559 m_aPages.back().appendPoint( aTranslate, aLine );
9560 aLine.append( " cm\n" );
9561 aLine.append( "/P" );
9562 aLine.append( nGradient );
9563 aLine.append( " sh Q\n" );
9564 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9566 // and draw the surrounding path
9567 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
9568 aLine.append( "S\n" );
9570 writeBuffer( aLine.getStr(), aLine.getLength() );
9573 void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
9575 MARK( "drawHatch" );
9577 updateGraphicsState();
9579 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
9580 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
9581 return;
9582 if( rPolyPoly.Count() )
9584 PolyPolygon aPolyPoly( rPolyPoly );
9586 aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
9587 push( PUSH_LINECOLOR );
9588 setLineColor( rHatch.GetColor() );
9589 getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, FALSE );
9590 pop();
9594 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall )
9596 MARK( "drawWallpaper" );
9598 bool bDrawColor = false;
9599 bool bDrawGradient = false;
9600 bool bDrawBitmap = false;
9602 BitmapEx aBitmap;
9603 Point aBmpPos = rRect.TopLeft();
9604 Size aBmpSize;
9605 if( rWall.IsBitmap() )
9607 aBitmap = rWall.GetBitmap();
9608 aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
9609 getMapMode(),
9610 getReferenceDevice(),
9611 aBitmap.GetPrefSize() );
9612 Rectangle aRect( rRect );
9613 if( rWall.IsRect() )
9615 aRect = rWall.GetRect();
9616 aBmpPos = aRect.TopLeft();
9617 aBmpSize = aRect.GetSize();
9619 if( rWall.GetStyle() != WALLPAPER_SCALE )
9621 if( rWall.GetStyle() != WALLPAPER_TILE )
9623 bDrawBitmap = true;
9624 if( rWall.IsGradient() )
9625 bDrawGradient = true;
9626 else
9627 bDrawColor = true;
9628 switch( rWall.GetStyle() )
9630 case WALLPAPER_TOPLEFT:
9631 break;
9632 case WALLPAPER_TOP:
9633 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
9634 break;
9635 case WALLPAPER_LEFT:
9636 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
9637 break;
9638 case WALLPAPER_TOPRIGHT:
9639 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
9640 break;
9641 case WALLPAPER_CENTER:
9642 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
9643 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
9644 break;
9645 case WALLPAPER_RIGHT:
9646 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
9647 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
9648 break;
9649 case WALLPAPER_BOTTOMLEFT:
9650 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
9651 break;
9652 case WALLPAPER_BOTTOM:
9653 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
9654 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
9655 break;
9656 case WALLPAPER_BOTTOMRIGHT:
9657 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
9658 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
9659 break;
9660 default: ;
9663 else
9665 // push the bitmap
9666 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) );
9668 // convert to page coordinates; this needs to be done here
9669 // since the emit does not know the page anymore
9670 Rectangle aConvertRect( aBmpPos, aBmpSize );
9671 m_aPages.back().convertRect( aConvertRect );
9673 OStringBuffer aNameBuf(16);
9674 aNameBuf.append( "Im" );
9675 aNameBuf.append( rEmit.m_nObject );
9676 OString aImageName( aNameBuf.makeStringAndClear() );
9678 // push the pattern
9679 OStringBuffer aTilingStream( 32 );
9680 appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
9681 aTilingStream.append( " 0 0 " );
9682 appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
9683 aTilingStream.append( " 0 0 cm\n/" );
9684 aTilingStream.append( aImageName );
9685 aTilingStream.append( " Do\n" );
9687 m_aTilings.push_back( TilingEmit() );
9688 m_aTilings.back().m_nObject = createObject();
9689 m_aTilings.back().m_aRectangle = Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
9690 m_aTilings.back().m_pTilingStream = new SvMemoryStream();
9691 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() );
9692 // phase the tiling so wallpaper begins on upper left
9693 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
9694 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
9695 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
9697 updateGraphicsState();
9699 OStringBuffer aObjName( 16 );
9700 aObjName.append( 'P' );
9701 aObjName.append( m_aTilings.back().m_nObject );
9702 OString aPatternName( aObjName.makeStringAndClear() );
9703 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
9705 // fill a rRect with the pattern
9706 OStringBuffer aLine( 100 );
9707 aLine.append( "q /Pattern cs /" );
9708 aLine.append( aPatternName );
9709 aLine.append( " scn " );
9710 m_aPages.back().appendRect( rRect, aLine );
9711 aLine.append( " f Q\n" );
9712 writeBuffer( aLine.getStr(), aLine.getLength() );
9715 else
9717 aBmpPos = aRect.TopLeft();
9718 aBmpSize = aRect.GetSize();
9719 bDrawBitmap = true;
9722 if( aBitmap.IsTransparent() )
9724 if( rWall.IsGradient() )
9725 bDrawGradient = true;
9726 else
9727 bDrawColor = true;
9730 else if( rWall.IsGradient() )
9731 bDrawGradient = true;
9732 else
9733 bDrawColor = true;
9735 if( bDrawGradient )
9737 drawGradient( rRect, rWall.GetGradient() );
9739 if( bDrawColor )
9741 Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
9742 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
9743 setLineColor( Color( COL_TRANSPARENT ) );
9744 setFillColor( rWall.GetColor() );
9745 drawRectangle( rRect );
9746 setLineColor( aOldLineColor );
9747 setFillColor( aOldFillColor );
9749 if( bDrawBitmap )
9751 // set temporary clip region since aBmpPos and aBmpSize
9752 // may be outside rRect
9753 OStringBuffer aLine( 20 );
9754 aLine.append( "q " );
9755 m_aPages.back().appendRect( rRect, aLine );
9756 aLine.append( " W n\n" );
9757 writeBuffer( aLine.getStr(), aLine.getLength() );
9758 drawBitmap( aBmpPos, aBmpSize, aBitmap );
9759 writeBuffer( "Q\n", 2 );
9763 void PDFWriterImpl::beginPattern( const Rectangle& rCellRect )
9765 beginRedirect( new SvMemoryStream(), rCellRect );
9768 sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform )
9770 Rectangle aConvertRect( getRedirectTargetRect() );
9771 DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" );
9773 // get scaling between current mapmode and PDF output
9774 Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) );
9775 double fSX = (double(aScaling.Width()) / 10000.0);
9776 double fSY = (double(aScaling.Height()) / 10000.0);
9778 // transform translation part of matrix
9779 Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] );
9780 aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation );
9782 sal_Int32 nTilingId = m_aTilings.size();
9783 m_aTilings.push_back( TilingEmit() );
9784 TilingEmit& rTile = m_aTilings.back();
9785 rTile.m_nObject = createObject();
9786 rTile.m_aResources = m_aOutputStreams.front().m_aResourceDict;
9787 rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX;
9788 rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY;
9789 rTile.m_aTransform.matrix[2] = aTranslation.Width();
9790 rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX;
9791 rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY;
9792 rTile.m_aTransform.matrix[5] = -aTranslation.Height();
9793 // caution: endRedirect pops the stream, so do this last
9794 rTile.m_pTilingStream = dynamic_cast<SvMemoryStream*>(endRedirect());
9795 // FIXME: bound rect will not work with rotated matrix
9796 rTile.m_aRectangle = Rectangle( Point(0,0), aConvertRect.GetSize() );
9797 rTile.m_aCellSize = aConvertRect.GetSize();
9799 OStringBuffer aObjName( 16 );
9800 aObjName.append( 'P' );
9801 aObjName.append( rTile.m_nObject );
9802 pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject );
9803 return nTilingId;
9806 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill )
9808 if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() )
9809 return;
9811 m_aPages.back().endStream();
9812 sal_Int32 nXObject = createObject();
9813 OStringBuffer aNameBuf( 16 );
9814 aNameBuf.append( "Pol" );
9815 aNameBuf.append( nXObject );
9816 OString aObjName( aNameBuf.makeStringAndClear() );
9817 Rectangle aObjRect;
9818 if( updateObject( nXObject ) )
9820 // get bounding rect of object
9821 PolyPolygon aSubDiv;
9822 rPolyPoly.AdaptiveSubdivide( aSubDiv );
9823 aObjRect = aSubDiv.GetBoundRect();
9824 Rectangle aConvObjRect( aObjRect );
9825 m_aPages.back().convertRect( aConvObjRect );
9827 // move polypolygon to bottom left of page
9828 PolyPolygon aLocalPath( rPolyPoly );
9829 sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72;
9830 sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72;
9831 Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode );
9832 sal_Int32 nXOff = aObjRect.Left();
9833 sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom();
9834 aLocalPath.Move( -nXOff, nYOff );
9836 // prepare XObject's content stream
9837 OStringBuffer aStream( 512 );
9838 aStream.append( "/Pattern cs /P" );
9839 aStream.append( m_aTilings[ nPattern ].m_nObject );
9840 aStream.append( " scn\n" );
9841 m_aPages.back().appendPolyPolygon( aLocalPath, aStream );
9842 aStream.append( bEOFill ? "f*" : "f" );
9843 SvMemoryStream aMemStream( aStream.getLength() );
9844 aMemStream.Write( aStream.getStr(), aStream.getLength() );
9845 bool bDeflate = compressStream( &aMemStream );
9846 aMemStream.Seek( STREAM_SEEK_TO_END );
9847 sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell();
9848 aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
9850 // add new XObject to global resource dict
9851 m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject;
9853 // write XObject
9854 OStringBuffer aLine( 512 );
9855 aLine.append( nXObject );
9856 aLine.append( " 0 obj\n"
9857 "<</Type/XObject/Subtype/Form/BBox[0 0 " );
9858 appendFixedInt( aConvObjRect.GetWidth(), aLine );
9859 aLine.append( ' ' );
9860 appendFixedInt( aConvObjRect.GetHeight(), aLine );
9861 aLine.append( "]/Length " );
9862 aLine.append( nStreamLen );
9863 if( bDeflate )
9864 aLine.append( "/Filter/FlateDecode" );
9865 aLine.append( ">>\n"
9866 "stream\n" );
9867 writeBuffer( aLine.getStr(), aLine.getLength() );
9868 checkAndEnableStreamEncryption( nXObject );
9869 writeBuffer( aMemStream.GetData(), nStreamLen );
9870 disableStreamEncryption();
9871 writeBuffer( "\nendstream\nendobj\n\n", 19 );
9873 m_aPages.back().beginStream();
9874 OStringBuffer aLine( 80 );
9875 aLine.append( "q 1 0 0 1 " );
9876 m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine );
9877 aLine.append( " cm/" );
9878 aLine.append( aObjName );
9879 aLine.append( " Do Q\n" );
9880 writeBuffer( aLine.getStr(), aLine.getLength() );
9883 void PDFWriterImpl::updateGraphicsState()
9885 OStringBuffer aLine( 256 );
9886 GraphicsState& rNewState = m_aGraphicsStack.front();
9887 // first set clip region since it might invalidate everything else
9889 if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) )
9891 rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion;
9893 Region& rNewClip = rNewState.m_aClipRegion;
9895 /* #103137# equality operator is not implemented
9896 * const as API promises but may change Region
9897 * from Polygon to rectangles. Arrrgghh !!!!
9899 Region aLeft = m_aCurrentPDFState.m_aClipRegion;
9900 Region aRight = rNewClip;
9901 if( aLeft != aRight )
9903 if( ! m_aCurrentPDFState.m_aClipRegion.IsEmpty() &&
9904 ! m_aCurrentPDFState.m_aClipRegion.IsNull() )
9906 aLine.append( "Q " );
9907 // invalidate everything but the clip region
9908 m_aCurrentPDFState = GraphicsState();
9909 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion);
9911 if( ! rNewClip.IsEmpty() && ! rNewClip.IsNull() )
9913 // clip region is always stored in private PDF mapmode
9914 MapMode aNewMapMode = rNewState.m_aMapMode;
9915 rNewState.m_aMapMode = m_aMapMode;
9916 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
9917 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
9919 aLine.append( "q " );
9920 if( rNewClip.HasPolyPolygon() )
9922 m_aPages.back().appendPolyPolygon( rNewClip.GetPolyPolygon(), aLine );
9923 aLine.append( "W* n\n" );
9925 else
9927 // need to clip all rectangles
9928 RegionHandle aHandle = rNewClip.BeginEnumRects();
9929 Rectangle aRect;
9930 while( rNewClip.GetNextEnumRect( aHandle, aRect ) )
9932 m_aPages.back().appendRect( aRect, aLine );
9933 if( aLine.getLength() > 80 )
9935 aLine.append( "\n" );
9936 writeBuffer( aLine.getStr(), aLine.getLength() );
9937 aLine.setLength( 0 );
9939 else
9940 aLine.append( ' ' );
9942 rNewClip.EndEnumRects( aHandle );
9943 aLine.append( "W* n\n" );
9946 rNewState.m_aMapMode = aNewMapMode;
9947 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
9948 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
9953 if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) )
9955 rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode;
9956 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
9959 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) )
9961 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont;
9962 getReferenceDevice()->SetFont( rNewState.m_aFont );
9963 getReferenceDevice()->ImplNewFont();
9966 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) )
9968 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode;
9969 getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
9972 if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) )
9974 rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage;
9975 getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
9978 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) )
9980 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor;
9981 if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
9982 rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
9984 appendStrokingColor( rNewState.m_aLineColor, aLine );
9985 aLine.append( "\n" );
9989 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) )
9991 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor;
9992 if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
9993 rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
9995 appendNonStrokingColor( rNewState.m_aFillColor, aLine );
9996 aLine.append( "\n" );
10000 if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) )
10002 rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent;
10003 if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent )
10005 // TODO: switch extended graphicsstate
10009 // everything is up to date now
10010 m_aCurrentPDFState = m_aGraphicsStack.front();
10011 if( aLine.getLength() )
10012 writeBuffer( aLine.getStr(), aLine.getLength() );
10015 /* #i47544# imitate OutputDevice behaviour:
10016 * if a font with a nontransparent color is set, it overwrites the current
10017 * text color. OTOH setting the text color will overwrite the color of the font.
10019 void PDFWriterImpl::setFont( const Font& rFont )
10021 Color aColor = rFont.GetColor();
10022 if( aColor == Color( COL_TRANSPARENT ) )
10023 aColor = m_aGraphicsStack.front().m_aFont.GetColor();
10024 m_aGraphicsStack.front().m_aFont = rFont;
10025 m_aGraphicsStack.front().m_aFont.SetColor( aColor );
10026 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont;
10029 void PDFWriterImpl::push( sal_uInt16 nFlags )
10031 m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
10032 m_aGraphicsStack.front().m_nFlags = nFlags;
10035 void PDFWriterImpl::pop()
10037 GraphicsState aState = m_aGraphicsStack.front();
10038 m_aGraphicsStack.pop_front();
10039 GraphicsState& rOld = m_aGraphicsStack.front();
10041 // move those parameters back that were not pushed
10042 // in the first place
10043 if( ! (aState.m_nFlags & PUSH_LINECOLOR) )
10044 setLineColor( aState.m_aLineColor );
10045 if( ! (aState.m_nFlags & PUSH_FILLCOLOR) )
10046 setFillColor( aState.m_aFillColor );
10047 if( ! (aState.m_nFlags & PUSH_FONT) )
10048 setFont( aState.m_aFont );
10049 if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) )
10050 setTextColor( aState.m_aFont.GetColor() );
10051 if( ! (aState.m_nFlags & PUSH_MAPMODE) )
10052 setMapMode( aState.m_aMapMode );
10053 if( ! (aState.m_nFlags & PUSH_CLIPREGION) )
10054 // do not use setClipRegion here
10055 // it would convert again assuming the current mapmode
10056 rOld.m_aClipRegion = aState.m_aClipRegion;
10057 if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) )
10058 setTextLineColor( aState.m_aTextLineColor );
10059 if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) )
10060 setOverlineColor( aState.m_aOverlineColor );
10061 if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) )
10062 setTextAlign( aState.m_aFont.GetAlign() );
10063 if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) )
10064 setTextFillColor( aState.m_aFont.GetFillColor() );
10065 if( ! (aState.m_nFlags & PUSH_REFPOINT) )
10067 // what ?
10069 // invalidate graphics state
10070 m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U);
10073 void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
10075 m_aGraphicsStack.front().m_aMapMode = rMapMode;
10076 getReferenceDevice()->SetMapMode( rMapMode );
10077 m_aCurrentPDFState.m_aMapMode = rMapMode;
10080 void PDFWriterImpl::setClipRegion( const Region& rRegion )
10082 Region aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10083 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10084 m_aGraphicsStack.front().m_aClipRegion = aRegion;
10085 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10088 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
10090 Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10091 m_aMapMode,
10092 getReferenceDevice(),
10093 Point( nX, nY ) ) );
10094 aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10095 m_aMapMode,
10096 getReferenceDevice(),
10097 Point() );
10098 m_aGraphicsStack.front().m_aClipRegion.Move( aPoint.X(), aPoint.Y() );
10099 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10102 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect )
10104 Rectangle aRect( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10105 m_aMapMode,
10106 getReferenceDevice(),
10107 rRect ) );
10108 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10109 return m_aGraphicsStack.front().m_aClipRegion.Intersect( aRect );
10113 bool PDFWriterImpl::intersectClipRegion( const Region& rRegion )
10115 Region aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10116 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10117 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10118 return m_aGraphicsStack.front().m_aClipRegion.Intersect( aRegion );
10121 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
10123 if( nPageNr < 0 )
10124 nPageNr = m_nCurrentPage;
10126 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10127 return;
10129 m_aNotes.push_back( PDFNoteEntry() );
10130 m_aNotes.back().m_nObject = createObject();
10131 m_aNotes.back().m_aContents = rNote;
10132 m_aNotes.back().m_aRect = rRect;
10133 // convert to default user space now, since the mapmode may change
10134 m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
10136 // insert note to page's annotation list
10137 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
10140 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr )
10142 if( nPageNr < 0 )
10143 nPageNr = m_nCurrentPage;
10145 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10146 return -1;
10148 sal_Int32 nRet = m_aLinks.size();
10150 m_aLinks.push_back( PDFLink() );
10151 m_aLinks.back().m_nObject = createObject();
10152 m_aLinks.back().m_nPage = nPageNr;
10153 m_aLinks.back().m_aRect = rRect;
10154 // convert to default user space now, since the mapmode may change
10155 m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
10157 // insert link to page's annotation list
10158 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
10160 return nRet;
10163 //--->i56629
10164 sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10166 if( nPageNr < 0 )
10167 nPageNr = m_nCurrentPage;
10169 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10170 return -1;
10172 sal_Int32 nRet = m_aNamedDests.size();
10174 m_aNamedDests.push_back( PDFNamedDest() );
10175 m_aNamedDests.back().m_aDestName = sDestName;
10176 m_aNamedDests.back().m_nPage = nPageNr;
10177 m_aNamedDests.back().m_eType = eType;
10178 m_aNamedDests.back().m_aRect = rRect;
10179 // convert to default user space now, since the mapmode may change
10180 m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
10182 return nRet;
10184 //<---i56629
10186 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10188 if( nPageNr < 0 )
10189 nPageNr = m_nCurrentPage;
10191 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10192 return -1;
10194 sal_Int32 nRet = m_aDests.size();
10196 m_aDests.push_back( PDFDest() );
10197 m_aDests.back().m_nPage = nPageNr;
10198 m_aDests.back().m_eType = eType;
10199 m_aDests.back().m_aRect = rRect;
10200 // convert to default user space now, since the mapmode may change
10201 m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
10203 return nRet;
10206 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
10208 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10209 return -1;
10210 if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
10211 return -2;
10213 m_aLinks[ nLinkId ].m_nDest = nDestId;
10215 return 0;
10218 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
10220 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10221 return -1;
10223 m_aLinks[ nLinkId ].m_nDest = -1;
10225 using namespace ::com::sun::star;
10227 if (!m_xTrans.is())
10229 uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() );
10230 if( xFact.is() )
10232 m_xTrans = uno::Reference < util::XURLTransformer >(
10233 xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY );
10237 util::URL aURL;
10238 aURL.Complete = rURL;
10240 if (m_xTrans.is())
10241 m_xTrans->parseStrict( aURL );
10243 m_aLinks[ nLinkId ].m_aURL = aURL.Complete;
10245 return 0;
10248 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
10250 m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
10253 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
10255 // create new item
10256 sal_Int32 nNewItem = m_aOutline.size();
10257 m_aOutline.push_back( PDFOutlineEntry() );
10259 // set item attributes
10260 setOutlineItemParent( nNewItem, nParent );
10261 setOutlineItemText( nNewItem, rText );
10262 setOutlineItemDest( nNewItem, nDestID );
10264 return nNewItem;
10267 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
10269 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10270 return -1;
10272 int nRet = 0;
10274 if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
10276 nNewParent = 0;
10277 nRet = -2;
10279 // remove item from previous parent
10280 sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID;
10281 if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() )
10283 PDFOutlineEntry& rParent = m_aOutline[ nParentID ];
10285 for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin();
10286 it != rParent.m_aChildren.end(); ++it )
10288 if( *it == nItem )
10290 rParent.m_aChildren.erase( it );
10291 break;
10296 // insert item to new parent's list of children
10297 m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
10299 return nRet;
10302 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
10304 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10305 return -1;
10307 m_aOutline[ nItem ].m_aTitle = rText;
10308 return 0;
10311 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
10313 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
10314 return -1;
10315 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
10316 return -2;
10317 m_aOutline[nItem].m_nDestID = nDestID;
10318 return 0;
10321 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
10323 static std::map< PDFWriter::StructElement, const char* > aTagStrings;
10324 if( aTagStrings.empty() )
10326 aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
10327 aTagStrings[ PDFWriter::Document ] = "Document";
10328 aTagStrings[ PDFWriter::Part ] = "Part";
10329 aTagStrings[ PDFWriter::Article ] = "Art";
10330 aTagStrings[ PDFWriter::Section ] = "Sect";
10331 aTagStrings[ PDFWriter::Division ] = "Div";
10332 aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote";
10333 aTagStrings[ PDFWriter::Caption ] = "Caption";
10334 aTagStrings[ PDFWriter::TOC ] = "TOC";
10335 aTagStrings[ PDFWriter::TOCI ] = "TOCI";
10336 aTagStrings[ PDFWriter::Index ] = "Index";
10337 aTagStrings[ PDFWriter::Paragraph ] = "P";
10338 aTagStrings[ PDFWriter::Heading ] = "H";
10339 aTagStrings[ PDFWriter::H1 ] = "H1";
10340 aTagStrings[ PDFWriter::H2 ] = "H2";
10341 aTagStrings[ PDFWriter::H3 ] = "H3";
10342 aTagStrings[ PDFWriter::H4 ] = "H4";
10343 aTagStrings[ PDFWriter::H5 ] = "H5";
10344 aTagStrings[ PDFWriter::H6 ] = "H6";
10345 aTagStrings[ PDFWriter::List ] = "L";
10346 aTagStrings[ PDFWriter::ListItem ] = "LI";
10347 aTagStrings[ PDFWriter::LILabel ] = "Lbl";
10348 aTagStrings[ PDFWriter::LIBody ] = "LBody";
10349 aTagStrings[ PDFWriter::Table ] = "Table";
10350 aTagStrings[ PDFWriter::TableRow ] = "TR";
10351 aTagStrings[ PDFWriter::TableHeader ] = "TH";
10352 aTagStrings[ PDFWriter::TableData ] = "TD";
10353 aTagStrings[ PDFWriter::Span ] = "Span";
10354 aTagStrings[ PDFWriter::Quote ] = "Quote";
10355 aTagStrings[ PDFWriter::Note ] = "Note";
10356 aTagStrings[ PDFWriter::Reference ] = "Reference";
10357 aTagStrings[ PDFWriter::BibEntry ] = "BibEntry";
10358 aTagStrings[ PDFWriter::Code ] = "Code";
10359 aTagStrings[ PDFWriter::Link ] = "Link";
10360 aTagStrings[ PDFWriter::Figure ] = "Figure";
10361 aTagStrings[ PDFWriter::Formula ] = "Formula";
10362 aTagStrings[ PDFWriter::Form ] = "Form";
10365 std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
10367 return it != aTagStrings.end() ? it->second : "Div";
10370 void PDFWriterImpl::beginStructureElementMCSeq()
10372 if( m_bEmitStructure &&
10373 m_nCurrentStructElement > 0 && // StructTreeRoot
10374 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
10377 PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
10378 OStringBuffer aLine( 128 );
10379 sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
10380 aLine.append( "/" );
10381 if( rEle.m_aAlias.getLength() > 0 )
10382 aLine.append( rEle.m_aAlias );
10383 else
10384 aLine.append( getStructureTag( rEle.m_eType ) );
10385 aLine.append( "<</MCID " );
10386 aLine.append( nMCID );
10387 aLine.append( ">>BDC\n" );
10388 writeBuffer( aLine.getStr(), aLine.getLength() );
10390 // update the element's content list
10391 #if OSL_DEBUG_LEVEL > 1
10392 fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n",
10393 nMCID,
10394 m_aPages[ m_nCurrentPage ].m_nPageObject,
10395 rEle.m_nFirstPageObject );
10396 #endif
10397 rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) );
10398 // update the page's mcid parent list
10399 m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
10400 // mark element MC sequence as open
10401 rEle.m_bOpenMCSeq = true;
10403 // handle artifacts
10404 else if( ! m_bEmitStructure && m_aContext.Tagged &&
10405 m_nCurrentStructElement > 0 &&
10406 m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
10407 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
10410 OStringBuffer aLine( 128 );
10411 aLine.append( "/Artifact BMC\n" );
10412 writeBuffer( aLine.getStr(), aLine.getLength() );
10413 // mark element MC sequence as open
10414 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
10418 void PDFWriterImpl::endStructureElementMCSeq()
10420 if( m_nCurrentStructElement > 0 && // StructTreeRoot
10421 ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
10422 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
10425 writeBuffer( "EMC\n", 4 );
10426 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
10430 bool PDFWriterImpl::checkEmitStructure()
10432 bool bEmit = false;
10433 if( m_aContext.Tagged )
10435 bEmit = true;
10436 sal_Int32 nEle = m_nCurrentStructElement;
10437 while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
10439 if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
10441 bEmit = false;
10442 break;
10444 nEle = m_aStructure[ nEle ].m_nParentElement;
10447 return bEmit;
10450 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias )
10452 if( m_nCurrentPage < 0 )
10453 return -1;
10455 if( ! m_aContext.Tagged )
10456 return -1;
10458 // close eventual current MC sequence
10459 endStructureElementMCSeq();
10461 if( m_nCurrentStructElement == 0 &&
10462 eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
10464 // struct tree root hit, but not beginning document
10465 // this might happen with setCurrentStructureElement
10466 // silently insert structure into document again if one properly exists
10467 if( ! m_aStructure[ 0 ].m_aChildren.empty() )
10469 PDFWriter::StructElement childType = PDFWriter::NonStructElement;
10470 sal_Int32 nNewCurElement = 0;
10471 const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
10472 for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
10473 childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
10475 nNewCurElement = *it;
10476 childType = m_aStructure[ nNewCurElement ].m_eType;
10478 if( childType == PDFWriter::Document )
10480 m_nCurrentStructElement = nNewCurElement;
10481 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" );
10483 else {
10484 DBG_ERROR( "document structure in disorder !" );
10487 else {
10488 DBG_ERROR( "PDF document structure MUST be contained in a Document element" );
10492 sal_Int32 nNewId = sal_Int32(m_aStructure.size());
10493 m_aStructure.push_back( PDFStructureElement() );
10494 PDFStructureElement& rEle = m_aStructure.back();
10495 rEle.m_eType = eType;
10496 rEle.m_nOwnElement = nNewId;
10497 rEle.m_nParentElement = m_nCurrentStructElement;
10498 rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject;
10499 m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
10500 m_nCurrentStructElement = nNewId;
10502 // handle alias names
10503 if( rAlias.getLength() && eType != PDFWriter::NonStructElement )
10505 OStringBuffer aNameBuf( rAlias.getLength() );
10506 appendName( rAlias, aNameBuf );
10507 OString aAliasName( aNameBuf.makeStringAndClear() );
10508 rEle.m_aAlias = aAliasName;
10509 m_aRoleMap[ aAliasName ] = getStructureTag( eType );
10512 #if OSL_DEBUG_LEVEL > 1
10513 OStringBuffer aLine( "beginStructureElement " );
10514 aLine.append( m_nCurrentStructElement );
10515 aLine.append( ": " );
10516 aLine.append( getStructureTag( eType ) );
10517 if( rEle.m_aAlias.getLength() )
10519 aLine.append( " aliased as \"" );
10520 aLine.append( rEle.m_aAlias );
10521 aLine.append( '\"' );
10523 emitComment( aLine.getStr() );
10524 #endif
10526 // check whether to emit structure henceforth
10527 m_bEmitStructure = checkEmitStructure();
10529 if( m_bEmitStructure ) // don't create nonexistant objects
10531 rEle.m_nObject = createObject();
10532 // update parent's kids list
10533 m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject );
10535 return nNewId;
10538 void PDFWriterImpl::endStructureElement()
10540 if( m_nCurrentPage < 0 )
10541 return;
10543 if( ! m_aContext.Tagged )
10544 return;
10546 if( m_nCurrentStructElement == 0 )
10548 // hit the struct tree root, that means there is an endStructureElement
10549 // without corresponding beginStructureElement
10550 return;
10553 // end the marked content sequence
10554 endStructureElementMCSeq();
10556 #if OSL_DEBUG_LEVEL > 1
10557 OStringBuffer aLine( "endStructureElement " );
10558 aLine.append( m_nCurrentStructElement );
10559 aLine.append( ": " );
10560 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
10561 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
10563 aLine.append( " aliased as \"" );
10564 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
10565 aLine.append( '\"' );
10567 #endif
10569 // "end" the structure element, the parent becomes current element
10570 m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
10572 // check whether to emit structure henceforth
10573 m_bEmitStructure = checkEmitStructure();
10575 #if OSL_DEBUG_LEVEL > 1
10576 if( m_bEmitStructure )
10577 emitComment( aLine.getStr() );
10578 #endif
10581 //---> i94258
10583 * This function adds an internal structure list container to overcome the 8191 elements array limitation
10584 * in kids element emission.
10585 * Recursive function
10588 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
10590 if( rEle.m_eType == PDFWriter::NonStructElement &&
10591 rEle.m_nOwnElement != rEle.m_nParentElement )
10592 return;
10594 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
10596 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
10598 PDFStructureElement& rChild = m_aStructure[ *it ];
10599 if( rChild.m_eType != PDFWriter::NonStructElement )
10601 //triggered when a child of the rEle element is found
10602 if( rChild.m_nParentElement == rEle.m_nOwnElement )
10603 addInternalStructureContainer( rChild );//examine the child
10604 else
10606 DBG_ERROR( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
10607 #if OSL_DEBUG_LEVEL > 1
10608 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
10609 #endif
10613 else
10615 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
10616 #if OSL_DEBUG_LEVEL > 1
10617 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
10618 #endif
10622 if( rEle.m_nOwnElement != rEle.m_nParentElement )
10624 if( !rEle.m_aKids.empty() )
10626 if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
10627 //then we need to add the containers for the kids elements
10628 // a list to be used for the new kid element
10629 std::list< PDFStructureElementKid > aNewKids;
10630 std::list< sal_Int32 > aNewChildren;
10632 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
10633 OStringBuffer aNameBuf( "Div" );
10634 OString aAliasName( aNameBuf.makeStringAndClear() );
10635 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
10637 while( rEle.m_aKids.size() > ncMaxPDFArraySize )
10639 sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
10640 sal_Int32 nNewId = sal_Int32(m_aStructure.size());
10641 m_aStructure.push_back( PDFStructureElement() );
10642 PDFStructureElement& rEleNew = m_aStructure.back();
10643 rEleNew.m_aAlias = aAliasName;
10644 rEleNew.m_eType = PDFWriter::Division; // a new Div type container
10645 rEleNew.m_nOwnElement = nNewId;
10646 rEleNew.m_nParentElement = nCurrentStructElement;
10647 //inherit the same page as the first child to be reparented
10648 rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
10649 rEleNew.m_nObject = createObject();//assign a PDF object number
10650 //add the object to the kid list of the parent
10651 aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) );
10652 aNewChildren.push_back( nNewId );
10654 std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
10655 std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
10656 advance( aChildEndIt, ncMaxPDFArraySize );
10657 advance( aKidEndIt, ncMaxPDFArraySize );
10659 rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
10660 rEle.m_aKids,
10661 rEle.m_aKids.begin(),
10662 aKidEndIt );
10663 rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
10664 rEle.m_aChildren,
10665 rEle.m_aChildren.begin(),
10666 aChildEndIt );
10667 // set the kid's new parent
10668 for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
10669 it != rEleNew.m_aChildren.end(); ++it )
10671 m_aStructure[ *it ].m_nParentElement = nNewId;
10674 //finally add the new kids resulting from the container added
10675 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
10676 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
10681 //<--- i94258
10683 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
10685 bool bSuccess = false;
10687 if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
10689 // end eventual previous marked content sequence
10690 endStructureElementMCSeq();
10692 m_nCurrentStructElement = nEle;
10693 m_bEmitStructure = checkEmitStructure();
10694 #if OSL_DEBUG_LEVEL > 1
10695 OStringBuffer aLine( "setCurrentStructureElement " );
10696 aLine.append( m_nCurrentStructElement );
10697 aLine.append( ": " );
10698 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
10699 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
10701 aLine.append( " aliased as \"" );
10702 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
10703 aLine.append( '\"' );
10705 if( ! m_bEmitStructure )
10706 aLine.append( " (inside NonStruct)" );
10707 emitComment( aLine.getStr() );
10708 #endif
10709 bSuccess = true;
10712 return bSuccess;
10715 sal_Int32 PDFWriterImpl::getCurrentStructureElement()
10717 return m_nCurrentStructElement;
10720 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
10722 if( !m_aContext.Tagged )
10723 return false;
10725 bool bInsert = false;
10726 if( m_nCurrentStructElement > 0 && m_bEmitStructure )
10728 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
10729 switch( eAttr )
10731 case PDFWriter::Placement:
10732 if( eVal == PDFWriter::Block ||
10733 eVal == PDFWriter::Inline ||
10734 eVal == PDFWriter::Before ||
10735 eVal == PDFWriter::Start ||
10736 eVal == PDFWriter::End )
10737 bInsert = true;
10738 break;
10739 case PDFWriter::WritingMode:
10740 if( eVal == PDFWriter::LrTb ||
10741 eVal == PDFWriter::RlTb ||
10742 eVal == PDFWriter::TbRl )
10744 bInsert = true;
10746 break;
10747 case PDFWriter::TextAlign:
10748 if( eVal == PDFWriter::Start ||
10749 eVal == PDFWriter::Center ||
10750 eVal == PDFWriter::End ||
10751 eVal == PDFWriter::Justify )
10753 if( eType == PDFWriter::Paragraph ||
10754 eType == PDFWriter::Heading ||
10755 eType == PDFWriter::H1 ||
10756 eType == PDFWriter::H2 ||
10757 eType == PDFWriter::H3 ||
10758 eType == PDFWriter::H4 ||
10759 eType == PDFWriter::H5 ||
10760 eType == PDFWriter::H6 ||
10761 eType == PDFWriter::List ||
10762 eType == PDFWriter::ListItem ||
10763 eType == PDFWriter::LILabel ||
10764 eType == PDFWriter::LIBody ||
10765 eType == PDFWriter::Table ||
10766 eType == PDFWriter::TableRow ||
10767 eType == PDFWriter::TableHeader ||
10768 eType == PDFWriter::TableData )
10770 bInsert = true;
10773 break;
10774 case PDFWriter::Width:
10775 case PDFWriter::Height:
10776 if( eVal == PDFWriter::Auto )
10778 if( eType == PDFWriter::Figure ||
10779 eType == PDFWriter::Formula ||
10780 eType == PDFWriter::Form ||
10781 eType == PDFWriter::Table ||
10782 eType == PDFWriter::TableHeader ||
10783 eType == PDFWriter::TableData )
10785 bInsert = true;
10788 break;
10789 case PDFWriter::BlockAlign:
10790 if( eVal == PDFWriter::Before ||
10791 eVal == PDFWriter::Middle ||
10792 eVal == PDFWriter::After ||
10793 eVal == PDFWriter::Justify )
10795 if( eType == PDFWriter::TableHeader ||
10796 eType == PDFWriter::TableData )
10798 bInsert = true;
10801 break;
10802 case PDFWriter::InlineAlign:
10803 if( eVal == PDFWriter::Start ||
10804 eVal == PDFWriter::Center ||
10805 eVal == PDFWriter::End )
10807 if( eType == PDFWriter::TableHeader ||
10808 eType == PDFWriter::TableData )
10810 bInsert = true;
10813 break;
10814 case PDFWriter::LineHeight:
10815 if( eVal == PDFWriter::Normal ||
10816 eVal == PDFWriter::Auto )
10818 // only for ILSE and BLSE
10819 if( eType == PDFWriter::Paragraph ||
10820 eType == PDFWriter::Heading ||
10821 eType == PDFWriter::H1 ||
10822 eType == PDFWriter::H2 ||
10823 eType == PDFWriter::H3 ||
10824 eType == PDFWriter::H4 ||
10825 eType == PDFWriter::H5 ||
10826 eType == PDFWriter::H6 ||
10827 eType == PDFWriter::List ||
10828 eType == PDFWriter::ListItem ||
10829 eType == PDFWriter::LILabel ||
10830 eType == PDFWriter::LIBody ||
10831 eType == PDFWriter::Table ||
10832 eType == PDFWriter::TableRow ||
10833 eType == PDFWriter::TableHeader ||
10834 eType == PDFWriter::TableData ||
10835 eType == PDFWriter::Span ||
10836 eType == PDFWriter::Quote ||
10837 eType == PDFWriter::Note ||
10838 eType == PDFWriter::Reference ||
10839 eType == PDFWriter::BibEntry ||
10840 eType == PDFWriter::Code ||
10841 eType == PDFWriter::Link )
10843 bInsert = true;
10846 break;
10847 case PDFWriter::TextDecorationType:
10848 if( eVal == PDFWriter::NONE ||
10849 eVal == PDFWriter::Underline ||
10850 eVal == PDFWriter::Overline ||
10851 eVal == PDFWriter::LineThrough )
10853 // only for ILSE and BLSE
10854 if( eType == PDFWriter::Paragraph ||
10855 eType == PDFWriter::Heading ||
10856 eType == PDFWriter::H1 ||
10857 eType == PDFWriter::H2 ||
10858 eType == PDFWriter::H3 ||
10859 eType == PDFWriter::H4 ||
10860 eType == PDFWriter::H5 ||
10861 eType == PDFWriter::H6 ||
10862 eType == PDFWriter::List ||
10863 eType == PDFWriter::ListItem ||
10864 eType == PDFWriter::LILabel ||
10865 eType == PDFWriter::LIBody ||
10866 eType == PDFWriter::Table ||
10867 eType == PDFWriter::TableRow ||
10868 eType == PDFWriter::TableHeader ||
10869 eType == PDFWriter::TableData ||
10870 eType == PDFWriter::Span ||
10871 eType == PDFWriter::Quote ||
10872 eType == PDFWriter::Note ||
10873 eType == PDFWriter::Reference ||
10874 eType == PDFWriter::BibEntry ||
10875 eType == PDFWriter::Code ||
10876 eType == PDFWriter::Link )
10878 bInsert = true;
10881 break;
10882 case PDFWriter::ListNumbering:
10883 if( eVal == PDFWriter::NONE ||
10884 eVal == PDFWriter::Disc ||
10885 eVal == PDFWriter::Circle ||
10886 eVal == PDFWriter::Square ||
10887 eVal == PDFWriter::Decimal ||
10888 eVal == PDFWriter::UpperRoman ||
10889 eVal == PDFWriter::LowerRoman ||
10890 eVal == PDFWriter::UpperAlpha ||
10891 eVal == PDFWriter::LowerAlpha )
10893 if( eType == PDFWriter::List )
10894 bInsert = true;
10896 break;
10897 default: break;
10901 if( bInsert )
10902 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
10903 #if OSL_DEBUG_LEVEL > 1
10904 else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
10905 fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
10906 getAttributeTag( eAttr ),
10907 getAttributeValueTag( eVal ),
10908 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
10909 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
10911 #endif
10913 return bInsert;
10916 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
10918 if( ! m_aContext.Tagged )
10919 return false;
10921 bool bInsert = false;
10922 if( m_nCurrentStructElement > 0 && m_bEmitStructure )
10924 if( eAttr == PDFWriter::Language )
10926 m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue );
10927 return true;
10930 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
10931 switch( eAttr )
10933 case PDFWriter::SpaceBefore:
10934 case PDFWriter::SpaceAfter:
10935 case PDFWriter::StartIndent:
10936 case PDFWriter::EndIndent:
10937 // just for BLSE
10938 if( eType == PDFWriter::Paragraph ||
10939 eType == PDFWriter::Heading ||
10940 eType == PDFWriter::H1 ||
10941 eType == PDFWriter::H2 ||
10942 eType == PDFWriter::H3 ||
10943 eType == PDFWriter::H4 ||
10944 eType == PDFWriter::H5 ||
10945 eType == PDFWriter::H6 ||
10946 eType == PDFWriter::List ||
10947 eType == PDFWriter::ListItem ||
10948 eType == PDFWriter::LILabel ||
10949 eType == PDFWriter::LIBody ||
10950 eType == PDFWriter::Table ||
10951 eType == PDFWriter::TableRow ||
10952 eType == PDFWriter::TableHeader ||
10953 eType == PDFWriter::TableData )
10955 bInsert = true;
10957 break;
10958 case PDFWriter::TextIndent:
10959 // paragraph like BLSE and additional elements
10960 if( eType == PDFWriter::Paragraph ||
10961 eType == PDFWriter::Heading ||
10962 eType == PDFWriter::H1 ||
10963 eType == PDFWriter::H2 ||
10964 eType == PDFWriter::H3 ||
10965 eType == PDFWriter::H4 ||
10966 eType == PDFWriter::H5 ||
10967 eType == PDFWriter::H6 ||
10968 eType == PDFWriter::LILabel ||
10969 eType == PDFWriter::LIBody ||
10970 eType == PDFWriter::TableHeader ||
10971 eType == PDFWriter::TableData )
10973 bInsert = true;
10975 break;
10976 case PDFWriter::Width:
10977 case PDFWriter::Height:
10978 if( eType == PDFWriter::Figure ||
10979 eType == PDFWriter::Formula ||
10980 eType == PDFWriter::Form ||
10981 eType == PDFWriter::Table ||
10982 eType == PDFWriter::TableHeader ||
10983 eType == PDFWriter::TableData )
10985 bInsert = true;
10987 break;
10988 case PDFWriter::LineHeight:
10989 case PDFWriter::BaselineShift:
10990 // only for ILSE and BLSE
10991 if( eType == PDFWriter::Paragraph ||
10992 eType == PDFWriter::Heading ||
10993 eType == PDFWriter::H1 ||
10994 eType == PDFWriter::H2 ||
10995 eType == PDFWriter::H3 ||
10996 eType == PDFWriter::H4 ||
10997 eType == PDFWriter::H5 ||
10998 eType == PDFWriter::H6 ||
10999 eType == PDFWriter::List ||
11000 eType == PDFWriter::ListItem ||
11001 eType == PDFWriter::LILabel ||
11002 eType == PDFWriter::LIBody ||
11003 eType == PDFWriter::Table ||
11004 eType == PDFWriter::TableRow ||
11005 eType == PDFWriter::TableHeader ||
11006 eType == PDFWriter::TableData ||
11007 eType == PDFWriter::Span ||
11008 eType == PDFWriter::Quote ||
11009 eType == PDFWriter::Note ||
11010 eType == PDFWriter::Reference ||
11011 eType == PDFWriter::BibEntry ||
11012 eType == PDFWriter::Code ||
11013 eType == PDFWriter::Link )
11015 bInsert = true;
11017 break;
11018 case PDFWriter::RowSpan:
11019 case PDFWriter::ColSpan:
11020 // only for table cells
11021 if( eType == PDFWriter::TableHeader ||
11022 eType == PDFWriter::TableData )
11024 bInsert = true;
11026 break;
11027 case PDFWriter::LinkAnnotation:
11028 if( eType == PDFWriter::Link )
11029 bInsert = true;
11030 break;
11031 default: break;
11035 if( bInsert )
11036 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
11037 #if OSL_DEBUG_LEVEL > 1
11038 else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11039 fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
11040 getAttributeTag( eAttr ),
11041 (int)nValue,
11042 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11043 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() );
11044 #endif
11046 return bInsert;
11049 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect )
11051 sal_Int32 nPageNr = m_nCurrentPage;
11052 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
11053 return;
11056 if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11058 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11059 if( eType == PDFWriter::Figure ||
11060 eType == PDFWriter::Formula ||
11061 eType == PDFWriter::Form ||
11062 eType == PDFWriter::Table )
11064 m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
11065 // convert to default user space now, since the mapmode may change
11066 m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
11071 void PDFWriterImpl::setActualText( const String& rText )
11073 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11075 m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
11079 void PDFWriterImpl::setAlternateText( const String& rText )
11081 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11083 m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
11087 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
11089 if( nPageNr < 0 )
11090 nPageNr = m_nCurrentPage;
11092 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11093 return;
11095 m_aPages[ nPageNr ].m_nDuration = nSeconds;
11098 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
11100 if( nPageNr < 0 )
11101 nPageNr = m_nCurrentPage;
11103 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11104 return;
11106 m_aPages[ nPageNr ].m_eTransition = eType;
11107 m_aPages[ nPageNr ].m_nTransTime = nMilliSec;
11110 void PDFWriterImpl::ensureUniqueRadioOnValues()
11112 // loop over radio groups
11113 for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
11114 group != m_aRadioGroupWidgets.end(); ++group )
11116 PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
11117 // check whether all kids have a unique OnValue
11118 std::hash_map< OUString, sal_Int32, OUStringHash > aOnValues;
11119 int nChildren = rGroupWidget.m_aKidsIndex.size();
11120 bool bIsUnique = true;
11121 for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
11123 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11124 const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
11125 #if OSL_DEBUG_LEVEL > 1
11126 fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() );
11127 #endif
11128 if( aOnValues.find( rVal ) == aOnValues.end() )
11130 aOnValues[ rVal ] = 1;
11132 else
11134 bIsUnique = false;
11137 if( ! bIsUnique )
11139 #if OSL_DEBUG_LEVEL > 1
11140 fprintf( stderr, "enforcing unique OnValues\n" );
11141 #endif
11142 // make unique by using ascending OnValues
11143 for( int nKid = 0; nKid < nChildren; nKid++ )
11145 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11146 PDFWidget& rKid = m_aWidgets[nKidIndex];
11147 rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) );
11148 if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11149 rKid.m_aValue = rKid.m_aOnValue;
11152 // finally move the "Yes" appearance to the OnValue appearance
11153 for( int nKid = 0; nKid < nChildren; nKid++ )
11155 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11156 PDFWidget& rKid = m_aWidgets[nKidIndex];
11157 PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
11158 if( app_it != rKid.m_aAppearances.end() )
11160 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
11161 if( stream_it != app_it->second.end() )
11163 SvMemoryStream* pStream = stream_it->second;
11164 app_it->second.erase( stream_it );
11165 OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
11166 appendName( rKid.m_aOnValue, aBuf );
11167 (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11169 #if OSL_DEBUG_LEVEL > 1
11170 else
11171 fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" );
11172 #endif
11174 // update selected radio button
11175 if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11177 rGroupWidget.m_aValue = rKid.m_aValue;
11183 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
11185 sal_Int32 nRadioGroupWidget = -1;
11187 std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
11189 if( it == m_aRadioGroupWidgets.end() )
11191 m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
11192 sal_Int32(m_aWidgets.size());
11194 // new group, insert the radiobutton
11195 m_aWidgets.push_back( PDFWidget() );
11196 m_aWidgets.back().m_nObject = createObject();
11197 m_aWidgets.back().m_nPage = m_nCurrentPage;
11198 m_aWidgets.back().m_eType = PDFWriter::RadioButton;
11199 m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
11200 m_aWidgets.back().m_nFlags |= 0x00008000;
11202 // create radio button field name
11203 const rtl::OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ?
11204 rBtn.Name : rBtn.Text;
11205 if( rName.getLength() )
11207 m_aWidgets.back().m_aName = convertWidgetFieldName( rName );
11209 else
11211 m_aWidgets.back().m_aName = "RadioGroup";
11212 m_aWidgets.back().m_aName += OString::valueOf( rBtn.RadioGroup );
11215 else
11216 nRadioGroupWidget = it->second;
11218 return nRadioGroupWidget;
11221 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
11223 if( nPageNr < 0 )
11224 nPageNr = m_nCurrentPage;
11226 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11227 return -1;
11229 m_aWidgets.push_back( PDFWidget() );
11230 sal_Int32 nNewWidget = m_aWidgets.size()-1;
11232 // create eventual radio button before getting any references
11233 // from m_aWidgets as the push_back operation potentially assigns new
11234 // memory to the vector and thereby invalidates the reference
11235 int nRadioGroupWidget = -1;
11236 if( rControl.getType() == PDFWriter::RadioButton )
11237 nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
11239 PDFWidget& rNewWidget = m_aWidgets[nNewWidget];
11240 rNewWidget.m_nObject = createObject();
11241 rNewWidget.m_aRect = rControl.Location;
11242 rNewWidget.m_nPage = nPageNr;
11243 rNewWidget.m_eType = rControl.getType();
11245 // for unknown reasons the radio buttons of a radio group must not have a
11246 // field name, else the buttons are in fact check boxes -
11247 // that is multiple buttons of the radio group can be selected
11248 if( rControl.getType() != PDFWriter::RadioButton )
11250 // acrobat reader since 3.0 does not support unicode text
11251 // strings for the field name; so we need to encode unicodes
11252 // larger than 255
11254 rNewWidget.m_aName =
11255 convertWidgetFieldName( (m_aContext.Version > PDFWriter::PDF_1_2) ?
11256 rControl.Name : rControl.Text );
11257 // #i88040# acrobat reader crashes on empty field names,
11258 // so always create one
11259 if( rNewWidget.m_aName.getLength() == 0 )
11261 OUStringBuffer aBuf( 32 );
11262 aBuf.appendAscii( "Widget" );
11263 aBuf.append( nNewWidget );
11264 rNewWidget.m_aName = convertWidgetFieldName( aBuf.makeStringAndClear() );
11267 rNewWidget.m_aDescription = rControl.Description;
11268 rNewWidget.m_aText = rControl.Text;
11269 rNewWidget.m_nTextStyle = rControl.TextStyle &
11270 ( TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP |
11271 TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM |
11272 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
11273 rNewWidget.m_nTabOrder = rControl.TabOrder;
11275 // various properties are set via the flags (/Ff) property of the field dict
11276 if( rControl.ReadOnly )
11277 rNewWidget.m_nFlags |= 1;
11278 if( rControl.getType() == PDFWriter::PushButton )
11280 const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
11281 if( rNewWidget.m_nTextStyle == 0 )
11282 rNewWidget.m_nTextStyle =
11283 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER |
11284 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11286 rNewWidget.m_nFlags |= 0x00010000;
11287 if( rBtn.URL.getLength() )
11288 rNewWidget.m_aListEntries.push_back( rBtn.URL );
11289 rNewWidget.m_bSubmit = rBtn.Submit;
11290 rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
11291 rNewWidget.m_nDest = rBtn.Dest;
11292 createDefaultPushButtonAppearance( rNewWidget, rBtn );
11294 else if( rControl.getType() == PDFWriter::RadioButton )
11296 const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
11297 if( rNewWidget.m_nTextStyle == 0 )
11298 rNewWidget.m_nTextStyle =
11299 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11300 /* PDF sees a RadioButton group as one radio button with
11301 * children which are in turn check boxes
11303 * so we need to create a radio button on demand for a new group
11304 * and insert a checkbox for each RadioButtonWidget as its child
11306 rNewWidget.m_eType = PDFWriter::CheckBox;
11307 rNewWidget.m_nRadioGroup = rBtn.RadioGroup;
11309 DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" );
11311 PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
11312 rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
11313 rRadioButton.m_aKidsIndex.push_back( nNewWidget );
11314 rNewWidget.m_nParent = rRadioButton.m_nObject;
11316 rNewWidget.m_aValue = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) );
11317 rNewWidget.m_aOnValue = rBtn.OnValue;
11318 if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected )
11320 rNewWidget.m_aValue = rNewWidget.m_aOnValue;
11321 rRadioButton.m_aValue = rNewWidget.m_aOnValue;
11323 createDefaultRadioButtonAppearance( rNewWidget, rBtn );
11325 // union rect of radio group
11326 Rectangle aRect = rNewWidget.m_aRect;
11327 m_aPages[ nPageNr ].convertRect( aRect );
11328 rRadioButton.m_aRect.Union( aRect );
11330 else if( rControl.getType() == PDFWriter::CheckBox )
11332 const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
11333 if( rNewWidget.m_nTextStyle == 0 )
11334 rNewWidget.m_nTextStyle =
11335 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11337 rNewWidget.m_aValue = OUString::createFromAscii( rBox.Checked ? "Yes" : "Off" );
11338 // create default appearance before m_aRect gets transformed
11339 createDefaultCheckBoxAppearance( rNewWidget, rBox );
11341 else if( rControl.getType() == PDFWriter::ListBox )
11343 if( rNewWidget.m_nTextStyle == 0 )
11344 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
11346 const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
11347 rNewWidget.m_aListEntries = rLstBox.Entries;
11348 rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
11349 rNewWidget.m_aValue = rLstBox.Text;
11350 if( rLstBox.DropDown )
11351 rNewWidget.m_nFlags |= 0x00020000;
11352 if( rLstBox.Sort )
11353 rNewWidget.m_nFlags |= 0x00080000;
11354 if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 )
11355 rNewWidget.m_nFlags |= 0x00200000;
11357 createDefaultListBoxAppearance( rNewWidget, rLstBox );
11359 else if( rControl.getType() == PDFWriter::ComboBox )
11361 if( rNewWidget.m_nTextStyle == 0 )
11362 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
11364 const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
11365 rNewWidget.m_aValue = rBox.Text;
11366 rNewWidget.m_aListEntries = rBox.Entries;
11367 rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
11368 if( rBox.Sort )
11369 rNewWidget.m_nFlags |= 0x00080000;
11371 PDFWriter::ListBoxWidget aLBox;
11372 aLBox.Name = rBox.Name;
11373 aLBox.Description = rBox.Description;
11374 aLBox.Text = rBox.Text;
11375 aLBox.TextStyle = rBox.TextStyle;
11376 aLBox.ReadOnly = rBox.ReadOnly;
11377 aLBox.Border = rBox.Border;
11378 aLBox.BorderColor = rBox.BorderColor;
11379 aLBox.Background = rBox.Background;
11380 aLBox.BackgroundColor = rBox.BackgroundColor;
11381 aLBox.TextFont = rBox.TextFont;
11382 aLBox.TextColor = rBox.TextColor;
11383 aLBox.DropDown = true;
11384 aLBox.Sort = rBox.Sort;
11385 aLBox.MultiSelect = false;
11386 aLBox.Entries = rBox.Entries;
11388 createDefaultListBoxAppearance( rNewWidget, aLBox );
11390 else if( rControl.getType() == PDFWriter::Edit )
11392 if( rNewWidget.m_nTextStyle == 0 )
11393 rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
11395 const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl);
11396 if( rEdit.MultiLine )
11398 rNewWidget.m_nFlags |= 0x00001000;
11399 rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11401 if( rEdit.Password )
11402 rNewWidget.m_nFlags |= 0x00002000;
11403 if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 )
11404 rNewWidget.m_nFlags |= 0x00100000;
11405 rNewWidget.m_nMaxLen = rEdit.MaxLen;
11406 rNewWidget.m_aValue = rEdit.Text;
11408 createDefaultEditAppearance( rNewWidget, rEdit );
11411 // convert to default user space now, since the mapmode may change
11412 // note: create default appearances before m_aRect gets transformed
11413 m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
11415 // insert widget to page's annotation list
11416 m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
11418 // mark page as having widgets
11419 m_aPages[ nPageNr ].m_bHasWidgets = true;
11421 return nNewWidget;
11424 void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl )
11426 if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() )
11427 return;
11429 PDFWidget& rWidget = m_aWidgets[ nControl ];
11430 m_nCurrentControl = nControl;
11432 SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 );
11433 // back conversion of control rect to current MapMode; necessary because
11434 // MapMode between createControl and beginControlAppearance
11435 // could have changed; therefore the widget rectangle is
11436 // already converted
11437 Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ),
11438 rWidget.m_aRect.GetSize() );
11439 aBack = lcl_convert( m_aMapMode,
11440 m_aGraphicsStack.front().m_aMapMode,
11441 getReferenceDevice(),
11442 aBack );
11443 beginRedirect( pControlStream, aBack );
11444 writeBuffer( "/Tx BMC\n", 8 );
11447 bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState )
11449 bool bRet = false;
11450 if( ! m_aOutputStreams.empty() )
11451 writeBuffer( "\nEMC\n", 5 );
11452 SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect());
11453 if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() )
11455 PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ];
11456 OString aState, aStyle;
11457 switch( rWidget.m_eType )
11459 case PDFWriter::PushButton:
11460 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
11462 aState = (eState == PDFWriter::Up) ? "N" : "D";
11463 aStyle = "Standard";
11465 break;
11466 case PDFWriter::CheckBox:
11467 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
11469 aState = "N";
11470 aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes";
11471 /* cf PDFReference 3rd ed. V1.4 p539:
11472 recommended name for on state is "Yes",
11473 recommended name for off state is "Off"
11476 break;
11477 case PDFWriter::RadioButton:
11478 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
11480 aState = "N";
11481 if( eState == PDFWriter::Up )
11482 aStyle = "Off";
11483 else
11485 OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 );
11486 appendName( rWidget.m_aOnValue, aBuf );
11487 aStyle = aBuf.makeStringAndClear();
11490 break;
11491 case PDFWriter::Edit:
11492 aState = "N";
11493 aStyle = "Standard";
11494 break;
11495 case PDFWriter::ListBox:
11496 case PDFWriter::ComboBox:
11497 break;
11499 if( aState.getLength() && aStyle.getLength() )
11501 // delete eventual existing stream
11502 PDFAppearanceStreams::iterator it =
11503 rWidget.m_aAppearances[ aState ].find( aStyle );
11504 if( it != rWidget.m_aAppearances[ aState ].end() )
11505 delete it->second;
11506 rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance;
11507 bRet = true;
11511 if( ! bRet )
11512 delete pAppearance;
11514 m_nCurrentControl = -1;
11516 return bRet;
11519 void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress )
11521 if( pStream )
11523 m_aAdditionalStreams.push_back( PDFAddStream() );
11524 PDFAddStream& rStream = m_aAdditionalStreams.back();
11525 rStream.m_aMimeType = rMimeType.Len()
11526 ? OUString( rMimeType )
11527 : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) );
11528 rStream.m_pStream = pStream;
11529 rStream.m_bCompress = bCompress;
11533 /*************************************************************
11534 begin i12626 methods
11536 Implements Algorithm 3.2, step 1 only
11538 void PDFWriterImpl::padPassword( rtl::OUString aPassword, sal_uInt8 *paPasswordTarget )
11540 // get ansi-1252 version of the password string CHECKIT ! i12626
11541 rtl::OString aString = rtl::OUStringToOString( aPassword, RTL_TEXTENCODING_MS_1252 );
11543 //copy the string to the target
11544 sal_Int32 nToCopy = ( aString.getLength() < 32 ) ? aString.getLength() : 32;
11545 sal_Int32 nCurrentChar;
11547 for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ )
11548 paPasswordTarget[nCurrentChar] = (sal_uInt8)( aString.getStr()[nCurrentChar] );
11550 //pad it
11551 if( nCurrentChar < 32 )
11552 {//fill with standard byte string
11553 sal_Int32 i,y;
11554 for( i = nCurrentChar, y = 0 ; i < 32; i++, y++ )
11555 paPasswordTarget[i] = m_nPadString[y];
11559 /**********************************
11560 Algorithm 3.2 Compute the encryption key used
11562 step 1 should already be done before calling, the paThePaddedPassword parameter should contain
11563 the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter,
11564 it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used
11566 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec.
11569 void PDFWriterImpl::computeEncryptionKey(sal_uInt8 *paThePaddedPassword, sal_uInt8 *paEncryptionKey )
11571 //step 2
11572 if( m_aDigest )
11574 rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, paThePaddedPassword, ENCRYPTED_PWD_SIZE );
11575 //step 3
11576 if( nError == rtl_Digest_E_None )
11577 nError = rtl_digest_updateMD5( m_aDigest, m_nEncryptedOwnerPassword , sizeof( m_nEncryptedOwnerPassword ) );
11578 //Step 4
11579 sal_uInt8 nPerm[4];
11581 nPerm[0] = (sal_uInt8)m_nAccessPermissions;
11582 nPerm[1] = (sal_uInt8)( m_nAccessPermissions >> 8 );
11583 nPerm[2] = (sal_uInt8)( m_nAccessPermissions >> 16 );
11584 nPerm[3] = (sal_uInt8)( m_nAccessPermissions >> 24 );
11586 if( nError == rtl_Digest_E_None )
11587 nError = rtl_digest_updateMD5( m_aDigest, nPerm , sizeof( nPerm ) );
11589 //step 5, get the document ID, binary form
11590 if( nError == rtl_Digest_E_None )
11591 nError = rtl_digest_updateMD5( m_aDigest, m_nDocID , sizeof( m_nDocID ) );
11592 //get the digest
11593 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
11594 if( nError == rtl_Digest_E_None )
11596 rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) );
11598 //step 6, only if 128 bit
11599 if( m_aContext.Security128bit )
11601 for( sal_Int32 i = 0; i < 50; i++ )
11603 nError = rtl_digest_updateMD5( m_aDigest, &nMD5Sum, sizeof( nMD5Sum ) );
11604 if( nError != rtl_Digest_E_None )
11605 break;
11606 rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) );
11610 //Step 7
11611 for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ )
11612 paEncryptionKey[i] = nMD5Sum[i];
11616 /**********************************
11617 Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member
11618 the step numbers down here correspond to the ones in PDF v.1.4 specfication
11620 void PDFWriterImpl::computeODictionaryValue()
11622 //step 1 already done, data is in m_nPaddedOwnerPassword
11623 //step 2
11624 if( m_aDigest )
11626 rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, &m_nPaddedOwnerPassword, sizeof( m_nPaddedOwnerPassword ) );
11627 if( nError == rtl_Digest_E_None )
11629 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
11631 rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof(nMD5Sum) );
11632 //step 3, only if 128 bit
11633 if( m_aContext.Security128bit )
11635 sal_Int32 i;
11636 for( i = 0; i < 50; i++ )
11638 nError = rtl_digest_updateMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) );
11639 if( nError != rtl_Digest_E_None )
11640 break;
11641 rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) );
11644 //Step 4, the key is in nMD5Sum
11645 //step 5 already done, data is in m_nPaddedUserPassword
11646 //step 6
11647 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode,
11648 nMD5Sum, m_nKeyLength , NULL, 0 );
11649 // encrypt the user password using the key set above
11650 rtl_cipher_encodeARCFOUR( m_aCipher, m_nPaddedUserPassword, sizeof( m_nPaddedUserPassword ), // the data to be encrypted
11651 m_nEncryptedOwnerPassword, sizeof( m_nEncryptedOwnerPassword ) ); //encrypted data, stored in class data member
11652 //Step 7, only if 128 bit
11653 if( m_aContext.Security128bit )
11655 sal_uInt32 i, y;
11656 sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key
11658 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
11660 for( y = 0; y < sizeof( nLocalKey ); y++ )
11661 nLocalKey[y] = (sal_uInt8)( nMD5Sum[y] ^ i );
11663 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode,
11664 nLocalKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area, on init can be NULL
11665 rtl_cipher_encodeARCFOUR( m_aCipher, m_nEncryptedOwnerPassword, sizeof( m_nEncryptedOwnerPassword ), // the data to be encrypted
11666 m_nEncryptedOwnerPassword, sizeof( m_nEncryptedOwnerPassword ) ); // encrypted data, can be the same as the input, encrypt "in place"
11667 //step 8, store in class data member
11674 /**********************************
11675 Algorithms 3.4 and 3.5 Compute the encryption dictionary /U value, save into the class data member, revision 2 (40 bit) or 3 (128 bit)
11677 void PDFWriterImpl::computeUDictionaryValue()
11679 //step 1, common to both 3.4 and 3.5
11680 computeEncryptionKey( m_nPaddedUserPassword , m_nEncryptionKey );
11682 if( m_aContext.Security128bit == false )
11684 //3.4
11685 //step 2 and 3
11686 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode,
11687 m_nEncryptionKey, 5 , // key and key length
11688 NULL, 0 ); //destination data area
11689 // encrypt the user password using the key set above, save for later use
11690 rtl_cipher_encodeARCFOUR( m_aCipher, m_nPadString, sizeof( m_nPadString ), // the data to be encrypted
11691 m_nEncryptedUserPassword, sizeof( m_nEncryptedUserPassword ) ); //encrypted data, stored in class data member
11693 else
11695 //or 3.5, for 128 bit security
11696 //step6, initilize the last 16 bytes of the encrypted user password to 0
11697 for(sal_uInt32 i = MD5_DIGEST_SIZE; i < sizeof( m_nEncryptedUserPassword ); i++)
11698 m_nEncryptedUserPassword[i] = 0;
11699 //step 2
11700 if( m_aDigest )
11702 rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, m_nPadString, sizeof( m_nPadString ) );
11703 //step 3
11704 if( nError == rtl_Digest_E_None )
11705 nError = rtl_digest_updateMD5( m_aDigest, m_nDocID , sizeof(m_nDocID) );
11707 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
11708 rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof(nMD5Sum) );
11709 //Step 4
11710 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode,
11711 m_nEncryptionKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area
11712 rtl_cipher_encodeARCFOUR( m_aCipher, nMD5Sum, sizeof( nMD5Sum ), // the data to be encrypted
11713 m_nEncryptedUserPassword, sizeof( nMD5Sum ) ); //encrypted data, stored in class data member
11714 //step 5
11715 sal_uInt32 i, y;
11716 sal_uInt8 nLocalKey[SECUR_128BIT_KEY];
11718 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
11720 for( y = 0; y < sizeof( nLocalKey ) ; y++ )
11721 nLocalKey[y] = (sal_uInt8)( m_nEncryptionKey[y] ^ i );
11723 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode,
11724 nLocalKey, SECUR_128BIT_KEY, // key and key length
11725 NULL, 0 ); //destination data area, on init can be NULL
11726 rtl_cipher_encodeARCFOUR( m_aCipher, m_nEncryptedUserPassword, SECUR_128BIT_KEY, // the data to be encrypted
11727 m_nEncryptedUserPassword, SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt "in place"
11733 /* init the encryption engine
11734 1. init the document id, used both for building the document id and for building the encryption key(s)
11735 2. build the encryption key following algorithms described in the PDF specification
11737 void PDFWriterImpl::initEncryption()
11739 m_aOwnerPassword = m_aContext.OwnerPassword;
11740 m_aUserPassword = m_aContext.UserPassword;
11741 /* password stuff computing, before sending out anything */
11742 DBG_ASSERT( m_aCipher != NULL, "PDFWriterImpl::initEncryption: a cipher (ARCFOUR) object is not available !" );
11743 DBG_ASSERT( m_aDigest != NULL, "PDFWriterImpl::initEncryption: a digest (MD5) object is not available !" );
11745 if( m_aCipher && m_aDigest )
11747 //if there is no owner password, force it to the user password
11748 if( m_aOwnerPassword.getLength() == 0 )
11749 m_aOwnerPassword = m_aUserPassword;
11751 initPadString();
11753 1) pad passwords
11755 padPassword( m_aOwnerPassword, m_nPaddedOwnerPassword );
11756 padPassword( m_aUserPassword, m_nPaddedUserPassword );
11758 2) compute the access permissions, in numerical form
11760 the default value depends on the revision 2 (40 bit) or 3 (128 bit security):
11761 - for 40 bit security the unused bit must be set to 1, since they are not used
11762 - for 128 bit security the same bit must be preset to 0 and set later if needed
11763 according to the table 3.15, pdf v 1.4 */
11764 m_nAccessPermissions = ( m_aContext.Security128bit ) ? 0xfffff0c0 : 0xffffffc0 ;
11766 /* check permissions for 40 bit security case */
11767 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanPrintTheDocument ) ? 1 << 2 : 0;
11768 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanModifyTheContent ) ? 1 << 3 : 0;
11769 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanCopyOrExtract ) ? 1 << 4 : 0;
11770 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanAddOrModify ) ? 1 << 5 : 0;
11771 m_nKeyLength = SECUR_40BIT_KEY;
11772 m_nRC4KeyLength = SECUR_40BIT_KEY+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5
11774 if( m_aContext.Security128bit )
11776 m_nKeyLength = SECUR_128BIT_KEY;
11777 m_nRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum
11778 // permitted value is 16
11779 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanFillInteractive ) ? 1 << 8 : 0;
11780 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanExtractForAccessibility ) ? 1 << 9 : 0;
11781 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanAssemble ) ? 1 << 10 : 0;
11782 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanPrintFull ) ? 1 << 11 : 0;
11784 computeODictionaryValue();
11785 computeUDictionaryValue();
11787 //clear out exceding key values, prepares for generation number default to 0 as well
11788 // see checkAndEnableStreamEncryption in pdfwriter_impl.hxx
11789 sal_Int32 i, y;
11790 for( i = m_nKeyLength, y = 0; y < 5 ; y++ )
11791 m_nEncryptionKey[i++] = 0;
11793 else //either no cipher or no digest or both, something is wrong with memory or something else
11794 m_aContext.Encrypt = false; //then turn the encryption off
11796 /* end i12626 methods */