update dev300-m58
[ooovba.git] / vcl / source / gdi / pdfwriter_impl.cxx
blobd2f01b947eec60b97b2e4c7d873ee53433dee96e
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 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
31 #define _USE_MATH_DEFINES
32 #include <math.h>
33 #include <algorithm>
35 #include <pdfwriter_impl.hxx>
36 #include <basegfx/polygon/b2dpolygon.hxx>
37 #include <basegfx/polygon/b2dpolypolygon.hxx>
38 #include <basegfx/polygon/b2dpolygontools.hxx>
39 #include <basegfx/polygon/b2dpolypolygontools.hxx>
40 #include <rtl/ustrbuf.hxx>
41 #include <tools/debug.hxx>
42 #include <tools/zcodec.hxx>
43 #include <tools/stream.hxx>
44 #include <tools/urlobj.hxx> //for relative url
45 #include <i18npool/mslangid.hxx>
46 #include <vcl/virdev.hxx>
47 #include <vcl/bmpacc.hxx>
48 #include <vcl/bitmapex.hxx>
49 #include <vcl/image.hxx>
50 #include <vcl/outdev.h>
51 #include <vcl/sallayout.hxx>
52 #include <vcl/metric.hxx>
53 #include <vcl/fontsubset.hxx>
54 #include <svsys.h>
55 #include <vcl/salgdi.hxx>
56 #include <vcl/svapp.hxx>
57 #include <osl/thread.h>
58 #include <osl/file.h>
59 #include <rtl/crc.h>
60 #include <rtl/digest.h>
61 #include <comphelper/processfactory.hxx>
62 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
63 #include <com/sun/star/util/URL.hpp>
65 #include "implncvt.hxx"
67 #include "cppuhelper/implbase1.hxx"
68 #include <icc/sRGB-IEC61966-2.1.hxx>
70 using namespace vcl;
71 using namespace rtl;
73 #if (OSL_DEBUG_LEVEL < 2)
74 #define COMPRESS_PAGES
75 #else
76 #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams
77 #endif
79 #ifdef DO_TEST_PDF
80 class PDFTestOutputStream : public PDFOutputStream
82 public:
83 virtual ~PDFTestOutputStream();
84 virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream );
87 PDFTestOutputStream::~PDFTestOutputStream()
91 void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream )
93 OString aStr( "lalala\ntest\ntest\ntest" );
94 com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() );
95 rtl_copyMemory( aData.getArray(), aStr.getStr(), aStr.getLength() );
96 xStream->writeBytes( aData );
99 // this test code cannot be used to test PDF/A-1 because it forces
100 // control item (widgets) to bypass the structure controlling
101 // the embedding of such elements in actual run
102 void doTestCode()
104 static const char* pHome = getenv( "HOME" );
105 rtl::OUString aTestFile( RTL_CONSTASCII_USTRINGPARAM( "file://" ) );
106 aTestFile += rtl::OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 );
107 aTestFile += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/pdf_export_test.pdf" ) );
109 PDFWriter::PDFWriterContext aContext;
110 aContext.URL = aTestFile;
111 aContext.Version = PDFWriter::PDF_1_4;
112 aContext.Tagged = true;
113 aContext.InitialPage = 2;
115 PDFWriter aWriter( aContext );
116 PDFDocInfo aDocInfo;
117 aDocInfo.Title = OUString( RTL_CONSTASCII_USTRINGPARAM( "PDF export test document" ) );
118 aDocInfo.Producer = OUString( RTL_CONSTASCII_USTRINGPARAM( "VCL" ) );
119 aWriter.SetDocInfo( aDocInfo );
120 aWriter.NewPage();
121 aWriter.BeginStructureElement( PDFWriter::Document );
122 // set duration of 3 sec for first page
123 aWriter.SetAutoAdvanceTime( 3 );
124 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
126 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
127 aWriter.SetLineColor( Color( COL_LIGHTGREEN ) );
128 aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
130 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
131 aWriter.SetTextColor( Color( COL_BLACK ) );
132 aWriter.SetLineColor( Color( COL_BLACK ) );
133 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
135 Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
136 aWriter.DrawRect( aRect );
137 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 1" ) ) );
138 sal_Int32 nFirstLink = aWriter.CreateLink( aRect );
139 PDFNote aNote;
140 aNote.Title = String( RTL_CONSTASCII_USTRINGPARAM( "A small test note" ) );
141 aNote.Contents = String( RTL_CONSTASCII_USTRINGPARAM( "There is no business like show business like no business i know. Everything about it is appealing." ) );
142 aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote );
144 Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
145 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
146 aWriter.DrawRect( aTargetRect );
147 aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest second link" ) ) );
148 sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect );
150 aWriter.BeginStructureElement( PDFWriter::Section );
151 aWriter.BeginStructureElement( PDFWriter::Heading );
152 aWriter.DrawText( Point(4500, 9000), String( RTL_CONSTASCII_USTRINGPARAM( "A small structure test" ) ) );
153 aWriter.EndStructureElement();
154 aWriter.BeginStructureElement( PDFWriter::Paragraph );
155 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
156 aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline );
157 aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
158 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." ) ),
159 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
161 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." ) ) );
162 aWriter.SetAlternateText( String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." ) ) );
163 aWriter.EndStructureElement();
164 sal_Int32 nLongPara = aWriter.BeginStructureElement( PDFWriter::Paragraph );
165 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
166 aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
167 String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph is nothing special either but ends on the next page structurewise" ) ),
168 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
171 aWriter.NewPage();
172 // test AddStream interface
173 aWriter.AddStream( String( RTL_CONSTASCII_USTRINGPARAM( "text/plain" ) ), new PDFTestOutputStream(), true );
174 // set transitional mode
175 aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 );
176 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
177 aWriter.SetTextColor( Color( COL_BLACK ) );
178 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
179 aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
180 String( RTL_CONSTASCII_USTRINGPARAM( "Here's where all things come to an end ... well at least the paragaph from the last page." ) ),
181 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
183 aWriter.EndStructureElement();
185 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
186 // disable structure
187 aWriter.BeginStructureElement( PDFWriter::NonStructElement );
188 aWriter.DrawRect( aRect );
189 aWriter.BeginStructureElement( PDFWriter::Paragraph );
190 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 2" ) ) );
191 sal_Int32 nSecondLink = aWriter.CreateLink( aRect );
193 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
194 aWriter.BeginStructureElement( PDFWriter::ListItem );
195 aWriter.DrawRect( aTargetRect );
196 aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest first link" ) ) );
197 sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect );
198 // enable structure
199 aWriter.EndStructureElement();
200 // add something to the long paragraph as an afterthought
201 sal_Int32 nSaveStruct = aWriter.GetCurrentStructureElement();
202 aWriter.SetCurrentStructureElement( nLongPara );
203 aWriter.DrawText( Rectangle( Point( 4500,4500 ), Size( 12000, 1000 ) ),
204 String( RTL_CONSTASCII_USTRINGPARAM( "Add something to the longish paragraph above." ) ),
205 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
206 aWriter.SetCurrentStructureElement( nSaveStruct );
207 aWriter.EndStructureElement();
208 aWriter.EndStructureElement();
209 aWriter.BeginStructureElement( PDFWriter::Figure );
210 aWriter.BeginStructureElement( PDFWriter::Caption );
211 aWriter.DrawText( Point( 4500, 9000 ), String( RTL_CONSTASCII_USTRINGPARAM( "Some drawing stuff inside the structure" ) ) );
212 aWriter.EndStructureElement();
213 aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
214 // test transparency
215 // draw background
216 Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
217 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
218 aWriter.DrawRect( aTranspRect );
219 aWriter.BeginTransparencyGroup();
221 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
222 aWriter.DrawEllipse( aTranspRect );
223 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
224 aWriter.DrawText( aTranspRect,
225 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
226 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
228 aWriter.EndTransparencyGroup( aTranspRect, 50 );
230 // prepare an alpha mask
231 Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
232 BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess();
233 for( int nX = 0; nX < 256; nX++ )
234 for( int nY = 0; nY < 256; nY++ )
235 pAcc->SetPixel( nX, nY, BitmapColor( (BYTE)((nX+nY)/2) ) );
236 aTransMask.ReleaseAccess( pAcc );
237 aTransMask.SetPrefMapMode( MAP_MM );
238 aTransMask.SetPrefSize( Size( 10, 10 ) );
240 aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask );
242 aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
243 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
244 aWriter.DrawRect( aTranspRect );
245 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
246 aWriter.DrawEllipse( aTranspRect );
247 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
248 aWriter.DrawText( aTranspRect,
249 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
250 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
251 aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
252 aWriter.SetFillColor( Color( COL_LIGHTRED ) );
253 aWriter.DrawRect( aTranspRect );
254 aWriter.BeginTransparencyGroup();
255 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
256 aWriter.DrawEllipse( aTranspRect );
257 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
258 aWriter.DrawText( aTranspRect,
259 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
260 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
261 aWriter.EndTransparencyGroup( aTranspRect, aTransMask );
263 Bitmap aImageBmp( Size( 256, 256 ), 24 );
264 pAcc = aImageBmp.AcquireWriteAccess();
265 pAcc->SetFillColor( Color( 0xff, 0, 0xff ) );
266 pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
267 aImageBmp.ReleaseAccess( pAcc );
268 BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) );
269 aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx );
272 aWriter.EndStructureElement();
273 aWriter.EndStructureElement();
275 LineInfo aLI( LINE_DASH, 3 );
276 aLI.SetDashCount( 2 );
277 aLI.SetDashLen( 50 );
278 aLI.SetDotCount( 2 );
279 aLI.SetDotLen( 25 );
280 aLI.SetDistance( 15 );
281 Point aLIPoints[] = { Point( 4000, 10000 ),
282 Point( 8000, 12000 ),
283 Point( 3000, 19000 ) };
284 Polygon aLIPoly( 3, aLIPoints );
285 aWriter.SetLineColor( Color( COL_BLUE ) );
286 aWriter.SetFillColor();
287 aWriter.DrawPolyLine( aLIPoly, aLI );
289 aLI.SetDashCount( 4 );
290 aLIPoly.Move( 1000, 1000 );
291 aWriter.DrawPolyLine( aLIPoly, aLI );
293 aWriter.NewPage();
294 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
295 Wallpaper aWall( aTransMask );
296 aWall.SetStyle( WALLPAPER_TILE );
297 aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall );
299 aWriter.Push( PUSH_ALL );
300 aWriter.BeginPattern(Rectangle(Point(0,0),Size(2000,1000)));
301 aWriter.SetFillColor( Color( COL_RED ) );
302 aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
303 Point aFillPoints[] = { Point( 1000, 0 ),
304 Point( 0, 1000 ),
305 Point( 2000, 1000 ) };
306 aWriter.DrawPolygon( Polygon( 3, aFillPoints ) );
307 aWriter.DrawBitmap( Point( 200, 200 ), Size( 1600, 600 ), aTransMask );
308 aWriter.DrawText( Rectangle( Point( 200, 200 ), Size( 1600, 600 ) ), String( RTL_CONSTASCII_USTRINGPARAM( "Pattern" ) ) );
309 sal_Int32 nPattern = aWriter.EndPattern( SvtGraphicFill::Transform() );
310 aWriter.Pop();
311 Rectangle aPolyRect( Point( 3800, 11200 ), Size( 10200, 6300 ) );
312 aWriter.DrawPolyPolygon( PolyPolygon( Polygon( aPolyRect ) ), nPattern, true );
313 aWriter.SetFillColor();
314 aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
315 aWriter.DrawRect( aPolyRect );
317 aWriter.NewPage();
318 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
319 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
320 aWriter.SetTextColor( Color( COL_BLACK ) );
321 aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
322 aWriter.DrawRect( aRect );
323 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "www.heise.de" ) ) );
324 sal_Int32 nURILink = aWriter.CreateLink( aRect );
325 aWriter.SetLinkURL( nURILink, OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) ) );
327 aWriter.SetLinkDest( nFirstLink, nFirstDest );
328 aWriter.SetLinkDest( nSecondLink, nSecondDest );
330 // include a button
331 PDFWriter::PushButtonWidget aBtn;
332 aBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testButton" ) );
333 aBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test button" ) );
334 aBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "hit me" ) );
335 aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
336 aBtn.Border = aBtn.Background = true;
337 aWriter.CreateControl( aBtn );
339 // include a uri button
340 PDFWriter::PushButtonWidget aUriBtn;
341 aUriBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "wwwButton" ) );
342 aUriBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A URI button" ) );
343 aUriBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to www" ) );
344 aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
345 aUriBtn.Border = aUriBtn.Background = true;
346 aUriBtn.URL = OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) );
347 aWriter.CreateControl( aUriBtn );
349 // include a dest button
350 PDFWriter::PushButtonWidget aDstBtn;
351 aDstBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "destButton" ) );
352 aDstBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A Dest button" ) );
353 aDstBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to paragraph" ) );
354 aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
355 aDstBtn.Border = aDstBtn.Background = true;
356 aDstBtn.Dest = nFirstDest;
357 aWriter.CreateControl( aDstBtn );
359 PDFWriter::CheckBoxWidget aCBox;
360 aCBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox" ) );
361 aCBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test check box" ) );
362 aCBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me" ) );
363 aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
364 aCBox.Checked = true;
365 aCBox.Border = aCBox.Background = false;
366 aWriter.CreateControl( aCBox );
368 PDFWriter::CheckBoxWidget aCBox2;
369 aCBox2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox2" ) );
370 aCBox2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "Another test check box" ) );
371 aCBox2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me right" ) );
372 aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
373 aCBox2.Checked = true;
374 aCBox2.Border = aCBox2.Background = false;
375 aCBox2.ButtonIsLeft = false;
376 aWriter.CreateControl( aCBox2 );
378 PDFWriter::RadioButtonWidget aRB1;
379 aRB1.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_1" ) );
380 aRB1.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 1" ) );
381 aRB1.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Despair" ) );
382 aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
383 aRB1.Selected = true;
384 aRB1.RadioGroup = 1;
385 aRB1.Border = aRB1.Background = true;
386 aRB1.ButtonIsLeft = false;
387 aRB1.BorderColor = Color( COL_LIGHTGREEN );
388 aRB1.BackgroundColor = Color( COL_LIGHTBLUE );
389 aRB1.TextColor = Color( COL_LIGHTRED );
390 aRB1.TextFont = Font( String( RTL_CONSTASCII_USTRINGPARAM( "Courier" ) ), Size( 0, 800 ) );
391 aWriter.CreateControl( aRB1 );
393 PDFWriter::RadioButtonWidget aRB2;
394 aRB2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb2_1" ) );
395 aRB2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 2 button 1" ) );
396 aRB2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Joy" ) );
397 aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
398 aRB2.Selected = true;
399 aRB2.RadioGroup = 2;
400 aWriter.CreateControl( aRB2 );
402 PDFWriter::RadioButtonWidget aRB3;
403 aRB3.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_2" ) );
404 aRB3.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 2" ) );
405 aRB3.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Desperation" ) );
406 aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
407 aRB3.Selected = true;
408 aRB3.RadioGroup = 1;
409 aWriter.CreateControl( aRB3 );
411 PDFWriter::EditWidget aEditBox;
412 aEditBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testEdit" ) );
413 aEditBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test edit field" ) );
414 aEditBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "A little test text" ) );
415 aEditBox.TextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
416 aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
417 aEditBox.MaxLen = 100;
418 aEditBox.Border = aEditBox.Background = true;
419 aEditBox.BorderColor = Color( COL_BLACK );
420 aWriter.CreateControl( aEditBox );
422 // normal list box
423 PDFWriter::ListBoxWidget aLstBox;
424 aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testListBox" ) );
425 aLstBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) );
426 aLstBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "select me" ) );
427 aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
428 aLstBox.Sort = true;
429 aLstBox.MultiSelect = true;
430 aLstBox.Border = aLstBox.Background = true;
431 aLstBox.BorderColor = Color( COL_BLACK );
432 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) ) );
433 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Two" ) ) );
434 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Three" ) ) );
435 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Four" ) ) );
436 aLstBox.SelectedEntries.push_back( 1 );
437 aLstBox.SelectedEntries.push_back( 2 );
438 aWriter.CreateControl( aLstBox );
440 // dropdown list box
441 aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testDropDownListBox" ) );
442 aLstBox.DropDown = true;
443 aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
444 aWriter.CreateControl( aLstBox );
446 // combo box
447 PDFWriter::ComboBoxWidget aComboBox;
448 aComboBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testComboBox" ) );
449 aComboBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "test a combobox" ) );
450 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Larry" ) ) );
451 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Curly" ) ) );
452 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Moe" ) ) );
453 aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
454 aWriter.CreateControl( aComboBox );
456 // test outlines
457 sal_Int32 nPage1OL = aWriter.CreateOutlineItem();
458 aWriter.SetOutlineItemText( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 1" ) ) );
459 aWriter.SetOutlineItemDest( nPage1OL, nSecondDest );
460 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2" ) ), nSecondDest );
461 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 revisited" ) ), nSecondDest );
462 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 again" ) ), nSecondDest );
463 sal_Int32 nPage2OL = aWriter.CreateOutlineItem();
464 aWriter.SetOutlineItemText( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 2" ) ) );
465 aWriter.CreateOutlineItem( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 1" ) ), nFirstDest );
467 aWriter.EndStructureElement(); // close document
468 aWriter.Emit();
470 #endif
472 static const sal_Int32 nLog10Divisor = 1;
473 static const double fDivisor = 10.0;
475 static inline double pixelToPoint( sal_Int32 px ) { return double(px)/fDivisor; }
476 static inline double pixelToPoint( double px ) { return px/fDivisor; }
477 static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); }
479 static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
481 static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
482 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
483 rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
484 rBuffer.append( pHexDigits[ nInt & 15 ] );
487 static void appendName( const OUString& rStr, OStringBuffer& rBuffer )
489 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
490 // I guess than when reading the #xx sequence it will count for a single character.
491 OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) );
492 const sal_Char* pStr = aStr.getStr();
493 int nLen = aStr.getLength();
494 for( int i = 0; i < nLen; i++ )
496 /* #i16920# PDF recommendation: output UTF8, any byte
497 * outside the interval [33(=ASCII'!');126(=ASCII'~')]
498 * should be escaped hexadecimal
499 * for the sake of ghostscript which also reads PDF
500 * but has a narrower acceptance rate we only pass
501 * alphanumerics and '-' literally.
503 if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) ||
504 (pStr[i] >= 'a' && pStr[i] <= 'z' ) ||
505 (pStr[i] >= '0' && pStr[i] <= '9' ) ||
506 pStr[i] == '-' )
508 rBuffer.append( pStr[i] );
510 else
512 rBuffer.append( '#' );
513 appendHex( (sal_Int8)pStr[i], rBuffer );
518 static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer )
520 //FIXME i59651 see above
521 while( pStr && *pStr )
523 if( (*pStr >= 'A' && *pStr <= 'Z' ) ||
524 (*pStr >= 'a' && *pStr <= 'z' ) ||
525 (*pStr >= '0' && *pStr <= '9' ) ||
526 *pStr == '-' )
528 rBuffer.append( *pStr );
530 else
532 rBuffer.append( '#' );
533 appendHex( (sal_Int8)*pStr, rBuffer );
535 pStr++;
539 //used only to emit encoded passwords
540 static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer )
542 while( nLength )
544 switch( *pStr )
546 case '\n' :
547 rBuffer.append( "\\n" );
548 break;
549 case '\r' :
550 rBuffer.append( "\\r" );
551 break;
552 case '\t' :
553 rBuffer.append( "\\t" );
554 break;
555 case '\b' :
556 rBuffer.append( "\\b" );
557 break;
558 case '\f' :
559 rBuffer.append( "\\f" );
560 break;
561 case '(' :
562 case ')' :
563 case '\\' :
564 rBuffer.append( "\\" );
565 rBuffer.append( (sal_Char) *pStr );
566 break;
567 default:
568 rBuffer.append( (sal_Char) *pStr );
569 break;
571 pStr++;
572 nLength--;
576 /**--->i56629
577 * Convert a string before using it.
579 * This string conversion function is needed because the destination name
580 * in a PDF file seen through an Internet browser should be
581 * specially crafted, in order to be used directly by the browser.
582 * In this way the fragment part of a hyperlink to a PDF file (e.g. something
583 * as 'test1/test2/a-file.pdf#thefragment) will be (hopefully) interpreted by the
584 * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
585 * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
586 * and go to named destination thefragment using default zoom'.
587 * The conversion is needed because in case of a fragment in the form: Slide%201
588 * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
589 * using this conversion, in both the generated named destinations, fragment and GoToR
590 * destination.
592 * The names for destinations are name objects and so they don't need to be encrypted
593 * even though they expose the content of PDF file (e.g. guessing the PDF content from the
594 * destination name).
596 * Fhurter limitation: it is advisable to use standard ASCII characters for
597 * OOo bookmarks.
599 static void appendDestinationName( const rtl::OUString& rString, OStringBuffer& rBuffer )
601 const sal_Unicode* pStr = rString.getStr();
602 sal_Int32 nLen = rString.getLength();
603 for( int i = 0; i < nLen; i++ )
605 sal_Unicode aChar = pStr[i];
606 if( (aChar >= '0' && aChar <= '9' ) ||
607 (aChar >= 'a' && aChar <= 'z' ) ||
608 (aChar >= 'A' && aChar <= 'Z' ) ||
609 aChar == '-' )
611 rBuffer.append((sal_Char)aChar);
613 else
615 sal_Int8 aValueHigh = sal_Int8(aChar >> 8);
616 if(aValueHigh > 0)
617 appendHex( aValueHigh, rBuffer );
618 appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
622 //<--- i56629
624 static void appendUnicodeTextString( const rtl::OUString& rString, OStringBuffer& rBuffer )
626 rBuffer.append( "FEFF" );
627 const sal_Unicode* pStr = rString.getStr();
628 sal_Int32 nLen = rString.getLength();
629 for( int i = 0; i < nLen; i++ )
631 sal_Unicode aChar = pStr[i];
632 appendHex( (sal_Int8)(aChar >> 8), rBuffer );
633 appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
637 OString PDFWriterImpl::convertWidgetFieldName( const rtl::OUString& rString )
639 OStringBuffer aBuffer( rString.getLength()+64 );
641 /* #i80258# previously we use appendName here
642 however we need a slightly different coding scheme than the normal
643 name encoding for field names
645 also replace all '.' by '_' as '.' indicates a hierarchy level which
646 we do not have here
649 OString aStr( OUStringToOString( rString, RTL_TEXTENCODING_UTF8 ) );
650 const sal_Char* pStr = aStr.getStr();
651 int nLen = aStr.getLength();
652 for( int i = 0; i < nLen; i++ )
654 /* #i16920# PDF recommendation: output UTF8, any byte
655 * outside the interval [33(=ASCII'!');126(=ASCII'~')]
656 * should be escaped hexadecimal
658 if( pStr[i] == '.' )
659 aBuffer.append( '_' );
660 else if( (pStr[i] >= 33 && pStr[i] <= 126 ) )
661 aBuffer.append( pStr[i] );
662 else
664 aBuffer.append( '#' );
665 appendHex( (sal_Int8)pStr[i], aBuffer );
669 OString aRet = aBuffer.makeStringAndClear();
670 std::hash_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aRet );
672 if( it != m_aFieldNameMap.end() ) // not unique
674 std::hash_map< OString, sal_Int32, OStringHash >::const_iterator check_it;
675 OString aTry;
678 OStringBuffer aUnique( aRet.getLength() + 16 );
679 aUnique.append( aRet );
680 aUnique.append( '_' );
681 aUnique.append( it->second );
682 it->second++;
683 aTry = aUnique.makeStringAndClear();
684 check_it = m_aFieldNameMap.find( aTry );
685 } while( check_it != m_aFieldNameMap.end() );
686 aRet = aTry;
688 else
689 m_aFieldNameMap[ aRet ] = 2;
690 return aRet;
693 static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor )
695 if( nValue < 0 )
697 rBuffer.append( '-' );
698 nValue = -nValue;
700 sal_Int32 nFactor = 1, nDiv = nPrecision;
701 while( nDiv-- )
702 nFactor *= 10;
704 sal_Int32 nInt = nValue / nFactor;
705 rBuffer.append( nInt );
706 if( nFactor > 1 )
708 sal_Int32 nDecimal = nValue % nFactor;
709 if( nDecimal )
711 rBuffer.append( '.' );
712 // omit trailing zeros
713 while( (nDecimal % 10) == 0 )
714 nDecimal /= 10;
715 rBuffer.append( nDecimal );
721 // appends a double. PDF does not accept exponential format, only fixed point
722 static void appendDouble( double fValue, OStringBuffer& rBuffer, int nPrecision = 5 )
724 bool bNeg = false;
725 if( fValue < 0.0 )
727 bNeg = true;
728 fValue=-fValue;
731 sal_Int64 nInt = (sal_Int64)fValue;
732 fValue -= (double)nInt;
733 // optimizing hardware may lead to a value of 1.0 after the subtraction
734 if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision )
736 nInt++;
737 fValue = 0.0;
739 sal_Int64 nFrac = 0;
740 if( fValue )
742 fValue *= pow( 10.0, (double)nPrecision );
743 nFrac = (sal_Int64)fValue;
745 if( bNeg && ( nInt || nFrac ) )
746 rBuffer.append( '-' );
747 rBuffer.append( nInt );
748 if( nFrac )
750 int i;
751 rBuffer.append( '.' );
752 sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5);
753 for ( i = 0; ( i < nPrecision ) && nFrac; i++ )
755 sal_Int64 nNumb = nFrac / nBound;
756 nFrac -= nNumb * nBound;
757 rBuffer.append( nNumb );
758 nBound /= 10;
764 static void appendColor( const Color& rColor, OStringBuffer& rBuffer )
767 if( rColor != Color( COL_TRANSPARENT ) )
769 appendDouble( (double)rColor.GetRed() / 255.0, rBuffer );
770 rBuffer.append( ' ' );
771 appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer );
772 rBuffer.append( ' ' );
773 appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer );
777 static void appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
779 if( rColor != Color( COL_TRANSPARENT ) )
781 appendColor( rColor, rBuffer );
782 rBuffer.append( " RG" );
786 static void appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
788 if( rColor != Color( COL_TRANSPARENT ) )
790 appendColor( rColor, rBuffer );
791 rBuffer.append( " rg" );
795 // matrix helper class
796 // TODO: use basegfx matrix class instead or derive from it
797 namespace vcl // TODO: use anonymous namespace to keep this class local
799 /* for sparse matrices of the form (2D linear transformations)
800 * f[0] f[1] 0
801 * f[2] f[3] 0
802 * f[4] f[5] 1
804 class Matrix3
806 double f[6];
808 void set( double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; }
809 public:
810 Matrix3();
811 ~Matrix3() {}
813 void skew( double alpha, double beta );
814 void scale( double sx, double sy );
815 void rotate( double angle );
816 void translate( double tx, double ty );
817 bool invert();
819 void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL );
821 Point transform( const Point& rPoint ) const;
825 Matrix3::Matrix3()
827 // initialize to unity
828 f[0] = 1.0;
829 f[1] = 0.0;
830 f[2] = 0.0;
831 f[3] = 1.0;
832 f[4] = 0.0;
833 f[5] = 0.0;
836 Point Matrix3::transform( const Point& rOrig ) const
838 double x = (double)rOrig.X(), y = (double)rOrig.Y();
839 return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) );
842 void Matrix3::skew( double alpha, double beta )
844 double fn[6];
845 double tb = tan( beta );
846 fn[0] = f[0] + f[2]*tb;
847 fn[1] = f[1];
848 fn[2] = f[2] + f[3]*tb;
849 fn[3] = f[3];
850 fn[4] = f[4] + f[5]*tb;
851 fn[5] = f[5];
852 if( alpha != 0.0 )
854 double ta = tan( alpha );
855 fn[1] += f[0]*ta;
856 fn[3] += f[2]*ta;
857 fn[5] += f[4]*ta;
859 set( fn );
862 void Matrix3::scale( double sx, double sy )
864 double fn[6];
865 fn[0] = sx*f[0];
866 fn[1] = sy*f[1];
867 fn[2] = sx*f[2];
868 fn[3] = sy*f[3];
869 fn[4] = sx*f[4];
870 fn[5] = sy*f[5];
871 set( fn );
874 void Matrix3::rotate( double angle )
876 double fn[6];
877 double fSin = sin(angle);
878 double fCos = cos(angle);
879 fn[0] = f[0]*fCos - f[1]*fSin;
880 fn[1] = f[0]*fSin + f[1]*fCos;
881 fn[2] = f[2]*fCos - f[3]*fSin;
882 fn[3] = f[2]*fSin + f[3]*fCos;
883 fn[4] = f[4]*fCos - f[5]*fSin;
884 fn[5] = f[4]*fSin + f[5]*fCos;
885 set( fn );
888 void Matrix3::translate( double tx, double ty )
890 f[4] += tx;
891 f[5] += ty;
894 bool Matrix3::invert()
896 // short circuit trivial cases
897 if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 )
899 f[4] = -f[4];
900 f[5] = -f[5];
901 return true;
904 // check determinant
905 const double fDet = f[0]*f[3]-f[1]*f[2];
906 if( fDet == 0.0 )
907 return false;
909 // invert the matrix
910 double fn[6];
911 fn[0] = +f[3] / fDet;
912 fn[1] = -f[1] / fDet;
913 fn[2] = -f[2] / fDet;
914 fn[3] = +f[0] / fDet;
916 // apply inversion to translation
917 fn[4] = -(f[4]*fn[0] + f[5]*fn[2]);
918 fn[5] = -(f[4]*fn[1] + f[5]*fn[3]);
920 set( fn );
921 return true;
924 void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack )
926 appendDouble( f[0], rBuffer );
927 rBuffer.append( ' ' );
928 appendDouble( f[1], rBuffer );
929 rBuffer.append( ' ' );
930 appendDouble( f[2], rBuffer );
931 rBuffer.append( ' ' );
932 appendDouble( f[3], rBuffer );
933 rBuffer.append( ' ' );
934 rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack );
937 static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList )
939 if( rList.empty() )
940 return;
941 rBuf.append( '/' );
942 rBuf.append( pPrefix );
943 rBuf.append( "<<" );
944 int ni = 0;
945 for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it )
947 if( it->first.getLength() && it->second > 0 )
949 rBuf.append( '/' );
950 rBuf.append( it->first );
951 rBuf.append( ' ' );
952 rBuf.append( it->second );
953 rBuf.append( " 0 R" );
954 if( ((++ni) & 7) == 0 )
955 rBuf.append( '\n' );
958 rBuf.append( ">>\n" );
961 void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject )
963 rBuf.append( "<</Font " );
964 rBuf.append( nFontDictObject );
965 rBuf.append( " 0 R\n" );
966 appendResourceMap( rBuf, "XObject", m_aXObjects );
967 appendResourceMap( rBuf, "ExtGState", m_aExtGStates );
968 appendResourceMap( rBuf, "Shading", m_aShadings );
969 appendResourceMap( rBuf, "Pattern", m_aPatterns );
970 rBuf.append( "/ProcSet[/PDF/Text" );
971 if( !m_aXObjects.empty() )
972 rBuf.append( "/ImageC/ImageI/ImageB" );
973 rBuf.append( "]\n>>\n" );
976 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
978 m_pWriter( pWriter ),
979 m_nPageWidth( nPageWidth ),
980 m_nPageHeight( nPageHeight ),
981 m_eOrientation( eOrientation ),
982 m_nPageObject( 0 ), // invalid object number
983 m_nPageIndex( -1 ), // invalid index
984 m_nStreamLengthObject( 0 ),
985 m_nBeginStreamPos( 0 ),
986 m_eTransition( PDFWriter::Regular ),
987 m_nTransTime( 0 ),
988 m_nDuration( 0 ),
989 m_bHasWidgets( false )
991 // object ref must be only ever updated in emit()
992 m_nPageObject = m_pWriter->createObject();
995 PDFWriterImpl::PDFPage::~PDFPage()
999 void PDFWriterImpl::PDFPage::beginStream()
1001 #if OSL_DEBUG_LEVEL > 1
1003 OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" );
1004 m_pWriter->emitComment( aLine.getStr() );
1006 #endif
1007 m_aStreamObjects.push_back(m_pWriter->createObject());
1008 if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) )
1009 return;
1011 m_nStreamLengthObject = m_pWriter->createObject();
1012 // write content stream header
1013 OStringBuffer aLine;
1014 aLine.append( m_aStreamObjects.back() );
1015 aLine.append( " 0 obj\n<</Length " );
1016 aLine.append( m_nStreamLengthObject );
1017 aLine.append( " 0 R" );
1018 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1019 aLine.append( "/Filter/FlateDecode" );
1020 #endif
1021 aLine.append( ">>\nstream\n" );
1022 if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) )
1023 return;
1024 if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &m_nBeginStreamPos ) )
1026 osl_closeFile( m_pWriter->m_aFile );
1027 m_pWriter->m_bOpen = false;
1029 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1030 m_pWriter->beginCompression();
1031 #endif
1032 m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() );
1035 void PDFWriterImpl::PDFPage::endStream()
1037 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1038 m_pWriter->endCompression();
1039 #endif
1040 sal_uInt64 nEndStreamPos;
1041 if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &nEndStreamPos ) )
1043 osl_closeFile( m_pWriter->m_aFile );
1044 m_pWriter->m_bOpen = false;
1045 return;
1047 m_pWriter->disableStreamEncryption();
1048 if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1049 return;
1050 // emit stream length object
1051 if( ! m_pWriter->updateObject( m_nStreamLengthObject ) )
1052 return;
1053 OStringBuffer aLine;
1054 aLine.append( m_nStreamLengthObject );
1055 aLine.append( " 0 obj\n" );
1056 aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) );
1057 aLine.append( "\nendobj\n\n" );
1058 m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1061 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject )
1063 // emit page object
1064 if( ! m_pWriter->updateObject( m_nPageObject ) )
1065 return false;
1066 OStringBuffer aLine;
1068 aLine.append( m_nPageObject );
1069 aLine.append( " 0 obj\n"
1070 "<</Type/Page/Parent " );
1071 aLine.append( nParentObject );
1072 aLine.append( " 0 R" );
1073 aLine.append( "/Resources " );
1074 aLine.append( m_pWriter->getResourceDictObj() );
1075 aLine.append( " 0 R" );
1076 if( m_nPageWidth && m_nPageHeight )
1078 aLine.append( "/MediaBox[0 0 " );
1079 aLine.append( m_nPageWidth );
1080 aLine.append( ' ' );
1081 aLine.append( m_nPageHeight );
1082 aLine.append( "]" );
1084 switch( m_eOrientation )
1086 case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
1087 case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
1088 case PDFWriter::Portrait: aLine.append( "/Rotate 0\n" );break;
1090 case PDFWriter::Inherit:
1091 default:
1092 break;
1094 int nAnnots = m_aAnnotations.size();
1095 if( nAnnots > 0 )
1097 aLine.append( "/Annots[\n" );
1098 for( int i = 0; i < nAnnots; i++ )
1100 aLine.append( m_aAnnotations[i] );
1101 aLine.append( " 0 R" );
1102 aLine.append( ((i+1)%15) ? " " : "\n" );
1104 aLine.append( "]\n" );
1106 #if 0
1107 // FIXME: implement tab order as Structure Tree
1108 if( m_bHasWidgets && m_pWriter->getVersion() >= PDFWriter::PDF_1_5 )
1109 aLine.append( " /Tabs /S\n" );
1110 #endif
1111 if( m_aMCIDParents.size() > 0 )
1113 OStringBuffer aStructParents( 1024 );
1114 aStructParents.append( "[ " );
1115 int nParents = m_aMCIDParents.size();
1116 for( int i = 0; i < nParents; i++ )
1118 aStructParents.append( m_aMCIDParents[i] );
1119 aStructParents.append( " 0 R" );
1120 aStructParents.append( ((i%10) == 9) ? "\n" : " " );
1122 aStructParents.append( "]" );
1123 m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() );
1125 aLine.append( "/StructParents " );
1126 aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) );
1127 aLine.append( "\n" );
1129 if( m_nDuration > 0 )
1131 aLine.append( "/Dur " );
1132 aLine.append( (sal_Int32)m_nDuration );
1133 aLine.append( "\n" );
1135 if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 )
1137 // transition duration
1138 aLine.append( "/Trans<</D " );
1139 appendDouble( (double)m_nTransTime/1000.0, aLine, 3 );
1140 aLine.append( "\n" );
1141 const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL;
1142 switch( m_eTransition )
1144 case PDFWriter::SplitHorizontalInward:
1145 pStyle = "Split"; pDm = "H"; pM = "I"; break;
1146 case PDFWriter::SplitHorizontalOutward:
1147 pStyle = "Split"; pDm = "H"; pM = "O"; break;
1148 case PDFWriter::SplitVerticalInward:
1149 pStyle = "Split"; pDm = "V"; pM = "I"; break;
1150 case PDFWriter::SplitVerticalOutward:
1151 pStyle = "Split"; pDm = "V"; pM = "O"; break;
1152 case PDFWriter::BlindsHorizontal:
1153 pStyle = "Blinds"; pDm = "H"; break;
1154 case PDFWriter::BlindsVertical:
1155 pStyle = "Blinds"; pDm = "V"; break;
1156 case PDFWriter::BoxInward:
1157 pStyle = "Box"; pM = "I"; break;
1158 case PDFWriter::BoxOutward:
1159 pStyle = "Box"; pM = "O"; break;
1160 case PDFWriter::WipeLeftToRight:
1161 pStyle = "Wipe"; pDi = "0"; break;
1162 case PDFWriter::WipeBottomToTop:
1163 pStyle = "Wipe"; pDi = "90"; break;
1164 case PDFWriter::WipeRightToLeft:
1165 pStyle = "Wipe"; pDi = "180"; break;
1166 case PDFWriter::WipeTopToBottom:
1167 pStyle = "Wipe"; pDi = "270"; break;
1168 case PDFWriter::Dissolve:
1169 pStyle = "Dissolve"; break;
1170 case PDFWriter::GlitterLeftToRight:
1171 pStyle = "Glitter"; pDi = "0"; break;
1172 case PDFWriter::GlitterTopToBottom:
1173 pStyle = "Glitter"; pDi = "270"; break;
1174 case PDFWriter::GlitterTopLeftToBottomRight:
1175 pStyle = "Glitter"; pDi = "315"; break;
1176 case PDFWriter::Regular:
1177 break;
1179 // transition style
1180 if( pStyle )
1182 aLine.append( "/S/" );
1183 aLine.append( pStyle );
1184 aLine.append( "\n" );
1186 if( pDm )
1188 aLine.append( "/Dm/" );
1189 aLine.append( pDm );
1190 aLine.append( "\n" );
1192 if( pM )
1194 aLine.append( "/M/" );
1195 aLine.append( pM );
1196 aLine.append( "\n" );
1198 if( pDi )
1200 aLine.append( "/Di " );
1201 aLine.append( pDi );
1202 aLine.append( "\n" );
1204 aLine.append( ">>\n" );
1206 if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 )
1208 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1210 aLine.append( "/Contents" );
1211 unsigned int nStreamObjects = m_aStreamObjects.size();
1212 if( nStreamObjects > 1 )
1213 aLine.append( '[' );
1214 for( unsigned int i = 0; i < m_aStreamObjects.size(); i++ )
1216 aLine.append( ' ' );
1217 aLine.append( m_aStreamObjects[i] );
1218 aLine.append( " 0 R" );
1220 if( nStreamObjects > 1 )
1221 aLine.append( ']' );
1222 aLine.append( ">>\nendobj\n\n" );
1223 return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1226 namespace vcl
1228 template < class GEOMETRY >
1229 GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
1231 GEOMETRY aPoint;
1232 if ( MAP_PIXEL == _rSource.GetMapUnit() )
1234 aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
1236 else
1238 aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
1240 return aPoint;
1244 void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const
1246 if( pOutPoint )
1248 Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1249 m_pWriter->m_aMapMode,
1250 m_pWriter->getReferenceDevice(),
1251 rPoint ) );
1252 *pOutPoint = aPoint;
1255 Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1256 m_pWriter->m_aMapMode,
1257 m_pWriter->getReferenceDevice(),
1258 rPoint ) );
1260 sal_Int32 nValue = aPoint.X();
1261 if( bNeg )
1262 nValue = -nValue;
1264 appendFixedInt( nValue, rBuffer );
1266 rBuffer.append( ' ' );
1268 nValue = pointToPixel(getHeight()) - aPoint.Y();
1269 if( bNeg )
1270 nValue = -nValue;
1272 appendFixedInt( nValue, rBuffer );
1275 void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const
1277 appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer );
1278 rBuffer.append( ' ' );
1279 appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false );
1280 rBuffer.append( ' ' );
1281 appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true );
1282 rBuffer.append( " re" );
1285 void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const
1287 Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1288 m_pWriter->m_aMapMode,
1289 m_pWriter->getReferenceDevice(),
1290 rRect.BottomLeft() + Point( 0, 1 )
1292 Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1293 m_pWriter->m_aMapMode,
1294 m_pWriter->getReferenceDevice(),
1295 rRect.GetSize() );
1296 rRect.Left() = aLL.X();
1297 rRect.Right() = aLL.X() + aSize.Width();
1298 rRect.Top() = pointToPixel(getHeight()) - aLL.Y();
1299 rRect.Bottom() = rRect.Top() + aSize.Height();
1302 void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1304 USHORT nPoints = rPoly.GetSize();
1306 * #108582# applications do weird things
1308 sal_uInt32 nBufLen = rBuffer.getLength();
1309 if( nPoints > 0 )
1311 const BYTE* pFlagArray = rPoly.GetConstFlagAry();
1312 appendPoint( rPoly[0], rBuffer );
1313 rBuffer.append( " m\n" );
1314 for( USHORT i = 1; i < nPoints; i++ )
1316 if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 )
1318 // bezier
1319 DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" );
1320 appendPoint( rPoly[i], rBuffer );
1321 rBuffer.append( " " );
1322 appendPoint( rPoly[i+1], rBuffer );
1323 rBuffer.append( " " );
1324 appendPoint( rPoly[i+2], rBuffer );
1325 rBuffer.append( " c" );
1326 i += 2; // add additionally consumed points
1328 else
1330 // line
1331 appendPoint( rPoly[i], rBuffer );
1332 rBuffer.append( " l" );
1334 if( (rBuffer.getLength() - nBufLen) > 65 )
1336 rBuffer.append( "\n" );
1337 nBufLen = rBuffer.getLength();
1339 else
1340 rBuffer.append( " " );
1342 if( bClose )
1343 rBuffer.append( "h\n" );
1347 void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1349 USHORT nPolygons = rPolyPoly.Count();
1350 for( USHORT n = 0; n < nPolygons; n++ )
1351 appendPolygon( rPolyPoly[n], rBuffer, bClose );
1354 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1356 sal_Int32 nValue = nLength;
1357 if ( nLength < 0 )
1359 rBuffer.append( '-' );
1360 nValue = -nLength;
1362 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1363 m_pWriter->m_aMapMode,
1364 m_pWriter->getReferenceDevice(),
1365 Size( nValue, nValue ) ) );
1366 nValue = bVertical ? aSize.Height() : aSize.Width();
1367 if( pOutLength )
1368 *pOutLength = ((nLength < 0 ) ? -nValue : nValue);
1370 appendFixedInt( nValue, rBuffer, 1 );
1373 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1375 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1376 m_pWriter->m_aMapMode,
1377 m_pWriter->getReferenceDevice(),
1378 Size( 1000, 1000 ) ) );
1379 if( pOutLength )
1380 *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0);
1381 fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0);
1382 appendDouble( fLength, rBuffer );
1385 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const
1387 bool bRet = true;
1388 if( rInfo.GetStyle() == LINE_DASH )
1390 rBuffer.append( "[ " );
1391 if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case
1393 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1394 rBuffer.append( ' ' );
1395 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1396 rBuffer.append( ' ' );
1398 else
1400 // check for implementation limits of dash array
1401 // in PDF reader apps (e.g. acroread)
1402 if( 2*(rInfo.GetDashCount() + rInfo.GetDotCount()) > 10 )
1403 bRet = false;
1404 for( int n = 0; n < rInfo.GetDashCount(); n++ )
1406 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1407 rBuffer.append( ' ' );
1408 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1409 rBuffer.append( ' ' );
1411 for( int m = 0; m < rInfo.GetDotCount(); m++ )
1413 appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer );
1414 rBuffer.append( ' ' );
1415 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1416 rBuffer.append( ' ' );
1419 rBuffer.append( "] 0 d\n" );
1421 if( rInfo.GetWidth() > 1 )
1423 appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer );
1424 rBuffer.append( " w\n" );
1426 else if( rInfo.GetWidth() == 0 )
1428 // "pixel" line
1429 appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->ImplGetDPIX()), rBuffer );
1430 rBuffer.append( " w\n" );
1432 return bRet;
1435 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const
1437 if( nWidth <= 0 )
1438 return;
1439 if( nDelta < 1 )
1440 nDelta = 1;
1442 rBuffer.append( "0 " );
1443 appendMappedLength( nY, rBuffer, true );
1444 rBuffer.append( " m\n" );
1445 for( sal_Int32 n = 0; n < nWidth; )
1447 n += nDelta;
1448 appendMappedLength( n, rBuffer, false );
1449 rBuffer.append( ' ' );
1450 appendMappedLength( nDelta+nY, rBuffer, true );
1451 rBuffer.append( ' ' );
1452 n += nDelta;
1453 appendMappedLength( n, rBuffer, false );
1454 rBuffer.append( ' ' );
1455 appendMappedLength( nY, rBuffer, true );
1456 rBuffer.append( " v " );
1457 if( n < nWidth )
1459 n += nDelta;
1460 appendMappedLength( n, rBuffer, false );
1461 rBuffer.append( ' ' );
1462 appendMappedLength( nY-nDelta, rBuffer, true );
1463 rBuffer.append( ' ' );
1464 n += nDelta;
1465 appendMappedLength( n, rBuffer, false );
1466 rBuffer.append( ' ' );
1467 appendMappedLength( nY, rBuffer, true );
1468 rBuffer.append( " v\n" );
1471 rBuffer.append( "S\n" );
1475 * class PDFWriterImpl
1478 PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext )
1480 m_pReferenceDevice( NULL ),
1481 m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ),
1482 m_nCurrentStructElement( 0 ),
1483 m_bEmitStructure( true ),
1484 m_bNewMCID( false ),
1485 m_nCurrentControl( -1 ),
1486 m_bEmbedStandardFonts( false ),
1487 m_nNextFID( 1 ),
1488 m_nInheritedPageWidth( 595 ), // default A4
1489 m_nInheritedPageHeight( 842 ), // default A4
1490 m_eInheritedOrientation( PDFWriter::Portrait ),
1491 m_nCurrentPage( -1 ),
1492 m_nResourceDict( -1 ),
1493 m_nFontDictObject( -1 ),
1494 m_pCodec( NULL ),
1495 m_aDocDigest( rtl_digest_createMD5() ),
1496 m_aCipher( (rtlCipher)NULL ),
1497 m_aDigest( NULL ),
1498 m_bEncryptThisStream( false ),
1499 m_aDocID( 32 ),
1500 m_aCreationDateString( 64 ),
1501 m_aCreationMetaDateString( 64 ),
1502 m_pEncryptionBuffer( NULL ),
1503 m_nEncryptionBufferSize( 0 ),
1504 m_bIsPDF_A1( false )
1506 #ifdef DO_TEST_PDF
1507 static bool bOnce = true;
1508 if( bOnce )
1510 bOnce = false;
1511 doTestCode();
1513 #endif
1514 m_aContext = rContext;
1515 m_aStructure.push_back( PDFStructureElement() );
1516 m_aStructure[0].m_nOwnElement = 0;
1517 m_aStructure[0].m_nParentElement = 0;
1519 Font aFont;
1520 aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
1521 aFont.SetSize( Size( 0, 12 ) );
1523 GraphicsState aState;
1524 aState.m_aMapMode = m_aMapMode;
1525 aState.m_aFont = aFont;
1526 m_aGraphicsStack.push_front( aState );
1528 oslFileError aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
1529 if( aError != osl_File_E_None )
1531 if( aError == osl_File_E_EXIST )
1533 aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write );
1534 if( aError == osl_File_E_None )
1535 aError = osl_setFileSize( m_aFile, 0 );
1538 if( aError != osl_File_E_None )
1539 return;
1541 m_bOpen = true;
1543 /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1545 m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1546 m_aDigest = rtl_digest_createMD5();
1548 /* the size of the Codec default maximum */
1549 checkEncryptionBufferSize( 0x4000 );
1551 // write header
1552 OStringBuffer aBuffer( 20 );
1553 aBuffer.append( "%PDF-" );
1554 switch( m_aContext.Version )
1556 case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break;
1557 case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break;
1558 case PDFWriter::PDF_A_1:
1559 default:
1560 case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break;
1561 case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break;
1563 // append something binary as comment (suggested in PDF Reference)
1564 aBuffer.append( "\n%äüöß\n" );
1565 if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) )
1567 osl_closeFile( m_aFile );
1568 m_bOpen = false;
1569 return;
1572 // insert outline root
1573 m_aOutline.push_back( PDFOutlineEntry() );
1575 m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1);
1576 if( m_bIsPDF_A1 )
1577 m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour
1579 m_bEmbedStandardFonts = m_aContext.EmbedStandardFonts;
1582 PDFWriterImpl::~PDFWriterImpl()
1584 if( m_aDocDigest )
1585 rtl_digest_destroyMD5( m_aDocDigest );
1586 delete static_cast<VirtualDevice*>(m_pReferenceDevice);
1588 if( m_aCipher )
1589 rtl_cipher_destroyARCFOUR( m_aCipher );
1590 if( m_aDigest )
1591 rtl_digest_destroyMD5( m_aDigest );
1593 rtl_freeMemory( m_pEncryptionBuffer );
1596 void PDFWriterImpl::setDocInfo( const PDFDocInfo& rInfo )
1598 m_aDocInfo.Title = rInfo.Title;
1599 m_aDocInfo.Author = rInfo.Author;
1600 m_aDocInfo.Subject = rInfo.Subject;
1601 m_aDocInfo.Keywords = rInfo.Keywords;
1602 m_aDocInfo.Creator = rInfo.Creator;
1603 m_aDocInfo.Producer = rInfo.Producer;
1605 //build the document id
1606 rtl::OString aInfoValuesOut;
1607 OStringBuffer aID( 1024 );
1608 if( m_aDocInfo.Title.Len() )
1609 appendUnicodeTextString( m_aDocInfo.Title, aID );
1610 if( m_aDocInfo.Author.Len() )
1611 appendUnicodeTextString( m_aDocInfo.Author, aID );
1612 if( m_aDocInfo.Subject.Len() )
1613 appendUnicodeTextString( m_aDocInfo.Subject, aID );
1614 if( m_aDocInfo.Keywords.Len() )
1615 appendUnicodeTextString( m_aDocInfo.Keywords, aID );
1616 if( m_aDocInfo.Creator.Len() )
1617 appendUnicodeTextString( m_aDocInfo.Creator, aID );
1618 if( m_aDocInfo.Producer.Len() )
1619 appendUnicodeTextString( m_aDocInfo.Producer, aID );
1621 TimeValue aTVal, aGMT;
1622 oslDateTime aDT;
1623 osl_getSystemTime( &aGMT );
1624 osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
1625 osl_getDateTimeFromTimeValue( &aTVal, &aDT );
1626 m_aCreationDateString.append( "D:" );
1627 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1628 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1629 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1630 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1631 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1632 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1633 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1634 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1635 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1636 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1637 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1638 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1639 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1640 m_aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1641 //--> i59651, we fill the Metadata date string as well, if PDF/A is requested
1642 if( m_bIsPDF_A1 )
1644 // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1645 // local time zone offset UTC only, whereas Acrobat 8 seems
1646 // to use the localtime notation only
1647 // according to a raccomandation in XMP Specification (Jan 2004, page 75)
1648 // the Acrobat way seems the right approach
1649 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1650 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1651 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1652 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1653 m_aCreationMetaDateString.append( "-" );
1654 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1655 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1656 m_aCreationMetaDateString.append( "-" );
1657 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1658 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1659 m_aCreationMetaDateString.append( "T" );
1660 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1661 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1662 m_aCreationMetaDateString.append( ":" );
1663 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1664 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1665 m_aCreationMetaDateString.append( ":" );
1666 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1667 m_aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1669 sal_uInt32 nDelta = 0;
1670 if( aGMT.Seconds > aTVal.Seconds )
1672 m_aCreationDateString.append( "-" );
1673 nDelta = aGMT.Seconds-aTVal.Seconds;
1674 if( m_bIsPDF_A1 )
1675 m_aCreationMetaDateString.append( "-" );
1677 else if( aGMT.Seconds < aTVal.Seconds )
1679 m_aCreationDateString.append( "+" );
1680 nDelta = aTVal.Seconds-aGMT.Seconds;
1681 if( m_bIsPDF_A1 )
1682 m_aCreationMetaDateString.append( "+" );
1684 else
1686 m_aCreationDateString.append( "Z" );
1687 if( m_bIsPDF_A1 )
1688 m_aCreationMetaDateString.append( "Z" );
1691 if( nDelta )
1693 m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1694 m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1695 m_aCreationDateString.append( "'" );
1696 m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1697 m_aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1698 if( m_bIsPDF_A1 )
1700 m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1701 m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1702 m_aCreationMetaDateString.append( ":" );
1703 m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1704 m_aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1707 m_aCreationDateString.append( "'" );
1708 aID.append( m_aCreationDateString.getStr(), m_aCreationDateString.getLength() );
1710 aInfoValuesOut = aID.makeStringAndClear();
1712 DBG_ASSERT( m_aDigest != NULL, "PDFWrite_Impl::setDocInfo: cannot obtain a digest object !" );
1714 m_aDocID.setLength( 0 );
1715 if( m_aDigest )
1717 osl_getSystemTime( &aGMT );
1718 rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, &aGMT, sizeof( aGMT ) );
1719 if( nError == rtl_Digest_E_None )
1720 nError = rtl_digest_updateMD5( m_aDigest, m_aContext.URL.getStr(), m_aContext.URL.getLength()*sizeof(sal_Unicode) );
1721 if( nError == rtl_Digest_E_None )
1722 nError = rtl_digest_updateMD5( m_aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() );
1723 if( nError == rtl_Digest_E_None )
1725 //the binary form of the doc id is needed for encryption stuff
1726 rtl_digest_getMD5( m_aDigest, m_nDocID, 16 );
1727 for( unsigned int i = 0; i < 16; i++ )
1728 appendHex( m_nDocID[i], m_aDocID );
1733 /* i12626 methods */
1735 check if the Unicode string must be encrypted or not, perform the requested task,
1736 append the string as unicode hex, encrypted if needed
1738 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
1740 rOutBuffer.append( "<" );
1741 if( m_aContext.Encrypt )
1743 const sal_Unicode* pStr = rInString.getStr();
1744 sal_Int32 nLen = rInString.getLength();
1745 //prepare a unicode string, encrypt it
1746 if( checkEncryptionBufferSize( nLen*2 ) )
1748 enableStringEncryption( nInObjectNumber );
1749 register sal_uInt8 *pCopy = m_pEncryptionBuffer;
1750 sal_Int32 nChars = 2;
1751 *pCopy++ = 0xFE;
1752 *pCopy++ = 0xFF;
1753 // we need to prepare a byte stream from the unicode string buffer
1754 for( register int i = 0; i < nLen; i++ )
1756 register sal_Unicode aUnChar = pStr[i];
1757 *pCopy++ = (sal_uInt8)( aUnChar >> 8 );
1758 *pCopy++ = (sal_uInt8)( aUnChar & 255 );
1759 nChars += 2;
1761 //encrypt in place
1762 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars );
1763 //now append, hexadecimal (appendHex), the encrypted result
1764 for(register int i = 0; i < nChars; i++)
1765 appendHex( m_pEncryptionBuffer[i], rOutBuffer );
1768 else
1769 appendUnicodeTextString( rInString, rOutBuffer );
1770 rOutBuffer.append( ">" );
1773 inline void PDFWriterImpl::appendLiteralStringEncrypt( rtl::OStringBuffer& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
1775 rOutBuffer.append( "(" );
1776 sal_Int32 nChars = rInString.getLength();
1777 //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
1778 if( m_aContext.Encrypt && checkEncryptionBufferSize( nChars ) )
1780 //encrypt the string in a buffer, then append it
1781 enableStringEncryption( nInObjectNumber );
1782 rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars );
1783 appendLiteralString( (const sal_Char*)m_pEncryptionBuffer, nChars, rOutBuffer );
1785 else
1786 appendLiteralString( rInString.getStr(), nChars , rOutBuffer );
1787 rOutBuffer.append( ")" );
1790 inline void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
1792 rtl::OStringBuffer aBufferString( rInString );
1793 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
1796 inline void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
1798 rtl::OString aBufferString( rtl::OUStringToOString( rInString, RTL_TEXTENCODING_ASCII_US ) );
1799 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
1802 /* end i12626 methods */
1804 void PDFWriterImpl::emitComment( const char* pComment )
1806 OStringBuffer aLine( 64 );
1807 aLine.append( "% " );
1808 aLine.append( (const sal_Char*)pComment );
1809 aLine.append( "\n" );
1810 writeBuffer( aLine.getStr(), aLine.getLength() );
1813 bool PDFWriterImpl::compressStream( SvMemoryStream* pStream )
1815 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
1816 pStream->Seek( STREAM_SEEK_TO_END );
1817 ULONG nEndPos = pStream->Tell();
1818 pStream->Seek( STREAM_SEEK_TO_BEGIN );
1819 ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
1820 SvMemoryStream aStream;
1821 pCodec->BeginCompression();
1822 pCodec->Write( aStream, (const BYTE*)pStream->GetData(), nEndPos );
1823 pCodec->EndCompression();
1824 delete pCodec;
1825 nEndPos = aStream.Tell();
1826 pStream->Seek( STREAM_SEEK_TO_BEGIN );
1827 aStream.Seek( STREAM_SEEK_TO_BEGIN );
1828 pStream->SetStreamSize( nEndPos );
1829 pStream->Write( aStream.GetData(), nEndPos );
1830 return true;
1831 #else
1832 (void)pStream;
1833 return false;
1834 #endif
1837 void PDFWriterImpl::beginCompression()
1839 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
1840 m_pCodec = new ZCodec( 0x4000, 0x4000 );
1841 m_pMemStream = new SvMemoryStream();
1842 m_pCodec->BeginCompression();
1843 #endif
1846 void PDFWriterImpl::endCompression()
1848 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
1849 if( m_pCodec )
1851 m_pCodec->EndCompression();
1852 delete m_pCodec;
1853 m_pCodec = NULL;
1854 sal_uInt64 nLen = m_pMemStream->Tell();
1855 m_pMemStream->Seek( 0 );
1856 writeBuffer( m_pMemStream->GetData(), nLen );
1857 delete m_pMemStream;
1858 m_pMemStream = NULL;
1860 #endif
1863 bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes )
1865 if( ! m_bOpen ) // we are already down the drain
1866 return false;
1868 if( ! nBytes ) // huh ?
1869 return true;
1871 if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
1873 m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END );
1874 m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) );
1875 return true;
1878 sal_uInt64 nWritten;
1879 if( m_pCodec )
1881 m_pCodec->Write( *m_pMemStream, static_cast<const BYTE*>(pBuffer), (ULONG)nBytes );
1882 nWritten = nBytes;
1884 else
1886 sal_Bool buffOK = sal_True;
1887 if( m_bEncryptThisStream )
1889 /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
1890 if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) != sal_False )
1891 rtl_cipher_encodeARCFOUR( m_aCipher,
1892 (sal_uInt8*)pBuffer, static_cast<sal_Size>(nBytes),
1893 m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) );
1896 const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer : pBuffer;
1897 if( m_aDocDigest )
1898 rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) );
1900 if( osl_writeFile( m_aFile,
1901 pWriteBuffer,
1902 nBytes, &nWritten ) != osl_File_E_None )
1903 nWritten = 0;
1905 if( nWritten != nBytes )
1907 osl_closeFile( m_aFile );
1908 m_bOpen = false;
1912 return nWritten == nBytes;
1915 OutputDevice* PDFWriterImpl::getReferenceDevice()
1917 if( ! m_pReferenceDevice )
1919 VirtualDevice* pVDev = new VirtualDevice( 0 );
1921 m_pReferenceDevice = pVDev;
1923 pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 );
1925 pVDev->SetOutputSizePixel( Size( 640, 480 ) );
1926 pVDev->SetMapMode( MAP_MM );
1928 m_pReferenceDevice->mpPDFWriter = this;
1929 m_pReferenceDevice->ImplUpdateFontData( TRUE );
1931 return m_pReferenceDevice;
1934 class ImplPdfBuiltinFontData : public ImplFontData
1936 private:
1937 const PDFWriterImpl::BuiltinFont& mrBuiltin;
1939 public:
1940 enum {PDF_FONT_MAGIC = 0xBDFF0A1C };
1941 ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& );
1942 const PDFWriterImpl::BuiltinFont* GetBuiltinFont() const { return &mrBuiltin; }
1944 virtual ImplFontData* Clone() const { return new ImplPdfBuiltinFontData(*this); }
1945 virtual ImplFontEntry* CreateFontInstance( ImplFontSelectData& ) const;
1946 virtual sal_IntPtr GetFontId() const { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); }
1949 inline const ImplPdfBuiltinFontData* GetPdfFontData( const ImplFontData* pFontData )
1951 const ImplPdfBuiltinFontData* pFD = NULL;
1952 if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) )
1953 pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData );
1954 return pFD;
1957 static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin )
1959 ImplDevFontAttributes aDFA;
1960 aDFA.maName = String::CreateFromAscii( rBuiltin.m_pName );
1961 aDFA.maStyleName = String::CreateFromAscii( rBuiltin.m_pStyleName );
1962 aDFA.meFamily = rBuiltin.m_eFamily;
1963 aDFA.mbSymbolFlag = (rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 );
1964 aDFA.mePitch = rBuiltin.m_ePitch;
1965 aDFA.meWeight = rBuiltin.m_eWeight;
1966 aDFA.meItalic = rBuiltin.m_eItalic;
1967 aDFA.meWidthType = rBuiltin.m_eWidthType;
1969 aDFA.mbOrientation = true;
1970 aDFA.mbDevice = true;
1971 aDFA.mnQuality = 50000;
1972 aDFA.mbSubsettable = false;
1973 aDFA.mbEmbeddable = false;
1974 return aDFA;
1977 ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin )
1978 : ImplFontData( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ),
1979 mrBuiltin( rBuiltin )
1982 ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
1984 ImplFontEntry* pEntry = new ImplFontEntry( rFSD );
1985 return pEntry;
1988 ImplDevFontList* PDFWriterImpl::filterDevFontList( ImplDevFontList* pFontList )
1990 DBG_ASSERT( m_aSubsets.size() == 0, "Fonts changing during PDF generation, document will be invalid" );
1991 ImplDevFontList* pFiltered = pFontList->Clone( true, true );
1993 // append the PDF builtin fonts
1994 if( !m_bIsPDF_A1 && !m_bEmbedStandardFonts)
1995 for( unsigned int i = 0; i < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); i++ )
1997 ImplFontData* pNewData = new ImplPdfBuiltinFontData( m_aBuiltinFonts[i] );
1998 pFiltered->Add( pNewData );
2000 return pFiltered;
2003 bool PDFWriterImpl::isBuiltinFont( const ImplFontData* pFont ) const
2005 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
2006 return (pFD != NULL);
2009 void PDFWriterImpl::getFontMetric( ImplFontSelectData* pSelect, ImplFontMetricData* pMetric ) const
2011 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2012 if( !pFD )
2013 return;
2014 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2016 pMetric->mnOrientation = sal::static_int_cast<short>(pSelect->mnOrientation);
2017 pMetric->meFamily = pBuiltinFont->m_eFamily;
2018 pMetric->mePitch = pBuiltinFont->m_ePitch;
2019 pMetric->meWeight = pBuiltinFont->m_eWeight;
2020 pMetric->meItalic = pBuiltinFont->m_eItalic;
2021 pMetric->mbSymbolFlag = pFD->IsSymbolFont();
2022 pMetric->mnWidth = pSelect->mnHeight;
2023 pMetric->mnAscent = ( pSelect->mnHeight * +pBuiltinFont->m_nAscent + 500 ) / 1000;
2024 pMetric->mnDescent = ( pSelect->mnHeight * -pBuiltinFont->m_nDescent + 500 ) / 1000;
2025 pMetric->mnIntLeading = 0;
2026 pMetric->mnExtLeading = 0;
2027 pMetric->mnSlant = 0;
2028 pMetric->mbScalableFont = true;
2029 pMetric->mbDevice = true;
2032 // -----------------------------------------------------------------------
2034 namespace vcl {
2036 class PDFSalLayout : public GenericSalLayout
2038 PDFWriterImpl& mrPDFWriterImpl;
2039 const PDFWriterImpl::BuiltinFont& mrBuiltinFont;
2040 bool mbIsSymbolFont;
2041 long mnPixelPerEM;
2042 String maOrigText;
2044 public:
2045 PDFSalLayout( PDFWriterImpl&,
2046 const PDFWriterImpl::BuiltinFont&,
2047 long nPixelPerEM, int nOrientation );
2049 void SetText( const String& rText ) { maOrigText = rText; }
2050 virtual bool LayoutText( ImplLayoutArgs& );
2051 virtual void InitFont() const;
2052 virtual void DrawText( SalGraphics& ) const;
2057 // -----------------------------------------------------------------------
2059 PDFSalLayout::PDFSalLayout( PDFWriterImpl& rPDFWriterImpl,
2060 const PDFWriterImpl::BuiltinFont& rBuiltinFont,
2061 long nPixelPerEM, int nOrientation )
2062 : mrPDFWriterImpl( rPDFWriterImpl ),
2063 mrBuiltinFont( rBuiltinFont ),
2064 mnPixelPerEM( nPixelPerEM )
2066 mbIsSymbolFont = (rBuiltinFont.m_eCharSet != RTL_TEXTENCODING_MS_1252);
2067 SetOrientation( nOrientation );
2070 // -----------------------------------------------------------------------
2072 bool PDFSalLayout::LayoutText( ImplLayoutArgs& rArgs )
2074 const String aText( rArgs.mpStr+rArgs.mnMinCharPos, sal::static_int_cast<xub_StrLen>(rArgs.mnEndCharPos-rArgs.mnMinCharPos) );
2075 SetText( aText );
2076 SetUnitsPerPixel( 1000 );
2078 rtl_UnicodeToTextConverter aConv = rtl_createTextToUnicodeConverter( mrBuiltinFont.m_eCharSet );
2080 Point aNewPos( 0, 0 );
2081 bool bRightToLeft;
2082 for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); )
2084 // TODO: handle unicode surrogates
2085 // on the other hand the PDF builtin fonts don't support them anyway
2086 sal_Unicode cChar = rArgs.mpStr[ nCharPos ];
2087 if( bRightToLeft )
2088 cChar = static_cast<sal_Unicode>(GetMirroredChar( cChar ));
2090 if( 1 ) // TODO: shortcut for ASCII?
2092 sal_Char aBuf[4];
2093 sal_uInt32 nInfo;
2094 sal_Size nSrcCvtChars;
2096 sal_Size nConv = rtl_convertUnicodeToText( aConv,
2097 NULL,
2098 &cChar, 1,
2099 aBuf, sizeof(aBuf)/sizeof(*aBuf),
2100 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR,
2101 &nInfo, &nSrcCvtChars );
2102 // check whether conversion was possible
2103 // else fallback font is needed as the standard fonts
2104 // are handled via WinAnsi encoding
2105 if( nConv > 0 )
2106 cChar = ((sal_Unicode)aBuf[0]) & 0x00ff;
2108 if( cChar & 0xff00 )
2110 cChar = 0; // NotDef glyph
2111 rArgs.NeedFallback( nCharPos, bRightToLeft );
2114 long nGlyphWidth = (long)mrBuiltinFont.m_aWidths[cChar] * mnPixelPerEM;
2115 long nGlyphFlags = 0; // builtin fonts don't have diacritic glyphs
2116 if( bRightToLeft )
2117 nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
2118 // TODO: get kerning from builtin fonts
2119 GlyphItem aGI( nCharPos, cChar, aNewPos, nGlyphFlags, nGlyphWidth );
2120 AppendGlyph( aGI );
2122 aNewPos.X() += nGlyphWidth;
2125 rtl_destroyUnicodeToTextConverter( aConv );
2127 return true;
2130 // -----------------------------------------------------------------------
2132 void PDFSalLayout::InitFont() const
2134 // TODO: recreate font with all its attributes
2137 // -----------------------------------------------------------------------
2139 void PDFSalLayout::DrawText( SalGraphics& ) const
2141 mrPDFWriterImpl.drawLayout( *const_cast<PDFSalLayout*>(this), maOrigText, true );
2144 // -----------------------------------------------------------------------
2146 SalLayout* PDFWriterImpl::GetTextLayout( ImplLayoutArgs& rArgs, ImplFontSelectData* pSelect )
2148 DBG_ASSERT( (pSelect->mpFontData != NULL),
2149 "PDFWriterImpl::GetTextLayout mpFontData is NULL" );
2151 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2152 if( !pFD )
2153 return NULL;
2154 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2156 long nPixelPerEM = pSelect->mnWidth ? pSelect->mnWidth : pSelect->mnHeight;
2157 int nOrientation = pSelect->mnOrientation;
2158 PDFSalLayout* pLayout = new PDFSalLayout( *this, *pBuiltinFont, nPixelPerEM, nOrientation );
2159 pLayout->SetText( rArgs.mpStr );
2160 return pLayout;
2163 sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
2165 if( m_aContext.Encrypt && m_aPages.empty() )
2166 initEncryption();
2168 endPage();
2169 m_nCurrentPage = m_aPages.size();
2170 m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) );
2171 m_aPages.back().m_nPageIndex = m_nCurrentPage;
2172 m_aPages.back().beginStream();
2174 // setup global graphics state
2175 // linewidth is "1 pixel" by default
2176 OStringBuffer aBuf( 16 );
2177 appendDouble( 72.0/double(getReferenceDevice()->ImplGetDPIX()), aBuf );
2178 aBuf.append( " w\n" );
2179 writeBuffer( aBuf.getStr(), aBuf.getLength() );
2181 return m_nCurrentPage;
2184 void PDFWriterImpl::endPage()
2186 if( m_aPages.begin() != m_aPages.end() )
2188 // close eventual MC sequence
2189 endStructureElementMCSeq();
2191 // sanity check
2192 if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2194 DBG_ERROR( "redirection across pages !!!" );
2195 m_aOutputStreams.clear(); // leak !
2196 m_aMapMode.SetOrigin( Point() );
2199 m_aGraphicsStack.clear();
2200 m_aGraphicsStack.push_back( GraphicsState() );
2202 // this should pop the PDF graphics stack if necessary
2203 updateGraphicsState();
2205 m_aPages.back().endStream();
2207 // reset the default font
2208 Font aFont;
2209 aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
2210 aFont.SetSize( Size( 0, 12 ) );
2212 m_aCurrentPDFState = m_aGraphicsStack.front();
2213 m_aGraphicsStack.front().m_aFont = aFont;
2215 for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin();
2216 it != m_aBitmaps.end(); ++it )
2218 if( ! it->m_aBitmap.IsEmpty() )
2220 writeBitmapObject( *it );
2221 it->m_aBitmap = BitmapEx();
2224 for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg )
2226 if( jpeg->m_pStream )
2228 writeJPG( *jpeg );
2229 delete jpeg->m_pStream;
2230 jpeg->m_pStream = NULL;
2231 jpeg->m_aMask = Bitmap();
2234 for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin();
2235 t != m_aTransparentObjects.end(); ++t )
2237 if( t->m_pContentStream )
2239 writeTransparentObject( *t );
2240 delete t->m_pContentStream;
2241 t->m_pContentStream = NULL;
2247 sal_Int32 PDFWriterImpl::createObject()
2249 m_aObjects.push_back( ~0U );
2250 return m_aObjects.size();
2253 bool PDFWriterImpl::updateObject( sal_Int32 n )
2255 if( ! m_bOpen )
2256 return false;
2258 sal_uInt64 nOffset = ~0U;
2259 oslFileError aError = osl_getFilePos( m_aFile, &nOffset );
2260 DBG_ASSERT( aError == osl_File_E_None, "could not register object" );
2261 if( aError != osl_File_E_None )
2263 osl_closeFile( m_aFile );
2264 m_bOpen = false;
2266 m_aObjects[ n-1 ] = nOffset;
2267 return aError == osl_File_E_None;
2270 #define CHECK_RETURN( x ) if( !(x) ) return 0
2272 sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject )
2274 if( nObject > 0 )
2276 OStringBuffer aLine( 1024 );
2278 aLine.append( nObject );
2279 aLine.append( " 0 obj\n"
2280 "<</Nums[\n" );
2281 sal_Int32 nTreeItems = m_aStructParentTree.size();
2282 for( sal_Int32 n = 0; n < nTreeItems; n++ )
2284 aLine.append( n );
2285 aLine.append( ' ' );
2286 aLine.append( m_aStructParentTree[n] );
2287 aLine.append( "\n" );
2289 aLine.append( "]>>\nendobj\n\n" );
2290 CHECK_RETURN( updateObject( nObject ) );
2291 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2293 return nObject;
2296 const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr )
2298 static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings;
2299 // fill maps once
2300 if( aAttributeStrings.empty() )
2302 aAttributeStrings[ PDFWriter::Placement ] = "Placement";
2303 aAttributeStrings[ PDFWriter::WritingMode ] = "WritingMode";
2304 aAttributeStrings[ PDFWriter::SpaceBefore ] = "SpaceBefore";
2305 aAttributeStrings[ PDFWriter::SpaceAfter ] = "SpaceAfter";
2306 aAttributeStrings[ PDFWriter::StartIndent ] = "StartIndent";
2307 aAttributeStrings[ PDFWriter::EndIndent ] = "EndIndent";
2308 aAttributeStrings[ PDFWriter::TextIndent ] = "TextIndent";
2309 aAttributeStrings[ PDFWriter::TextAlign ] = "TextAlign";
2310 aAttributeStrings[ PDFWriter::Width ] = "Width";
2311 aAttributeStrings[ PDFWriter::Height ] = "Height";
2312 aAttributeStrings[ PDFWriter::BlockAlign ] = "BlockAlign";
2313 aAttributeStrings[ PDFWriter::InlineAlign ] = "InlineAlign";
2314 aAttributeStrings[ PDFWriter::LineHeight ] = "LineHeight";
2315 aAttributeStrings[ PDFWriter::BaselineShift ] = "BaselineShift";
2316 aAttributeStrings[ PDFWriter::TextDecorationType ] = "TextDecorationType";
2317 aAttributeStrings[ PDFWriter::ListNumbering ] = "ListNumbering";
2318 aAttributeStrings[ PDFWriter::RowSpan ] = "RowSpan";
2319 aAttributeStrings[ PDFWriter::ColSpan ] = "ColSpan";
2320 aAttributeStrings[ PDFWriter::LinkAnnotation ] = "LinkAnnotation";
2323 std::map< PDFWriter::StructAttribute, const char* >::const_iterator it =
2324 aAttributeStrings.find( eAttr );
2326 #if OSL_DEBUG_LEVEL > 1
2327 if( it == aAttributeStrings.end() )
2328 fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr );
2329 #endif
2331 return it != aAttributeStrings.end() ? it->second : "";
2334 const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal )
2336 static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings;
2338 if( aValueStrings.empty() )
2340 aValueStrings[ PDFWriter::NONE ] = "None";
2341 aValueStrings[ PDFWriter::Block ] = "Block";
2342 aValueStrings[ PDFWriter::Inline ] = "Inline";
2343 aValueStrings[ PDFWriter::Before ] = "Before";
2344 aValueStrings[ PDFWriter::After ] = "After";
2345 aValueStrings[ PDFWriter::Start ] = "Start";
2346 aValueStrings[ PDFWriter::End ] = "End";
2347 aValueStrings[ PDFWriter::LrTb ] = "LrTb";
2348 aValueStrings[ PDFWriter::RlTb ] = "RlTb";
2349 aValueStrings[ PDFWriter::TbRl ] = "TbRl";
2350 aValueStrings[ PDFWriter::Center ] = "Center";
2351 aValueStrings[ PDFWriter::Justify ] = "Justify";
2352 aValueStrings[ PDFWriter::Auto ] = "Auto";
2353 aValueStrings[ PDFWriter::Middle ] = "Middle";
2354 aValueStrings[ PDFWriter::Normal ] = "Normal";
2355 aValueStrings[ PDFWriter::Underline ] = "Underline";
2356 aValueStrings[ PDFWriter::Overline ] = "Overline";
2357 aValueStrings[ PDFWriter::LineThrough ] = "LineThrough";
2358 aValueStrings[ PDFWriter::Disc ] = "Disc";
2359 aValueStrings[ PDFWriter::Circle ] = "Circle";
2360 aValueStrings[ PDFWriter::Square ] = "Square";
2361 aValueStrings[ PDFWriter::Decimal ] = "Decimal";
2362 aValueStrings[ PDFWriter::UpperRoman ] = "UpperRoman";
2363 aValueStrings[ PDFWriter::LowerRoman ] = "LowerRoman";
2364 aValueStrings[ PDFWriter::UpperAlpha ] = "UpperAlpha";
2365 aValueStrings[ PDFWriter::LowerAlpha ] = "LowerAlpha";
2368 std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it =
2369 aValueStrings.find( eVal );
2371 #if OSL_DEBUG_LEVEL > 1
2372 if( it == aValueStrings.end() )
2373 fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal );
2374 #endif
2376 return it != aValueStrings.end() ? it->second : "";
2379 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt )
2381 o_rLine.append( "/" );
2382 o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) );
2384 if( i_rVal.eValue != PDFWriter::Invalid )
2386 o_rLine.append( "/" );
2387 o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) );
2389 else
2391 // numerical value
2392 o_rLine.append( " " );
2393 if( i_bIsFixedInt )
2394 appendFixedInt( i_rVal.nValue, o_rLine );
2395 else
2396 o_rLine.append( i_rVal.nValue );
2398 o_rLine.append( "\n" );
2401 OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
2403 // create layout, list and table attribute sets
2404 OStringBuffer aLayout(256), aList(64), aTable(64);
2405 for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin();
2406 it != i_rEle.m_aAttributes.end(); ++it )
2408 if( it->first == PDFWriter::ListNumbering )
2409 appendStructureAttributeLine( it->first, it->second, aList, true );
2410 else if( it->first == PDFWriter::RowSpan ||
2411 it->first == PDFWriter::ColSpan )
2412 appendStructureAttributeLine( it->first, it->second, aTable, false );
2413 else if( it->first == PDFWriter::LinkAnnotation )
2415 sal_Int32 nLink = it->second.nValue;
2416 std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
2417 m_aLinkPropertyMap.find( nLink );
2418 if( link_it != m_aLinkPropertyMap.end() )
2419 nLink = link_it->second;
2420 if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() )
2422 // update struct parent of link
2423 OStringBuffer aStructParentEntry( 32 );
2424 aStructParentEntry.append( i_rEle.m_nObject );
2425 aStructParentEntry.append( " 0 R" );
2426 m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() );
2427 m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1;
2429 sal_Int32 nRefObject = createObject();
2430 OStringBuffer aRef( 256 );
2431 aRef.append( nRefObject );
2432 aRef.append( " 0 obj\n"
2433 "<</Type/OBJR/Obj " );
2434 aRef.append( m_aLinks[ nLink ].m_nObject );
2435 aRef.append( " 0 R>>\n"
2436 "endobj\n\n"
2438 updateObject( nRefObject );
2439 writeBuffer( aRef.getStr(), aRef.getLength() );
2441 i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) );
2443 else
2445 DBG_ERROR( "unresolved link id for Link structure" );
2446 #if OSL_DEBUG_LEVEL > 1
2447 fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink );
2449 OStringBuffer aLine( "unresolved link id " );
2450 aLine.append( nLink );
2451 aLine.append( " for Link structure" );
2452 emitComment( aLine.getStr() );
2454 #endif
2457 else
2458 appendStructureAttributeLine( it->first, it->second, aLayout, true );
2460 if( ! i_rEle.m_aBBox.IsEmpty() )
2462 aLayout.append( "/BBox[" );
2463 appendFixedInt( i_rEle.m_aBBox.Left(), aLayout );
2464 aLayout.append( " " );
2465 appendFixedInt( i_rEle.m_aBBox.Top(), aLayout );
2466 aLayout.append( " " );
2467 appendFixedInt( i_rEle.m_aBBox.Right(), aLayout );
2468 aLayout.append( " " );
2469 appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout );
2470 aLayout.append( "]\n" );
2473 std::vector< sal_Int32 > aAttribObjects;
2474 if( aLayout.getLength() )
2476 aAttribObjects.push_back( createObject() );
2477 updateObject( aAttribObjects.back() );
2478 OStringBuffer aObj( 64 );
2479 aObj.append( aAttribObjects.back() );
2480 aObj.append( " 0 obj\n"
2481 "<</O/Layout\n" );
2482 aLayout.append( ">>\nendobj\n\n" );
2483 writeBuffer( aObj.getStr(), aObj.getLength() );
2484 writeBuffer( aLayout.getStr(), aLayout.getLength() );
2486 if( aList.getLength() )
2488 aAttribObjects.push_back( createObject() );
2489 updateObject( aAttribObjects.back() );
2490 OStringBuffer aObj( 64 );
2491 aObj.append( aAttribObjects.back() );
2492 aObj.append( " 0 obj\n"
2493 "<</O/List\n" );
2494 aList.append( ">>\nendobj\n\n" );
2495 writeBuffer( aObj.getStr(), aObj.getLength() );
2496 writeBuffer( aList.getStr(), aList.getLength() );
2498 if( aTable.getLength() )
2500 aAttribObjects.push_back( createObject() );
2501 updateObject( aAttribObjects.back() );
2502 OStringBuffer aObj( 64 );
2503 aObj.append( aAttribObjects.back() );
2504 aObj.append( " 0 obj\n"
2505 "<</O/Table\n" );
2506 aTable.append( ">>\nendobj\n\n" );
2507 writeBuffer( aObj.getStr(), aObj.getLength() );
2508 writeBuffer( aTable.getStr(), aTable.getLength() );
2511 OStringBuffer aRet( 64 );
2512 if( aAttribObjects.size() > 1 )
2513 aRet.append( " [" );
2514 for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin();
2515 at_it != aAttribObjects.end(); ++at_it )
2517 aRet.append( " " );
2518 aRet.append( *at_it );
2519 aRet.append( " 0 R" );
2521 if( aAttribObjects.size() > 1 )
2522 aRet.append( " ]" );
2523 return aRet.makeStringAndClear();
2526 sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
2529 // do not emit NonStruct and its children
2530 rEle.m_eType == PDFWriter::NonStructElement &&
2531 rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root
2533 return 0;
2535 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
2537 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
2539 PDFStructureElement& rChild = m_aStructure[ *it ];
2540 if( rChild.m_eType != PDFWriter::NonStructElement )
2542 if( rChild.m_nParentElement == rEle.m_nOwnElement )
2543 emitStructure( rChild );
2544 else
2546 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure element" );
2547 #if OSL_DEBUG_LEVEL > 1
2548 fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
2549 #endif
2553 else
2555 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
2556 #if OSL_DEBUG_LEVEL > 1
2557 fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
2558 #endif
2562 OStringBuffer aLine( 512 );
2563 aLine.append( rEle.m_nObject );
2564 aLine.append( " 0 obj\n"
2565 "<</Type" );
2566 sal_Int32 nParentTree = -1;
2567 if( rEle.m_nOwnElement == rEle.m_nParentElement )
2569 nParentTree = createObject();
2570 CHECK_RETURN( nParentTree );
2571 aLine.append( "/StructTreeRoot\n" );
2572 aLine.append( "/ParentTree " );
2573 aLine.append( nParentTree );
2574 aLine.append( " 0 R\n" );
2575 if( ! m_aRoleMap.empty() )
2577 aLine.append( "/RoleMap<<" );
2578 for( std::hash_map<OString,OString,OStringHash>::const_iterator
2579 it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it )
2581 aLine.append( '/' );
2582 aLine.append(it->first);
2583 aLine.append( '/' );
2584 aLine.append( it->second );
2585 aLine.append( '\n' );
2587 aLine.append( ">>\n" );
2590 else
2592 aLine.append( "/StructElem\n"
2593 "/S/" );
2594 if( rEle.m_aAlias.getLength() > 0 )
2595 aLine.append( rEle.m_aAlias );
2596 else
2597 aLine.append( getStructureTag( rEle.m_eType ) );
2598 aLine.append( "\n"
2599 "/P " );
2600 aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject );
2601 aLine.append( " 0 R\n"
2602 "/Pg " );
2603 aLine.append( rEle.m_nFirstPageObject );
2604 aLine.append( " 0 R\n" );
2605 if( rEle.m_aActualText.getLength() )
2607 aLine.append( "/ActualText" );
2608 appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine );
2609 aLine.append( "\n" );
2611 if( rEle.m_aAltText.getLength() )
2613 aLine.append( "/Alt" );
2614 appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine );
2615 aLine.append( "\n" );
2618 if( ! rEle.m_aBBox.IsEmpty() || rEle.m_aAttributes.size() )
2620 OString aAttribs = emitStructureAttributes( rEle );
2621 if( aAttribs.getLength() )
2623 aLine.append( "/A" );
2624 aLine.append( aAttribs );
2625 aLine.append( "\n" );
2628 if( rEle.m_aLocale.Language.getLength() > 0 )
2630 OUStringBuffer aLocBuf( 16 );
2631 aLocBuf.append( rEle.m_aLocale.Language.toAsciiLowerCase() );
2632 if( rEle.m_aLocale.Country.getLength() > 0 )
2634 aLocBuf.append( sal_Unicode('-') );
2635 aLocBuf.append( rEle.m_aLocale.Country );
2637 aLine.append( "/Lang" );
2638 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine );
2639 aLine.append( "\n" );
2641 if( ! rEle.m_aKids.empty() )
2643 unsigned int i = 0;
2644 aLine.append( "/K[" );
2645 for( std::list< PDFStructureElementKid >::const_iterator it =
2646 rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ )
2648 if( it->nMCID == -1 )
2650 aLine.append( it->nObject );
2651 aLine.append( " 0 R" );
2652 aLine.append( ( (i & 15) == 15 ) ? "\n" : " " );
2654 else
2656 if( it->nObject == rEle.m_nFirstPageObject )
2658 aLine.append( it->nMCID );
2659 aLine.append( " " );
2661 else
2663 aLine.append( "<</Type/MCR/Pg " );
2664 aLine.append( it->nObject );
2665 aLine.append( " 0 R /MCID " );
2666 aLine.append( it->nMCID );
2667 aLine.append( ">>\n" );
2671 aLine.append( "]\n" );
2673 aLine.append( ">>\nendobj\n\n" );
2675 CHECK_RETURN( updateObject( rEle.m_nObject ) );
2676 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2678 CHECK_RETURN( emitStructParentTree( nParentTree ) );
2680 return rEle.m_nObject;
2683 bool PDFWriterImpl::emitGradients()
2685 for( std::list<GradientEmit>::iterator it = m_aGradients.begin();
2686 it != m_aGradients.end(); ++it )
2688 CHECK_RETURN( writeGradientFunction( *it ) );
2690 return true;
2693 bool PDFWriterImpl::emitTilings()
2695 OStringBuffer aTilingObj( 1024 );
2697 for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it )
2699 DBG_ASSERT( it->m_pTilingStream, "tiling without stream" );
2700 if( ! it->m_pTilingStream )
2701 continue;
2703 aTilingObj.setLength( 0 );
2705 #if OSL_DEBUG_LEVEL > 1
2707 OStringBuffer aLine( "PDFWriterImpl::emitTilings" );
2708 emitComment( aLine.getStr() );
2710 #endif
2712 sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left();
2713 sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top();
2714 sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth();
2715 sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight();
2716 if( it->m_aCellSize.Width() == 0 )
2717 it->m_aCellSize.Width() = nW;
2718 if( it->m_aCellSize.Height() == 0 )
2719 it->m_aCellSize.Height() = nH;
2721 bool bDeflate = compressStream( it->m_pTilingStream );
2722 it->m_pTilingStream->Seek( STREAM_SEEK_TO_END );
2723 sal_Size nTilingStreamSize = it->m_pTilingStream->Tell();
2724 it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN );
2726 // write pattern object
2727 aTilingObj.append( it->m_nObject );
2728 aTilingObj.append( " 0 obj\n" );
2729 aTilingObj.append( "<</Type/Pattern/PatternType 1\n"
2730 "/PaintType 1\n"
2731 "/TilingType 2\n"
2732 "/BBox[" );
2733 appendFixedInt( nX, aTilingObj );
2734 aTilingObj.append( ' ' );
2735 appendFixedInt( nY, aTilingObj );
2736 aTilingObj.append( ' ' );
2737 appendFixedInt( nX+nW, aTilingObj );
2738 aTilingObj.append( ' ' );
2739 appendFixedInt( nY+nH, aTilingObj );
2740 aTilingObj.append( "]\n"
2741 "/XStep " );
2742 appendFixedInt( it->m_aCellSize.Width(), aTilingObj );
2743 aTilingObj.append( "\n"
2744 "/YStep " );
2745 appendFixedInt( it->m_aCellSize.Height(), aTilingObj );
2746 aTilingObj.append( "\n" );
2747 if( it->m_aTransform.matrix[0] != 1.0 ||
2748 it->m_aTransform.matrix[1] != 0.0 ||
2749 it->m_aTransform.matrix[3] != 0.0 ||
2750 it->m_aTransform.matrix[4] != 1.0 ||
2751 it->m_aTransform.matrix[2] != 0.0 ||
2752 it->m_aTransform.matrix[5] != 0.0 )
2754 aTilingObj.append( "/Matrix [" );
2755 // TODO: scaling, mirroring on y, etc
2756 appendDouble( it->m_aTransform.matrix[0], aTilingObj );
2757 aTilingObj.append( ' ' );
2758 appendDouble( it->m_aTransform.matrix[1], aTilingObj );
2759 aTilingObj.append( ' ' );
2760 appendDouble( it->m_aTransform.matrix[3], aTilingObj );
2761 aTilingObj.append( ' ' );
2762 appendDouble( it->m_aTransform.matrix[4], aTilingObj );
2763 aTilingObj.append( ' ' );
2764 appendDouble( it->m_aTransform.matrix[2], aTilingObj );
2765 aTilingObj.append( ' ' );
2766 appendDouble( it->m_aTransform.matrix[5], aTilingObj );
2767 aTilingObj.append( "]\n" );
2769 aTilingObj.append( "/Resources" );
2770 it->m_aResources.append( aTilingObj, getFontDictObject() );
2771 if( bDeflate )
2772 aTilingObj.append( "/Filter/FlateDecode" );
2773 aTilingObj.append( "/Length " );
2774 aTilingObj.append( (sal_Int32)nTilingStreamSize );
2775 aTilingObj.append( ">>\nstream\n" );
2776 CHECK_RETURN( updateObject( it->m_nObject ) );
2777 CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
2778 checkAndEnableStreamEncryption( it->m_nObject );
2779 nTilingStreamSize = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize );
2780 delete it->m_pTilingStream;
2781 it->m_pTilingStream = NULL;
2782 if( nTilingStreamSize == 0 )
2783 return false;
2784 disableStreamEncryption();
2785 aTilingObj.setLength( 0 );
2786 aTilingObj.append( "\nendstream\nendobj\n\n" );
2787 CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
2789 return true;
2792 sal_Int32 PDFWriterImpl::emitBuiltinFont( const ImplFontData* pFont, sal_Int32 nFontObject )
2794 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
2795 if( !pFD )
2796 return 0;
2797 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2799 OStringBuffer aLine( 1024 );
2801 if( nFontObject <= 0 )
2802 nFontObject = createObject();
2803 CHECK_RETURN( updateObject( nFontObject ) );
2804 aLine.append( nFontObject );
2805 aLine.append( " 0 obj\n"
2806 "<</Type/Font/Subtype/Type1/BaseFont/" );
2807 appendName( pBuiltinFont->m_pPSName, aLine );
2808 aLine.append( "\n" );
2809 if( pBuiltinFont->m_eCharSet == RTL_TEXTENCODING_MS_1252 )
2810 aLine.append( "/Encoding/WinAnsiEncoding\n" );
2811 aLine.append( ">>\nendobj\n\n" );
2812 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2813 return nFontObject;
2816 typedef int ThreeInts[3];
2817 static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen,
2818 ThreeInts& rSegmentLengths )
2820 if( !pFontBytes || (nByteLen < 0) )
2821 return false;
2822 const unsigned char* pPtr = pFontBytes;
2823 const unsigned char* pEnd = pFontBytes + nByteLen;
2825 for( int i = 0; i < 3; ++i) {
2826 // read segment1 header
2827 if( pPtr+6 >= pEnd )
2828 return false;
2829 if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) )
2830 return false;
2831 const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2];
2832 if( nLen <= 0)
2833 return false;
2834 rSegmentLengths[i] = nLen;
2835 pPtr += nLen + 6;
2838 // read segment-end header
2839 if( pPtr+2 >= pEnd )
2840 return false;
2841 if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) )
2842 return false;
2844 return true;
2847 // TODO: always subset instead of embedding the full font => this method becomes obsolete then
2848 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFontData* pFont, EmbedFont& rEmbed )
2850 std::map< sal_Int32, sal_Int32 > aRet;
2851 if( isBuiltinFont( pFont ) )
2853 aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont );
2854 return aRet;
2857 sal_Int32 nFontObject = 0;
2858 sal_Int32 nStreamObject = 0;
2859 sal_Int32 nFontDescriptor = 0;
2861 // prepare font encoding
2862 const Ucs2SIntMap* pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pFont, NULL );
2863 sal_Int32 nToUnicodeStream = 0;
2864 sal_uInt8 nEncoding[256];
2865 sal_Ucs nEncodedCodes[256];
2866 if( pEncoding )
2868 memset( nEncodedCodes, 0, sizeof(nEncodedCodes) );
2869 memset( nEncoding, 0, sizeof(nEncoding) );
2870 for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it )
2872 if( it->second != -1 )
2874 sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff);
2875 nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode );
2876 nEncodedCodes[ nCode ] = it->first;
2881 FontSubsetInfo aInfo;
2882 sal_Int32 pWidths[256];
2883 const unsigned char* pFontData = NULL;
2884 long nFontLen = 0;
2885 sal_Int32 nLength1, nLength2;
2886 if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL )
2888 if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 )
2889 goto streamend;
2890 // see whether it is pfb or pfa; if it is a pfb, fill ranges
2891 // of 6 bytes that are not part of the font program
2892 std::list< int > aSections;
2893 std::list< int >::const_iterator it;
2894 int nIndex = 0;
2895 while( pFontData[nIndex] == 0x80 && nIndex < nFontLen-1 )
2897 aSections.push_back( nIndex );
2898 if( pFontData[nIndex+1] == 0x03 )
2899 break;
2900 sal_Int32 nBytes =
2901 ((sal_Int32)pFontData[nIndex+2]) |
2902 ((sal_Int32)pFontData[nIndex+3]) << 8 |
2903 ((sal_Int32)pFontData[nIndex+4]) << 16 |
2904 ((sal_Int32)pFontData[nIndex+5]) << 24;
2905 nIndex += nBytes+6;
2908 // search for eexec
2909 // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below
2910 nIndex = 0;
2911 int nEndAsciiIndex;
2912 int nBeginBinaryIndex;
2913 int nEndBinaryIndex;
2916 while( nIndex < nFontLen-4 &&
2917 ( pFontData[nIndex] != 'e' ||
2918 pFontData[nIndex+1] != 'e' ||
2919 pFontData[nIndex+2] != 'x' ||
2920 pFontData[nIndex+3] != 'e' ||
2921 pFontData[nIndex+4] != 'c'
2924 nIndex++;
2925 // check whether we are in a excluded section
2926 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
2928 } while( it != aSections.end() && nIndex < nFontLen-4 );
2929 // this should end the ascii part
2930 if( nIndex > nFontLen-5 )
2931 goto streamend;
2933 nEndAsciiIndex = nIndex+4;
2934 // now count backwards until we can account for 512 '0'
2935 // which is the endmarker of the (hopefully) binary data
2936 // do not count the pfb header sections
2937 int nFound = 0;
2938 nIndex = nFontLen-1;
2939 while( nIndex > 0 && nFound < 512 )
2941 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
2943 if( it == aSections.end() )
2945 // inside the 512 '0' block there may only be whitespace
2946 // according to T1 spec; probably it would be to simple
2947 // if all fonts complied
2948 if( pFontData[nIndex] == '0' )
2949 nFound++;
2950 else if( nFound > 0 &&
2951 pFontData[nIndex] != '\r' &&
2952 pFontData[nIndex] != '\t' &&
2953 pFontData[nIndex] != '\n' &&
2954 pFontData[nIndex] != ' ' )
2955 break;
2957 nIndex--;
2960 if( nIndex < 1 || nIndex <= nEndAsciiIndex )
2961 goto streamend;
2962 // there may be whitespace to ignore before the 512 '0'
2963 while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' )
2965 nIndex--;
2966 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
2968 if( it != aSections.end() )
2970 nIndex = (*it)-1;
2971 break; // this is surely a binary boundary, in ascii case it wouldn't matter
2974 nEndBinaryIndex = nIndex;
2976 // and count forward again to the point where we have nFound '0'
2977 // to get the corect value for nLength3
2978 sal_Int32 nLength3 = 0;
2979 sal_Int32 nL3Index = nIndex;
2980 while( nFound && nL3Index < nFontLen )
2982 for( it = aSections.begin(); it != aSections.end() && (nL3Index < *it || nL3Index > ((*it) + 5) ); ++it )
2984 if( it == aSections.end() )
2986 // inside the 512 '0' block there may only be whitespace
2987 // according to T1 spec; probably it would be to simple
2988 // if all fonts complied
2989 if( pFontData[nL3Index] == '0' )
2990 nFound--;
2991 nLength3++;
2993 nL3Index++;
2996 // search for beginning of binary section
2997 nBeginBinaryIndex = nEndAsciiIndex;
3000 nBeginBinaryIndex++;
3001 for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it )
3003 } while( nBeginBinaryIndex < nEndBinaryIndex &&
3004 ( pFontData[nBeginBinaryIndex] == '\r' ||
3005 pFontData[nBeginBinaryIndex] == '\n' ||
3006 it != aSections.end() ) );
3008 // it seems to be vital to copy the exact whitespace between binary data
3009 // and eexec, else a invalid font results. so make nEndAsciiIndex
3010 // always immediate in front of nBeginBinaryIndex
3011 nEndAsciiIndex = nBeginBinaryIndex-1;
3012 for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it )
3014 if( it != aSections.end() )
3015 nEndAsciiIndex = (*it)-1;
3017 nLength1 = nEndAsciiIndex+1; // including the last character
3018 for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it )
3019 nLength1 -= 6; // decrease by pfb section size
3021 // if the first four bytes are all ascii hex characters, then binary data
3022 // has to be converted to real binary data
3023 for( nIndex = 0; nIndex < 4 &&
3024 ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) ||
3025 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) ||
3026 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' )
3027 ); ++nIndex )
3029 bool bConvertHexData = true;
3030 if( nIndex < 4 )
3032 bConvertHexData = false;
3033 nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte
3034 for( it = aSections.begin(); it != aSections.end(); ++it )
3035 if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex )
3036 nLength2 -= 6;
3038 else
3040 // count the hex ascii characters to get nLength2
3041 nLength2 = 0;
3042 int nNextSectionIndex = 0;
3043 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3045 if( it != aSections.end() )
3046 nNextSectionIndex = *it;
3047 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3049 if( nIndex == nNextSectionIndex )
3051 nIndex += 6;
3052 ++it;
3053 nNextSectionIndex = (it == aSections.end() ? 0 : *it );
3055 if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) ||
3056 ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) ||
3057 ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) )
3058 nLength2++;
3060 DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" );
3061 nLength2 /= 2;
3064 // now we can actually write the font stream !
3065 #if OSL_DEBUG_LEVEL > 1
3067 OStringBuffer aLine( " PDFWriterImpl::emitEmbeddedFont" );
3068 emitComment( aLine.getStr() );
3070 #endif
3071 OStringBuffer aLine( 512 );
3072 nStreamObject = createObject();
3073 if( !updateObject(nStreamObject))
3074 goto streamend;
3075 sal_Int32 nStreamLengthObject = createObject();
3076 aLine.append( nStreamObject );
3077 aLine.append( " 0 obj\n"
3078 "<</Length " );
3079 aLine.append( nStreamLengthObject );
3080 aLine.append( " 0 R"
3081 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3082 "/Filter/FlateDecode"
3083 #endif
3084 "/Length1 " );
3085 aLine.append( nLength1 );
3086 aLine.append( " /Length2 " );
3087 aLine.append( nLength2 );
3088 aLine.append( " /Length3 ");
3089 aLine.append( nLength3 );
3090 aLine.append( ">>\n"
3091 "stream\n" );
3092 if( !writeBuffer( aLine.getStr(), aLine.getLength() ) )
3093 goto streamend;
3095 sal_uInt64 nBeginStreamPos = 0;
3096 osl_getFilePos( m_aFile, &nBeginStreamPos );
3098 beginCompression();
3099 checkAndEnableStreamEncryption( nStreamObject );
3101 // write ascii section
3102 if( aSections.begin() == aSections.end() )
3104 if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) )
3106 endCompression();
3107 disableStreamEncryption();
3108 goto streamend;
3111 else
3113 // first section always starts at 0
3114 it = aSections.begin();
3115 nIndex = (*it)+6;
3116 ++it;
3117 while( *it < nEndAsciiIndex )
3119 if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) )
3121 endCompression();
3122 disableStreamEncryption();
3123 goto streamend;
3125 nIndex = (*it)+6;
3126 ++it;
3128 // write partial last section
3129 if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) )
3131 endCompression();
3132 disableStreamEncryption();
3133 goto streamend;
3137 // write binary section
3138 if( ! bConvertHexData )
3140 if( aSections.begin() == aSections.end() )
3142 if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) )
3144 endCompression();
3145 disableStreamEncryption();
3146 goto streamend;
3149 else
3151 for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it )
3153 // write first partial section
3154 if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) )
3156 endCompression();
3157 disableStreamEncryption();
3158 goto streamend;
3160 // write following sections
3161 while( it != aSections.end() )
3163 nIndex = (*it)+6;
3164 ++it;
3165 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3167 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3168 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3170 endCompression();
3171 disableStreamEncryption();
3172 goto streamend;
3178 else
3180 unsigned char* pWriteBuffer = (unsigned char*)rtl_allocateMemory( nLength2 );
3181 memset( pWriteBuffer, 0, nLength2 );
3182 int nWriteIndex = 0;
3184 int nNextSectionIndex = 0;
3185 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3187 if( it != aSections.end() )
3188 nNextSectionIndex = *it;
3189 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3191 if( nIndex == nNextSectionIndex )
3193 nIndex += 6;
3194 ++it;
3195 nNextSectionIndex = (it == aSections.end() ? nFontLen : *it );
3197 unsigned char cNibble = 0x80;
3198 if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' )
3199 cNibble = pFontData[nIndex] - '0';
3200 else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' )
3201 cNibble = pFontData[nIndex] - 'a' + 10;
3202 else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' )
3203 cNibble = pFontData[nIndex] - 'A' + 10;
3204 if( cNibble != 0x80 )
3206 if( !(nWriteIndex & 1 ) )
3207 cNibble <<= 4;
3208 pWriteBuffer[ nWriteIndex/2 ] |= cNibble;
3209 nWriteIndex++;
3212 if( ! writeBuffer( pWriteBuffer, nLength2 ) )
3214 endCompression();
3215 disableStreamEncryption();
3216 goto streamend;
3218 rtl_freeMemory( pWriteBuffer );
3220 if( aSections.empty() )
3222 if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) )
3224 endCompression();
3225 disableStreamEncryption();
3226 goto streamend;
3229 else
3231 // write rest of this section
3232 if( nIndex < nNextSectionIndex )
3234 if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) )
3236 endCompression();
3237 disableStreamEncryption();
3238 goto streamend;
3241 // write following sections
3242 while( it != aSections.end() )
3244 nIndex = (*it)+6;
3245 ++it;
3246 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3248 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3249 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3251 endCompression();
3252 disableStreamEncryption();
3253 goto streamend;
3259 endCompression();
3260 disableStreamEncryption();
3263 sal_uInt64 nEndStreamPos = 0;
3264 osl_getFilePos( m_aFile, &nEndStreamPos );
3266 // and finally close the stream
3267 aLine.setLength( 0 );
3268 aLine.append( "\nendstream\nendobj\n\n" );
3269 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3270 goto streamend;
3272 // write stream length object
3273 aLine.setLength( 0 );
3274 if( ! updateObject( nStreamLengthObject ) )
3275 goto streamend;
3276 aLine.append( nStreamLengthObject );
3277 aLine.append( " 0 obj\n" );
3278 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) );
3279 aLine.append( "\nendobj\n\n" );
3280 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3281 goto streamend;
3283 else
3285 rtl::OStringBuffer aErrorComment( 256 );
3286 aErrorComment.append( "GetEmbedFontData failed for font \"" );
3287 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3288 aErrorComment.append( '\"' );
3289 if( pFont->GetSlant() == ITALIC_NORMAL )
3290 aErrorComment.append( " italic" );
3291 else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3292 aErrorComment.append( " oblique" );
3293 aErrorComment.append( " weight=" );
3294 aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3295 emitComment( aErrorComment.getStr() );
3298 if( nStreamObject )
3299 // write font descriptor
3300 nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject );
3302 if( nFontDescriptor )
3304 if( pEncoding )
3305 nToUnicodeStream = createToUnicodeCMap( nEncoding, nEncodedCodes, sizeof(nEncoding)/sizeof(nEncoding[0]) );
3307 // write font object
3308 sal_Int32 nObject = createObject();
3309 if( ! updateObject( nObject ) )
3310 goto streamend;
3312 OStringBuffer aLine( 1024 );
3313 aLine.append( nObject );
3314 aLine.append( " 0 obj\n"
3315 "<</Type/Font/Subtype/Type1/BaseFont/" );
3316 appendName( aInfo.m_aPSName, aLine );
3317 aLine.append( "\n" );
3318 if( !pFont->mbSymbolFlag && pEncoding == 0 )
3319 aLine.append( "/Encoding/WinAnsiEncoding\n" );
3320 if( nToUnicodeStream )
3322 aLine.append( "/ToUnicode " );
3323 aLine.append( nToUnicodeStream );
3324 aLine.append( " 0 R\n" );
3326 aLine.append( "/FirstChar 0 /LastChar 255\n"
3327 "/Widths[" );
3328 for( int i = 0; i < 256; i++ )
3330 aLine.append( pWidths[i] );
3331 aLine.append( ((i&15) == 15) ? "\n" : " " );
3333 aLine.append( "]\n"
3334 "/FontDescriptor " );
3335 aLine.append( nFontDescriptor );
3336 aLine.append( " 0 R>>\n"
3337 "endobj\n\n" );
3338 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3339 goto streamend;
3341 nFontObject = nObject;
3343 aRet[ rEmbed.m_nNormalFontID ] = nObject;
3345 // write additional encodings
3346 for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it )
3348 sal_Int32 aEncWidths[ 256 ];
3349 // emit encoding dict
3350 sal_Int32 nEncObject = createObject();
3351 if( ! updateObject( nEncObject ) )
3352 goto streamend;
3354 OutputDevice* pRef = getReferenceDevice();
3355 pRef->Push( PUSH_FONT | PUSH_MAPMODE );
3356 pRef->SetMapMode( MapMode( MAP_PIXEL ) );
3357 Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) );
3358 aFont.SetWeight( pFont->GetWeight() );
3359 aFont.SetItalic( pFont->GetSlant() );
3360 aFont.SetPitch( pFont->GetPitch() );
3361 pRef->SetFont( aFont );
3362 pRef->ImplNewFont();
3364 aLine.setLength( 0 );
3365 aLine.append( nEncObject );
3366 aLine.append( " 0 obj\n"
3367 "<</Type/Encoding/Differences[ 0\n" );
3368 int nEncoded = 0;
3369 for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it )
3371 String aStr( str_it->m_aUnicode );
3372 aEncWidths[nEncoded] = pRef->GetTextWidth( aStr );
3373 nEncodedCodes[nEncoded] = str_it->m_aUnicode;
3374 nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded);
3376 aLine.append( " /" );
3377 aLine.append( str_it->m_aName );
3378 if( !((++nEncoded) & 15) )
3379 aLine.append( "\n" );
3381 aLine.append( "]>>\n"
3382 "endobj\n\n" );
3384 pRef->Pop();
3386 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3387 goto streamend;
3389 nToUnicodeStream = createToUnicodeCMap( nEncoding, nEncodedCodes, nEncoded );
3391 nObject = createObject();
3392 if( ! updateObject( nObject ) )
3393 goto streamend;
3395 aLine.setLength( 0 );
3396 aLine.append( nObject );
3397 aLine.append( " 0 obj\n"
3398 "<</Type/Font/Subtype/Type1/BaseFont/" );
3399 appendName( aInfo.m_aPSName, aLine );
3400 aLine.append( "\n" );
3401 aLine.append( "/Encoding " );
3402 aLine.append( nEncObject );
3403 aLine.append( " 0 R\n" );
3404 if( nToUnicodeStream )
3406 aLine.append( "/ToUnicode " );
3407 aLine.append( nToUnicodeStream );
3408 aLine.append( " 0 R\n" );
3410 aLine.append( "/FirstChar 0\n"
3411 "/LastChar " );
3412 aLine.append( (sal_Int32)(nEncoded-1) );
3413 aLine.append( "\n"
3414 "/Widths[" );
3415 for( int i = 0; i < nEncoded; i++ )
3417 aLine.append( aEncWidths[i] );
3418 aLine.append( ((i&15) == 15) ? "\n" : " " );
3420 aLine.append( " ]\n"
3421 "/FontDescriptor " );
3422 aLine.append( nFontDescriptor );
3423 aLine.append( " 0 R>>\n"
3424 "endobj\n\n" );
3425 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3426 goto streamend;
3428 aRet[ enc_it->m_nFontID ] = nObject;
3432 streamend:
3433 if( pFontData )
3434 m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3436 return aRet;
3439 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
3441 if( nSubsetID )
3443 for( int i = 0; i < 6; i++ )
3445 int nOffset = (nSubsetID % 26);
3446 nSubsetID /= 26;
3447 rBuffer.append( (sal_Char)('A'+nOffset) );
3449 rBuffer.append( '+' );
3451 appendName( rPSName, rBuffer );
3454 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, sal_Ucs* pUnicodes, int nGlyphs )
3456 int nMapped = 0, n = 0;
3457 for( n = 0; n < nGlyphs; n++ )
3458 if( pUnicodes[n] )
3459 nMapped++;
3461 if( nMapped == 0 )
3462 return 0;
3464 sal_Int32 nStream = createObject();
3465 CHECK_RETURN( updateObject( nStream ) );
3467 OStringBuffer aContents( 1024 );
3468 aContents.append(
3469 "/CIDInit/ProcSet findresource begin\n"
3470 "12 dict begin\n"
3471 "begincmap\n"
3472 "/CIDSystemInfo<<\n"
3473 "/Registry (Adobe)\n"
3474 "/Ordering (UCS)\n"
3475 "/Supplement 0\n"
3476 ">> def\n"
3477 "/CMapName/Adobe-Identity-UCS def\n"
3478 "/CMapType 2 def\n"
3479 "1 begincodespacerange\n"
3480 "<00> <FF>\n"
3481 "endcodespacerange\n"
3483 int nCount = 0;
3484 for( n = 0; n < nGlyphs; n++ )
3486 if( pUnicodes[n] )
3488 if( (nCount % 100) == 0 )
3490 if( nCount )
3491 aContents.append( "endbfchar\n" );
3492 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) );
3493 aContents.append( " beginbfchar\n" );
3495 aContents.append( '<' );
3496 appendHex( (sal_Int8)pEncoding[n], aContents );
3497 aContents.append( "> <" );
3498 // TODO: handle unicodes>U+FFFF
3499 appendHex( (sal_Int8)(pUnicodes[n] / 256), aContents );
3500 appendHex( (sal_Int8)(pUnicodes[n] & 255), aContents );
3501 aContents.append( ">\n" );
3502 nCount++;
3505 aContents.append( "endbfchar\n"
3506 "endcmap\n"
3507 "CMapName currentdict /CMap defineresource pop\n"
3508 "end\n"
3509 "end\n" );
3510 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3511 ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
3512 SvMemoryStream aStream;
3513 pCodec->BeginCompression();
3514 pCodec->Write( aStream, (const BYTE*)aContents.getStr(), aContents.getLength() );
3515 pCodec->EndCompression();
3516 delete pCodec;
3517 #endif
3519 #if OSL_DEBUG_LEVEL > 1
3521 OStringBuffer aLine( " PDFWriterImpl::createToUnicodeCMap" );
3522 emitComment( aLine.getStr() );
3524 #endif
3525 OStringBuffer aLine( 40 );
3527 aLine.append( nStream );
3528 aLine.append( " 0 obj\n<</Length " );
3529 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3530 sal_Int32 nLen = (sal_Int32)aStream.Tell();
3531 aStream.Seek( 0 );
3532 aLine.append( nLen );
3533 aLine.append( "/Filter/FlateDecode" );
3534 #else
3535 aLine.append( aContents.getLength() );
3536 #endif
3537 aLine.append( ">>\nstream\n" );
3538 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3539 checkAndEnableStreamEncryption( nStream );
3540 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3541 CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
3542 #else
3543 CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
3544 #endif
3545 disableStreamEncryption();
3546 aLine.setLength( 0 );
3547 aLine.append( "\nendstream\n"
3548 "endobj\n\n" );
3549 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3550 return nStream;
3553 sal_Int32 PDFWriterImpl::emitFontDescriptor( const ImplFontData* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
3555 OStringBuffer aLine( 1024 );
3556 // get font flags, see PDF reference 1.4 p. 358
3557 // possibly characters outside Adobe standard encoding
3558 // so set Symbolic flag
3559 sal_Int32 nFontFlags = (1<<2);
3560 if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE )
3561 nFontFlags |= (1 << 6);
3562 if( pFont->GetPitch() == PITCH_FIXED )
3563 nFontFlags |= 1;
3564 if( pFont->GetFamilyType() == FAMILY_SCRIPT )
3565 nFontFlags |= (1 << 3);
3566 else if( pFont->GetFamilyType() == FAMILY_ROMAN )
3567 nFontFlags |= (1 << 1);
3569 sal_Int32 nFontDescriptor = createObject();
3570 CHECK_RETURN( updateObject( nFontDescriptor ) );
3571 aLine.setLength( 0 );
3572 aLine.append( nFontDescriptor );
3573 aLine.append( " 0 obj\n"
3574 "<</Type/FontDescriptor/FontName/" );
3575 appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
3576 aLine.append( "\n"
3577 "/Flags " );
3578 aLine.append( nFontFlags );
3579 aLine.append( "\n"
3580 "/FontBBox[" );
3581 // note: Top and Bottom are reversed in VCL and PDF rectangles
3582 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() );
3583 aLine.append( ' ' );
3584 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() );
3585 aLine.append( ' ' );
3586 aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() );
3587 aLine.append( ' ' );
3588 aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) );
3589 aLine.append( "]/ItalicAngle " );
3590 if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL )
3591 aLine.append( "-30" );
3592 else
3593 aLine.append( "0" );
3594 aLine.append( "\n"
3595 "/Ascent " );
3596 aLine.append( (sal_Int32)rInfo.m_nAscent );
3597 aLine.append( "\n"
3598 "/Descent " );
3599 aLine.append( (sal_Int32)-rInfo.m_nDescent );
3600 aLine.append( "\n"
3601 "/CapHeight " );
3602 aLine.append( (sal_Int32)rInfo.m_nCapHeight );
3603 // According to PDF reference 1.4 StemV is required
3604 // seems a tad strange to me, but well ...
3605 aLine.append( "\n"
3606 "/StemV 80\n"
3607 "/FontFile" );
3608 switch( rInfo.m_nFontType )
3610 case FontSubsetInfo::SFNT_TTF:
3611 aLine.append( '2' );
3612 break;
3613 case FontSubsetInfo::TYPE1_PFA:
3614 case FontSubsetInfo::TYPE1_PFB:
3615 break;
3616 default:
3617 DBG_ERROR( "unknown fonttype in PDF font descriptor" );
3618 return 0;
3620 aLine.append( ' ' );
3621 aLine.append( nFontStream );
3622 aLine.append( " 0 R>>\n"
3623 "endobj\n\n" );
3624 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3626 return nFontDescriptor;
3629 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const
3631 for( std::map< sal_Int32, sal_Int32 >::const_iterator it =
3632 m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it )
3634 rDict.append( m_aBuiltinFonts[it->first].getNameObject() );
3635 rDict.append( ' ' );
3636 rDict.append( it->second );
3637 rDict.append( " 0 R" );
3641 bool PDFWriterImpl::emitFonts()
3643 if( ! m_pReferenceDevice->ImplGetGraphics() )
3644 return false;
3646 OStringBuffer aLine( 1024 );
3648 std::map< sal_Int32, sal_Int32 > aFontIDToObject;
3650 OUString aTmpName;
3651 osl_createTempFile( NULL, NULL, &aTmpName.pData );
3652 for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it )
3654 for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit )
3656 sal_Int32 pGlyphIDs[ 256 ];
3657 sal_Int32 pWidths[ 256 ];
3658 sal_uInt8 pEncoding[ 256 ];
3659 sal_Ucs pUnicodes[ 256 ];
3660 int nGlyphs = 1;
3661 // fill arrays and prepare encoding index map
3662 sal_Int32 nToUnicodeStream = 0;
3664 memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) );
3665 memset( pEncoding, 0, sizeof( pEncoding ) );
3666 memset( pUnicodes, 0, sizeof( pUnicodes ) );
3667 for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit )
3669 sal_uInt8 nEnc = fit->second.m_nSubsetGlyphID;
3671 DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" );
3672 DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" );
3674 pGlyphIDs[ nEnc ] = fit->first;
3675 pEncoding[ nEnc ] = nEnc;
3676 pUnicodes[ nEnc ] = fit->second.m_aUnicode;
3677 if( pUnicodes[ nEnc ] )
3678 nToUnicodeStream = 1;
3679 if( nGlyphs < 256 )
3680 nGlyphs++;
3681 else
3683 DBG_ERROR( "too many glyphs for subset" );
3686 FontSubsetInfo aSubsetInfo;
3687 if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
3689 // create font stream
3690 oslFileHandle aFontFile;
3691 CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) );
3692 // get file size
3693 sal_uInt64 nLength1;
3694 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) );
3695 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength1 ) ) );
3696 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
3698 #if OSL_DEBUG_LEVEL > 1
3700 OStringBuffer aLine1( " PDFWriterImpl::emitFonts" );
3701 emitComment( aLine1.getStr() );
3703 #endif
3704 sal_Int32 nFontStream = createObject();
3705 sal_Int32 nStreamLengthObject = createObject();
3706 CHECK_RETURN( updateObject( nFontStream ) );
3707 aLine.setLength( 0 );
3708 aLine.append( nFontStream );
3709 aLine.append( " 0 obj\n"
3710 "<</Length " );
3711 aLine.append( (sal_Int32)nStreamLengthObject );
3712 aLine.append( " 0 R"
3713 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3714 "/Filter/FlateDecode"
3715 #endif
3716 "/Length1 " );
3718 sal_uInt64 nStartPos = 0;
3719 if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF )
3721 aLine.append( (sal_Int32)nLength1 );
3723 aLine.append( ">>\n"
3724 "stream\n" );
3725 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3726 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
3728 // copy font file
3729 beginCompression();
3730 checkAndEnableStreamEncryption( nFontStream );
3731 sal_Bool bEOF = sal_False;
3734 char buf[8192];
3735 sal_uInt64 nRead;
3736 CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) );
3737 CHECK_RETURN( writeBuffer( buf, nRead ) );
3738 CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) );
3739 } while( ! bEOF );
3741 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 )
3743 // TODO: implement
3744 DBG_ERROR( "PDFWriterImpl does not support CFF-font subsets yet!" );
3746 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA?
3748 unsigned char* pBuffer = new unsigned char[ (int)nLength1 ];
3750 sal_uInt64 nBytesRead = 0;
3751 CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer, nLength1, &nBytesRead ) ) );
3752 DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" );
3753 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
3754 // get the PFB-segment lengths
3755 ThreeInts aSegmentLengths = {0,0,0};
3756 getPfbSegmentLengths( pBuffer, (int)nBytesRead, aSegmentLengths );
3757 // the lengths below are mandatory for PDF-exported Type1 fonts
3758 // because the PFB segment headers get stripped! WhyOhWhy.
3759 aLine.append( (sal_Int32)aSegmentLengths[0] );
3760 aLine.append( "/Length2 " );
3761 aLine.append( (sal_Int32)aSegmentLengths[1] );
3762 aLine.append( "/Length3 " );
3763 aLine.append( (sal_Int32)aSegmentLengths[2] );
3765 aLine.append( ">>\n"
3766 "stream\n" );
3767 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3768 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
3770 // emit PFB-sections without section headers
3771 beginCompression();
3772 checkAndEnableStreamEncryption( nFontStream );
3773 CHECK_RETURN( writeBuffer( pBuffer+ 6, aSegmentLengths[0] ) );
3774 CHECK_RETURN( writeBuffer( pBuffer+12 + aSegmentLengths[0], aSegmentLengths[1] ) );
3775 CHECK_RETURN( writeBuffer( pBuffer+18 + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) );
3777 delete[] pBuffer;
3779 else
3781 fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType);
3782 aLine.append( "0 >>\nstream\n" );
3785 endCompression();
3786 disableStreamEncryption();
3787 // close the file
3788 osl_closeFile( aFontFile );
3790 sal_uInt64 nEndPos = 0;
3791 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) );
3792 // end the stream
3793 aLine.setLength( 0 );
3794 aLine.append( "\nendstream\nendobj\n\n" );
3795 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3797 // emit stream length object
3798 CHECK_RETURN( updateObject( nStreamLengthObject ) );
3799 aLine.setLength( 0 );
3800 aLine.append( nStreamLengthObject );
3801 aLine.append( " 0 obj\n" );
3802 aLine.append( (sal_Int64)(nEndPos-nStartPos) );
3803 aLine.append( "\nendobj\n\n" );
3804 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3806 // write font descriptor
3807 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream );
3809 if( nToUnicodeStream )
3810 nToUnicodeStream = createToUnicodeCMap( pEncoding, pUnicodes, nGlyphs );
3812 sal_Int32 nFontObject = createObject();
3813 CHECK_RETURN( updateObject( nFontObject ) );
3814 aLine.setLength( 0 );
3815 aLine.append( nFontObject );
3817 aLine.append( " 0 obj\n" );
3818 aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ?
3819 "<</Type/Font/Subtype/Type1/BaseFont/" :
3820 "<</Type/Font/Subtype/TrueType/BaseFont/" );
3821 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine );
3822 aLine.append( "\n"
3823 "/FirstChar 0\n"
3824 "/LastChar " );
3825 aLine.append( (sal_Int32)(nGlyphs-1) );
3826 aLine.append( "\n"
3827 "/Widths[" );
3828 for( int i = 0; i < nGlyphs; i++ )
3830 aLine.append( pWidths[ i ] );
3831 aLine.append( ((i & 15) == 15) ? "\n" : " " );
3833 aLine.append( "]\n"
3834 "/FontDescriptor " );
3835 aLine.append( nFontDescriptor );
3836 aLine.append( " 0 R\n" );
3837 if( nToUnicodeStream )
3839 aLine.append( "/ToUnicode " );
3840 aLine.append( nToUnicodeStream );
3841 aLine.append( " 0 R\n" );
3843 aLine.append( ">>\n"
3844 "endobj\n\n" );
3845 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3847 aFontIDToObject[ lit->m_nFontID ] = nFontObject;
3849 else
3851 const ImplFontData* pFont = it->first;
3852 rtl::OStringBuffer aErrorComment( 256 );
3853 aErrorComment.append( "CreateFontSubset failed for font \"" );
3854 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3855 aErrorComment.append( '\"' );
3856 if( pFont->GetSlant() == ITALIC_NORMAL )
3857 aErrorComment.append( " italic" );
3858 else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3859 aErrorComment.append( " oblique" );
3860 aErrorComment.append( " weight=" );
3861 aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3862 emitComment( aErrorComment.getStr() );
3866 osl_removeFile( aTmpName.pData );
3868 // emit embedded fonts
3869 for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit )
3871 std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second );
3872 for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
3874 CHECK_RETURN( fit->second );
3875 aFontIDToObject[ fit->first ] = fit->second;
3879 OStringBuffer aFontDict( 1024 );
3880 aFontDict.append( getFontDictObject() );
3881 aFontDict.append( " 0 obj\n"
3882 "<<" );
3883 int ni = 0;
3884 for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit )
3886 aFontDict.append( "/F" );
3887 aFontDict.append( mit->first );
3888 aFontDict.append( ' ' );
3889 aFontDict.append( mit->second );
3890 aFontDict.append( " 0 R" );
3891 if( ((++ni) & 7) == 0 )
3892 aFontDict.append( '\n' );
3894 // emit builtin font for widget apperances / variable text
3895 for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin();
3896 it != m_aBuiltinFontToObjectMap.end(); ++it )
3898 ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]);
3899 it->second = emitBuiltinFont( &aData, it->second );
3901 appendBuiltinFontsToDict( aFontDict );
3902 aFontDict.append( "\n>>\nendobj\n\n" );
3904 CHECK_RETURN( updateObject( getFontDictObject() ) );
3905 CHECK_RETURN( writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) );
3906 return true;
3909 sal_Int32 PDFWriterImpl::emitResources()
3911 // emit shadings
3912 if( ! m_aGradients.empty() )
3913 CHECK_RETURN( emitGradients() );
3914 // emit tilings
3915 if( ! m_aTilings.empty() )
3916 CHECK_RETURN( emitTilings() );
3918 // emit font dict
3919 CHECK_RETURN( emitFonts() );
3921 // emit Resource dict
3922 OStringBuffer aLine( 512 );
3923 sal_Int32 nResourceDict = getResourceDictObj();
3924 CHECK_RETURN( updateObject( nResourceDict ) );
3925 aLine.setLength( 0 );
3926 aLine.append( nResourceDict );
3927 aLine.append( " 0 obj\n" );
3928 m_aGlobalResourceDict.append( aLine, getFontDictObject() );
3929 aLine.append( "endobj\n\n" );
3930 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3931 return nResourceDict;
3934 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
3935 sal_Int32 nItemLevel,
3936 sal_Int32 nCurrentItemId )
3938 /* The /Count number of an item is
3939 positive: the number of visible subitems
3940 negative: the negative number of subitems that will become visible if
3941 the item gets opened
3942 see PDF ref 1.4 p 478
3945 sal_Int32 nCount = 0;
3947 if( m_aContext.OpenBookmarkLevels < 0 || // all levels arevisible
3948 m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible
3951 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
3952 sal_Int32 nChildren = rItem.m_aChildren.size();
3953 for( sal_Int32 i = 0; i < nChildren; i++ )
3954 nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
3955 rCounts[nCurrentItemId] = nCount;
3956 // return 1 (this item) + visible sub items
3957 if( nCount < 0 )
3958 nCount = 0;
3959 nCount++;
3961 else
3963 // this bookmark level is invisible
3964 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
3965 sal_Int32 nChildren = rItem.m_aChildren.size();
3966 rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
3967 for( sal_Int32 i = 0; i < nChildren; i++ )
3968 updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
3969 nCount = -1;
3972 return nCount;
3975 sal_Int32 PDFWriterImpl::emitOutline()
3977 int i, nItems = m_aOutline.size();
3979 // do we have an outline at all ?
3980 if( nItems < 2 )
3981 return 0;
3983 // reserve object numbers for all outline items
3984 for( i = 0; i < nItems; ++i )
3985 m_aOutline[i].m_nObject = createObject();
3987 // update all parent, next and prev object ids
3988 for( i = 0; i < nItems; ++i )
3990 PDFOutlineEntry& rItem = m_aOutline[i];
3991 int nChildren = rItem.m_aChildren.size();
3993 if( nChildren )
3995 for( int n = 0; n < nChildren; ++n )
3997 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
3999 rChild.m_nParentObject = rItem.m_nObject;
4000 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
4001 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
4007 // calculate Count entries for all items
4008 std::vector< sal_Int32 > aCounts( nItems );
4009 updateOutlineItemCount( aCounts, 0, 0 );
4011 // emit hierarchy
4012 for( i = 0; i < nItems; ++i )
4014 PDFOutlineEntry& rItem = m_aOutline[i];
4015 OStringBuffer aLine( 1024 );
4017 CHECK_RETURN( updateObject( rItem.m_nObject ) );
4018 aLine.append( rItem.m_nObject );
4019 aLine.append( " 0 obj\n" );
4020 aLine.append( "<<" );
4021 // number of visible children (all levels)
4022 if( i > 0 || aCounts[0] > 0 )
4024 aLine.append( "/Count " );
4025 aLine.append( aCounts[i] );
4027 if( ! rItem.m_aChildren.empty() )
4029 // children list: First, Last
4030 aLine.append( "/First " );
4031 aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
4032 aLine.append( " 0 R/Last " );
4033 aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
4034 aLine.append( " 0 R\n" );
4036 if( i > 0 )
4038 // Title, Dest, Parent, Prev, Next
4039 aLine.append( "/Title" );
4040 appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
4041 aLine.append( "\n" );
4042 // Dest is not required
4043 if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() )
4045 aLine.append( "/Dest" );
4046 appendDest( rItem.m_nDestID, aLine );
4048 aLine.append( "/Parent " );
4049 aLine.append( rItem.m_nParentObject );
4050 aLine.append( " 0 R" );
4051 if( rItem.m_nPrevObject )
4053 aLine.append( "/Prev " );
4054 aLine.append( rItem.m_nPrevObject );
4055 aLine.append( " 0 R" );
4057 if( rItem.m_nNextObject )
4059 aLine.append( "/Next " );
4060 aLine.append( rItem.m_nNextObject );
4061 aLine.append( " 0 R" );
4064 aLine.append( ">>\nendobj\n\n" );
4065 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4068 return m_aOutline[0].m_nObject;
4071 #undef CHECK_RETURN
4072 #define CHECK_RETURN( x ) if( !x ) return false
4074 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
4076 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() )
4078 #if OSL_DEBUG_LEVEL > 1
4079 fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID );
4080 #endif
4081 return false;
4085 const PDFDest& rDest = m_aDests[ nDestID ];
4086 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
4088 rBuffer.append( '[' );
4089 rBuffer.append( rDestPage.m_nPageObject );
4090 rBuffer.append( " 0 R" );
4092 switch( rDest.m_eType )
4094 case PDFWriter::XYZ:
4095 default:
4096 rBuffer.append( "/XYZ " );
4097 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4098 rBuffer.append( ' ' );
4099 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4100 rBuffer.append( " 0" );
4101 break;
4102 case PDFWriter::Fit:
4103 rBuffer.append( "/Fit" );
4104 break;
4105 case PDFWriter::FitRectangle:
4106 rBuffer.append( "/FitR " );
4107 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4108 rBuffer.append( ' ' );
4109 appendFixedInt( rDest.m_aRect.Top(), rBuffer );
4110 rBuffer.append( ' ' );
4111 appendFixedInt( rDest.m_aRect.Right(), rBuffer );
4112 rBuffer.append( ' ' );
4113 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4114 break;
4115 case PDFWriter::FitHorizontal:
4116 rBuffer.append( "/FitH " );
4117 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4118 break;
4119 case PDFWriter::FitVertical:
4120 rBuffer.append( "/FitV " );
4121 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4122 break;
4123 case PDFWriter::FitPageBoundingBox:
4124 rBuffer.append( "/FitB" );
4125 break;
4126 case PDFWriter::FitPageBoundingBoxHorizontal:
4127 rBuffer.append( "/FitBH " );
4128 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4129 break;
4130 case PDFWriter::FitPageBoundingBoxVertical:
4131 rBuffer.append( "/FitBV " );
4132 appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4133 break;
4135 rBuffer.append( ']' );
4137 return true;
4140 bool PDFWriterImpl::emitLinkAnnotations()
4142 int nAnnots = m_aLinks.size();
4143 for( int i = 0; i < nAnnots; i++ )
4145 const PDFLink& rLink = m_aLinks[i];
4146 if( ! updateObject( rLink.m_nObject ) )
4147 continue;
4149 OStringBuffer aLine( 1024 );
4150 aLine.append( rLink.m_nObject );
4151 aLine.append( " 0 obj\n" );
4152 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4153 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4154 aLine.append( "<</Type/Annot" );
4155 if( m_bIsPDF_A1 )
4156 aLine.append( "/F 4" );
4157 aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
4159 appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
4160 aLine.append( ' ' );
4161 appendFixedInt( rLink.m_aRect.Top(), aLine );
4162 aLine.append( ' ' );
4163 appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
4164 aLine.append( ' ' );
4165 appendFixedInt( rLink.m_aRect.Bottom(), aLine );
4166 aLine.append( "]" );
4167 if( rLink.m_nDest >= 0 )
4169 aLine.append( "/Dest" );
4170 appendDest( rLink.m_nDest, aLine );
4172 else
4174 /*--->i56629
4175 destination is external to the document, so
4176 we check in the following sequence:
4178 if target type is neither .pdf, nor .od[tpgs], then
4179 check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
4180 end processing
4181 else if target is .od[tpgs]: then
4182 if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file
4183 processing continue
4185 if (new)target is .pdf : then
4186 if GotToR is requested, then
4187 convert the target in GoToR where the fragment of the URI is
4188 considered the named destination in the target file, set relative or absolute as requested
4189 else strip the fragment from URL and then set URI or 'launch application' as requested
4192 // FIXME: check if the decode mechanisms for URL processing throughout this implementation
4193 // are the correct one!!
4195 // extract target file type
4196 INetURLObject aDocumentURL( m_aContext.BaseURL );
4197 INetURLObject aTargetURL( rLink.m_aURL );
4198 sal_Int32 nChangeFileExtensionToPDF = 0;
4199 sal_Int32 nSetGoToRMode = 0;
4200 sal_Bool bTargetHasPDFExtension = sal_False;
4201 INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
4202 sal_Bool bIsUNCPath = sal_False;
4203 // check if the protocol is a known one, or if there is no protocol at all (on target only)
4204 // if there is no protocol, make the target relative to the current document directory
4205 // getting the needed URL information from the current document path
4206 if( eTargetProtocol == INET_PROT_NOT_VALID )
4208 if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.compareToAscii( "\\\\\\\\", 4 ) == 0)
4210 bIsUNCPath = sal_True;
4212 else
4214 INetURLObject aNewBase( aDocumentURL );//duplicate document URL
4215 aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
4216 //target document
4217 aNewBase.insertName( rLink.m_aURL );
4218 aTargetURL = aNewBase;//reassign the new target URL
4219 //recompute the target protocol, with the new URL
4220 //normal URL processing resumes
4221 eTargetProtocol = aTargetURL.GetProtocol();
4225 rtl::OUString aFileExtension = aTargetURL.GetFileExtension();
4227 // Check if the URL ends in '/': if yes it's a directory,
4228 // it will be forced to a URI link.
4229 // possibly a malformed URI, leave it as it is, force as URI
4230 if( aTargetURL.hasFinalSlash() )
4231 m_aContext.DefaultLinkAction = PDFWriter::URIAction;
4233 if( aFileExtension.getLength() > 0 )
4235 if( m_aContext.ConvertOOoTargetToPDFTarget )
4237 //examine the file type (.odm .odt. .odp, odg, ods)
4238 if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odm" ) ) ) )
4239 nChangeFileExtensionToPDF++;
4240 if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odt" ) ) ) )
4241 nChangeFileExtensionToPDF++;
4242 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odp" ) ) ) )
4243 nChangeFileExtensionToPDF++;
4244 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odg" ) ) ) )
4245 nChangeFileExtensionToPDF++;
4246 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ods" ) ) ) )
4247 nChangeFileExtensionToPDF++;
4248 if( nChangeFileExtensionToPDF )
4249 aTargetURL.setExtension(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4251 //check if extension is pdf, see if GoToR should be forced
4252 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4253 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
4254 nSetGoToRMode++;
4256 //prepare the URL, if relative or not
4257 INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
4258 //queue the string common to all types of actions
4259 aLine.append( "/A<</Type/Action/S");
4260 if( bIsUNCPath ) // handle Win UNC paths
4262 aLine.append( "/Launch/Win<</F" );
4263 // INetURLObject is not good with UNC paths, use original path
4264 appendLiteralStringEncrypt( rLink.m_aURL, rLink.m_nObject, aLine );
4265 aLine.append( ">>" );
4267 else
4269 sal_Int32 nSetRelative = 0;
4270 //check if relative file link is requested and if the protocol is 'file://'
4271 if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE )
4272 nSetRelative++;
4274 rtl::OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is,
4275 if( nSetGoToRMode == 0 )
4276 switch( m_aContext.DefaultLinkAction )
4278 default:
4279 case PDFWriter::URIAction :
4280 case PDFWriter::URIActionDestination :
4281 aLine.append( "/URI/URI" );
4282 break;
4283 case PDFWriter::LaunchAction:
4284 // now:
4285 // if a launch action is requested and the hyperlink target has a fragment
4286 // and the target file does not have a pdf extension, or it's not a 'file:://' protocol
4287 // then force the uri action on it
4288 // This code will permit the correct opening of application on web pages, the one that
4289 // normally have fragments (but I may be wrong...)
4290 // and will force the use of URI when the protocol is not file://
4291 if( (aFragment.getLength() > 0 && !bTargetHasPDFExtension) ||
4292 eTargetProtocol != INET_PROT_FILE )
4293 aLine.append( "/URI/URI" );
4294 else
4295 aLine.append( "/Launch/F" );
4296 break;
4298 //fragment are encoded in the same way as in the named destination processing
4299 rtl::OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET );
4300 if( nSetGoToRMode )
4301 {//add the fragment
4302 aLine.append("/GoToR");
4303 aLine.append("/F");
4304 appendLiteralStringEncrypt( nSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark,
4305 INetURLObject::WAS_ENCODED,
4306 INetURLObject::DECODE_WITH_CHARSET ) :
4307 aURLNoMark, rLink.m_nObject, aLine );
4308 if( aFragment.getLength() > 0 )
4310 aLine.append("/D/");
4311 appendDestinationName( aFragment , aLine );
4314 else
4316 // change the fragment to accomodate the bookmark (only if the file extension is PDF and
4317 // the requested action is of the correct type)
4318 if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination &&
4319 bTargetHasPDFExtension && aFragment.getLength() > 0 )
4321 OStringBuffer aLineLoc( 1024 );
4322 appendDestinationName( aFragment , aLineLoc );
4323 //substitute the fragment
4324 aTargetURL.SetMark( aLineLoc.getStr() );
4326 rtl::OUString aURL = aTargetURL.GetMainURL( (nSetRelative || eTargetProtocol == INET_PROT_FILE) ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE );
4327 // check if we have a URL available, if the string is empty, set it as the original one
4328 // if( aURL.getLength() == 0 )
4329 // appendLiteralStringEncrypt( rLink.m_aURL , rLink.m_nObject, aLine );
4330 // else
4331 appendLiteralStringEncrypt( nSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL ) :
4332 aURL , rLink.m_nObject, aLine );
4334 //<--- i56629
4336 aLine.append( ">>\n" );
4338 if( rLink.m_nStructParent > 0 )
4340 aLine.append( "/StructParent " );
4341 aLine.append( rLink.m_nStructParent );
4343 aLine.append( ">>\nendobj\n\n" );
4344 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4347 return true;
4350 bool PDFWriterImpl::emitNoteAnnotations()
4352 // emit note annotations
4353 int nAnnots = m_aNotes.size();
4354 for( int i = 0; i < nAnnots; i++ )
4356 const PDFNoteEntry& rNote = m_aNotes[i];
4357 if( ! updateObject( rNote.m_nObject ) )
4358 return false;
4360 OStringBuffer aLine( 1024 );
4361 aLine.append( rNote.m_nObject );
4362 aLine.append( " 0 obj\n" );
4363 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4364 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4365 aLine.append( "<</Type/Annot" );
4366 if( m_bIsPDF_A1 )
4367 aLine.append( "/F 4" );
4368 aLine.append( "/Subtype/Text/Rect[" );
4370 appendFixedInt( rNote.m_aRect.Left(), aLine );
4371 aLine.append( ' ' );
4372 appendFixedInt( rNote.m_aRect.Top(), aLine );
4373 aLine.append( ' ' );
4374 appendFixedInt( rNote.m_aRect.Right(), aLine );
4375 aLine.append( ' ' );
4376 appendFixedInt( rNote.m_aRect.Bottom(), aLine );
4377 aLine.append( "]" );
4379 // contents of the note (type text string)
4380 aLine.append( "/Contents\n" );
4381 appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
4382 aLine.append( "\n" );
4384 // optional title
4385 if( rNote.m_aContents.Title.Len() )
4387 aLine.append( "/T" );
4388 appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
4389 aLine.append( "\n" );
4392 aLine.append( ">>\nendobj\n\n" );
4393 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4395 return true;
4398 Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font& rAppSetFont )
4400 bool bAdjustSize = false;
4402 Font aFont( rControlFont );
4403 if( ! aFont.GetName().Len() )
4405 aFont = rAppSetFont;
4406 if( rControlFont.GetHeight() )
4407 aFont.SetSize( Size( 0, rControlFont.GetHeight() ) );
4408 else
4409 bAdjustSize = true;
4410 if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
4411 aFont.SetItalic( rControlFont.GetItalic() );
4412 if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
4413 aFont.SetWeight( rControlFont.GetWeight() );
4415 else if( ! aFont.GetHeight() )
4417 aFont.SetSize( rAppSetFont.GetSize() );
4418 bAdjustSize = true;
4420 if( bAdjustSize )
4422 Size aFontSize = aFont.GetSize();
4423 OutputDevice* pDefDev = Application::GetDefaultDevice();
4424 aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
4425 aFont.SetSize( aFontSize );
4427 return aFont;
4430 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont )
4432 sal_Int32 nBest = 4; // default to Helvetica
4433 OUString aFontName( rFont.GetName() );
4434 aFontName = aFontName.toAsciiLowerCase();
4436 if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "times" ) ) ) != -1 )
4437 nBest = 8;
4438 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "courier" ) ) ) != -1 )
4439 nBest = 0;
4440 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "dingbats" ) ) ) != -1 )
4441 nBest = 13;
4442 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "symbol" ) ) ) != -1 )
4443 nBest = 12;
4444 if( nBest < 12 )
4446 if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
4447 nBest += 1;
4448 if( rFont.GetWeight() > WEIGHT_MEDIUM )
4449 nBest += 2;
4452 if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() )
4453 m_aBuiltinFontToObjectMap[ nBest ] = createObject();
4455 return nBest;
4458 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
4460 return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1;
4463 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget )
4465 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4467 // save graphics state
4468 push( sal::static_int_cast<sal_uInt16>(~0U) );
4470 // transform relative to control's coordinates since an
4471 // appearance stream is a form XObject
4472 // this relies on the m_aRect member of rButton NOT already being transformed
4473 // to default user space
4474 if( rWidget.Background || rWidget.Border )
4476 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) );
4477 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) );
4478 drawRectangle( rWidget.Location );
4480 // prepare font to use
4481 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
4482 setFont( aFont );
4483 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
4485 drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
4487 // create DA string while local mapmode is still in place
4488 // (that is before endRedirect())
4489 OStringBuffer aDA( 256 );
4490 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
4491 Font aDummyFont( String( RTL_CONSTASCII_USTRINGPARAM( "Helvetica" ) ), aFont.GetSize() );
4492 sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont );
4493 aDA.append( ' ' );
4494 aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() );
4495 aDA.append( ' ' );
4496 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4497 aDA.append( " Tf" );
4498 rButton.m_aDAString = aDA.makeStringAndClear();
4500 pop();
4502 rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
4504 /* seems like a bad hack but at least works in both AR5 and 6:
4505 we draw the button ourselves and tell AR
4506 the button would be totally transparent with no text
4508 One would expect that simply setting a normal appearance
4509 should suffice, but no, as soon as the user actually presses
4510 the button and an action is tied to it (gasp! a button that
4511 does something) the appearance gets replaced by some crap that AR
4512 creates on the fly even if no DA or MK is given. On AR6 at least
4513 the DA and MK work as expected, but on AR5 this creates a region
4514 filled with the background color but nor text. Urgh.
4516 rButton.m_aMKDict = "/BC [] /BG [] /CA";
4517 rButton.m_aMKDictCAString = "";
4520 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern,
4521 const PDFWriter::AnyWidget& rWidget,
4522 const StyleSettings& rSettings )
4524 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
4526 if( rWidget.Background || rWidget.Border )
4528 if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) )
4530 sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500;
4531 if( nDelta < 1 )
4532 nDelta = 1;
4533 setLineColor( Color( COL_TRANSPARENT ) );
4534 Rectangle aRect = rIntern.m_aRect;
4535 setFillColor( rSettings.GetLightBorderColor() );
4536 drawRectangle( aRect );
4537 aRect.Left() += nDelta; aRect.Top() += nDelta;
4538 aRect.Right() -= nDelta; aRect.Bottom() -= nDelta;
4539 setFillColor( rSettings.GetFieldColor() );
4540 drawRectangle( aRect );
4541 setFillColor( rSettings.GetLightColor() );
4542 drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
4543 drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
4544 setFillColor( rSettings.GetDarkShadowColor() );
4545 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
4546 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
4548 else
4550 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) );
4551 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4552 drawRectangle( rIntern.m_aRect );
4555 if( rWidget.Border )
4557 // adjust edit area accounting for border
4558 sal_Int32 nDelta = aFont.GetHeight()/4;
4559 if( nDelta < 1 )
4560 nDelta = 1;
4561 rIntern.m_aRect.Left() += nDelta;
4562 rIntern.m_aRect.Top() += nDelta;
4563 rIntern.m_aRect.Right() -= nDelta;
4564 rIntern.m_aRect.Bottom()-= nDelta;
4567 return aFont;
4570 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget )
4572 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4573 SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
4575 push( sal::static_int_cast<sal_uInt16>(~0U) );
4577 // prepare font to use, draw field border
4578 Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
4579 sal_Int32 nBest = getBestBuiltinFont( aFont );
4581 // prepare DA string
4582 OStringBuffer aDA( 32 );
4583 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4584 aDA.append( ' ' );
4585 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4586 aDA.append( ' ' );
4587 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4588 aDA.append( " Tf" );
4590 /* create an empty appearance stream, let the viewer create
4591 the appearance at runtime. This is because AR5 seems to
4592 paint the widget appearance always, and a dynamically created
4593 appearance on top of it. AR6 is well behaved in that regard, so
4594 that behaviour seems to be a bug. Anyway this empty appearance
4595 relies on /NeedAppearances in the AcroForm dictionary set to "true"
4597 beginRedirect( pEditStream, rEdit.m_aRect );
4598 OStringBuffer aAppearance( 32 );
4599 aAppearance.append( "/Tx BMC\nEMC\n" );
4600 writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4602 endRedirect();
4603 pop();
4605 rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
4607 rEdit.m_aDAString = aDA.makeStringAndClear();
4610 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget )
4612 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4613 SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
4615 push( sal::static_int_cast<sal_uInt16>(~0U) );
4617 // prepare font to use, draw field border
4618 Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
4619 sal_Int32 nBest = getBestBuiltinFont( aFont );
4621 beginRedirect( pListBoxStream, rBox.m_aRect );
4622 OStringBuffer aAppearance( 64 );
4624 #if 0
4625 if( ! rWidget.DropDown )
4627 // prepare linewidth for DA string hack, see below
4628 Size aFontSize = lcl_convert( m_aGraphicsStack.front().m_aMapMode,
4629 m_aMapMode,
4630 getReferenceDevice(),
4631 Size( 0, aFont.GetHeight() ) );
4632 sal_Int32 nLW = aFontSize.Height() / 40;
4633 appendFixedInt( nLW > 0 ? nLW : 1, aAppearance );
4634 aAppearance.append( " w\n" );
4635 writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4636 aAppearance.setLength( 0 );
4638 #endif
4640 setLineColor( Color( COL_TRANSPARENT ) );
4641 setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
4642 drawRectangle( rBox.m_aRect );
4644 // empty appearance, see createDefaultEditAppearance for reference
4645 aAppearance.append( "/Tx BMC\nEMC\n" );
4646 writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4648 endRedirect();
4649 pop();
4651 rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
4653 // prepare DA string
4654 OStringBuffer aDA( 256 );
4655 #if 0
4656 if( !rWidget.DropDown )
4658 /* another of AR5's peculiarities: the selected item of a choice
4659 field is highlighted using the non stroking color - same as the
4660 text color. so workaround that by using text rendering mode 2
4661 (fill, then stroke) and set the stroking color
4663 appendStrokingColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ), aDA );
4664 aDA.append( " 2 Tr " );
4666 #endif
4667 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4668 aDA.append( ' ' );
4669 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4670 aDA.append( ' ' );
4671 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4672 aDA.append( " Tf" );
4673 rBox.m_aDAString = aDA.makeStringAndClear();
4676 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget )
4678 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4680 // save graphics state
4681 push( sal::static_int_cast<sal_uInt16>(~0U) );
4683 if( rWidget.Background || rWidget.Border )
4685 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
4686 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4687 drawRectangle( rBox.m_aRect );
4690 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
4691 setFont( aFont );
4692 Size aFontSize = aFont.GetSize();
4693 if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
4694 aFontSize.Height() = rBox.m_aRect.GetHeight();
4695 sal_Int32 nDelta = aFontSize.Height()/10;
4696 if( nDelta < 1 )
4697 nDelta = 1;
4699 Rectangle aCheckRect, aTextRect;
4700 if( rWidget.ButtonIsLeft )
4702 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
4703 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4704 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
4705 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4707 // #i74206# handle small controls without text area
4708 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4710 aCheckRect.Right() -= nDelta;
4711 aCheckRect.Top() += nDelta/2;
4712 aCheckRect.Bottom() -= nDelta - (nDelta/2);
4715 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
4716 aTextRect.Top() = rBox.m_aRect.Top();
4717 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4718 aTextRect.Bottom() = rBox.m_aRect.Bottom();
4720 else
4722 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
4723 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4724 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
4725 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4727 // #i74206# handle small controls without text area
4728 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4730 aCheckRect.Left() += nDelta;
4731 aCheckRect.Top() += nDelta/2;
4732 aCheckRect.Bottom() -= nDelta - (nDelta/2);
4735 aTextRect.Left() = rBox.m_aRect.Left();
4736 aTextRect.Top() = rBox.m_aRect.Top();
4737 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4738 aTextRect.Bottom() = rBox.m_aRect.Bottom();
4740 setLineColor( Color( COL_BLACK ) );
4741 setFillColor( Color( COL_TRANSPARENT ) );
4742 OStringBuffer aLW( 32 );
4743 aLW.append( "q " );
4744 m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
4745 aLW.append( " w " );
4746 writeBuffer( aLW.getStr(), aLW.getLength() );
4747 drawRectangle( aCheckRect );
4748 writeBuffer( " Q\n", 3 );
4749 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4750 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
4752 pop();
4754 OStringBuffer aDA( 256 );
4755 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4756 sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
4757 aDA.append( ' ' );
4758 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4759 aDA.append( " 0 Tf" );
4760 rBox.m_aDAString = aDA.makeStringAndClear();
4761 rBox.m_aMKDict = "/CA";
4762 rBox.m_aMKDictCAString = "8";
4763 rBox.m_aRect = aCheckRect;
4765 // create appearance streams
4766 sal_Char cMark = '8';
4767 sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)];
4768 nCharXOffset *= aCheckRect.GetHeight();
4769 nCharXOffset /= 2000;
4770 sal_Int32 nCharYOffset = 1000-
4771 (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative
4772 nCharYOffset *= aCheckRect.GetHeight();
4773 nCharYOffset /= 2000;
4775 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
4776 beginRedirect( pCheckStream, aCheckRect );
4777 aDA.append( "/Tx BMC\nq BT\n" );
4778 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4779 aDA.append( ' ' );
4780 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4781 aDA.append( ' ' );
4782 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
4783 aDA.append( " Tf\n" );
4784 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
4785 aDA.append( " " );
4786 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
4787 aDA.append( " Td (" );
4788 aDA.append( cMark );
4789 aDA.append( ") Tj\nET\nQ\nEMC\n" );
4790 writeBuffer( aDA.getStr(), aDA.getLength() );
4791 endRedirect();
4792 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
4794 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
4795 beginRedirect( pUncheckStream, aCheckRect );
4796 writeBuffer( "/Tx BMC\nEMC\n", 12 );
4797 endRedirect();
4798 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
4801 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget )
4803 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4805 // save graphics state
4806 push( sal::static_int_cast<sal_uInt16>(~0U) );
4808 if( rWidget.Background || rWidget.Border )
4810 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
4811 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4812 drawRectangle( rBox.m_aRect );
4815 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
4816 setFont( aFont );
4817 Size aFontSize = aFont.GetSize();
4818 if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
4819 aFontSize.Height() = rBox.m_aRect.GetHeight();
4820 sal_Int32 nDelta = aFontSize.Height()/10;
4821 if( nDelta < 1 )
4822 nDelta = 1;
4824 Rectangle aCheckRect, aTextRect;
4825 if( rWidget.ButtonIsLeft )
4827 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
4828 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4829 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
4830 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4832 // #i74206# handle small controls without text area
4833 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4835 aCheckRect.Right() -= nDelta;
4836 aCheckRect.Top() += nDelta/2;
4837 aCheckRect.Bottom() -= nDelta - (nDelta/2);
4840 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
4841 aTextRect.Top() = rBox.m_aRect.Top();
4842 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4843 aTextRect.Bottom() = rBox.m_aRect.Bottom();
4845 else
4847 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
4848 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4849 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
4850 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4852 // #i74206# handle small controls without text area
4853 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4855 aCheckRect.Left() += nDelta;
4856 aCheckRect.Top() += nDelta/2;
4857 aCheckRect.Bottom() -= nDelta - (nDelta/2);
4860 aTextRect.Left() = rBox.m_aRect.Left();
4861 aTextRect.Top() = rBox.m_aRect.Top();
4862 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4863 aTextRect.Bottom() = rBox.m_aRect.Bottom();
4865 setLineColor( Color( COL_BLACK ) );
4866 setFillColor( Color( COL_TRANSPARENT ) );
4867 OStringBuffer aLW( 32 );
4868 aLW.append( "q " );
4869 m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
4870 aLW.append( " w " );
4871 writeBuffer( aLW.getStr(), aLW.getLength() );
4872 drawEllipse( aCheckRect );
4873 writeBuffer( " Q\n", 3 );
4874 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4875 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
4877 pop();
4879 OStringBuffer aDA( 256 );
4880 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4881 sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
4882 aDA.append( ' ' );
4883 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4884 aDA.append( " 0 Tf" );
4885 rBox.m_aDAString = aDA.makeStringAndClear();
4886 //to encrypt this (el)
4887 rBox.m_aMKDict = "/CA";
4888 //after this assignement, to m_aMKDic cannot be added anything
4889 rBox.m_aMKDictCAString = "l";
4891 rBox.m_aRect = aCheckRect;
4893 // create appearance streams
4894 push( sal::static_int_cast<sal_uInt16>(~0U) );
4895 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
4897 beginRedirect( pCheckStream, aCheckRect );
4898 aDA.append( "/Tx BMC\nq BT\n" );
4899 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4900 aDA.append( ' ' );
4901 aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4902 aDA.append( ' ' );
4903 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
4904 aDA.append( " Tf\n0 0 Td\nET\nQ\n" );
4905 writeBuffer( aDA.getStr(), aDA.getLength() );
4906 setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4907 setLineColor( Color( COL_TRANSPARENT ) );
4908 aCheckRect.Left() += 3*nDelta;
4909 aCheckRect.Top() += 3*nDelta;
4910 aCheckRect.Bottom() -= 3*nDelta;
4911 aCheckRect.Right() -= 3*nDelta;
4912 drawEllipse( aCheckRect );
4913 writeBuffer( "\nEMC\n", 5 );
4914 endRedirect();
4916 pop();
4917 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
4919 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
4920 beginRedirect( pUncheckStream, aCheckRect );
4921 writeBuffer( "/Tx BMC\nEMC\n", 12 );
4922 endRedirect();
4923 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
4926 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
4929 // TODO: check and insert default streams
4930 rtl::OString aStandardAppearance;
4931 switch( rWidget.m_eType )
4933 case PDFWriter::CheckBox:
4934 aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
4935 break;
4936 default:
4937 break;
4940 if( rWidget.m_aAppearances.size() )
4942 rAnnotDict.append( "/AP<<\n" );
4943 for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it )
4945 rAnnotDict.append( "/" );
4946 rAnnotDict.append( dict_it->first );
4947 bool bUseSubDict = (dict_it->second.size() > 1);
4948 rAnnotDict.append( bUseSubDict ? "<<" : " " );
4950 for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin();
4951 stream_it != dict_it->second.end(); ++stream_it )
4953 SvMemoryStream* pApppearanceStream = stream_it->second;
4954 dict_it->second[ stream_it->first ] = NULL;
4956 bool bDeflate = compressStream( pApppearanceStream );
4958 pApppearanceStream->Seek( STREAM_SEEK_TO_END );
4959 sal_Int64 nStreamLen = pApppearanceStream->Tell();
4960 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
4961 sal_Int32 nObject = createObject();
4962 CHECK_RETURN( updateObject( nObject ) );
4963 #if OSL_DEBUG_LEVEL > 1
4965 OStringBuffer aLine( " PDFWriterImpl::emitAppearances" );
4966 emitComment( aLine.getStr() );
4968 #endif
4969 OStringBuffer aLine;
4970 aLine.append( nObject );
4972 aLine.append( " 0 obj\n"
4973 "<</Type/XObject\n"
4974 "/Subtype/Form\n"
4975 "/BBox[0 0 " );
4976 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
4977 aLine.append( " " );
4978 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
4979 aLine.append( "]\n"
4980 "/Resources " );
4981 aLine.append( getResourceDictObj() );
4982 aLine.append( " 0 R\n"
4983 "/Length " );
4984 aLine.append( nStreamLen );
4985 aLine.append( "\n" );
4986 if( bDeflate )
4987 aLine.append( "/Filter/FlateDecode\n" );
4988 aLine.append( ">>\nstream\n" );
4989 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4990 checkAndEnableStreamEncryption( nObject );
4991 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
4992 disableStreamEncryption();
4993 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
4995 if( bUseSubDict )
4997 rAnnotDict.append( " /" );
4998 rAnnotDict.append( stream_it->first );
4999 rAnnotDict.append( " " );
5001 rAnnotDict.append( nObject );
5002 rAnnotDict.append( " 0 R" );
5004 delete pApppearanceStream;
5007 rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
5009 rAnnotDict.append( ">>\n" );
5010 if( aStandardAppearance.getLength() )
5012 rAnnotDict.append( "/AS /" );
5013 rAnnotDict.append( aStandardAppearance );
5014 rAnnotDict.append( "\n" );
5018 return true;
5021 bool PDFWriterImpl::emitWidgetAnnotations()
5023 ensureUniqueRadioOnValues();
5025 int nAnnots = m_aWidgets.size();
5026 for( int a = 0; a < nAnnots; a++ )
5028 PDFWidget& rWidget = m_aWidgets[a];
5030 OStringBuffer aLine( 1024 );
5031 OStringBuffer aValue( 256 );
5032 aLine.append( rWidget.m_nObject );
5033 aLine.append( " 0 obj\n"
5034 "<<" );
5035 // emit widget annotation only for terminal fields
5036 if( rWidget.m_aKids.empty() )
5038 aLine.append( "/Type/Annot/Subtype/Widget/F 4\n"
5039 "/Rect[" );
5040 appendFixedInt( rWidget.m_aRect.Left()-1, aLine );
5041 aLine.append( ' ' );
5042 appendFixedInt( rWidget.m_aRect.Top()+1, aLine );
5043 aLine.append( ' ' );
5044 appendFixedInt( rWidget.m_aRect.Right()+1, aLine );
5045 aLine.append( ' ' );
5046 appendFixedInt( rWidget.m_aRect.Bottom()-1, aLine );
5047 aLine.append( "]\n" );
5049 aLine.append( "/FT/" );
5050 switch( rWidget.m_eType )
5052 case PDFWriter::RadioButton:
5053 case PDFWriter::CheckBox:
5054 // for radio buttons only the RadioButton field, not the
5055 // CheckBox children should have a value, else acrobat reader
5056 // does not always check the right button
5057 // of course real check boxes (not belonging to a readio group)
5058 // need their values, too
5059 if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
5061 aValue.append( "/" );
5062 // check for radio group with all buttons unpressed
5063 if( rWidget.m_aValue.getLength() == 0 )
5064 aValue.append( "Off" );
5065 else
5066 appendName( rWidget.m_aValue, aValue );
5068 case PDFWriter::PushButton:
5069 aLine.append( "Btn" );
5070 break;
5071 case PDFWriter::ListBox:
5072 if( rWidget.m_nFlags & 0x200000 ) // multiselect
5074 aValue.append( "[" );
5075 for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
5077 sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
5078 if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
5079 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
5081 aValue.append( "]" );
5083 else if( rWidget.m_aSelectedEntries.size() > 0 &&
5084 rWidget.m_aSelectedEntries[0] >= 0 &&
5085 rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
5087 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
5089 else
5090 appendUnicodeTextStringEncrypt( rtl::OUString(), rWidget.m_nObject, aValue );
5091 aLine.append( "Ch" );
5092 break;
5093 case PDFWriter::ComboBox:
5094 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5095 aLine.append( "Ch" );
5096 break;
5097 case PDFWriter::Edit:
5098 aLine.append( "Tx" );
5099 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5100 break;
5102 aLine.append( "\n" );
5103 aLine.append( "/P " );
5104 aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
5105 aLine.append( " 0 R\n" );
5107 if( rWidget.m_nParent )
5109 aLine.append( "/Parent " );
5110 aLine.append( rWidget.m_nParent );
5111 aLine.append( " 0 R\n" );
5113 if( rWidget.m_aKids.size() )
5115 aLine.append( "/Kids[" );
5116 for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ )
5118 aLine.append( rWidget.m_aKids[i] );
5119 aLine.append( " 0 R" );
5120 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5122 aLine.append( "]\n" );
5124 if( rWidget.m_aName.getLength() )
5126 aLine.append( "/T" );
5127 appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
5128 aLine.append( "\n" );
5130 if( m_aContext.Version > PDFWriter::PDF_1_2 )
5132 // the alternate field name should be unicode able since it is
5133 // supposed to be used in UI
5134 aLine.append( "/TU" );
5135 appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
5136 aLine.append( "\n" );
5139 if( rWidget.m_nFlags )
5141 aLine.append( "/Ff " );
5142 aLine.append( rWidget.m_nFlags );
5143 aLine.append( "\n" );
5145 if( aValue.getLength() )
5147 OString aVal = aValue.makeStringAndClear();
5148 aLine.append( "/V " );
5149 aLine.append( aVal );
5150 aLine.append( "\n"
5151 "/DV " );
5152 aLine.append( aVal );
5153 aLine.append( "\n" );
5155 if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
5157 sal_Int32 nTI = -1;
5158 aLine.append( "/Opt[\n" );
5159 sal_Int32 i = 0;
5160 for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i )
5162 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine );
5163 aLine.append( "\n" );
5164 if( *it == rWidget.m_aValue )
5165 nTI = i;
5167 aLine.append( "]\n" );
5168 if( nTI > 0 )
5170 aLine.append( "/TI " );
5171 aLine.append( nTI );
5172 aLine.append( "\n" );
5173 if( rWidget.m_nFlags & 0x200000 ) // Multiselect
5175 aLine.append( "/I [" );
5176 aLine.append( nTI );
5177 aLine.append( "]\n" );
5181 if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
5183 aLine.append( "/MaxLen " );
5184 aLine.append( rWidget.m_nMaxLen );
5185 aLine.append( "\n" );
5187 if( rWidget.m_eType == PDFWriter::PushButton )
5189 if(!m_bIsPDF_A1)
5191 OStringBuffer aDest;
5192 if( appendDest( rWidget.m_nDest, aDest ) )
5194 aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
5195 aLine.append( aDest.makeStringAndClear() );
5196 aLine.append( ">>>>\n" );
5198 else if( rWidget.m_aListEntries.empty() )
5200 // create a reset form action
5201 aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
5203 else if( rWidget.m_bSubmit )
5205 // create a submit form action
5206 aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
5207 appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine );
5208 aLine.append( "/Flags " );
5210 sal_Int32 nFlags = 0;
5211 switch( m_aContext.SubmitFormat )
5213 case PDFWriter::HTML:
5214 nFlags |= 4;
5215 break;
5216 case PDFWriter::XML:
5217 if( m_aContext.Version > PDFWriter::PDF_1_3 )
5218 nFlags |= 32;
5219 break;
5220 case PDFWriter::PDF:
5221 if( m_aContext.Version > PDFWriter::PDF_1_3 )
5222 nFlags |= 256;
5223 break;
5224 case PDFWriter::FDF:
5225 default:
5226 break;
5228 if( rWidget.m_bSubmitGet )
5229 nFlags |= 8;
5230 aLine.append( nFlags );
5231 aLine.append( ">>>>\n" );
5233 else
5235 // create a URI action
5236 aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
5237 aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
5238 aLine.append( ")>>>>\n" );
5241 else
5242 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA );
5244 if( rWidget.m_aDAString.getLength() )
5246 aLine.append( "/DR<</Font<<" );
5247 appendBuiltinFontsToDict( aLine );
5248 aLine.append( ">>>>\n" );
5249 aLine.append( "/DA" );
5250 appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
5251 aLine.append( "\n" );
5252 if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER )
5253 aLine.append( "/Q 1\n" );
5254 else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT )
5255 aLine.append( "/Q 2\n" );
5257 // appearance charactristics for terminal fields
5258 // which are supposed to have an appearance constructed
5259 // by the viewer application
5260 if( rWidget.m_aMKDict.getLength() )
5262 aLine.append( "/MK<<" );
5263 aLine.append( rWidget.m_aMKDict );
5264 //add the CA string, encrypting it
5265 appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
5266 aLine.append( ">>\n" );
5269 CHECK_RETURN( emitAppearances( rWidget, aLine ) );
5271 aLine.append( ">>\n"
5272 "endobj\n\n" );
5273 CHECK_RETURN( updateObject( rWidget.m_nObject ) );
5274 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5276 return true;
5279 bool PDFWriterImpl::emitAnnotations()
5281 if( m_aPages.size() < 1 )
5282 return false;
5284 CHECK_RETURN( emitLinkAnnotations() );
5286 CHECK_RETURN( emitNoteAnnotations() );
5288 CHECK_RETURN( emitWidgetAnnotations() );
5290 return true;
5293 #undef CHECK_RETURN
5294 #define CHECK_RETURN( x ) if( !x ) return false
5296 bool PDFWriterImpl::emitCatalog()
5298 // build page tree
5299 // currently there is only one node that contains all leaves
5301 // first create a page tree node id
5302 sal_Int32 nTreeNode = createObject();
5304 // emit global resource dictionary (page emit needs it)
5305 CHECK_RETURN( emitResources() );
5307 // emit all pages
5308 for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it )
5309 if( ! it->emit( nTreeNode ) )
5310 return false;
5312 sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
5314 sal_Int32 nOutlineDict = emitOutline();
5316 //emit Output intent i59651
5317 sal_Int32 nOutputIntentObject = emitOutputIntent();
5319 //emit metadata
5320 sal_Int32 nMetadataObject = emitDocumentMetadata();
5322 sal_Int32 nStructureDict = 0;
5323 if(m_aStructure.size() > 1)
5325 ///check if dummy structure containers are needed
5326 addInternalStructureContainer(m_aStructure[0]);
5327 nStructureDict = m_aStructure[0].m_nObject = createObject();
5328 emitStructure( m_aStructure[ 0 ] );
5331 // adjust tree node file offset
5332 if( ! updateObject( nTreeNode ) )
5333 return false;
5335 // emit tree node
5336 OStringBuffer aLine( 2048 );
5337 aLine.append( nTreeNode );
5338 aLine.append( " 0 obj\n" );
5339 aLine.append( "<</Type/Pages\n" );
5340 aLine.append( "/Resources " );
5341 aLine.append( getResourceDictObj() );
5342 aLine.append( " 0 R\n" );
5344 switch( m_eInheritedOrientation )
5346 case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
5347 case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
5349 case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant
5350 case PDFWriter::Portrait:
5351 default:
5352 break;
5354 sal_Int32 nMediaBoxWidth = 0;
5355 sal_Int32 nMediaBoxHeight = 0;
5356 if( m_aPages.empty() ) // sanity check, this should not happen
5358 nMediaBoxWidth = m_nInheritedPageWidth;
5359 nMediaBoxHeight = m_nInheritedPageHeight;
5361 else
5363 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter )
5365 if( iter->m_nPageWidth > nMediaBoxWidth )
5366 nMediaBoxWidth = iter->m_nPageWidth;
5367 if( iter->m_nPageHeight > nMediaBoxHeight )
5368 nMediaBoxHeight = iter->m_nPageHeight;
5371 aLine.append( "/MediaBox[ 0 0 " );
5372 aLine.append( nMediaBoxWidth );
5373 aLine.append( ' ' );
5374 aLine.append( nMediaBoxHeight );
5375 aLine.append( " ]\n"
5376 "/Kids[ " );
5377 unsigned int i = 0;
5378 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ )
5380 aLine.append( iter->m_nPageObject );
5381 aLine.append( " 0 R" );
5382 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5384 aLine.append( "]\n"
5385 "/Count " );
5386 aLine.append( (sal_Int32)m_aPages.size() );
5387 aLine.append( ">>\n"
5388 "endobj\n\n" );
5389 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5391 // emit annotation objects
5392 CHECK_RETURN( emitAnnotations() );
5394 // emit Catalog
5395 m_nCatalogObject = createObject();
5396 if( ! updateObject( m_nCatalogObject ) )
5397 return false;
5398 aLine.setLength( 0 );
5399 aLine.append( m_nCatalogObject );
5400 aLine.append( " 0 obj\n"
5401 "<</Type/Catalog/Pages " );
5402 aLine.append( nTreeNode );
5403 aLine.append( " 0 R\n" );
5404 //--->i56629
5405 //check if there are named destinations to emit (root must be inside the catalog)
5406 if( nNamedDestinationsDictionary )
5408 aLine.append("/Dests ");
5409 aLine.append( nNamedDestinationsDictionary );
5410 aLine.append( " 0 R\n" );
5412 //<----
5413 if( m_aContext.PageLayout != PDFWriter::DefaultLayout )
5414 switch( m_aContext.PageLayout )
5416 default :
5417 case PDFWriter::SinglePage :
5418 aLine.append( "/PageLayout/SinglePage\n" );
5419 break;
5420 case PDFWriter::Continuous :
5421 aLine.append( "/PageLayout/OneColumn\n" );
5422 break;
5423 case PDFWriter::ContinuousFacing :
5424 //the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5425 aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5426 break;
5428 if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode )
5429 switch( m_aContext.PDFDocumentMode )
5431 default :
5432 aLine.append( "/PageMode/UseNone\n" );
5433 break;
5434 case PDFWriter::UseOutlines :
5435 aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5436 break;
5437 case PDFWriter::UseThumbs :
5438 aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5439 break;
5441 else if( m_aContext.OpenInFullScreenMode )
5442 aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5444 OStringBuffer aInitPageRef;
5445 if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() )
5447 aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5448 aInitPageRef.append( " 0 R" );
5450 else
5451 aInitPageRef.append( "0" );
5452 switch( m_aContext.PDFDocumentAction )
5454 case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default
5455 default:
5456 if( aInitPageRef.getLength() > 1 )
5458 aLine.append( "/OpenAction[" );
5459 aLine.append( aInitPageRef );
5460 aLine.append( " /XYZ null null 0]\n" );
5462 break;
5463 case PDFWriter::FitInWindow :
5464 aLine.append( "/OpenAction[" );
5465 aLine.append( aInitPageRef );
5466 aLine.append( " /Fit]\n" ); //Open fit page
5467 break;
5468 case PDFWriter::FitWidth :
5469 aLine.append( "/OpenAction[" );
5470 aLine.append( aInitPageRef );
5471 aLine.append( " /FitH " );
5472 aLine.append( m_nInheritedPageHeight );//Open fit width
5473 aLine.append( "]\n" );
5474 break;
5475 case PDFWriter::FitVisible :
5476 aLine.append( "/OpenAction[" );
5477 aLine.append( aInitPageRef );
5478 aLine.append( " /FitBH " );
5479 aLine.append( m_nInheritedPageHeight );//Open fit visible
5480 aLine.append( "]\n" );
5481 break;
5482 case PDFWriter::ActionZoom :
5483 aLine.append( "/OpenAction[" );
5484 aLine.append( aInitPageRef );
5485 aLine.append( " /XYZ null null " );
5486 if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5487 aLine.append( (double)m_aContext.Zoom/100.0 );
5488 else
5489 aLine.append( "0" );
5490 aLine.append( "]\n" );
5491 break;
5493 // viewer preferences, if we had some, then emit
5494 if( m_aContext.HideViewerToolbar ||
5495 ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aDocInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) ||
5496 m_aContext.HideViewerMenubar ||
5497 m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
5498 m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
5499 m_aContext.OpenInFullScreenMode )
5501 aLine.append( "/ViewerPreferences<<" );
5502 if( m_aContext.HideViewerToolbar )
5503 aLine.append( "/HideToolbar true\n" );
5504 if( m_aContext.HideViewerMenubar )
5505 aLine.append( "/HideMenubar true\n" );
5506 if( m_aContext.HideViewerWindowControls )
5507 aLine.append( "/HideWindowUI true\n" );
5508 if( m_aContext.FitWindow )
5509 aLine.append( "/FitWindow true\n" );
5510 if( m_aContext.CenterWindow )
5511 aLine.append( "/CenterWindow true\n" );
5512 if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aDocInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle )
5513 aLine.append( "/DisplayDocTitle true\n" );
5514 if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing )
5515 aLine.append( "/Direction/R2L\n" );
5516 if( m_aContext.OpenInFullScreenMode )
5517 switch( m_aContext.PDFDocumentMode )
5519 default :
5520 case PDFWriter::ModeDefault :
5521 aLine.append( "/NonFullScreenPageMode/UseNone\n" );
5522 break;
5523 case PDFWriter::UseOutlines :
5524 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
5525 break;
5526 case PDFWriter::UseThumbs :
5527 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
5528 break;
5530 aLine.append( ">>\n" );
5533 if( nOutlineDict )
5535 aLine.append( "/Outlines " );
5536 aLine.append( nOutlineDict );
5537 aLine.append( " 0 R\n" );
5539 if( nStructureDict )
5541 aLine.append( "/StructTreeRoot " );
5542 aLine.append( nStructureDict );
5543 aLine.append( " 0 R\n" );
5545 if( m_aContext.DocumentLocale.Language.getLength() > 0 )
5547 OUStringBuffer aLocBuf( 16 );
5548 aLocBuf.append( m_aContext.DocumentLocale.Language.toAsciiLowerCase() );
5549 if( m_aContext.DocumentLocale.Country.getLength() > 0 )
5551 aLocBuf.append( sal_Unicode('-') );
5552 aLocBuf.append( m_aContext.DocumentLocale.Country );
5554 aLine.append( "/Lang" );
5555 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
5556 aLine.append( "\n" );
5558 if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 )
5560 aLine.append( "/MarkInfo<</Marked true>>\n" );
5562 if( m_aWidgets.size() > 0 )
5564 aLine.append( "/AcroForm<</Fields[\n" );
5565 int nWidgets = m_aWidgets.size();
5566 int nOut = 0;
5567 for( int j = 0; j < nWidgets; j++ )
5569 // output only root fields
5570 if( m_aWidgets[j].m_nParent < 1 )
5572 aLine.append( m_aWidgets[j].m_nObject );
5573 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
5576 aLine.append( "\n]/DR " );
5577 aLine.append( getResourceDictObj() );
5578 aLine.append( " 0 R" );
5579 if( m_bIsPDF_A1 )
5580 aLine.append( ">>\n" );
5581 else
5582 aLine.append( "/NeedAppearances true>>\n" );
5584 //--->i59651
5585 //check if there is a Metadata object
5586 if( nOutputIntentObject )
5588 aLine.append("/OutputIntents[");
5589 aLine.append( nOutputIntentObject );
5590 aLine.append( " 0 R]" );
5592 if( nMetadataObject )
5594 aLine.append("/Metadata ");
5595 aLine.append( nMetadataObject );
5596 aLine.append( " 0 R" );
5598 //<----
5599 aLine.append( ">>\n"
5600 "endobj\n\n" );
5601 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5603 return true;
5606 sal_Int32 PDFWriterImpl::emitInfoDict( )
5608 sal_Int32 nObject = createObject();
5610 if( updateObject( nObject ) )
5612 OStringBuffer aLine( 1024 );
5613 aLine.append( nObject );
5614 aLine.append( " 0 obj\n"
5615 "<<" );
5616 if( m_aDocInfo.Title.Len() )
5618 aLine.append( "/Title" );
5619 appendUnicodeTextStringEncrypt( m_aDocInfo.Title, nObject, aLine );
5620 aLine.append( "\n" );
5622 if( m_aDocInfo.Author.Len() )
5624 aLine.append( "/Author" );
5625 appendUnicodeTextStringEncrypt( m_aDocInfo.Author, nObject, aLine );
5626 aLine.append( "\n" );
5628 if( m_aDocInfo.Subject.Len() )
5630 aLine.append( "/Subject" );
5631 appendUnicodeTextStringEncrypt( m_aDocInfo.Subject, nObject, aLine );
5632 aLine.append( "\n" );
5634 if( m_aDocInfo.Keywords.Len() )
5636 aLine.append( "/Keywords" );
5637 appendUnicodeTextStringEncrypt( m_aDocInfo.Keywords, nObject, aLine );
5638 aLine.append( "\n" );
5640 if( m_aDocInfo.Creator.Len() )
5642 aLine.append( "/Creator" );
5643 appendUnicodeTextStringEncrypt( m_aDocInfo.Creator, nObject, aLine );
5644 aLine.append( "\n" );
5646 if( m_aDocInfo.Producer.Len() )
5648 aLine.append( "/Producer" );
5649 appendUnicodeTextStringEncrypt( m_aDocInfo.Producer, nObject, aLine );
5650 aLine.append( "\n" );
5653 aLine.append( "/CreationDate" );
5654 appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine );
5655 aLine.append( ">>\nendobj\n\n" );
5656 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
5657 nObject = 0;
5659 else
5660 nObject = 0;
5662 return nObject;
5665 //--->i56629
5666 // Part of this function may be shared with method appendDest.
5668 sal_Int32 PDFWriterImpl::emitNamedDestinations()
5670 sal_Int32 nCount = m_aNamedDests.size();
5671 if( nCount <= 0 )
5672 return 0;//define internal error
5674 //get the object number for all the destinations
5675 sal_Int32 nObject = createObject();
5677 if( updateObject( nObject ) )
5679 //emit the dictionary
5680 OStringBuffer aLine( 1024 );
5681 aLine.append( nObject );
5682 aLine.append( " 0 obj\n"
5683 "<<" );
5685 sal_Int32 nDestID;
5686 for( nDestID = 0; nDestID < nCount; nDestID++ )
5688 const PDFNamedDest& rDest = m_aNamedDests[ nDestID ];
5689 // In order to correctly function both under an Internet browser and
5690 // directly with a reader (provided the reader has the feature) we
5691 // need to set the name of the destination the same way it will be encoded
5692 // in an Internet link
5693 INetURLObject aLocalURL(
5694 OUString( RTL_CONSTASCII_USTRINGPARAM( "http://ahost.ax" ) ) ); //dummy location, won't be used
5695 aLocalURL.SetMark( rDest.m_aDestName );
5697 const rtl::OUString aName = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as
5698 // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
5699 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
5701 aLine.append( '/' );
5702 appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
5703 aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
5704 //maps the preceeding character properly
5705 aLine.append( rDestPage.m_nPageObject );
5706 aLine.append( " 0 R" );
5708 switch( rDest.m_eType )
5710 case PDFWriter::XYZ:
5711 default:
5712 aLine.append( "/XYZ " );
5713 appendFixedInt( rDest.m_aRect.Left(), aLine );
5714 aLine.append( ' ' );
5715 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5716 aLine.append( " 0" );
5717 break;
5718 case PDFWriter::Fit:
5719 aLine.append( "/Fit" );
5720 break;
5721 case PDFWriter::FitRectangle:
5722 aLine.append( "/FitR " );
5723 appendFixedInt( rDest.m_aRect.Left(), aLine );
5724 aLine.append( ' ' );
5725 appendFixedInt( rDest.m_aRect.Top(), aLine );
5726 aLine.append( ' ' );
5727 appendFixedInt( rDest.m_aRect.Right(), aLine );
5728 aLine.append( ' ' );
5729 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5730 break;
5731 case PDFWriter::FitHorizontal:
5732 aLine.append( "/FitH " );
5733 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5734 break;
5735 case PDFWriter::FitVertical:
5736 aLine.append( "/FitV " );
5737 appendFixedInt( rDest.m_aRect.Left(), aLine );
5738 break;
5739 case PDFWriter::FitPageBoundingBox:
5740 aLine.append( "/FitB" );
5741 break;
5742 case PDFWriter::FitPageBoundingBoxHorizontal:
5743 aLine.append( "/FitBH " );
5744 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
5745 break;
5746 case PDFWriter::FitPageBoundingBoxVertical:
5747 aLine.append( "/FitBV " );
5748 appendFixedInt( rDest.m_aRect.Left(), aLine );
5749 break;
5751 aLine.append( "]\n" );
5753 //close
5755 aLine.append( ">>\nendobj\n\n" );
5756 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
5757 nObject = 0;
5759 else
5760 nObject = 0;
5762 return nObject;
5764 //<--- i56629
5766 //--->i59651
5767 // emits the output intent dictionary
5769 sal_Int32 PDFWriterImpl::emitOutputIntent()
5771 if( !m_bIsPDF_A1 )
5772 return 0;
5774 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
5776 OStringBuffer aLine( 1024 );
5777 sal_Int32 nICCObject = createObject();
5778 sal_Int32 nStreamLengthObject = createObject();
5780 aLine.append( nICCObject );
5781 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
5782 aLine.append( " 0 obj\n<</N 3/Length " );
5783 aLine.append( nStreamLengthObject );
5784 aLine.append( " 0 R" );
5785 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
5786 aLine.append( "/Filter/FlateDecode" );
5787 #endif
5788 aLine.append( ">>\nstream\n" );
5789 CHECK_RETURN( updateObject( nICCObject ) );
5790 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5791 //get file position
5792 sal_uInt64 nBeginStreamPos = 0;
5793 osl_getFilePos( m_aFile, &nBeginStreamPos );
5794 beginCompression();
5795 checkAndEnableStreamEncryption( nICCObject );
5796 sal_Int32 nStreamSize = writeBuffer( nsRGB_ICC_profile, (sal_Int32) sizeof( nsRGB_ICC_profile ) );
5797 disableStreamEncryption();
5798 endCompression();
5799 sal_uInt64 nEndStreamPos = 0;
5800 osl_getFilePos( m_aFile, &nEndStreamPos );
5802 if( nStreamSize == 0 )
5803 return 0;
5804 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
5805 return 0 ;
5806 aLine.setLength( 0 );
5808 //emit the stream length object
5809 CHECK_RETURN( updateObject( nStreamLengthObject ) );
5810 aLine.setLength( 0 );
5811 aLine.append( nStreamLengthObject );
5812 aLine.append( " 0 obj\n" );
5813 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
5814 aLine.append( "\nendobj\n\n" );
5815 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5816 aLine.setLength( 0 );
5818 //emit the OutputIntent dictionary
5819 sal_Int32 nOIObject = createObject();
5820 CHECK_RETURN( updateObject( nOIObject ) );
5821 aLine.append( nOIObject );
5822 aLine.append( " 0 obj\n"
5823 "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
5825 rtl::OUString aComment( RTL_CONSTASCII_USTRINGPARAM( "sRGB IEC61966-2.1" ) );
5826 appendLiteralStringEncrypt( aComment ,nOIObject, aLine );
5827 aLine.append("/DestOutputProfile ");
5828 aLine.append( nICCObject );
5829 aLine.append( " 0 R>>\nendobj\n\n" );;
5830 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5832 return nOIObject;
5835 // formats the string for the XML stream
5836 static void escapeStringXML( const rtl::OUString& rStr, rtl::OUString &rValue)
5838 const sal_Unicode* pUni = rStr.getStr();
5839 int nLen = rStr.getLength();
5840 for( ; nLen; nLen--, pUni++ )
5842 switch( *pUni )
5844 case sal_Unicode('&'):
5845 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&amp;" ) );
5846 break;
5847 case sal_Unicode('<'):
5848 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&lt;" ) );
5849 break;
5850 case sal_Unicode('>'):
5851 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&gt;" ) );
5852 break;
5853 case sal_Unicode('\''):
5854 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&apos;" ) );
5855 break;
5856 case sal_Unicode('"'):
5857 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&quot;" ) );
5858 break;
5859 default:
5860 rValue += rtl::OUString( *pUni );
5861 break;
5866 // emits the document metadata
5868 sal_Int32 PDFWriterImpl::emitDocumentMetadata()
5870 if( !m_bIsPDF_A1 )
5871 return 0;
5873 //get the object number for all the destinations
5874 sal_Int32 nObject = createObject();
5876 if( updateObject( nObject ) )
5878 // the following string are written in UTF-8 unicode
5879 OStringBuffer aMetadataStream( 8192 );
5881 aMetadataStream.append( "<?xpacket begin=\"" );
5882 // this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used
5883 // as a byte-order marker.
5884 aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) );
5885 aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
5886 aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
5887 aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
5888 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
5889 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
5890 aMetadataStream.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
5891 aMetadataStream.append( " <pdfaid:part>1</pdfaid:part>\n" );
5892 aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" );
5893 aMetadataStream.append( " </rdf:Description>\n" );
5894 //... Dublin Core properties go here
5895 if( m_aDocInfo.Title.Len() ||
5896 m_aDocInfo.Author.Len() ||
5897 m_aDocInfo.Subject.Len() )
5899 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
5900 aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
5901 if( m_aDocInfo.Title.Len() )
5903 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
5904 aMetadataStream.append( " <dc:title>\n" );
5905 aMetadataStream.append( " <rdf:Alt>\n" );
5906 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
5907 rtl::OUString aTitle;
5908 escapeStringXML( m_aDocInfo.Title, aTitle );
5909 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) );
5910 aMetadataStream.append( "</rdf:li>\n" );
5911 aMetadataStream.append( " </rdf:Alt>\n" );
5912 aMetadataStream.append( " </dc:title>\n" );
5914 if( m_aDocInfo.Author.Len() )
5916 aMetadataStream.append( " <dc:creator>\n" );
5917 aMetadataStream.append( " <rdf:Seq>\n" );
5918 aMetadataStream.append( " <rdf:li>" );
5919 rtl::OUString aAuthor;
5920 escapeStringXML( m_aDocInfo.Author, aAuthor );
5921 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) );
5922 aMetadataStream.append( "</rdf:li>\n" );
5923 aMetadataStream.append( " </rdf:Seq>\n" );
5924 aMetadataStream.append( " </dc:creator>\n" );
5926 if( m_aDocInfo.Subject.Len() )
5928 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
5929 aMetadataStream.append( " <dc:description>\n" );
5930 aMetadataStream.append( " <rdf:Alt>\n" );
5931 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
5932 rtl::OUString aSubject;
5933 escapeStringXML( m_aDocInfo.Subject, aSubject );
5934 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) );
5935 aMetadataStream.append( "</rdf:li>\n" );
5936 aMetadataStream.append( " </rdf:Alt>\n" );
5937 aMetadataStream.append( " </dc:description>\n" );
5939 aMetadataStream.append( " </rdf:Description>\n" );
5942 //... PDF properties go here
5943 if( m_aDocInfo.Producer.Len() ||
5944 m_aDocInfo.Keywords.Len() )
5946 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
5947 aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
5948 if( m_aDocInfo.Producer.Len() )
5950 aMetadataStream.append( " <pdf:Producer>" );
5951 rtl::OUString aProducer;
5952 escapeStringXML( m_aDocInfo.Producer, aProducer );
5953 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) );
5954 aMetadataStream.append( "</pdf:Producer>\n" );
5956 if( m_aDocInfo.Keywords.Len() )
5958 aMetadataStream.append( " <pdf:Keywords>" );
5959 rtl::OUString aKeywords;
5960 escapeStringXML( m_aDocInfo.Keywords, aKeywords );
5961 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) );
5962 aMetadataStream.append( "</pdf:Keywords>\n" );
5964 aMetadataStream.append( " </rdf:Description>\n" );
5967 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
5968 aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
5969 if( m_aDocInfo.Creator.Len() )
5971 aMetadataStream.append( " <xmp:CreatorTool>" );
5972 rtl::OUString aCreator;
5973 escapeStringXML( m_aDocInfo.Creator, aCreator );
5974 aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) );
5975 aMetadataStream.append( "</xmp:CreatorTool>\n" );
5977 //creation date
5978 aMetadataStream.append( " <xmp:CreateDate>" );
5979 aMetadataStream.append( m_aCreationMetaDateString );
5980 aMetadataStream.append( "</xmp:CreateDate>\n" );
5982 aMetadataStream.append( " </rdf:Description>\n" );
5983 aMetadataStream.append( " </rdf:RDF>\n" );
5984 aMetadataStream.append( "</x:xmpmeta>\n" );
5986 //add the padding
5987 for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ )
5989 aMetadataStream.append( " " );
5990 if( nSpaces % 100 == 0 )
5991 aMetadataStream.append( "\n" );
5994 aMetadataStream.append( "<?xpacket end=\"w\"?>\n" );
5996 OStringBuffer aMetadataObj( 1024 );
5998 aMetadataObj.append( nObject );
5999 aMetadataObj.append( " 0 obj\n" );
6001 aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
6003 aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() );
6004 aMetadataObj.append( ">>\nstream\n" );
6005 CHECK_RETURN( writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) );
6006 //emit the stream
6007 CHECK_RETURN( writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) );
6009 aMetadataObj.setLength( 0 );
6010 aMetadataObj.append( "\nendstream\nendobj\n\n" );
6011 if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
6012 nObject = 0;
6014 else
6015 nObject = 0;
6017 return nObject;
6019 //<---i59651
6021 bool PDFWriterImpl::emitTrailer()
6023 // emit doc info
6024 OString aInfoValuesOut;
6025 sal_Int32 nDocInfoObject = emitInfoDict( );
6027 sal_Int32 nSecObject = 0;
6029 if( m_aContext.Encrypt == true )
6031 //emit the security information
6032 //must be emitted as indirect dictionary object, since
6033 //Acrobat Reader 5 works only with this kind of implementation
6034 nSecObject = createObject();
6036 if( updateObject( nSecObject ) )
6038 OStringBuffer aLineS( 1024 );
6039 aLineS.append( nSecObject );
6040 aLineS.append( " 0 obj\n"
6041 "<</Filter/Standard/V " );
6042 // check the version
6043 if( m_aContext.Security128bit == true )
6044 aLineS.append( "2/Length 128/R 3" );
6045 else
6046 aLineS.append( "1/R 2" );
6048 // emit the owner password, must not be encrypted
6049 aLineS.append( "/O(" );
6050 appendLiteralString( (const sal_Char*)m_nEncryptedOwnerPassword, 32, aLineS );
6051 aLineS.append( ")/U(" );
6052 appendLiteralString( (const sal_Char*)m_nEncryptedUserPassword, 32, aLineS );
6053 aLineS.append( ")/P " );// the permission set
6054 aLineS.append( m_nAccessPermissions );
6055 aLineS.append( ">>\nendobj\n\n" );
6056 if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) )
6057 nSecObject = 0;
6059 else
6060 nSecObject = 0;
6062 // emit xref table
6063 // remember start
6064 sal_uInt64 nXRefOffset = 0;
6065 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) );
6066 CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
6068 sal_Int32 nObjects = m_aObjects.size();
6069 OStringBuffer aLine;
6070 aLine.append( "0 " );
6071 aLine.append( (sal_Int32)(nObjects+1) );
6072 aLine.append( "\n" );
6073 aLine.append( "0000000000 65535 f \n" );
6074 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6076 for( sal_Int32 i = 0; i < nObjects; i++ )
6078 aLine.setLength( 0 );
6079 OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] );
6080 for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
6081 aLine.append( '0' );
6082 aLine.append( aOffset );
6083 aLine.append( " 00000 n \n" );
6084 DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" );
6085 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6088 // prepare document checksum
6089 OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
6090 if( m_aDocDigest )
6092 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
6093 rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) );
6094 for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ )
6095 appendHex( nMD5Sum[i], aDocChecksum );
6097 // document id set in setDocInfo method
6098 // emit trailer
6099 aLine.setLength( 0 );
6100 aLine.append( "trailer\n"
6101 "<</Size " );
6102 aLine.append( (sal_Int32)(nObjects+1) );
6103 aLine.append( "/Root " );
6104 aLine.append( m_nCatalogObject );
6105 aLine.append( " 0 R\n" );
6106 if( nSecObject |= 0 )
6108 aLine.append( "/Encrypt ");
6109 aLine.append( nSecObject );
6110 aLine.append( " 0 R\n" );
6112 if( nDocInfoObject )
6114 aLine.append( "/Info " );
6115 aLine.append( nDocInfoObject );
6116 aLine.append( " 0 R\n" );
6118 if( m_aDocID.getLength() )
6120 aLine.append( "/ID [ <" );
6121 aLine.append( m_aDocID.getStr(), m_aDocID.getLength() );
6122 aLine.append( ">\n"
6123 "<" );
6124 aLine.append( m_aDocID.getStr(), m_aDocID.getLength() );
6125 aLine.append( "> ]\n" );
6127 if( aDocChecksum.getLength() )
6129 aLine.append( "/DocChecksum /" );
6130 aLine.append( aDocChecksum );
6131 aLine.append( "\n" );
6133 if( m_aAdditionalStreams.size() > 0 )
6135 aLine.append( "/AdditionalStreams [" );
6136 for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ )
6138 aLine.append( "/" );
6139 appendName( m_aAdditionalStreams[i].m_aMimeType, aLine );
6140 aLine.append( " " );
6141 aLine.append( m_aAdditionalStreams[i].m_nStreamObject );
6142 aLine.append( " 0 R\n" );
6144 aLine.append( "]\n" );
6146 aLine.append( ">>\n"
6147 "startxref\n" );
6148 aLine.append( (sal_Int64)nXRefOffset );
6149 aLine.append( "\n"
6150 "%%EOF\n" );
6151 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6153 return true;
6156 struct AnnotationSortEntry
6158 sal_Int32 nTabOrder;
6159 sal_Int32 nObject;
6160 sal_Int32 nWidgetIndex;
6162 AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
6163 nTabOrder( nTab ),
6164 nObject( nObj ),
6165 nWidgetIndex( nI )
6169 struct AnnotSortContainer
6171 std::set< sal_Int32 > aObjects;
6172 std::vector< AnnotationSortEntry > aSortedAnnots;
6175 struct AnnotSorterLess
6177 std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets;
6179 AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {}
6181 bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
6183 if( rLeft.nTabOrder < rRight.nTabOrder )
6184 return true;
6185 if( rRight.nTabOrder < rLeft.nTabOrder )
6186 return false;
6187 if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
6188 return false;
6189 if( rRight.nWidgetIndex < 0 )
6190 return true;
6191 if( rLeft.nWidgetIndex < 0 )
6192 return false;
6193 // remember: widget rects are in PDF coordinates, so they are ordered down up
6194 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
6195 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
6196 return true;
6197 if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
6198 m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
6199 return false;
6200 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
6201 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
6202 return true;
6203 return false;
6207 void PDFWriterImpl::sortWidgets()
6209 // sort widget annotations on each page as per their
6210 // TabOrder attribute
6211 std::hash_map< sal_Int32, AnnotSortContainer > sorted;
6212 int nWidgets = m_aWidgets.size();
6213 for( int nW = 0; nW < nWidgets; nW++ )
6215 const PDFWidget& rWidget = m_aWidgets[nW];
6216 AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
6217 // optimize vector allocation
6218 if( rCont.aSortedAnnots.empty() )
6219 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
6220 // insert widget to tab sorter
6221 // RadioButtons are not page annotations, only their individual check boxes are
6222 if( rWidget.m_eType != PDFWriter::RadioButton )
6224 rCont.aObjects.insert( rWidget.m_nObject );
6225 rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) );
6228 for( std::hash_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it )
6230 // append entries for non widget annotations
6231 PDFPage& rPage = m_aPages[ it->first ];
6232 unsigned int nAnnots = rPage.m_aAnnotations.size();
6233 for( unsigned int nA = 0; nA < nAnnots; nA++ )
6234 if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end())
6235 it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) );
6237 AnnotSorterLess aLess( m_aWidgets );
6238 std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess );
6239 // sanity check
6240 if( it->second.aSortedAnnots.size() == nAnnots)
6242 for( unsigned int nA = 0; nA < nAnnots; nA++ )
6243 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject;
6245 else
6247 DBG_ASSERT( 0, "wrong number of sorted annotations" );
6248 #if OSL_DEBUG_LEVEL > 0
6249 fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n"
6250 " %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots );
6251 #endif
6255 // FIXME: implement tab order in structure tree for PDF 1.5
6258 namespace vcl {
6259 class PDFStreamIf :
6260 public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream >
6262 PDFWriterImpl* m_pWriter;
6263 bool m_bWrite;
6264 public:
6265 PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
6266 virtual ~PDFStreamIf();
6268 virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw();
6269 virtual void SAL_CALL flush() throw();
6270 virtual void SAL_CALL closeOutput() throw();
6274 PDFStreamIf::~PDFStreamIf()
6278 void SAL_CALL PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw()
6280 if( m_bWrite )
6282 sal_Int32 nBytes = aData.getLength();
6283 if( nBytes > 0 )
6284 m_pWriter->writeBuffer( aData.getConstArray(), nBytes );
6288 void SAL_CALL PDFStreamIf::flush() throw()
6292 void SAL_CALL PDFStreamIf::closeOutput() throw()
6294 m_bWrite = false;
6297 bool PDFWriterImpl::emitAdditionalStreams()
6299 unsigned int nStreams = m_aAdditionalStreams.size();
6300 for( unsigned int i = 0; i < nStreams; i++ )
6302 PDFAddStream& rStream = m_aAdditionalStreams[i];
6303 rStream.m_nStreamObject = createObject();
6304 sal_Int32 nSizeObject = createObject();
6306 if( ! updateObject( rStream.m_nStreamObject ) )
6307 return false;
6309 OStringBuffer aLine;
6310 aLine.append( rStream.m_nStreamObject );
6311 aLine.append( " 0 obj\n<</Length " );
6312 aLine.append( nSizeObject );
6313 aLine.append( " 0 R" );
6314 if( rStream.m_bCompress )
6315 aLine.append( "/Filter/FlateDecode" );
6316 aLine.append( ">>\nstream\n" );
6317 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6318 return false;
6319 sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0;
6320 if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) )
6322 osl_closeFile( m_aFile );
6323 m_bOpen = false;
6325 if( rStream.m_bCompress )
6326 beginCompression();
6328 checkAndEnableStreamEncryption( rStream.m_nStreamObject );
6329 com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) );
6330 rStream.m_pStream->write( xStream );
6331 xStream.clear();
6332 delete rStream.m_pStream;
6333 rStream.m_pStream = NULL;
6334 disableStreamEncryption();
6336 if( rStream.m_bCompress )
6337 endCompression();
6339 if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) )
6341 osl_closeFile( m_aFile );
6342 m_bOpen = false;
6343 return false;
6345 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6346 return false ;
6347 // emit stream length object
6348 if( ! updateObject( nSizeObject ) )
6349 return false;
6350 aLine.setLength( 0 );
6351 aLine.append( nSizeObject );
6352 aLine.append( " 0 obj\n" );
6353 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6354 aLine.append( "\nendobj\n\n" );
6355 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6356 return false;
6358 return true;
6361 bool PDFWriterImpl::emit()
6363 endPage();
6365 // resort structure tree and annotations if necessary
6366 // needed for widget tab order
6367 sortWidgets();
6369 // emit additional streams
6370 CHECK_RETURN( emitAdditionalStreams() );
6372 // emit catalog
6373 CHECK_RETURN( emitCatalog() );
6375 // emit trailer
6376 CHECK_RETURN( emitTrailer() );
6378 osl_closeFile( m_aFile );
6379 m_bOpen = false;
6381 return true;
6384 std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors()
6386 return m_aErrors;
6390 void PDFWriterImpl::registerGlyphs( int nGlyphs,
6391 sal_GlyphId* pGlyphs,
6392 sal_Int32* pGlyphWidths,
6393 sal_Ucs* pUnicodes,
6394 sal_uInt8* pMappedGlyphs,
6395 sal_Int32* pMappedFontObjects,
6396 const ImplFontData* pFallbackFonts[] )
6398 const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6399 for( int i = 0; i < nGlyphs; i++ )
6401 const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB);
6402 const ImplFontData* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont;
6404 if( isBuiltinFont( pCurrentFont ) )
6406 sal_Int32 nFontID = 0;
6407 FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6408 if( it != m_aEmbeddedFonts.end() )
6409 nFontID = it->second.m_nNormalFontID;
6410 else
6412 nFontID = m_nNextFID++;
6413 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6414 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6417 pGlyphWidths[ i ] = 0;
6418 pMappedGlyphs[ i ] = sal::static_int_cast<sal_Int8>( nFontGlyphId );
6419 pMappedFontObjects[ i ] = nFontID;
6420 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pCurrentFont );
6421 if( pFD )
6423 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
6424 pGlyphWidths[i] = pBuiltinFont->m_aWidths[ nFontGlyphId & 0x00ff ];
6427 else if( pCurrentFont->mbSubsettable )
6429 FontSubset& rSubset = m_aSubsets[ pCurrentFont ];
6430 // search for font specific glyphID
6431 FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId );
6432 if( it != rSubset.m_aMapping.end() )
6434 pMappedFontObjects[i] = it->second.m_nFontID;
6435 pMappedGlyphs[i] = it->second.m_nSubsetGlyphID;
6437 else
6439 // create new subset if necessary
6440 if( rSubset.m_aSubsets.empty()
6441 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
6443 rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) );
6446 // copy font id
6447 pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID;
6448 // create new glyph in subset
6449 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
6450 pMappedGlyphs[i] = nNewId;
6452 // add new glyph to emitted font subset
6453 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
6454 rNewGlyphEmit.m_nSubsetGlyphID = nNewId;
6455 rNewGlyphEmit.m_aUnicode = (pUnicodes ? pUnicodes[i] : 0);
6457 // add new glyph to font mapping
6458 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
6459 rNewGlyph.m_nFontID = pMappedFontObjects[i];
6460 rNewGlyph.m_nSubsetGlyphID = nNewId;
6462 getReferenceDevice()->ImplGetGraphics();
6463 const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0);
6464 pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont,
6465 nFontGlyphId,
6466 bVertical,
6467 m_pReferenceDevice->mpGraphics );
6469 else if( pCurrentFont->IsEmbeddable() )
6471 sal_Int32 nFontID = 0;
6472 FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6473 if( it != m_aEmbeddedFonts.end() )
6474 nFontID = it->second.m_nNormalFontID;
6475 else
6477 nFontID = m_nNextFID++;
6478 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6479 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6481 EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont];
6483 const Ucs2SIntMap* pEncoding = NULL;
6484 const Ucs2OStrMap* pNonEncoded = NULL;
6485 getReferenceDevice()->ImplGetGraphics();
6486 pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded );
6488 Ucs2SIntMap::const_iterator enc_it;
6489 Ucs2OStrMap::const_iterator nonenc_it;
6491 sal_Int32 nCurFontID = nFontID;
6492 sal_Ucs cChar = pUnicodes[i];
6493 if( pEncoding )
6495 enc_it = pEncoding->find( cChar );
6496 if( enc_it != pEncoding->end() && enc_it->second > 0 )
6498 DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" );
6499 cChar = (sal_Ucs)enc_it->second;
6501 else if( (enc_it == pEncoding->end() || enc_it->second == -1) &&
6502 pNonEncoded &&
6503 (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() )
6505 nCurFontID = 0;
6506 // find non encoded glyph
6507 for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it )
6509 if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() )
6511 nCurFontID = nec_it->m_nFontID;
6512 cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ];
6513 break;
6516 if( nCurFontID == 0 ) // new nonencoded glyph
6518 if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 )
6520 rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() );
6521 rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++;
6523 EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back();
6524 rEncoding.m_aEncVector.push_back( EmbedCode() );
6525 rEncoding.m_aEncVector.back().m_aUnicode = cChar;
6526 rEncoding.m_aEncVector.back().m_aName = nonenc_it->second;
6527 rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1);
6528 nCurFontID = rEncoding.m_nFontID;
6529 cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ];
6532 else
6533 pEncoding = NULL;
6535 if( ! pEncoding )
6537 if( cChar & 0xff00 )
6539 // some characters can be used by conversion
6540 if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area
6541 cChar -= 0xf000;
6542 else
6544 String aString(cChar);
6545 ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 );
6546 cChar = ((sal_Ucs)aChar.GetChar( 0 )) & 0x00ff;
6551 pMappedGlyphs[ i ] = (sal_Int8)cChar;
6552 pMappedFontObjects[ i ] = nCurFontID;
6553 pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont,
6554 (pEncoding ? pUnicodes[i] : cChar) | GF_ISCHAR,
6555 false,
6556 m_pReferenceDevice->mpGraphics );
6561 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines )
6563 push( PUSH_ALL );
6565 FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief();
6567 Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor();
6568 Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
6569 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
6570 Color aReliefColor( COL_LIGHTGRAY );
6571 if( aTextColor == COL_BLACK )
6572 aTextColor = Color( COL_WHITE );
6573 if( aTextLineColor == COL_BLACK )
6574 aTextLineColor = Color( COL_WHITE );
6575 if( aOverlineColor == COL_BLACK )
6576 aOverlineColor = Color( COL_WHITE );
6577 if( aTextColor == COL_WHITE )
6578 aReliefColor = Color( COL_BLACK );
6580 Font aSetFont = m_aCurrentPDFState.m_aFont;
6581 aSetFont.SetRelief( RELIEF_NONE );
6582 aSetFont.SetShadow( FALSE );
6584 aSetFont.SetColor( aReliefColor );
6585 setTextLineColor( aReliefColor );
6586 setOverlineColor( aReliefColor );
6587 setFont( aSetFont );
6588 long nOff = 1 + getReferenceDevice()->mnDPIX/300;
6589 if( eRelief == RELIEF_ENGRAVED )
6590 nOff = -nOff;
6592 rLayout.DrawOffset() += Point( nOff, nOff );
6593 updateGraphicsState();
6594 drawLayout( rLayout, rText, bTextLines );
6596 rLayout.DrawOffset() -= Point( nOff, nOff );
6597 setTextLineColor( aTextLineColor );
6598 setOverlineColor( aOverlineColor );
6599 aSetFont.SetColor( aTextColor );
6600 setFont( aSetFont );
6601 updateGraphicsState();
6602 drawLayout( rLayout, rText, bTextLines );
6604 // clean up the mess
6605 pop();
6608 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines )
6610 Font aSaveFont = m_aCurrentPDFState.m_aFont;
6611 Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
6612 Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
6614 Font& rFont = m_aCurrentPDFState.m_aFont;
6615 if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 )
6616 rFont.SetColor( Color( COL_LIGHTGRAY ) );
6617 else
6618 rFont.SetColor( Color( COL_BLACK ) );
6619 rFont.SetShadow( FALSE );
6620 rFont.SetOutline( FALSE );
6621 setFont( rFont );
6622 setTextLineColor( rFont.GetColor() );
6623 setOverlineColor( rFont.GetColor() );
6624 updateGraphicsState();
6626 long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24);
6627 if( rFont.IsOutline() )
6628 nOff++;
6629 rLayout.DrawBase() += Point( nOff, nOff );
6630 drawLayout( rLayout, rText, bTextLines );
6631 rLayout.DrawBase() -= Point( nOff, nOff );
6633 setFont( aSaveFont );
6634 setTextLineColor( aSaveTextLineColor );
6635 setOverlineColor( aSaveOverlineColor );
6636 updateGraphicsState();
6639 void PDFWriterImpl::drawVerticalGlyphs(
6640 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
6641 OStringBuffer& rLine,
6642 const Point& rAlignOffset,
6643 const Matrix3& rRotScale,
6644 double fAngle,
6645 double fXScale,
6646 double fSkew,
6647 sal_Int32 nFontHeight )
6649 long nXOffset = 0;
6650 Point aCurPos( rGlyphs[0].m_aPos );
6651 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
6652 aCurPos += rAlignOffset;
6653 for( size_t i = 0; i < rGlyphs.size(); i++ )
6655 // have to emit each glyph on its own
6656 double fDeltaAngle = 0.0;
6657 double fYScale = 1.0;
6658 double fTempXScale = fXScale;
6659 double fSkewB = fSkew;
6660 double fSkewA = 0.0;
6662 Point aDeltaPos;
6663 if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL )
6665 fDeltaAngle = M_PI/2.0;
6666 aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent();
6667 aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale);
6668 fYScale = fXScale;
6669 fTempXScale = 1.0;
6670 fSkewA = -fSkewB;
6671 fSkewB = 0.0;
6673 else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR )
6675 fDeltaAngle = -M_PI/2.0;
6676 aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale);
6677 aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent();
6678 fYScale = fXScale;
6679 fTempXScale = 1.0;
6680 fSkewA = fSkewB;
6681 fSkewB = 0.0;
6683 aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) );
6684 if( i < rGlyphs.size()-1 )
6685 nXOffset += rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
6686 if( ! rGlyphs[i].m_nGlyphId )
6687 continue;
6689 aDeltaPos = rRotScale.transform( aDeltaPos );
6691 Matrix3 aMat;
6692 if( fSkewB != 0.0 || fSkewA != 0.0 )
6693 aMat.skew( fSkewA, fSkewB );
6694 aMat.scale( fTempXScale, fYScale );
6695 aMat.rotate( fAngle+fDeltaAngle );
6696 aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
6697 aMat.append( m_aPages.back(), rLine );
6698 rLine.append( " Tm" );
6699 if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
6701 rLine.append( " /F" );
6702 rLine.append( rGlyphs[i].m_nMappedFontId );
6703 rLine.append( ' ' );
6704 m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
6705 rLine.append( " Tf" );
6707 rLine.append( "<" );
6708 appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
6709 rLine.append( ">Tj\n" );
6713 void PDFWriterImpl::drawHorizontalGlyphs(
6714 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
6715 OStringBuffer& rLine,
6716 const Point& rAlignOffset,
6717 double fAngle,
6718 double fXScale,
6719 double fSkew,
6720 sal_Int32 nFontHeight,
6721 sal_Int32 nPixelFontHeight
6724 // horizontal (= normal) case
6726 // fill in run end indices
6727 // end is marked by index of the first glyph of the next run
6728 // a run is marked by same mapped font id and same Y position
6729 std::vector< sal_uInt32 > aRunEnds;
6730 aRunEnds.reserve( rGlyphs.size() );
6731 for( size_t i = 1; i < rGlyphs.size(); i++ )
6733 if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
6734 rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
6736 aRunEnds.push_back(i);
6739 // last run ends at last glyph
6740 aRunEnds.push_back( rGlyphs.size() );
6742 // loop over runs of the same font
6743 sal_uInt32 nBeginRun = 0;
6744 for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
6746 // setup text matrix
6747 Point aCurPos = rGlyphs[nBeginRun].m_aPos;
6748 // back transformation to current coordinate system
6749 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
6750 aCurPos += rAlignOffset;
6751 // the first run can be set with "Td" operator
6752 // subsequent use of that operator would move
6753 // the texline matrix relative to what was set before
6754 // making use of that would drive us into rounding issues
6755 Matrix3 aMat;
6756 if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
6758 m_aPages.back().appendPoint( aCurPos, rLine, false );
6759 rLine.append( " Td " );
6761 else
6763 if( fSkew != 0.0 )
6764 aMat.skew( 0.0, fSkew );
6765 aMat.scale( fXScale, 1.0 );
6766 aMat.rotate( fAngle );
6767 aMat.translate( aCurPos.X(), aCurPos.Y() );
6768 aMat.append( m_aPages.back(), rLine );
6769 rLine.append( " Tm\n" );
6771 // set up correct font
6772 rLine.append( "/F" );
6773 rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
6774 rLine.append( ' ' );
6775 m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
6776 rLine.append( " Tf" );
6778 // output glyphs using Tj or TJ
6779 OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
6780 aKernedLine.append( "[<" );
6781 aUnkernedLine.append( '<' );
6782 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
6783 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
6785 aMat.invert();
6786 bool bNeedKern = false;
6787 for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
6789 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
6790 // check if default glyph positioning is sufficient
6791 const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
6792 const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
6793 double fAdvance = aThisPos.X() - aPrevPos.X();
6794 fAdvance *= 1000.0 / nPixelFontHeight;
6795 const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5);
6796 if( nAdjustment != 0 )
6798 // apply individual glyph positioning
6799 bNeedKern = true;
6800 aKernedLine.append( ">" );
6801 aKernedLine.append( nAdjustment );
6802 aKernedLine.append( "<" );
6804 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
6806 aKernedLine.append( ">]TJ\n" );
6807 aUnkernedLine.append( ">Tj\n" );
6808 rLine.append( bNeedKern ? aKernedLine : aUnkernedLine );
6810 // set beginning of next run
6811 nBeginRun = aRunEnds[nRun];
6815 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines )
6817 // relief takes precedence over shadow (see outdev3.cxx)
6818 if( m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE )
6820 drawRelief( rLayout, rText, bTextLines );
6821 return;
6823 else if( m_aCurrentPDFState.m_aFont.IsShadow() )
6824 drawShadow( rLayout, rText, bTextLines );
6826 OStringBuffer aLine( 512 );
6828 const int nMaxGlyphs = 256;
6830 sal_GlyphId pGlyphs[nMaxGlyphs];
6831 sal_Int32 pGlyphWidths[nMaxGlyphs];
6832 sal_uInt8 pMappedGlyphs[nMaxGlyphs];
6833 sal_Int32 pMappedFontObjects[nMaxGlyphs];
6834 sal_Ucs pUnicodes[nMaxGlyphs];
6835 int pCharPosAry[nMaxGlyphs];
6836 sal_Int32 nAdvanceWidths[nMaxGlyphs];
6837 const ImplFontData* pFallbackFonts[nMaxGlyphs];
6838 bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
6839 int nGlyphs;
6840 int nIndex = 0;
6841 int nMinCharPos = 0, nMaxCharPos = rText.Len()-1;
6842 double fXScale = 1.0;
6843 double fSkew = 0.0;
6844 sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight;
6845 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
6847 // transform font height back to current units
6848 // note: the layout calculates in outdevs device pixel !!
6849 sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
6850 if( m_aCurrentPDFState.m_aFont.GetWidth() )
6852 Font aFont( m_aCurrentPDFState.m_aFont );
6853 aFont.SetWidth( 0 );
6854 FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
6855 if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() )
6857 fXScale =
6858 (double)m_aCurrentPDFState.m_aFont.GetWidth() /
6859 (double)aMetric.GetWidth();
6861 // force state before GetFontMetric
6862 m_pReferenceDevice->ImplNewFont();
6865 // perform artificial italics if necessary
6866 if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
6867 m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
6868 !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL ||
6869 m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE )
6872 fSkew = M_PI/12.0;
6875 // if the mapmode is distorted we need to adjust for that also
6876 if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
6878 fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
6881 int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
6882 // normalize angles
6883 while( nAngle < 0 )
6884 nAngle += 3600;
6885 nAngle = nAngle % 3600;
6886 double fAngle = (double)nAngle * M_PI / 1800.0;
6888 Matrix3 aRotScale;
6889 aRotScale.scale( fXScale, 1.0 );
6890 if( fAngle != 0.0 )
6891 aRotScale.rotate( -fAngle );
6893 bool bPop = false;
6894 bool bABold = false;
6895 // artificial bold necessary ?
6896 if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
6897 m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
6899 if( ! bPop )
6900 aLine.append( "q " );
6901 bPop = true;
6902 bABold = true;
6904 // setup text colors (if necessary)
6905 Color aStrokeColor( COL_TRANSPARENT );
6906 Color aNonStrokeColor( COL_TRANSPARENT );
6908 if( m_aCurrentPDFState.m_aFont.IsOutline() )
6910 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6911 aNonStrokeColor = Color( COL_WHITE );
6913 else
6914 aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6915 if( bABold )
6916 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
6918 if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
6920 if( ! bPop )
6921 aLine.append( "q " );
6922 bPop = true;
6923 appendStrokingColor( aStrokeColor, aLine );
6924 aLine.append( "\n" );
6926 if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
6928 if( ! bPop )
6929 aLine.append( "q " );
6930 bPop = true;
6931 appendNonStrokingColor( aNonStrokeColor, aLine );
6932 aLine.append( "\n" );
6935 // begin text object
6936 aLine.append( "BT\n" );
6937 // outline attribute ?
6938 if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
6940 // set correct text mode, set stroke width
6941 aLine.append( "2 Tr " ); // fill, then stroke
6943 if( m_aCurrentPDFState.m_aFont.IsOutline() )
6945 // unclear what to do in case of outline and artificial bold
6946 // for the time being outline wins
6947 aLine.append( "0.25 w \n" );
6949 else
6951 double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0;
6952 m_aPages.back().appendMappedLength( fW, aLine );
6953 aLine.append ( " w\n" );
6957 FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
6959 // collect the glyphs into a single array
6960 const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
6961 std::vector< PDFGlyph > aGlyphs;
6962 aGlyphs.reserve( nTmpMaxGlyphs );
6963 // first get all the glyphs and register them; coordinates still in Pixel
6964 Point aGNGlyphPos;
6965 while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 )
6967 for( int i = 0; i < nGlyphs; i++ )
6969 pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] );
6971 if( (pGlyphs[i] & GF_ISCHAR) )
6972 pUnicodes[i] = static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK);
6973 else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
6975 pUnicodes[i] = rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) );
6976 // #i36691# hack that is needed because currently the pGlyphs[]
6977 // argument is ignored for embeddable fonts and so the layout
6978 // engine's glyph work is ignored (i.e. char mirroring)
6979 // TODO: a real solution would be to map the layout engine's
6980 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
6981 // back to unicode and then to embeddable font's encoding
6982 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL )
6983 pUnicodes[i] = static_cast<sal_Ucs>(GetMirroredChar(pUnicodes[i]));
6985 else
6986 pUnicodes[i] = 0;
6987 // note: in case of ctl one character may result
6988 // in multiple glyphs. The current SalLayout
6989 // implementations set -1 then to indicate that no direct
6990 // mapping is possible
6993 registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, pUnicodes, pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
6995 for( int i = 0; i < nGlyphs; i++ )
6997 aGlyphs.push_back( PDFGlyph( aGNGlyphPos,
6998 pGlyphWidths[i],
6999 pGlyphs[i],
7000 pMappedFontObjects[i],
7001 pMappedGlyphs[i] ) );
7002 if( bVertical )
7003 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7004 else
7005 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7009 Point aAlignOffset;
7010 if ( eAlign == ALIGN_BOTTOM )
7011 aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
7012 else if ( eAlign == ALIGN_TOP )
7013 aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
7014 if( aAlignOffset.X() || aAlignOffset.Y() )
7015 aAlignOffset = aRotScale.transform( aAlignOffset );
7017 /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
7018 string contained only on of the UTF16 BOMs
7020 if( ! aGlyphs.empty() )
7022 if( bVertical )
7023 drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
7024 else
7025 drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
7028 // end textobject
7029 aLine.append( "ET\n" );
7030 if( bPop )
7031 aLine.append( "Q\n" );
7033 writeBuffer( aLine.getStr(), aLine.getLength() );
7035 // draw eventual textlines
7036 FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
7037 FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
7038 FontUnderline eOverline = m_aCurrentPDFState.m_aFont.GetOverline();
7039 if( bTextLines &&
7041 ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) ||
7042 ( eOverline != UNDERLINE_NONE && eOverline != UNDERLINE_DONTKNOW ) ||
7043 ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
7047 BOOL bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
7048 if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
7050 Point aPos, aStartPt;
7051 sal_Int32 nWidth = 0, nAdvance=0;
7052 for( int nStart = 0;;)
7054 sal_GlyphId nGlyphIndex;
7055 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7056 break;
7058 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7060 if( !nWidth )
7061 aStartPt = aPos;
7063 nWidth += nAdvance;
7065 else if( nWidth > 0 )
7067 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7068 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7069 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7070 nWidth = 0;
7074 if( nWidth > 0 )
7076 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7077 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7078 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7081 else
7083 Point aStartPt = rLayout.GetDrawPosition();
7084 int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
7085 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7086 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7087 eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7091 // write eventual emphasis marks
7092 if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
7094 PolyPolygon aEmphPoly;
7095 Rectangle aEmphRect1;
7096 Rectangle aEmphRect2;
7097 long nEmphYOff;
7098 long nEmphWidth;
7099 long nEmphHeight;
7100 BOOL bEmphPolyLine;
7101 FontEmphasisMark nEmphMark;
7103 push( PUSH_ALL );
7105 aLine.setLength( 0 );
7106 aLine.append( "q\n" );
7108 nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
7109 if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7110 nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
7111 else
7112 nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
7113 m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
7114 bEmphPolyLine,
7115 aEmphRect1,
7116 aEmphRect2,
7117 nEmphYOff,
7118 nEmphWidth,
7119 nEmphMark,
7120 m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight),
7121 m_pReferenceDevice->mpFontEntry->mnOrientation );
7122 if ( bEmphPolyLine )
7124 setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
7125 setFillColor( Color( COL_TRANSPARENT ) );
7127 else
7129 setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
7130 setLineColor( Color( COL_TRANSPARENT ) );
7132 writeBuffer( aLine.getStr(), aLine.getLength() );
7134 Point aOffset = Point(0,0);
7136 if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7137 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff;
7138 else
7139 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff;
7141 long nEmphWidth2 = nEmphWidth / 2;
7142 long nEmphHeight2 = nEmphHeight / 2;
7143 aOffset += Point( nEmphWidth2, nEmphHeight2 );
7145 if ( eAlign == ALIGN_BOTTOM )
7146 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent;
7147 else if ( eAlign == ALIGN_TOP )
7148 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent;
7150 for( int nStart = 0;;)
7152 Point aPos;
7153 sal_GlyphId nGlyphIndex;
7154 sal_Int32 nAdvance;
7155 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7156 break;
7158 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7160 Point aAdjOffset = aOffset;
7161 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2;
7162 aAdjOffset = aRotScale.transform( aAdjOffset );
7164 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
7166 aPos += aAdjOffset;
7167 aPos = m_pReferenceDevice->PixelToLogic( aPos );
7168 drawEmphasisMark( aPos.X(), aPos.Y(),
7169 aEmphPoly, bEmphPolyLine,
7170 aEmphRect1, aEmphRect2 );
7174 writeBuffer( "Q\n", 2 );
7175 pop();
7180 void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
7181 const PolyPolygon& rPolyPoly, BOOL bPolyLine,
7182 const Rectangle& rRect1, const Rectangle& rRect2 )
7184 // TODO: pass nWidth as width of this mark
7185 // long nWidth = 0;
7187 if ( rPolyPoly.Count() )
7189 if ( bPolyLine )
7191 Polygon aPoly = rPolyPoly.GetObject( 0 );
7192 aPoly.Move( nX, nY );
7193 drawPolyLine( aPoly );
7195 else
7197 PolyPolygon aPolyPoly = rPolyPoly;
7198 aPolyPoly.Move( nX, nY );
7199 drawPolyPolygon( aPolyPoly );
7203 if ( !rRect1.IsEmpty() )
7205 Rectangle aRect( Point( nX+rRect1.Left(),
7206 nY+rRect1.Top() ), rRect1.GetSize() );
7207 drawRectangle( aRect );
7210 if ( !rRect2.IsEmpty() )
7212 Rectangle aRect( Point( nX+rRect2.Left(),
7213 nY+rRect2.Top() ), rRect2.GetSize() );
7215 drawRectangle( aRect );
7219 void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7221 MARK( "drawText" );
7223 updateGraphicsState();
7225 // get a layout from the OuputDevice's SalGraphics
7226 // this also enforces font substitution and sets the font on SalGraphics
7227 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
7228 if( pLayout )
7230 drawLayout( *pLayout, rText, bTextLines );
7231 pLayout->Release();
7235 void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7237 MARK( "drawText with array" );
7239 updateGraphicsState();
7241 // get a layout from the OuputDevice's SalGraphics
7242 // this also enforces font substitution and sets the font on SalGraphics
7243 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
7244 if( pLayout )
7246 drawLayout( *pLayout, rText, bTextLines );
7247 pLayout->Release();
7251 void PDFWriterImpl::drawStretchText( const Point& rPos, ULONG nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7253 MARK( "drawStretchText" );
7255 updateGraphicsState();
7257 // get a layout from the OuputDevice's SalGraphics
7258 // this also enforces font substitution and sets the font on SalGraphics
7259 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
7260 if( pLayout )
7262 drawLayout( *pLayout, rText, bTextLines );
7263 pLayout->Release();
7267 void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, USHORT nStyle, bool bTextLines )
7269 long nWidth = rRect.GetWidth();
7270 long nHeight = rRect.GetHeight();
7272 if ( nWidth <= 0 || nHeight <= 0 )
7273 return;
7275 MARK( "drawText with rectangle" );
7277 updateGraphicsState();
7279 // clip with rectangle
7280 OStringBuffer aLine;
7281 aLine.append( "q " );
7282 m_aPages.back().appendRect( rRect, aLine );
7283 aLine.append( " W* n\n" );
7284 writeBuffer( aLine.getStr(), aLine.getLength() );
7286 // if disabled text is needed, put in here
7288 Point aPos = rRect.TopLeft();
7290 long nTextHeight = m_pReferenceDevice->GetTextHeight();
7291 xub_StrLen nMnemonicPos = STRING_NOTFOUND;
7293 String aStr = rOrigStr;
7294 if ( nStyle & TEXT_DRAW_MNEMONIC )
7295 aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos );
7297 // multiline text
7298 if ( nStyle & TEXT_DRAW_MULTILINE )
7300 XubString aLastLine;
7301 ImplMultiTextLineInfo aMultiLineInfo;
7302 ImplTextLineInfo* pLineInfo;
7303 long nMaxTextWidth;
7304 xub_StrLen i;
7305 xub_StrLen nLines;
7306 xub_StrLen nFormatLines;
7308 if ( nTextHeight )
7310 nMaxTextWidth = m_pReferenceDevice->ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle );
7311 nLines = (xub_StrLen)(nHeight/nTextHeight);
7312 nFormatLines = aMultiLineInfo.Count();
7313 if ( !nLines )
7314 nLines = 1;
7315 if ( nFormatLines > nLines )
7317 if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
7319 // handle last line
7320 nFormatLines = nLines-1;
7322 pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
7323 aLastLine = aStr.Copy( pLineInfo->GetIndex() );
7324 aLastLine.ConvertLineEnd( LINEEND_LF );
7325 // replace line feed by space
7326 xub_StrLen nLastLineLen = aLastLine.Len();
7327 for ( i = 0; i < nLastLineLen; i++ )
7329 if ( aLastLine.GetChar( i ) == _LF )
7330 aLastLine.SetChar( i, ' ' );
7332 aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
7333 nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
7334 nStyle |= TEXT_DRAW_TOP;
7338 // vertical alignment
7339 if ( nStyle & TEXT_DRAW_BOTTOM )
7340 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
7341 else if ( nStyle & TEXT_DRAW_VCENTER )
7342 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
7344 // draw all lines excluding the last
7345 for ( i = 0; i < nFormatLines; i++ )
7347 pLineInfo = aMultiLineInfo.GetLine( i );
7348 if ( nStyle & TEXT_DRAW_RIGHT )
7349 aPos.X() += nWidth-pLineInfo->GetWidth();
7350 else if ( nStyle & TEXT_DRAW_CENTER )
7351 aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
7352 xub_StrLen nIndex = pLineInfo->GetIndex();
7353 xub_StrLen nLineLen = pLineInfo->GetLen();
7354 drawText( aPos, aStr, nIndex, nLineLen, bTextLines );
7355 // mnemonics should not appear in documents,
7356 // if the need arises, put them in here
7357 aPos.Y() += nTextHeight;
7358 aPos.X() = rRect.Left();
7362 // output last line left adjusted since it was shortened
7363 if ( aLastLine.Len() )
7364 drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines );
7367 else
7369 long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7371 // Evt. Text kuerzen
7372 if ( nTextWidth > nWidth )
7374 if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) )
7376 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
7377 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
7378 nStyle |= TEXT_DRAW_LEFT;
7379 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7383 // vertical alignment
7384 if ( nStyle & TEXT_DRAW_RIGHT )
7385 aPos.X() += nWidth-nTextWidth;
7386 else if ( nStyle & TEXT_DRAW_CENTER )
7387 aPos.X() += (nWidth-nTextWidth)/2;
7389 if ( nStyle & TEXT_DRAW_BOTTOM )
7390 aPos.Y() += nHeight-nTextHeight;
7391 else if ( nStyle & TEXT_DRAW_VCENTER )
7392 aPos.Y() += (nHeight-nTextHeight)/2;
7394 // mnemonics should be inserted here if the need arises
7396 // draw the actual text
7397 drawText( aPos, aStr, 0, STRING_LEN, bTextLines );
7400 // reset clip region to original value
7401 aLine.setLength( 0 );
7402 aLine.append( "Q\n" );
7403 writeBuffer( aLine.getStr(), aLine.getLength() );
7406 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
7408 MARK( "drawLine" );
7410 updateGraphicsState();
7412 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7413 return;
7415 OStringBuffer aLine;
7416 m_aPages.back().appendPoint( rStart, aLine );
7417 aLine.append( " m " );
7418 m_aPages.back().appendPoint( rStop, aLine );
7419 aLine.append( " l S\n" );
7421 writeBuffer( aLine.getStr(), aLine.getLength() );
7424 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
7426 MARK( "drawLine with LineInfo" );
7427 updateGraphicsState();
7429 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7430 return;
7432 if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 )
7434 drawLine( rStart, rStop );
7435 return;
7438 OStringBuffer aLine;
7440 aLine.append( "q " );
7441 if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
7443 m_aPages.back().appendPoint( rStart, aLine );
7444 aLine.append( " m " );
7445 m_aPages.back().appendPoint( rStop, aLine );
7446 aLine.append( " l S Q\n" );
7448 writeBuffer( aLine.getStr(), aLine.getLength() );
7450 else
7452 PDFWriter::ExtLineInfo aInfo;
7453 convertLineInfoToExtLineInfo( rInfo, aInfo );
7454 Point aPolyPoints[2] = { rStart, rStop };
7455 Polygon aPoly( 2, aPolyPoints );
7456 drawPolyLine( aPoly, aInfo );
7460 void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth )
7462 Point aDiff( rStop-rStart );
7463 double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) );
7464 if( fLen < 1.0 )
7465 return;
7467 MARK( "drawWaveLine" );
7468 updateGraphicsState();
7470 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7471 return;
7473 OStringBuffer aLine( 512 );
7474 aLine.append( "q " );
7475 m_aPages.back().appendMappedLength( nLineWidth, aLine, true );
7476 aLine.append( " w " );
7478 appendDouble( (double)aDiff.X()/fLen, aLine );
7479 aLine.append( ' ' );
7480 appendDouble( -(double)aDiff.Y()/fLen, aLine );
7481 aLine.append( ' ' );
7482 appendDouble( (double)aDiff.Y()/fLen, aLine );
7483 aLine.append( ' ' );
7484 appendDouble( (double)aDiff.X()/fLen, aLine );
7485 aLine.append( ' ' );
7486 m_aPages.back().appendPoint( rStart, aLine );
7487 aLine.append( " cm " );
7488 m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine );
7489 aLine.append( "Q\n" );
7490 writeBuffer( aLine.getStr(), aLine.getLength() );
7493 #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x )
7494 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
7496 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
7498 // note: units in pFontEntry are ref device pixel
7499 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
7500 long nLineHeight = 0;
7501 long nLinePos = 0;
7503 appendStrokingColor( aColor, aLine );
7504 aLine.append( "\n" );
7506 if ( bIsAbove )
7508 if ( !pFontEntry->maMetric.mnAboveWUnderlineSize )
7509 m_pReferenceDevice->ImplInitAboveTextLineSize();
7510 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize );
7511 nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset );
7513 else
7515 if ( !pFontEntry->maMetric.mnWUnderlineSize )
7516 m_pReferenceDevice->ImplInitTextLineSize();
7517 nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize );
7518 nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset );
7520 if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
7521 nLineHeight = 3;
7523 long nLineWidth = getReferenceDevice()->mnDPIX/450;
7524 if ( ! nLineWidth )
7525 nLineWidth = 1;
7527 if ( eTextLine == UNDERLINE_BOLDWAVE )
7528 nLineWidth = 3*nLineWidth;
7530 m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
7531 aLine.append( " w " );
7533 if ( eTextLine == UNDERLINE_DOUBLEWAVE )
7535 long nOrgLineHeight = nLineHeight;
7536 nLineHeight /= 3;
7537 if ( nLineHeight < 2 )
7539 if ( nOrgLineHeight > 1 )
7540 nLineHeight = 2;
7541 else
7542 nLineHeight = 1;
7544 long nLineDY = nOrgLineHeight-(nLineHeight*2);
7545 if ( nLineDY < nLineWidth )
7546 nLineDY = nLineWidth;
7547 long nLineDY2 = nLineDY/2;
7548 if ( !nLineDY2 )
7549 nLineDY2 = 1;
7551 nLinePos -= nLineWidth-nLineDY2;
7553 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
7555 nLinePos += nLineWidth+nLineDY;
7556 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
7558 else
7560 if ( eTextLine != UNDERLINE_BOLDWAVE )
7561 nLinePos -= nLineWidth/2;
7562 m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
7566 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
7568 // note: units in pFontEntry are ref device pixel
7569 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
7570 long nLineHeight = 0;
7571 long nLinePos = 0;
7572 long nLinePos2 = 0;
7574 if ( eTextLine > UNDERLINE_BOLDWAVE )
7575 eTextLine = UNDERLINE_SINGLE;
7577 switch ( eTextLine )
7579 case UNDERLINE_SINGLE:
7580 case UNDERLINE_DOTTED:
7581 case UNDERLINE_DASH:
7582 case UNDERLINE_LONGDASH:
7583 case UNDERLINE_DASHDOT:
7584 case UNDERLINE_DASHDOTDOT:
7585 if ( bIsAbove )
7587 if ( !pFontEntry->maMetric.mnAboveUnderlineSize )
7588 m_pReferenceDevice->ImplInitAboveTextLineSize();
7589 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize );
7590 nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset );
7592 else
7594 if ( !pFontEntry->maMetric.mnUnderlineSize )
7595 m_pReferenceDevice->ImplInitTextLineSize();
7596 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize );
7597 nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset );
7599 break;
7600 case UNDERLINE_BOLD:
7601 case UNDERLINE_BOLDDOTTED:
7602 case UNDERLINE_BOLDDASH:
7603 case UNDERLINE_BOLDLONGDASH:
7604 case UNDERLINE_BOLDDASHDOT:
7605 case UNDERLINE_BOLDDASHDOTDOT:
7606 if ( bIsAbove )
7608 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize )
7609 m_pReferenceDevice->ImplInitAboveTextLineSize();
7610 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize );
7611 nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset );
7613 else
7615 if ( !pFontEntry->maMetric.mnBUnderlineSize )
7616 m_pReferenceDevice->ImplInitTextLineSize();
7617 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize );
7618 nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset );
7619 nLinePos += nLineHeight/2;
7621 break;
7622 case UNDERLINE_DOUBLE:
7623 if ( bIsAbove )
7625 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize )
7626 m_pReferenceDevice->ImplInitAboveTextLineSize();
7627 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize );
7628 nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 );
7629 nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 );
7631 else
7633 if ( !pFontEntry->maMetric.mnDUnderlineSize )
7634 m_pReferenceDevice->ImplInitTextLineSize();
7635 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize );
7636 nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 );
7637 nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 );
7639 default:
7640 break;
7643 if ( nLineHeight )
7645 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
7646 aLine.append( " w " );
7647 appendStrokingColor( aColor, aLine );
7648 aLine.append( "\n" );
7650 switch ( eTextLine )
7652 case UNDERLINE_DOTTED:
7653 case UNDERLINE_BOLDDOTTED:
7654 aLine.append( "[ " );
7655 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7656 aLine.append( " ] 0 d\n" );
7657 break;
7658 case UNDERLINE_DASH:
7659 case UNDERLINE_LONGDASH:
7660 case UNDERLINE_BOLDDASH:
7661 case UNDERLINE_BOLDLONGDASH:
7663 sal_Int32 nDashLength = 4*nLineHeight;
7664 sal_Int32 nVoidLength = 2*nLineHeight;
7665 if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) )
7666 nDashLength = 8*nLineHeight;
7668 aLine.append( "[ " );
7669 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7670 aLine.append( ' ' );
7671 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7672 aLine.append( " ] 0 d\n" );
7674 break;
7675 case UNDERLINE_DASHDOT:
7676 case UNDERLINE_BOLDDASHDOT:
7678 sal_Int32 nDashLength = 4*nLineHeight;
7679 sal_Int32 nVoidLength = 2*nLineHeight;
7680 aLine.append( "[ " );
7681 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7682 aLine.append( ' ' );
7683 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7684 aLine.append( ' ' );
7685 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7686 aLine.append( ' ' );
7687 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7688 aLine.append( " ] 0 d\n" );
7690 break;
7691 case UNDERLINE_DASHDOTDOT:
7692 case UNDERLINE_BOLDDASHDOTDOT:
7694 sal_Int32 nDashLength = 4*nLineHeight;
7695 sal_Int32 nVoidLength = 2*nLineHeight;
7696 aLine.append( "[ " );
7697 m_aPages.back().appendMappedLength( nDashLength, aLine, false );
7698 aLine.append( ' ' );
7699 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7700 aLine.append( ' ' );
7701 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7702 aLine.append( ' ' );
7703 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7704 aLine.append( ' ' );
7705 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
7706 aLine.append( ' ' );
7707 m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
7708 aLine.append( " ] 0 d\n" );
7710 break;
7711 default:
7712 break;
7715 aLine.append( "0 " );
7716 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
7717 aLine.append( " m " );
7718 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
7719 aLine.append( ' ' );
7720 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
7721 aLine.append( " l S\n" );
7722 if ( eTextLine == UNDERLINE_DOUBLE )
7724 aLine.append( "0 " );
7725 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
7726 aLine.append( " m " );
7727 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
7728 aLine.append( ' ' );
7729 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
7730 aLine.append( " l S\n" );
7735 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
7737 // note: units in pFontEntry are ref device pixel
7738 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
7739 long nLineHeight = 0;
7740 long nLinePos = 0;
7741 long nLinePos2 = 0;
7743 if ( eStrikeout > STRIKEOUT_X )
7744 eStrikeout = STRIKEOUT_SINGLE;
7746 switch ( eStrikeout )
7748 case STRIKEOUT_SINGLE:
7749 if ( !pFontEntry->maMetric.mnStrikeoutSize )
7750 m_pReferenceDevice->ImplInitTextLineSize();
7751 nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize );
7752 nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset );
7753 break;
7754 case STRIKEOUT_BOLD:
7755 if ( !pFontEntry->maMetric.mnBStrikeoutSize )
7756 m_pReferenceDevice->ImplInitTextLineSize();
7757 nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize );
7758 nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset );
7759 break;
7760 case STRIKEOUT_DOUBLE:
7761 if ( !pFontEntry->maMetric.mnDStrikeoutSize )
7762 m_pReferenceDevice->ImplInitTextLineSize();
7763 nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize );
7764 nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 );
7765 nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 );
7766 break;
7767 default:
7768 break;
7771 if ( nLineHeight )
7773 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
7774 aLine.append( " w " );
7775 appendStrokingColor( aColor, aLine );
7776 aLine.append( "\n" );
7778 aLine.append( "0 " );
7779 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
7780 aLine.append( " m " );
7781 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
7782 aLine.append( ' ' );
7783 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
7784 aLine.append( " l S\n" );
7786 if ( eStrikeout == STRIKEOUT_DOUBLE )
7788 aLine.append( "0 " );
7789 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
7790 aLine.append( " m " );
7791 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
7792 aLine.append( ' ' );
7793 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
7794 aLine.append( " l S\n" );
7799 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
7801 String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" );
7802 String aStrikeout = aStrikeoutChar;
7803 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
7804 aStrikeout.Append( aStrikeout );
7806 // do not get broader than nWidth modulo 1 character
7807 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
7808 aStrikeout.Erase( 0, 1 );
7809 aStrikeout.Append( aStrikeoutChar );
7810 BOOL bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
7811 if ( bShadow )
7813 Font aFont = m_aCurrentPDFState.m_aFont;
7814 aFont.SetShadow( FALSE );
7815 setFont( aFont );
7816 updateGraphicsState();
7819 // strikeout string is left aligned non-CTL text
7820 ULONG nOrigTLM = m_pReferenceDevice->GetLayoutMode();
7821 m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED );
7822 drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false );
7823 m_pReferenceDevice->SetLayoutMode( nOrigTLM );
7825 if ( bShadow )
7827 Font aFont = m_aCurrentPDFState.m_aFont;
7828 aFont.SetShadow( TRUE );
7829 setFont( aFont );
7830 updateGraphicsState();
7834 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove )
7836 if ( !nWidth ||
7837 ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
7838 ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) &&
7839 ((eOverline == UNDERLINE_NONE)||(eOverline == UNDERLINE_DONTKNOW)) ) )
7840 return;
7842 MARK( "drawTextLine" );
7843 updateGraphicsState();
7845 // note: units in pFontEntry are ref device pixel
7846 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
7847 Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
7848 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7849 Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
7850 bool bStrikeoutDone = false;
7851 bool bUnderlineDone = false;
7852 bool bOverlineDone = false;
7854 if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
7856 drawStrikeoutChar( rPos, nWidth, eStrikeout );
7857 bStrikeoutDone = true;
7860 Point aPos( rPos );
7861 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
7862 if( eAlign == ALIGN_TOP )
7863 aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent );
7864 else if( eAlign == ALIGN_BOTTOM )
7865 aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent );
7867 OStringBuffer aLine( 512 );
7868 // save GS
7869 aLine.append( "q " );
7871 // rotate and translate matrix
7872 double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
7873 Matrix3 aMat;
7874 aMat.rotate( fAngle );
7875 aMat.translate( aPos.X(), aPos.Y() );
7876 aMat.append( m_aPages.back(), aLine );
7877 aLine.append( " cm\n" );
7879 if ( aUnderlineColor.GetTransparency() != 0 )
7880 aUnderlineColor = aStrikeoutColor;
7882 if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
7883 (eUnderline == UNDERLINE_WAVE) ||
7884 (eUnderline == UNDERLINE_DOUBLEWAVE) ||
7885 (eUnderline == UNDERLINE_BOLDWAVE) )
7887 drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
7888 bUnderlineDone = true;
7891 if ( (eOverline == UNDERLINE_SMALLWAVE) ||
7892 (eOverline == UNDERLINE_WAVE) ||
7893 (eOverline == UNDERLINE_DOUBLEWAVE) ||
7894 (eOverline == UNDERLINE_BOLDWAVE) )
7896 drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
7897 bOverlineDone = true;
7900 if ( !bUnderlineDone )
7902 drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
7905 if ( !bOverlineDone )
7907 drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
7910 if ( !bStrikeoutDone )
7912 drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
7915 aLine.append( "Q\n" );
7916 writeBuffer( aLine.getStr(), aLine.getLength() );
7919 void PDFWriterImpl::drawPolygon( const Polygon& rPoly )
7921 MARK( "drawPolygon" );
7923 updateGraphicsState();
7925 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
7926 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
7927 return;
7929 int nPoints = rPoly.GetSize();
7930 OStringBuffer aLine( 20 * nPoints );
7931 m_aPages.back().appendPolygon( rPoly, aLine );
7932 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
7933 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
7934 aLine.append( "B*\n" );
7935 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
7936 aLine.append( "S\n" );
7937 else
7938 aLine.append( "f*\n" );
7940 writeBuffer( aLine.getStr(), aLine.getLength() );
7943 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly )
7945 MARK( "drawPolyPolygon" );
7947 updateGraphicsState();
7949 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
7950 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
7951 return;
7953 int nPolygons = rPolyPoly.Count();
7955 OStringBuffer aLine( 40 * nPolygons );
7956 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
7957 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
7958 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
7959 aLine.append( "B*\n" );
7960 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
7961 aLine.append( "S\n" );
7962 else
7963 aLine.append( "f*\n" );
7965 writeBuffer( aLine.getStr(), aLine.getLength() );
7968 void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
7970 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
7971 nTransparentPercent = nTransparentPercent % 100;
7973 MARK( "drawTransparent" );
7975 updateGraphicsState();
7977 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
7978 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
7979 return;
7981 if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 )
7983 m_aErrors.insert( m_bIsPDF_A1 ?
7984 PDFWriter::Warning_Transparency_Omitted_PDFA :
7985 PDFWriter::Warning_Transparency_Omitted_PDF13 );
7987 drawPolyPolygon( rPolyPoly );
7988 return;
7991 // create XObject
7992 m_aTransparentObjects.push_back( TransparencyEmit() );
7993 // FIXME: polygons with beziers may yield incorrect bound rect
7994 m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect();
7995 // convert rectangle to default user space
7996 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
7997 m_aTransparentObjects.back().m_nObject = createObject();
7998 m_aTransparentObjects.back().m_nExtGStateObject = createObject();
7999 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
8000 m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 );
8001 // create XObject's content stream
8002 OStringBuffer aContent( 256 );
8003 m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
8004 if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
8005 m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
8006 aContent.append( " B*\n" );
8007 else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
8008 aContent.append( " S\n" );
8009 else
8010 aContent.append( " f*\n" );
8011 m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() );
8013 OStringBuffer aObjName( 16 );
8014 aObjName.append( "Tr" );
8015 aObjName.append( m_aTransparentObjects.back().m_nObject );
8016 OString aTrName( aObjName.makeStringAndClear() );
8017 aObjName.append( "EGS" );
8018 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8019 OString aExtName( aObjName.makeStringAndClear() );
8021 OStringBuffer aLine( 80 );
8022 // insert XObject
8023 aLine.append( "q /" );
8024 aLine.append( aExtName );
8025 aLine.append( " gs /" );
8026 aLine.append( aTrName );
8027 aLine.append( " Do Q\n" );
8028 writeBuffer( aLine.getStr(), aLine.getLength() );
8030 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8031 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8034 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
8036 if( nObject >= 0 )
8038 switch( eKind )
8040 case ResXObject:
8041 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
8042 if( ! m_aOutputStreams.empty() )
8043 m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
8044 break;
8045 case ResExtGState:
8046 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
8047 if( ! m_aOutputStreams.empty() )
8048 m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
8049 break;
8050 case ResShading:
8051 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
8052 if( ! m_aOutputStreams.empty() )
8053 m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
8054 break;
8055 case ResPattern:
8056 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
8057 if( ! m_aOutputStreams.empty() )
8058 m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
8059 break;
8064 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect )
8066 push( PUSH_ALL );
8068 setClipRegion( Region() );
8069 updateGraphicsState();
8071 m_aOutputStreams.push_front( StreamRedirect() );
8072 m_aOutputStreams.front().m_pStream = pStream;
8073 m_aOutputStreams.front().m_aMapMode = m_aMapMode;
8075 if( !rTargetRect.IsEmpty() )
8077 m_aOutputStreams.front().m_aTargetRect =
8078 lcl_convert( m_aGraphicsStack.front().m_aMapMode,
8079 m_aMapMode,
8080 getReferenceDevice(),
8081 rTargetRect );
8082 Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
8083 long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
8084 aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
8085 m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
8088 // setup graphics state for independent object stream
8090 // force reemitting colors
8091 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8092 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8095 Rectangle PDFWriterImpl::getRedirectTargetRect() const
8097 return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect;
8100 SvStream* PDFWriterImpl::endRedirect()
8102 SvStream* pStream = NULL;
8103 if( ! m_aOutputStreams.empty() )
8105 pStream = m_aOutputStreams.front().m_pStream;
8106 m_aMapMode = m_aOutputStreams.front().m_aMapMode;
8107 m_aOutputStreams.pop_front();
8110 pop();
8111 // force reemitting colors
8112 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8113 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8115 updateGraphicsState();
8117 return pStream;
8120 void PDFWriterImpl::beginTransparencyGroup()
8122 updateGraphicsState();
8123 if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8124 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8127 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
8129 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8130 nTransparentPercent = nTransparentPercent % 100;
8132 if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8134 // create XObject
8135 m_aTransparentObjects.push_back( TransparencyEmit() );
8136 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox;
8137 // convert rectangle to default user space
8138 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8139 m_aTransparentObjects.back().m_nObject = createObject();
8140 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
8141 // get XObject's content stream
8142 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8143 m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8145 OStringBuffer aObjName( 16 );
8146 aObjName.append( "Tr" );
8147 aObjName.append( m_aTransparentObjects.back().m_nObject );
8148 OString aTrName( aObjName.makeStringAndClear() );
8149 aObjName.append( "EGS" );
8150 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8151 OString aExtName( aObjName.makeStringAndClear() );
8153 OStringBuffer aLine( 80 );
8154 // insert XObject
8155 aLine.append( "q /" );
8156 aLine.append( aExtName );
8157 aLine.append( " gs /" );
8158 aLine.append( aTrName );
8159 aLine.append( " Do Q\n" );
8160 writeBuffer( aLine.getStr(), aLine.getLength() );
8162 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8163 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8167 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask )
8169 if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8171 // create XObject
8172 m_aTransparentObjects.push_back( TransparencyEmit() );
8173 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox;
8174 // convert rectangle to default user space
8175 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8176 m_aTransparentObjects.back().m_nObject = createObject();
8177 m_aTransparentObjects.back().m_fAlpha = 0.0;
8178 // get XObject's content stream
8179 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8180 m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8182 // draw soft mask
8183 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8184 drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask );
8185 m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect());
8187 OStringBuffer aObjName( 16 );
8188 aObjName.append( "Tr" );
8189 aObjName.append( m_aTransparentObjects.back().m_nObject );
8190 OString aTrName( aObjName.makeStringAndClear() );
8191 aObjName.append( "EGS" );
8192 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8193 OString aExtName( aObjName.makeStringAndClear() );
8195 OStringBuffer aLine( 80 );
8196 // insert XObject
8197 aLine.append( "q /" );
8198 aLine.append( aExtName );
8199 aLine.append( " gs /" );
8200 aLine.append( aTrName );
8201 aLine.append( " Do Q\n" );
8202 writeBuffer( aLine.getStr(), aLine.getLength() );
8204 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8205 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8209 void PDFWriterImpl::drawRectangle( const Rectangle& rRect )
8211 MARK( "drawRectangle" );
8213 updateGraphicsState();
8215 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8216 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8217 return;
8219 OStringBuffer aLine( 40 );
8220 m_aPages.back().appendRect( rRect, aLine );
8222 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8223 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8224 aLine.append( " B*\n" );
8225 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8226 aLine.append( " S\n" );
8227 else
8228 aLine.append( " f*\n" );
8230 writeBuffer( aLine.getStr(), aLine.getLength() );
8233 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
8235 MARK( "drawRectangle with rounded edges" );
8237 if( !nHorzRound && !nVertRound )
8238 drawRectangle( rRect );
8240 updateGraphicsState();
8242 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8243 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8244 return;
8246 if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
8247 nHorzRound = rRect.GetWidth()/2;
8248 if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
8249 nVertRound = rRect.GetHeight()/2;
8251 Point aPoints[16];
8252 const double kappa = 0.5522847498;
8253 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
8254 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
8256 aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
8257 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8258 aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
8259 aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() );
8261 aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
8262 aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky );
8263 aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
8264 aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky );
8266 aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
8267 aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() );
8268 aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
8269 aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
8271 aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
8272 aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
8273 aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
8274 aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
8277 OStringBuffer aLine( 80 );
8278 m_aPages.back().appendPoint( aPoints[1], aLine );
8279 aLine.append( " m " );
8280 m_aPages.back().appendPoint( aPoints[2], aLine );
8281 aLine.append( " l " );
8282 m_aPages.back().appendPoint( aPoints[3], aLine );
8283 aLine.append( ' ' );
8284 m_aPages.back().appendPoint( aPoints[4], aLine );
8285 aLine.append( ' ' );
8286 m_aPages.back().appendPoint( aPoints[5], aLine );
8287 aLine.append( " c\n" );
8288 m_aPages.back().appendPoint( aPoints[6], aLine );
8289 aLine.append( " l " );
8290 m_aPages.back().appendPoint( aPoints[7], aLine );
8291 aLine.append( ' ' );
8292 m_aPages.back().appendPoint( aPoints[8], aLine );
8293 aLine.append( ' ' );
8294 m_aPages.back().appendPoint( aPoints[9], aLine );
8295 aLine.append( " c\n" );
8296 m_aPages.back().appendPoint( aPoints[10], aLine );
8297 aLine.append( " l " );
8298 m_aPages.back().appendPoint( aPoints[11], aLine );
8299 aLine.append( ' ' );
8300 m_aPages.back().appendPoint( aPoints[12], aLine );
8301 aLine.append( ' ' );
8302 m_aPages.back().appendPoint( aPoints[13], aLine );
8303 aLine.append( " c\n" );
8304 m_aPages.back().appendPoint( aPoints[14], aLine );
8305 aLine.append( " l " );
8306 m_aPages.back().appendPoint( aPoints[15], aLine );
8307 aLine.append( ' ' );
8308 m_aPages.back().appendPoint( aPoints[0], aLine );
8309 aLine.append( ' ' );
8310 m_aPages.back().appendPoint( aPoints[1], aLine );
8311 aLine.append( " c " );
8313 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8314 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8315 aLine.append( "b*\n" );
8316 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8317 aLine.append( "s\n" );
8318 else
8319 aLine.append( "f*\n" );
8321 writeBuffer( aLine.getStr(), aLine.getLength() );
8324 void PDFWriterImpl::drawEllipse( const Rectangle& rRect )
8326 MARK( "drawEllipse" );
8328 updateGraphicsState();
8330 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8331 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8332 return;
8334 Point aPoints[12];
8335 const double kappa = 0.5522847498;
8336 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
8337 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
8339 aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
8340 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8341 aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() );
8343 aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
8344 aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky );
8345 aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky );
8347 aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
8348 aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() );
8349 aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() );
8351 aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
8352 aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky );
8353 aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
8355 OStringBuffer aLine( 80 );
8356 m_aPages.back().appendPoint( aPoints[1], aLine );
8357 aLine.append( " m " );
8358 m_aPages.back().appendPoint( aPoints[2], aLine );
8359 aLine.append( ' ' );
8360 m_aPages.back().appendPoint( aPoints[3], aLine );
8361 aLine.append( ' ' );
8362 m_aPages.back().appendPoint( aPoints[4], aLine );
8363 aLine.append( " c\n" );
8364 m_aPages.back().appendPoint( aPoints[5], aLine );
8365 aLine.append( ' ' );
8366 m_aPages.back().appendPoint( aPoints[6], aLine );
8367 aLine.append( ' ' );
8368 m_aPages.back().appendPoint( aPoints[7], aLine );
8369 aLine.append( " c\n" );
8370 m_aPages.back().appendPoint( aPoints[8], aLine );
8371 aLine.append( ' ' );
8372 m_aPages.back().appendPoint( aPoints[9], aLine );
8373 aLine.append( ' ' );
8374 m_aPages.back().appendPoint( aPoints[10], aLine );
8375 aLine.append( " c\n" );
8376 m_aPages.back().appendPoint( aPoints[11], aLine );
8377 aLine.append( ' ' );
8378 m_aPages.back().appendPoint( aPoints[0], aLine );
8379 aLine.append( ' ' );
8380 m_aPages.back().appendPoint( aPoints[1], aLine );
8381 aLine.append( " c " );
8383 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8384 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8385 aLine.append( "b*\n" );
8386 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8387 aLine.append( "s\n" );
8388 else
8389 aLine.append( "f*\n" );
8391 writeBuffer( aLine.getStr(), aLine.getLength() );
8394 static double calcAngle( const Rectangle& rRect, const Point& rPoint )
8396 Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
8397 (rRect.Top()+rRect.Bottom()+1)/2);
8398 Point aPoint = rPoint - aOrigin;
8400 double fX = (double)aPoint.X();
8401 double fY = (double)-aPoint.Y();
8403 if( rRect.GetWidth() > rRect.GetHeight() )
8404 fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
8405 else if( rRect.GetHeight() > rRect.GetWidth() )
8406 fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
8407 return atan2( fY, fX );
8410 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
8412 MARK( "drawArc" );
8414 updateGraphicsState();
8416 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8417 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8418 return;
8420 // calculate start and stop angles
8421 const double fStartAngle = calcAngle( rRect, rStart );
8422 double fStopAngle = calcAngle( rRect, rStop );
8423 while( fStopAngle < fStartAngle )
8424 fStopAngle += 2.0*M_PI;
8425 const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
8426 const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
8427 const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
8428 const double halfWidth = (double)rRect.GetWidth()/2.0;
8429 const double halfHeight = (double)rRect.GetHeight()/2.0;
8431 const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
8432 (rRect.Top()+rRect.Bottom()+1)/2 );
8434 OStringBuffer aLine( 30*nFragments );
8435 Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
8436 -(int)(halfHeight * sin(fStartAngle) ) );
8437 aPoint += aCenter;
8438 m_aPages.back().appendPoint( aPoint, aLine );
8439 aLine.append( " m " );
8440 if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
8442 for( int i = 0; i < nFragments; i++ )
8444 const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
8445 const double fStopFragment = fStartFragment + fFragmentDelta;
8446 aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
8447 -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
8448 aPoint += aCenter;
8449 m_aPages.back().appendPoint( aPoint, aLine );
8450 aLine.append( ' ' );
8452 aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
8453 -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
8454 aPoint += aCenter;
8455 m_aPages.back().appendPoint( aPoint, aLine );
8456 aLine.append( ' ' );
8458 aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
8459 -(int)(halfHeight * sin(fStopFragment) ) );
8460 aPoint += aCenter;
8461 m_aPages.back().appendPoint( aPoint, aLine );
8462 aLine.append( " c\n" );
8465 if( bWithChord || bWithPie )
8467 if( bWithPie )
8469 m_aPages.back().appendPoint( aCenter, aLine );
8470 aLine.append( " l " );
8472 aLine.append( "h " );
8474 if( ! bWithChord && ! bWithPie )
8475 aLine.append( "S\n" );
8476 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8477 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8478 aLine.append( "B*\n" );
8479 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8480 aLine.append( "S\n" );
8481 else
8482 aLine.append( "f*\n" );
8484 writeBuffer( aLine.getStr(), aLine.getLength() );
8487 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly )
8489 MARK( "drawPolyLine" );
8491 USHORT nPoints = rPoly.GetSize();
8492 if( nPoints < 2 )
8493 return;
8495 updateGraphicsState();
8497 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8498 return;
8500 OStringBuffer aLine( 20 * nPoints );
8501 m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
8502 aLine.append( "S\n" );
8504 writeBuffer( aLine.getStr(), aLine.getLength() );
8507 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo )
8509 MARK( "drawPolyLine with LineInfo" );
8511 updateGraphicsState();
8513 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8514 return;
8516 OStringBuffer aLine;
8517 aLine.append( "q " );
8518 if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
8520 writeBuffer( aLine.getStr(), aLine.getLength() );
8521 drawPolyLine( rPoly );
8522 writeBuffer( "Q\n", 2 );
8524 else
8526 PDFWriter::ExtLineInfo aInfo;
8527 convertLineInfoToExtLineInfo( rInfo, aInfo );
8528 drawPolyLine( rPoly, aInfo );
8532 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
8534 DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" );
8535 rOut.m_fLineWidth = rIn.GetWidth();
8536 rOut.m_fTransparency = 0.0;
8537 rOut.m_eCap = PDFWriter::capButt;
8538 rOut.m_eJoin = PDFWriter::joinMiter;
8539 rOut.m_fMiterLimit = 10;
8540 rOut.m_aDashArray.clear();
8542 int nDashes = rIn.GetDashCount();
8543 int nDashLen = rIn.GetDashLen();
8544 int nDistance = rIn.GetDistance();
8545 for( int n = 0; n < nDashes; n++ )
8547 rOut.m_aDashArray.push_back( nDashLen );
8548 rOut.m_aDashArray.push_back( nDistance );
8550 int nDots = rIn.GetDotCount();
8551 int nDotLen = rIn.GetDotLen();
8552 for( int n = 0; n < nDots; n++ )
8554 rOut.m_aDashArray.push_back( nDotLen );
8555 rOut.m_aDashArray.push_back( nDistance );
8559 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
8561 MARK( "drawPolyLine with ExtLineInfo" );
8563 updateGraphicsState();
8565 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8566 return;
8568 if( rInfo.m_fTransparency >= 1.0 )
8569 return;
8571 if( rInfo.m_fTransparency != 0.0 )
8572 beginTransparencyGroup();
8574 OStringBuffer aLine;
8575 aLine.append( "q " );
8576 m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
8577 aLine.append( " w" );
8578 if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader
8580 switch( rInfo.m_eCap )
8582 default:
8583 case PDFWriter::capButt: aLine.append( " 0 J" );break;
8584 case PDFWriter::capRound: aLine.append( " 1 J" );break;
8585 case PDFWriter::capSquare: aLine.append( " 2 J" );break;
8587 switch( rInfo.m_eJoin )
8589 default:
8590 case PDFWriter::joinMiter:
8592 double fLimit = rInfo.m_fMiterLimit;
8593 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
8594 fLimit = fLimit / rInfo.m_fLineWidth;
8595 if( fLimit < 1.0 )
8596 fLimit = 1.0;
8597 aLine.append( " 0 j " );
8598 appendDouble( fLimit, aLine );
8599 aLine.append( " M" );
8601 break;
8602 case PDFWriter::joinRound: aLine.append( " 1 j" );break;
8603 case PDFWriter::joinBevel: aLine.append( " 2 j" );break;
8605 if( rInfo.m_aDashArray.size() > 0 )
8607 aLine.append( " [ " );
8608 for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
8609 it != rInfo.m_aDashArray.end(); ++it )
8611 m_aPages.back().appendMappedLength( *it, aLine );
8612 aLine.append( ' ' );
8614 aLine.append( "] 0 d" );
8616 aLine.append( "\n" );
8617 writeBuffer( aLine.getStr(), aLine.getLength() );
8618 drawPolyLine( rPoly );
8620 else
8622 basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
8623 basegfx::B2DPolyPolygon aPolyPoly;
8625 basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
8627 // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
8628 // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
8629 // this line needs to be removed and the loop below adapted accordingly
8630 aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
8632 const sal_uInt32 nPolygonCount(aPolyPoly.count());
8634 for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
8636 aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
8637 aPoly = aPolyPoly.getB2DPolygon( nPoly );
8638 const sal_uInt32 nPointCount(aPoly.count());
8640 if(nPointCount)
8642 const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
8643 basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
8645 for(sal_uInt32 a(0); a < nEdgeCount; a++)
8647 if( a > 0 )
8648 aLine.append( " " );
8649 const sal_uInt32 nNextIndex((a + 1) % nPointCount);
8650 const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
8652 m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
8653 FRound(aCurrent.getY()) ),
8654 aLine );
8655 aLine.append( " m " );
8656 m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
8657 FRound(aNext.getY()) ),
8658 aLine );
8659 aLine.append( " l" );
8661 // prepare next edge
8662 aCurrent = aNext;
8666 aLine.append( " S " );
8667 writeBuffer( aLine.getStr(), aLine.getLength() );
8669 writeBuffer( "Q\n", 2 );
8671 if( rInfo.m_fTransparency != 0.0 )
8673 // FIXME: actually this may be incorrect with bezier polygons
8674 Rectangle aBoundRect( rPoly.GetBoundRect() );
8675 // avoid clipping with thick lines
8676 if( rInfo.m_fLineWidth > 0.0 )
8678 sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
8679 aBoundRect.Top() -= nLW;
8680 aBoundRect.Left() -= nLW;
8681 aBoundRect.Right() += nLW;
8682 aBoundRect.Bottom() += nLW;
8684 endTransparencyGroup( aBoundRect, (USHORT)(100.0*rInfo.m_fTransparency) );
8688 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
8690 MARK( "drawPixel" );
8692 Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
8694 if( aColor == Color( COL_TRANSPARENT ) )
8695 return;
8697 // pixels are drawn in line color, so have to set
8698 // the nonstroking color to line color
8699 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
8700 setFillColor( aColor );
8702 updateGraphicsState();
8704 OStringBuffer aLine( 20 );
8705 m_aPages.back().appendPoint( rPoint, aLine );
8706 aLine.append( ' ' );
8707 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine );
8708 aLine.append( ' ' );
8709 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine );
8710 aLine.append( " re f\n" );
8711 writeBuffer( aLine.getStr(), aLine.getLength() );
8713 setFillColor( aOldFillColor );
8716 void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors )
8718 MARK( "drawPixel with Polygon" );
8720 updateGraphicsState();
8722 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors )
8723 return;
8725 USHORT nPoints = rPoints.GetSize();
8726 OStringBuffer aLine( nPoints*40 );
8727 aLine.append( "q " );
8728 if( ! pColors )
8730 appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine );
8731 aLine.append( ' ' );
8734 OStringBuffer aPixel(32);
8735 aPixel.append( ' ' );
8736 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel );
8737 aPixel.append( ' ' );
8738 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel );
8739 OString aPixelStr = aPixel.makeStringAndClear();
8740 for( USHORT i = 0; i < nPoints; i++ )
8742 if( pColors )
8744 if( pColors[i] == Color( COL_TRANSPARENT ) )
8745 continue;
8747 appendNonStrokingColor( pColors[i], aLine );
8748 aLine.append( ' ' );
8750 m_aPages.back().appendPoint( rPoints[i], aLine );
8751 aLine.append( aPixelStr );
8752 aLine.append( " re f\n" );
8754 aLine.append( "Q\n" );
8755 writeBuffer( aLine.getStr(), aLine.getLength() );
8758 class AccessReleaser
8760 BitmapReadAccess* m_pAccess;
8761 public:
8762 AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){}
8763 ~AccessReleaser() { delete m_pAccess; }
8766 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
8768 CHECK_RETURN( updateObject( rObject.m_nObject ) );
8770 bool bFlateFilter = compressStream( rObject.m_pContentStream );
8771 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
8772 ULONG nSize = rObject.m_pContentStream->Tell();
8773 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
8774 #if OSL_DEBUG_LEVEL > 1
8776 OStringBuffer aLine( " PDFWriterImpl::writeTransparentObject" );
8777 emitComment( aLine.getStr() );
8779 #endif
8780 OStringBuffer aLine( 512 );
8781 CHECK_RETURN( updateObject( rObject.m_nObject ) );
8782 aLine.append( rObject.m_nObject );
8783 aLine.append( " 0 obj\n"
8784 "<</Type/XObject\n"
8785 "/Subtype/Form\n"
8786 "/BBox[ " );
8787 appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
8788 aLine.append( ' ' );
8789 appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
8790 aLine.append( ' ' );
8791 appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
8792 aLine.append( ' ' );
8793 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
8794 aLine.append( " ]\n" );
8795 /* #i42884# the PDF reference recommends that each Form XObject
8796 * should have a resource dict; alas if that is the same object
8797 * as the one of the page it triggers an endless recursion in
8798 * acroread 5 (6 and up have that fixed). Since we have only one
8799 * resource dict anyway, let's use the one from the page by NOT
8800 * emitting a Resources entry.
8802 #if 0
8803 aLine.append( " /Resources " );
8804 aLine.append( getResourceDictObj() );
8805 aLine.append( " 0 R\n" );
8806 #endif
8808 aLine.append( "/Length " );
8809 aLine.append( (sal_Int32)(nSize) );
8810 aLine.append( "\n" );
8811 if( bFlateFilter )
8812 aLine.append( "/Filter/FlateDecode\n" );
8813 aLine.append( ">>\n"
8814 "stream\n" );
8815 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8816 checkAndEnableStreamEncryption( rObject.m_nObject );
8817 CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
8818 disableStreamEncryption();
8819 aLine.setLength( 0 );
8820 aLine.append( "\n"
8821 "endstream\n"
8822 "endobj\n\n" );
8823 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8825 // write ExtGState dict for this XObject
8826 aLine.setLength( 0 );
8827 aLine.append( rObject.m_nExtGStateObject );
8828 aLine.append( " 0 obj\n"
8829 "<<" );
8830 if( ! rObject.m_pSoftMaskStream )
8832 //i59651
8833 if( m_bIsPDF_A1 )
8835 aLine.append( "/CA 1.0/ca 1.0" );
8836 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
8838 else
8840 aLine.append( "/CA " );
8841 appendDouble( rObject.m_fAlpha, aLine );
8842 aLine.append( "\n"
8843 " /ca " );
8844 appendDouble( rObject.m_fAlpha, aLine );
8846 aLine.append( "\n" );
8848 else
8850 if( m_bIsPDF_A1 )
8852 aLine.append( "/SMask/None" );
8853 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
8855 else
8857 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
8858 sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
8859 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
8860 sal_Int32 nMaskObject = createObject();
8861 aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
8862 aLine.append( nMaskObject );
8863 aLine.append( " 0 R>>\n" );
8865 OStringBuffer aMask;
8866 aMask.append( nMaskObject );
8867 aMask.append( " 0 obj\n"
8868 "<</Type/XObject\n"
8869 "/Subtype/Form\n"
8870 "/BBox[" );
8871 appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
8872 aMask.append( ' ' );
8873 appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
8874 aMask.append( ' ' );
8875 appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
8876 aMask.append( ' ' );
8877 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
8878 aMask.append( "]\n" );
8880 /* #i42884# see above */
8881 #if 0
8882 aLine.append( "/Resources " );
8883 aMask.append( getResourceDictObj() );
8884 aMask.append( " 0 R\n" );
8885 #endif
8887 aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
8888 aMask.append( "/Length " );
8889 aMask.append( nMaskSize );
8890 aMask.append( ">>\n"
8891 "stream\n" );
8892 CHECK_RETURN( updateObject( nMaskObject ) );
8893 checkAndEnableStreamEncryption( nMaskObject );
8894 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
8895 CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
8896 disableStreamEncryption();
8897 aMask.setLength( 0 );
8898 aMask.append( "\nendstream\n"
8899 "endobj\n\n" );
8900 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
8903 aLine.append( ">>\n"
8904 "endobj\n\n" );
8905 CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) );
8906 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8908 return true;
8911 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
8913 sal_Int32 nFunctionObject = createObject();
8914 CHECK_RETURN( updateObject( nFunctionObject ) );
8916 OutputDevice* pRefDevice = getReferenceDevice();
8917 pRefDevice->Push( PUSH_ALL );
8918 if( rObject.m_aSize.Width() > pRefDevice->GetOutputSizePixel().Width() )
8919 rObject.m_aSize.Width() = pRefDevice->GetOutputSizePixel().Width();
8920 if( rObject.m_aSize.Height() > pRefDevice->GetOutputSizePixel().Height() )
8921 rObject.m_aSize.Height() = pRefDevice->GetOutputSizePixel().Height();
8922 pRefDevice->SetMapMode( MapMode( MAP_PIXEL ) );
8923 pRefDevice->DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
8925 Bitmap aSample = pRefDevice->GetBitmap( Point( 0, 0 ), rObject.m_aSize );
8926 BitmapReadAccess* pAccess = aSample.AcquireReadAccess();
8927 AccessReleaser aReleaser( pAccess );
8929 Size aSize = aSample.GetSizePixel();
8931 sal_Int32 nStreamLengthObject = createObject();
8932 #if OSL_DEBUG_LEVEL > 1
8934 OStringBuffer aLine( " PDFWriterImpl::writeGradientFunction" );
8935 emitComment( aLine.getStr() );
8937 #endif
8938 OStringBuffer aLine( 120 );
8939 aLine.append( nFunctionObject );
8940 aLine.append( " 0 obj\n"
8941 "<</FunctionType 0\n"
8942 "/Domain[ 0 1 0 1 ]\n"
8943 "/Size[ " );
8944 aLine.append( (sal_Int32)aSize.Width() );
8945 aLine.append( ' ' );
8946 aLine.append( (sal_Int32)aSize.Height() );
8947 aLine.append( " ]\n"
8948 "/BitsPerSample 8\n"
8949 "/Range[ 0 1 0 1 0 1 ]\n"
8950 "/Length " );
8951 aLine.append( nStreamLengthObject );
8952 aLine.append( " 0 R\n"
8953 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
8954 "/Filter/FlateDecode"
8955 #endif
8956 ">>\n"
8957 "stream\n" );
8958 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8960 sal_uInt64 nStartStreamPos = 0;
8961 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) );
8963 checkAndEnableStreamEncryption( nFunctionObject );
8964 beginCompression();
8965 for( int y = 0; y < aSize.Height(); y++ )
8967 for( int x = 0; x < aSize.Width(); x++ )
8969 sal_uInt8 aCol[3];
8970 BitmapColor aColor = pAccess->GetColor( y, x );
8971 aCol[0] = aColor.GetRed();
8972 aCol[1] = aColor.GetGreen();
8973 aCol[2] = aColor.GetBlue();
8974 CHECK_RETURN( writeBuffer( aCol, 3 ) );
8977 endCompression();
8978 disableStreamEncryption();
8980 sal_uInt64 nEndStreamPos = 0;
8981 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) );
8983 aLine.setLength( 0 );
8984 aLine.append( "\nendstream\nendobj\n\n" );
8985 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8987 // write stream length
8988 CHECK_RETURN( updateObject( nStreamLengthObject ) );
8989 aLine.setLength( 0 );
8990 aLine.append( nStreamLengthObject );
8991 aLine.append( " 0 obj\n" );
8992 aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
8993 aLine.append( "\nendobj\n\n" );
8994 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8996 CHECK_RETURN( updateObject( rObject.m_nObject ) );
8997 aLine.setLength( 0 );
8998 aLine.append( rObject.m_nObject );
8999 aLine.append( " 0 obj\n"
9000 "<</ShadingType 1\n"
9001 "/ColorSpace/DeviceRGB\n"
9002 "/AntiAlias true\n"
9003 "/Domain[ 0 1 0 1 ]\n"
9004 "/Matrix[ " );
9005 aLine.append( (sal_Int32)aSize.Width() );
9006 aLine.append( " 0 0 " );
9007 aLine.append( (sal_Int32)aSize.Height() );
9008 aLine.append( " 0 0 ]\n"
9009 "/Function " );
9010 aLine.append( nFunctionObject );
9011 aLine.append( " 0 R\n"
9012 ">>\n"
9013 "endobj\n\n" );
9014 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9016 pRefDevice->Pop();
9018 return true;
9021 bool PDFWriterImpl::writeJPG( JPGEmit& rObject )
9023 CHECK_RETURN( rObject.m_pStream );
9024 CHECK_RETURN( updateObject( rObject.m_nObject ) );
9026 sal_Int32 nLength = 0;
9027 rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
9028 nLength = rObject.m_pStream->Tell();
9029 rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
9031 sal_Int32 nMaskObject = 0;
9032 if( !!rObject.m_aMask )
9034 if( rObject.m_aMask.GetBitCount() == 1 ||
9035 ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651
9038 nMaskObject = createObject();
9040 else if( m_bIsPDF_A1 )
9041 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9042 else if( m_aContext.Version < PDFWriter::PDF_1_4 )
9043 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
9046 #if OSL_DEBUG_LEVEL > 1
9048 OStringBuffer aLine( " PDFWriterImpl::writeJPG" );
9049 emitComment( aLine.getStr() );
9051 #endif
9053 OStringBuffer aLine(200);
9054 aLine.append( rObject.m_nObject );
9055 aLine.append( " 0 obj\n"
9056 "<</Type/XObject/Subtype/Image/Width " );
9057 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
9058 aLine.append( " /Height " );
9059 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
9060 aLine.append( " /BitsPerComponent 8 " );
9061 if( rObject.m_bTrueColor )
9062 aLine.append( "/ColorSpace/DeviceRGB" );
9063 else
9064 aLine.append( "/ColorSpace/DeviceGray" );
9065 aLine.append( "/Filter/DCTDecode/Length " );
9066 aLine.append( nLength );
9067 if( nMaskObject )
9069 aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
9070 aLine.append( nMaskObject );
9071 aLine.append( " 0 R " );
9073 aLine.append( ">>\nstream\n" );
9074 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9076 checkAndEnableStreamEncryption( rObject.m_nObject );
9077 CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
9078 disableStreamEncryption();
9080 aLine.setLength( 0 );
9081 aLine.append( "\nendstream\nendobj\n\n" );
9082 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9084 if( nMaskObject )
9086 BitmapEmit aEmit;
9087 aEmit.m_nObject = nMaskObject;
9088 if( rObject.m_aMask.GetBitCount() == 1 )
9089 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
9090 else if( rObject.m_aMask.GetBitCount() == 8 )
9091 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
9092 writeBitmapObject( aEmit, true );
9095 return true;
9098 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
9100 CHECK_RETURN( updateObject( rObject.m_nObject ) );
9102 Bitmap aBitmap;
9103 Color aTransparentColor( COL_TRANSPARENT );
9104 bool bWriteMask = false;
9105 if( ! bMask )
9107 aBitmap = rObject.m_aBitmap.GetBitmap();
9108 if( rObject.m_aBitmap.IsAlpha() )
9110 if( m_aContext.Version >= PDFWriter::PDF_1_4 )
9111 bWriteMask = true;
9112 // else draw without alpha channel
9114 else
9116 switch( rObject.m_aBitmap.GetTransparentType() )
9118 case TRANSPARENT_NONE:
9119 // comes from drawMask function
9120 if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask )
9121 bMask = true;
9122 break;
9123 case TRANSPARENT_COLOR:
9124 aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
9125 break;
9126 case TRANSPARENT_BITMAP:
9127 bWriteMask = true;
9128 break;
9132 else
9134 if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
9136 aBitmap = rObject.m_aBitmap.GetMask();
9137 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
9138 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
9140 else if( aBitmap.GetBitCount() != 8 )
9142 aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap();
9143 aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
9144 DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" );
9148 BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();
9149 AccessReleaser aReleaser( pAccess );
9151 bool bTrueColor;
9152 sal_Int32 nBitsPerComponent;
9153 switch( aBitmap.GetBitCount() )
9155 case 1:
9156 case 2:
9157 case 4:
9158 case 8:
9159 bTrueColor = false;
9160 nBitsPerComponent = aBitmap.GetBitCount();
9161 break;
9162 default:
9163 bTrueColor = true;
9164 nBitsPerComponent = 8;
9165 break;
9168 sal_Int32 nStreamLengthObject = createObject();
9169 sal_Int32 nMaskObject = 0;
9171 #if OSL_DEBUG_LEVEL > 1
9173 OStringBuffer aLine( " PDFWriterImpl::writeBitmapObject" );
9174 emitComment( aLine.getStr() );
9176 #endif
9177 OStringBuffer aLine(1024);
9178 aLine.append( rObject.m_nObject );
9179 aLine.append( " 0 obj\n"
9180 "<</Type/XObject/Subtype/Image/Width " );
9181 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9182 aLine.append( " /Height " );
9183 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
9184 aLine.append( " /BitsPerComponent " );
9185 aLine.append( nBitsPerComponent );
9186 aLine.append( " /Length " );
9187 aLine.append( nStreamLengthObject );
9188 aLine.append( " 0 R\n" );
9189 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9190 aLine.append( "/Filter/FlateDecode" );
9191 #endif
9192 if( ! bMask )
9194 aLine.append( "/ColorSpace" );
9195 if( bTrueColor )
9196 aLine.append( "/DeviceRGB\n" );
9197 else if( aBitmap.HasGreyPalette() )
9199 aLine.append( "/DeviceGray\n" );
9200 if( aBitmap.GetBitCount() == 1 )
9202 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
9203 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9204 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9205 if( nBlackIndex == 1 )
9206 aLine.append( "/Decode[1 0]\n" );
9209 else
9211 aLine.append( "[ /Indexed/DeviceRGB " );
9212 aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
9213 aLine.append( "\n<" );
9214 if( m_aContext.Encrypt )
9216 enableStringEncryption( rObject.m_nObject );
9217 //check encryption buffer size
9218 if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
9220 int nChar = 0;
9221 //fill the encryption buffer
9222 for( USHORT i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9224 const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9225 m_pEncryptionBuffer[nChar++] = rColor.GetRed();
9226 m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
9227 m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
9229 //encrypt the colorspace lookup table
9230 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
9231 //now queue the data for output
9232 nChar = 0;
9233 for( USHORT i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9235 appendHex(m_pEncryptionBuffer[nChar++], aLine );
9236 appendHex(m_pEncryptionBuffer[nChar++], aLine );
9237 appendHex(m_pEncryptionBuffer[nChar++], aLine );
9241 else //no encryption requested (PDF/A-1a program flow drops here)
9243 for( USHORT i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9245 const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9246 appendHex( rColor.GetRed(), aLine );
9247 appendHex( rColor.GetGreen(), aLine );
9248 appendHex( rColor.GetBlue(), aLine );
9251 aLine.append( ">\n]\n" );
9254 else
9256 if( aBitmap.GetBitCount() == 1 )
9258 aLine.append( " /ImageMask true\n" );
9259 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9260 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9261 if( nBlackIndex )
9262 aLine.append( "/Decode[ 1 0 ]\n" );
9263 else
9264 aLine.append( "/Decode[ 0 1 ]\n" );
9266 else if( aBitmap.GetBitCount() == 8 )
9268 aLine.append( "/ColorSpace/DeviceGray\n"
9269 "/Decode [ 1 0 ]\n" );
9273 if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651
9275 if( bWriteMask )
9277 nMaskObject = createObject();
9278 if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 )
9279 aLine.append( "/SMask " );
9280 else
9281 aLine.append( "/Mask " );
9282 aLine.append( nMaskObject );
9283 aLine.append( " 0 R\n" );
9285 else if( aTransparentColor != Color( COL_TRANSPARENT ) )
9287 aLine.append( "/Mask[ " );
9288 if( bTrueColor )
9290 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9291 aLine.append( ' ' );
9292 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9293 aLine.append( ' ' );
9294 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9295 aLine.append( ' ' );
9296 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9297 aLine.append( ' ' );
9298 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9299 aLine.append( ' ' );
9300 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9302 else
9304 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
9305 aLine.append( nIndex );
9307 aLine.append( " ]\n" );
9310 else if( m_bIsPDF_A1 )
9311 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9313 aLine.append( ">>\n"
9314 "stream\n" );
9315 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9316 sal_uInt64 nStartPos = 0;
9317 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) );
9319 checkAndEnableStreamEncryption( rObject.m_nObject );
9320 beginCompression();
9321 if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
9323 const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U );
9325 for( int i = 0; i < pAccess->Height(); i++ )
9327 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
9330 else
9332 const int nScanLineBytes = pAccess->Width()*3;
9333 sal_uInt8 *pCol = (sal_uInt8*)rtl_allocateMemory( nScanLineBytes );
9334 for( int y = 0; y < pAccess->Height(); y++ )
9336 for( int x = 0; x < pAccess->Width(); x++ )
9338 BitmapColor aColor = pAccess->GetColor( y, x );
9339 pCol[3*x+0] = aColor.GetRed();
9340 pCol[3*x+1] = aColor.GetGreen();
9341 pCol[3*x+2] = aColor.GetBlue();
9343 CHECK_RETURN( writeBuffer( pCol, nScanLineBytes ) );
9345 rtl_freeMemory( pCol );
9347 endCompression();
9348 disableStreamEncryption();
9350 sal_uInt64 nEndPos = 0;
9351 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) );
9352 aLine.setLength( 0 );
9353 aLine.append( "\nendstream\nendobj\n\n" );
9354 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9355 CHECK_RETURN( updateObject( nStreamLengthObject ) );
9356 aLine.setLength( 0 );
9357 aLine.append( nStreamLengthObject );
9358 aLine.append( " 0 obj\n" );
9359 aLine.append( (sal_Int64)(nEndPos-nStartPos) );
9360 aLine.append( "\nendobj\n\n" );
9361 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9363 if( nMaskObject )
9365 BitmapEmit aEmit;
9366 aEmit.m_nObject = nMaskObject;
9367 aEmit.m_aBitmap = rObject.m_aBitmap;
9368 return writeBitmapObject( aEmit, true );
9371 return true;
9374 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask )
9376 MARK( "drawJPGBitmap" );
9378 OStringBuffer aLine( 80 );
9379 updateGraphicsState();
9381 // #i40055# sanity check
9382 if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
9383 return;
9384 if( ! (rSizePixel.Width() && rSizePixel.Height()) )
9385 return;
9387 SvMemoryStream* pStream = new SvMemoryStream;
9388 rDCTData.Seek( 0 );
9389 *pStream << rDCTData;
9390 pStream->Seek( STREAM_SEEK_TO_END );
9392 BitmapID aID;
9393 aID.m_aPixelSize = rSizePixel;
9394 aID.m_nSize = pStream->Tell();
9395 pStream->Seek( STREAM_SEEK_TO_BEGIN );
9396 aID.m_nChecksum = rtl_crc32( 0, pStream->GetData(), aID.m_nSize );
9397 if( ! rMask.IsEmpty() )
9398 aID.m_nMaskChecksum = rMask.GetChecksum();
9400 std::list< JPGEmit >::const_iterator it;
9401 for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
9403 if( it == m_aJPGs.end() )
9405 m_aJPGs.push_front( JPGEmit() );
9406 JPGEmit& rEmit = m_aJPGs.front();
9407 rEmit.m_nObject = createObject();
9408 rEmit.m_aID = aID;
9409 rEmit.m_pStream = pStream;
9410 rEmit.m_bTrueColor = bIsTrueColor;
9411 if( !! rMask && rMask.GetSizePixel() == rSizePixel )
9412 rEmit.m_aMask = rMask;
9414 it = m_aJPGs.begin();
9416 else
9417 delete pStream;
9419 aLine.append( "q " );
9420 sal_Int32 nCheckWidth = 0;
9421 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth );
9422 aLine.append( " 0 0 " );
9423 sal_Int32 nCheckHeight = 0;
9424 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight );
9425 aLine.append( ' ' );
9426 m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
9427 aLine.append( " cm\n/Im" );
9428 aLine.append( it->m_nObject );
9429 aLine.append( " Do Q\n" );
9430 if( nCheckWidth == 0 || nCheckHeight == 0 )
9432 // #i97512# avoid invalid current matrix
9433 aLine.setLength( 0 );
9434 aLine.append( "\n%jpeg image /Im" );
9435 aLine.append( it->m_nObject );
9436 aLine.append( " scaled to zero size, omitted\n" );
9438 writeBuffer( aLine.getStr(), aLine.getLength() );
9440 OStringBuffer aObjName( 16 );
9441 aObjName.append( "Im" );
9442 aObjName.append( it->m_nObject );
9443 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
9447 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
9449 OStringBuffer aLine( 80 );
9450 updateGraphicsState();
9452 aLine.append( "q " );
9453 if( rFillColor != Color( COL_TRANSPARENT ) )
9455 appendNonStrokingColor( rFillColor, aLine );
9456 aLine.append( ' ' );
9458 sal_Int32 nCheckWidth = 0;
9459 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth );
9460 aLine.append( " 0 0 " );
9461 sal_Int32 nCheckHeight = 0;
9462 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight );
9463 aLine.append( ' ' );
9464 m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
9465 aLine.append( " cm\n/Im" );
9466 aLine.append( rBitmap.m_nObject );
9467 aLine.append( " Do Q\n" );
9468 if( nCheckWidth == 0 || nCheckHeight == 0 )
9470 // #i97512# avoid invalid current matrix
9471 aLine.setLength( 0 );
9472 aLine.append( "\n%bitmap image /Im" );
9473 aLine.append( rBitmap.m_nObject );
9474 aLine.append( " scaled to zero size, omitted\n" );
9476 writeBuffer( aLine.getStr(), aLine.getLength() );
9479 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& rBitmap, bool bDrawMask )
9481 BitmapID aID;
9482 aID.m_aPixelSize = rBitmap.GetSizePixel();
9483 aID.m_nSize = rBitmap.GetBitCount();
9484 aID.m_nChecksum = rBitmap.GetBitmap().GetChecksum();
9485 aID.m_nMaskChecksum = 0;
9486 if( rBitmap.IsAlpha() )
9487 aID.m_nMaskChecksum = rBitmap.GetAlpha().GetChecksum();
9488 else
9490 Bitmap aMask = rBitmap.GetMask();
9491 if( ! aMask.IsEmpty() )
9492 aID.m_nMaskChecksum = aMask.GetChecksum();
9494 std::list< BitmapEmit >::const_iterator it;
9495 for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
9497 if( aID == it->m_aID )
9498 break;
9500 if( it == m_aBitmaps.end() )
9502 m_aBitmaps.push_front( BitmapEmit() );
9503 m_aBitmaps.front().m_aID = aID;
9504 m_aBitmaps.front().m_aBitmap = rBitmap;
9505 m_aBitmaps.front().m_nObject = createObject();
9506 m_aBitmaps.front().m_bDrawMask = bDrawMask;
9507 it = m_aBitmaps.begin();
9510 OStringBuffer aObjName( 16 );
9511 aObjName.append( "Im" );
9512 aObjName.append( it->m_nObject );
9513 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
9515 return *it;
9518 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap )
9520 MARK( "drawBitmap (Bitmap)" );
9522 // #i40055# sanity check
9523 if( ! (rDestSize.Width() && rDestSize.Height()) )
9524 return;
9526 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) );
9527 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
9530 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
9532 MARK( "drawBitmap (BitmapEx)" );
9534 // #i40055# sanity check
9535 if( ! (rDestSize.Width() && rDestSize.Height()) )
9536 return;
9538 const BitmapEmit& rEmit = createBitmapEmit( rBitmap );
9539 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
9542 void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor )
9544 MARK( "drawMask" );
9546 // #i40055# sanity check
9547 if( ! (rDestSize.Width() && rDestSize.Height()) )
9548 return;
9550 Bitmap aBitmap( rBitmap );
9551 if( aBitmap.GetBitCount() > 1 )
9552 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
9553 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
9555 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true );
9556 drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor );
9559 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
9561 Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
9562 MapMode( MAP_POINT ),
9563 getReferenceDevice(),
9564 rSize ) );
9565 // check if we already have this gradient
9566 std::list<GradientEmit>::iterator it;
9567 for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
9569 if( it->m_aGradient == rGradient )
9571 if( it->m_aSize.Width() < aPtSize.Width() )
9572 it->m_aSize.Width() = aPtSize.Width();
9573 if( it->m_aSize.Height() <= aPtSize.Height() )
9574 it->m_aSize.Height() = aPtSize.Height();
9575 break;
9578 if( it == m_aGradients.end() )
9580 m_aGradients.push_front( GradientEmit() );
9581 m_aGradients.front().m_aGradient = rGradient;
9582 m_aGradients.front().m_nObject = createObject();
9583 m_aGradients.front().m_aSize = aPtSize;
9584 it = m_aGradients.begin();
9587 OStringBuffer aObjName( 16 );
9588 aObjName.append( 'P' );
9589 aObjName.append( it->m_nObject );
9590 pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
9592 return it->m_nObject;
9595 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient )
9597 MARK( "drawGradient (Rectangle)" );
9599 if( m_aContext.Version == PDFWriter::PDF_1_2 )
9601 drawRectangle( rRect );
9602 return;
9605 sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
9607 Point aTranslate( rRect.BottomLeft() );
9608 aTranslate += Point( 0, 1 );
9610 updateGraphicsState();
9612 OStringBuffer aLine( 80 );
9613 aLine.append( "q 1 0 0 1 " );
9614 m_aPages.back().appendPoint( aTranslate, aLine );
9615 aLine.append( " cm " );
9616 // if a stroke is appended reset the clip region before stroke
9617 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9618 aLine.append( "q " );
9619 aLine.append( "0 0 " );
9620 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
9621 aLine.append( ' ' );
9622 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
9623 aLine.append( " re W n\n" );
9625 aLine.append( "/P" );
9626 aLine.append( nGradient );
9627 aLine.append( " sh " );
9628 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9630 aLine.append( "Q 0 0 " );
9631 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
9632 aLine.append( ' ' );
9633 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
9634 aLine.append( " re S " );
9636 aLine.append( "Q\n" );
9637 writeBuffer( aLine.getStr(), aLine.getLength() );
9640 void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
9642 MARK( "drawGradient (PolyPolygon)" );
9644 if( m_aContext.Version == PDFWriter::PDF_1_2 )
9646 drawPolyPolygon( rPolyPoly );
9647 return;
9650 sal_Int32 nGradient = createGradient( rGradient, rPolyPoly.GetBoundRect().GetSize() );
9652 updateGraphicsState();
9654 Rectangle aBoundRect = rPolyPoly.GetBoundRect();
9655 Point aTranslate = aBoundRect.BottomLeft() + Point( 0, 1 );
9656 int nPolygons = rPolyPoly.Count();
9658 OStringBuffer aLine( 80*nPolygons );
9659 aLine.append( "q " );
9660 // set PolyPolygon as clip path
9661 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
9662 aLine.append( "W* n\n" );
9663 aLine.append( "1 0 0 1 " );
9664 m_aPages.back().appendPoint( aTranslate, aLine );
9665 aLine.append( " cm\n" );
9666 aLine.append( "/P" );
9667 aLine.append( nGradient );
9668 aLine.append( " sh Q\n" );
9669 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9671 // and draw the surrounding path
9672 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
9673 aLine.append( "S\n" );
9675 writeBuffer( aLine.getStr(), aLine.getLength() );
9678 void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
9680 MARK( "drawHatch" );
9682 updateGraphicsState();
9684 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
9685 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
9686 return;
9687 if( rPolyPoly.Count() )
9689 PolyPolygon aPolyPoly( rPolyPoly );
9691 aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
9692 push( PUSH_LINECOLOR );
9693 setLineColor( rHatch.GetColor() );
9694 getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, FALSE );
9695 pop();
9699 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall )
9701 MARK( "drawWallpaper" );
9703 bool bDrawColor = false;
9704 bool bDrawGradient = false;
9705 bool bDrawBitmap = false;
9707 BitmapEx aBitmap;
9708 Point aBmpPos = rRect.TopLeft();
9709 Size aBmpSize;
9710 if( rWall.IsBitmap() )
9712 aBitmap = rWall.GetBitmap();
9713 aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
9714 getMapMode(),
9715 getReferenceDevice(),
9716 aBitmap.GetPrefSize() );
9717 Rectangle aRect( rRect );
9718 if( rWall.IsRect() )
9720 aRect = rWall.GetRect();
9721 aBmpPos = aRect.TopLeft();
9722 aBmpSize = aRect.GetSize();
9724 if( rWall.GetStyle() != WALLPAPER_SCALE )
9726 if( rWall.GetStyle() != WALLPAPER_TILE )
9728 bDrawBitmap = true;
9729 if( rWall.IsGradient() )
9730 bDrawGradient = true;
9731 else
9732 bDrawColor = true;
9733 switch( rWall.GetStyle() )
9735 case WALLPAPER_TOPLEFT:
9736 break;
9737 case WALLPAPER_TOP:
9738 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
9739 break;
9740 case WALLPAPER_LEFT:
9741 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
9742 break;
9743 case WALLPAPER_TOPRIGHT:
9744 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
9745 break;
9746 case WALLPAPER_CENTER:
9747 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
9748 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
9749 break;
9750 case WALLPAPER_RIGHT:
9751 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
9752 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
9753 break;
9754 case WALLPAPER_BOTTOMLEFT:
9755 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
9756 break;
9757 case WALLPAPER_BOTTOM:
9758 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
9759 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
9760 break;
9761 case WALLPAPER_BOTTOMRIGHT:
9762 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
9763 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
9764 break;
9765 default: ;
9768 else
9770 // push the bitmap
9771 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) );
9773 // convert to page coordinates; this needs to be done here
9774 // since the emit does not know the page anymore
9775 Rectangle aConvertRect( aBmpPos, aBmpSize );
9776 m_aPages.back().convertRect( aConvertRect );
9778 OStringBuffer aNameBuf(16);
9779 aNameBuf.append( "Im" );
9780 aNameBuf.append( rEmit.m_nObject );
9781 OString aImageName( aNameBuf.makeStringAndClear() );
9783 // push the pattern
9784 OStringBuffer aTilingStream( 32 );
9785 appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
9786 aTilingStream.append( " 0 0 " );
9787 appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
9788 aTilingStream.append( " 0 0 cm\n/" );
9789 aTilingStream.append( aImageName );
9790 aTilingStream.append( " Do\n" );
9792 m_aTilings.push_back( TilingEmit() );
9793 m_aTilings.back().m_nObject = createObject();
9794 m_aTilings.back().m_aRectangle = Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
9795 m_aTilings.back().m_pTilingStream = new SvMemoryStream();
9796 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() );
9797 // phase the tiling so wallpaper begins on upper left
9798 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
9799 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
9800 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
9802 updateGraphicsState();
9804 OStringBuffer aObjName( 16 );
9805 aObjName.append( 'P' );
9806 aObjName.append( m_aTilings.back().m_nObject );
9807 OString aPatternName( aObjName.makeStringAndClear() );
9808 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
9810 // fill a rRect with the pattern
9811 OStringBuffer aLine( 100 );
9812 aLine.append( "q /Pattern cs /" );
9813 aLine.append( aPatternName );
9814 aLine.append( " scn " );
9815 m_aPages.back().appendRect( rRect, aLine );
9816 aLine.append( " f Q\n" );
9817 writeBuffer( aLine.getStr(), aLine.getLength() );
9820 else
9822 aBmpPos = aRect.TopLeft();
9823 aBmpSize = aRect.GetSize();
9824 bDrawBitmap = true;
9827 if( aBitmap.IsTransparent() )
9829 if( rWall.IsGradient() )
9830 bDrawGradient = true;
9831 else
9832 bDrawColor = true;
9835 else if( rWall.IsGradient() )
9836 bDrawGradient = true;
9837 else
9838 bDrawColor = true;
9840 if( bDrawGradient )
9842 drawGradient( rRect, rWall.GetGradient() );
9844 if( bDrawColor )
9846 Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
9847 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
9848 setLineColor( Color( COL_TRANSPARENT ) );
9849 setFillColor( rWall.GetColor() );
9850 drawRectangle( rRect );
9851 setLineColor( aOldLineColor );
9852 setFillColor( aOldFillColor );
9854 if( bDrawBitmap )
9856 // set temporary clip region since aBmpPos and aBmpSize
9857 // may be outside rRect
9858 OStringBuffer aLine( 20 );
9859 aLine.append( "q " );
9860 m_aPages.back().appendRect( rRect, aLine );
9861 aLine.append( " W n\n" );
9862 writeBuffer( aLine.getStr(), aLine.getLength() );
9863 drawBitmap( aBmpPos, aBmpSize, aBitmap );
9864 writeBuffer( "Q\n", 2 );
9868 void PDFWriterImpl::beginPattern( const Rectangle& rCellRect )
9870 beginRedirect( new SvMemoryStream(), rCellRect );
9873 sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform )
9875 Rectangle aConvertRect( getRedirectTargetRect() );
9876 DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" );
9878 // get scaling between current mapmode and PDF output
9879 Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) );
9880 double fSX = (double(aScaling.Width()) / 10000.0);
9881 double fSY = (double(aScaling.Height()) / 10000.0);
9883 // transform translation part of matrix
9884 Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] );
9885 aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation );
9887 sal_Int32 nTilingId = m_aTilings.size();
9888 m_aTilings.push_back( TilingEmit() );
9889 TilingEmit& rTile = m_aTilings.back();
9890 rTile.m_nObject = createObject();
9891 rTile.m_aResources = m_aOutputStreams.front().m_aResourceDict;
9892 rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX;
9893 rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY;
9894 rTile.m_aTransform.matrix[2] = aTranslation.Width();
9895 rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX;
9896 rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY;
9897 rTile.m_aTransform.matrix[5] = -aTranslation.Height();
9898 // caution: endRedirect pops the stream, so do this last
9899 rTile.m_pTilingStream = dynamic_cast<SvMemoryStream*>(endRedirect());
9900 // FIXME: bound rect will not work with rotated matrix
9901 rTile.m_aRectangle = Rectangle( Point(0,0), aConvertRect.GetSize() );
9902 rTile.m_aCellSize = aConvertRect.GetSize();
9904 OStringBuffer aObjName( 16 );
9905 aObjName.append( 'P' );
9906 aObjName.append( rTile.m_nObject );
9907 pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject );
9908 return nTilingId;
9911 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill )
9913 if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() )
9914 return;
9916 m_aPages.back().endStream();
9917 sal_Int32 nXObject = createObject();
9918 OStringBuffer aNameBuf( 16 );
9919 aNameBuf.append( "Pol" );
9920 aNameBuf.append( nXObject );
9921 OString aObjName( aNameBuf.makeStringAndClear() );
9922 Rectangle aObjRect;
9923 if( updateObject( nXObject ) )
9925 // get bounding rect of object
9926 PolyPolygon aSubDiv;
9927 rPolyPoly.AdaptiveSubdivide( aSubDiv );
9928 aObjRect = aSubDiv.GetBoundRect();
9929 Rectangle aConvObjRect( aObjRect );
9930 m_aPages.back().convertRect( aConvObjRect );
9932 // move polypolygon to bottom left of page
9933 PolyPolygon aLocalPath( rPolyPoly );
9934 sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72;
9935 sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72;
9936 Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode );
9937 sal_Int32 nXOff = aObjRect.Left();
9938 sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom();
9939 aLocalPath.Move( -nXOff, nYOff );
9941 // prepare XObject's content stream
9942 OStringBuffer aStream( 512 );
9943 aStream.append( "/Pattern cs /P" );
9944 aStream.append( m_aTilings[ nPattern ].m_nObject );
9945 aStream.append( " scn\n" );
9946 m_aPages.back().appendPolyPolygon( aLocalPath, aStream );
9947 aStream.append( bEOFill ? "f*" : "f" );
9948 SvMemoryStream aMemStream( aStream.getLength() );
9949 aMemStream.Write( aStream.getStr(), aStream.getLength() );
9950 bool bDeflate = compressStream( &aMemStream );
9951 aMemStream.Seek( STREAM_SEEK_TO_END );
9952 sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell();
9953 aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
9955 // add new XObject to global resource dict
9956 m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject;
9958 // write XObject
9959 OStringBuffer aLine( 512 );
9960 aLine.append( nXObject );
9961 aLine.append( " 0 obj\n"
9962 "<</Type/XObject/Subtype/Form/BBox[0 0 " );
9963 appendFixedInt( aConvObjRect.GetWidth(), aLine );
9964 aLine.append( ' ' );
9965 appendFixedInt( aConvObjRect.GetHeight(), aLine );
9966 aLine.append( "]/Length " );
9967 aLine.append( nStreamLen );
9968 if( bDeflate )
9969 aLine.append( "/Filter/FlateDecode" );
9970 aLine.append( ">>\n"
9971 "stream\n" );
9972 writeBuffer( aLine.getStr(), aLine.getLength() );
9973 checkAndEnableStreamEncryption( nXObject );
9974 writeBuffer( aMemStream.GetData(), nStreamLen );
9975 disableStreamEncryption();
9976 writeBuffer( "\nendstream\nendobj\n\n", 19 );
9978 m_aPages.back().beginStream();
9979 OStringBuffer aLine( 80 );
9980 aLine.append( "q 1 0 0 1 " );
9981 m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine );
9982 aLine.append( " cm/" );
9983 aLine.append( aObjName );
9984 aLine.append( " Do Q\n" );
9985 writeBuffer( aLine.getStr(), aLine.getLength() );
9988 void PDFWriterImpl::updateGraphicsState()
9990 OStringBuffer aLine( 256 );
9991 GraphicsState& rNewState = m_aGraphicsStack.front();
9992 // first set clip region since it might invalidate everything else
9994 if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) )
9996 rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion;
9998 Region& rNewClip = rNewState.m_aClipRegion;
10000 /* #103137# equality operator is not implemented
10001 * const as API promises but may change Region
10002 * from Polygon to rectangles. Arrrgghh !!!!
10004 Region aLeft = m_aCurrentPDFState.m_aClipRegion;
10005 Region aRight = rNewClip;
10006 if( aLeft != aRight )
10008 if( ! m_aCurrentPDFState.m_aClipRegion.IsEmpty() &&
10009 ! m_aCurrentPDFState.m_aClipRegion.IsNull() )
10011 aLine.append( "Q " );
10012 // invalidate everything but the clip region
10013 m_aCurrentPDFState = GraphicsState();
10014 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion);
10016 if( ! rNewClip.IsEmpty() && ! rNewClip.IsNull() )
10018 // clip region is always stored in private PDF mapmode
10019 MapMode aNewMapMode = rNewState.m_aMapMode;
10020 rNewState.m_aMapMode = m_aMapMode;
10021 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10022 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10024 aLine.append( "q " );
10025 if( rNewClip.HasPolyPolygon() )
10027 m_aPages.back().appendPolyPolygon( rNewClip.GetPolyPolygon(), aLine );
10028 aLine.append( "W* n\n" );
10030 else
10032 // need to clip all rectangles
10033 RegionHandle aHandle = rNewClip.BeginEnumRects();
10034 Rectangle aRect;
10035 while( rNewClip.GetNextEnumRect( aHandle, aRect ) )
10037 m_aPages.back().appendRect( aRect, aLine );
10038 if( aLine.getLength() > 80 )
10040 aLine.append( "\n" );
10041 writeBuffer( aLine.getStr(), aLine.getLength() );
10042 aLine.setLength( 0 );
10044 else
10045 aLine.append( ' ' );
10047 rNewClip.EndEnumRects( aHandle );
10048 aLine.append( "W* n\n" );
10051 rNewState.m_aMapMode = aNewMapMode;
10052 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10053 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10058 if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) )
10060 rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode;
10061 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10064 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) )
10066 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont;
10067 getReferenceDevice()->SetFont( rNewState.m_aFont );
10068 getReferenceDevice()->ImplNewFont();
10071 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) )
10073 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode;
10074 getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
10077 if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) )
10079 rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage;
10080 getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
10083 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) )
10085 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor;
10086 if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
10087 rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
10089 appendStrokingColor( rNewState.m_aLineColor, aLine );
10090 aLine.append( "\n" );
10094 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) )
10096 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor;
10097 if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
10098 rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
10100 appendNonStrokingColor( rNewState.m_aFillColor, aLine );
10101 aLine.append( "\n" );
10105 if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) )
10107 rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent;
10108 if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent )
10110 // TODO: switch extended graphicsstate
10114 // everything is up to date now
10115 m_aCurrentPDFState = m_aGraphicsStack.front();
10116 if( aLine.getLength() )
10117 writeBuffer( aLine.getStr(), aLine.getLength() );
10120 /* #i47544# imitate OutputDevice behaviour:
10121 * if a font with a nontransparent color is set, it overwrites the current
10122 * text color. OTOH setting the text color will overwrite the color of the font.
10124 void PDFWriterImpl::setFont( const Font& rFont )
10126 Color aColor = rFont.GetColor();
10127 if( aColor == Color( COL_TRANSPARENT ) )
10128 aColor = m_aGraphicsStack.front().m_aFont.GetColor();
10129 m_aGraphicsStack.front().m_aFont = rFont;
10130 m_aGraphicsStack.front().m_aFont.SetColor( aColor );
10131 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont;
10134 void PDFWriterImpl::push( sal_uInt16 nFlags )
10136 m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
10137 m_aGraphicsStack.front().m_nFlags = nFlags;
10140 void PDFWriterImpl::pop()
10142 GraphicsState aState = m_aGraphicsStack.front();
10143 m_aGraphicsStack.pop_front();
10144 GraphicsState& rOld = m_aGraphicsStack.front();
10146 // move those parameters back that were not pushed
10147 // in the first place
10148 if( ! (aState.m_nFlags & PUSH_LINECOLOR) )
10149 setLineColor( aState.m_aLineColor );
10150 if( ! (aState.m_nFlags & PUSH_FILLCOLOR) )
10151 setFillColor( aState.m_aFillColor );
10152 if( ! (aState.m_nFlags & PUSH_FONT) )
10153 setFont( aState.m_aFont );
10154 if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) )
10155 setTextColor( aState.m_aFont.GetColor() );
10156 if( ! (aState.m_nFlags & PUSH_MAPMODE) )
10157 setMapMode( aState.m_aMapMode );
10158 if( ! (aState.m_nFlags & PUSH_CLIPREGION) )
10159 // do not use setClipRegion here
10160 // it would convert again assuming the current mapmode
10161 rOld.m_aClipRegion = aState.m_aClipRegion;
10162 if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) )
10163 setTextLineColor( aState.m_aTextLineColor );
10164 if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) )
10165 setOverlineColor( aState.m_aOverlineColor );
10166 if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) )
10167 setTextAlign( aState.m_aFont.GetAlign() );
10168 if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) )
10169 setTextFillColor( aState.m_aFont.GetFillColor() );
10170 if( ! (aState.m_nFlags & PUSH_REFPOINT) )
10172 // what ?
10174 // invalidate graphics state
10175 m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U);
10178 void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
10180 m_aGraphicsStack.front().m_aMapMode = rMapMode;
10181 getReferenceDevice()->SetMapMode( rMapMode );
10182 m_aCurrentPDFState.m_aMapMode = rMapMode;
10185 void PDFWriterImpl::setClipRegion( const Region& rRegion )
10187 Region aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10188 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10189 m_aGraphicsStack.front().m_aClipRegion = aRegion;
10190 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10193 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
10195 Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10196 m_aMapMode,
10197 getReferenceDevice(),
10198 Point( nX, nY ) ) );
10199 aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10200 m_aMapMode,
10201 getReferenceDevice(),
10202 Point() );
10203 m_aGraphicsStack.front().m_aClipRegion.Move( aPoint.X(), aPoint.Y() );
10204 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10207 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect )
10209 Rectangle aRect( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10210 m_aMapMode,
10211 getReferenceDevice(),
10212 rRect ) );
10213 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10214 return m_aGraphicsStack.front().m_aClipRegion.Intersect( aRect );
10218 bool PDFWriterImpl::intersectClipRegion( const Region& rRegion )
10220 Region aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10221 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10222 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10223 return m_aGraphicsStack.front().m_aClipRegion.Intersect( aRegion );
10226 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
10228 if( nPageNr < 0 )
10229 nPageNr = m_nCurrentPage;
10231 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10232 return;
10234 m_aNotes.push_back( PDFNoteEntry() );
10235 m_aNotes.back().m_nObject = createObject();
10236 m_aNotes.back().m_aContents = rNote;
10237 m_aNotes.back().m_aRect = rRect;
10238 // convert to default user space now, since the mapmode may change
10239 m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
10241 // insert note to page's annotation list
10242 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
10245 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr )
10247 if( nPageNr < 0 )
10248 nPageNr = m_nCurrentPage;
10250 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10251 return -1;
10253 sal_Int32 nRet = m_aLinks.size();
10255 m_aLinks.push_back( PDFLink() );
10256 m_aLinks.back().m_nObject = createObject();
10257 m_aLinks.back().m_nPage = nPageNr;
10258 m_aLinks.back().m_aRect = rRect;
10259 // convert to default user space now, since the mapmode may change
10260 m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
10262 // insert link to page's annotation list
10263 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
10265 return nRet;
10268 //--->i56629
10269 sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10271 if( nPageNr < 0 )
10272 nPageNr = m_nCurrentPage;
10274 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10275 return -1;
10277 sal_Int32 nRet = m_aNamedDests.size();
10279 m_aNamedDests.push_back( PDFNamedDest() );
10280 m_aNamedDests.back().m_aDestName = sDestName;
10281 m_aNamedDests.back().m_nPage = nPageNr;
10282 m_aNamedDests.back().m_eType = eType;
10283 m_aNamedDests.back().m_aRect = rRect;
10284 // convert to default user space now, since the mapmode may change
10285 m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
10287 return nRet;
10289 //<---i56629
10291 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10293 if( nPageNr < 0 )
10294 nPageNr = m_nCurrentPage;
10296 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10297 return -1;
10299 sal_Int32 nRet = m_aDests.size();
10301 m_aDests.push_back( PDFDest() );
10302 m_aDests.back().m_nPage = nPageNr;
10303 m_aDests.back().m_eType = eType;
10304 m_aDests.back().m_aRect = rRect;
10305 // convert to default user space now, since the mapmode may change
10306 m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
10308 return nRet;
10311 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
10313 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10314 return -1;
10315 if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
10316 return -2;
10318 m_aLinks[ nLinkId ].m_nDest = nDestId;
10320 return 0;
10323 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
10325 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10326 return -1;
10328 m_aLinks[ nLinkId ].m_nDest = -1;
10330 using namespace ::com::sun::star;
10332 if (!m_xTrans.is())
10334 uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() );
10335 if( xFact.is() )
10337 m_xTrans = uno::Reference < util::XURLTransformer >(
10338 xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY );
10342 util::URL aURL;
10343 aURL.Complete = rURL;
10345 if (m_xTrans.is())
10346 m_xTrans->parseStrict( aURL );
10348 m_aLinks[ nLinkId ].m_aURL = aURL.Complete;
10350 return 0;
10353 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
10355 m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
10358 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
10360 // create new item
10361 sal_Int32 nNewItem = m_aOutline.size();
10362 m_aOutline.push_back( PDFOutlineEntry() );
10364 // set item attributes
10365 setOutlineItemParent( nNewItem, nParent );
10366 setOutlineItemText( nNewItem, rText );
10367 setOutlineItemDest( nNewItem, nDestID );
10369 return nNewItem;
10372 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
10374 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10375 return -1;
10377 int nRet = 0;
10379 if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
10381 nNewParent = 0;
10382 nRet = -2;
10384 // remove item from previous parent
10385 sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID;
10386 if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() )
10388 PDFOutlineEntry& rParent = m_aOutline[ nParentID ];
10390 for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin();
10391 it != rParent.m_aChildren.end(); ++it )
10393 if( *it == nItem )
10395 rParent.m_aChildren.erase( it );
10396 break;
10401 // insert item to new parent's list of children
10402 m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
10404 return nRet;
10407 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
10409 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10410 return -1;
10412 m_aOutline[ nItem ].m_aTitle = rText;
10413 return 0;
10416 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
10418 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
10419 return -1;
10420 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
10421 return -2;
10422 m_aOutline[nItem].m_nDestID = nDestID;
10423 return 0;
10426 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
10428 static std::map< PDFWriter::StructElement, const char* > aTagStrings;
10429 if( aTagStrings.empty() )
10431 aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
10432 aTagStrings[ PDFWriter::Document ] = "Document";
10433 aTagStrings[ PDFWriter::Part ] = "Part";
10434 aTagStrings[ PDFWriter::Article ] = "Art";
10435 aTagStrings[ PDFWriter::Section ] = "Sect";
10436 aTagStrings[ PDFWriter::Division ] = "Div";
10437 aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote";
10438 aTagStrings[ PDFWriter::Caption ] = "Caption";
10439 aTagStrings[ PDFWriter::TOC ] = "TOC";
10440 aTagStrings[ PDFWriter::TOCI ] = "TOCI";
10441 aTagStrings[ PDFWriter::Index ] = "Index";
10442 aTagStrings[ PDFWriter::Paragraph ] = "P";
10443 aTagStrings[ PDFWriter::Heading ] = "H";
10444 aTagStrings[ PDFWriter::H1 ] = "H1";
10445 aTagStrings[ PDFWriter::H2 ] = "H2";
10446 aTagStrings[ PDFWriter::H3 ] = "H3";
10447 aTagStrings[ PDFWriter::H4 ] = "H4";
10448 aTagStrings[ PDFWriter::H5 ] = "H5";
10449 aTagStrings[ PDFWriter::H6 ] = "H6";
10450 aTagStrings[ PDFWriter::List ] = "L";
10451 aTagStrings[ PDFWriter::ListItem ] = "LI";
10452 aTagStrings[ PDFWriter::LILabel ] = "Lbl";
10453 aTagStrings[ PDFWriter::LIBody ] = "LBody";
10454 aTagStrings[ PDFWriter::Table ] = "Table";
10455 aTagStrings[ PDFWriter::TableRow ] = "TR";
10456 aTagStrings[ PDFWriter::TableHeader ] = "TH";
10457 aTagStrings[ PDFWriter::TableData ] = "TD";
10458 aTagStrings[ PDFWriter::Span ] = "Span";
10459 aTagStrings[ PDFWriter::Quote ] = "Quote";
10460 aTagStrings[ PDFWriter::Note ] = "Note";
10461 aTagStrings[ PDFWriter::Reference ] = "Reference";
10462 aTagStrings[ PDFWriter::BibEntry ] = "BibEntry";
10463 aTagStrings[ PDFWriter::Code ] = "Code";
10464 aTagStrings[ PDFWriter::Link ] = "Link";
10465 aTagStrings[ PDFWriter::Figure ] = "Figure";
10466 aTagStrings[ PDFWriter::Formula ] = "Formula";
10467 aTagStrings[ PDFWriter::Form ] = "Form";
10470 std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
10472 return it != aTagStrings.end() ? it->second : "Div";
10475 void PDFWriterImpl::beginStructureElementMCSeq()
10477 if( m_bEmitStructure &&
10478 m_nCurrentStructElement > 0 && // StructTreeRoot
10479 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
10482 PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
10483 OStringBuffer aLine( 128 );
10484 sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
10485 aLine.append( "/" );
10486 if( rEle.m_aAlias.getLength() > 0 )
10487 aLine.append( rEle.m_aAlias );
10488 else
10489 aLine.append( getStructureTag( rEle.m_eType ) );
10490 aLine.append( "<</MCID " );
10491 aLine.append( nMCID );
10492 aLine.append( ">>BDC\n" );
10493 writeBuffer( aLine.getStr(), aLine.getLength() );
10495 // update the element's content list
10496 #if OSL_DEBUG_LEVEL > 1
10497 fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n",
10498 nMCID,
10499 m_aPages[ m_nCurrentPage ].m_nPageObject,
10500 rEle.m_nFirstPageObject );
10501 #endif
10502 rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) );
10503 // update the page's mcid parent list
10504 m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
10505 // mark element MC sequence as open
10506 rEle.m_bOpenMCSeq = true;
10508 // handle artifacts
10509 else if( ! m_bEmitStructure && m_aContext.Tagged &&
10510 m_nCurrentStructElement > 0 &&
10511 m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
10512 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
10515 OStringBuffer aLine( 128 );
10516 aLine.append( "/Artifact BMC\n" );
10517 writeBuffer( aLine.getStr(), aLine.getLength() );
10518 // mark element MC sequence as open
10519 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
10523 void PDFWriterImpl::endStructureElementMCSeq()
10525 if( m_nCurrentStructElement > 0 && // StructTreeRoot
10526 ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
10527 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
10530 writeBuffer( "EMC\n", 4 );
10531 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
10535 bool PDFWriterImpl::checkEmitStructure()
10537 bool bEmit = false;
10538 if( m_aContext.Tagged )
10540 bEmit = true;
10541 sal_Int32 nEle = m_nCurrentStructElement;
10542 while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
10544 if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
10546 bEmit = false;
10547 break;
10549 nEle = m_aStructure[ nEle ].m_nParentElement;
10552 return bEmit;
10555 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias )
10557 if( m_nCurrentPage < 0 )
10558 return -1;
10560 if( ! m_aContext.Tagged )
10561 return -1;
10563 // close eventual current MC sequence
10564 endStructureElementMCSeq();
10566 if( m_nCurrentStructElement == 0 &&
10567 eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
10569 // struct tree root hit, but not beginning document
10570 // this might happen with setCurrentStructureElement
10571 // silently insert structure into document again if one properly exists
10572 if( ! m_aStructure[ 0 ].m_aChildren.empty() )
10574 PDFWriter::StructElement childType = PDFWriter::NonStructElement;
10575 sal_Int32 nNewCurElement = 0;
10576 const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
10577 for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
10578 childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
10580 nNewCurElement = *it;
10581 childType = m_aStructure[ nNewCurElement ].m_eType;
10583 if( childType == PDFWriter::Document )
10585 m_nCurrentStructElement = nNewCurElement;
10586 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" );
10588 else {
10589 DBG_ERROR( "document structure in disorder !" );
10592 else {
10593 DBG_ERROR( "PDF document structure MUST be contained in a Document element" );
10597 sal_Int32 nNewId = sal_Int32(m_aStructure.size());
10598 m_aStructure.push_back( PDFStructureElement() );
10599 PDFStructureElement& rEle = m_aStructure.back();
10600 rEle.m_eType = eType;
10601 rEle.m_nOwnElement = nNewId;
10602 rEle.m_nParentElement = m_nCurrentStructElement;
10603 rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject;
10604 m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
10605 m_nCurrentStructElement = nNewId;
10607 // handle alias names
10608 if( rAlias.getLength() && eType != PDFWriter::NonStructElement )
10610 OStringBuffer aNameBuf( rAlias.getLength() );
10611 appendName( rAlias, aNameBuf );
10612 OString aAliasName( aNameBuf.makeStringAndClear() );
10613 rEle.m_aAlias = aAliasName;
10614 m_aRoleMap[ aAliasName ] = getStructureTag( eType );
10617 #if OSL_DEBUG_LEVEL > 1
10618 OStringBuffer aLine( "beginStructureElement " );
10619 aLine.append( m_nCurrentStructElement );
10620 aLine.append( ": " );
10621 aLine.append( getStructureTag( eType ) );
10622 if( rEle.m_aAlias.getLength() )
10624 aLine.append( " aliased as \"" );
10625 aLine.append( rEle.m_aAlias );
10626 aLine.append( '\"' );
10628 emitComment( aLine.getStr() );
10629 #endif
10631 // check whether to emit structure henceforth
10632 m_bEmitStructure = checkEmitStructure();
10634 if( m_bEmitStructure ) // don't create nonexistant objects
10636 rEle.m_nObject = createObject();
10637 // update parent's kids list
10638 m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject );
10640 return nNewId;
10643 void PDFWriterImpl::endStructureElement()
10645 if( m_nCurrentPage < 0 )
10646 return;
10648 if( ! m_aContext.Tagged )
10649 return;
10651 if( m_nCurrentStructElement == 0 )
10653 // hit the struct tree root, that means there is an endStructureElement
10654 // without corresponding beginStructureElement
10655 return;
10658 // end the marked content sequence
10659 endStructureElementMCSeq();
10661 #if OSL_DEBUG_LEVEL > 1
10662 OStringBuffer aLine( "endStructureElement " );
10663 aLine.append( m_nCurrentStructElement );
10664 aLine.append( ": " );
10665 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
10666 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
10668 aLine.append( " aliased as \"" );
10669 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
10670 aLine.append( '\"' );
10672 #endif
10674 // "end" the structure element, the parent becomes current element
10675 m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
10677 // check whether to emit structure henceforth
10678 m_bEmitStructure = checkEmitStructure();
10680 #if OSL_DEBUG_LEVEL > 1
10681 if( m_bEmitStructure )
10682 emitComment( aLine.getStr() );
10683 #endif
10686 //---> i94258
10688 * This function adds an internal structure list container to overcome the 8191 elements array limitation
10689 * in kids element emission.
10690 * Recursive function
10693 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
10695 if( rEle.m_eType == PDFWriter::NonStructElement &&
10696 rEle.m_nOwnElement != rEle.m_nParentElement )
10697 return;
10699 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
10701 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
10703 PDFStructureElement& rChild = m_aStructure[ *it ];
10704 if( rChild.m_eType != PDFWriter::NonStructElement )
10706 //triggered when a child of the rEle element is found
10707 if( rChild.m_nParentElement == rEle.m_nOwnElement )
10708 addInternalStructureContainer( rChild );//examine the child
10709 else
10711 DBG_ERROR( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
10712 #if OSL_DEBUG_LEVEL > 1
10713 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
10714 #endif
10718 else
10720 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
10721 #if OSL_DEBUG_LEVEL > 1
10722 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
10723 #endif
10727 if( rEle.m_nOwnElement != rEle.m_nParentElement )
10729 if( !rEle.m_aKids.empty() )
10731 if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
10732 //then we need to add the containers for the kids elements
10733 // a list to be used for the new kid element
10734 std::list< PDFStructureElementKid > aNewKids;
10735 std::list< sal_Int32 > aNewChildren;
10737 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
10738 OStringBuffer aNameBuf( "Div" );
10739 OString aAliasName( aNameBuf.makeStringAndClear() );
10740 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
10742 while( rEle.m_aKids.size() > ncMaxPDFArraySize )
10744 sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
10745 sal_Int32 nNewId = sal_Int32(m_aStructure.size());
10746 m_aStructure.push_back( PDFStructureElement() );
10747 PDFStructureElement& rEleNew = m_aStructure.back();
10748 rEleNew.m_aAlias = aAliasName;
10749 rEleNew.m_eType = PDFWriter::Division; // a new Div type container
10750 rEleNew.m_nOwnElement = nNewId;
10751 rEleNew.m_nParentElement = nCurrentStructElement;
10752 //inherit the same page as the first child to be reparented
10753 rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
10754 rEleNew.m_nObject = createObject();//assign a PDF object number
10755 //add the object to the kid list of the parent
10756 aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) );
10757 aNewChildren.push_back( nNewId );
10759 std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
10760 std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
10761 advance( aChildEndIt, ncMaxPDFArraySize );
10762 advance( aKidEndIt, ncMaxPDFArraySize );
10764 rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
10765 rEle.m_aKids,
10766 rEle.m_aKids.begin(),
10767 aKidEndIt );
10768 rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
10769 rEle.m_aChildren,
10770 rEle.m_aChildren.begin(),
10771 aChildEndIt );
10772 // set the kid's new parent
10773 for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
10774 it != rEleNew.m_aChildren.end(); ++it )
10776 m_aStructure[ *it ].m_nParentElement = nNewId;
10779 //finally add the new kids resulting from the container added
10780 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
10781 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
10786 //<--- i94258
10788 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
10790 bool bSuccess = false;
10792 if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
10794 // end eventual previous marked content sequence
10795 endStructureElementMCSeq();
10797 m_nCurrentStructElement = nEle;
10798 m_bEmitStructure = checkEmitStructure();
10799 #if OSL_DEBUG_LEVEL > 1
10800 OStringBuffer aLine( "setCurrentStructureElement " );
10801 aLine.append( m_nCurrentStructElement );
10802 aLine.append( ": " );
10803 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
10804 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
10806 aLine.append( " aliased as \"" );
10807 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
10808 aLine.append( '\"' );
10810 if( ! m_bEmitStructure )
10811 aLine.append( " (inside NonStruct)" );
10812 emitComment( aLine.getStr() );
10813 #endif
10814 bSuccess = true;
10817 return bSuccess;
10820 sal_Int32 PDFWriterImpl::getCurrentStructureElement()
10822 return m_nCurrentStructElement;
10825 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
10827 if( !m_aContext.Tagged )
10828 return false;
10830 bool bInsert = false;
10831 if( m_nCurrentStructElement > 0 && m_bEmitStructure )
10833 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
10834 switch( eAttr )
10836 case PDFWriter::Placement:
10837 if( eVal == PDFWriter::Block ||
10838 eVal == PDFWriter::Inline ||
10839 eVal == PDFWriter::Before ||
10840 eVal == PDFWriter::Start ||
10841 eVal == PDFWriter::End )
10842 bInsert = true;
10843 break;
10844 case PDFWriter::WritingMode:
10845 if( eVal == PDFWriter::LrTb ||
10846 eVal == PDFWriter::RlTb ||
10847 eVal == PDFWriter::TbRl )
10849 bInsert = true;
10851 break;
10852 case PDFWriter::TextAlign:
10853 if( eVal == PDFWriter::Start ||
10854 eVal == PDFWriter::Center ||
10855 eVal == PDFWriter::End ||
10856 eVal == PDFWriter::Justify )
10858 if( eType == PDFWriter::Paragraph ||
10859 eType == PDFWriter::Heading ||
10860 eType == PDFWriter::H1 ||
10861 eType == PDFWriter::H2 ||
10862 eType == PDFWriter::H3 ||
10863 eType == PDFWriter::H4 ||
10864 eType == PDFWriter::H5 ||
10865 eType == PDFWriter::H6 ||
10866 eType == PDFWriter::List ||
10867 eType == PDFWriter::ListItem ||
10868 eType == PDFWriter::LILabel ||
10869 eType == PDFWriter::LIBody ||
10870 eType == PDFWriter::Table ||
10871 eType == PDFWriter::TableRow ||
10872 eType == PDFWriter::TableHeader ||
10873 eType == PDFWriter::TableData )
10875 bInsert = true;
10878 break;
10879 case PDFWriter::Width:
10880 case PDFWriter::Height:
10881 if( eVal == PDFWriter::Auto )
10883 if( eType == PDFWriter::Figure ||
10884 eType == PDFWriter::Formula ||
10885 eType == PDFWriter::Form ||
10886 eType == PDFWriter::Table ||
10887 eType == PDFWriter::TableHeader ||
10888 eType == PDFWriter::TableData )
10890 bInsert = true;
10893 break;
10894 case PDFWriter::BlockAlign:
10895 if( eVal == PDFWriter::Before ||
10896 eVal == PDFWriter::Middle ||
10897 eVal == PDFWriter::After ||
10898 eVal == PDFWriter::Justify )
10900 if( eType == PDFWriter::TableHeader ||
10901 eType == PDFWriter::TableData )
10903 bInsert = true;
10906 break;
10907 case PDFWriter::InlineAlign:
10908 if( eVal == PDFWriter::Start ||
10909 eVal == PDFWriter::Center ||
10910 eVal == PDFWriter::End )
10912 if( eType == PDFWriter::TableHeader ||
10913 eType == PDFWriter::TableData )
10915 bInsert = true;
10918 break;
10919 case PDFWriter::LineHeight:
10920 if( eVal == PDFWriter::Normal ||
10921 eVal == PDFWriter::Auto )
10923 // only for ILSE and BLSE
10924 if( eType == PDFWriter::Paragraph ||
10925 eType == PDFWriter::Heading ||
10926 eType == PDFWriter::H1 ||
10927 eType == PDFWriter::H2 ||
10928 eType == PDFWriter::H3 ||
10929 eType == PDFWriter::H4 ||
10930 eType == PDFWriter::H5 ||
10931 eType == PDFWriter::H6 ||
10932 eType == PDFWriter::List ||
10933 eType == PDFWriter::ListItem ||
10934 eType == PDFWriter::LILabel ||
10935 eType == PDFWriter::LIBody ||
10936 eType == PDFWriter::Table ||
10937 eType == PDFWriter::TableRow ||
10938 eType == PDFWriter::TableHeader ||
10939 eType == PDFWriter::TableData ||
10940 eType == PDFWriter::Span ||
10941 eType == PDFWriter::Quote ||
10942 eType == PDFWriter::Note ||
10943 eType == PDFWriter::Reference ||
10944 eType == PDFWriter::BibEntry ||
10945 eType == PDFWriter::Code ||
10946 eType == PDFWriter::Link )
10948 bInsert = true;
10951 break;
10952 case PDFWriter::TextDecorationType:
10953 if( eVal == PDFWriter::NONE ||
10954 eVal == PDFWriter::Underline ||
10955 eVal == PDFWriter::Overline ||
10956 eVal == PDFWriter::LineThrough )
10958 // only for ILSE and BLSE
10959 if( eType == PDFWriter::Paragraph ||
10960 eType == PDFWriter::Heading ||
10961 eType == PDFWriter::H1 ||
10962 eType == PDFWriter::H2 ||
10963 eType == PDFWriter::H3 ||
10964 eType == PDFWriter::H4 ||
10965 eType == PDFWriter::H5 ||
10966 eType == PDFWriter::H6 ||
10967 eType == PDFWriter::List ||
10968 eType == PDFWriter::ListItem ||
10969 eType == PDFWriter::LILabel ||
10970 eType == PDFWriter::LIBody ||
10971 eType == PDFWriter::Table ||
10972 eType == PDFWriter::TableRow ||
10973 eType == PDFWriter::TableHeader ||
10974 eType == PDFWriter::TableData ||
10975 eType == PDFWriter::Span ||
10976 eType == PDFWriter::Quote ||
10977 eType == PDFWriter::Note ||
10978 eType == PDFWriter::Reference ||
10979 eType == PDFWriter::BibEntry ||
10980 eType == PDFWriter::Code ||
10981 eType == PDFWriter::Link )
10983 bInsert = true;
10986 break;
10987 case PDFWriter::ListNumbering:
10988 if( eVal == PDFWriter::NONE ||
10989 eVal == PDFWriter::Disc ||
10990 eVal == PDFWriter::Circle ||
10991 eVal == PDFWriter::Square ||
10992 eVal == PDFWriter::Decimal ||
10993 eVal == PDFWriter::UpperRoman ||
10994 eVal == PDFWriter::LowerRoman ||
10995 eVal == PDFWriter::UpperAlpha ||
10996 eVal == PDFWriter::LowerAlpha )
10998 if( eType == PDFWriter::List )
10999 bInsert = true;
11001 break;
11002 default: break;
11006 if( bInsert )
11007 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
11008 #if OSL_DEBUG_LEVEL > 1
11009 else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11010 fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
11011 getAttributeTag( eAttr ),
11012 getAttributeValueTag( eVal ),
11013 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11014 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
11016 #endif
11018 return bInsert;
11021 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
11023 if( ! m_aContext.Tagged )
11024 return false;
11026 bool bInsert = false;
11027 if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11029 if( eAttr == PDFWriter::Language )
11031 m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue );
11032 return true;
11035 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11036 switch( eAttr )
11038 case PDFWriter::SpaceBefore:
11039 case PDFWriter::SpaceAfter:
11040 case PDFWriter::StartIndent:
11041 case PDFWriter::EndIndent:
11042 // just for BLSE
11043 if( eType == PDFWriter::Paragraph ||
11044 eType == PDFWriter::Heading ||
11045 eType == PDFWriter::H1 ||
11046 eType == PDFWriter::H2 ||
11047 eType == PDFWriter::H3 ||
11048 eType == PDFWriter::H4 ||
11049 eType == PDFWriter::H5 ||
11050 eType == PDFWriter::H6 ||
11051 eType == PDFWriter::List ||
11052 eType == PDFWriter::ListItem ||
11053 eType == PDFWriter::LILabel ||
11054 eType == PDFWriter::LIBody ||
11055 eType == PDFWriter::Table ||
11056 eType == PDFWriter::TableRow ||
11057 eType == PDFWriter::TableHeader ||
11058 eType == PDFWriter::TableData )
11060 bInsert = true;
11062 break;
11063 case PDFWriter::TextIndent:
11064 // paragraph like BLSE and additional elements
11065 if( eType == PDFWriter::Paragraph ||
11066 eType == PDFWriter::Heading ||
11067 eType == PDFWriter::H1 ||
11068 eType == PDFWriter::H2 ||
11069 eType == PDFWriter::H3 ||
11070 eType == PDFWriter::H4 ||
11071 eType == PDFWriter::H5 ||
11072 eType == PDFWriter::H6 ||
11073 eType == PDFWriter::LILabel ||
11074 eType == PDFWriter::LIBody ||
11075 eType == PDFWriter::TableHeader ||
11076 eType == PDFWriter::TableData )
11078 bInsert = true;
11080 break;
11081 case PDFWriter::Width:
11082 case PDFWriter::Height:
11083 if( eType == PDFWriter::Figure ||
11084 eType == PDFWriter::Formula ||
11085 eType == PDFWriter::Form ||
11086 eType == PDFWriter::Table ||
11087 eType == PDFWriter::TableHeader ||
11088 eType == PDFWriter::TableData )
11090 bInsert = true;
11092 break;
11093 case PDFWriter::LineHeight:
11094 case PDFWriter::BaselineShift:
11095 // only for ILSE and BLSE
11096 if( eType == PDFWriter::Paragraph ||
11097 eType == PDFWriter::Heading ||
11098 eType == PDFWriter::H1 ||
11099 eType == PDFWriter::H2 ||
11100 eType == PDFWriter::H3 ||
11101 eType == PDFWriter::H4 ||
11102 eType == PDFWriter::H5 ||
11103 eType == PDFWriter::H6 ||
11104 eType == PDFWriter::List ||
11105 eType == PDFWriter::ListItem ||
11106 eType == PDFWriter::LILabel ||
11107 eType == PDFWriter::LIBody ||
11108 eType == PDFWriter::Table ||
11109 eType == PDFWriter::TableRow ||
11110 eType == PDFWriter::TableHeader ||
11111 eType == PDFWriter::TableData ||
11112 eType == PDFWriter::Span ||
11113 eType == PDFWriter::Quote ||
11114 eType == PDFWriter::Note ||
11115 eType == PDFWriter::Reference ||
11116 eType == PDFWriter::BibEntry ||
11117 eType == PDFWriter::Code ||
11118 eType == PDFWriter::Link )
11120 bInsert = true;
11122 break;
11123 case PDFWriter::RowSpan:
11124 case PDFWriter::ColSpan:
11125 // only for table cells
11126 if( eType == PDFWriter::TableHeader ||
11127 eType == PDFWriter::TableData )
11129 bInsert = true;
11131 break;
11132 case PDFWriter::LinkAnnotation:
11133 if( eType == PDFWriter::Link )
11134 bInsert = true;
11135 break;
11136 default: break;
11140 if( bInsert )
11141 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
11142 #if OSL_DEBUG_LEVEL > 1
11143 else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11144 fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
11145 getAttributeTag( eAttr ),
11146 (int)nValue,
11147 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11148 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() );
11149 #endif
11151 return bInsert;
11154 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect )
11156 sal_Int32 nPageNr = m_nCurrentPage;
11157 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
11158 return;
11161 if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11163 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11164 if( eType == PDFWriter::Figure ||
11165 eType == PDFWriter::Formula ||
11166 eType == PDFWriter::Form ||
11167 eType == PDFWriter::Table )
11169 m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
11170 // convert to default user space now, since the mapmode may change
11171 m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
11176 void PDFWriterImpl::setActualText( const String& rText )
11178 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11180 m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
11184 void PDFWriterImpl::setAlternateText( const String& rText )
11186 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11188 m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
11192 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
11194 if( nPageNr < 0 )
11195 nPageNr = m_nCurrentPage;
11197 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11198 return;
11200 m_aPages[ nPageNr ].m_nDuration = nSeconds;
11203 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
11205 if( nPageNr < 0 )
11206 nPageNr = m_nCurrentPage;
11208 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11209 return;
11211 m_aPages[ nPageNr ].m_eTransition = eType;
11212 m_aPages[ nPageNr ].m_nTransTime = nMilliSec;
11215 void PDFWriterImpl::ensureUniqueRadioOnValues()
11217 // loop over radio groups
11218 for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
11219 group != m_aRadioGroupWidgets.end(); ++group )
11221 PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
11222 // check whether all kids have a unique OnValue
11223 std::hash_map< OUString, sal_Int32, OUStringHash > aOnValues;
11224 int nChildren = rGroupWidget.m_aKidsIndex.size();
11225 bool bIsUnique = true;
11226 for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
11228 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11229 const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
11230 #if OSL_DEBUG_LEVEL > 1
11231 fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() );
11232 #endif
11233 if( aOnValues.find( rVal ) == aOnValues.end() )
11235 aOnValues[ rVal ] = 1;
11237 else
11239 bIsUnique = false;
11242 if( ! bIsUnique )
11244 #if OSL_DEBUG_LEVEL > 1
11245 fprintf( stderr, "enforcing unique OnValues\n" );
11246 #endif
11247 // make unique by using ascending OnValues
11248 for( int nKid = 0; nKid < nChildren; nKid++ )
11250 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11251 PDFWidget& rKid = m_aWidgets[nKidIndex];
11252 rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) );
11253 if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11254 rKid.m_aValue = rKid.m_aOnValue;
11257 // finally move the "Yes" appearance to the OnValue appearance
11258 for( int nKid = 0; nKid < nChildren; nKid++ )
11260 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11261 PDFWidget& rKid = m_aWidgets[nKidIndex];
11262 PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
11263 if( app_it != rKid.m_aAppearances.end() )
11265 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
11266 if( stream_it != app_it->second.end() )
11268 SvMemoryStream* pStream = stream_it->second;
11269 app_it->second.erase( stream_it );
11270 OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
11271 appendName( rKid.m_aOnValue, aBuf );
11272 (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11274 #if OSL_DEBUG_LEVEL > 1
11275 else
11276 fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" );
11277 #endif
11279 // update selected radio button
11280 if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11282 rGroupWidget.m_aValue = rKid.m_aValue;
11288 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
11290 sal_Int32 nRadioGroupWidget = -1;
11292 std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
11294 if( it == m_aRadioGroupWidgets.end() )
11296 m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
11297 sal_Int32(m_aWidgets.size());
11299 // new group, insert the radiobutton
11300 m_aWidgets.push_back( PDFWidget() );
11301 m_aWidgets.back().m_nObject = createObject();
11302 m_aWidgets.back().m_nPage = m_nCurrentPage;
11303 m_aWidgets.back().m_eType = PDFWriter::RadioButton;
11304 m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
11305 m_aWidgets.back().m_nFlags |= 0x00008000;
11307 // create radio button field name
11308 const rtl::OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ?
11309 rBtn.Name : rBtn.Text;
11310 if( rName.getLength() )
11312 m_aWidgets.back().m_aName = convertWidgetFieldName( rName );
11314 else
11316 m_aWidgets.back().m_aName = "RadioGroup";
11317 m_aWidgets.back().m_aName += OString::valueOf( rBtn.RadioGroup );
11320 else
11321 nRadioGroupWidget = it->second;
11323 return nRadioGroupWidget;
11326 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
11328 if( nPageNr < 0 )
11329 nPageNr = m_nCurrentPage;
11331 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11332 return -1;
11334 m_aWidgets.push_back( PDFWidget() );
11335 sal_Int32 nNewWidget = m_aWidgets.size()-1;
11337 // create eventual radio button before getting any references
11338 // from m_aWidgets as the push_back operation potentially assigns new
11339 // memory to the vector and thereby invalidates the reference
11340 int nRadioGroupWidget = -1;
11341 if( rControl.getType() == PDFWriter::RadioButton )
11342 nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
11344 PDFWidget& rNewWidget = m_aWidgets[nNewWidget];
11345 rNewWidget.m_nObject = createObject();
11346 rNewWidget.m_aRect = rControl.Location;
11347 rNewWidget.m_nPage = nPageNr;
11348 rNewWidget.m_eType = rControl.getType();
11350 // for unknown reasons the radio buttons of a radio group must not have a
11351 // field name, else the buttons are in fact check boxes -
11352 // that is multiple buttons of the radio group can be selected
11353 if( rControl.getType() != PDFWriter::RadioButton )
11355 // acrobat reader since 3.0 does not support unicode text
11356 // strings for the field name; so we need to encode unicodes
11357 // larger than 255
11359 rNewWidget.m_aName =
11360 convertWidgetFieldName( (m_aContext.Version > PDFWriter::PDF_1_2) ?
11361 rControl.Name : rControl.Text );
11362 // #i88040# acrobat reader crashes on empty field names,
11363 // so always create one
11364 if( rNewWidget.m_aName.getLength() == 0 )
11366 OUStringBuffer aBuf( 32 );
11367 aBuf.appendAscii( "Widget" );
11368 aBuf.append( nNewWidget );
11369 rNewWidget.m_aName = convertWidgetFieldName( aBuf.makeStringAndClear() );
11372 rNewWidget.m_aDescription = rControl.Description;
11373 rNewWidget.m_aText = rControl.Text;
11374 rNewWidget.m_nTextStyle = rControl.TextStyle &
11375 ( TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP |
11376 TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM |
11377 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
11378 rNewWidget.m_nTabOrder = rControl.TabOrder;
11380 // various properties are set via the flags (/Ff) property of the field dict
11381 if( rControl.ReadOnly )
11382 rNewWidget.m_nFlags |= 1;
11383 if( rControl.getType() == PDFWriter::PushButton )
11385 const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
11386 if( rNewWidget.m_nTextStyle == 0 )
11387 rNewWidget.m_nTextStyle =
11388 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER |
11389 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11391 rNewWidget.m_nFlags |= 0x00010000;
11392 if( rBtn.URL.getLength() )
11393 rNewWidget.m_aListEntries.push_back( rBtn.URL );
11394 rNewWidget.m_bSubmit = rBtn.Submit;
11395 rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
11396 rNewWidget.m_nDest = rBtn.Dest;
11397 createDefaultPushButtonAppearance( rNewWidget, rBtn );
11399 else if( rControl.getType() == PDFWriter::RadioButton )
11401 const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
11402 if( rNewWidget.m_nTextStyle == 0 )
11403 rNewWidget.m_nTextStyle =
11404 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11405 /* PDF sees a RadioButton group as one radio button with
11406 * children which are in turn check boxes
11408 * so we need to create a radio button on demand for a new group
11409 * and insert a checkbox for each RadioButtonWidget as its child
11411 rNewWidget.m_eType = PDFWriter::CheckBox;
11412 rNewWidget.m_nRadioGroup = rBtn.RadioGroup;
11414 DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" );
11416 PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
11417 rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
11418 rRadioButton.m_aKidsIndex.push_back( nNewWidget );
11419 rNewWidget.m_nParent = rRadioButton.m_nObject;
11421 rNewWidget.m_aValue = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) );
11422 rNewWidget.m_aOnValue = rBtn.OnValue;
11423 if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected )
11425 rNewWidget.m_aValue = rNewWidget.m_aOnValue;
11426 rRadioButton.m_aValue = rNewWidget.m_aOnValue;
11428 createDefaultRadioButtonAppearance( rNewWidget, rBtn );
11430 // union rect of radio group
11431 Rectangle aRect = rNewWidget.m_aRect;
11432 m_aPages[ nPageNr ].convertRect( aRect );
11433 rRadioButton.m_aRect.Union( aRect );
11435 else if( rControl.getType() == PDFWriter::CheckBox )
11437 const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
11438 if( rNewWidget.m_nTextStyle == 0 )
11439 rNewWidget.m_nTextStyle =
11440 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11442 rNewWidget.m_aValue = OUString::createFromAscii( rBox.Checked ? "Yes" : "Off" );
11443 // create default appearance before m_aRect gets transformed
11444 createDefaultCheckBoxAppearance( rNewWidget, rBox );
11446 else if( rControl.getType() == PDFWriter::ListBox )
11448 if( rNewWidget.m_nTextStyle == 0 )
11449 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
11451 const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
11452 rNewWidget.m_aListEntries = rLstBox.Entries;
11453 rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
11454 rNewWidget.m_aValue = rLstBox.Text;
11455 if( rLstBox.DropDown )
11456 rNewWidget.m_nFlags |= 0x00020000;
11457 if( rLstBox.Sort )
11458 rNewWidget.m_nFlags |= 0x00080000;
11459 if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 )
11460 rNewWidget.m_nFlags |= 0x00200000;
11462 createDefaultListBoxAppearance( rNewWidget, rLstBox );
11464 else if( rControl.getType() == PDFWriter::ComboBox )
11466 if( rNewWidget.m_nTextStyle == 0 )
11467 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
11469 const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
11470 rNewWidget.m_aValue = rBox.Text;
11471 rNewWidget.m_aListEntries = rBox.Entries;
11472 rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
11473 if( rBox.Sort )
11474 rNewWidget.m_nFlags |= 0x00080000;
11476 PDFWriter::ListBoxWidget aLBox;
11477 aLBox.Name = rBox.Name;
11478 aLBox.Description = rBox.Description;
11479 aLBox.Text = rBox.Text;
11480 aLBox.TextStyle = rBox.TextStyle;
11481 aLBox.ReadOnly = rBox.ReadOnly;
11482 aLBox.Border = rBox.Border;
11483 aLBox.BorderColor = rBox.BorderColor;
11484 aLBox.Background = rBox.Background;
11485 aLBox.BackgroundColor = rBox.BackgroundColor;
11486 aLBox.TextFont = rBox.TextFont;
11487 aLBox.TextColor = rBox.TextColor;
11488 aLBox.DropDown = true;
11489 aLBox.Sort = rBox.Sort;
11490 aLBox.MultiSelect = false;
11491 aLBox.Entries = rBox.Entries;
11493 createDefaultListBoxAppearance( rNewWidget, aLBox );
11495 else if( rControl.getType() == PDFWriter::Edit )
11497 if( rNewWidget.m_nTextStyle == 0 )
11498 rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
11500 const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl);
11501 if( rEdit.MultiLine )
11503 rNewWidget.m_nFlags |= 0x00001000;
11504 rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11506 if( rEdit.Password )
11507 rNewWidget.m_nFlags |= 0x00002000;
11508 if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 )
11509 rNewWidget.m_nFlags |= 0x00100000;
11510 rNewWidget.m_nMaxLen = rEdit.MaxLen;
11511 rNewWidget.m_aValue = rEdit.Text;
11513 createDefaultEditAppearance( rNewWidget, rEdit );
11516 // convert to default user space now, since the mapmode may change
11517 // note: create default appearances before m_aRect gets transformed
11518 m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
11520 // insert widget to page's annotation list
11521 m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
11523 // mark page as having widgets
11524 m_aPages[ nPageNr ].m_bHasWidgets = true;
11526 return nNewWidget;
11529 void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl )
11531 if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() )
11532 return;
11534 PDFWidget& rWidget = m_aWidgets[ nControl ];
11535 m_nCurrentControl = nControl;
11537 SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 );
11538 // back conversion of control rect to current MapMode; necessary because
11539 // MapMode between createControl and beginControlAppearance
11540 // could have changed; therefore the widget rectangle is
11541 // already converted
11542 Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ),
11543 rWidget.m_aRect.GetSize() );
11544 aBack = lcl_convert( m_aMapMode,
11545 m_aGraphicsStack.front().m_aMapMode,
11546 getReferenceDevice(),
11547 aBack );
11548 beginRedirect( pControlStream, aBack );
11549 writeBuffer( "/Tx BMC\n", 8 );
11552 bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState )
11554 bool bRet = false;
11555 if( ! m_aOutputStreams.empty() )
11556 writeBuffer( "\nEMC\n", 5 );
11557 SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect());
11558 if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() )
11560 PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ];
11561 OString aState, aStyle;
11562 switch( rWidget.m_eType )
11564 case PDFWriter::PushButton:
11565 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
11567 aState = (eState == PDFWriter::Up) ? "N" : "D";
11568 aStyle = "Standard";
11570 break;
11571 case PDFWriter::CheckBox:
11572 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
11574 aState = "N";
11575 aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes";
11576 /* cf PDFReference 3rd ed. V1.4 p539:
11577 recommended name for on state is "Yes",
11578 recommended name for off state is "Off"
11581 break;
11582 case PDFWriter::RadioButton:
11583 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
11585 aState = "N";
11586 if( eState == PDFWriter::Up )
11587 aStyle = "Off";
11588 else
11590 OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 );
11591 appendName( rWidget.m_aOnValue, aBuf );
11592 aStyle = aBuf.makeStringAndClear();
11595 break;
11596 case PDFWriter::Edit:
11597 aState = "N";
11598 aStyle = "Standard";
11599 break;
11600 case PDFWriter::ListBox:
11601 case PDFWriter::ComboBox:
11602 break;
11604 if( aState.getLength() && aStyle.getLength() )
11606 // delete eventual existing stream
11607 PDFAppearanceStreams::iterator it =
11608 rWidget.m_aAppearances[ aState ].find( aStyle );
11609 if( it != rWidget.m_aAppearances[ aState ].end() )
11610 delete it->second;
11611 rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance;
11612 bRet = true;
11616 if( ! bRet )
11617 delete pAppearance;
11619 m_nCurrentControl = -1;
11621 return bRet;
11624 void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress )
11626 if( pStream )
11628 m_aAdditionalStreams.push_back( PDFAddStream() );
11629 PDFAddStream& rStream = m_aAdditionalStreams.back();
11630 rStream.m_aMimeType = rMimeType.Len()
11631 ? OUString( rMimeType )
11632 : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) );
11633 rStream.m_pStream = pStream;
11634 rStream.m_bCompress = bCompress;
11638 /*************************************************************
11639 begin i12626 methods
11641 Implements Algorithm 3.2, step 1 only
11643 void PDFWriterImpl::padPassword( rtl::OUString aPassword, sal_uInt8 *paPasswordTarget )
11645 // get ansi-1252 version of the password string CHECKIT ! i12626
11646 rtl::OString aString = rtl::OUStringToOString( aPassword, RTL_TEXTENCODING_MS_1252 );
11648 //copy the string to the target
11649 sal_Int32 nToCopy = ( aString.getLength() < 32 ) ? aString.getLength() : 32;
11650 sal_Int32 nCurrentChar;
11652 for( nCurrentChar = 0; nCurrentChar < nToCopy; nCurrentChar++ )
11653 paPasswordTarget[nCurrentChar] = (sal_uInt8)( aString.getStr()[nCurrentChar] );
11655 //pad it
11656 if( nCurrentChar < 32 )
11657 {//fill with standard byte string
11658 sal_Int32 i,y;
11659 for( i = nCurrentChar, y = 0 ; i < 32; i++, y++ )
11660 paPasswordTarget[i] = m_nPadString[y];
11664 /**********************************
11665 Algorithm 3.2 Compute the encryption key used
11667 step 1 should already be done before calling, the paThePaddedPassword parameter should contain
11668 the padded password and must be 32 byte long, the encryption key is returned into the paEncryptionKey parameter,
11669 it will be 16 byte long for 128 bit security; for 40 bit security only the first 5 bytes are used
11671 TODO: in pdf ver 1.5 and 1.6 the step 6 is different, should be implemented. See spec.
11674 void PDFWriterImpl::computeEncryptionKey(sal_uInt8 *paThePaddedPassword, sal_uInt8 *paEncryptionKey )
11676 //step 2
11677 if( m_aDigest )
11679 rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, paThePaddedPassword, ENCRYPTED_PWD_SIZE );
11680 //step 3
11681 if( nError == rtl_Digest_E_None )
11682 nError = rtl_digest_updateMD5( m_aDigest, m_nEncryptedOwnerPassword , sizeof( m_nEncryptedOwnerPassword ) );
11683 //Step 4
11684 sal_uInt8 nPerm[4];
11686 nPerm[0] = (sal_uInt8)m_nAccessPermissions;
11687 nPerm[1] = (sal_uInt8)( m_nAccessPermissions >> 8 );
11688 nPerm[2] = (sal_uInt8)( m_nAccessPermissions >> 16 );
11689 nPerm[3] = (sal_uInt8)( m_nAccessPermissions >> 24 );
11691 if( nError == rtl_Digest_E_None )
11692 nError = rtl_digest_updateMD5( m_aDigest, nPerm , sizeof( nPerm ) );
11694 //step 5, get the document ID, binary form
11695 if( nError == rtl_Digest_E_None )
11696 nError = rtl_digest_updateMD5( m_aDigest, m_nDocID , sizeof( m_nDocID ) );
11697 //get the digest
11698 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
11699 if( nError == rtl_Digest_E_None )
11701 rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) );
11703 //step 6, only if 128 bit
11704 if( m_aContext.Security128bit )
11706 for( sal_Int32 i = 0; i < 50; i++ )
11708 nError = rtl_digest_updateMD5( m_aDigest, &nMD5Sum, sizeof( nMD5Sum ) );
11709 if( nError != rtl_Digest_E_None )
11710 break;
11711 rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) );
11715 //Step 7
11716 for( sal_Int32 i = 0; i < MD5_DIGEST_SIZE; i++ )
11717 paEncryptionKey[i] = nMD5Sum[i];
11721 /**********************************
11722 Algorithm 3.3 Compute the encryption dictionary /O value, save into the class data member
11723 the step numbers down here correspond to the ones in PDF v.1.4 specfication
11725 void PDFWriterImpl::computeODictionaryValue()
11727 //step 1 already done, data is in m_nPaddedOwnerPassword
11728 //step 2
11729 if( m_aDigest )
11731 rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, &m_nPaddedOwnerPassword, sizeof( m_nPaddedOwnerPassword ) );
11732 if( nError == rtl_Digest_E_None )
11734 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
11736 rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof(nMD5Sum) );
11737 //step 3, only if 128 bit
11738 if( m_aContext.Security128bit )
11740 sal_Int32 i;
11741 for( i = 0; i < 50; i++ )
11743 nError = rtl_digest_updateMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) );
11744 if( nError != rtl_Digest_E_None )
11745 break;
11746 rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof( nMD5Sum ) );
11749 //Step 4, the key is in nMD5Sum
11750 //step 5 already done, data is in m_nPaddedUserPassword
11751 //step 6
11752 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode,
11753 nMD5Sum, m_nKeyLength , NULL, 0 );
11754 // encrypt the user password using the key set above
11755 rtl_cipher_encodeARCFOUR( m_aCipher, m_nPaddedUserPassword, sizeof( m_nPaddedUserPassword ), // the data to be encrypted
11756 m_nEncryptedOwnerPassword, sizeof( m_nEncryptedOwnerPassword ) ); //encrypted data, stored in class data member
11757 //Step 7, only if 128 bit
11758 if( m_aContext.Security128bit )
11760 sal_uInt32 i, y;
11761 sal_uInt8 nLocalKey[ SECUR_128BIT_KEY ]; // 16 = 128 bit key
11763 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
11765 for( y = 0; y < sizeof( nLocalKey ); y++ )
11766 nLocalKey[y] = (sal_uInt8)( nMD5Sum[y] ^ i );
11768 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode,
11769 nLocalKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area, on init can be NULL
11770 rtl_cipher_encodeARCFOUR( m_aCipher, m_nEncryptedOwnerPassword, sizeof( m_nEncryptedOwnerPassword ), // the data to be encrypted
11771 m_nEncryptedOwnerPassword, sizeof( m_nEncryptedOwnerPassword ) ); // encrypted data, can be the same as the input, encrypt "in place"
11772 //step 8, store in class data member
11779 /**********************************
11780 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)
11782 void PDFWriterImpl::computeUDictionaryValue()
11784 //step 1, common to both 3.4 and 3.5
11785 computeEncryptionKey( m_nPaddedUserPassword , m_nEncryptionKey );
11787 if( m_aContext.Security128bit == false )
11789 //3.4
11790 //step 2 and 3
11791 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode,
11792 m_nEncryptionKey, 5 , // key and key length
11793 NULL, 0 ); //destination data area
11794 // encrypt the user password using the key set above, save for later use
11795 rtl_cipher_encodeARCFOUR( m_aCipher, m_nPadString, sizeof( m_nPadString ), // the data to be encrypted
11796 m_nEncryptedUserPassword, sizeof( m_nEncryptedUserPassword ) ); //encrypted data, stored in class data member
11798 else
11800 //or 3.5, for 128 bit security
11801 //step6, initilize the last 16 bytes of the encrypted user password to 0
11802 for(sal_uInt32 i = MD5_DIGEST_SIZE; i < sizeof( m_nEncryptedUserPassword ); i++)
11803 m_nEncryptedUserPassword[i] = 0;
11804 //step 2
11805 if( m_aDigest )
11807 rtlDigestError nError = rtl_digest_updateMD5( m_aDigest, m_nPadString, sizeof( m_nPadString ) );
11808 //step 3
11809 if( nError == rtl_Digest_E_None )
11810 nError = rtl_digest_updateMD5( m_aDigest, m_nDocID , sizeof(m_nDocID) );
11812 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
11813 rtl_digest_getMD5( m_aDigest, nMD5Sum, sizeof(nMD5Sum) );
11814 //Step 4
11815 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode,
11816 m_nEncryptionKey, SECUR_128BIT_KEY, NULL, 0 ); //destination data area
11817 rtl_cipher_encodeARCFOUR( m_aCipher, nMD5Sum, sizeof( nMD5Sum ), // the data to be encrypted
11818 m_nEncryptedUserPassword, sizeof( nMD5Sum ) ); //encrypted data, stored in class data member
11819 //step 5
11820 sal_uInt32 i, y;
11821 sal_uInt8 nLocalKey[SECUR_128BIT_KEY];
11823 for( i = 1; i <= 19; i++ ) // do it 19 times, start with 1
11825 for( y = 0; y < sizeof( nLocalKey ) ; y++ )
11826 nLocalKey[y] = (sal_uInt8)( m_nEncryptionKey[y] ^ i );
11828 rtl_cipher_initARCFOUR( m_aCipher, rtl_Cipher_DirectionEncode,
11829 nLocalKey, SECUR_128BIT_KEY, // key and key length
11830 NULL, 0 ); //destination data area, on init can be NULL
11831 rtl_cipher_encodeARCFOUR( m_aCipher, m_nEncryptedUserPassword, SECUR_128BIT_KEY, // the data to be encrypted
11832 m_nEncryptedUserPassword, SECUR_128BIT_KEY ); // encrypted data, can be the same as the input, encrypt "in place"
11838 /* init the encryption engine
11839 1. init the document id, used both for building the document id and for building the encryption key(s)
11840 2. build the encryption key following algorithms described in the PDF specification
11842 void PDFWriterImpl::initEncryption()
11844 m_aOwnerPassword = m_aContext.OwnerPassword;
11845 m_aUserPassword = m_aContext.UserPassword;
11846 /* password stuff computing, before sending out anything */
11847 DBG_ASSERT( m_aCipher != NULL, "PDFWriterImpl::initEncryption: a cipher (ARCFOUR) object is not available !" );
11848 DBG_ASSERT( m_aDigest != NULL, "PDFWriterImpl::initEncryption: a digest (MD5) object is not available !" );
11850 if( m_aCipher && m_aDigest )
11852 //if there is no owner password, force it to the user password
11853 if( m_aOwnerPassword.getLength() == 0 )
11854 m_aOwnerPassword = m_aUserPassword;
11856 initPadString();
11858 1) pad passwords
11860 padPassword( m_aOwnerPassword, m_nPaddedOwnerPassword );
11861 padPassword( m_aUserPassword, m_nPaddedUserPassword );
11863 2) compute the access permissions, in numerical form
11865 the default value depends on the revision 2 (40 bit) or 3 (128 bit security):
11866 - for 40 bit security the unused bit must be set to 1, since they are not used
11867 - for 128 bit security the same bit must be preset to 0 and set later if needed
11868 according to the table 3.15, pdf v 1.4 */
11869 m_nAccessPermissions = ( m_aContext.Security128bit ) ? 0xfffff0c0 : 0xffffffc0 ;
11871 /* check permissions for 40 bit security case */
11872 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanPrintTheDocument ) ? 1 << 2 : 0;
11873 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanModifyTheContent ) ? 1 << 3 : 0;
11874 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanCopyOrExtract ) ? 1 << 4 : 0;
11875 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanAddOrModify ) ? 1 << 5 : 0;
11876 m_nKeyLength = SECUR_40BIT_KEY;
11877 m_nRC4KeyLength = SECUR_40BIT_KEY+5; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 5
11879 if( m_aContext.Security128bit )
11881 m_nKeyLength = SECUR_128BIT_KEY;
11882 m_nRC4KeyLength = 16; // for this value see PDF spec v 1.4, algorithm 3.1 step 4, where n is 16, thus maximum
11883 // permitted value is 16
11884 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanFillInteractive ) ? 1 << 8 : 0;
11885 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanExtractForAccessibility ) ? 1 << 9 : 0;
11886 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanAssemble ) ? 1 << 10 : 0;
11887 m_nAccessPermissions |= ( m_aContext.AccessPermissions.CanPrintFull ) ? 1 << 11 : 0;
11889 computeODictionaryValue();
11890 computeUDictionaryValue();
11892 //clear out exceding key values, prepares for generation number default to 0 as well
11893 // see checkAndEnableStreamEncryption in pdfwriter_impl.hxx
11894 sal_Int32 i, y;
11895 for( i = m_nKeyLength, y = 0; y < 5 ; y++ )
11896 m_nEncryptionKey[i++] = 0;
11898 else //either no cipher or no digest or both, something is wrong with memory or something else
11899 m_aContext.Encrypt = false; //then turn the encryption off
11901 /* end i12626 methods */