xref: /aoo42x/main/vcl/source/gdi/pdfwriter_impl.cxx (revision 0835fbc2)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
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.
14  *
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).
20  *
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.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 #define _USE_MATH_DEFINES
32 #include <math.h>
33 #include <algorithm>
34 
35 #include <tools/urlobj.hxx>
36 
37 #include <pdfwriter_impl.hxx>
38 
39 #include <basegfx/polygon/b2dpolygon.hxx>
40 #include <basegfx/polygon/b2dpolypolygon.hxx>
41 #include <basegfx/polygon/b2dpolygontools.hxx>
42 #include <basegfx/polygon/b2dpolypolygontools.hxx>
43 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
44 #include <basegfx/matrix/b2dhommatrix.hxx>
45 
46 #include <osl/thread.h>
47 #include <osl/file.h>
48 
49 #include <rtl/crc.h>
50 #include <rtl/digest.h>
51 #include <rtl/ustrbuf.hxx>
52 
53 #include <tools/debug.hxx>
54 #include <tools/zcodec.hxx>
55 #include <tools/stream.hxx>
56 
57 #include <i18npool/mslangid.hxx>
58 
59 #include <vcl/virdev.hxx>
60 #include <vcl/bmpacc.hxx>
61 #include <vcl/bitmapex.hxx>
62 #include <vcl/image.hxx>
63 #include <vcl/metric.hxx>
64 #include <vcl/svapp.hxx>
65 #include <vcl/lineinfo.hxx>
66 #include "vcl/cvtgrf.hxx"
67 #include "vcl/strhelper.hxx"
68 
69 #include <fontsubset.hxx>
70 #include <outdev.h>
71 #include <sallayout.hxx>
72 #include <textlayout.hxx>
73 #include <salgdi.hxx>
74 
75 #include <icc/sRGB-IEC61966-2.1.hxx>
76 
77 #include <comphelper/processfactory.hxx>
78 
79 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
80 #include <com/sun/star/util/URL.hpp>
81 
82 #include "cppuhelper/implbase1.hxx"
83 
84 using namespace vcl;
85 using namespace rtl;
86 
87 #if (OSL_DEBUG_LEVEL < 2)
88 #define COMPRESS_PAGES
89 #else
90 #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams
91 #endif
92 
93 #ifdef DO_TEST_PDF
94 class PDFTestOutputStream : public PDFOutputStream
95 {
96     public:
97     virtual ~PDFTestOutputStream();
98     virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream );
99 };
100 
101 PDFTestOutputStream::~PDFTestOutputStream()
102 {
103 }
104 
105 void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream )
106 {
107     OString aStr( "lalala\ntest\ntest\ntest" );
108     com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() );
109     rtl_copyMemory( aData.getArray(), aStr.getStr(), aStr.getLength() );
110     xStream->writeBytes( aData );
111 }
112 
113 // this test code cannot be used to test PDF/A-1 because it forces
114 // control item (widgets) to bypass the structure controlling
115 // the embedding of such elements in actual run
116 void doTestCode()
117 {
118     static const char* pHome = getenv( "HOME"  );
119     rtl::OUString aTestFile( RTL_CONSTASCII_USTRINGPARAM( "file://" ) );
120     aTestFile += rtl::OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 );
121     aTestFile += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/pdf_export_test.pdf" ) );
122 
123     PDFWriter::PDFWriterContext aContext;
124     aContext.URL			= aTestFile;
125     aContext.Version		= PDFWriter::PDF_1_4;
126     aContext.Tagged			= true;
127     aContext.InitialPage    = 2;
128     aContext.DocumentInfo.Title = OUString( RTL_CONSTASCII_USTRINGPARAM( "PDF export test document" ) );
129     aContext.DocumentInfo.Producer = OUString( RTL_CONSTASCII_USTRINGPARAM( "VCL" ) );
130 
131     PDFWriter aWriter( aContext );
132     aWriter.NewPage( 595, 842 );
133     aWriter.BeginStructureElement( PDFWriter::Document );
134     // set duration of 3 sec for first page
135     aWriter.SetAutoAdvanceTime( 3 );
136     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
137 
138     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
139     aWriter.SetLineColor( Color( COL_LIGHTGREEN ) );
140     aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
141 
142     aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
143     aWriter.SetTextColor( Color( COL_BLACK ) );
144     aWriter.SetLineColor( Color( COL_BLACK ) );
145     aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
146 
147     Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
148     aWriter.DrawRect( aRect );
149     aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 1" ) ) );
150     sal_Int32 nFirstLink = aWriter.CreateLink( aRect );
151     PDFNote aNote;
152     aNote.Title = String( RTL_CONSTASCII_USTRINGPARAM( "A small test note" ) );
153     aNote.Contents = String( RTL_CONSTASCII_USTRINGPARAM( "There is no business like show business like no business i know. Everything about it is appealing." ) );
154     aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote );
155 
156     Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
157     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
158     aWriter.DrawRect( aTargetRect );
159     aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest second link" ) ) );
160     sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect );
161 
162     aWriter.BeginStructureElement( PDFWriter::Section );
163     aWriter.BeginStructureElement( PDFWriter::Heading );
164     aWriter.DrawText( Point(4500, 9000), String( RTL_CONSTASCII_USTRINGPARAM( "A small structure test" ) ) );
165     aWriter.EndStructureElement();
166     aWriter.BeginStructureElement( PDFWriter::Paragraph );
167     aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
168     aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline );
169     aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
170                       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." ) ),
171                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
172                       );
173     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." ) ) );
174     aWriter.SetAlternateText( String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." ) ) );
175     aWriter.EndStructureElement();
176     sal_Int32 nLongPara = aWriter.BeginStructureElement( PDFWriter::Paragraph );
177     aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
178     aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
179                       String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph is nothing special either but ends on the next page structurewise" ) ),
180                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
181                       );
182 
183     aWriter.NewPage( 595, 842 );
184     // test AddStream interface
185     aWriter.AddStream( String( RTL_CONSTASCII_USTRINGPARAM( "text/plain" ) ), new PDFTestOutputStream(), true );
186     // set transitional mode
187     aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 );
188     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
189     aWriter.SetTextColor( Color( COL_BLACK ) );
190     aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
191     aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
192                       String( RTL_CONSTASCII_USTRINGPARAM( "Here's where all things come to an end ... well at least the paragaph from the last page." ) ),
193                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
194                       );
195     aWriter.EndStructureElement();
196 
197     aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
198     // disable structure
199     aWriter.BeginStructureElement( PDFWriter::NonStructElement );
200     aWriter.DrawRect( aRect );
201     aWriter.BeginStructureElement( PDFWriter::Paragraph );
202     aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 2" ) ) );
203     sal_Int32 nSecondLink = aWriter.CreateLink( aRect );
204 
205     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
206     aWriter.BeginStructureElement( PDFWriter::ListItem );
207     aWriter.DrawRect( aTargetRect );
208     aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest first link" ) ) );
209     sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect );
210     // enable structure
211     aWriter.EndStructureElement();
212     // add something to the long paragraph as an afterthought
213     sal_Int32 nSaveStruct = aWriter.GetCurrentStructureElement();
214     aWriter.SetCurrentStructureElement( nLongPara );
215     aWriter.DrawText( Rectangle( Point( 4500,4500 ),  Size( 12000, 1000 ) ),
216                       String( RTL_CONSTASCII_USTRINGPARAM( "Add something to the longish paragraph above." ) ),
217                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
218     aWriter.SetCurrentStructureElement( nSaveStruct );
219     aWriter.EndStructureElement();
220     aWriter.EndStructureElement();
221     aWriter.BeginStructureElement( PDFWriter::Figure );
222     aWriter.BeginStructureElement( PDFWriter::Caption );
223     aWriter.DrawText( Point( 4500, 9000 ), String( RTL_CONSTASCII_USTRINGPARAM( "Some drawing stuff inside the structure" ) ) );
224     aWriter.EndStructureElement();
225 
226     // test clipping
227     basegfx::B2DPolyPolygon aClip;
228     basegfx::B2DPolygon aClipPoly;
229     aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) );
230     aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) );
231     aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) );
232     aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) );
233     aClipPoly.setClosed( true );
234     //aClipPoly.flip();
235     aClip.append( aClipPoly );
236 
237     aWriter.Push( PUSH_CLIPREGION | PUSH_FILLCOLOR );
238     aWriter.SetClipRegion( aClip );
239     aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
240     aWriter.MoveClipRegion( 1000, 500 );
241     aWriter.SetFillColor( Color( COL_RED ) );
242     aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
243     aWriter.Pop();
244     // test transparency
245     // draw background
246     Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
247     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
248     aWriter.DrawRect( aTranspRect );
249     aWriter.BeginTransparencyGroup();
250 
251     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
252     aWriter.DrawEllipse( aTranspRect );
253     aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
254     aWriter.DrawText( aTranspRect,
255                       String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
256                       TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
257 
258     aWriter.EndTransparencyGroup( aTranspRect, 50 );
259 
260     // prepare an alpha mask
261     Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
262     BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess();
263     for( int nX = 0; nX < 256; nX++ )
264         for( int nY = 0; nY < 256; nY++ )
265             pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) );
266     aTransMask.ReleaseAccess( pAcc );
267     aTransMask.SetPrefMapMode( MAP_MM );
268     aTransMask.SetPrefSize( Size( 10, 10 ) );
269 
270     aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask );
271 
272     aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
273     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
274     aWriter.DrawRect( aTranspRect );
275     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
276     aWriter.DrawEllipse( aTranspRect );
277     aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
278     aWriter.DrawText( aTranspRect,
279                       String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
280                       TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
281     aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
282     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
283     aWriter.DrawRect( aTranspRect );
284     aWriter.BeginTransparencyGroup();
285     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
286     aWriter.DrawEllipse( aTranspRect );
287     aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
288     aWriter.DrawText( aTranspRect,
289                       String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
290                       TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
291     aWriter.EndTransparencyGroup( aTranspRect, aTransMask );
292 
293     Bitmap aImageBmp( Size( 256, 256 ), 24 );
294     pAcc = aImageBmp.AcquireWriteAccess();
295     pAcc->SetFillColor( Color( 0xff, 0, 0xff ) );
296     pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
297     aImageBmp.ReleaseAccess( pAcc );
298     BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) );
299     aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx );
300 
301 
302     aWriter.EndStructureElement();
303     aWriter.EndStructureElement();
304 
305     LineInfo aLI( LINE_DASH, 3 );
306     aLI.SetDashCount( 2 );
307     aLI.SetDashLen( 50 );
308     aLI.SetDotCount( 2 );
309     aLI.SetDotLen( 25 );
310     aLI.SetDistance( 15 );
311     Point aLIPoints[] = { Point( 4000, 10000 ),
312                           Point( 8000, 12000 ),
313                           Point( 3000, 19000 ) };
314     Polygon aLIPoly( 3, aLIPoints );
315     aWriter.SetLineColor( Color( COL_BLUE ) );
316     aWriter.SetFillColor();
317     aWriter.DrawPolyLine( aLIPoly, aLI );
318 
319     aLI.SetDashCount( 4 );
320     aLIPoly.Move( 1000, 1000 );
321     aWriter.DrawPolyLine( aLIPoly, aLI );
322 
323     aWriter.NewPage( 595, 842 );
324     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
325     Wallpaper aWall( aTransMask );
326     aWall.SetStyle( WALLPAPER_TILE );
327     aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall );
328 
329     aWriter.Push( PUSH_ALL );
330     aWriter.BeginPattern(Rectangle(Point(0,0),Size(2000,1000)));
331     aWriter.SetFillColor( Color( COL_RED ) );
332     aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
333     Point aFillPoints[] = { Point( 1000, 0 ),
334                             Point( 0, 1000 ),
335                             Point( 2000, 1000 ) };
336     aWriter.DrawPolygon( Polygon( 3, aFillPoints ) );
337     aWriter.DrawBitmap( Point( 200, 200 ), Size( 1600, 600 ), aTransMask );
338     aWriter.DrawText( Rectangle( Point( 200, 200 ), Size( 1600, 600 ) ), String( RTL_CONSTASCII_USTRINGPARAM( "Pattern" ) ) );
339     sal_Int32 nPattern = aWriter.EndPattern( SvtGraphicFill::Transform() );
340     aWriter.Pop();
341     Rectangle aPolyRect( Point( 3800, 11200 ), Size( 10200, 6300 ) );
342     aWriter.DrawPolyPolygon( PolyPolygon( Polygon( aPolyRect ) ), nPattern, true );
343     aWriter.SetFillColor();
344     aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
345     aWriter.DrawRect( aPolyRect );
346 
347     aWriter.NewPage( 595, 842 );
348     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
349     aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
350     aWriter.SetTextColor( Color( COL_BLACK ) );
351     aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
352     aWriter.DrawRect( aRect );
353     aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "www.heise.de" ) ) );
354     sal_Int32 nURILink = aWriter.CreateLink( aRect );
355     aWriter.SetLinkURL( nURILink, OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) ) );
356 
357     aWriter.SetLinkDest( nFirstLink, nFirstDest );
358     aWriter.SetLinkDest( nSecondLink, nSecondDest );
359 
360     // include a button
361     PDFWriter::PushButtonWidget aBtn;
362     aBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testButton" ) );
363     aBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test button" ) );
364     aBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "hit me" ) );
365     aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
366     aBtn.Border = aBtn.Background = true;
367     aWriter.CreateControl( aBtn );
368 
369     // include a uri button
370     PDFWriter::PushButtonWidget aUriBtn;
371     aUriBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "wwwButton" ) );
372     aUriBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A URI button" ) );
373     aUriBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to www" ) );
374     aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
375     aUriBtn.Border = aUriBtn.Background = true;
376     aUriBtn.URL = OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) );
377     aWriter.CreateControl( aUriBtn );
378 
379     // include a dest button
380     PDFWriter::PushButtonWidget aDstBtn;
381     aDstBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "destButton" ) );
382     aDstBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A Dest button" ) );
383     aDstBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to paragraph" ) );
384     aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
385     aDstBtn.Border = aDstBtn.Background = true;
386     aDstBtn.Dest = nFirstDest;
387     aWriter.CreateControl( aDstBtn );
388 
389     PDFWriter::CheckBoxWidget aCBox;
390     aCBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox" ) );
391     aCBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test check box" ) );
392     aCBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me" ) );
393     aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
394     aCBox.Checked = true;
395     aCBox.Border = aCBox.Background = false;
396     aWriter.CreateControl( aCBox );
397 
398     PDFWriter::CheckBoxWidget aCBox2;
399     aCBox2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox2" ) );
400     aCBox2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "Another test check box" ) );
401     aCBox2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me right" ) );
402     aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
403     aCBox2.Checked = true;
404     aCBox2.Border = aCBox2.Background = false;
405     aCBox2.ButtonIsLeft = false;
406     aWriter.CreateControl( aCBox2 );
407 
408     PDFWriter::RadioButtonWidget aRB1;
409     aRB1.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_1" ) );
410     aRB1.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 1" ) );
411     aRB1.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Despair" ) );
412     aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
413     aRB1.Selected = true;
414     aRB1.RadioGroup = 1;
415     aRB1.Border = aRB1.Background = true;
416     aRB1.ButtonIsLeft = false;
417     aRB1.BorderColor = Color( COL_LIGHTGREEN );
418     aRB1.BackgroundColor = Color( COL_LIGHTBLUE );
419     aRB1.TextColor = Color( COL_LIGHTRED );
420     aRB1.TextFont = Font( String( RTL_CONSTASCII_USTRINGPARAM( "Courier" ) ), Size( 0, 800 ) );
421     aWriter.CreateControl( aRB1 );
422 
423     PDFWriter::RadioButtonWidget aRB2;
424     aRB2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb2_1" ) );
425     aRB2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 2 button 1" ) );
426     aRB2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Joy" ) );
427     aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
428     aRB2.Selected = true;
429     aRB2.RadioGroup = 2;
430     aWriter.CreateControl( aRB2 );
431 
432     PDFWriter::RadioButtonWidget aRB3;
433     aRB3.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_2" ) );
434     aRB3.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 2" ) );
435     aRB3.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Desperation" ) );
436     aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
437     aRB3.Selected = true;
438     aRB3.RadioGroup = 1;
439     aWriter.CreateControl( aRB3 );
440 
441     PDFWriter::EditWidget aEditBox;
442     aEditBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testEdit" ) );
443     aEditBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test edit field" ) );
444     aEditBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "A little test text" ) );
445     aEditBox.TextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
446     aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
447     aEditBox.MaxLen = 100;
448     aEditBox.Border = aEditBox.Background = true;
449     aEditBox.BorderColor = Color( COL_BLACK );
450     aWriter.CreateControl( aEditBox );
451 
452     // normal list box
453     PDFWriter::ListBoxWidget aLstBox;
454     aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testListBox" ) );
455     aLstBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) );
456     aLstBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "select me" ) );
457     aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
458     aLstBox.Sort = true;
459     aLstBox.MultiSelect = true;
460     aLstBox.Border = aLstBox.Background = true;
461     aLstBox.BorderColor = Color( COL_BLACK );
462     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) ) );
463     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Two" ) ) );
464     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Three" ) ) );
465     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Four" ) ) );
466     aLstBox.SelectedEntries.push_back( 1 );
467     aLstBox.SelectedEntries.push_back( 2 );
468     aWriter.CreateControl( aLstBox );
469 
470     // dropdown list box
471     aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testDropDownListBox" ) );
472     aLstBox.DropDown = true;
473     aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
474     aWriter.CreateControl( aLstBox );
475 
476     // combo box
477     PDFWriter::ComboBoxWidget aComboBox;
478     aComboBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testComboBox" ) );
479     aComboBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "test a combobox" ) );
480     aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Larry" ) ) );
481     aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Curly" ) ) );
482     aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Moe" ) ) );
483     aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
484     aWriter.CreateControl( aComboBox );
485 
486     // test outlines
487     sal_Int32 nPage1OL = aWriter.CreateOutlineItem();
488     aWriter.SetOutlineItemText( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 1" ) ) );
489     aWriter.SetOutlineItemDest( nPage1OL, nSecondDest );
490     aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2" ) ), nSecondDest );
491     aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 revisited" ) ), nSecondDest );
492     aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 again" ) ), nSecondDest );
493     sal_Int32 nPage2OL = aWriter.CreateOutlineItem();
494     aWriter.SetOutlineItemText( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 2" ) ) );
495     aWriter.CreateOutlineItem( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 1" ) ), nFirstDest );
496 
497     aWriter.EndStructureElement(); // close document
498     aWriter.Emit();
499 }
500 #endif
501 
502 static const sal_Int32 nLog10Divisor = 1;
503 static const double fDivisor = 10.0;
504 
505 static inline double pixelToPoint( sal_Int32 px ) { return double(px)/fDivisor; }
506 static inline double pixelToPoint( double px ) { return px/fDivisor; }
507 static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); }
508 
509 const sal_uInt8 PDFWriterImpl::s_nPadString[32] =
510 {
511     0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
512     0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
513 };
514 
515 static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
516 {
517     static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
518                                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
519     rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
520     rBuffer.append( pHexDigits[ nInt & 15 ] );
521 }
522 
523 static void appendName( const OUString& rStr, OStringBuffer& rBuffer )
524 {
525 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
526 // I guess than when reading the #xx sequence it will count for a single character.
527     OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) );
528     const sal_Char* pStr = aStr.getStr();
529     int nLen = aStr.getLength();
530     for( int i = 0; i < nLen; i++ )
531     {
532         /*  #i16920# PDF recommendation: output UTF8, any byte
533          *  outside the interval [33(=ASCII'!');126(=ASCII'~')]
534          *  should be escaped hexadecimal
535          *  for the sake of ghostscript which also reads PDF
536          *  but has a narrower acceptance rate we only pass
537          *  alphanumerics and '-' literally.
538          */
539         if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) ||
540             (pStr[i] >= 'a' && pStr[i] <= 'z' ) ||
541             (pStr[i] >= '0' && pStr[i] <= '9' ) ||
542             pStr[i] == '-' )
543         {
544             rBuffer.append( pStr[i] );
545         }
546         else
547         {
548             rBuffer.append( '#' );
549             appendHex( (sal_Int8)pStr[i], rBuffer );
550         }
551     }
552 }
553 
554 static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer )
555 {
556 //FIXME i59651 see above
557     while( pStr && *pStr )
558     {
559         if( (*pStr >= 'A' && *pStr <= 'Z' ) ||
560             (*pStr >= 'a' && *pStr <= 'z' ) ||
561             (*pStr >= '0' && *pStr <= '9' ) ||
562             *pStr == '-' )
563         {
564             rBuffer.append( *pStr );
565         }
566         else
567         {
568             rBuffer.append( '#' );
569             appendHex( (sal_Int8)*pStr, rBuffer );
570         }
571         pStr++;
572     }
573 }
574 
575 //used only to emit encoded passwords
576 static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer )
577 {
578 	while( nLength )
579 	{
580 		switch( *pStr )
581 		{
582 		case '\n' :
583 			rBuffer.append( "\\n" );
584 			break;
585 		case '\r' :
586 			rBuffer.append( "\\r" );
587 			break;
588 		case '\t' :
589 			rBuffer.append( "\\t" );
590 			break;
591 		case '\b' :
592 			rBuffer.append( "\\b" );
593 			break;
594 		case '\f' :
595 			rBuffer.append( "\\f" );
596 			break;
597 		case '(' :
598 		case ')' :
599 		case '\\' :
600 			rBuffer.append( "\\" );
601 			rBuffer.append( (sal_Char) *pStr );
602 			break;
603 		default:
604 			rBuffer.append( (sal_Char) *pStr );
605 			break;
606 		}
607 		pStr++;
608 		nLength--;
609 	}
610 }
611 
612 /**--->i56629
613  * Convert a string before using it.
614  *
615  * This string conversion function is needed because the destination name
616  * in a PDF file seen through an Internet browser should be
617  * specially crafted, in order to be used directly by the browser.
618  * In this way the fragment part of a hyperlink to a PDF file (e.g. something
619  * as 'test1/test2/a-file.pdf#thefragment) will be (hopefully) interpreted by the
620  * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
621  * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
622  * and go to named destination thefragment using default zoom'.
623  * The conversion is needed because in case of a fragment in the form: Slide%201
624  * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
625  * using this conversion, in both the generated named destinations, fragment and GoToR
626  * destination.
627  *
628  * The names for destinations are name objects and so they don't need to be encrypted
629  * even though they expose the content of PDF file (e.g. guessing the PDF content from the
630  * destination name).
631  *
632  * Fhurter limitation: it is advisable to use standard ASCII characters for
633  * OOo bookmarks.
634 */
635 static void appendDestinationName( const rtl::OUString& rString, OStringBuffer& rBuffer )
636 {
637     const sal_Unicode* pStr = rString.getStr();
638     sal_Int32 nLen = rString.getLength();
639     for( int i = 0; i < nLen; i++ )
640     {
641         sal_Unicode aChar = pStr[i];
642         if( (aChar >= '0' && aChar <= '9' ) ||
643             (aChar >= 'a' && aChar <= 'z' ) ||
644             (aChar >= 'A' && aChar <= 'Z' ) ||
645             aChar == '-' )
646         {
647             rBuffer.append((sal_Char)aChar);
648         }
649         else
650         {
651             sal_Int8 aValueHigh = sal_Int8(aChar >> 8);
652             if(aValueHigh > 0)
653                 appendHex( aValueHigh, rBuffer );
654             appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
655         }
656     }
657 }
658 //<--- i56629
659 
660 static void appendUnicodeTextString( const rtl::OUString& rString, OStringBuffer& rBuffer )
661 {
662 	rBuffer.append( "FEFF" );
663 	const sal_Unicode* pStr = rString.getStr();
664 	sal_Int32 nLen = rString.getLength();
665 	for( int i = 0; i < nLen; i++ )
666 	{
667 		sal_Unicode aChar = pStr[i];
668 		appendHex( (sal_Int8)(aChar >> 8), rBuffer );
669 		appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
670 	}
671 }
672 
673 void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl )
674 {
675     /* #i80258# previously we use appendName here
676        however we need a slightly different coding scheme than the normal
677        name encoding for field names
678     */
679     const OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ? i_rControl.Name : i_rControl.Text;
680     OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) );
681     const sal_Char* pStr = aStr.getStr();
682     int nLen = aStr.getLength();
683 
684     OStringBuffer aBuffer( rName.getLength()+64 );
685     for( int i = 0; i < nLen; i++ )
686     {
687         /*  #i16920# PDF recommendation: output UTF8, any byte
688          *  outside the interval [32(=ASCII' ');126(=ASCII'~')]
689          *  should be escaped hexadecimal
690          */
691         if( (pStr[i] >= 32 && pStr[i] <= 126 ) )
692             aBuffer.append( pStr[i] );
693         else
694         {
695             aBuffer.append( '#' );
696             appendHex( (sal_Int8)pStr[i], aBuffer );
697         }
698     }
699 
700     OString aFullName( aBuffer.makeStringAndClear() );
701 
702     /* #i82785# create hierarchical fields down to the for each dot in i_rName */
703     sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0;
704     OString aPartialName;
705     OString aDomain;
706     do
707     {
708         nLastTokenIndex = nTokenIndex;
709         aPartialName = aFullName.getToken( 0, '.', nTokenIndex );
710         if( nTokenIndex != -1 )
711         {
712             // find or create a hierarchical field
713             // first find the fully qualified name up to this field
714             aDomain = aFullName.copy( 0, nTokenIndex-1 );
715             std::hash_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
716             if( it == m_aFieldNameMap.end() )
717             {
718                  // create new hierarchy field
719                 sal_Int32 nNewWidget = m_aWidgets.size();
720                 m_aWidgets.push_back( PDFWidget() );
721                 m_aWidgets[nNewWidget].m_nObject = createObject();
722                 m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy;
723                 m_aWidgets[nNewWidget].m_aName = aPartialName;
724                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
725                 m_aFieldNameMap[aDomain] = nNewWidget;
726                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
727                 if( nLastTokenIndex > 0 )
728                 {
729                     // this field is not a root field and
730                     // needs to be inserted to its parent
731                     OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) );
732                     it = m_aFieldNameMap.find( aParentDomain );
733                     OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" );
734                     if( it != m_aFieldNameMap.end()  )
735                     {
736                         OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" );
737                         if( it->second < sal_Int32(m_aWidgets.size()) )
738                         {
739                             PDFWidget& rParentField( m_aWidgets[it->second] );
740                             rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject );
741                             rParentField.m_aKidsIndex.push_back( nNewWidget );
742                             m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject;
743                         }
744                     }
745                 }
746             }
747             else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy )
748             {
749                 // this is invalid, someone tries to have a terminal field as parent
750                 // example: a button with the name foo.bar exists and
751                 // another button is named foo.bar.no
752                 // workaround: put the second terminal field as much up in the hierarchy as
753                 // necessary to have a non-terminal field as parent (or none at all)
754                 // since it->second already is terminal, we just need to use its parent
755                 aDomain = OString();
756                 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
757                 if( nLastTokenIndex > 0 )
758                 {
759                     aDomain = aFullName.copy( 0, nLastTokenIndex-1 );
760                     OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() );
761                     aBuf.append( aDomain );
762                     aBuf.append( '.' );
763                     aBuf.append( aPartialName );
764                     aFullName = aBuf.makeStringAndClear();
765                 }
766                 else
767                     aFullName = aPartialName;
768                 break;
769             }
770         }
771     } while( nTokenIndex != -1 );
772 
773     // insert widget into its hierarchy field
774     if( aDomain.getLength() )
775     {
776         std::hash_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
777         if( it != m_aFieldNameMap.end() )
778         {
779             OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" );
780             if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) )
781             {
782                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject;
783                 m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject);
784                 m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex );
785             }
786         }
787     }
788 
789     if( aPartialName.getLength() == 0 )
790     {
791         // how funny, an empty field name
792         if( i_rControl.getType() == PDFWriter::RadioButton )
793         {
794             aPartialName  = "RadioGroup";
795             aPartialName += OString::valueOf( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup );
796         }
797         else
798             aPartialName = OString( "Widget" );
799     }
800 
801     if( ! m_aContext.AllowDuplicateFieldNames )
802     {
803         std::hash_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aFullName );
804 
805         if( it != m_aFieldNameMap.end() ) // not unique
806         {
807             std::hash_map< OString, sal_Int32, OStringHash >::const_iterator check_it;
808             OString aTry;
809             sal_Int32 nTry = 2;
810             do
811             {
812                 OStringBuffer aUnique( aFullName.getLength() + 16 );
813                 aUnique.append( aFullName );
814                 aUnique.append( '_' );
815                 aUnique.append( nTry++ );
816                 aTry = aUnique.makeStringAndClear();
817                 check_it = m_aFieldNameMap.find( aTry );
818             } while( check_it != m_aFieldNameMap.end() );
819             aFullName = aTry;
820             m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
821             aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
822         }
823         else
824             m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
825     }
826 
827     // finally
828     m_aWidgets[i_nWidgetIndex].m_aName = aPartialName;
829 }
830 
831 static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor )
832 {
833     if( nValue < 0 )
834     {
835         rBuffer.append( '-' );
836         nValue = -nValue;
837     }
838     sal_Int32 nFactor = 1, nDiv = nPrecision;
839     while( nDiv-- )
840         nFactor *= 10;
841 
842     sal_Int32 nInt		= nValue / nFactor;
843     rBuffer.append( nInt );
844     if( nFactor > 1 )
845     {
846         sal_Int32 nDecimal	= nValue % nFactor;
847         if( nDecimal )
848         {
849             rBuffer.append( '.' );
850             // omit trailing zeros
851             while( (nDecimal % 10) == 0 )
852                 nDecimal /= 10;
853             rBuffer.append( nDecimal );
854         }
855     }
856 }
857 
858 
859 // appends a double. PDF does not accept exponential format, only fixed point
860 static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 )
861 {
862     bool bNeg = false;
863     if( fValue < 0.0 )
864     {
865         bNeg = true;
866         fValue=-fValue;
867     }
868 
869     sal_Int64 nInt = (sal_Int64)fValue;
870     fValue -= (double)nInt;
871     // optimizing hardware may lead to a value of 1.0 after the subtraction
872     if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision )
873     {
874         nInt++;
875         fValue = 0.0;
876     }
877     sal_Int64 nFrac = 0;
878     if( fValue )
879     {
880         fValue *= pow( 10.0, (double)nPrecision );
881         nFrac = (sal_Int64)fValue;
882     }
883     if( bNeg && ( nInt || nFrac ) )
884         rBuffer.append( '-' );
885     rBuffer.append( nInt );
886     if( nFrac )
887     {
888 		int i;
889         rBuffer.append( '.' );
890 		sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5);
891 		for ( i = 0; ( i < nPrecision ) && nFrac; i++ )
892 		{
893 			sal_Int64 nNumb = nFrac / nBound;
894 			nFrac -= nNumb * nBound;
895 			rBuffer.append( nNumb );
896 			nBound /= 10;
897 		}
898     }
899 }
900 
901 
902 static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey = false )
903 {
904 
905     if( rColor != Color( COL_TRANSPARENT ) )
906     {
907         if( bConvertToGrey )
908         {
909             sal_uInt8 cByte = rColor.GetLuminance();
910             appendDouble( (double)cByte / 255.0, rBuffer );
911         }
912         else
913         {
914             appendDouble( (double)rColor.GetRed() / 255.0, rBuffer );
915             rBuffer.append( ' ' );
916             appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer );
917             rBuffer.append( ' ' );
918             appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer );
919         }
920     }
921 }
922 
923 void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
924 {
925     if( rColor != Color( COL_TRANSPARENT ) )
926     {
927         bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
928         appendColor( rColor, rBuffer, bGrey );
929         rBuffer.append( bGrey ? " G" : " RG" );
930     }
931 }
932 
933 void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
934 {
935     if( rColor != Color( COL_TRANSPARENT ) )
936     {
937         bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
938         appendColor( rColor, rBuffer, bGrey );
939         rBuffer.append( bGrey ? " g" : " rg" );
940     }
941 }
942 
943 // matrix helper class
944 // TODO: use basegfx matrix class instead or derive from it
945 namespace vcl // TODO: use anonymous namespace to keep this class local
946 {
947 /*	for sparse matrices of the form (2D linear transformations)
948  *  f[0] f[1] 0
949  *  f[2] f[3] 0
950  *  f[4] f[5] 1
951  */
952 class Matrix3
953 {
954     double f[6];
955 
956     void set( double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; }
957 public:
958     Matrix3();
959     ~Matrix3() {}
960 
961     void skew( double alpha, double beta );
962     void scale( double sx, double sy );
963     void rotate( double angle );
964     void translate( double tx, double ty );
965     bool invert();
966 
967     void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL );
968 
969     Point transform( const Point& rPoint ) const;
970 };
971 }
972 
973 Matrix3::Matrix3()
974 {
975     // initialize to unity
976     f[0] = 1.0;
977     f[1] = 0.0;
978     f[2] = 0.0;
979     f[3] = 1.0;
980     f[4] = 0.0;
981     f[5] = 0.0;
982 }
983 
984 Point Matrix3::transform( const Point& rOrig ) const
985 {
986     double x = (double)rOrig.X(), y = (double)rOrig.Y();
987     return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) );
988 }
989 
990 void Matrix3::skew( double alpha, double beta )
991 {
992     double fn[6];
993     double tb = tan( beta );
994     fn[0] = f[0] + f[2]*tb;
995     fn[1] = f[1];
996     fn[2] = f[2] + f[3]*tb;
997     fn[3] = f[3];
998     fn[4] = f[4] + f[5]*tb;
999     fn[5] = f[5];
1000     if( alpha != 0.0 )
1001     {
1002         double ta = tan( alpha );
1003         fn[1] += f[0]*ta;
1004         fn[3] += f[2]*ta;
1005         fn[5] += f[4]*ta;
1006     }
1007     set( fn );
1008 }
1009 
1010 void Matrix3::scale( double sx, double sy )
1011 {
1012     double fn[6];
1013     fn[0] = sx*f[0];
1014     fn[1] = sy*f[1];
1015     fn[2] = sx*f[2];
1016     fn[3] = sy*f[3];
1017     fn[4] = sx*f[4];
1018     fn[5] = sy*f[5];
1019     set( fn );
1020 }
1021 
1022 void Matrix3::rotate( double angle )
1023 {
1024     double fn[6];
1025     double fSin = sin(angle);
1026     double fCos = cos(angle);
1027     fn[0] = f[0]*fCos - f[1]*fSin;
1028     fn[1] = f[0]*fSin + f[1]*fCos;
1029     fn[2] = f[2]*fCos - f[3]*fSin;
1030     fn[3] = f[2]*fSin + f[3]*fCos;
1031     fn[4] = f[4]*fCos - f[5]*fSin;
1032     fn[5] = f[4]*fSin + f[5]*fCos;
1033     set( fn );
1034 }
1035 
1036 void Matrix3::translate( double tx, double ty )
1037 {
1038     f[4] += tx;
1039     f[5] += ty;
1040 }
1041 
1042 bool Matrix3::invert()
1043 {
1044 	// short circuit trivial cases
1045 	if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 )
1046 	{
1047 		f[4] = -f[4];
1048 		f[5] = -f[5];
1049 		return true;
1050 	}
1051 
1052 	// check determinant
1053 	const double fDet = f[0]*f[3]-f[1]*f[2];
1054 	if( fDet == 0.0 )
1055 		return false;
1056 
1057 	// invert the matrix
1058 	double fn[6];
1059 	fn[0] = +f[3] / fDet;
1060 	fn[1] = -f[1] / fDet;
1061 	fn[2] = -f[2] / fDet;
1062 	fn[3] = +f[0] / fDet;
1063 
1064 	// apply inversion to translation
1065 	fn[4] = -(f[4]*fn[0] + f[5]*fn[2]);
1066 	fn[5] = -(f[4]*fn[1] + f[5]*fn[3]);
1067 
1068 	set( fn );
1069 	return true;
1070 }
1071 
1072 void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack )
1073 {
1074     appendDouble( f[0], rBuffer );
1075     rBuffer.append( ' ' );
1076     appendDouble( f[1], rBuffer );
1077     rBuffer.append( ' ' );
1078     appendDouble( f[2], rBuffer );
1079     rBuffer.append( ' ' );
1080     appendDouble( f[3], rBuffer );
1081     rBuffer.append( ' ' );
1082     rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack );
1083 }
1084 
1085 static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList )
1086 {
1087     if( rList.empty() )
1088         return;
1089     rBuf.append( '/' );
1090     rBuf.append( pPrefix );
1091     rBuf.append( "<<" );
1092     int ni = 0;
1093     for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it )
1094     {
1095         if( it->first.getLength() && it->second > 0 )
1096         {
1097             rBuf.append( '/' );
1098             rBuf.append( it->first );
1099             rBuf.append( ' ' );
1100             rBuf.append( it->second );
1101             rBuf.append( " 0 R" );
1102             if( ((++ni) & 7) == 0 )
1103                 rBuf.append( '\n' );
1104         }
1105     }
1106     rBuf.append( ">>\n" );
1107 }
1108 
1109 void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject )
1110 {
1111     rBuf.append( "<</Font " );
1112     rBuf.append( nFontDictObject );
1113     rBuf.append( " 0 R\n" );
1114     appendResourceMap( rBuf, "XObject", m_aXObjects );
1115     appendResourceMap( rBuf, "ExtGState", m_aExtGStates );
1116     appendResourceMap( rBuf, "Shading", m_aShadings );
1117     appendResourceMap( rBuf, "Pattern", m_aPatterns );
1118     rBuf.append( "/ProcSet[/PDF/Text" );
1119     if( !m_aXObjects.empty() )
1120         rBuf.append( "/ImageC/ImageI/ImageB" );
1121     rBuf.append( "]\n>>\n" );
1122 };
1123 
1124 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
1125         :
1126         m_pWriter( pWriter ),
1127         m_nPageWidth( nPageWidth ),
1128         m_nPageHeight( nPageHeight ),
1129         m_eOrientation( eOrientation ),
1130         m_nPageObject( 0 ),  // invalid object number
1131         m_nPageIndex( -1 ), // invalid index
1132         m_nStreamLengthObject( 0 ),
1133         m_nBeginStreamPos( 0 ),
1134         m_eTransition( PDFWriter::Regular ),
1135         m_nTransTime( 0 ),
1136         m_nDuration( 0 ),
1137         m_bHasWidgets( false )
1138 {
1139     // object ref must be only ever updated in emit()
1140     m_nPageObject = m_pWriter->createObject();
1141 }
1142 
1143 PDFWriterImpl::PDFPage::~PDFPage()
1144 {
1145 }
1146 
1147 void PDFWriterImpl::PDFPage::beginStream()
1148 {
1149 #if OSL_DEBUG_LEVEL > 1
1150     {
1151         OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" );
1152          m_pWriter->emitComment( aLine.getStr() );
1153     }
1154 #endif
1155     m_aStreamObjects.push_back(m_pWriter->createObject());
1156     if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) )
1157         return;
1158 
1159     m_nStreamLengthObject = m_pWriter->createObject();
1160     // write content stream header
1161     OStringBuffer aLine;
1162     aLine.append( m_aStreamObjects.back() );
1163     aLine.append( " 0 obj\n<</Length " );
1164     aLine.append( m_nStreamLengthObject );
1165     aLine.append( " 0 R" );
1166 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1167     aLine.append( "/Filter/FlateDecode" );
1168 #endif
1169     aLine.append( ">>\nstream\n" );
1170     if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) )
1171         return;
1172     if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &m_nBeginStreamPos ) )
1173     {
1174         osl_closeFile( m_pWriter->m_aFile );
1175         m_pWriter->m_bOpen = false;
1176     }
1177 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1178     m_pWriter->beginCompression();
1179 #endif
1180     m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() );
1181 }
1182 
1183 void PDFWriterImpl::PDFPage::endStream()
1184 {
1185 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1186     m_pWriter->endCompression();
1187 #endif
1188     sal_uInt64 nEndStreamPos;
1189     if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &nEndStreamPos ) )
1190     {
1191         osl_closeFile( m_pWriter->m_aFile );
1192         m_pWriter->m_bOpen = false;
1193         return;
1194     }
1195     m_pWriter->disableStreamEncryption();
1196     if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1197         return;
1198     // emit stream length object
1199     if( ! m_pWriter->updateObject( m_nStreamLengthObject ) )
1200         return;
1201     OStringBuffer aLine;
1202     aLine.append( m_nStreamLengthObject );
1203     aLine.append( " 0 obj\n" );
1204     aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) );
1205     aLine.append( "\nendobj\n\n" );
1206     m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1207 }
1208 
1209 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject )
1210 {
1211     // emit page object
1212     if( ! m_pWriter->updateObject( m_nPageObject ) )
1213         return false;
1214     OStringBuffer aLine;
1215 
1216     aLine.append( m_nPageObject );
1217     aLine.append( " 0 obj\n"
1218                   "<</Type/Page/Parent " );
1219     aLine.append( nParentObject );
1220     aLine.append( " 0 R" );
1221     aLine.append( "/Resources " );
1222     aLine.append( m_pWriter->getResourceDictObj() );
1223     aLine.append( " 0 R" );
1224     if( m_nPageWidth && m_nPageHeight )
1225     {
1226         aLine.append( "/MediaBox[0 0 " );
1227         aLine.append( m_nPageWidth );
1228         aLine.append( ' ' );
1229         aLine.append( m_nPageHeight );
1230         aLine.append( "]" );
1231     }
1232     switch( m_eOrientation )
1233     {
1234         case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
1235         case PDFWriter::Seascape:  aLine.append( "/Rotate -90\n" );break;
1236         case PDFWriter::Portrait:  aLine.append( "/Rotate 0\n" );break;
1237 
1238         case PDFWriter::Inherit:
1239         default:
1240             break;
1241     }
1242     int nAnnots = m_aAnnotations.size();
1243     if( nAnnots > 0 )
1244     {
1245         aLine.append( "/Annots[\n" );
1246         for( int i = 0; i < nAnnots; i++ )
1247         {
1248             aLine.append( m_aAnnotations[i] );
1249             aLine.append( " 0 R" );
1250             aLine.append( ((i+1)%15) ? " " : "\n" );
1251         }
1252         aLine.append( "]\n" );
1253     }
1254     #if 0
1255     // FIXME: implement tab order as Structure Tree
1256     if( m_bHasWidgets && m_pWriter->getVersion() >= PDFWriter::PDF_1_5 )
1257         aLine.append( "   /Tabs /S\n" );
1258     #endif
1259     if( m_aMCIDParents.size() > 0 )
1260     {
1261         OStringBuffer aStructParents( 1024 );
1262         aStructParents.append( "[ " );
1263         int nParents = m_aMCIDParents.size();
1264         for( int i = 0; i < nParents; i++ )
1265         {
1266             aStructParents.append( m_aMCIDParents[i] );
1267             aStructParents.append( " 0 R" );
1268             aStructParents.append( ((i%10) == 9) ? "\n" : " " );
1269         }
1270         aStructParents.append( "]" );
1271         m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() );
1272 
1273         aLine.append( "/StructParents " );
1274         aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) );
1275         aLine.append( "\n" );
1276     }
1277     if( m_nDuration > 0 )
1278     {
1279         aLine.append( "/Dur " );
1280         aLine.append( (sal_Int32)m_nDuration );
1281         aLine.append( "\n" );
1282     }
1283     if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 )
1284     {
1285         // transition duration
1286         aLine.append( "/Trans<</D " );
1287         appendDouble( (double)m_nTransTime/1000.0, aLine, 3 );
1288         aLine.append( "\n" );
1289         const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL;
1290         switch( m_eTransition )
1291         {
1292             case PDFWriter::SplitHorizontalInward:
1293                 pStyle = "Split"; pDm = "H"; pM = "I"; break;
1294             case PDFWriter::SplitHorizontalOutward:
1295                 pStyle = "Split"; pDm = "H"; pM = "O"; break;
1296             case PDFWriter::SplitVerticalInward:
1297                 pStyle = "Split"; pDm = "V"; pM = "I"; break;
1298             case PDFWriter::SplitVerticalOutward:
1299                 pStyle = "Split"; pDm = "V"; pM = "O"; break;
1300             case PDFWriter::BlindsHorizontal:
1301                 pStyle = "Blinds"; pDm = "H"; break;
1302             case PDFWriter::BlindsVertical:
1303                 pStyle = "Blinds"; pDm = "V"; break;
1304             case PDFWriter::BoxInward:
1305                 pStyle = "Box"; pM = "I"; break;
1306             case PDFWriter::BoxOutward:
1307                 pStyle = "Box"; pM = "O"; break;
1308             case PDFWriter::WipeLeftToRight:
1309                 pStyle = "Wipe"; pDi = "0"; break;
1310             case PDFWriter::WipeBottomToTop:
1311                 pStyle = "Wipe"; pDi = "90"; break;
1312             case PDFWriter::WipeRightToLeft:
1313                 pStyle = "Wipe"; pDi = "180"; break;
1314             case PDFWriter::WipeTopToBottom:
1315                 pStyle = "Wipe"; pDi = "270"; break;
1316             case PDFWriter::Dissolve:
1317                 pStyle = "Dissolve"; break;
1318             case PDFWriter::GlitterLeftToRight:
1319                 pStyle = "Glitter"; pDi = "0"; break;
1320             case PDFWriter::GlitterTopToBottom:
1321                 pStyle = "Glitter"; pDi = "270"; break;
1322             case PDFWriter::GlitterTopLeftToBottomRight:
1323                 pStyle = "Glitter"; pDi = "315"; break;
1324             case PDFWriter::Regular:
1325                 break;
1326         }
1327         // transition style
1328         if( pStyle )
1329         {
1330             aLine.append( "/S/" );
1331             aLine.append( pStyle );
1332             aLine.append( "\n" );
1333         }
1334         if( pDm )
1335         {
1336             aLine.append( "/Dm/" );
1337             aLine.append( pDm );
1338             aLine.append( "\n" );
1339         }
1340         if( pM )
1341         {
1342             aLine.append( "/M/" );
1343             aLine.append( pM );
1344             aLine.append( "\n" );
1345         }
1346         if( pDi  )
1347         {
1348             aLine.append( "/Di " );
1349             aLine.append( pDi );
1350             aLine.append( "\n" );
1351         }
1352         aLine.append( ">>\n" );
1353     }
1354     if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 )
1355     {
1356         aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1357     }
1358     aLine.append( "/Contents" );
1359     unsigned int nStreamObjects = m_aStreamObjects.size();
1360     if( nStreamObjects > 1 )
1361         aLine.append( '[' );
1362     for( unsigned int i = 0; i < m_aStreamObjects.size(); i++ )
1363     {
1364         aLine.append( ' ' );
1365         aLine.append( m_aStreamObjects[i] );
1366         aLine.append( " 0 R" );
1367     }
1368     if( nStreamObjects > 1 )
1369         aLine.append( ']' );
1370     aLine.append( ">>\nendobj\n\n" );
1371     return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1372 }
1373 
1374 namespace vcl
1375 {
1376 template < class GEOMETRY >
1377 GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
1378 {
1379     GEOMETRY aPoint;
1380     if ( MAP_PIXEL == _rSource.GetMapUnit() )
1381     {
1382         aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
1383     }
1384     else
1385     {
1386         aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
1387     }
1388     return aPoint;
1389 }
1390 }
1391 
1392 void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const
1393 {
1394     if( pOutPoint )
1395     {
1396         Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1397                                    m_pWriter->m_aMapMode,
1398                                    m_pWriter->getReferenceDevice(),
1399                                    rPoint ) );
1400         *pOutPoint = aPoint;
1401     }
1402 
1403     Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1404                                m_pWriter->m_aMapMode,
1405                                m_pWriter->getReferenceDevice(),
1406                                rPoint ) );
1407 
1408     sal_Int32 nValue	= aPoint.X();
1409     if( bNeg )
1410         nValue = -nValue;
1411 
1412     appendFixedInt( nValue, rBuffer );
1413 
1414     rBuffer.append( ' ' );
1415 
1416     nValue		= pointToPixel(getHeight()) - aPoint.Y();
1417     if( bNeg )
1418         nValue = -nValue;
1419 
1420     appendFixedInt( nValue, rBuffer );
1421 }
1422 
1423 void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const
1424 {
1425     double fValue	= pixelToPoint(rPoint.getX());
1426 
1427     appendDouble( fValue, rBuffer, nLog10Divisor );
1428 
1429     rBuffer.append( ' ' );
1430 
1431     fValue		= double(getHeight()) - pixelToPoint(rPoint.getY());
1432 
1433     appendDouble( fValue, rBuffer, nLog10Divisor );
1434 }
1435 
1436 void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const
1437 {
1438     appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer );
1439     rBuffer.append( ' ' );
1440     appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false );
1441     rBuffer.append( ' ' );
1442     appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true );
1443     rBuffer.append( " re" );
1444 }
1445 
1446 void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const
1447 {
1448     Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1449                              m_pWriter->m_aMapMode,
1450                              m_pWriter->getReferenceDevice(),
1451                              rRect.BottomLeft() + Point( 0, 1 )
1452                              );
1453     Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1454                               m_pWriter->m_aMapMode,
1455                               m_pWriter->getReferenceDevice(),
1456                               rRect.GetSize() );
1457     rRect.Left()	= aLL.X();
1458     rRect.Right()	= aLL.X() + aSize.Width();
1459     rRect.Top()		= pointToPixel(getHeight()) - aLL.Y();
1460     rRect.Bottom()	= rRect.Top() + aSize.Height();
1461 }
1462 
1463 void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1464 {
1465     sal_uInt16 nPoints = rPoly.GetSize();
1466     /*
1467      *  #108582# applications do weird things
1468      */
1469     sal_uInt32 nBufLen = rBuffer.getLength();
1470     if( nPoints > 0 )
1471     {
1472         const sal_uInt8* pFlagArray = rPoly.GetConstFlagAry();
1473         appendPoint( rPoly[0], rBuffer );
1474         rBuffer.append( " m\n" );
1475         for( sal_uInt16 i = 1; i < nPoints; i++ )
1476         {
1477             if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 )
1478             {
1479                 // bezier
1480                 DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" );
1481                 appendPoint( rPoly[i], rBuffer );
1482                 rBuffer.append( " " );
1483                 appendPoint( rPoly[i+1], rBuffer );
1484                 rBuffer.append( " " );
1485                 appendPoint( rPoly[i+2], rBuffer );
1486                 rBuffer.append( " c" );
1487                 i += 2; // add additionally consumed points
1488             }
1489             else
1490             {
1491                 // line
1492                 appendPoint( rPoly[i], rBuffer );
1493                 rBuffer.append( " l" );
1494             }
1495             if( (rBuffer.getLength() - nBufLen) > 65 )
1496             {
1497                 rBuffer.append( "\n" );
1498                 nBufLen = rBuffer.getLength();
1499             }
1500             else
1501                 rBuffer.append( " " );
1502         }
1503         if( bClose )
1504             rBuffer.append( "h\n" );
1505     }
1506 }
1507 
1508 void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1509 {
1510     basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1511                                             m_pWriter->m_aMapMode,
1512                                             m_pWriter->getReferenceDevice(),
1513                                             rPoly ) );
1514 
1515     if( basegfx::tools::isRectangle( aPoly ) )
1516     {
1517         basegfx::B2DRange aRange( aPoly.getB2DRange() );
1518         basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() );
1519         appendPixelPoint( aBL, rBuffer );
1520         rBuffer.append( ' ' );
1521         appendMappedLength( aRange.getWidth(), rBuffer, false, NULL, nLog10Divisor );
1522         rBuffer.append( ' ' );
1523         appendMappedLength( aRange.getHeight(), rBuffer, true, NULL, nLog10Divisor );
1524         rBuffer.append( " re\n" );
1525         return;
1526     }
1527     sal_uInt32 nPoints = aPoly.count();
1528     if( nPoints > 0 )
1529     {
1530         sal_uInt32 nBufLen = rBuffer.getLength();
1531         basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) );
1532         appendPixelPoint( aLastPoint, rBuffer );
1533         rBuffer.append( " m\n" );
1534         for( sal_uInt32 i = 1; i <= nPoints; i++ )
1535         {
1536             if( i != nPoints || aPoly.isClosed() )
1537             {
1538                 sal_uInt32 nCurPoint  = i % nPoints;
1539                 sal_uInt32 nLastPoint = i-1;
1540                 basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) );
1541                 if( aPoly.isNextControlPointUsed( nLastPoint ) &&
1542                     aPoly.isPrevControlPointUsed( nCurPoint ) )
1543                 {
1544                     appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1545                     rBuffer.append( ' ' );
1546                     appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1547                     rBuffer.append( ' ' );
1548                     appendPixelPoint( aPoint, rBuffer );
1549                     rBuffer.append( " c" );
1550                 }
1551                 else if( aPoly.isNextControlPointUsed( nLastPoint ) )
1552                 {
1553                     appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1554                     rBuffer.append( ' ' );
1555                     appendPixelPoint( aPoint, rBuffer );
1556                     rBuffer.append( " y" );
1557                 }
1558                 else if( aPoly.isPrevControlPointUsed( nCurPoint ) )
1559                 {
1560                     appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1561                     rBuffer.append( ' ' );
1562                     appendPixelPoint( aPoint, rBuffer );
1563                     rBuffer.append( " v" );
1564                 }
1565                 else
1566                 {
1567                     appendPixelPoint( aPoint, rBuffer );
1568                     rBuffer.append( " l" );
1569                 }
1570                 if( (rBuffer.getLength() - nBufLen) > 65 )
1571                 {
1572                     rBuffer.append( "\n" );
1573                     nBufLen = rBuffer.getLength();
1574                 }
1575                 else
1576                     rBuffer.append( " " );
1577             }
1578         }
1579         if( bClose )
1580             rBuffer.append( "h\n" );
1581     }
1582 }
1583 
1584 void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1585 {
1586     sal_uInt16 nPolygons = rPolyPoly.Count();
1587     for( sal_uInt16 n = 0; n < nPolygons; n++ )
1588         appendPolygon( rPolyPoly[n], rBuffer, bClose );
1589 }
1590 
1591 void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1592 {
1593     sal_uInt32 nPolygons = rPolyPoly.count();
1594     for( sal_uInt32 n = 0; n < nPolygons; n++ )
1595         appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer, bClose );
1596 }
1597 
1598 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1599 {
1600     sal_Int32 nValue = nLength;
1601     if ( nLength < 0 )
1602     {
1603         rBuffer.append( '-' );
1604         nValue = -nLength;
1605     }
1606     Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1607                              m_pWriter->m_aMapMode,
1608                              m_pWriter->getReferenceDevice(),
1609                              Size( nValue, nValue ) ) );
1610     nValue = bVertical ? aSize.Height() : aSize.Width();
1611     if( pOutLength )
1612         *pOutLength = ((nLength < 0 ) ? -nValue : nValue);
1613 
1614     appendFixedInt( nValue, rBuffer, 1 );
1615 }
1616 
1617 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength, sal_Int32 nPrecision ) const
1618 {
1619     Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1620                              m_pWriter->m_aMapMode,
1621                              m_pWriter->getReferenceDevice(),
1622                              Size( 1000, 1000 )	) );
1623     if( pOutLength )
1624         *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0);
1625     fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0);
1626     appendDouble( fLength, rBuffer, nPrecision );
1627 }
1628 
1629 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const
1630 {
1631     bool bRet = true;
1632     if( rInfo.GetStyle() == LINE_DASH )
1633     {
1634         rBuffer.append( "[ " );
1635         if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case
1636         {
1637             appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1638             rBuffer.append( ' ' );
1639             appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1640             rBuffer.append( ' ' );
1641         }
1642         else
1643         {
1644             // check for implementation limits of dash array
1645             // in PDF reader apps (e.g. acroread)
1646             if( 2*(rInfo.GetDashCount() + rInfo.GetDotCount()) > 10 )
1647                 bRet = false;
1648             for( int n = 0; n < rInfo.GetDashCount(); n++ )
1649             {
1650                 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1651                 rBuffer.append( ' ' );
1652                 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1653                 rBuffer.append( ' ' );
1654             }
1655             for( int m = 0; m < rInfo.GetDotCount(); m++ )
1656             {
1657                 appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer );
1658                 rBuffer.append( ' ' );
1659                 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1660                 rBuffer.append( ' ' );
1661             }
1662         }
1663         rBuffer.append( "] 0 d\n" );
1664     }
1665     if( rInfo.GetWidth() > 1 )
1666     {
1667         appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer );
1668         rBuffer.append( " w\n" );
1669     }
1670     else if( rInfo.GetWidth() == 0 )
1671     {
1672         // "pixel" line
1673         appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->ImplGetDPIX()), rBuffer );
1674         rBuffer.append( " w\n" );
1675     }
1676     return bRet;
1677 }
1678 
1679 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const
1680 {
1681     if( nWidth <= 0 )
1682         return;
1683     if( nDelta < 1 )
1684         nDelta = 1;
1685 
1686     rBuffer.append( "0 " );
1687     appendMappedLength( nY, rBuffer, true );
1688     rBuffer.append( " m\n" );
1689     for( sal_Int32 n = 0; n < nWidth; )
1690     {
1691         n += nDelta;
1692         appendMappedLength( n, rBuffer, false );
1693         rBuffer.append( ' ' );
1694         appendMappedLength( nDelta+nY, rBuffer, true );
1695         rBuffer.append( ' ' );
1696         n += nDelta;
1697         appendMappedLength( n, rBuffer, false );
1698         rBuffer.append( ' ' );
1699         appendMappedLength( nY, rBuffer, true );
1700         rBuffer.append( " v " );
1701         if( n < nWidth )
1702         {
1703             n += nDelta;
1704             appendMappedLength( n, rBuffer, false );
1705             rBuffer.append( ' ' );
1706             appendMappedLength( nY-nDelta, rBuffer, true );
1707             rBuffer.append( ' ' );
1708             n += nDelta;
1709             appendMappedLength( n, rBuffer, false );
1710             rBuffer.append( ' ' );
1711             appendMappedLength( nY, rBuffer, true );
1712             rBuffer.append( " v\n" );
1713         }
1714     }
1715     rBuffer.append( "S\n" );
1716 }
1717 
1718 /*
1719  *  class PDFWriterImpl
1720  */
1721 
1722  PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext,
1723                                const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc,
1724                                PDFWriter& i_rOuterFace)
1725         :
1726         m_pReferenceDevice( NULL ),
1727         m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ),
1728         m_nCurrentStructElement( 0 ),
1729         m_bEmitStructure( true ),
1730         m_bNewMCID( false ),
1731         m_nCurrentControl( -1 ),
1732         m_bEmbedStandardFonts( false ),
1733         m_nNextFID( 1 ),
1734         m_nInheritedPageWidth( 595 ),  // default A4
1735         m_nInheritedPageHeight( 842 ), // default A4
1736         m_eInheritedOrientation( PDFWriter::Portrait ),
1737         m_nCurrentPage( -1 ),
1738         m_nResourceDict( -1 ),
1739         m_nFontDictObject( -1 ),
1740         m_pCodec( NULL ),
1741         m_aDocDigest( rtl_digest_createMD5() ),
1742 		m_aCipher( (rtlCipher)NULL ),
1743 		m_aDigest( NULL ),
1744 		m_bEncryptThisStream( false ),
1745 		m_pEncryptionBuffer( NULL ),
1746 		m_nEncryptionBufferSize( 0 ),
1747         m_bIsPDF_A1( false ),
1748         m_rOuterFace( i_rOuterFace )
1749 {
1750 #ifdef DO_TEST_PDF
1751     static bool bOnce = true;
1752     if( bOnce )
1753     {
1754         bOnce = false;
1755         doTestCode();
1756     }
1757 #endif
1758     m_aContext = rContext;
1759     m_aStructure.push_back( PDFStructureElement() );
1760     m_aStructure[0].m_nOwnElement		= 0;
1761     m_aStructure[0].m_nParentElement	= 0;
1762 
1763     Font aFont;
1764     aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
1765     aFont.SetSize( Size( 0, 12 ) );
1766 
1767     GraphicsState aState;
1768     aState.m_aMapMode		= m_aMapMode;
1769     aState.m_aFont			= aFont;
1770     m_aGraphicsStack.push_front( aState );
1771 
1772     oslFileError  aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
1773     if( aError != osl_File_E_None )
1774     {
1775         if( aError == osl_File_E_EXIST )
1776         {
1777             aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write );
1778             if( aError == osl_File_E_None )
1779                 aError = osl_setFileSize( m_aFile, 0 );
1780         }
1781     }
1782     if( aError != osl_File_E_None )
1783         return;
1784 
1785     m_bOpen = true;
1786 
1787     // setup DocInfo
1788     setupDocInfo();
1789 
1790     /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1791 	m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1792 	m_aDigest = rtl_digest_createMD5();
1793 
1794 	/* the size of the Codec default maximum */
1795 	checkEncryptionBufferSize( 0x4000 );
1796 
1797 	if( xEnc.is() )
1798 	    prepareEncryption( xEnc );
1799 
1800 	if( m_aContext.Encryption.Encrypt() )
1801 	{
1802 	    // sanity check
1803 	    if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE ||
1804 	        m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE ||
1805 	        m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH
1806 	       )
1807 	    {
1808 	        // the field lengths are invalid ? This was not setup by initEncryption.
1809 	        // do not encrypt after all
1810 	        m_aContext.Encryption.OValue.clear();
1811 	        m_aContext.Encryption.UValue.clear();
1812 	        OSL_ENSURE( 0, "encryption data failed sanity check, encryption disabled" );
1813 	    }
1814 	    else // setup key lengths
1815 	        m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength );
1816 	}
1817 
1818     // write header
1819     OStringBuffer aBuffer( 20 );
1820     aBuffer.append( "%PDF-" );
1821     switch( m_aContext.Version )
1822     {
1823         case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break;
1824         case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break;
1825         case PDFWriter::PDF_A_1:
1826         default:
1827         case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break;
1828         case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break;
1829     }
1830     // append something binary as comment (suggested in PDF Reference)
1831     aBuffer.append( "\n%äüöß\n" );
1832     if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) )
1833     {
1834         osl_closeFile( m_aFile );
1835         m_bOpen = false;
1836         return;
1837     }
1838 
1839     // insert outline root
1840     m_aOutline.push_back( PDFOutlineEntry() );
1841 
1842     m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1);
1843     if( m_bIsPDF_A1 )
1844         m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour
1845 
1846     m_bEmbedStandardFonts = m_aContext.EmbedStandardFonts;
1847 }
1848 
1849 PDFWriterImpl::~PDFWriterImpl()
1850 {
1851     if( m_aDocDigest )
1852         rtl_digest_destroyMD5( m_aDocDigest );
1853     delete static_cast<VirtualDevice*>(m_pReferenceDevice);
1854 
1855 	if( m_aCipher )
1856 		rtl_cipher_destroyARCFOUR( m_aCipher );
1857 	if( m_aDigest )
1858 		rtl_digest_destroyMD5( m_aDigest );
1859 
1860     rtl_freeMemory( m_pEncryptionBuffer );
1861 }
1862 
1863 void PDFWriterImpl::setupDocInfo()
1864 {
1865     std::vector< sal_uInt8 > aId;
1866     computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString );
1867     if( m_aContext.Encryption.DocumentIdentifier.empty() )
1868         m_aContext.Encryption.DocumentIdentifier = aId;
1869 }
1870 
1871 void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier,
1872                                                const vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
1873                                                rtl::OString& o_rCString1,
1874                                                rtl::OString& o_rCString2
1875                                                )
1876 {
1877     o_rIdentifier.clear();
1878 
1879     //build the document id
1880 	rtl::OString aInfoValuesOut;
1881 	OStringBuffer aID( 1024 );
1882 	if( i_rDocInfo.Title.Len() )
1883 		appendUnicodeTextString( i_rDocInfo.Title, aID );
1884 	if( i_rDocInfo.Author.Len() )
1885 		appendUnicodeTextString( i_rDocInfo.Author, aID );
1886 	if( i_rDocInfo.Subject.Len() )
1887 		appendUnicodeTextString( i_rDocInfo.Subject, aID );
1888 	if( i_rDocInfo.Keywords.Len() )
1889 		appendUnicodeTextString( i_rDocInfo.Keywords, aID );
1890 	if( i_rDocInfo.Creator.Len() )
1891 		appendUnicodeTextString( i_rDocInfo.Creator, aID );
1892 	if( i_rDocInfo.Producer.Len() )
1893 		appendUnicodeTextString( i_rDocInfo.Producer, aID );
1894 
1895 	TimeValue aTVal, aGMT;
1896 	oslDateTime aDT;
1897 	osl_getSystemTime( &aGMT );
1898 	osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
1899 	osl_getDateTimeFromTimeValue( &aTVal, &aDT );
1900 	rtl::OStringBuffer aCreationDateString(64), aCreationMetaDateString(64);
1901 	aCreationDateString.append( "D:" );
1902 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1903 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1904 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1905 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1906 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1907 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1908 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1909 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1910 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1911 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1912 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1913 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1914 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1915 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1916 
1917 	//--> i59651, we fill the Metadata date string as well, if PDF/A is requested
1918     // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1919     // local time zone offset UTC only, whereas Acrobat 8 seems
1920     // to use the localtime notation only
1921     // according to a raccomandation in XMP Specification (Jan 2004, page 75)
1922     // the Acrobat way seems the right approach
1923     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1924     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1925     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1926     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1927     aCreationMetaDateString.append( "-" );
1928     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1929     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1930     aCreationMetaDateString.append( "-" );
1931     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1932     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1933     aCreationMetaDateString.append( "T" );
1934     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1935     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1936     aCreationMetaDateString.append( ":" );
1937     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1938     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1939     aCreationMetaDateString.append( ":" );
1940     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1941     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1942 
1943 	sal_uInt32 nDelta = 0;
1944 	if( aGMT.Seconds > aTVal.Seconds )
1945 	{
1946 		aCreationDateString.append( "-" );
1947 		nDelta = aGMT.Seconds-aTVal.Seconds;
1948 		aCreationMetaDateString.append( "-" );
1949 	}
1950 	else if( aGMT.Seconds < aTVal.Seconds )
1951 	{
1952 		aCreationDateString.append( "+" );
1953 		nDelta = aTVal.Seconds-aGMT.Seconds;
1954 		aCreationMetaDateString.append( "+" );
1955 	}
1956 	else
1957     {
1958 		aCreationDateString.append( "Z" );
1959 		aCreationMetaDateString.append( "Z" );
1960 
1961     }
1962 	if( nDelta )
1963 	{
1964 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1965 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1966 		aCreationDateString.append( "'" );
1967 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1968 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1969 
1970 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1971 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1972 		aCreationMetaDateString.append( ":" );
1973 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1974 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1975 	}
1976 	aCreationDateString.append( "'" );
1977 	aID.append( aCreationDateString.getStr(), aCreationDateString.getLength() );
1978 
1979 	aInfoValuesOut = aID.makeStringAndClear();
1980 	o_rCString1 = aCreationDateString.makeStringAndClear();
1981 	o_rCString2 = aCreationMetaDateString.makeStringAndClear();
1982 
1983 	rtlDigest aDigest = rtl_digest_createMD5();
1984 	OSL_ENSURE( aDigest != NULL, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" );
1985 	if( aDigest )
1986 	{
1987 		rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) );
1988 		if( nError == rtl_Digest_E_None )
1989 			nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() );
1990 		if( nError == rtl_Digest_E_None )
1991 		{
1992 		    o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 );
1993 		    //the binary form of the doc id is needed for encryption stuff
1994 			rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 );
1995 		}
1996 	}
1997 }
1998 
1999 /* i12626 methods */
2000 /*
2001 check if the Unicode string must be encrypted or not, perform the requested task,
2002 append the string as unicode hex, encrypted if needed
2003  */
2004 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2005 {
2006 	rOutBuffer.append( "<" );
2007 	if( m_aContext.Encryption.Encrypt() )
2008 	{
2009 		const sal_Unicode* pStr = rInString.getStr();
2010 		sal_Int32 nLen = rInString.getLength();
2011 //prepare a unicode string, encrypt it
2012 		if( checkEncryptionBufferSize( nLen*2 ) )
2013 		{
2014 			enableStringEncryption( nInObjectNumber );
2015 			register sal_uInt8 *pCopy = m_pEncryptionBuffer;
2016 			sal_Int32 nChars = 2;
2017 			*pCopy++ = 0xFE;
2018 			*pCopy++ = 0xFF;
2019 // we need to prepare a byte stream from the unicode string buffer
2020 			for( register int i = 0; i < nLen; i++ )
2021 			{
2022 				register sal_Unicode aUnChar = pStr[i];
2023 				*pCopy++ = (sal_uInt8)( aUnChar >> 8 );
2024 				*pCopy++ = (sal_uInt8)( aUnChar & 255 );
2025 				nChars += 2;
2026 			}
2027 //encrypt in place
2028 			rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars );
2029 //now append, hexadecimal (appendHex), the encrypted result
2030 			for(register int i = 0; i < nChars; i++)
2031 				appendHex( m_pEncryptionBuffer[i], rOutBuffer );
2032 		}
2033 	}
2034 	else
2035 		appendUnicodeTextString( rInString, rOutBuffer );
2036 	rOutBuffer.append( ">" );
2037 }
2038 
2039 inline void PDFWriterImpl::appendLiteralStringEncrypt( rtl::OStringBuffer& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
2040 {
2041 	rOutBuffer.append( "(" );
2042 	sal_Int32 nChars = rInString.getLength();
2043 //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
2044 	if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) )
2045 	{
2046 //encrypt the string in a buffer, then append it
2047 		enableStringEncryption( nInObjectNumber );
2048 		rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars );
2049 		appendLiteralString( (const sal_Char*)m_pEncryptionBuffer, nChars, rOutBuffer );
2050 	}
2051 	else
2052         appendLiteralString( rInString.getStr(), nChars , rOutBuffer );
2053 	rOutBuffer.append( ")" );
2054 }
2055 
2056 inline void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
2057 {
2058 	rtl::OStringBuffer aBufferString( rInString );
2059 	appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2060 }
2061 
2062 void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc )
2063 {
2064 	rtl::OString aBufferString( rtl::OUStringToOString( rInString, nEnc ) );
2065 	sal_Int32 nLen = aBufferString.getLength();
2066 	rtl::OStringBuffer aBuf( nLen );
2067 	const sal_Char* pT = aBufferString.getStr();
2068 
2069 	for( sal_Int32 i = 0; i < nLen; i++, pT++ )
2070 	{
2071 	    if( (*pT & 0x80) == 0 )
2072 	        aBuf.append( *pT );
2073 	    else
2074 	    {
2075 	        aBuf.append( '<' );
2076 	        appendHex( *pT, aBuf );
2077 	        aBuf.append( '>' );
2078 	    }
2079 	}
2080 	aBufferString = aBuf.makeStringAndClear();
2081 	appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2082 }
2083 
2084 /* end i12626 methods */
2085 
2086 void PDFWriterImpl::emitComment( const char* pComment )
2087 {
2088     OStringBuffer aLine( 64 );
2089     aLine.append( "% " );
2090     aLine.append( (const sal_Char*)pComment );
2091     aLine.append( "\n" );
2092     writeBuffer( aLine.getStr(), aLine.getLength() );
2093 }
2094 
2095 bool PDFWriterImpl::compressStream( SvMemoryStream* pStream )
2096 {
2097 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2098     pStream->Seek( STREAM_SEEK_TO_END );
2099     sal_uLong nEndPos = pStream->Tell();
2100     pStream->Seek( STREAM_SEEK_TO_BEGIN );
2101     ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
2102     SvMemoryStream aStream;
2103     pCodec->BeginCompression();
2104     pCodec->Write( aStream, (const sal_uInt8*)pStream->GetData(), nEndPos );
2105     pCodec->EndCompression();
2106     delete pCodec;
2107     nEndPos = aStream.Tell();
2108     pStream->Seek( STREAM_SEEK_TO_BEGIN );
2109     aStream.Seek( STREAM_SEEK_TO_BEGIN );
2110     pStream->SetStreamSize( nEndPos );
2111     pStream->Write( aStream.GetData(), nEndPos );
2112     return true;
2113 #else
2114     (void)pStream;
2115     return false;
2116 #endif
2117 }
2118 
2119 void PDFWriterImpl::beginCompression()
2120 {
2121 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2122     m_pCodec = new ZCodec( 0x4000, 0x4000 );
2123     m_pMemStream = new SvMemoryStream();
2124     m_pCodec->BeginCompression();
2125 #endif
2126 }
2127 
2128 void PDFWriterImpl::endCompression()
2129 {
2130 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2131     if( m_pCodec )
2132     {
2133         m_pCodec->EndCompression();
2134         delete m_pCodec;
2135         m_pCodec = NULL;
2136         sal_uInt64 nLen = m_pMemStream->Tell();
2137         m_pMemStream->Seek( 0 );
2138         writeBuffer( m_pMemStream->GetData(), nLen );
2139         delete m_pMemStream;
2140         m_pMemStream = NULL;
2141     }
2142 #endif
2143 }
2144 
2145 bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes )
2146 {
2147     if( ! m_bOpen ) // we are already down the drain
2148         return false;
2149 
2150     if( ! nBytes ) // huh ?
2151         return true;
2152 
2153     if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2154     {
2155         m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END );
2156         m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) );
2157         return true;
2158     }
2159 
2160     sal_uInt64 nWritten;
2161     if( m_pCodec )
2162     {
2163         m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes );
2164         nWritten = nBytes;
2165     }
2166     else
2167     {
2168 		sal_Bool  buffOK = sal_True;
2169 		if( m_bEncryptThisStream )
2170 		{
2171 /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
2172 			if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) != sal_False )
2173 				rtl_cipher_encodeARCFOUR( m_aCipher,
2174                                           (sal_uInt8*)pBuffer, static_cast<sal_Size>(nBytes),
2175                                           m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) );
2176 		}
2177 
2178         const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer  : pBuffer;
2179         if( m_aDocDigest )
2180             rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) );
2181 
2182         if( osl_writeFile( m_aFile,
2183                            pWriteBuffer,
2184                            nBytes, &nWritten ) != osl_File_E_None )
2185             nWritten = 0;
2186 
2187         if( nWritten != nBytes )
2188         {
2189             osl_closeFile( m_aFile );
2190             m_bOpen = false;
2191         }
2192     }
2193 
2194     return nWritten == nBytes;
2195 }
2196 
2197 OutputDevice* PDFWriterImpl::getReferenceDevice()
2198 {
2199     if( ! m_pReferenceDevice )
2200     {
2201         VirtualDevice*  pVDev = new VirtualDevice( 0 );
2202 
2203         m_pReferenceDevice = pVDev;
2204 
2205         if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 )
2206             pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 );
2207         else
2208             pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy );
2209 
2210         pVDev->SetOutputSizePixel( Size( 640, 480 ) );
2211         pVDev->SetMapMode( MAP_MM );
2212 
2213         m_pReferenceDevice->mpPDFWriter = this;
2214         m_pReferenceDevice->ImplUpdateFontData( sal_True );
2215     }
2216     return m_pReferenceDevice;
2217 }
2218 
2219 class ImplPdfBuiltinFontData : public ImplFontData
2220 {
2221 private:
2222     const PDFWriterImpl::BuiltinFont& mrBuiltin;
2223 
2224 public:
2225     enum {PDF_FONT_MAGIC = 0xBDFF0A1C };
2226                                         ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& );
2227     const PDFWriterImpl::BuiltinFont*   GetBuiltinFont() const  { return &mrBuiltin; }
2228 
2229     virtual ImplFontData*               Clone() const { return new ImplPdfBuiltinFontData(*this); }
2230     virtual ImplFontEntry*              CreateFontInstance( ImplFontSelectData& ) const;
2231     virtual sal_IntPtr                  GetFontId() const { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); }
2232 };
2233 
2234 inline const ImplPdfBuiltinFontData* GetPdfFontData( const ImplFontData* pFontData )
2235 {
2236     const ImplPdfBuiltinFontData* pFD = NULL;
2237     if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) )
2238         pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData );
2239     return pFD;
2240 }
2241 
2242 static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin )
2243 {
2244     ImplDevFontAttributes aDFA;
2245     aDFA.maName         = String::CreateFromAscii( rBuiltin.m_pName );
2246     aDFA.maStyleName    = String::CreateFromAscii( rBuiltin.m_pStyleName );
2247     aDFA.meFamily       = rBuiltin.m_eFamily;
2248     aDFA.mbSymbolFlag   = (rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 );
2249     aDFA.mePitch        = rBuiltin.m_ePitch;
2250     aDFA.meWeight       = rBuiltin.m_eWeight;
2251     aDFA.meItalic       = rBuiltin.m_eItalic;
2252     aDFA.meWidthType    = rBuiltin.m_eWidthType;
2253 
2254     aDFA.mbOrientation  = true;
2255     aDFA.mbDevice       = true;
2256     aDFA.mnQuality      = 50000;
2257     aDFA.mbSubsettable  = false;
2258     aDFA.mbEmbeddable   = false;
2259     return aDFA;
2260 }
2261 
2262 ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin )
2263 :   ImplFontData( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ),
2264     mrBuiltin( rBuiltin )
2265 {}
2266 
2267 ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
2268 {
2269     ImplFontEntry* pEntry = new ImplFontEntry( rFSD );
2270     return pEntry;
2271 }
2272 
2273 ImplDevFontList* PDFWriterImpl::filterDevFontList( ImplDevFontList* pFontList )
2274 {
2275     DBG_ASSERT( m_aSubsets.size() == 0, "Fonts changing during PDF generation, document will be invalid" );
2276     ImplDevFontList* pFiltered = pFontList->Clone( true, true );
2277 
2278     // append the PDF builtin fonts
2279     if( !m_bIsPDF_A1 && !m_bEmbedStandardFonts)
2280         for( unsigned int i = 0; i < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); i++ )
2281         {
2282             ImplFontData* pNewData = new ImplPdfBuiltinFontData( m_aBuiltinFonts[i] );
2283             pFiltered->Add( pNewData );
2284         }
2285     return pFiltered;
2286 }
2287 
2288 bool PDFWriterImpl::isBuiltinFont( const ImplFontData* pFont ) const
2289 {
2290     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
2291     return (pFD != NULL);
2292 }
2293 
2294 void PDFWriterImpl::getFontMetric( ImplFontSelectData* pSelect, ImplFontMetricData* pMetric ) const
2295 {
2296     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2297     if( !pFD )
2298         return;
2299     const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2300 
2301     pMetric->mnOrientation	= sal::static_int_cast<short>(pSelect->mnOrientation);
2302     pMetric->meFamily		= pBuiltinFont->m_eFamily;
2303     pMetric->mePitch		= pBuiltinFont->m_ePitch;
2304     pMetric->meWeight		= pBuiltinFont->m_eWeight;
2305     pMetric->meItalic		= pBuiltinFont->m_eItalic;
2306     pMetric->mbSymbolFlag	= pFD->IsSymbolFont();
2307     pMetric->mnWidth		= pSelect->mnHeight;
2308     pMetric->mnAscent		= ( pSelect->mnHeight * +pBuiltinFont->m_nAscent + 500 ) / 1000;
2309     pMetric->mnDescent		= ( pSelect->mnHeight * -pBuiltinFont->m_nDescent + 500 ) / 1000;
2310     pMetric->mnIntLeading	= 0;
2311     pMetric->mnExtLeading	= 0;
2312     pMetric->mnSlant		= 0;
2313     pMetric->mbScalableFont	= true;
2314     pMetric->mbDevice		= true;
2315 }
2316 
2317 // -----------------------------------------------------------------------
2318 
2319 namespace vcl {
2320 
2321 class PDFSalLayout : public GenericSalLayout
2322 {
2323     PDFWriterImpl&  mrPDFWriterImpl;
2324     const PDFWriterImpl::BuiltinFont& mrBuiltinFont;
2325     bool            mbIsSymbolFont;
2326     long            mnPixelPerEM;
2327     String          maOrigText;
2328 
2329 public:
2330                     PDFSalLayout( PDFWriterImpl&,
2331                                   const PDFWriterImpl::BuiltinFont&,
2332                                   long nPixelPerEM, int nOrientation );
2333 
2334     void            SetText( const String& rText )  { maOrigText = rText; }
2335     virtual bool    LayoutText( ImplLayoutArgs& );
2336     virtual void    InitFont() const;
2337     virtual void    DrawText( SalGraphics& ) const;
2338 };
2339 
2340 }
2341 
2342 // -----------------------------------------------------------------------
2343 
2344 PDFSalLayout::PDFSalLayout( PDFWriterImpl& rPDFWriterImpl,
2345     const PDFWriterImpl::BuiltinFont& rBuiltinFont,
2346     long nPixelPerEM, int nOrientation )
2347 :   mrPDFWriterImpl( rPDFWriterImpl ),
2348     mrBuiltinFont( rBuiltinFont ),
2349     mnPixelPerEM( nPixelPerEM )
2350 {
2351     mbIsSymbolFont = (rBuiltinFont.m_eCharSet != RTL_TEXTENCODING_MS_1252);
2352     SetOrientation( nOrientation );
2353 }
2354 
2355 // -----------------------------------------------------------------------
2356 
2357 bool PDFSalLayout::LayoutText( ImplLayoutArgs& rArgs )
2358 {
2359     const String aText( rArgs.mpStr+rArgs.mnMinCharPos, sal::static_int_cast<xub_StrLen>(rArgs.mnEndCharPos-rArgs.mnMinCharPos) );
2360     SetText( aText );
2361     SetUnitsPerPixel( 1000 );
2362 
2363     rtl_UnicodeToTextConverter aConv = rtl_createTextToUnicodeConverter( mrBuiltinFont.m_eCharSet );
2364 
2365     Point aNewPos( 0, 0 );
2366     bool bRightToLeft;
2367     for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); )
2368     {
2369         // TODO: handle unicode surrogates
2370         // on the other hand the PDF builtin fonts don't support them anyway
2371         sal_Unicode cChar = rArgs.mpStr[ nCharPos ];
2372         if( bRightToLeft )
2373             cChar = static_cast<sal_Unicode>(GetMirroredChar( cChar ));
2374 
2375         if( 1 ) // TODO: shortcut for ASCII?
2376         {
2377             sal_Char aBuf[4];
2378             sal_uInt32 nInfo;
2379             sal_Size nSrcCvtChars;
2380 
2381             sal_Size nConv = rtl_convertUnicodeToText( aConv,
2382                                                        NULL,
2383                                                        &cChar, 1,
2384                                                        aBuf, sizeof(aBuf)/sizeof(*aBuf),
2385                                                        RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR,
2386                                                        &nInfo, &nSrcCvtChars );
2387             // check whether conversion was possible
2388             // else fallback font is needed as the standard fonts
2389             // are handled via WinAnsi encoding
2390             if( nConv > 0 )
2391                 cChar = ((sal_Unicode)aBuf[0]) & 0x00ff;
2392         }
2393         if( cChar & 0xff00 )
2394         {
2395             cChar = 0;   // NotDef glyph
2396             rArgs.NeedFallback( nCharPos, bRightToLeft );
2397         }
2398 
2399         long nGlyphWidth = (long)mrBuiltinFont.m_aWidths[cChar] * mnPixelPerEM;
2400         long nGlyphFlags = 0; // builtin fonts don't have diacritic glyphs
2401         if( bRightToLeft )
2402             nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
2403         // TODO: get kerning from builtin fonts
2404         GlyphItem aGI( nCharPos, cChar, aNewPos, nGlyphFlags, nGlyphWidth );
2405         AppendGlyph( aGI );
2406 
2407         aNewPos.X() += nGlyphWidth;
2408     }
2409 
2410     rtl_destroyUnicodeToTextConverter( aConv );
2411 
2412     return true;
2413 }
2414 
2415 // -----------------------------------------------------------------------
2416 
2417 void PDFSalLayout::InitFont() const
2418 {
2419     // TODO: recreate font with all its attributes
2420 }
2421 
2422 // -----------------------------------------------------------------------
2423 
2424 void PDFSalLayout::DrawText( SalGraphics& ) const
2425 {
2426     mrPDFWriterImpl.drawLayout( *const_cast<PDFSalLayout*>(this), maOrigText, true );
2427 }
2428 
2429 // -----------------------------------------------------------------------
2430 
2431 SalLayout* PDFWriterImpl::GetTextLayout( ImplLayoutArgs& rArgs, ImplFontSelectData* pSelect )
2432 {
2433     DBG_ASSERT( (pSelect->mpFontData != NULL),
2434         "PDFWriterImpl::GetTextLayout mpFontData is NULL" );
2435 
2436     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2437     if( !pFD )
2438         return NULL;
2439     const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2440 
2441     long nPixelPerEM = pSelect->mnWidth ? pSelect->mnWidth : pSelect->mnHeight;
2442     int nOrientation = pSelect->mnOrientation;
2443     PDFSalLayout* pLayout = new PDFSalLayout( *this, *pBuiltinFont, nPixelPerEM, nOrientation );
2444     pLayout->SetText( rArgs.mpStr );
2445     return pLayout;
2446 }
2447 
2448 sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
2449 {
2450     endPage();
2451     m_nCurrentPage = m_aPages.size();
2452     m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) );
2453     m_aPages.back().m_nPageIndex = m_nCurrentPage;
2454     m_aPages.back().beginStream();
2455 
2456     // setup global graphics state
2457     // linewidth is "1 pixel" by default
2458     OStringBuffer aBuf( 16 );
2459     appendDouble( 72.0/double(getReferenceDevice()->ImplGetDPIX()), aBuf );
2460     aBuf.append( " w\n" );
2461     writeBuffer( aBuf.getStr(), aBuf.getLength() );
2462 
2463     return m_nCurrentPage;
2464 }
2465 
2466 void PDFWriterImpl::endPage()
2467 {
2468     if( m_aPages.begin() != m_aPages.end() )
2469     {
2470         // close eventual MC sequence
2471         endStructureElementMCSeq();
2472 
2473         // sanity check
2474         if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2475         {
2476             DBG_ERROR( "redirection across pages !!!" );
2477             m_aOutputStreams.clear(); // leak !
2478             m_aMapMode.SetOrigin( Point() );
2479         }
2480 
2481         m_aGraphicsStack.clear();
2482         m_aGraphicsStack.push_back( GraphicsState() );
2483 
2484         // this should pop the PDF graphics stack if necessary
2485         updateGraphicsState();
2486 
2487         m_aPages.back().endStream();
2488 
2489         // reset the default font
2490         Font aFont;
2491         aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
2492         aFont.SetSize( Size( 0, 12 ) );
2493 
2494         m_aCurrentPDFState = m_aGraphicsStack.front();
2495         m_aGraphicsStack.front().m_aFont =  aFont;
2496 
2497         for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin();
2498              it != m_aBitmaps.end(); ++it )
2499         {
2500             if( ! it->m_aBitmap.IsEmpty() )
2501             {
2502                 writeBitmapObject( *it );
2503                 it->m_aBitmap = BitmapEx();
2504             }
2505         }
2506         for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg )
2507         {
2508             if( jpeg->m_pStream )
2509             {
2510                 writeJPG( *jpeg );
2511                 delete jpeg->m_pStream;
2512                 jpeg->m_pStream = NULL;
2513                 jpeg->m_aMask = Bitmap();
2514             }
2515         }
2516         for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin();
2517              t != m_aTransparentObjects.end(); ++t )
2518         {
2519             if( t->m_pContentStream )
2520             {
2521                 writeTransparentObject( *t );
2522                 delete t->m_pContentStream;
2523                 t->m_pContentStream = NULL;
2524             }
2525         }
2526     }
2527 }
2528 
2529 sal_Int32 PDFWriterImpl::createObject()
2530 {
2531     m_aObjects.push_back( ~0U );
2532     return m_aObjects.size();
2533 }
2534 
2535 bool PDFWriterImpl::updateObject( sal_Int32 n )
2536 {
2537     if( ! m_bOpen )
2538         return false;
2539 
2540     sal_uInt64 nOffset = ~0U;
2541     oslFileError aError = osl_getFilePos( m_aFile, &nOffset );
2542     DBG_ASSERT( aError == osl_File_E_None, "could not register object" );
2543     if( aError != osl_File_E_None )
2544     {
2545         osl_closeFile( m_aFile );
2546         m_bOpen = false;
2547     }
2548     m_aObjects[ n-1 ] = nOffset;
2549     return aError == osl_File_E_None;
2550 }
2551 
2552 #define CHECK_RETURN( x ) if( !(x) ) return 0
2553 
2554 sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject )
2555 {
2556     if( nObject > 0 )
2557     {
2558         OStringBuffer aLine( 1024 );
2559 
2560         aLine.append( nObject );
2561         aLine.append( " 0 obj\n"
2562                       "<</Nums[\n" );
2563         sal_Int32 nTreeItems = m_aStructParentTree.size();
2564         for( sal_Int32 n = 0; n < nTreeItems; n++ )
2565         {
2566             aLine.append( n );
2567             aLine.append( ' ' );
2568             aLine.append( m_aStructParentTree[n] );
2569             aLine.append( "\n" );
2570         }
2571         aLine.append( "]>>\nendobj\n\n" );
2572         CHECK_RETURN( updateObject( nObject ) );
2573         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2574     }
2575     return nObject;
2576 }
2577 
2578 const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr )
2579 {
2580     static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings;
2581     // fill maps once
2582     if( aAttributeStrings.empty() )
2583     {
2584         aAttributeStrings[ PDFWriter::Placement ]			= "Placement";
2585         aAttributeStrings[ PDFWriter::WritingMode ]			= "WritingMode";
2586         aAttributeStrings[ PDFWriter::SpaceBefore ]			= "SpaceBefore";
2587         aAttributeStrings[ PDFWriter::SpaceAfter ]			= "SpaceAfter";
2588         aAttributeStrings[ PDFWriter::StartIndent ]			= "StartIndent";
2589         aAttributeStrings[ PDFWriter::EndIndent ]			= "EndIndent";
2590         aAttributeStrings[ PDFWriter::TextIndent ]			= "TextIndent";
2591         aAttributeStrings[ PDFWriter::TextAlign ]			= "TextAlign";
2592         aAttributeStrings[ PDFWriter::Width ]				= "Width";
2593         aAttributeStrings[ PDFWriter::Height ]				= "Height";
2594         aAttributeStrings[ PDFWriter::BlockAlign ]			= "BlockAlign";
2595         aAttributeStrings[ PDFWriter::InlineAlign ]			= "InlineAlign";
2596         aAttributeStrings[ PDFWriter::LineHeight ]			= "LineHeight";
2597         aAttributeStrings[ PDFWriter::BaselineShift ]		= "BaselineShift";
2598         aAttributeStrings[ PDFWriter::TextDecorationType ]	= "TextDecorationType";
2599         aAttributeStrings[ PDFWriter::ListNumbering ]		= "ListNumbering";
2600         aAttributeStrings[ PDFWriter::RowSpan ]				= "RowSpan";
2601         aAttributeStrings[ PDFWriter::ColSpan ]				= "ColSpan";
2602         aAttributeStrings[ PDFWriter::LinkAnnotation ]      = "LinkAnnotation";
2603     }
2604 
2605     std::map< PDFWriter::StructAttribute, const char* >::const_iterator it =
2606         aAttributeStrings.find( eAttr );
2607 
2608 #if OSL_DEBUG_LEVEL > 1
2609     if( it == aAttributeStrings.end() )
2610         fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr );
2611 #endif
2612 
2613     return it != aAttributeStrings.end() ? it->second : "";
2614 }
2615 
2616 const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal )
2617 {
2618     static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings;
2619 
2620     if( aValueStrings.empty() )
2621     {
2622         aValueStrings[ PDFWriter::NONE ]					= "None";
2623         aValueStrings[ PDFWriter::Block ]					= "Block";
2624         aValueStrings[ PDFWriter::Inline ]					= "Inline";
2625         aValueStrings[ PDFWriter::Before ]					= "Before";
2626         aValueStrings[ PDFWriter::After ]					= "After";
2627         aValueStrings[ PDFWriter::Start ]					= "Start";
2628         aValueStrings[ PDFWriter::End ]						= "End";
2629         aValueStrings[ PDFWriter::LrTb ]					= "LrTb";
2630         aValueStrings[ PDFWriter::RlTb ]					= "RlTb";
2631         aValueStrings[ PDFWriter::TbRl ]					= "TbRl";
2632         aValueStrings[ PDFWriter::Center ]					= "Center";
2633         aValueStrings[ PDFWriter::Justify ]					= "Justify";
2634         aValueStrings[ PDFWriter::Auto ]					= "Auto";
2635         aValueStrings[ PDFWriter::Middle ]					= "Middle";
2636         aValueStrings[ PDFWriter::Normal ]					= "Normal";
2637         aValueStrings[ PDFWriter::Underline ]				= "Underline";
2638 		aValueStrings[ PDFWriter::Overline ]				= "Overline";
2639         aValueStrings[ PDFWriter::LineThrough ]				= "LineThrough";
2640         aValueStrings[ PDFWriter::Disc ]					= "Disc";
2641         aValueStrings[ PDFWriter::Circle ]					= "Circle";
2642         aValueStrings[ PDFWriter::Square ]					= "Square";
2643         aValueStrings[ PDFWriter::Decimal ]					= "Decimal";
2644         aValueStrings[ PDFWriter::UpperRoman ]				= "UpperRoman";
2645         aValueStrings[ PDFWriter::LowerRoman ]				= "LowerRoman";
2646         aValueStrings[ PDFWriter::UpperAlpha ]				= "UpperAlpha";
2647         aValueStrings[ PDFWriter::LowerAlpha ]				= "LowerAlpha";
2648     }
2649 
2650     std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it =
2651         aValueStrings.find( eVal );
2652 
2653 #if OSL_DEBUG_LEVEL > 1
2654     if( it == aValueStrings.end() )
2655         fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal );
2656 #endif
2657 
2658     return it != aValueStrings.end() ? it->second : "";
2659 }
2660 
2661 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt )
2662 {
2663     o_rLine.append( "/" );
2664     o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) );
2665 
2666     if( i_rVal.eValue != PDFWriter::Invalid )
2667     {
2668         o_rLine.append( "/" );
2669         o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) );
2670     }
2671     else
2672     {
2673         // numerical value
2674         o_rLine.append( " " );
2675         if( i_bIsFixedInt )
2676             appendFixedInt( i_rVal.nValue, o_rLine );
2677         else
2678             o_rLine.append( i_rVal.nValue );
2679     }
2680     o_rLine.append( "\n" );
2681 }
2682 
2683 OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
2684 {
2685     // create layout, list and table attribute sets
2686     OStringBuffer aLayout(256), aList(64), aTable(64);
2687     for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin();
2688          it != i_rEle.m_aAttributes.end(); ++it )
2689     {
2690         if( it->first == PDFWriter::ListNumbering )
2691             appendStructureAttributeLine( it->first, it->second, aList, true );
2692         else if( it->first == PDFWriter::RowSpan ||
2693                  it->first == PDFWriter::ColSpan )
2694             appendStructureAttributeLine( it->first, it->second, aTable, false );
2695         else if( it->first == PDFWriter::LinkAnnotation )
2696         {
2697             sal_Int32 nLink = it->second.nValue;
2698             std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
2699                 m_aLinkPropertyMap.find( nLink );
2700             if( link_it != m_aLinkPropertyMap.end() )
2701                 nLink = link_it->second;
2702             if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() )
2703             {
2704                 // update struct parent of link
2705                 OStringBuffer aStructParentEntry( 32 );
2706                 aStructParentEntry.append( i_rEle.m_nObject );
2707                 aStructParentEntry.append( " 0 R" );
2708                 m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() );
2709                 m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1;
2710 
2711                 sal_Int32 nRefObject = createObject();
2712                 OStringBuffer aRef( 256 );
2713                 aRef.append( nRefObject );
2714                 aRef.append( " 0 obj\n"
2715                              "<</Type/OBJR/Obj " );
2716                 aRef.append( m_aLinks[ nLink ].m_nObject );
2717                 aRef.append( " 0 R>>\n"
2718                              "endobj\n\n"
2719                              );
2720                 updateObject( nRefObject );
2721                 writeBuffer( aRef.getStr(), aRef.getLength() );
2722 
2723                 i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) );
2724             }
2725             else
2726             {
2727                 DBG_ERROR( "unresolved link id for Link structure" );
2728 #if OSL_DEBUG_LEVEL > 1
2729                 fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink );
2730                 {
2731                     OStringBuffer aLine( "unresolved link id " );
2732                     aLine.append( nLink );
2733                     aLine.append( " for Link structure" );
2734                     emitComment( aLine.getStr() );
2735                 }
2736 #endif
2737             }
2738         }
2739         else
2740             appendStructureAttributeLine( it->first, it->second, aLayout, true );
2741     }
2742     if( ! i_rEle.m_aBBox.IsEmpty() )
2743     {
2744         aLayout.append( "/BBox[" );
2745         appendFixedInt( i_rEle.m_aBBox.Left(), aLayout );
2746         aLayout.append( " " );
2747         appendFixedInt( i_rEle.m_aBBox.Top(), aLayout );
2748         aLayout.append( " " );
2749         appendFixedInt( i_rEle.m_aBBox.Right(), aLayout );
2750         aLayout.append( " " );
2751         appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout );
2752         aLayout.append( "]\n" );
2753     }
2754 
2755     std::vector< sal_Int32 > aAttribObjects;
2756     if( aLayout.getLength() )
2757     {
2758         aAttribObjects.push_back( createObject() );
2759         updateObject( aAttribObjects.back() );
2760         OStringBuffer aObj( 64 );
2761         aObj.append( aAttribObjects.back() );
2762         aObj.append( " 0 obj\n"
2763                      "<</O/Layout\n" );
2764         aLayout.append( ">>\nendobj\n\n" );
2765         writeBuffer( aObj.getStr(), aObj.getLength() );
2766         writeBuffer( aLayout.getStr(), aLayout.getLength() );
2767     }
2768     if( aList.getLength() )
2769     {
2770         aAttribObjects.push_back( createObject() );
2771         updateObject( aAttribObjects.back() );
2772         OStringBuffer aObj( 64 );
2773         aObj.append( aAttribObjects.back() );
2774         aObj.append( " 0 obj\n"
2775                      "<</O/List\n" );
2776         aList.append( ">>\nendobj\n\n" );
2777         writeBuffer( aObj.getStr(), aObj.getLength() );
2778         writeBuffer( aList.getStr(), aList.getLength() );
2779     }
2780     if( aTable.getLength() )
2781     {
2782         aAttribObjects.push_back( createObject() );
2783         updateObject( aAttribObjects.back() );
2784         OStringBuffer aObj( 64 );
2785         aObj.append( aAttribObjects.back() );
2786         aObj.append( " 0 obj\n"
2787                      "<</O/Table\n" );
2788         aTable.append( ">>\nendobj\n\n" );
2789         writeBuffer( aObj.getStr(), aObj.getLength() );
2790         writeBuffer( aTable.getStr(), aTable.getLength() );
2791     }
2792 
2793     OStringBuffer aRet( 64 );
2794     if( aAttribObjects.size() > 1 )
2795         aRet.append( " [" );
2796     for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin();
2797          at_it != aAttribObjects.end(); ++at_it )
2798     {
2799         aRet.append( " " );
2800         aRet.append( *at_it );
2801         aRet.append( " 0 R" );
2802     }
2803     if( aAttribObjects.size() > 1 )
2804         aRet.append( " ]" );
2805     return aRet.makeStringAndClear();
2806 }
2807 
2808 sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
2809 {
2810     if(
2811        // do not emit NonStruct and its children
2812        rEle.m_eType == PDFWriter::NonStructElement &&
2813        rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root
2814        )
2815         return 0;
2816 
2817     for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
2818     {
2819         if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
2820         {
2821             PDFStructureElement& rChild = m_aStructure[ *it ];
2822             if( rChild.m_eType != PDFWriter::NonStructElement )
2823             {
2824                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
2825                     emitStructure( rChild );
2826                 else
2827                 {
2828                     DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure element" );
2829 #if OSL_DEBUG_LEVEL > 1
2830                     fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
2831 #endif
2832                 }
2833             }
2834         }
2835         else
2836         {
2837             DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
2838 #if OSL_DEBUG_LEVEL > 1
2839             fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
2840 #endif
2841         }
2842     }
2843 
2844     OStringBuffer aLine( 512 );
2845     aLine.append( rEle.m_nObject );
2846     aLine.append( " 0 obj\n"
2847                   "<</Type" );
2848     sal_Int32 nParentTree = -1;
2849     if( rEle.m_nOwnElement == rEle.m_nParentElement )
2850     {
2851         nParentTree = createObject();
2852         CHECK_RETURN( nParentTree );
2853         aLine.append( "/StructTreeRoot\n" );
2854         aLine.append( "/ParentTree " );
2855         aLine.append( nParentTree );
2856         aLine.append( " 0 R\n" );
2857         if( ! m_aRoleMap.empty() )
2858         {
2859             aLine.append( "/RoleMap<<" );
2860             for( std::hash_map<OString,OString,OStringHash>::const_iterator
2861                  it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it )
2862             {
2863                 aLine.append( '/' );
2864                 aLine.append(it->first);
2865                 aLine.append( '/' );
2866                 aLine.append( it->second );
2867                 aLine.append( '\n' );
2868             }
2869             aLine.append( ">>\n" );
2870         }
2871     }
2872     else
2873     {
2874         aLine.append( "/StructElem\n"
2875                       "/S/" );
2876         if( rEle.m_aAlias.getLength() > 0 )
2877             aLine.append( rEle.m_aAlias );
2878         else
2879             aLine.append( getStructureTag( rEle.m_eType ) );
2880         aLine.append( "\n"
2881                       "/P " );
2882         aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject );
2883         aLine.append( " 0 R\n"
2884                       "/Pg " );
2885         aLine.append( rEle.m_nFirstPageObject );
2886         aLine.append( " 0 R\n" );
2887         if( rEle.m_aActualText.getLength() )
2888         {
2889             aLine.append( "/ActualText" );
2890             appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine );
2891             aLine.append( "\n" );
2892         }
2893         if( rEle.m_aAltText.getLength() )
2894         {
2895             aLine.append( "/Alt" );
2896             appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine );
2897             aLine.append( "\n" );
2898         }
2899     }
2900     if( ! rEle.m_aBBox.IsEmpty() || rEle.m_aAttributes.size() )
2901     {
2902         OString aAttribs =  emitStructureAttributes( rEle );
2903         if( aAttribs.getLength() )
2904         {
2905             aLine.append( "/A" );
2906             aLine.append( aAttribs );
2907             aLine.append( "\n" );
2908         }
2909     }
2910     if( rEle.m_aLocale.Language.getLength() > 0 )
2911     {
2912         OUStringBuffer aLocBuf( 16 );
2913         aLocBuf.append( rEle.m_aLocale.Language.toAsciiLowerCase() );
2914         if( rEle.m_aLocale.Country.getLength() > 0 )
2915         {
2916             aLocBuf.append( sal_Unicode('-') );
2917             aLocBuf.append( rEle.m_aLocale.Country );
2918         }
2919         aLine.append( "/Lang" );
2920         appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine );
2921         aLine.append( "\n" );
2922     }
2923     if( ! rEle.m_aKids.empty() )
2924     {
2925         unsigned int i = 0;
2926         aLine.append( "/K[" );
2927         for( std::list< PDFStructureElementKid >::const_iterator it =
2928                  rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ )
2929         {
2930             if( it->nMCID == -1 )
2931             {
2932                 aLine.append( it->nObject );
2933                 aLine.append( " 0 R" );
2934                 aLine.append( ( (i & 15) == 15 ) ? "\n" : " " );
2935             }
2936             else
2937             {
2938                 if( it->nObject == rEle.m_nFirstPageObject )
2939                 {
2940                     aLine.append( it->nMCID );
2941                     aLine.append( " " );
2942                 }
2943                 else
2944                 {
2945                     aLine.append( "<</Type/MCR/Pg " );
2946                     aLine.append( it->nObject );
2947                     aLine.append( " 0 R /MCID " );
2948                     aLine.append( it->nMCID );
2949                     aLine.append( ">>\n" );
2950                 }
2951             }
2952         }
2953         aLine.append( "]\n" );
2954     }
2955     aLine.append( ">>\nendobj\n\n" );
2956 
2957     CHECK_RETURN( updateObject( rEle.m_nObject ) );
2958     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2959 
2960     CHECK_RETURN( emitStructParentTree( nParentTree ) );
2961 
2962     return rEle.m_nObject;
2963 }
2964 
2965 bool PDFWriterImpl::emitGradients()
2966 {
2967     for( std::list<GradientEmit>::iterator it = m_aGradients.begin();
2968          it != m_aGradients.end(); ++it )
2969     {
2970         CHECK_RETURN( writeGradientFunction( *it ) );
2971     }
2972     return true;
2973 }
2974 
2975 bool PDFWriterImpl::emitTilings()
2976 {
2977     OStringBuffer aTilingObj( 1024 );
2978 
2979     for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it )
2980     {
2981         DBG_ASSERT( it->m_pTilingStream, "tiling without stream" );
2982         if( ! it->m_pTilingStream )
2983             continue;
2984 
2985         aTilingObj.setLength( 0 );
2986 
2987         #if OSL_DEBUG_LEVEL > 1
2988         emitComment( "PDFWriterImpl::emitTilings" );
2989         #endif
2990 
2991         sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left();
2992         sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top();
2993         sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth();
2994         sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight();
2995         if( it->m_aCellSize.Width() == 0 )
2996             it->m_aCellSize.Width() = nW;
2997         if( it->m_aCellSize.Height() == 0 )
2998             it->m_aCellSize.Height() = nH;
2999 
3000         bool bDeflate = compressStream( it->m_pTilingStream );
3001         it->m_pTilingStream->Seek( STREAM_SEEK_TO_END );
3002         sal_Size nTilingStreamSize = it->m_pTilingStream->Tell();
3003         it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN );
3004 
3005         // write pattern object
3006         aTilingObj.append( it->m_nObject );
3007         aTilingObj.append( " 0 obj\n" );
3008         aTilingObj.append( "<</Type/Pattern/PatternType 1\n"
3009                            "/PaintType 1\n"
3010                            "/TilingType 2\n"
3011                            "/BBox[" );
3012         appendFixedInt( nX, aTilingObj );
3013         aTilingObj.append( ' ' );
3014         appendFixedInt( nY, aTilingObj );
3015         aTilingObj.append( ' ' );
3016         appendFixedInt( nX+nW, aTilingObj );
3017         aTilingObj.append( ' ' );
3018         appendFixedInt( nY+nH, aTilingObj );
3019         aTilingObj.append( "]\n"
3020                            "/XStep " );
3021         appendFixedInt( it->m_aCellSize.Width(), aTilingObj );
3022         aTilingObj.append( "\n"
3023                            "/YStep " );
3024         appendFixedInt( it->m_aCellSize.Height(), aTilingObj );
3025         aTilingObj.append( "\n" );
3026         if( it->m_aTransform.matrix[0] != 1.0 ||
3027             it->m_aTransform.matrix[1] != 0.0 ||
3028             it->m_aTransform.matrix[3] != 0.0 ||
3029             it->m_aTransform.matrix[4] != 1.0 ||
3030             it->m_aTransform.matrix[2] != 0.0 ||
3031             it->m_aTransform.matrix[5] != 0.0 )
3032         {
3033             aTilingObj.append( "/Matrix [" );
3034             // TODO: scaling, mirroring on y, etc
3035             appendDouble( it->m_aTransform.matrix[0], aTilingObj );
3036             aTilingObj.append( ' ' );
3037             appendDouble( it->m_aTransform.matrix[1], aTilingObj );
3038             aTilingObj.append( ' ' );
3039             appendDouble( it->m_aTransform.matrix[3], aTilingObj );
3040             aTilingObj.append( ' ' );
3041             appendDouble( it->m_aTransform.matrix[4], aTilingObj );
3042             aTilingObj.append( ' ' );
3043             appendDouble( it->m_aTransform.matrix[2], aTilingObj );
3044             aTilingObj.append( ' ' );
3045             appendDouble( it->m_aTransform.matrix[5], aTilingObj );
3046             aTilingObj.append( "]\n" );
3047         }
3048         aTilingObj.append( "/Resources" );
3049         it->m_aResources.append( aTilingObj, getFontDictObject() );
3050         if( bDeflate )
3051             aTilingObj.append( "/Filter/FlateDecode" );
3052         aTilingObj.append( "/Length " );
3053         aTilingObj.append( (sal_Int32)nTilingStreamSize );
3054         aTilingObj.append( ">>\nstream\n" );
3055         CHECK_RETURN( updateObject( it->m_nObject ) );
3056         CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
3057         checkAndEnableStreamEncryption( it->m_nObject );
3058         nTilingStreamSize = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize );
3059         delete it->m_pTilingStream;
3060         it->m_pTilingStream = NULL;
3061         if( nTilingStreamSize == 0 )
3062             return false;
3063         disableStreamEncryption();
3064         aTilingObj.setLength( 0 );
3065         aTilingObj.append( "\nendstream\nendobj\n\n" );
3066         CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
3067     }
3068     return true;
3069 }
3070 
3071 sal_Int32 PDFWriterImpl::emitBuiltinFont( const ImplFontData* pFont, sal_Int32 nFontObject )
3072 {
3073     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
3074     if( !pFD )
3075         return 0;
3076     const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
3077 
3078     OStringBuffer aLine( 1024 );
3079 
3080     if( nFontObject <= 0 )
3081         nFontObject = createObject();
3082     CHECK_RETURN( updateObject( nFontObject ) );
3083     aLine.append( nFontObject );
3084     aLine.append( " 0 obj\n"
3085                   "<</Type/Font/Subtype/Type1/BaseFont/" );
3086     appendName( pBuiltinFont->m_pPSName, aLine );
3087     aLine.append( "\n" );
3088     if( pBuiltinFont->m_eCharSet == RTL_TEXTENCODING_MS_1252 )
3089          aLine.append( "/Encoding/WinAnsiEncoding\n" );
3090     aLine.append( ">>\nendobj\n\n" );
3091     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3092     return nFontObject;
3093 }
3094 
3095 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const ImplFontData* pFont, EmbedFont& rEmbed )
3096 {
3097     std::map< sal_Int32, sal_Int32 > aRet;
3098     if( isBuiltinFont( pFont ) )
3099     {
3100         aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont );
3101         return aRet;
3102     }
3103 
3104     sal_Int32 nFontObject = 0;
3105     sal_Int32 nFontDescriptor = 0;
3106     rtl::OString aSubType( "/Type1" );
3107     FontSubsetInfo aInfo;
3108     // fill in dummy values
3109     aInfo.m_nAscent = 1000;
3110 	aInfo.m_nDescent = 200;
3111 	aInfo.m_nCapHeight = 1000;
3112 	aInfo.m_aFontBBox = Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) );
3113     aInfo.m_aPSName = pFont->maName;
3114     sal_Int32 pWidths[256];
3115     rtl_zeroMemory( pWidths, sizeof(pWidths) );
3116     if( pFont->IsEmbeddable() )
3117     {
3118         const unsigned char* pFontData = NULL;
3119         long nFontLen = 0;
3120         sal_Ucs nEncodedCodes[256];
3121         sal_Int32 pEncWidths[256];
3122         if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pEncWidths, aInfo, &nFontLen )) != NULL )
3123         {
3124             m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3125             for( int i = 0; i < 256; i++ )
3126             {
3127                 if( nEncodedCodes[i] >= 32 && nEncodedCodes[i] < 256 )
3128                 {
3129                     pWidths[i] = pEncWidths[ i ];
3130                 }
3131             }
3132         }
3133     }
3134     else if( pFont->mbSubsettable )
3135     {
3136         aSubType = rtl::OString( "/TrueType" );
3137         Int32Vector aGlyphWidths;
3138         Ucs2UIntMap aUnicodeMap;
3139         m_pReferenceDevice->mpGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap );
3140 
3141         OUString aTmpName;
3142         osl_createTempFile( NULL, NULL, &aTmpName.pData );
3143         sal_Int32 pGlyphIDs[ 256 ];
3144         sal_uInt8 pEncoding[ 256 ];
3145         sal_Ucs   pUnicodes[ 256 ];
3146         sal_Int32 pDuWidths[ 256 ];
3147 
3148         memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) );
3149         memset( pEncoding, 0, sizeof( pEncoding ) );
3150         memset( pUnicodes, 0, sizeof( pUnicodes ) );
3151         memset( pDuWidths, 0, sizeof( pDuWidths ) );
3152 
3153         for( sal_Ucs c = 32; c < 256; c++ )
3154         {
3155             pUnicodes[c] = c;
3156             pEncoding[c] = c;
3157             pGlyphIDs[c] = 0;
3158             if( aUnicodeMap.find( c ) != aUnicodeMap.end() )
3159                 pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ];
3160         }
3161 
3162         m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, pFont, pGlyphIDs, pEncoding, pDuWidths, 256, aInfo );
3163         osl_removeFile( aTmpName.pData );
3164     }
3165     else
3166     {
3167         DBG_ERROR( "system font neither embeddable nor subsettable" );
3168     }
3169 
3170     // write font descriptor
3171     nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 );
3172     if( nFontDescriptor )
3173     {
3174         // write font object
3175         sal_Int32 nObject = createObject();
3176         if( updateObject( nObject ) )
3177         {
3178             OStringBuffer aLine( 1024 );
3179             aLine.append( nObject );
3180             aLine.append( " 0 obj\n"
3181                           "<</Type/Font/Subtype" );
3182             aLine.append( aSubType );
3183             aLine.append( "/BaseFont/" );
3184             appendName( aInfo.m_aPSName, aLine );
3185             aLine.append( "\n" );
3186             if( !pFont->mbSymbolFlag )
3187                 aLine.append( "/Encoding/WinAnsiEncoding\n" );
3188             aLine.append( "/FirstChar 32 /LastChar 255\n"
3189                           "/Widths[" );
3190             for( int i = 32; i < 256; i++ )
3191             {
3192                 aLine.append( pWidths[i] );
3193                 aLine.append( ((i&15) == 15) ? "\n" : " " );
3194             }
3195             aLine.append( "]\n"
3196                           "/FontDescriptor " );
3197             aLine.append( nFontDescriptor );
3198             aLine.append( " 0 R>>\n"
3199                           "endobj\n\n" );
3200             writeBuffer( aLine.getStr(), aLine.getLength() );
3201 
3202             nFontObject = nObject;
3203             aRet[ rEmbed.m_nNormalFontID ] = nObject;
3204         }
3205     }
3206 
3207     return aRet;
3208 }
3209 
3210 typedef int ThreeInts[3];
3211 static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen,
3212 	ThreeInts& rSegmentLengths )
3213 {
3214 	if( !pFontBytes || (nByteLen < 0) )
3215 		return false;
3216 	const unsigned char* pPtr = pFontBytes;
3217 	const unsigned char* pEnd = pFontBytes + nByteLen;
3218 
3219 	for( int i = 0; i < 3; ++i) {
3220 		// read segment1 header
3221 		if( pPtr+6 >= pEnd )
3222 			return false;
3223 		if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) )
3224 			return false;
3225 		const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2];
3226 		if( nLen <= 0)
3227 			return false;
3228 		rSegmentLengths[i] = nLen;
3229 		pPtr += nLen + 6;
3230 	}
3231 
3232 	// read segment-end header
3233 	if( pPtr+2 >= pEnd )
3234 		return false;
3235 	if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) )
3236 		return false;
3237 
3238 	return true;
3239 }
3240 
3241 struct FontException : public std::exception
3242 {
3243 };
3244 
3245 // TODO: always subset instead of embedding the full font => this method becomes obsolete then
3246 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFontData* pFont, EmbedFont& rEmbed )
3247 {
3248     std::map< sal_Int32, sal_Int32 > aRet;
3249     if( isBuiltinFont( pFont ) )
3250     {
3251         aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont );
3252         return aRet;
3253     }
3254 
3255     sal_Int32 nFontObject = 0;
3256     sal_Int32 nStreamObject = 0;
3257     sal_Int32 nFontDescriptor = 0;
3258 
3259     // prepare font encoding
3260     const Ucs2SIntMap* pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pFont, NULL );
3261     sal_Int32 nToUnicodeStream = 0;
3262     sal_uInt8 nEncoding[256];
3263     sal_Ucs nEncodedCodes[256];
3264     std::vector<sal_Ucs> aUnicodes;
3265     aUnicodes.reserve( 256 );
3266     sal_Int32 pUnicodesPerGlyph[256];
3267     sal_Int32 pEncToUnicodeIndex[256];
3268     if( pEncoding )
3269     {
3270         rtl_zeroMemory( nEncoding, sizeof(nEncoding) );
3271         rtl_zeroMemory( nEncodedCodes, sizeof(nEncodedCodes) );
3272         rtl_zeroMemory( pUnicodesPerGlyph, sizeof(pUnicodesPerGlyph) );
3273         rtl_zeroMemory( pEncToUnicodeIndex, sizeof(pEncToUnicodeIndex) );
3274         for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it )
3275         {
3276             if( it->second != -1 )
3277             {
3278                 sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff);
3279                 nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode );
3280                 nEncodedCodes[ nCode ] = it->first;
3281                 pEncToUnicodeIndex[ nCode ] = static_cast<sal_Int32>(aUnicodes.size());
3282                 aUnicodes.push_back( it->first );
3283                 pUnicodesPerGlyph[ nCode ] = 1;
3284             }
3285         }
3286     }
3287 
3288     FontSubsetInfo aInfo;
3289     sal_Int32 pWidths[256];
3290     const unsigned char* pFontData = NULL;
3291     long nFontLen = 0;
3292     sal_Int32 nLength1, nLength2;
3293     try
3294     {
3295         if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL )
3296         {
3297             if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 )
3298                 throw FontException();
3299             // see whether it is pfb or pfa; if it is a pfb, fill ranges
3300             // of 6 bytes that are not part of the font program
3301             std::list< int > aSections;
3302             std::list< int >::const_iterator it;
3303             int nIndex = 0;
3304             while( pFontData[nIndex] == 0x80 && nIndex < nFontLen-1 )
3305             {
3306                 aSections.push_back( nIndex );
3307                 if( pFontData[nIndex+1] == 0x03 )
3308                     break;
3309                 sal_Int32 nBytes =
3310                 ((sal_Int32)pFontData[nIndex+2])			|
3311                 ((sal_Int32)pFontData[nIndex+3]) << 8		|
3312                 ((sal_Int32)pFontData[nIndex+4]) << 16		|
3313                 ((sal_Int32)pFontData[nIndex+5]) << 24;
3314                 nIndex += nBytes+6;
3315             }
3316 
3317             // search for eexec
3318             // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below
3319             nIndex = 0;
3320             int nEndAsciiIndex;
3321             int nBeginBinaryIndex;
3322             int nEndBinaryIndex;
3323             do
3324             {
3325                 while( nIndex < nFontLen-4 &&
3326                     ( pFontData[nIndex] != 'e'	||
3327                         pFontData[nIndex+1] != 'e' ||
3328                         pFontData[nIndex+2] != 'x' ||
3329                         pFontData[nIndex+3] != 'e' ||
3330                         pFontData[nIndex+4] != 'c'
3331                         )
3332                     )
3333                 nIndex++;
3334                 // check whether we are in a excluded section
3335                 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3336                     ;
3337             } while( it != aSections.end() && nIndex < nFontLen-4 );
3338             // this should end the ascii part
3339             if( nIndex > nFontLen-5 )
3340                 throw FontException();
3341 
3342             nEndAsciiIndex = nIndex+4;
3343             // now count backwards until we can account for 512 '0'
3344             // which is the endmarker of the (hopefully) binary data
3345             // do not count the pfb header sections
3346             int nFound = 0;
3347             nIndex =  nFontLen-1;
3348             while( nIndex > 0 && nFound < 512 )
3349             {
3350                 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3351                     ;
3352                 if( it == aSections.end() )
3353                 {
3354                     // inside the 512 '0' block there may only be whitespace
3355                     // according to T1 spec; probably it would be to simple
3356                     // if all fonts complied
3357                     if( pFontData[nIndex] == '0' )
3358                         nFound++;
3359                         else if( nFound > 0					&&
3360                             pFontData[nIndex] != '\r'		&&
3361                         pFontData[nIndex] != '\t'		&&
3362                         pFontData[nIndex] != '\n'		&&
3363                         pFontData[nIndex] != ' ' )
3364                         break;
3365                 }
3366                 nIndex--;
3367             }
3368 
3369             if( nIndex < 1 || nIndex <= nEndAsciiIndex )
3370                 throw FontException();
3371 
3372             // nLength3 is the rest of the file - excluding any section headers
3373             // nIndex now points to the first of the 512 '0' characters marking the
3374             // fixed content portion
3375             sal_Int32 nLength3 = nFontLen - nIndex;
3376             for( it = aSections.begin(); it != aSections.end(); ++it )
3377             {
3378                 if( *it >= nIndex  )
3379                 {
3380                 // special case: nIndex inside a section marker
3381                     if( nIndex >= (*it) && (*it)+5 > nIndex )
3382                         nLength3 -= (*it)+5 - nIndex;
3383                     else
3384                     {
3385                         if( *it < nFontLen - 6 )
3386                             nLength3 -= 6;
3387                         else // the last section 0x8003 is only 2 bytes after all
3388                             nLength3 -= (nFontLen - *it);
3389                     }
3390                 }
3391             }
3392 
3393             // there may be whitespace to ignore before the 512 '0'
3394             while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' )
3395             {
3396                 nIndex--;
3397                 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3398                     ;
3399                 if( it != aSections.end() )
3400                 {
3401                     nIndex = (*it)-1;
3402                     break; // this is surely a binary boundary, in ascii case it wouldn't matter
3403                 }
3404             }
3405             nEndBinaryIndex = nIndex;
3406 
3407             // search for beginning of binary section
3408             nBeginBinaryIndex = nEndAsciiIndex;
3409             do
3410             {
3411                 nBeginBinaryIndex++;
3412                 for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it )
3413                     ;
3414                     } while( nBeginBinaryIndex < nEndBinaryIndex &&
3415                         ( pFontData[nBeginBinaryIndex] == '\r'	||
3416                             pFontData[nBeginBinaryIndex] == '\n'	||
3417                             it != aSections.end() ) );
3418 
3419                     // it seems to be vital to copy the exact whitespace between binary data
3420                     // and eexec, else a invalid font results. so make nEndAsciiIndex
3421                     // always immediate in front of nBeginBinaryIndex
3422                     nEndAsciiIndex = nBeginBinaryIndex-1;
3423                     for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it )
3424                         ;
3425                     if( it != aSections.end() )
3426                         nEndAsciiIndex = (*it)-1;
3427 
3428                     nLength1 = nEndAsciiIndex+1; // including the last character
3429                     for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it )
3430                         nLength1 -= 6; // decrease by pfb section size
3431 
3432                     // if the first four bytes are all ascii hex characters, then binary data
3433                     // has to be converted to real binary data
3434                     for( nIndex = 0; nIndex < 4 &&
3435                         ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) ||
3436                             ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) ||
3437                             ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' )
3438                             ); ++nIndex )
3439                     ;
3440                     bool bConvertHexData = true;
3441                     if( nIndex < 4 )
3442                     {
3443                         bConvertHexData = false;
3444                         nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte
3445                         for( it = aSections.begin(); it != aSections.end(); ++it )
3446                             if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex )
3447                             nLength2 -= 6;
3448                     }
3449                     else
3450                     {
3451                         // count the hex ascii characters to get nLength2
3452                         nLength2 = 0;
3453                         int nNextSectionIndex = 0;
3454                         for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3455                             ;
3456                         if( it != aSections.end() )
3457                             nNextSectionIndex = *it;
3458                         for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3459                         {
3460                             if( nIndex == nNextSectionIndex )
3461                             {
3462                                 nIndex += 6;
3463                                 ++it;
3464                                 nNextSectionIndex = (it == aSections.end() ? 0 : *it );
3465                             }
3466                             if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) ||
3467                                 ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) ||
3468                             ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) )
3469                             nLength2++;
3470                         }
3471                         DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" );
3472                         nLength2 /= 2;
3473                     }
3474 
3475                     // now we can actually write the font stream !
3476                     #if OSL_DEBUG_LEVEL > 1
3477                     emitComment( " PDFWriterImpl::emitEmbeddedFont" );
3478                     #endif
3479                     OStringBuffer aLine( 512 );
3480                     nStreamObject = createObject();
3481                     if( !updateObject(nStreamObject))
3482                         throw FontException();
3483                     sal_Int32 nStreamLengthObject = createObject();
3484                     aLine.append( nStreamObject );
3485                     aLine.append( " 0 obj\n"
3486                         "<</Length " );
3487                     aLine.append( nStreamLengthObject );
3488                     aLine.append( " 0 R"
3489                         #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3490                         "/Filter/FlateDecode"
3491                         #endif
3492                         "/Length1 " );
3493                     aLine.append( nLength1 );
3494                     aLine.append( " /Length2 " );
3495                     aLine.append( nLength2 );
3496                     aLine.append( " /Length3 ");
3497                     aLine.append( nLength3 );
3498                     aLine.append( ">>\n"
3499                         "stream\n" );
3500                     if( !writeBuffer( aLine.getStr(), aLine.getLength() ) )
3501                         throw FontException();
3502 
3503                     sal_uInt64 nBeginStreamPos = 0;
3504                     osl_getFilePos( m_aFile, &nBeginStreamPos );
3505 
3506                     beginCompression();
3507                     checkAndEnableStreamEncryption( nStreamObject );
3508 
3509                     // write ascii section
3510                     if( aSections.begin() == aSections.end() )
3511                     {
3512                         if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) )
3513                             throw FontException();
3514                     }
3515                     else
3516                     {
3517                         // first section always starts at 0
3518                         it = aSections.begin();
3519                         nIndex = (*it)+6;
3520                         ++it;
3521                         while( *it < nEndAsciiIndex )
3522                         {
3523                             if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) )
3524                                 throw FontException();
3525                             nIndex = (*it)+6;
3526                             ++it;
3527                         }
3528                         // write partial last section
3529                         if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) )
3530                             throw FontException();
3531                     }
3532 
3533                     // write binary section
3534                     if( ! bConvertHexData )
3535                     {
3536                         if( aSections.begin() == aSections.end() )
3537                         {
3538                             if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) )
3539                                 throw FontException();
3540                         }
3541                         else
3542                         {
3543                             for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it )
3544                                 ;
3545                             // write first partial section
3546                             if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) )
3547                                 throw FontException();
3548                             // write following sections
3549                             while( it != aSections.end() )
3550                             {
3551                                 nIndex = (*it)+6;
3552                                 ++it;
3553                                 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3554                                 {
3555                                     sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3556                                     if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3557                                         throw FontException();
3558                                 }
3559                             }
3560                         }
3561                     }
3562                     else
3563                     {
3564                         boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] );
3565                         rtl_zeroMemory( pWriteBuffer.get(), nLength2 );
3566                         int nWriteIndex = 0;
3567 
3568                         int nNextSectionIndex = 0;
3569                         for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3570                             ;
3571                         if( it != aSections.end() )
3572                             nNextSectionIndex = *it;
3573                         for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3574                         {
3575                             if( nIndex == nNextSectionIndex )
3576                             {
3577                                 nIndex += 6;
3578                                 ++it;
3579                                 nNextSectionIndex = (it == aSections.end() ? nFontLen : *it );
3580                             }
3581                             unsigned char cNibble = 0x80;
3582                             if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' )
3583                                 cNibble = pFontData[nIndex] - '0';
3584                             else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' )
3585                                 cNibble = pFontData[nIndex] - 'a' + 10;
3586                             else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' )
3587                                 cNibble = pFontData[nIndex] - 'A' + 10;
3588                             if( cNibble != 0x80 )
3589                             {
3590                                 if( !(nWriteIndex & 1 ) )
3591                                     cNibble <<= 4;
3592                                 pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble;
3593                                 nWriteIndex++;
3594                             }
3595                         }
3596                         if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) )
3597                             throw FontException();
3598                         if( aSections.empty() )
3599                         {
3600                             if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) )
3601                                 throw FontException();
3602                         }
3603                         else
3604                         {
3605                             // write rest of this section
3606                             if( nIndex < nNextSectionIndex )
3607                             {
3608                                 if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) )
3609                                     throw FontException();
3610                             }
3611                             // write following sections
3612                             while( it != aSections.end() )
3613                             {
3614                                 nIndex = (*it)+6;
3615                                 ++it;
3616                                 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3617                                 {
3618                                     sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3619                                     if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3620                                         throw FontException();
3621                                 }
3622                             }
3623                         }
3624                     }
3625                     endCompression();
3626                     disableStreamEncryption();
3627 
3628 
3629                     sal_uInt64 nEndStreamPos = 0;
3630                     osl_getFilePos( m_aFile, &nEndStreamPos );
3631 
3632                     // and finally close the stream
3633                     aLine.setLength( 0 );
3634                     aLine.append( "\nendstream\nendobj\n\n" );
3635                     if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3636                         throw FontException();
3637 
3638                     // write stream length object
3639                     aLine.setLength( 0 );
3640                     if( ! updateObject( nStreamLengthObject ) )
3641                         throw FontException();
3642                     aLine.append( nStreamLengthObject );
3643                     aLine.append( " 0 obj\n" );
3644                     aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) );
3645                     aLine.append( "\nendobj\n\n" );
3646                     if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3647                         throw FontException();
3648         }
3649         else
3650         {
3651             rtl::OStringBuffer aErrorComment( 256 );
3652             aErrorComment.append( "GetEmbedFontData failed for font \"" );
3653             aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3654             aErrorComment.append( '\"' );
3655             if( pFont->GetSlant() == ITALIC_NORMAL )
3656                 aErrorComment.append( " italic" );
3657             else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3658                 aErrorComment.append( " oblique" );
3659             aErrorComment.append( " weight=" );
3660             aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3661             emitComment( aErrorComment.getStr() );
3662         }
3663 
3664         if( nStreamObject )
3665             // write font descriptor
3666         nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject );
3667 
3668         if( nFontDescriptor )
3669         {
3670             if( pEncoding )
3671                 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, sizeof(nEncoding)/sizeof(nEncoding[0]) );
3672 
3673             // write font object
3674             sal_Int32 nObject = createObject();
3675             if( ! updateObject( nObject ) )
3676                 throw FontException();
3677 
3678             OStringBuffer aLine( 1024 );
3679             aLine.append( nObject );
3680             aLine.append( " 0 obj\n"
3681                 "<</Type/Font/Subtype/Type1/BaseFont/" );
3682             appendName( aInfo.m_aPSName, aLine );
3683             aLine.append( "\n" );
3684             if( !pFont->mbSymbolFlag &&  pEncoding == 0 )
3685                 aLine.append( "/Encoding/WinAnsiEncoding\n" );
3686             if( nToUnicodeStream )
3687             {
3688                 aLine.append( "/ToUnicode " );
3689                 aLine.append( nToUnicodeStream );
3690                 aLine.append( " 0 R\n" );
3691             }
3692             aLine.append( "/FirstChar 0 /LastChar 255\n"
3693                 "/Widths[" );
3694             for( int i = 0; i < 256; i++ )
3695             {
3696                 aLine.append( pWidths[i] );
3697                 aLine.append( ((i&15) == 15) ? "\n" : " " );
3698             }
3699             aLine.append( "]\n"
3700                 "/FontDescriptor " );
3701             aLine.append( nFontDescriptor );
3702             aLine.append( " 0 R>>\n"
3703                 "endobj\n\n" );
3704             if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3705                 throw FontException();
3706 
3707             nFontObject = nObject;
3708 
3709             aRet[ rEmbed.m_nNormalFontID ] = nObject;
3710 
3711             // write additional encodings
3712             for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it )
3713             {
3714                 sal_Int32 aEncWidths[ 256 ];
3715                 // emit encoding dict
3716                 sal_Int32 nEncObject = createObject();
3717                 if( ! updateObject( nEncObject ) )
3718                     throw FontException();
3719 
3720                 OutputDevice* pRef = getReferenceDevice();
3721                 pRef->Push( PUSH_FONT | PUSH_MAPMODE );
3722                 pRef->SetMapMode( MapMode( MAP_PIXEL ) );
3723                 Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) );
3724                 aFont.SetWeight( pFont->GetWeight() );
3725                 aFont.SetItalic( pFont->GetSlant() );
3726                 aFont.SetPitch( pFont->GetPitch() );
3727                 pRef->SetFont( aFont );
3728                 pRef->ImplNewFont();
3729 
3730                 aLine.setLength( 0 );
3731                 aLine.append( nEncObject );
3732                 aLine.append( " 0 obj\n"
3733                     "<</Type/Encoding/Differences[ 0\n" );
3734                 int nEncoded = 0;
3735                 aUnicodes.clear();
3736                 for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it )
3737                 {
3738                     String aStr( str_it->m_aUnicode );
3739                     aEncWidths[nEncoded] = pRef->GetTextWidth( aStr );
3740                     nEncodedCodes[nEncoded] = str_it->m_aUnicode;
3741                     nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded);
3742                     pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size());
3743                     aUnicodes.push_back( nEncodedCodes[nEncoded] );
3744                     pUnicodesPerGlyph[nEncoded] = 1;
3745 
3746                     aLine.append( " /" );
3747                     aLine.append( str_it->m_aName );
3748                     if( !((++nEncoded) & 15) )
3749                         aLine.append( "\n" );
3750                 }
3751                 aLine.append( "]>>\n"
3752                     "endobj\n\n" );
3753 
3754                 pRef->Pop();
3755 
3756                 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3757                     throw FontException();
3758 
3759                 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded );
3760 
3761                 nObject = createObject();
3762                 if( ! updateObject( nObject ) )
3763                     throw FontException();
3764 
3765                 aLine.setLength( 0 );
3766                 aLine.append( nObject );
3767                 aLine.append( " 0 obj\n"
3768                     "<</Type/Font/Subtype/Type1/BaseFont/" );
3769                 appendName( aInfo.m_aPSName, aLine );
3770                 aLine.append( "\n" );
3771                 aLine.append( "/Encoding " );
3772                 aLine.append( nEncObject );
3773                 aLine.append( " 0 R\n" );
3774                 if( nToUnicodeStream )
3775                 {
3776                     aLine.append( "/ToUnicode " );
3777                     aLine.append( nToUnicodeStream );
3778                     aLine.append( " 0 R\n" );
3779                 }
3780                 aLine.append( "/FirstChar 0\n"
3781                     "/LastChar " );
3782                 aLine.append( (sal_Int32)(nEncoded-1) );
3783                 aLine.append( "\n"
3784                     "/Widths[" );
3785                 for( int i = 0; i < nEncoded; i++ )
3786                 {
3787                     aLine.append( aEncWidths[i] );
3788                     aLine.append( ((i&15) == 15) ? "\n" : " " );
3789                 }
3790                 aLine.append( " ]\n"
3791                     "/FontDescriptor " );
3792                 aLine.append( nFontDescriptor );
3793                 aLine.append( " 0 R>>\n"
3794                     "endobj\n\n" );
3795                 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3796                     throw FontException();
3797 
3798                 aRet[ enc_it->m_nFontID ] = nObject;
3799             }
3800         }
3801     }
3802     catch( FontException& )
3803     {
3804         // these do nothing in case there was no compression or encryption ongoing
3805         endCompression();
3806         disableStreamEncryption();
3807     }
3808 
3809     if( pFontData )
3810         m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3811 
3812     return aRet;
3813 }
3814 
3815 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
3816 {
3817     if( nSubsetID )
3818     {
3819         for( int i = 0; i < 6; i++ )
3820         {
3821             int nOffset = (nSubsetID % 26);
3822             nSubsetID /= 26;
3823             rBuffer.append( (sal_Char)('A'+nOffset) );
3824         }
3825         rBuffer.append( '+' );
3826     }
3827     appendName( rPSName, rBuffer );
3828 }
3829 
3830 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding,
3831                                               sal_Ucs* pUnicodes,
3832                                               sal_Int32* pUnicodesPerGlyph,
3833                                               sal_Int32* pEncToUnicodeIndex,
3834                                               int nGlyphs )
3835 {
3836     int nMapped = 0, n = 0;
3837     for( n = 0; n < nGlyphs; n++ )
3838         if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3839             nMapped++;
3840 
3841     if( nMapped == 0 )
3842         return 0;
3843 
3844     sal_Int32 nStream = createObject();
3845     CHECK_RETURN( updateObject( nStream ) );
3846 
3847     OStringBuffer aContents( 1024 );
3848     aContents.append(
3849                      "/CIDInit/ProcSet findresource begin\n"
3850                      "12 dict begin\n"
3851                      "begincmap\n"
3852                      "/CIDSystemInfo<<\n"
3853                      "/Registry (Adobe)\n"
3854                      "/Ordering (UCS)\n"
3855                      "/Supplement 0\n"
3856                      ">> def\n"
3857                      "/CMapName/Adobe-Identity-UCS def\n"
3858                      "/CMapType 2 def\n"
3859                      "1 begincodespacerange\n"
3860                      "<00> <FF>\n"
3861                      "endcodespacerange\n"
3862                      );
3863     int nCount = 0;
3864     for( n = 0; n < nGlyphs; n++ )
3865     {
3866         if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3867         {
3868             if( (nCount % 100) == 0 )
3869             {
3870                 if( nCount )
3871                     aContents.append( "endbfchar\n" );
3872                 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) );
3873                 aContents.append( " beginbfchar\n" );
3874             }
3875             aContents.append( '<' );
3876             appendHex( (sal_Int8)pEncoding[n], aContents );
3877             aContents.append( "> <" );
3878             // TODO: handle unicodes>U+FFFF
3879             sal_Int32 nIndex = pEncToUnicodeIndex[n];
3880 	        for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ )
3881 	        {
3882                 appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents );
3883                 appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents );
3884             }
3885             aContents.append( ">\n" );
3886             nCount++;
3887         }
3888     }
3889     aContents.append( "endbfchar\n"
3890                       "endcmap\n"
3891                       "CMapName currentdict /CMap defineresource pop\n"
3892                       "end\n"
3893                       "end\n" );
3894 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3895     ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
3896     SvMemoryStream aStream;
3897     pCodec->BeginCompression();
3898     pCodec->Write( aStream, (const sal_uInt8*)aContents.getStr(), aContents.getLength() );
3899     pCodec->EndCompression();
3900     delete pCodec;
3901 #endif
3902 
3903     #if OSL_DEBUG_LEVEL > 1
3904     emitComment( "PDFWriterImpl::createToUnicodeCMap" );
3905     #endif
3906     OStringBuffer aLine( 40 );
3907 
3908     aLine.append( nStream );
3909     aLine.append( " 0 obj\n<</Length " );
3910 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3911     sal_Int32 nLen = (sal_Int32)aStream.Tell();
3912     aStream.Seek( 0 );
3913     aLine.append( nLen );
3914     aLine.append( "/Filter/FlateDecode" );
3915 #else
3916     aLine.append( aContents.getLength() );
3917 #endif
3918     aLine.append( ">>\nstream\n" );
3919     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3920     checkAndEnableStreamEncryption( nStream );
3921 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3922     CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
3923 #else
3924     CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
3925 #endif
3926     disableStreamEncryption();
3927     aLine.setLength( 0 );
3928     aLine.append( "\nendstream\n"
3929                   "endobj\n\n" );
3930     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3931     return nStream;
3932 }
3933 
3934 sal_Int32 PDFWriterImpl::emitFontDescriptor( const ImplFontData* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
3935 {
3936     OStringBuffer aLine( 1024 );
3937     // get font flags, see PDF reference 1.4 p. 358
3938     // possibly characters outside Adobe standard encoding
3939     // so set Symbolic flag
3940     sal_Int32 nFontFlags = (1<<2);
3941     if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE )
3942         nFontFlags |= (1 << 6);
3943     if( pFont->GetPitch() == PITCH_FIXED )
3944         nFontFlags |= 1;
3945     if( pFont->GetFamilyType() == FAMILY_SCRIPT )
3946         nFontFlags |= (1 << 3);
3947     else if( pFont->GetFamilyType() == FAMILY_ROMAN )
3948         nFontFlags |= (1 << 1);
3949 
3950     sal_Int32 nFontDescriptor = createObject();
3951     CHECK_RETURN( updateObject( nFontDescriptor ) );
3952     aLine.setLength( 0 );
3953     aLine.append( nFontDescriptor );
3954     aLine.append( " 0 obj\n"
3955                   "<</Type/FontDescriptor/FontName/" );
3956     appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
3957     aLine.append( "\n"
3958                   "/Flags " );
3959     aLine.append( nFontFlags );
3960     aLine.append( "\n"
3961                   "/FontBBox[" );
3962     // note: Top and Bottom are reversed in VCL and PDF rectangles
3963     aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() );
3964     aLine.append( ' ' );
3965     aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() );
3966     aLine.append( ' ' );
3967     aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() );
3968     aLine.append( ' ' );
3969     aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) );
3970     aLine.append( "]/ItalicAngle " );
3971     if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL )
3972         aLine.append( "-30" );
3973     else
3974         aLine.append( "0" );
3975     aLine.append( "\n"
3976                   "/Ascent " );
3977     aLine.append( (sal_Int32)rInfo.m_nAscent );
3978     aLine.append( "\n"
3979                   "/Descent " );
3980     aLine.append( (sal_Int32)-rInfo.m_nDescent );
3981     aLine.append( "\n"
3982                   "/CapHeight " );
3983     aLine.append( (sal_Int32)rInfo.m_nCapHeight );
3984     // According to PDF reference 1.4 StemV is required
3985     // seems a tad strange to me, but well ...
3986     aLine.append( "\n"
3987                   "/StemV 80\n" );
3988     if( nFontStream )
3989     {
3990         aLine.append( "/FontFile" );
3991         switch( rInfo.m_nFontType )
3992         {
3993             case FontSubsetInfo::SFNT_TTF:
3994                 aLine.append( '2' );
3995                 break;
3996             case FontSubsetInfo::TYPE1_PFA:
3997             case FontSubsetInfo::TYPE1_PFB:
3998             case FontSubsetInfo::ANY_TYPE1:
3999                 break;
4000             default:
4001                 DBG_ERROR( "unknown fonttype in PDF font descriptor" );
4002                 return 0;
4003         }
4004         aLine.append( ' ' );
4005         aLine.append( nFontStream );
4006         aLine.append( " 0 R\n" );
4007     }
4008     aLine.append( ">>\n"
4009                   "endobj\n\n" );
4010     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4011 
4012     return nFontDescriptor;
4013 }
4014 
4015 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const
4016 {
4017     for( std::map< sal_Int32, sal_Int32 >::const_iterator it =
4018          m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it )
4019     {
4020         rDict.append( m_aBuiltinFonts[it->first].getNameObject() );
4021         rDict.append( ' ' );
4022         rDict.append( it->second );
4023         rDict.append( " 0 R" );
4024     }
4025 }
4026 
4027 bool PDFWriterImpl::emitFonts()
4028 {
4029     if( ! m_pReferenceDevice->ImplGetGraphics() )
4030         return false;
4031 
4032     OStringBuffer aLine( 1024 );
4033 
4034     std::map< sal_Int32, sal_Int32 > aFontIDToObject;
4035 
4036     OUString aTmpName;
4037     osl_createTempFile( NULL, NULL, &aTmpName.pData );
4038     for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it )
4039     {
4040         for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit )
4041         {
4042             sal_Int32 pGlyphIDs[ 256 ];
4043             sal_Int32 pWidths[ 256 ];
4044             sal_uInt8 pEncoding[ 256 ];
4045             sal_Int32 pEncToUnicodeIndex[ 256 ];
4046             sal_Int32 pUnicodesPerGlyph[ 256 ];
4047             std::vector<sal_Ucs> aUnicodes;
4048             aUnicodes.reserve( 256 );
4049             int nGlyphs = 1;
4050             // fill arrays and prepare encoding index map
4051             sal_Int32 nToUnicodeStream = 0;
4052 
4053             rtl_zeroMemory( pGlyphIDs, sizeof( pGlyphIDs ) );
4054             rtl_zeroMemory( pEncoding, sizeof( pEncoding ) );
4055             rtl_zeroMemory( pUnicodesPerGlyph, sizeof( pUnicodesPerGlyph ) );
4056             rtl_zeroMemory( pEncToUnicodeIndex, sizeof( pEncToUnicodeIndex ) );
4057             for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit )
4058             {
4059                 sal_uInt8 nEnc = fit->second.getGlyphId();
4060 
4061                 DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" );
4062                 DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" );
4063 
4064                 pGlyphIDs[ nEnc ] = fit->first;
4065                 pEncoding[ nEnc ] = nEnc;
4066                 pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size());
4067                 pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes();
4068                 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ )
4069                     aUnicodes.push_back( fit->second.getCode( n ) );
4070                 if( fit->second.getCode(0) )
4071                     nToUnicodeStream = 1;
4072                 if( nGlyphs < 256 )
4073                     nGlyphs++;
4074                 else
4075                 {
4076                     DBG_ERROR( "too many glyphs for subset" );
4077                 }
4078             }
4079             FontSubsetInfo aSubsetInfo;
4080             if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
4081             {
4082                 // create font stream
4083                 oslFileHandle aFontFile;
4084                 CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) );
4085                 // get file size
4086                 sal_uInt64 nLength1;
4087                 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) );
4088                 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength1 ) ) );
4089                 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
4090 
4091                 #if OSL_DEBUG_LEVEL > 1
4092                 emitComment( "PDFWriterImpl::emitFonts" );
4093                 #endif
4094                 sal_Int32 nFontStream = createObject();
4095                 sal_Int32 nStreamLengthObject = createObject();
4096                 CHECK_RETURN( updateObject( nFontStream ) );
4097                 aLine.setLength( 0 );
4098                 aLine.append( nFontStream );
4099                 aLine.append( " 0 obj\n"
4100                              "<</Length " );
4101                 aLine.append( (sal_Int32)nStreamLengthObject );
4102                 aLine.append( " 0 R"
4103                              #ifndef DEBUG_DISABLE_PDFCOMPRESSION
4104                              "/Filter/FlateDecode"
4105                              #endif
4106                              "/Length1 " );
4107 
4108                 sal_uInt64 nStartPos = 0;
4109                 if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF )
4110                 {
4111                     aLine.append( (sal_Int32)nLength1 );
4112 
4113                     aLine.append( ">>\n"
4114                                  "stream\n" );
4115                     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4116                     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
4117 
4118                     // copy font file
4119                     beginCompression();
4120                     checkAndEnableStreamEncryption( nFontStream );
4121                     sal_Bool bEOF = sal_False;
4122                     do
4123                     {
4124                         char buf[8192];
4125                         sal_uInt64 nRead;
4126                         CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) );
4127                         CHECK_RETURN( writeBuffer( buf, nRead ) );
4128                         CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) );
4129                     } while( ! bEOF );
4130                 }
4131                 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 )
4132                 {
4133                     // TODO: implement
4134                     DBG_ERROR( "PDFWriterImpl does not support CFF-font subsets yet!" );
4135                 }
4136                 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA?
4137                 {
4138                     boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] );
4139 
4140                     sal_uInt64 nBytesRead = 0;
4141                     CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer.get(), nLength1, &nBytesRead ) ) );
4142                     DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" );
4143                     CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
4144                     // get the PFB-segment lengths
4145                     ThreeInts aSegmentLengths = {0,0,0};
4146                     getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths );
4147                     // the lengths below are mandatory for PDF-exported Type1 fonts
4148                     // because the PFB segment headers get stripped! WhyOhWhy.
4149                     aLine.append( (sal_Int32)aSegmentLengths[0] );
4150                     aLine.append( "/Length2 " );
4151                     aLine.append( (sal_Int32)aSegmentLengths[1] );
4152                     aLine.append( "/Length3 " );
4153                     aLine.append( (sal_Int32)aSegmentLengths[2] );
4154 
4155                     aLine.append( ">>\n"
4156                                  "stream\n" );
4157                     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4158                     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
4159 
4160                     // emit PFB-sections without section headers
4161                     beginCompression();
4162                     checkAndEnableStreamEncryption( nFontStream );
4163                     CHECK_RETURN( writeBuffer( &pBuffer[6], aSegmentLengths[0] ) );
4164                     CHECK_RETURN( writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) );
4165                     CHECK_RETURN( writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) );
4166                 }
4167                 else
4168                 {
4169                     fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType);
4170                     aLine.append( "0 >>\nstream\n" );
4171                 }
4172 
4173                 endCompression();
4174                 disableStreamEncryption();
4175                 // close the file
4176                 osl_closeFile( aFontFile );
4177 
4178                 sal_uInt64 nEndPos = 0;
4179                 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) );
4180                 // end the stream
4181                 aLine.setLength( 0 );
4182                 aLine.append( "\nendstream\nendobj\n\n" );
4183                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4184 
4185                 // emit stream length object
4186                 CHECK_RETURN( updateObject( nStreamLengthObject ) );
4187                 aLine.setLength( 0 );
4188                 aLine.append( nStreamLengthObject );
4189                 aLine.append( " 0 obj\n" );
4190                 aLine.append( (sal_Int64)(nEndPos-nStartPos) );
4191                 aLine.append( "\nendobj\n\n" );
4192                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4193 
4194                 // write font descriptor
4195                 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream );
4196 
4197                 if( nToUnicodeStream )
4198                     nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs );
4199 
4200                 sal_Int32 nFontObject = createObject();
4201                 CHECK_RETURN( updateObject( nFontObject ) );
4202                 aLine.setLength( 0 );
4203                 aLine.append( nFontObject );
4204 
4205                 aLine.append( " 0 obj\n" );
4206                 aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ?
4207                              "<</Type/Font/Subtype/Type1/BaseFont/" :
4208                              "<</Type/Font/Subtype/TrueType/BaseFont/" );
4209                 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine );
4210                 aLine.append( "\n"
4211                              "/FirstChar 0\n"
4212                              "/LastChar " );
4213                 aLine.append( (sal_Int32)(nGlyphs-1) );
4214                 aLine.append( "\n"
4215                              "/Widths[" );
4216                 for( int i = 0; i < nGlyphs; i++ )
4217                 {
4218                     aLine.append( pWidths[ i ] );
4219                     aLine.append( ((i & 15) == 15) ? "\n" : " " );
4220                 }
4221                 aLine.append( "]\n"
4222                              "/FontDescriptor " );
4223                 aLine.append( nFontDescriptor );
4224                 aLine.append( " 0 R\n" );
4225                 if( nToUnicodeStream )
4226                 {
4227                     aLine.append( "/ToUnicode " );
4228                     aLine.append( nToUnicodeStream );
4229                     aLine.append( " 0 R\n" );
4230                 }
4231                 aLine.append( ">>\n"
4232                              "endobj\n\n" );
4233                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4234 
4235                 aFontIDToObject[ lit->m_nFontID ] = nFontObject;
4236             }
4237             else
4238             {
4239                 const ImplFontData* pFont = it->first;
4240                 rtl::OStringBuffer aErrorComment( 256 );
4241                 aErrorComment.append( "CreateFontSubset failed for font \"" );
4242                 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
4243                 aErrorComment.append( '\"' );
4244                 if( pFont->GetSlant() == ITALIC_NORMAL )
4245                     aErrorComment.append( " italic" );
4246                 else if( pFont->GetSlant() == ITALIC_OBLIQUE )
4247                     aErrorComment.append( " oblique" );
4248                 aErrorComment.append( " weight=" );
4249                 aErrorComment.append( sal_Int32(pFont->GetWeight()) );
4250                 emitComment( aErrorComment.getStr() );
4251             }
4252         }
4253     }
4254     osl_removeFile( aTmpName.pData );
4255 
4256     // emit embedded fonts
4257     for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit )
4258     {
4259         std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second );
4260         for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4261         {
4262             CHECK_RETURN( fit->second );
4263             aFontIDToObject[ fit->first ] = fit->second;
4264         }
4265     }
4266 
4267     // emit system fonts
4268     for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit )
4269     {
4270         std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second );
4271         for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4272         {
4273             CHECK_RETURN( fit->second );
4274             aFontIDToObject[ fit->first ] = fit->second;
4275         }
4276     }
4277 
4278     OStringBuffer aFontDict( 1024 );
4279     aFontDict.append( getFontDictObject() );
4280     aFontDict.append( " 0 obj\n"
4281                      "<<" );
4282     int ni = 0;
4283     for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit )
4284     {
4285         aFontDict.append( "/F" );
4286         aFontDict.append( mit->first );
4287         aFontDict.append( ' ' );
4288         aFontDict.append( mit->second );
4289         aFontDict.append( " 0 R" );
4290         if( ((++ni) & 7) == 0 )
4291             aFontDict.append( '\n' );
4292     }
4293     // emit builtin font for widget apperances / variable text
4294     for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin();
4295         it != m_aBuiltinFontToObjectMap.end(); ++it )
4296     {
4297         ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]);
4298         it->second = emitBuiltinFont( &aData, it->second );
4299     }
4300     appendBuiltinFontsToDict( aFontDict );
4301     aFontDict.append( "\n>>\nendobj\n\n" );
4302 
4303     CHECK_RETURN( updateObject( getFontDictObject() ) );
4304     CHECK_RETURN( writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) );
4305     return true;
4306 }
4307 
4308 sal_Int32 PDFWriterImpl::emitResources()
4309 {
4310     // emit shadings
4311     if( ! m_aGradients.empty() )
4312         CHECK_RETURN( emitGradients() );
4313     // emit tilings
4314     if( ! m_aTilings.empty() )
4315         CHECK_RETURN( emitTilings() );
4316 
4317     // emit font dict
4318     CHECK_RETURN( emitFonts() );
4319 
4320     // emit Resource dict
4321     OStringBuffer aLine( 512 );
4322     sal_Int32 nResourceDict = getResourceDictObj();
4323     CHECK_RETURN( updateObject( nResourceDict ) );
4324     aLine.setLength( 0 );
4325     aLine.append( nResourceDict );
4326     aLine.append( " 0 obj\n" );
4327     m_aGlobalResourceDict.append( aLine, getFontDictObject() );
4328     aLine.append( "endobj\n\n" );
4329     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4330     return nResourceDict;
4331 }
4332 
4333 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
4334                                                  sal_Int32 nItemLevel,
4335                                                  sal_Int32 nCurrentItemId )
4336 {
4337     /* The /Count number of an item is
4338        positive: the number of visible subitems
4339        negative: the negative number of subitems that will become visible if
4340                  the item gets opened
4341        see PDF ref 1.4 p 478
4342     */
4343 
4344     sal_Int32 nCount = 0;
4345 
4346     if( m_aContext.OpenBookmarkLevels < 0           || // all levels arevisible
4347         m_aContext.OpenBookmarkLevels >= nItemLevel    // this level is visible
4348       )
4349     {
4350         PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4351         sal_Int32 nChildren = rItem.m_aChildren.size();
4352         for( sal_Int32 i = 0; i < nChildren; i++ )
4353             nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4354         rCounts[nCurrentItemId] = nCount;
4355         // return 1 (this item) + visible sub items
4356         if( nCount < 0 )
4357             nCount = 0;
4358         nCount++;
4359     }
4360     else
4361     {
4362         // this bookmark level is invisible
4363         PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4364         sal_Int32 nChildren = rItem.m_aChildren.size();
4365         rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
4366         for( sal_Int32 i = 0; i < nChildren; i++ )
4367             updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4368         nCount = -1;
4369     }
4370 
4371     return nCount;
4372 }
4373 
4374 sal_Int32 PDFWriterImpl::emitOutline()
4375 {
4376     int i, nItems = m_aOutline.size();
4377 
4378     // do we have an outline at all ?
4379     if( nItems < 2 )
4380         return 0;
4381 
4382     // reserve object numbers for all outline items
4383     for( i = 0; i < nItems; ++i )
4384         m_aOutline[i].m_nObject = createObject();
4385 
4386     // update all parent, next and prev object ids
4387     for( i = 0; i < nItems; ++i )
4388     {
4389         PDFOutlineEntry& rItem = m_aOutline[i];
4390         int nChildren = rItem.m_aChildren.size();
4391 
4392         if( nChildren )
4393         {
4394             for( int n = 0; n < nChildren; ++n )
4395             {
4396                 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
4397 
4398                 rChild.m_nParentObject = rItem.m_nObject;
4399                 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
4400                 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
4401             }
4402 
4403         }
4404     }
4405 
4406     // calculate Count entries for all items
4407     std::vector< sal_Int32 > aCounts( nItems );
4408     updateOutlineItemCount( aCounts, 0, 0 );
4409 
4410     // emit hierarchy
4411     for( i = 0; i < nItems; ++i )
4412     {
4413         PDFOutlineEntry& rItem = m_aOutline[i];
4414         OStringBuffer aLine( 1024 );
4415 
4416         CHECK_RETURN( updateObject( rItem.m_nObject ) );
4417         aLine.append( rItem.m_nObject );
4418         aLine.append( " 0 obj\n" );
4419         aLine.append( "<<" );
4420         // number of visible children (all levels)
4421         if( i > 0 || aCounts[0] > 0 )
4422         {
4423             aLine.append( "/Count " );
4424             aLine.append( aCounts[i] );
4425         }
4426         if( ! rItem.m_aChildren.empty() )
4427         {
4428             // children list: First, Last
4429             aLine.append( "/First " );
4430             aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
4431             aLine.append( " 0 R/Last " );
4432             aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
4433             aLine.append( " 0 R\n" );
4434         }
4435         if( i > 0 )
4436         {
4437             // Title, Dest, Parent, Prev, Next
4438             aLine.append( "/Title" );
4439             appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
4440             aLine.append( "\n" );
4441             // Dest is not required
4442             if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() )
4443             {
4444                 aLine.append( "/Dest" );
4445                 appendDest( rItem.m_nDestID, aLine );
4446             }
4447             aLine.append( "/Parent " );
4448             aLine.append( rItem.m_nParentObject );
4449             aLine.append( " 0 R" );
4450             if( rItem.m_nPrevObject )
4451             {
4452                 aLine.append( "/Prev " );
4453                 aLine.append( rItem.m_nPrevObject );
4454                 aLine.append( " 0 R" );
4455             }
4456             if( rItem.m_nNextObject )
4457             {
4458                 aLine.append( "/Next " );
4459                 aLine.append( rItem.m_nNextObject );
4460                 aLine.append( " 0 R" );
4461             }
4462         }
4463         aLine.append( ">>\nendobj\n\n" );
4464         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4465     }
4466 
4467     return m_aOutline[0].m_nObject;
4468 }
4469 
4470 #undef CHECK_RETURN
4471 #define CHECK_RETURN( x ) if( !x ) return false
4472 
4473 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
4474 {
4475     if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() )
4476     {
4477 #if OSL_DEBUG_LEVEL > 1
4478         fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID );
4479 #endif
4480         return false;
4481     }
4482 
4483 
4484     const PDFDest& rDest		= m_aDests[ nDestID ];
4485     const PDFPage& rDestPage	= m_aPages[ rDest.m_nPage ];
4486 
4487     rBuffer.append( '[' );
4488     rBuffer.append( rDestPage.m_nPageObject );
4489     rBuffer.append( " 0 R" );
4490 
4491     switch( rDest.m_eType )
4492     {
4493         case PDFWriter::XYZ:
4494         default:
4495             rBuffer.append( "/XYZ " );
4496             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4497             rBuffer.append( ' ' );
4498             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4499             rBuffer.append( " 0" );
4500             break;
4501         case PDFWriter::Fit:
4502             rBuffer.append( "/Fit" );
4503             break;
4504         case PDFWriter::FitRectangle:
4505             rBuffer.append( "/FitR " );
4506             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4507             rBuffer.append( ' ' );
4508             appendFixedInt( rDest.m_aRect.Top(), rBuffer );
4509             rBuffer.append( ' ' );
4510             appendFixedInt( rDest.m_aRect.Right(), rBuffer );
4511             rBuffer.append( ' ' );
4512             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4513             break;
4514         case PDFWriter::FitHorizontal:
4515             rBuffer.append( "/FitH " );
4516             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4517             break;
4518         case PDFWriter::FitVertical:
4519             rBuffer.append( "/FitV " );
4520             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4521             break;
4522         case PDFWriter::FitPageBoundingBox:
4523             rBuffer.append( "/FitB" );
4524             break;
4525         case PDFWriter::FitPageBoundingBoxHorizontal:
4526             rBuffer.append( "/FitBH " );
4527             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4528             break;
4529         case PDFWriter::FitPageBoundingBoxVertical:
4530             rBuffer.append( "/FitBV " );
4531             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4532             break;
4533     }
4534     rBuffer.append( ']' );
4535 
4536     return true;
4537 }
4538 
4539 bool PDFWriterImpl::emitLinkAnnotations()
4540 {
4541     int nAnnots = m_aLinks.size();
4542     for( int i = 0; i < nAnnots; i++ )
4543     {
4544         const PDFLink& rLink			= m_aLinks[i];
4545         if( ! updateObject( rLink.m_nObject ) )
4546             continue;
4547 
4548         OStringBuffer aLine( 1024 );
4549         aLine.append( rLink.m_nObject );
4550         aLine.append( " 0 obj\n" );
4551 //i59651  key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4552 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4553         aLine.append( "<</Type/Annot" );
4554         if( m_bIsPDF_A1 )
4555             aLine.append( "/F 4" );
4556         aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
4557 
4558         appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
4559         aLine.append( ' ' );
4560         appendFixedInt( rLink.m_aRect.Top(), aLine );
4561         aLine.append( ' ' );
4562         appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
4563         aLine.append( ' ' );
4564         appendFixedInt( rLink.m_aRect.Bottom(), aLine );
4565         aLine.append( "]" );
4566         if( rLink.m_nDest >= 0 )
4567         {
4568             aLine.append( "/Dest" );
4569             appendDest( rLink.m_nDest, aLine );
4570         }
4571         else
4572         {
4573 /*--->i56629
4574 destination is external to the document, so
4575 we check in the following sequence:
4576 
4577  if target type is neither .pdf, nor .od[tpgs], then
4578           check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
4579                              end processing
4580  else if target is .od[tpgs]: then
4581       if conversion of type from od[tpgs]  to pdf is requested, convert it and this becomes the new target file
4582       processing continue
4583 
4584  if (new)target is .pdf : then
4585      if GotToR is requested, then
4586            convert the target in GoToR where the fragment of the URI is
4587            considered the named destination in the target file, set relative or absolute as requested
4588      else strip the fragment from URL and then set URI or 'launch application' as requested
4589 */
4590 //
4591 // FIXME: check if the decode mechanisms for URL processing throughout this implementation
4592 // are the correct one!!
4593 //
4594 // extract target file type
4595             INetURLObject aDocumentURL( m_aContext.BaseURL );
4596             INetURLObject aTargetURL( rLink.m_aURL );
4597             sal_Int32   nChangeFileExtensionToPDF = 0;
4598             sal_Int32   nSetGoToRMode = 0;
4599             sal_Bool    bTargetHasPDFExtension = sal_False;
4600             INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
4601 			sal_Bool    bIsUNCPath = sal_False;
4602 // check if the protocol is a known one, or if there is no protocol at all (on target only)
4603 // if there is no protocol, make the target relative to the current document directory
4604 // getting the needed URL information from the current document path
4605             if( eTargetProtocol == INET_PROT_NOT_VALID )
4606             {
4607 				if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.compareToAscii( "\\\\\\\\", 4 ) == 0)
4608 				{
4609 					bIsUNCPath = sal_True;
4610 				}
4611 				else
4612 				{
4613 					INetURLObject aNewBase( aDocumentURL );//duplicate document URL
4614 					aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
4615 											  //target document
4616 					aNewBase.insertName( rLink.m_aURL );
4617 					aTargetURL = aNewBase;//reassign the new target URL
4618 //recompute the target protocol, with the new URL
4619 //normal URL processing resumes
4620 					eTargetProtocol = aTargetURL.GetProtocol();
4621 				}
4622             }
4623 
4624             rtl::OUString aFileExtension = aTargetURL.GetFileExtension();
4625 
4626 // Check if the URL ends in '/': if yes it's a directory,
4627 // it will be forced to a URI link.
4628 // possibly a malformed URI, leave it as it is, force as URI
4629             if( aTargetURL.hasFinalSlash() )
4630                 m_aContext.DefaultLinkAction = PDFWriter::URIAction;
4631 
4632             if( aFileExtension.getLength() > 0 )
4633             {
4634                 if( m_aContext.ConvertOOoTargetToPDFTarget )
4635                 {
4636 //examine the file type (.odm .odt. .odp, odg, ods)
4637                     if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odm" ) ) ) )
4638                         nChangeFileExtensionToPDF++;
4639                     if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odt" ) ) ) )
4640                         nChangeFileExtensionToPDF++;
4641                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odp" ) ) ) )
4642                         nChangeFileExtensionToPDF++;
4643                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odg" ) ) ) )
4644                         nChangeFileExtensionToPDF++;
4645                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ods" ) ) ) )
4646                         nChangeFileExtensionToPDF++;
4647                     if( nChangeFileExtensionToPDF )
4648                         aTargetURL.setExtension(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4649                 }
4650 //check if extension is pdf, see if GoToR should be forced
4651                 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4652                 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
4653                     nSetGoToRMode++;
4654             }
4655 //prepare the URL, if relative or not
4656             INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
4657 //queue the string common to all types of actions
4658             aLine.append( "/A<</Type/Action/S");
4659 			if( bIsUNCPath ) // handle Win UNC paths
4660 			{
4661 				aLine.append( "/Launch/Win<</F" );
4662 				// INetURLObject is not good with UNC paths, use original path
4663 				appendLiteralStringEncrypt(  rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4664 				aLine.append( ">>" );
4665 			}
4666 			else
4667 			{
4668 			    bool bSetRelative = false;
4669 			    bool bFileSpec = false;
4670 //check if relative file link is requested and if the protocol is 'file://'
4671 				if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE )
4672 					bSetRelative = true;
4673 
4674 				rtl::OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is,
4675 				if( nSetGoToRMode == 0 )
4676 				{
4677 					switch( m_aContext.DefaultLinkAction )
4678 					{
4679 					default:
4680 					case PDFWriter::URIAction :
4681 					case PDFWriter::URIActionDestination :
4682 						aLine.append( "/URI/URI" );
4683 						break;
4684 					case PDFWriter::LaunchAction:
4685 // now:
4686 // if a launch action is requested and the hyperlink target has a fragment
4687 // and the target file does not have a pdf extension, or it's not a 'file:://' protocol
4688 // then force the uri action on it
4689 // This code will permit the correct opening of application on web pages, the one that
4690 // normally have fragments (but I may be wrong...)
4691 // and will force the use of URI when the protocol is not file://
4692 						if( (aFragment.getLength() > 0 && !bTargetHasPDFExtension) ||
4693 										eTargetProtocol != INET_PROT_FILE )
4694 							aLine.append( "/URI/URI" );
4695 						else
4696 						{
4697 							aLine.append( "/Launch/F" );
4698 							bFileSpec = true;
4699 						}
4700 						break;
4701 					}
4702 				}
4703 //fragment are encoded in the same way as in the named destination processing
4704 				if( nSetGoToRMode )
4705 				{//add the fragment
4706 				    rtl::OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET );
4707 					aLine.append("/GoToR");
4708 					aLine.append("/F");
4709 					bFileSpec = true;
4710 					appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark,
4711 																						 INetURLObject::WAS_ENCODED,
4712 																						 INetURLObject::DECODE_WITH_CHARSET ) :
4713 																   aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4714 					if( aFragment.getLength() > 0 )
4715 					{
4716 						aLine.append("/D/");
4717 						appendDestinationName( aFragment , aLine );
4718 					}
4719 				}
4720 				else
4721 				{
4722 // change the fragment to accomodate the bookmark (only if the file extension is PDF and
4723 // the requested action is of the correct type)
4724 					if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination &&
4725 							   bTargetHasPDFExtension && aFragment.getLength() > 0 )
4726 					{
4727 						OStringBuffer aLineLoc( 1024 );
4728 						appendDestinationName( aFragment , aLineLoc );
4729 //substitute the fragment
4730 						aTargetURL.SetMark( aLineLoc.getStr() );
4731 					}
4732 					rtl::OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE );
4733 // check if we have a URL available, if the string is empty, set it as the original one
4734 //                 if( aURL.getLength() == 0 )
4735 //                     appendLiteralStringEncrypt( rLink.m_aURL , rLink.m_nObject, aLine );
4736 //                 else
4737 						appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL,
4738 						                                                                    INetURLObject::WAS_ENCODED,
4739 						                                                                    bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE
4740 						                                                                    ) :
4741 																   aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4742 				}
4743 //<--- i56629
4744 			}
4745             aLine.append( ">>\n" );
4746         }
4747         if( rLink.m_nStructParent > 0 )
4748         {
4749             aLine.append( "/StructParent " );
4750             aLine.append( rLink.m_nStructParent );
4751         }
4752         aLine.append( ">>\nendobj\n\n" );
4753         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4754     }
4755 
4756     return true;
4757 }
4758 
4759 bool PDFWriterImpl::emitNoteAnnotations()
4760 {
4761     // emit note annotations
4762     int nAnnots = m_aNotes.size();
4763     for( int i = 0; i < nAnnots; i++ )
4764     {
4765         const PDFNoteEntry& rNote		= m_aNotes[i];
4766         if( ! updateObject( rNote.m_nObject ) )
4767             return false;
4768 
4769         OStringBuffer aLine( 1024 );
4770         aLine.append( rNote.m_nObject );
4771         aLine.append( " 0 obj\n" );
4772 //i59651  key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4773 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4774         aLine.append( "<</Type/Annot" );
4775         if( m_bIsPDF_A1 )
4776             aLine.append( "/F 4" );
4777         aLine.append( "/Subtype/Text/Rect[" );
4778 
4779         appendFixedInt( rNote.m_aRect.Left(), aLine );
4780         aLine.append( ' ' );
4781         appendFixedInt( rNote.m_aRect.Top(), aLine );
4782         aLine.append( ' ' );
4783         appendFixedInt( rNote.m_aRect.Right(), aLine );
4784         aLine.append( ' ' );
4785         appendFixedInt( rNote.m_aRect.Bottom(), aLine );
4786         aLine.append( "]" );
4787 
4788         // contents of the note (type text string)
4789         aLine.append( "/Contents\n" );
4790         appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
4791         aLine.append( "\n" );
4792 
4793         // optional title
4794         if( rNote.m_aContents.Title.Len() )
4795         {
4796             aLine.append( "/T" );
4797             appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
4798             aLine.append( "\n" );
4799         }
4800 
4801         aLine.append( ">>\nendobj\n\n" );
4802         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4803     }
4804     return true;
4805 }
4806 
4807 Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font&  rAppSetFont )
4808 {
4809     bool bAdjustSize = false;
4810 
4811     Font aFont( rControlFont );
4812     if( ! aFont.GetName().Len() )
4813     {
4814         aFont = rAppSetFont;
4815         if( rControlFont.GetHeight() )
4816             aFont.SetSize( Size( 0, rControlFont.GetHeight() ) );
4817         else
4818             bAdjustSize = true;
4819         if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
4820             aFont.SetItalic( rControlFont.GetItalic() );
4821         if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
4822             aFont.SetWeight( rControlFont.GetWeight() );
4823     }
4824     else if( ! aFont.GetHeight() )
4825     {
4826         aFont.SetSize( rAppSetFont.GetSize() );
4827         bAdjustSize = true;
4828     }
4829     if( bAdjustSize )
4830     {
4831         Size aFontSize = aFont.GetSize();
4832         OutputDevice* pDefDev = Application::GetDefaultDevice();
4833         aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
4834         aFont.SetSize( aFontSize );
4835     }
4836     return aFont;
4837 }
4838 
4839 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont )
4840 {
4841     sal_Int32 nBest = 4; // default to Helvetica
4842     OUString aFontName( rFont.GetName() );
4843     aFontName = aFontName.toAsciiLowerCase();
4844 
4845     if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "times" ) ) ) != -1 )
4846         nBest = 8;
4847     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "courier" ) ) ) != -1 )
4848         nBest = 0;
4849     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "dingbats" ) ) ) != -1 )
4850         nBest = 13;
4851     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "symbol" ) ) ) != -1 )
4852         nBest = 12;
4853     if( nBest < 12 )
4854     {
4855         if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
4856             nBest += 1;
4857         if( rFont.GetWeight() > WEIGHT_MEDIUM )
4858             nBest += 2;
4859     }
4860 
4861     if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() )
4862         m_aBuiltinFontToObjectMap[ nBest ] = createObject();
4863 
4864     return nBest;
4865 }
4866 
4867 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
4868 {
4869     return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1;
4870 }
4871 
4872 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget )
4873 {
4874     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4875 
4876     // save graphics state
4877     push( sal::static_int_cast<sal_uInt16>(~0U) );
4878 
4879     // transform relative to control's coordinates since an
4880     // appearance stream is a form XObject
4881     // this relies on the m_aRect member of rButton NOT already being transformed
4882     // to default user space
4883     if( rWidget.Background || rWidget.Border )
4884     {
4885         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) );
4886         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) );
4887         drawRectangle( rWidget.Location );
4888     }
4889     // prepare font to use
4890     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
4891     setFont( aFont );
4892     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
4893 
4894     drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
4895 
4896     // create DA string while local mapmode is still in place
4897     // (that is before endRedirect())
4898     OStringBuffer aDA( 256 );
4899     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
4900     Font aDummyFont( String( RTL_CONSTASCII_USTRINGPARAM( "Helvetica" ) ), aFont.GetSize() );
4901     sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont );
4902     aDA.append( ' ' );
4903     aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() );
4904     aDA.append( ' ' );
4905     m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4906     aDA.append( " Tf" );
4907     rButton.m_aDAString = aDA.makeStringAndClear();
4908 
4909     pop();
4910 
4911     rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
4912 
4913     /* seems like a bad hack but at least works in both AR5 and 6:
4914        we draw the button ourselves and tell AR
4915        the button would be totally transparent with no text
4916 
4917        One would expect that simply setting a normal appearance
4918        should suffice, but no, as soon as the user actually presses
4919        the button and an action is tied to it (gasp! a button that
4920        does something) the appearance gets replaced by some crap that AR
4921        creates on the fly even if no DA or MK is given. On AR6 at least
4922        the DA and MK work as expected, but on AR5 this creates a region
4923        filled with the background color but nor text. Urgh.
4924     */
4925     rButton.m_aMKDict = "/BC [] /BG [] /CA";
4926     rButton.m_aMKDictCAString = "";
4927 }
4928 
4929 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern,
4930                                      const PDFWriter::AnyWidget& rWidget,
4931                                      const StyleSettings& rSettings )
4932 {
4933     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
4934 
4935     if( rWidget.Background || rWidget.Border )
4936     {
4937         if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) )
4938         {
4939             sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500;
4940             if( nDelta < 1 )
4941                 nDelta = 1;
4942             setLineColor( Color( COL_TRANSPARENT ) );
4943             Rectangle aRect = rIntern.m_aRect;
4944             setFillColor( rSettings.GetLightBorderColor() );
4945             drawRectangle( aRect );
4946             aRect.Left()  += nDelta; aRect.Top()     += nDelta;
4947             aRect.Right() -= nDelta; aRect.Bottom()  -= nDelta;
4948             setFillColor( rSettings.GetFieldColor() );
4949             drawRectangle( aRect );
4950             setFillColor( rSettings.GetLightColor() );
4951             drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
4952             drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
4953             setFillColor( rSettings.GetDarkShadowColor() );
4954             drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
4955             drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
4956         }
4957         else
4958         {
4959             setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) );
4960             setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4961             drawRectangle( rIntern.m_aRect );
4962         }
4963 
4964         if( rWidget.Border )
4965         {
4966             // adjust edit area accounting for border
4967             sal_Int32 nDelta = aFont.GetHeight()/4;
4968             if( nDelta < 1 )
4969                 nDelta = 1;
4970             rIntern.m_aRect.Left()	+= nDelta;
4971             rIntern.m_aRect.Top()	+= nDelta;
4972             rIntern.m_aRect.Right()	-= nDelta;
4973             rIntern.m_aRect.Bottom()-= nDelta;
4974         }
4975     }
4976     return aFont;
4977 }
4978 
4979 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget )
4980 {
4981     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4982     SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
4983 
4984     push( sal::static_int_cast<sal_uInt16>(~0U) );
4985 
4986     // prepare font to use, draw field border
4987     Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
4988     sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
4989 
4990     // prepare DA string
4991     OStringBuffer aDA( 32 );
4992     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4993     aDA.append( ' ' );
4994     if( m_aContext.FieldsUseSystemFonts )
4995     {
4996         aDA.append( "/F" );
4997         aDA.append( nBest );
4998 
4999         OStringBuffer aDR( 32 );
5000         aDR.append( "/Font " );
5001         aDR.append( getFontDictObject() );
5002         aDR.append( " 0 R" );
5003         rEdit.m_aDRDict = aDR.makeStringAndClear();
5004     }
5005     else
5006         aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5007     aDA.append( ' ' );
5008     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
5009     aDA.append( " Tf" );
5010 
5011     /*  create an empty appearance stream, let the viewer create
5012         the appearance at runtime. This is because AR5 seems to
5013         paint the widget appearance always, and a dynamically created
5014         appearance on top of it. AR6 is well behaved in that regard, so
5015         that behaviour seems to be a bug. Anyway this empty appearance
5016         relies on /NeedAppearances in the AcroForm dictionary set to "true"
5017      */
5018     beginRedirect( pEditStream, rEdit.m_aRect );
5019     OStringBuffer aAppearance( 32 );
5020     aAppearance.append( "/Tx BMC\nEMC\n" );
5021     writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5022 
5023     endRedirect();
5024     pop();
5025 
5026     rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
5027 
5028     rEdit.m_aDAString = aDA.makeStringAndClear();
5029 }
5030 
5031 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget )
5032 {
5033     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5034     SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
5035 
5036     push( sal::static_int_cast<sal_uInt16>(~0U) );
5037 
5038     // prepare font to use, draw field border
5039     Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
5040     sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
5041 
5042     beginRedirect( pListBoxStream, rBox.m_aRect );
5043     OStringBuffer aAppearance( 64 );
5044 
5045 #if 0
5046     if( ! rWidget.DropDown )
5047     {
5048         // prepare linewidth for DA string hack, see below
5049         Size aFontSize = lcl_convert( m_aGraphicsStack.front().m_aMapMode,
5050                                       m_aMapMode,
5051                                       getReferenceDevice(),
5052                                       Size( 0, aFont.GetHeight() ) );
5053         sal_Int32 nLW = aFontSize.Height() / 40;
5054         appendFixedInt( nLW > 0 ? nLW : 1, aAppearance );
5055         aAppearance.append( " w\n" );
5056         writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5057         aAppearance.setLength( 0 );
5058     }
5059 #endif
5060 
5061     setLineColor( Color( COL_TRANSPARENT ) );
5062     setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
5063     drawRectangle( rBox.m_aRect );
5064 
5065     // empty appearance, see createDefaultEditAppearance for reference
5066     aAppearance.append( "/Tx BMC\nEMC\n" );
5067     writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5068 
5069     endRedirect();
5070     pop();
5071 
5072     rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
5073 
5074     // prepare DA string
5075     OStringBuffer aDA( 256 );
5076 #if 0
5077     if( !rWidget.DropDown )
5078     {
5079         /* another of AR5's peculiarities: the selected item of a choice
5080            field is highlighted using the non stroking color - same as the
5081            text color. so workaround that by using text rendering mode 2
5082            (fill, then stroke) and set the stroking color
5083          */
5084         appendStrokingColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ), aDA );
5085         aDA.append( " 2 Tr " );
5086     }
5087 #endif
5088     // prepare DA string
5089     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
5090     aDA.append( ' ' );
5091     if( m_aContext.FieldsUseSystemFonts )
5092     {
5093         aDA.append( "/F" );
5094         aDA.append( nBest );
5095 
5096         OStringBuffer aDR( 32 );
5097         aDR.append( "/Font " );
5098         aDR.append( getFontDictObject() );
5099         aDR.append( " 0 R" );
5100         rBox.m_aDRDict = aDR.makeStringAndClear();
5101     }
5102     else
5103         aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5104     aDA.append( ' ' );
5105     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
5106     aDA.append( " Tf" );
5107     rBox.m_aDAString = aDA.makeStringAndClear();
5108 }
5109 
5110 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget )
5111 {
5112     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5113 
5114     // save graphics state
5115     push( sal::static_int_cast<sal_uInt16>(~0U) );
5116 
5117     if( rWidget.Background || rWidget.Border )
5118     {
5119         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5120         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5121         drawRectangle( rBox.m_aRect );
5122     }
5123 
5124     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5125     setFont( aFont );
5126     Size aFontSize = aFont.GetSize();
5127     if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5128         aFontSize.Height() = rBox.m_aRect.GetHeight();
5129     sal_Int32 nDelta = aFontSize.Height()/10;
5130     if( nDelta < 1 )
5131         nDelta = 1;
5132 
5133     Rectangle aCheckRect, aTextRect;
5134     if( rWidget.ButtonIsLeft )
5135     {
5136         aCheckRect.Left()	= rBox.m_aRect.Left() + nDelta;
5137         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5138         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5139         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5140 
5141         // #i74206# handle small controls without text area
5142         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5143         {
5144             aCheckRect.Right()  -= nDelta;
5145             aCheckRect.Top()    += nDelta/2;
5146             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5147         }
5148 
5149         aTextRect.Left()	= rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5150         aTextRect.Top()		= rBox.m_aRect.Top();
5151         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5152         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5153     }
5154     else
5155     {
5156         aCheckRect.Left()	= rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5157         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5158         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5159         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5160 
5161         // #i74206# handle small controls without text area
5162         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5163         {
5164             aCheckRect.Left()   += nDelta;
5165             aCheckRect.Top()    += nDelta/2;
5166             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5167         }
5168 
5169         aTextRect.Left()	= rBox.m_aRect.Left();
5170         aTextRect.Top()		= rBox.m_aRect.Top();
5171         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5172         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5173     }
5174     setLineColor( Color( COL_BLACK ) );
5175     setFillColor( Color( COL_TRANSPARENT ) );
5176     OStringBuffer aLW( 32 );
5177     aLW.append( "q " );
5178     m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
5179     aLW.append( " w " );
5180     writeBuffer( aLW.getStr(), aLW.getLength() );
5181     drawRectangle( aCheckRect );
5182     writeBuffer( " Q\n", 3 );
5183     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5184     drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5185 
5186     pop();
5187 
5188     OStringBuffer aDA( 256 );
5189     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5190     sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
5191     aDA.append( ' ' );
5192     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5193     aDA.append( " 0 Tf" );
5194     rBox.m_aDAString = aDA.makeStringAndClear();
5195     rBox.m_aMKDict = "/CA";
5196     rBox.m_aMKDictCAString = "8";
5197     rBox.m_aRect = aCheckRect;
5198 
5199     // create appearance streams
5200     sal_Char cMark = '8';
5201     sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)];
5202     nCharXOffset *= aCheckRect.GetHeight();
5203     nCharXOffset /= 2000;
5204     sal_Int32 nCharYOffset = 1000-
5205         (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative
5206     nCharYOffset *= aCheckRect.GetHeight();
5207     nCharYOffset /= 2000;
5208 
5209     SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5210     beginRedirect( pCheckStream, aCheckRect );
5211     aDA.append( "/Tx BMC\nq BT\n" );
5212     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5213     aDA.append( ' ' );
5214     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5215     aDA.append( ' ' );
5216     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5217     aDA.append( " Tf\n" );
5218     m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
5219 	aDA.append( " " );
5220 	m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
5221 	aDA.append( " Td (" );
5222 	aDA.append( cMark );
5223 	aDA.append( ") Tj\nET\nQ\nEMC\n" );
5224     writeBuffer( aDA.getStr(), aDA.getLength() );
5225     endRedirect();
5226     rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5227 
5228     SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5229     beginRedirect( pUncheckStream, aCheckRect );
5230     writeBuffer( "/Tx BMC\nEMC\n", 12 );
5231     endRedirect();
5232     rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5233 }
5234 
5235 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget )
5236 {
5237     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5238 
5239     // save graphics state
5240     push( sal::static_int_cast<sal_uInt16>(~0U) );
5241 
5242     if( rWidget.Background || rWidget.Border )
5243     {
5244         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5245         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5246         drawRectangle( rBox.m_aRect );
5247     }
5248 
5249     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5250     setFont( aFont );
5251     Size aFontSize = aFont.GetSize();
5252     if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5253         aFontSize.Height() = rBox.m_aRect.GetHeight();
5254     sal_Int32 nDelta = aFontSize.Height()/10;
5255     if( nDelta < 1 )
5256         nDelta = 1;
5257 
5258     Rectangle aCheckRect, aTextRect;
5259     if( rWidget.ButtonIsLeft )
5260     {
5261         aCheckRect.Left()	= rBox.m_aRect.Left() + nDelta;
5262         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5263         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5264         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5265 
5266         // #i74206# handle small controls without text area
5267         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5268         {
5269             aCheckRect.Right()  -= nDelta;
5270             aCheckRect.Top()    += nDelta/2;
5271             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5272         }
5273 
5274         aTextRect.Left()	= rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5275         aTextRect.Top()		= rBox.m_aRect.Top();
5276         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5277         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5278     }
5279     else
5280     {
5281         aCheckRect.Left()	= rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5282         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5283         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5284         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5285 
5286         // #i74206# handle small controls without text area
5287         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5288         {
5289             aCheckRect.Left()   += nDelta;
5290             aCheckRect.Top()    += nDelta/2;
5291             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5292         }
5293 
5294         aTextRect.Left()	= rBox.m_aRect.Left();
5295         aTextRect.Top()		= rBox.m_aRect.Top();
5296         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5297         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5298     }
5299     setLineColor( Color( COL_BLACK ) );
5300     setFillColor( Color( COL_TRANSPARENT ) );
5301     OStringBuffer aLW( 32 );
5302     aLW.append( "q " );
5303     m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
5304     aLW.append( " w " );
5305     writeBuffer( aLW.getStr(), aLW.getLength() );
5306     drawEllipse( aCheckRect );
5307     writeBuffer( " Q\n", 3 );
5308     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5309     drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5310 
5311     pop();
5312 
5313     OStringBuffer aDA( 256 );
5314     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5315     sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
5316     aDA.append( ' ' );
5317     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5318     aDA.append( " 0 Tf" );
5319     rBox.m_aDAString = aDA.makeStringAndClear();
5320 //to encrypt this (el)
5321     rBox.m_aMKDict = "/CA";
5322 //after this assignement, to m_aMKDic cannot be added anything
5323     rBox.m_aMKDictCAString = "l";
5324 
5325     rBox.m_aRect = aCheckRect;
5326 
5327     // create appearance streams
5328     push( sal::static_int_cast<sal_uInt16>(~0U) );
5329     SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5330 
5331     beginRedirect( pCheckStream, aCheckRect );
5332     aDA.append( "/Tx BMC\nq BT\n" );
5333     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5334     aDA.append( ' ' );
5335     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5336     aDA.append( ' ' );
5337     m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5338     aDA.append( " Tf\n0 0 Td\nET\nQ\n" );
5339     writeBuffer( aDA.getStr(), aDA.getLength() );
5340     setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5341     setLineColor( Color( COL_TRANSPARENT ) );
5342     aCheckRect.Left()	+= 3*nDelta;
5343     aCheckRect.Top()	+= 3*nDelta;
5344     aCheckRect.Bottom()	-= 3*nDelta;
5345     aCheckRect.Right()	-= 3*nDelta;
5346     drawEllipse( aCheckRect );
5347     writeBuffer( "\nEMC\n", 5 );
5348     endRedirect();
5349 
5350     pop();
5351     rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5352 
5353     SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5354     beginRedirect( pUncheckStream, aCheckRect );
5355     writeBuffer( "/Tx BMC\nEMC\n", 12 );
5356     endRedirect();
5357     rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5358 }
5359 
5360 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
5361 {
5362 
5363     // TODO: check and insert default streams
5364     rtl::OString aStandardAppearance;
5365     switch( rWidget.m_eType )
5366     {
5367         case PDFWriter::CheckBox:
5368             aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
5369             break;
5370         default:
5371             break;
5372     }
5373 
5374     if( rWidget.m_aAppearances.size() )
5375     {
5376         rAnnotDict.append( "/AP<<\n" );
5377         for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it )
5378         {
5379             rAnnotDict.append( "/" );
5380             rAnnotDict.append( dict_it->first );
5381             bool bUseSubDict = (dict_it->second.size() > 1);
5382             rAnnotDict.append( bUseSubDict ? "<<" : " " );
5383 
5384             for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin();
5385                  stream_it != dict_it->second.end(); ++stream_it )
5386             {
5387                 SvMemoryStream* pApppearanceStream = stream_it->second;
5388                 dict_it->second[ stream_it->first ] = NULL;
5389 
5390                 bool bDeflate = compressStream( pApppearanceStream );
5391 
5392                 pApppearanceStream->Seek( STREAM_SEEK_TO_END );
5393                 sal_Int64 nStreamLen = pApppearanceStream->Tell();
5394                 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
5395                 sal_Int32 nObject = createObject();
5396                 CHECK_RETURN( updateObject( nObject ) );
5397                 #if OSL_DEBUG_LEVEL > 1
5398                 emitComment( "PDFWriterImpl::emitAppearances" );
5399                 #endif
5400                 OStringBuffer aLine;
5401                 aLine.append( nObject );
5402 
5403                 aLine.append( " 0 obj\n"
5404                               "<</Type/XObject\n"
5405                               "/Subtype/Form\n"
5406                               "/BBox[0 0 " );
5407                 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
5408                 aLine.append( " " );
5409                 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
5410                 aLine.append( "]\n"
5411                               "/Resources " );
5412                 aLine.append( getResourceDictObj() );
5413                 aLine.append( " 0 R\n"
5414                               "/Length " );
5415                 aLine.append( nStreamLen );
5416                 aLine.append( "\n" );
5417                 if( bDeflate )
5418                     aLine.append( "/Filter/FlateDecode\n" );
5419                 aLine.append( ">>\nstream\n" );
5420                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5421                 checkAndEnableStreamEncryption( nObject );
5422                 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
5423                 disableStreamEncryption();
5424                 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
5425 
5426                 if( bUseSubDict )
5427                 {
5428                     rAnnotDict.append( " /" );
5429                     rAnnotDict.append( stream_it->first );
5430                     rAnnotDict.append( " " );
5431                 }
5432                 rAnnotDict.append( nObject );
5433                 rAnnotDict.append( " 0 R" );
5434 
5435                 delete pApppearanceStream;
5436             }
5437 
5438             rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
5439         }
5440         rAnnotDict.append( ">>\n" );
5441         if( aStandardAppearance.getLength() )
5442         {
5443             rAnnotDict.append( "/AS /" );
5444             rAnnotDict.append( aStandardAppearance );
5445             rAnnotDict.append( "\n" );
5446         }
5447     }
5448 
5449     return true;
5450 }
5451 
5452 bool PDFWriterImpl::emitWidgetAnnotations()
5453 {
5454     ensureUniqueRadioOnValues();
5455 
5456     int nAnnots = m_aWidgets.size();
5457     for( int a = 0; a < nAnnots; a++ )
5458     {
5459         PDFWidget& rWidget = m_aWidgets[a];
5460 
5461         OStringBuffer aLine( 1024 );
5462         OStringBuffer aValue( 256 );
5463         aLine.append( rWidget.m_nObject );
5464         aLine.append( " 0 obj\n"
5465                       "<<" );
5466         if( rWidget.m_eType != PDFWriter::Hierarchy )
5467         {
5468             // emit widget annotation only for terminal fields
5469             if( rWidget.m_aKids.empty() )
5470             {
5471                 aLine.append( "/Type/Annot/Subtype/Widget/F 4\n"
5472                               "/Rect[" );
5473                 appendFixedInt( rWidget.m_aRect.Left()-1, aLine );
5474                 aLine.append( ' ' );
5475                 appendFixedInt( rWidget.m_aRect.Top()+1, aLine );
5476                 aLine.append( ' ' );
5477                 appendFixedInt( rWidget.m_aRect.Right()+1, aLine );
5478                 aLine.append( ' ' );
5479                 appendFixedInt( rWidget.m_aRect.Bottom()-1, aLine );
5480                 aLine.append( "]\n" );
5481             }
5482             aLine.append( "/FT/" );
5483             switch( rWidget.m_eType )
5484             {
5485                 case PDFWriter::RadioButton:
5486                 case PDFWriter::CheckBox:
5487                     // for radio buttons only the RadioButton field, not the
5488                     // CheckBox children should have a value, else acrobat reader
5489                     // does not always check the right button
5490                     // of course real check boxes (not belonging to a readio group)
5491                     // need their values, too
5492                     if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
5493                     {
5494                         aValue.append( "/" );
5495                         // check for radio group with all buttons unpressed
5496                         if( rWidget.m_aValue.getLength() == 0 )
5497                             aValue.append( "Off" );
5498                         else
5499                             appendName( rWidget.m_aValue, aValue );
5500                     }
5501                 case PDFWriter::PushButton:
5502                     aLine.append( "Btn" );
5503                     break;
5504                 case PDFWriter::ListBox:
5505                     if( rWidget.m_nFlags & 0x200000 ) // multiselect
5506                     {
5507                         aValue.append( "[" );
5508                         for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
5509                         {
5510                             sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
5511                             if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
5512                                 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
5513                         }
5514                         aValue.append( "]" );
5515                     }
5516                     else if( rWidget.m_aSelectedEntries.size() > 0 &&
5517                              rWidget.m_aSelectedEntries[0] >= 0 &&
5518                              rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
5519                     {
5520                         appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
5521                     }
5522                     else
5523                         appendUnicodeTextStringEncrypt( rtl::OUString(), rWidget.m_nObject, aValue );
5524                     aLine.append( "Ch" );
5525                     break;
5526                 case PDFWriter::ComboBox:
5527                     appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5528                     aLine.append( "Ch" );
5529                     break;
5530                 case PDFWriter::Edit:
5531                     aLine.append( "Tx" );
5532                     appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5533                     break;
5534                 case PDFWriter::Hierarchy: // make the compiler happy
5535                     break;
5536             }
5537             aLine.append( "\n" );
5538             aLine.append( "/P " );
5539             aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
5540             aLine.append( " 0 R\n" );
5541         }
5542         if( rWidget.m_nParent )
5543         {
5544             aLine.append( "/Parent " );
5545             aLine.append( rWidget.m_nParent );
5546             aLine.append( " 0 R\n" );
5547         }
5548         if( rWidget.m_aKids.size() )
5549         {
5550             aLine.append( "/Kids[" );
5551             for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ )
5552             {
5553                 aLine.append( rWidget.m_aKids[i] );
5554                 aLine.append( " 0 R" );
5555                 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5556             }
5557             aLine.append( "]\n" );
5558         }
5559         if( rWidget.m_aName.getLength() )
5560         {
5561             aLine.append( "/T" );
5562             appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
5563             aLine.append( "\n" );
5564         }
5565         if( m_aContext.Version > PDFWriter::PDF_1_2 && rWidget.m_aDescription.getLength() )
5566         {
5567             // the alternate field name should be unicode able since it is
5568             // supposed to be used in UI
5569             aLine.append( "/TU" );
5570             appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
5571             aLine.append( "\n" );
5572         }
5573 
5574         if( rWidget.m_nFlags )
5575         {
5576             aLine.append( "/Ff " );
5577             aLine.append( rWidget.m_nFlags );
5578             aLine.append( "\n" );
5579         }
5580         if( aValue.getLength() )
5581         {
5582             OString aVal = aValue.makeStringAndClear();
5583             aLine.append( "/V " );
5584             aLine.append( aVal );
5585             aLine.append( "\n"
5586                           "/DV " );
5587             aLine.append( aVal );
5588             aLine.append( "\n" );
5589         }
5590         if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
5591         {
5592             sal_Int32 nTI = -1;
5593             aLine.append( "/Opt[\n" );
5594             sal_Int32 i = 0;
5595             for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i )
5596             {
5597                 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine );
5598                 aLine.append( "\n" );
5599                 if( *it == rWidget.m_aValue )
5600                     nTI = i;
5601             }
5602             aLine.append( "]\n" );
5603             if( nTI > 0 )
5604             {
5605                 aLine.append( "/TI " );
5606                 aLine.append( nTI );
5607                 aLine.append( "\n" );
5608                 if( rWidget.m_nFlags & 0x200000 ) // Multiselect
5609                 {
5610                     aLine.append( "/I [" );
5611                     aLine.append( nTI );
5612                     aLine.append( "]\n" );
5613                 }
5614             }
5615         }
5616         if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
5617         {
5618             aLine.append( "/MaxLen " );
5619             aLine.append( rWidget.m_nMaxLen );
5620             aLine.append( "\n" );
5621         }
5622         if( rWidget.m_eType == PDFWriter::PushButton )
5623         {
5624             if(!m_bIsPDF_A1)
5625             {
5626                 OStringBuffer aDest;
5627                 if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) )
5628                 {
5629                     aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
5630                     aLine.append( aDest.makeStringAndClear() );
5631                     aLine.append( ">>>>\n" );
5632                 }
5633                 else if( rWidget.m_aListEntries.empty() )
5634                 {
5635                     // create a reset form action
5636                     aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
5637                 }
5638                 else if( rWidget.m_bSubmit )
5639                 {
5640                     // create a submit form action
5641                     aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
5642                     appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() );
5643                     aLine.append( "/Flags " );
5644 
5645                     sal_Int32 nFlags = 0;
5646                     switch( m_aContext.SubmitFormat )
5647                     {
5648                     case PDFWriter::HTML:
5649                         nFlags |= 4;
5650                         break;
5651                     case PDFWriter::XML:
5652                         if( m_aContext.Version > PDFWriter::PDF_1_3 )
5653                             nFlags |= 32;
5654                         break;
5655                     case PDFWriter::PDF:
5656                         if( m_aContext.Version > PDFWriter::PDF_1_3 )
5657                             nFlags |= 256;
5658                         break;
5659                     case PDFWriter::FDF:
5660                     default:
5661                         break;
5662                     }
5663                     if( rWidget.m_bSubmitGet )
5664                         nFlags |= 8;
5665                     aLine.append( nFlags );
5666                     aLine.append( ">>>>\n" );
5667                 }
5668                 else
5669                 {
5670                     // create a URI action
5671                     aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
5672                     aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
5673                     aLine.append( ")>>>>\n" );
5674                 }
5675             }
5676             else
5677                 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA );
5678         }
5679         if( rWidget.m_aDAString.getLength() )
5680         {
5681             if( rWidget.m_aDRDict.getLength() )
5682             {
5683                 aLine.append( "/DR<<" );
5684                 aLine.append( rWidget.m_aDRDict );
5685                 aLine.append( ">>\n" );
5686             }
5687             else
5688             {
5689                 aLine.append( "/DR<</Font<<" );
5690                 appendBuiltinFontsToDict( aLine );
5691                 aLine.append( ">>>>\n" );
5692             }
5693             aLine.append( "/DA" );
5694             appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
5695             aLine.append( "\n" );
5696             if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER )
5697                 aLine.append( "/Q 1\n" );
5698             else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT )
5699                 aLine.append( "/Q 2\n" );
5700         }
5701         // appearance charactristics for terminal fields
5702         // which are supposed to have an appearance constructed
5703         // by the viewer application
5704         if( rWidget.m_aMKDict.getLength() )
5705         {
5706             aLine.append( "/MK<<" );
5707             aLine.append( rWidget.m_aMKDict );
5708 //add the CA string, encrypting it
5709             appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
5710             aLine.append( ">>\n" );
5711         }
5712 
5713         CHECK_RETURN( emitAppearances( rWidget, aLine ) );
5714 
5715         aLine.append( ">>\n"
5716                       "endobj\n\n" );
5717         CHECK_RETURN( updateObject( rWidget.m_nObject ) );
5718         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5719     }
5720     return true;
5721 }
5722 
5723 bool PDFWriterImpl::emitAnnotations()
5724 {
5725     if( m_aPages.size() < 1 )
5726         return false;
5727 
5728     CHECK_RETURN( emitLinkAnnotations() );
5729 
5730     CHECK_RETURN( emitNoteAnnotations() );
5731 
5732     CHECK_RETURN( emitWidgetAnnotations() );
5733 
5734     return true;
5735 }
5736 
5737 #undef CHECK_RETURN
5738 #define CHECK_RETURN( x ) if( !x ) return false
5739 
5740 bool PDFWriterImpl::emitCatalog()
5741 {
5742     // build page tree
5743     // currently there is only one node that contains all leaves
5744 
5745     // first create a page tree node id
5746     sal_Int32 nTreeNode = createObject();
5747 
5748     // emit global resource dictionary (page emit needs it)
5749     CHECK_RETURN( emitResources() );
5750 
5751     // emit all pages
5752     for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it )
5753         if( ! it->emit( nTreeNode ) )
5754             return false;
5755 
5756     sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
5757 
5758     sal_Int32 nOutlineDict = emitOutline();
5759 
5760     //emit Output intent i59651
5761     sal_Int32 nOutputIntentObject = emitOutputIntent();
5762 
5763     //emit metadata
5764     sal_Int32 nMetadataObject = emitDocumentMetadata();
5765 
5766     sal_Int32 nStructureDict = 0;
5767     if(m_aStructure.size() > 1)
5768     {
5769 ///check if dummy structure containers are needed
5770         addInternalStructureContainer(m_aStructure[0]);
5771         nStructureDict = m_aStructure[0].m_nObject = createObject();
5772         emitStructure( m_aStructure[ 0 ] );
5773     }
5774 
5775     // adjust tree node file offset
5776     if( ! updateObject( nTreeNode ) )
5777         return false;
5778 
5779     // emit tree node
5780     OStringBuffer aLine( 2048 );
5781     aLine.append( nTreeNode );
5782     aLine.append( " 0 obj\n" );
5783     aLine.append( "<</Type/Pages\n" );
5784     aLine.append( "/Resources " );
5785     aLine.append( getResourceDictObj() );
5786     aLine.append( " 0 R\n" );
5787 
5788     switch( m_eInheritedOrientation )
5789     {
5790         case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
5791         case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
5792 
5793         case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant
5794         case PDFWriter::Portrait:
5795         default:
5796             break;
5797     }
5798     sal_Int32 nMediaBoxWidth = 0;
5799     sal_Int32 nMediaBoxHeight = 0;
5800     if( m_aPages.empty() ) // sanity check, this should not happen
5801     {
5802         nMediaBoxWidth = m_nInheritedPageWidth;
5803         nMediaBoxHeight = m_nInheritedPageHeight;
5804     }
5805     else
5806     {
5807         for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter )
5808         {
5809             if( iter->m_nPageWidth > nMediaBoxWidth )
5810                 nMediaBoxWidth = iter->m_nPageWidth;
5811             if( iter->m_nPageHeight > nMediaBoxHeight )
5812                 nMediaBoxHeight = iter->m_nPageHeight;
5813         }
5814     }
5815     aLine.append( "/MediaBox[ 0 0 " );
5816     aLine.append( nMediaBoxWidth );
5817     aLine.append( ' ' );
5818     aLine.append( nMediaBoxHeight );
5819     aLine.append( " ]\n"
5820                   "/Kids[ " );
5821     unsigned int i = 0;
5822     for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ )
5823     {
5824         aLine.append( iter->m_nPageObject );
5825         aLine.append( " 0 R" );
5826         aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5827     }
5828     aLine.append( "]\n"
5829                   "/Count " );
5830     aLine.append( (sal_Int32)m_aPages.size() );
5831     aLine.append( ">>\n"
5832                   "endobj\n\n" );
5833     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5834 
5835     // emit annotation objects
5836     CHECK_RETURN( emitAnnotations() );
5837 
5838     // emit Catalog
5839     m_nCatalogObject = createObject();
5840     if( ! updateObject( m_nCatalogObject ) )
5841         return false;
5842     aLine.setLength( 0 );
5843     aLine.append( m_nCatalogObject );
5844     aLine.append( " 0 obj\n"
5845                   "<</Type/Catalog/Pages " );
5846     aLine.append( nTreeNode );
5847     aLine.append( " 0 R\n" );
5848 //--->i56629
5849 //check if there are named destinations to emit (root must be inside the catalog)
5850     if( nNamedDestinationsDictionary )
5851     {
5852         aLine.append("/Dests ");
5853         aLine.append( nNamedDestinationsDictionary );
5854         aLine.append( " 0 R\n" );
5855     }
5856 //<----
5857     if( m_aContext.PageLayout != PDFWriter::DefaultLayout )
5858         switch(  m_aContext.PageLayout )
5859         {
5860         default :
5861         case  PDFWriter::SinglePage :
5862             aLine.append( "/PageLayout/SinglePage\n" );
5863             break;
5864         case  PDFWriter::Continuous :
5865             aLine.append( "/PageLayout/OneColumn\n" );
5866             break;
5867         case  PDFWriter::ContinuousFacing :
5868 //the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5869             aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5870             break;
5871         }
5872     if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode )
5873         switch(  m_aContext.PDFDocumentMode )
5874         {
5875         default :
5876             aLine.append( "/PageMode/UseNone\n" );
5877             break;
5878         case PDFWriter::UseOutlines :
5879             aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5880             break;
5881         case PDFWriter::UseThumbs :
5882             aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5883             break;
5884         }
5885     else if( m_aContext.OpenInFullScreenMode )
5886         aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5887 
5888     OStringBuffer aInitPageRef;
5889     if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() )
5890     {
5891         aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5892         aInitPageRef.append( " 0 R" );
5893     }
5894     else
5895         aInitPageRef.append( "0" );
5896     switch( m_aContext.PDFDocumentAction )
5897     {
5898     case PDFWriter::ActionDefault :     //do nothing, this is the Acrobat default
5899     default:
5900         if( aInitPageRef.getLength() > 1 )
5901         {
5902             aLine.append( "/OpenAction[" );
5903             aLine.append( aInitPageRef );
5904             aLine.append( " /XYZ null null 0]\n" );
5905         }
5906         break;
5907     case PDFWriter::FitInWindow :
5908         aLine.append( "/OpenAction[" );
5909         aLine.append( aInitPageRef );
5910         aLine.append( " /Fit]\n" ); //Open fit page
5911         break;
5912     case PDFWriter::FitWidth :
5913         aLine.append( "/OpenAction[" );
5914         aLine.append( aInitPageRef );
5915         aLine.append( " /FitH " );
5916         aLine.append( m_nInheritedPageHeight );//Open fit width
5917         aLine.append( "]\n" );
5918         break;
5919     case PDFWriter::FitVisible :
5920         aLine.append( "/OpenAction[" );
5921         aLine.append( aInitPageRef );
5922         aLine.append( " /FitBH " );
5923         aLine.append( m_nInheritedPageHeight );//Open fit visible
5924         aLine.append( "]\n" );
5925         break;
5926     case PDFWriter::ActionZoom :
5927         aLine.append( "/OpenAction[" );
5928         aLine.append( aInitPageRef );
5929         aLine.append( " /XYZ null null " );
5930         if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5931             aLine.append( (double)m_aContext.Zoom/100.0 );
5932         else
5933             aLine.append( "0" );
5934         aLine.append( "]\n" );
5935         break;
5936     }
5937 // viewer preferences, if we had some, then emit
5938     if( m_aContext.HideViewerToolbar ||
5939         ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) ||
5940         m_aContext.HideViewerMenubar ||
5941         m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
5942         m_aContext.CenterWindow || (m_aContext.FirstPageLeft  &&  m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
5943         m_aContext.OpenInFullScreenMode )
5944     {
5945         aLine.append( "/ViewerPreferences<<" );
5946         if( m_aContext.HideViewerToolbar )
5947             aLine.append( "/HideToolbar true\n" );
5948         if( m_aContext.HideViewerMenubar )
5949             aLine.append( "/HideMenubar true\n" );
5950         if( m_aContext.HideViewerWindowControls )
5951             aLine.append( "/HideWindowUI true\n" );
5952         if( m_aContext.FitWindow )
5953             aLine.append( "/FitWindow true\n" );
5954         if( m_aContext.CenterWindow )
5955             aLine.append( "/CenterWindow true\n" );
5956         if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle )
5957             aLine.append( "/DisplayDocTitle true\n" );
5958         if( m_aContext.FirstPageLeft &&  m_aContext.PageLayout == PDFWriter::ContinuousFacing )
5959             aLine.append( "/Direction/R2L\n" );
5960         if( m_aContext.OpenInFullScreenMode )
5961             switch( m_aContext.PDFDocumentMode )
5962             {
5963             default :
5964             case PDFWriter::ModeDefault :
5965                 aLine.append( "/NonFullScreenPageMode/UseNone\n" );
5966                 break;
5967             case PDFWriter::UseOutlines :
5968                 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
5969                 break;
5970             case PDFWriter::UseThumbs :
5971                 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
5972                 break;
5973             }
5974         aLine.append( ">>\n" );
5975     }
5976 
5977     if( nOutlineDict )
5978     {
5979         aLine.append( "/Outlines " );
5980         aLine.append( nOutlineDict );
5981         aLine.append( " 0 R\n" );
5982     }
5983     if( nStructureDict )
5984     {
5985         aLine.append( "/StructTreeRoot " );
5986         aLine.append( nStructureDict );
5987         aLine.append( " 0 R\n" );
5988     }
5989     if( m_aContext.DocumentLocale.Language.getLength() > 0 )
5990     {
5991         OUStringBuffer aLocBuf( 16 );
5992         aLocBuf.append( m_aContext.DocumentLocale.Language.toAsciiLowerCase() );
5993         if( m_aContext.DocumentLocale.Country.getLength() > 0 )
5994         {
5995             aLocBuf.append( sal_Unicode('-') );
5996             aLocBuf.append( m_aContext.DocumentLocale.Country );
5997         }
5998         aLine.append( "/Lang" );
5999         appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
6000         aLine.append( "\n" );
6001     }
6002     if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 )
6003     {
6004         aLine.append( "/MarkInfo<</Marked true>>\n" );
6005     }
6006     if( m_aWidgets.size() > 0 )
6007     {
6008         aLine.append( "/AcroForm<</Fields[\n" );
6009         int nWidgets = m_aWidgets.size();
6010         int nOut = 0;
6011         for( int j = 0; j < nWidgets; j++ )
6012         {
6013             // output only root fields
6014             if( m_aWidgets[j].m_nParent < 1 )
6015             {
6016                 aLine.append( m_aWidgets[j].m_nObject );
6017                 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
6018             }
6019         }
6020         aLine.append( "\n]/DR " );
6021         aLine.append( getResourceDictObj() );
6022         aLine.append( " 0 R" );
6023         if( m_bIsPDF_A1 )
6024             aLine.append( ">>\n" );
6025         else
6026             aLine.append( "/NeedAppearances true>>\n" );
6027     }
6028 //--->i59651
6029 //check if there is a Metadata object
6030     if( nOutputIntentObject )
6031     {
6032         aLine.append("/OutputIntents[");
6033         aLine.append( nOutputIntentObject );
6034         aLine.append( " 0 R]" );
6035     }
6036     if( nMetadataObject )
6037     {
6038         aLine.append("/Metadata ");
6039         aLine.append( nMetadataObject );
6040         aLine.append( " 0 R" );
6041     }
6042 //<----
6043     aLine.append( ">>\n"
6044                   "endobj\n\n" );
6045     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6046 
6047     return true;
6048 }
6049 
6050 sal_Int32 PDFWriterImpl::emitInfoDict( )
6051 {
6052     sal_Int32 nObject = createObject();
6053 
6054     if( updateObject( nObject ) )
6055     {
6056         OStringBuffer aLine( 1024 );
6057         aLine.append( nObject );
6058         aLine.append( " 0 obj\n"
6059                       "<<" );
6060         if( m_aContext.DocumentInfo.Title.Len() )
6061         {
6062             aLine.append( "/Title" );
6063             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine );
6064             aLine.append( "\n" );
6065         }
6066         if( m_aContext.DocumentInfo.Author.Len() )
6067         {
6068             aLine.append( "/Author" );
6069             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine );
6070             aLine.append( "\n" );
6071         }
6072         if( m_aContext.DocumentInfo.Subject.Len() )
6073         {
6074             aLine.append( "/Subject" );
6075             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine );
6076             aLine.append( "\n" );
6077         }
6078         if( m_aContext.DocumentInfo.Keywords.Len() )
6079         {
6080             aLine.append( "/Keywords" );
6081             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine );
6082             aLine.append( "\n" );
6083         }
6084         if( m_aContext.DocumentInfo.Creator.Len() )
6085         {
6086             aLine.append( "/Creator" );
6087             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine );
6088             aLine.append( "\n" );
6089         }
6090         if( m_aContext.DocumentInfo.Producer.Len() )
6091         {
6092             aLine.append( "/Producer" );
6093             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine );
6094             aLine.append( "\n" );
6095         }
6096 
6097          aLine.append( "/CreationDate" );
6098          appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine );
6099         aLine.append( ">>\nendobj\n\n" );
6100         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6101             nObject = 0;
6102     }
6103     else
6104         nObject = 0;
6105 
6106     return nObject;
6107 }
6108 
6109 //--->i56629
6110 // Part of this function may be shared with method appendDest.
6111 //
6112 sal_Int32 PDFWriterImpl::emitNamedDestinations()
6113 {
6114     sal_Int32  nCount = m_aNamedDests.size();
6115     if( nCount <= 0 )
6116         return 0;//define internal error
6117 
6118 //get the object number for all the destinations
6119     sal_Int32 nObject = createObject();
6120 
6121     if( updateObject( nObject ) )
6122     {
6123 //emit the dictionary
6124         OStringBuffer aLine( 1024 );
6125         aLine.append( nObject );
6126         aLine.append( " 0 obj\n"
6127                       "<<" );
6128 
6129         sal_Int32  nDestID;
6130         for( nDestID = 0; nDestID < nCount; nDestID++ )
6131         {
6132             const PDFNamedDest& rDest   = m_aNamedDests[ nDestID ];
6133 // In order to correctly function both under an Internet browser and
6134 // directly with a reader (provided the reader has the feature) we
6135 // need to set the name of the destination the same way it will be encoded
6136 // in an Internet link
6137             INetURLObject aLocalURL(
6138                 OUString( RTL_CONSTASCII_USTRINGPARAM( "http://ahost.ax" ) ) ); //dummy location, won't be used
6139             aLocalURL.SetMark( rDest.m_aDestName );
6140 
6141             const rtl::OUString aName   = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as
6142             // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
6143             const PDFPage& rDestPage    = m_aPages[ rDest.m_nPage ];
6144 
6145             aLine.append( '/' );
6146             appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
6147             aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
6148                                  //maps the preceeding character properly
6149             aLine.append( rDestPage.m_nPageObject );
6150             aLine.append( " 0 R" );
6151 
6152             switch( rDest.m_eType )
6153             {
6154             case PDFWriter::XYZ:
6155             default:
6156                 aLine.append( "/XYZ " );
6157                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6158                 aLine.append( ' ' );
6159                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6160                 aLine.append( " 0" );
6161                 break;
6162             case PDFWriter::Fit:
6163                 aLine.append( "/Fit" );
6164                 break;
6165             case PDFWriter::FitRectangle:
6166                 aLine.append( "/FitR " );
6167                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6168                 aLine.append( ' ' );
6169                 appendFixedInt( rDest.m_aRect.Top(), aLine );
6170                 aLine.append( ' ' );
6171                 appendFixedInt( rDest.m_aRect.Right(), aLine );
6172                 aLine.append( ' ' );
6173                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6174                 break;
6175             case PDFWriter::FitHorizontal:
6176                 aLine.append( "/FitH " );
6177                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6178                 break;
6179             case PDFWriter::FitVertical:
6180                 aLine.append( "/FitV " );
6181                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6182                 break;
6183             case PDFWriter::FitPageBoundingBox:
6184                 aLine.append( "/FitB" );
6185                 break;
6186             case PDFWriter::FitPageBoundingBoxHorizontal:
6187                 aLine.append( "/FitBH " );
6188                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6189                 break;
6190             case PDFWriter::FitPageBoundingBoxVertical:
6191                 aLine.append( "/FitBV " );
6192                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6193                 break;
6194             }
6195             aLine.append( "]\n" );
6196         }
6197 //close
6198 
6199         aLine.append( ">>\nendobj\n\n" );
6200         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6201             nObject = 0;
6202     }
6203     else
6204         nObject = 0;
6205 
6206     return nObject;
6207 }
6208 //<--- i56629
6209 
6210 //--->i59651
6211 // emits the output intent dictionary
6212 
6213 sal_Int32 PDFWriterImpl::emitOutputIntent()
6214 {
6215     if( !m_bIsPDF_A1 )
6216         return 0;
6217 
6218 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
6219 
6220     OStringBuffer aLine( 1024 );
6221     sal_Int32 nICCObject = createObject();
6222     sal_Int32 nStreamLengthObject = createObject();
6223 
6224     aLine.append( nICCObject );
6225 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
6226     aLine.append( " 0 obj\n<</N 3/Length " );
6227     aLine.append( nStreamLengthObject );
6228     aLine.append( " 0 R" );
6229 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
6230     aLine.append( "/Filter/FlateDecode" );
6231 #endif
6232     aLine.append( ">>\nstream\n" );
6233     CHECK_RETURN( updateObject( nICCObject ) );
6234     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6235 //get file position
6236     sal_uInt64 nBeginStreamPos = 0;
6237     osl_getFilePos( m_aFile, &nBeginStreamPos );
6238     beginCompression();
6239     checkAndEnableStreamEncryption( nICCObject );
6240     sal_Int32 nStreamSize = writeBuffer( nsRGB_ICC_profile, (sal_Int32) sizeof( nsRGB_ICC_profile ) );
6241     disableStreamEncryption();
6242     endCompression();
6243     sal_uInt64 nEndStreamPos = 0;
6244     osl_getFilePos( m_aFile, &nEndStreamPos );
6245 
6246     if( nStreamSize == 0 )
6247         return 0;
6248     if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6249         return 0 ;
6250     aLine.setLength( 0 );
6251 
6252 //emit the stream length   object
6253     CHECK_RETURN( updateObject( nStreamLengthObject ) );
6254     aLine.setLength( 0 );
6255     aLine.append( nStreamLengthObject );
6256     aLine.append( " 0 obj\n" );
6257     aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6258     aLine.append( "\nendobj\n\n" );
6259     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6260     aLine.setLength( 0 );
6261 
6262 //emit the OutputIntent dictionary
6263     sal_Int32 nOIObject = createObject();
6264     CHECK_RETURN( updateObject( nOIObject ) );
6265     aLine.append( nOIObject );
6266     aLine.append( " 0 obj\n"
6267                   "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
6268 
6269     rtl::OUString aComment( RTL_CONSTASCII_USTRINGPARAM( "sRGB IEC61966-2.1" ) );
6270     appendLiteralStringEncrypt( aComment ,nOIObject, aLine );
6271     aLine.append("/DestOutputProfile ");
6272     aLine.append( nICCObject );
6273     aLine.append( " 0 R>>\nendobj\n\n" );;
6274     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6275 
6276     return nOIObject;
6277 }
6278 
6279 // formats the string for the XML stream
6280 static void escapeStringXML( const rtl::OUString& rStr, rtl::OUString &rValue)
6281 {
6282     const sal_Unicode* pUni = rStr.getStr();
6283     int nLen = rStr.getLength();
6284     for( ; nLen; nLen--, pUni++ )
6285     {
6286         switch( *pUni )
6287         {
6288         case sal_Unicode('&'):
6289             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&amp;" ) );
6290         break;
6291         case sal_Unicode('<'):
6292             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&lt;" ) );
6293         break;
6294         case sal_Unicode('>'):
6295             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&gt;" ) );
6296         break;
6297         case sal_Unicode('\''):
6298             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&apos;" ) );
6299         break;
6300         case sal_Unicode('"'):
6301             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&quot;" ) );
6302         break;
6303         default:
6304             rValue += rtl::OUString( *pUni );
6305             break;
6306         }
6307     }
6308 }
6309 
6310 // emits the document metadata
6311 //
6312 sal_Int32 PDFWriterImpl::emitDocumentMetadata()
6313 {
6314     if( !m_bIsPDF_A1 )
6315         return 0;
6316 
6317     //get the object number for all the destinations
6318     sal_Int32 nObject = createObject();
6319 
6320     if( updateObject( nObject ) )
6321     {
6322 // the following string are written in UTF-8 unicode
6323         OStringBuffer aMetadataStream( 8192 );
6324 
6325         aMetadataStream.append( "<?xpacket begin=\"" );
6326 // this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used
6327 // as a byte-order marker.
6328         aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) );
6329         aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
6330         aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
6331         aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
6332 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
6333         aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6334         aMetadataStream.append( "      xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
6335         aMetadataStream.append( "   <pdfaid:part>1</pdfaid:part>\n" );
6336         aMetadataStream.append( "   <pdfaid:conformance>A</pdfaid:conformance>\n" );
6337         aMetadataStream.append( "  </rdf:Description>\n" );
6338 //... Dublin Core properties go here
6339         if( m_aContext.DocumentInfo.Title.Len() ||
6340             m_aContext.DocumentInfo.Author.Len() ||
6341             m_aContext.DocumentInfo.Subject.Len() )
6342         {
6343             aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6344             aMetadataStream.append( "      xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
6345             if( m_aContext.DocumentInfo.Title.Len() )
6346             {
6347 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6348                 aMetadataStream.append( "   <dc:title>\n" );
6349                 aMetadataStream.append( "    <rdf:Alt>\n" );
6350                 aMetadataStream.append( "     <rdf:li xml:lang=\"x-default\">" );
6351                 rtl::OUString aTitle;
6352                 escapeStringXML( m_aContext.DocumentInfo.Title, aTitle );
6353                 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 )  );
6354                 aMetadataStream.append( "</rdf:li>\n" );
6355                 aMetadataStream.append( "    </rdf:Alt>\n" );
6356                 aMetadataStream.append( "   </dc:title>\n" );
6357             }
6358             if( m_aContext.DocumentInfo.Author.Len() )
6359             {
6360                 aMetadataStream.append( "   <dc:creator>\n" );
6361                 aMetadataStream.append( "    <rdf:Seq>\n" );
6362                 aMetadataStream.append( "     <rdf:li>" );
6363                 rtl::OUString aAuthor;
6364                 escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor );
6365                 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 )  );
6366                 aMetadataStream.append( "</rdf:li>\n" );
6367                 aMetadataStream.append( "    </rdf:Seq>\n" );
6368                 aMetadataStream.append( "   </dc:creator>\n" );
6369             }
6370             if( m_aContext.DocumentInfo.Subject.Len() )
6371             {
6372 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6373                 aMetadataStream.append( "   <dc:description>\n" );
6374                 aMetadataStream.append( "    <rdf:Alt>\n" );
6375                 aMetadataStream.append( "     <rdf:li xml:lang=\"x-default\">" );
6376                 rtl::OUString aSubject;
6377                 escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject );
6378                 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 )  );
6379                 aMetadataStream.append( "</rdf:li>\n" );
6380                 aMetadataStream.append( "    </rdf:Alt>\n" );
6381                 aMetadataStream.append( "   </dc:description>\n" );
6382             }
6383             aMetadataStream.append( "  </rdf:Description>\n" );
6384         }
6385 
6386 //... PDF properties go here
6387         if( m_aContext.DocumentInfo.Producer.Len() ||
6388             m_aContext.DocumentInfo.Keywords.Len() )
6389         {
6390             aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6391             aMetadataStream.append( "     xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
6392             if( m_aContext.DocumentInfo.Producer.Len() )
6393             {
6394                 aMetadataStream.append( "   <pdf:Producer>" );
6395                 rtl::OUString aProducer;
6396                 escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer );
6397                 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 )  );
6398                 aMetadataStream.append( "</pdf:Producer>\n" );
6399             }
6400             if( m_aContext.DocumentInfo.Keywords.Len() )
6401             {
6402                 aMetadataStream.append( "   <pdf:Keywords>" );
6403                 rtl::OUString aKeywords;
6404                 escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords );
6405                 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 )  );
6406                 aMetadataStream.append( "</pdf:Keywords>\n" );
6407             }
6408             aMetadataStream.append( "  </rdf:Description>\n" );
6409         }
6410 
6411         aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6412         aMetadataStream.append( "    xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
6413         if( m_aContext.DocumentInfo.Creator.Len() )
6414         {
6415             aMetadataStream.append( "   <xmp:CreatorTool>" );
6416             rtl::OUString aCreator;
6417             escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator );
6418             aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 )  );
6419             aMetadataStream.append( "</xmp:CreatorTool>\n" );
6420         }
6421 //creation date
6422         aMetadataStream.append( "   <xmp:CreateDate>" );
6423         aMetadataStream.append( m_aCreationMetaDateString );
6424         aMetadataStream.append( "</xmp:CreateDate>\n" );
6425 
6426         aMetadataStream.append( "  </rdf:Description>\n" );
6427         aMetadataStream.append( " </rdf:RDF>\n" );
6428         aMetadataStream.append( "</x:xmpmeta>\n" );
6429 
6430 //add the padding
6431         for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ )
6432         {
6433             aMetadataStream.append( " " );
6434             if( nSpaces % 100 == 0 )
6435                 aMetadataStream.append( "\n" );
6436         }
6437 
6438         aMetadataStream.append( "<?xpacket end=\"w\"?>\n" );
6439 
6440         OStringBuffer aMetadataObj( 1024 );
6441 
6442         aMetadataObj.append( nObject );
6443         aMetadataObj.append( " 0 obj\n" );
6444 
6445         aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
6446 
6447         aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() );
6448         aMetadataObj.append( ">>\nstream\n" );
6449         CHECK_RETURN( writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) );
6450 //emit the stream
6451         CHECK_RETURN( writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) );
6452 
6453         aMetadataObj.setLength( 0 );
6454         aMetadataObj.append( "\nendstream\nendobj\n\n" );
6455         if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
6456             nObject = 0;
6457     }
6458     else
6459         nObject = 0;
6460 
6461     return nObject;
6462 }
6463 //<---i59651
6464 
6465 bool PDFWriterImpl::emitTrailer()
6466 {
6467     // emit doc info
6468     OString aInfoValuesOut;
6469     sal_Int32 nDocInfoObject = emitInfoDict( );
6470 
6471     sal_Int32 nSecObject = 0;
6472 
6473 	if( m_aContext.Encryption.Encrypt() )
6474 	{
6475 //emit the security information
6476 //must be emitted as indirect dictionary object, since
6477 //Acrobat Reader 5 works only with this kind of implementation
6478 		nSecObject = createObject();
6479 
6480 		if( updateObject( nSecObject ) )
6481 		{
6482 			OStringBuffer aLineS( 1024 );
6483 			aLineS.append( nSecObject );
6484 			aLineS.append( " 0 obj\n"
6485 						   "<</Filter/Standard/V " );
6486 			// check the version
6487 			if( m_aContext.Encryption.Security128bit )
6488 				aLineS.append( "2/Length 128/R 3" );
6489 			else
6490 				aLineS.append( "1/R 2" );
6491 
6492 			// emit the owner password, must not be encrypted
6493 			aLineS.append( "/O(" );
6494 			appendLiteralString( (const sal_Char*)&m_aContext.Encryption.OValue[0], sal_Int32(m_aContext.Encryption.OValue.size()), aLineS );
6495 			aLineS.append( ")/U(" );
6496 			appendLiteralString( (const sal_Char*)&m_aContext.Encryption.UValue[0], sal_Int32(m_aContext.Encryption.UValue.size()), aLineS );
6497 			aLineS.append( ")/P " );// the permission set
6498 			aLineS.append( m_nAccessPermissions );
6499 			aLineS.append( ">>\nendobj\n\n" );
6500 			if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) )
6501 				nSecObject = 0;
6502 		}
6503 		else
6504 			nSecObject = 0;
6505 	}
6506     // emit xref table
6507     // remember start
6508     sal_uInt64 nXRefOffset = 0;
6509     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) );
6510     CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
6511 
6512     sal_Int32 nObjects = m_aObjects.size();
6513     OStringBuffer aLine;
6514     aLine.append( "0 " );
6515     aLine.append( (sal_Int32)(nObjects+1) );
6516     aLine.append( "\n" );
6517     aLine.append( "0000000000 65535 f \n" );
6518     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6519 
6520     for( sal_Int32 i = 0; i < nObjects; i++ )
6521     {
6522         aLine.setLength( 0 );
6523         OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] );
6524         for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
6525             aLine.append( '0' );
6526         aLine.append( aOffset );
6527         aLine.append( " 00000 n \n" );
6528         DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" );
6529         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6530     }
6531 
6532     // prepare document checksum
6533     OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
6534     if( m_aDocDigest )
6535     {
6536         sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
6537         rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) );
6538         for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ )
6539             appendHex( nMD5Sum[i], aDocChecksum );
6540     }
6541     // document id set in setDocInfo method
6542     // emit trailer
6543     aLine.setLength( 0 );
6544     aLine.append( "trailer\n"
6545                   "<</Size " );
6546     aLine.append( (sal_Int32)(nObjects+1) );
6547     aLine.append( "/Root " );
6548     aLine.append( m_nCatalogObject );
6549     aLine.append( " 0 R\n" );
6550     if( nSecObject |= 0 )
6551     {
6552         aLine.append( "/Encrypt ");
6553         aLine.append( nSecObject );
6554         aLine.append( " 0 R\n" );
6555     }
6556     if( nDocInfoObject )
6557     {
6558         aLine.append( "/Info " );
6559         aLine.append( nDocInfoObject );
6560         aLine.append( " 0 R\n" );
6561     }
6562     if( ! m_aContext.Encryption.DocumentIdentifier.empty() )
6563     {
6564         aLine.append( "/ID [ <" );
6565         for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6566              it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6567         {
6568             appendHex( sal_Int8(*it), aLine );
6569         }
6570         aLine.append( ">\n"
6571                       "<" );
6572         for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6573              it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6574         {
6575             appendHex( sal_Int8(*it), aLine );
6576         }
6577         aLine.append( "> ]\n" );
6578     }
6579     if( aDocChecksum.getLength() )
6580     {
6581         aLine.append( "/DocChecksum /" );
6582         aLine.append( aDocChecksum );
6583         aLine.append( "\n" );
6584     }
6585     if( m_aAdditionalStreams.size() > 0 )
6586     {
6587         aLine.append( "/AdditionalStreams [" );
6588         for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ )
6589         {
6590             aLine.append( "/" );
6591             appendName( m_aAdditionalStreams[i].m_aMimeType, aLine );
6592             aLine.append( " " );
6593             aLine.append( m_aAdditionalStreams[i].m_nStreamObject );
6594             aLine.append( " 0 R\n" );
6595         }
6596         aLine.append( "]\n" );
6597     }
6598     aLine.append( ">>\n"
6599                   "startxref\n" );
6600     aLine.append( (sal_Int64)nXRefOffset );
6601     aLine.append( "\n"
6602                   "%%EOF\n" );
6603     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6604 
6605     return true;
6606 }
6607 
6608 struct AnnotationSortEntry
6609 {
6610     sal_Int32 nTabOrder;
6611     sal_Int32 nObject;
6612     sal_Int32 nWidgetIndex;
6613 
6614     AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
6615         nTabOrder( nTab ),
6616         nObject( nObj ),
6617         nWidgetIndex( nI )
6618     {}
6619 };
6620 
6621 struct AnnotSortContainer
6622 {
6623     std::set< sal_Int32 >               aObjects;
6624     std::vector< AnnotationSortEntry >    aSortedAnnots;
6625 };
6626 
6627 struct AnnotSorterLess
6628 {
6629     std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets;
6630 
6631     AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {}
6632 
6633     bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
6634     {
6635         if( rLeft.nTabOrder < rRight.nTabOrder )
6636             return true;
6637         if( rRight.nTabOrder < rLeft.nTabOrder )
6638             return false;
6639         if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
6640             return false;
6641         if( rRight.nWidgetIndex < 0 )
6642             return true;
6643         if( rLeft.nWidgetIndex < 0 )
6644             return false;
6645         // remember: widget rects are in PDF coordinates, so they are ordered down up
6646         if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
6647             m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
6648             return true;
6649         if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
6650             m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
6651             return false;
6652         if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
6653             m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
6654             return true;
6655         return false;
6656     }
6657 };
6658 
6659 void PDFWriterImpl::sortWidgets()
6660 {
6661     // sort widget annotations on each page as per their
6662     // TabOrder attribute
6663     std::hash_map< sal_Int32, AnnotSortContainer > sorted;
6664     int nWidgets = m_aWidgets.size();
6665     for( int nW = 0; nW < nWidgets; nW++ )
6666     {
6667         const PDFWidget& rWidget = m_aWidgets[nW];
6668         if( rWidget.m_nPage >= 0 )
6669         {
6670             AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
6671             // optimize vector allocation
6672             if( rCont.aSortedAnnots.empty() )
6673                 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
6674             // insert widget to tab sorter
6675             // RadioButtons are not page annotations, only their individual check boxes are
6676             if( rWidget.m_eType != PDFWriter::RadioButton )
6677             {
6678                 rCont.aObjects.insert( rWidget.m_nObject );
6679                 rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) );
6680             }
6681         }
6682     }
6683     for( std::hash_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it )
6684     {
6685         // append entries for non widget annotations
6686         PDFPage& rPage = m_aPages[ it->first ];
6687         unsigned int nAnnots = rPage.m_aAnnotations.size();
6688         for( unsigned int nA = 0; nA < nAnnots; nA++ )
6689             if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end())
6690                 it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) );
6691 
6692         AnnotSorterLess aLess( m_aWidgets );
6693         std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess );
6694         // sanity check
6695         if( it->second.aSortedAnnots.size() == nAnnots)
6696         {
6697             for( unsigned int nA = 0; nA < nAnnots; nA++ )
6698                 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject;
6699         }
6700         else
6701         {
6702             DBG_ASSERT( 0, "wrong number of sorted annotations" );
6703             #if OSL_DEBUG_LEVEL > 0
6704             fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n"
6705 					 "    %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots );
6706             #endif
6707         }
6708     }
6709 
6710     // FIXME: implement tab order in structure tree for PDF 1.5
6711 }
6712 
6713 namespace vcl {
6714 class PDFStreamIf :
6715 		public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream	>
6716 {
6717     PDFWriterImpl*  m_pWriter;
6718     bool            m_bWrite;
6719     public:
6720     PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
6721     virtual ~PDFStreamIf();
6722 
6723     virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw();
6724     virtual void SAL_CALL flush() throw();
6725     virtual void SAL_CALL closeOutput() throw();
6726 };
6727 }
6728 
6729 PDFStreamIf::~PDFStreamIf()
6730 {
6731 }
6732 
6733 void SAL_CALL  PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw()
6734 {
6735     if( m_bWrite )
6736     {
6737         sal_Int32 nBytes = aData.getLength();
6738         if( nBytes > 0 )
6739             m_pWriter->writeBuffer( aData.getConstArray(), nBytes );
6740     }
6741 }
6742 
6743 void SAL_CALL PDFStreamIf::flush() throw()
6744 {
6745 }
6746 
6747 void SAL_CALL PDFStreamIf::closeOutput() throw()
6748 {
6749     m_bWrite = false;
6750 }
6751 
6752 bool PDFWriterImpl::emitAdditionalStreams()
6753 {
6754     unsigned int nStreams = m_aAdditionalStreams.size();
6755     for( unsigned int i = 0; i < nStreams; i++ )
6756     {
6757         PDFAddStream& rStream = m_aAdditionalStreams[i];
6758         rStream.m_nStreamObject = createObject();
6759         sal_Int32 nSizeObject = createObject();
6760 
6761         if( ! updateObject( rStream.m_nStreamObject ) )
6762             return false;
6763 
6764         OStringBuffer aLine;
6765         aLine.append( rStream.m_nStreamObject );
6766         aLine.append( " 0 obj\n<</Length " );
6767         aLine.append( nSizeObject );
6768         aLine.append( " 0 R" );
6769         if( rStream.m_bCompress )
6770             aLine.append( "/Filter/FlateDecode" );
6771         aLine.append( ">>\nstream\n" );
6772         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6773             return false;
6774         sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0;
6775         if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) )
6776         {
6777             osl_closeFile( m_aFile );
6778             m_bOpen = false;
6779         }
6780         if( rStream.m_bCompress )
6781             beginCompression();
6782 
6783         checkAndEnableStreamEncryption( rStream.m_nStreamObject );
6784         com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) );
6785         rStream.m_pStream->write( xStream );
6786         xStream.clear();
6787         delete rStream.m_pStream;
6788         rStream.m_pStream = NULL;
6789         disableStreamEncryption();
6790 
6791         if( rStream.m_bCompress )
6792             endCompression();
6793 
6794         if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) )
6795         {
6796             osl_closeFile( m_aFile );
6797             m_bOpen = false;
6798             return false;
6799         }
6800         if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6801             return false ;
6802         // emit stream length object
6803         if( ! updateObject( nSizeObject ) )
6804             return false;
6805         aLine.setLength( 0 );
6806         aLine.append( nSizeObject );
6807         aLine.append( " 0 obj\n" );
6808         aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6809         aLine.append( "\nendobj\n\n" );
6810         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6811             return false;
6812     }
6813     return true;
6814 }
6815 
6816 bool PDFWriterImpl::emit()
6817 {
6818     endPage();
6819 
6820     // resort structure tree and annotations if necessary
6821     // needed for widget tab order
6822     sortWidgets();
6823 
6824     // emit additional streams
6825     CHECK_RETURN( emitAdditionalStreams() );
6826 
6827     // emit catalog
6828     CHECK_RETURN( emitCatalog() );
6829 
6830     // emit trailer
6831     CHECK_RETURN( emitTrailer() );
6832 
6833     osl_closeFile( m_aFile );
6834     m_bOpen = false;
6835 
6836     return true;
6837 }
6838 
6839 std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors()
6840 {
6841     return m_aErrors;
6842 }
6843 
6844 sal_Int32 PDFWriterImpl::getSystemFont( const Font& i_rFont )
6845 {
6846     getReferenceDevice()->Push();
6847     getReferenceDevice()->SetFont( i_rFont );
6848     getReferenceDevice()->ImplNewFont();
6849 
6850     const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6851     sal_Int32 nFontID = 0;
6852     FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont );
6853     if( it != m_aSystemFonts.end() )
6854         nFontID = it->second.m_nNormalFontID;
6855     else
6856     {
6857         nFontID = m_nNextFID++;
6858         m_aSystemFonts[ pDevFont ] = EmbedFont();
6859         m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID;
6860     }
6861 
6862     getReferenceDevice()->Pop();
6863     getReferenceDevice()->ImplNewFont();
6864 
6865     return nFontID;
6866 }
6867 
6868 void PDFWriterImpl::registerGlyphs( int nGlyphs,
6869                                     sal_GlyphId* pGlyphs,
6870                                     sal_Int32* pGlyphWidths,
6871                                     sal_Ucs* pUnicodes,
6872                                     sal_Int32* pUnicodesPerGlyph,
6873                                     sal_uInt8* pMappedGlyphs,
6874                                     sal_Int32* pMappedFontObjects,
6875                                     const ImplFontData* pFallbackFonts[] )
6876 {
6877     const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6878     sal_Ucs* pCurUnicode = pUnicodes;
6879     for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ )
6880     {
6881         const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB);
6882         const ImplFontData* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont;
6883 
6884         if( isBuiltinFont( pCurrentFont ) )
6885         {
6886             sal_Int32 nFontID = 0;
6887             FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6888             if( it != m_aEmbeddedFonts.end() )
6889                 nFontID = it->second.m_nNormalFontID;
6890             else
6891             {
6892                 nFontID = m_nNextFID++;
6893                 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6894                 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6895             }
6896 
6897             pGlyphWidths[ i ] = 0;
6898             pMappedGlyphs[ i ] = sal::static_int_cast<sal_Int8>( nFontGlyphId );
6899             pMappedFontObjects[ i ] = nFontID;
6900             const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pCurrentFont );
6901             if( pFD )
6902             {
6903                 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
6904                 pGlyphWidths[i] = pBuiltinFont->m_aWidths[ nFontGlyphId & 0x00ff ];
6905             }
6906         }
6907         else if( pCurrentFont->mbSubsettable )
6908         {
6909             FontSubset& rSubset = m_aSubsets[ pCurrentFont ];
6910             // search for font specific glyphID
6911             FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId );
6912             if( it != rSubset.m_aMapping.end() )
6913             {
6914                 pMappedFontObjects[i] = it->second.m_nFontID;
6915                 pMappedGlyphs[i] = it->second.m_nSubsetGlyphID;
6916             }
6917             else
6918             {
6919                 // create new subset if necessary
6920                 if( rSubset.m_aSubsets.empty()
6921                 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
6922                 {
6923                     rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) );
6924                 }
6925 
6926                 // copy font id
6927                 pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID;
6928                 // create new glyph in subset
6929                 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
6930                 pMappedGlyphs[i] = nNewId;
6931 
6932                 // add new glyph to emitted font subset
6933                 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
6934                 rNewGlyphEmit.setGlyphId( nNewId );
6935                 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ )
6936                     rNewGlyphEmit.addCode( pCurUnicode[n] );
6937 
6938                 // add new glyph to font mapping
6939                 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
6940                 rNewGlyph.m_nFontID = pMappedFontObjects[i];
6941                 rNewGlyph.m_nSubsetGlyphID = nNewId;
6942             }
6943             getReferenceDevice()->ImplGetGraphics();
6944             const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0);
6945             pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont,
6946                                                           nFontGlyphId,
6947                                                           bVertical,
6948                                                           m_pReferenceDevice->mpGraphics );
6949         }
6950         else if( pCurrentFont->IsEmbeddable() )
6951         {
6952             sal_Int32 nFontID = 0;
6953             FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6954             if( it != m_aEmbeddedFonts.end() )
6955                 nFontID = it->second.m_nNormalFontID;
6956             else
6957             {
6958                 nFontID = m_nNextFID++;
6959                 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6960                 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6961             }
6962             EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont];
6963 
6964             const Ucs2SIntMap* pEncoding = NULL;
6965             const Ucs2OStrMap* pNonEncoded = NULL;
6966             getReferenceDevice()->ImplGetGraphics();
6967             pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded );
6968 
6969             Ucs2SIntMap::const_iterator enc_it;
6970             Ucs2OStrMap::const_iterator nonenc_it;
6971 
6972             sal_Int32 nCurFontID = nFontID;
6973             sal_Ucs cChar = *pCurUnicode;
6974             if( pEncoding )
6975             {
6976                 enc_it = pEncoding->find( cChar );
6977                 if( enc_it != pEncoding->end() && enc_it->second > 0 )
6978                 {
6979                     DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" );
6980                     cChar = (sal_Ucs)enc_it->second;
6981                 }
6982                 else if( (enc_it == pEncoding->end() || enc_it->second == -1) &&
6983                          pNonEncoded &&
6984                          (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() )
6985                 {
6986                     nCurFontID = 0;
6987                     // find non encoded glyph
6988                     for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it )
6989                     {
6990                         if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() )
6991                         {
6992                             nCurFontID = nec_it->m_nFontID;
6993                             cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ];
6994                             break;
6995                         }
6996                     }
6997                     if( nCurFontID == 0 ) // new nonencoded glyph
6998                     {
6999                         if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 )
7000                         {
7001                             rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() );
7002                             rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++;
7003                         }
7004                         EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back();
7005                         rEncoding.m_aEncVector.push_back( EmbedCode() );
7006                         rEncoding.m_aEncVector.back().m_aUnicode = cChar;
7007                         rEncoding.m_aEncVector.back().m_aName = nonenc_it->second;
7008                         rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1);
7009                         nCurFontID = rEncoding.m_nFontID;
7010                         cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ];
7011                     }
7012                 }
7013                 else
7014                     pEncoding = NULL;
7015             }
7016             if( ! pEncoding )
7017             {
7018                 if( cChar & 0xff00 )
7019                 {
7020                     // some characters can be used by conversion
7021                     if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area
7022                         cChar -= 0xf000;
7023                     else
7024                     {
7025                         String aString(cChar);
7026                         ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 );
7027                         cChar = ((sal_Ucs)aChar.GetChar( 0 )) & 0x00ff;
7028                     }
7029                 }
7030             }
7031 
7032             pMappedGlyphs[ i ] = (sal_Int8)cChar;
7033             pMappedFontObjects[ i ] = nCurFontID;
7034             pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont,
7035                                                             (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR,
7036                                                             false,
7037                                                             m_pReferenceDevice->mpGraphics );
7038         }
7039     }
7040 }
7041 
7042 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines )
7043 {
7044     push( PUSH_ALL );
7045 
7046     FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief();
7047 
7048     Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor();
7049     Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7050     Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7051     Color aReliefColor( COL_LIGHTGRAY );
7052     if( aTextColor == COL_BLACK )
7053         aTextColor = Color( COL_WHITE );
7054     if( aTextLineColor == COL_BLACK )
7055         aTextLineColor = Color( COL_WHITE );
7056     if( aOverlineColor == COL_BLACK )
7057         aOverlineColor = Color( COL_WHITE );
7058     if( aTextColor == COL_WHITE )
7059         aReliefColor = Color( COL_BLACK );
7060 
7061     Font aSetFont = m_aCurrentPDFState.m_aFont;
7062     aSetFont.SetRelief( RELIEF_NONE );
7063     aSetFont.SetShadow( sal_False );
7064 
7065     aSetFont.SetColor( aReliefColor );
7066     setTextLineColor( aReliefColor );
7067     setOverlineColor( aReliefColor );
7068     setFont( aSetFont );
7069     long nOff = 1 + getReferenceDevice()->mnDPIX/300;
7070     if( eRelief == RELIEF_ENGRAVED )
7071         nOff = -nOff;
7072 
7073     rLayout.DrawOffset() += Point( nOff, nOff );
7074     updateGraphicsState();
7075     drawLayout( rLayout, rText, bTextLines );
7076 
7077     rLayout.DrawOffset() -= Point( nOff, nOff );
7078     setTextLineColor( aTextLineColor );
7079     setOverlineColor( aOverlineColor );
7080     aSetFont.SetColor( aTextColor );
7081     setFont( aSetFont );
7082     updateGraphicsState();
7083     drawLayout( rLayout, rText, bTextLines );
7084 
7085     // clean up the mess
7086     pop();
7087 }
7088 
7089 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines )
7090 {
7091     Font aSaveFont = m_aCurrentPDFState.m_aFont;
7092     Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7093     Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7094 
7095     Font& rFont = m_aCurrentPDFState.m_aFont;
7096     if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 )
7097         rFont.SetColor( Color( COL_LIGHTGRAY ) );
7098     else
7099         rFont.SetColor( Color( COL_BLACK ) );
7100     rFont.SetShadow( sal_False );
7101     rFont.SetOutline( sal_False );
7102     setFont( rFont );
7103     setTextLineColor( rFont.GetColor() );
7104     setOverlineColor( rFont.GetColor() );
7105     updateGraphicsState();
7106 
7107     long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24);
7108     if( rFont.IsOutline() )
7109         nOff++;
7110     rLayout.DrawBase() += Point( nOff, nOff );
7111     drawLayout( rLayout, rText, bTextLines );
7112     rLayout.DrawBase() -= Point( nOff, nOff );
7113 
7114     setFont( aSaveFont );
7115     setTextLineColor( aSaveTextLineColor );
7116     setOverlineColor( aSaveOverlineColor );
7117     updateGraphicsState();
7118 }
7119 
7120 void PDFWriterImpl::drawVerticalGlyphs(
7121         const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7122         OStringBuffer& rLine,
7123         const Point& rAlignOffset,
7124         const Matrix3& rRotScale,
7125         double fAngle,
7126         double fXScale,
7127         double fSkew,
7128         sal_Int32 nFontHeight )
7129 {
7130     long nXOffset = 0;
7131     Point aCurPos( rGlyphs[0].m_aPos );
7132     aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7133     aCurPos += rAlignOffset;
7134     for( size_t i = 0; i < rGlyphs.size(); i++ )
7135     {
7136         // have to emit each glyph on its own
7137         double fDeltaAngle = 0.0;
7138         double fYScale = 1.0;
7139         double fTempXScale = fXScale;
7140         double fSkewB = fSkew;
7141         double fSkewA = 0.0;
7142 
7143         Point aDeltaPos;
7144         if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL )
7145         {
7146             fDeltaAngle = M_PI/2.0;
7147             aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent();
7148             aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale);
7149             fYScale = fXScale;
7150             fTempXScale = 1.0;
7151             fSkewA = -fSkewB;
7152             fSkewB = 0.0;
7153         }
7154         else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR )
7155         {
7156             fDeltaAngle = -M_PI/2.0;
7157             aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale);
7158             aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent();
7159             fYScale = fXScale;
7160             fTempXScale = 1.0;
7161             fSkewA = fSkewB;
7162             fSkewB = 0.0;
7163         }
7164         aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) );
7165         if( i < rGlyphs.size()-1 )
7166             nXOffset += rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
7167         if( ! rGlyphs[i].m_nGlyphId )
7168             continue;
7169 
7170         aDeltaPos = rRotScale.transform( aDeltaPos );
7171 
7172         Matrix3 aMat;
7173         if( fSkewB != 0.0 || fSkewA != 0.0 )
7174             aMat.skew( fSkewA, fSkewB );
7175         aMat.scale( fTempXScale, fYScale );
7176         aMat.rotate( fAngle+fDeltaAngle );
7177         aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
7178         aMat.append( m_aPages.back(), rLine );
7179         rLine.append( " Tm" );
7180         if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
7181         {
7182             rLine.append( " /F" );
7183             rLine.append( rGlyphs[i].m_nMappedFontId );
7184             rLine.append( ' ' );
7185             m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7186             rLine.append( " Tf" );
7187         }
7188         rLine.append( "<" );
7189         appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
7190         rLine.append( ">Tj\n" );
7191     }
7192 }
7193 
7194 void PDFWriterImpl::drawHorizontalGlyphs(
7195         const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7196         OStringBuffer& rLine,
7197         const Point& rAlignOffset,
7198         double fAngle,
7199         double fXScale,
7200         double fSkew,
7201         sal_Int32 nFontHeight,
7202         sal_Int32 nPixelFontHeight
7203         )
7204 {
7205     // horizontal (= normal) case
7206 
7207     // fill in  run end indices
7208     // end is marked by index of the first glyph of the next run
7209     // a run is marked by same mapped font id and same Y position
7210     std::vector< sal_uInt32 > aRunEnds;
7211     aRunEnds.reserve( rGlyphs.size() );
7212     for( size_t i = 1; i < rGlyphs.size(); i++ )
7213     {
7214         if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
7215             rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
7216         {
7217             aRunEnds.push_back(i);
7218         }
7219     }
7220     // last run ends at last glyph
7221     aRunEnds.push_back( rGlyphs.size() );
7222 
7223     // loop over runs of the same font
7224     sal_uInt32 nBeginRun = 0;
7225     for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
7226     {
7227         // setup text matrix
7228         Point aCurPos = rGlyphs[nBeginRun].m_aPos;
7229         // back transformation to current coordinate system
7230         aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7231         aCurPos += rAlignOffset;
7232         // the first run can be set with "Td" operator
7233         // subsequent use of that operator would move
7234         // the texline matrix relative to what was set before
7235         // making use of that would drive us into rounding issues
7236         Matrix3 aMat;
7237         if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
7238         {
7239             m_aPages.back().appendPoint( aCurPos, rLine, false );
7240             rLine.append( " Td " );
7241         }
7242         else
7243         {
7244             if( fSkew != 0.0 )
7245                 aMat.skew( 0.0, fSkew );
7246             aMat.scale( fXScale, 1.0 );
7247             aMat.rotate( fAngle );
7248             aMat.translate( aCurPos.X(), aCurPos.Y() );
7249             aMat.append( m_aPages.back(), rLine );
7250             rLine.append( " Tm\n" );
7251         }
7252         // set up correct font
7253         rLine.append( "/F" );
7254         rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
7255         rLine.append( ' ' );
7256         m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7257         rLine.append( " Tf" );
7258 
7259         // output glyphs using Tj or TJ
7260         OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
7261         aKernedLine.append( "[<" );
7262         aUnkernedLine.append( '<' );
7263         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
7264         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
7265 
7266         aMat.invert();
7267         bool bNeedKern = false;
7268         for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
7269         {
7270             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
7271             // check if default glyph positioning is sufficient
7272             const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
7273             const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
7274             double fAdvance = aThisPos.X() - aPrevPos.X();
7275             fAdvance *= 1000.0 / nPixelFontHeight;
7276             const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5);
7277             if( nAdjustment != 0 )
7278             {
7279                 // apply individual glyph positioning
7280                 bNeedKern = true;
7281                 aKernedLine.append( ">" );
7282                 aKernedLine.append( nAdjustment );
7283                 aKernedLine.append( "<" );
7284             }
7285             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
7286         }
7287         aKernedLine.append( ">]TJ\n" );
7288         aUnkernedLine.append( ">Tj\n" );
7289         rLine.append( bNeedKern ? aKernedLine : aUnkernedLine );
7290 
7291         // set beginning of next run
7292         nBeginRun = aRunEnds[nRun];
7293     }
7294 }
7295 
7296 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines )
7297 {
7298     // relief takes precedence over shadow (see outdev3.cxx)
7299     if(  m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE )
7300     {
7301         drawRelief( rLayout, rText, bTextLines );
7302         return;
7303     }
7304     else if( m_aCurrentPDFState.m_aFont.IsShadow() )
7305         drawShadow( rLayout, rText, bTextLines );
7306 
7307     OStringBuffer aLine( 512 );
7308 
7309     const int nMaxGlyphs = 256;
7310 
7311     sal_GlyphId pGlyphs[nMaxGlyphs];
7312     sal_Int32 pGlyphWidths[nMaxGlyphs];
7313     sal_uInt8 pMappedGlyphs[nMaxGlyphs];
7314     sal_Int32 pMappedFontObjects[nMaxGlyphs];
7315     std::vector<sal_Ucs> aUnicodes;
7316     aUnicodes.reserve( nMaxGlyphs );
7317     sal_Int32 pUnicodesPerGlyph[nMaxGlyphs];
7318     int pCharPosAry[nMaxGlyphs];
7319     sal_Int32 nAdvanceWidths[nMaxGlyphs];
7320     const ImplFontData* pFallbackFonts[nMaxGlyphs];
7321     bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
7322     int nGlyphs;
7323     int nIndex = 0;
7324     int nMinCharPos = 0, nMaxCharPos = rText.Len()-1;
7325     double fXScale = 1.0;
7326     double fSkew = 0.0;
7327     sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight;
7328     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
7329 
7330     // transform font height back to current units
7331     // note: the layout calculates in outdevs device pixel !!
7332     sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
7333     if( m_aCurrentPDFState.m_aFont.GetWidth() )
7334     {
7335         Font aFont( m_aCurrentPDFState.m_aFont );
7336         aFont.SetWidth( 0 );
7337         FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
7338         if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() )
7339         {
7340             fXScale =
7341                 (double)m_aCurrentPDFState.m_aFont.GetWidth() /
7342                 (double)aMetric.GetWidth();
7343         }
7344         // force state before GetFontMetric
7345         m_pReferenceDevice->ImplNewFont();
7346     }
7347 
7348     // perform artificial italics if necessary
7349     if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
7350           m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
7351         !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL ||
7352            m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE )
7353         )
7354     {
7355         fSkew = M_PI/12.0;
7356     }
7357 
7358     // if the mapmode is distorted we need to adjust for that also
7359     if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
7360     {
7361         fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
7362     }
7363 
7364     int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
7365     // normalize angles
7366     while( nAngle < 0 )
7367         nAngle += 3600;
7368     nAngle = nAngle % 3600;
7369     double fAngle = (double)nAngle * M_PI / 1800.0;
7370 
7371     Matrix3 aRotScale;
7372     aRotScale.scale( fXScale, 1.0 );
7373     if( fAngle != 0.0 )
7374         aRotScale.rotate( -fAngle );
7375 
7376     bool bPop = false;
7377     bool bABold = false;
7378     // artificial bold necessary ?
7379     if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
7380         m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
7381     {
7382         if( ! bPop )
7383             aLine.append( "q " );
7384         bPop = true;
7385         bABold = true;
7386     }
7387     // setup text colors (if necessary)
7388     Color aStrokeColor( COL_TRANSPARENT );
7389     Color aNonStrokeColor( COL_TRANSPARENT );
7390 
7391     if( m_aCurrentPDFState.m_aFont.IsOutline() )
7392     {
7393         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7394         aNonStrokeColor = Color( COL_WHITE );
7395     }
7396     else
7397         aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7398     if( bABold )
7399         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7400 
7401     if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
7402     {
7403         if( ! bPop )
7404             aLine.append( "q " );
7405         bPop = true;
7406         appendStrokingColor( aStrokeColor, aLine );
7407         aLine.append( "\n" );
7408     }
7409     if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
7410     {
7411         if( ! bPop )
7412             aLine.append( "q " );
7413         bPop = true;
7414         appendNonStrokingColor( aNonStrokeColor, aLine );
7415         aLine.append( "\n" );
7416     }
7417 
7418     // begin text object
7419     aLine.append( "BT\n" );
7420     // outline attribute ?
7421     if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
7422     {
7423         // set correct text mode, set stroke width
7424         aLine.append( "2 Tr " ); // fill, then stroke
7425 
7426         if( m_aCurrentPDFState.m_aFont.IsOutline() )
7427         {
7428             // unclear what to do in case of outline and artificial bold
7429             // for the time being outline wins
7430             aLine.append( "0.25 w \n" );
7431         }
7432         else
7433         {
7434             double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0;
7435             m_aPages.back().appendMappedLength( fW, aLine );
7436             aLine.append ( " w\n" );
7437         }
7438     }
7439 
7440     FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
7441 
7442     // collect the glyphs into a single array
7443     const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
7444     std::vector< PDFGlyph > aGlyphs;
7445     aGlyphs.reserve( nTmpMaxGlyphs );
7446     // first get all the glyphs and register them; coordinates still in Pixel
7447     Point aGNGlyphPos;
7448     while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 )
7449     {
7450         aUnicodes.clear();
7451         for( int i = 0; i < nGlyphs; i++ )
7452         {
7453             pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] );
7454 
7455             // default case: 1 glyph is one unicode
7456             pUnicodesPerGlyph[i] = 1;
7457             if( (pGlyphs[i] & GF_ISCHAR) )
7458             {
7459                 aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) );
7460             }
7461             else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
7462             {
7463                 int nChars = 1;
7464                 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) );
7465                 pUnicodesPerGlyph[i] = 1;
7466                 // try to handle ligatures and such
7467                 if( i < nGlyphs-1 )
7468                 {
7469                     nChars = pCharPosAry[i+1] - pCharPosAry[i];
7470                     // #i115618# fix for simple RTL+CTL cases
7471                     // TODO: sanitize for RTL ligatures, more complex CTL, etc.
7472                     if( nChars < 0 )
7473                         nChars = -nChars;
7474 		    else if( nChars == 0 )
7475                         nChars = 1;
7476                     pUnicodesPerGlyph[i] = nChars;
7477                     for( int n = 1; n < nChars; n++ )
7478                         aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) );
7479                 }
7480                 // #i36691# hack that is needed because currently the pGlyphs[]
7481                 // argument is ignored for embeddable fonts and so the layout
7482                 // engine's glyph work is ignored (i.e. char mirroring)
7483                 // TODO: a real solution would be to map the layout engine's
7484                 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
7485                 // back to unicode and then to embeddable font's encoding
7486                 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL )
7487                 {
7488                     size_t nI = aUnicodes.size()-1;
7489                     for( int n = 0; n < nChars; n++, nI-- )
7490                         aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI]));
7491                 }
7492             }
7493             else
7494                 aUnicodes.push_back( 0 );
7495             // note: in case of ctl one character may result
7496             // in multiple glyphs. The current SalLayout
7497             // implementations set -1 then to indicate that no direct
7498             // mapping is possible
7499         }
7500 
7501         registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
7502 
7503         for( int i = 0; i < nGlyphs; i++ )
7504         {
7505             aGlyphs.push_back( PDFGlyph( aGNGlyphPos,
7506                                          pGlyphWidths[i],
7507                                          pGlyphs[i],
7508                                          pMappedFontObjects[i],
7509                                          pMappedGlyphs[i] ) );
7510             if( bVertical )
7511                 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7512             else
7513                 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7514         }
7515     }
7516 
7517     Point aAlignOffset;
7518     if ( eAlign == ALIGN_BOTTOM )
7519         aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
7520     else if ( eAlign == ALIGN_TOP )
7521         aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
7522     if( aAlignOffset.X() || aAlignOffset.Y() )
7523         aAlignOffset = aRotScale.transform( aAlignOffset );
7524 
7525     /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
7526        string contained only on of the UTF16 BOMs
7527     */
7528     if( ! aGlyphs.empty() )
7529     {
7530         if( bVertical )
7531             drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
7532         else
7533             drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
7534     }
7535 
7536     // end textobject
7537     aLine.append( "ET\n" );
7538     if( bPop )
7539         aLine.append( "Q\n" );
7540 
7541     writeBuffer( aLine.getStr(), aLine.getLength() );
7542 
7543     // draw eventual textlines
7544     FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
7545     FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
7546     FontUnderline eOverline  = m_aCurrentPDFState.m_aFont.GetOverline();
7547     if( bTextLines &&
7548         (
7549          ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) ||
7550          ( eOverline  != UNDERLINE_NONE && eOverline  != UNDERLINE_DONTKNOW ) ||
7551          ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
7552          )
7553         )
7554     {
7555         sal_Bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
7556         if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
7557         {
7558             Point aPos, aStartPt;
7559             sal_Int32 nWidth = 0, nAdvance=0;
7560             for( int nStart = 0;;)
7561             {
7562                 sal_GlyphId nGlyphIndex;
7563                 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7564                     break;
7565 
7566                 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7567                 {
7568                     if( !nWidth )
7569                         aStartPt = aPos;
7570 
7571                     nWidth += nAdvance;
7572                 }
7573                 else if( nWidth > 0 )
7574                 {
7575                     drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7576                                   m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7577                                   eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7578                     nWidth = 0;
7579                 }
7580             }
7581 
7582             if( nWidth > 0 )
7583             {
7584                 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7585                               m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7586                               eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7587             }
7588         }
7589         else
7590         {
7591             Point aStartPt = rLayout.GetDrawPosition();
7592             int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
7593             drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7594                           m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7595                           eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7596         }
7597     }
7598 
7599     // write eventual emphasis marks
7600     if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
7601     {
7602         PolyPolygon 			aEmphPoly;
7603         Rectangle				aEmphRect1;
7604         Rectangle				aEmphRect2;
7605         long					nEmphYOff;
7606         long					nEmphWidth;
7607         long					nEmphHeight;
7608         sal_Bool					bEmphPolyLine;
7609         FontEmphasisMark		nEmphMark;
7610 
7611         push( PUSH_ALL );
7612 
7613         aLine.setLength( 0 );
7614         aLine.append( "q\n" );
7615 
7616         nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
7617         if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7618             nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
7619         else
7620             nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
7621         m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
7622                                                  bEmphPolyLine,
7623                                                  aEmphRect1,
7624                                                  aEmphRect2,
7625                                                  nEmphYOff,
7626                                                  nEmphWidth,
7627                                                  nEmphMark,
7628                                                  m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight),
7629                                                  m_pReferenceDevice->mpFontEntry->mnOrientation );
7630         if ( bEmphPolyLine )
7631         {
7632             setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
7633             setFillColor( Color( COL_TRANSPARENT ) );
7634         }
7635         else
7636         {
7637             setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
7638             setLineColor( Color( COL_TRANSPARENT ) );
7639         }
7640         writeBuffer( aLine.getStr(), aLine.getLength() );
7641 
7642         Point aOffset = Point(0,0);
7643 
7644         if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7645             aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff;
7646         else
7647             aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff;
7648 
7649         long nEmphWidth2     = nEmphWidth / 2;
7650         long nEmphHeight2    = nEmphHeight / 2;
7651         aOffset += Point( nEmphWidth2, nEmphHeight2 );
7652 
7653         if ( eAlign == ALIGN_BOTTOM )
7654             aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent;
7655         else if ( eAlign == ALIGN_TOP )
7656             aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent;
7657 
7658         for( int nStart = 0;;)
7659         {
7660             Point aPos;
7661             sal_GlyphId nGlyphIndex;
7662             sal_Int32 nAdvance;
7663             if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7664                 break;
7665 
7666             if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7667             {
7668                 Point aAdjOffset = aOffset;
7669                 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2;
7670                 aAdjOffset = aRotScale.transform( aAdjOffset );
7671 
7672                 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
7673 
7674                 aPos += aAdjOffset;
7675                 aPos = m_pReferenceDevice->PixelToLogic( aPos );
7676                 drawEmphasisMark( aPos.X(), aPos.Y(),
7677                                   aEmphPoly, bEmphPolyLine,
7678                                   aEmphRect1, aEmphRect2 );
7679             }
7680         }
7681 
7682         writeBuffer( "Q\n", 2 );
7683         pop();
7684     }
7685 
7686 }
7687 
7688 void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
7689                                       const PolyPolygon& rPolyPoly, sal_Bool bPolyLine,
7690                                       const Rectangle& rRect1, const Rectangle& rRect2 )
7691 {
7692     // TODO: pass nWidth as width of this mark
7693     // long nWidth = 0;
7694 
7695     if ( rPolyPoly.Count() )
7696     {
7697         if ( bPolyLine )
7698         {
7699             Polygon aPoly = rPolyPoly.GetObject( 0 );
7700             aPoly.Move( nX, nY );
7701             drawPolyLine( aPoly );
7702         }
7703         else
7704         {
7705             PolyPolygon aPolyPoly = rPolyPoly;
7706             aPolyPoly.Move( nX, nY );
7707             drawPolyPolygon( aPolyPoly );
7708         }
7709     }
7710 
7711     if ( !rRect1.IsEmpty() )
7712     {
7713         Rectangle aRect( Point( nX+rRect1.Left(),
7714                                 nY+rRect1.Top() ), rRect1.GetSize() );
7715         drawRectangle( aRect );
7716     }
7717 
7718     if ( !rRect2.IsEmpty() )
7719     {
7720         Rectangle aRect( Point( nX+rRect2.Left(),
7721                                 nY+rRect2.Top() ), rRect2.GetSize() );
7722 
7723         drawRectangle( aRect );
7724     }
7725 }
7726 
7727 void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7728 {
7729     MARK( "drawText" );
7730 
7731     updateGraphicsState();
7732 
7733     // get a layout from the OuputDevice's SalGraphics
7734     // this also enforces font substitution and sets the font on SalGraphics
7735     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
7736     if( pLayout )
7737     {
7738         drawLayout( *pLayout, rText, bTextLines );
7739         pLayout->Release();
7740     }
7741 }
7742 
7743 void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7744 {
7745     MARK( "drawText with array" );
7746 
7747     updateGraphicsState();
7748 
7749     // get a layout from the OuputDevice's SalGraphics
7750     // this also enforces font substitution and sets the font on SalGraphics
7751     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
7752     if( pLayout )
7753     {
7754         drawLayout( *pLayout, rText, bTextLines );
7755         pLayout->Release();
7756     }
7757 }
7758 
7759 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7760 {
7761     MARK( "drawStretchText" );
7762 
7763     updateGraphicsState();
7764 
7765     // get a layout from the OuputDevice's SalGraphics
7766     // this also enforces font substitution and sets the font on SalGraphics
7767     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
7768     if( pLayout )
7769     {
7770         drawLayout( *pLayout, rText, bTextLines );
7771         pLayout->Release();
7772     }
7773 }
7774 
7775 void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines )
7776 {
7777     long        nWidth          = rRect.GetWidth();
7778     long        nHeight         = rRect.GetHeight();
7779 
7780     if ( nWidth <= 0 || nHeight <= 0 )
7781         return;
7782 
7783     MARK( "drawText with rectangle" );
7784 
7785     updateGraphicsState();
7786 
7787     // clip with rectangle
7788     OStringBuffer aLine;
7789     aLine.append( "q " );
7790     m_aPages.back().appendRect( rRect, aLine );
7791     aLine.append( " W* n\n" );
7792     writeBuffer( aLine.getStr(), aLine.getLength() );
7793 
7794     // if disabled text is needed, put in here
7795 
7796     Point       aPos            = rRect.TopLeft();
7797 
7798     long		nTextHeight		= m_pReferenceDevice->GetTextHeight();
7799     xub_StrLen  nMnemonicPos    = STRING_NOTFOUND;
7800 
7801     String aStr = rOrigStr;
7802     if ( nStyle & TEXT_DRAW_MNEMONIC )
7803         aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos );
7804 
7805     // multiline text
7806     if ( nStyle & TEXT_DRAW_MULTILINE )
7807     {
7808         XubString               aLastLine;
7809         ImplMultiTextLineInfo   aMultiLineInfo;
7810         ImplTextLineInfo*       pLineInfo;
7811         long                    nMaxTextWidth;
7812         xub_StrLen              i;
7813         xub_StrLen              nLines;
7814         xub_StrLen              nFormatLines;
7815 
7816         if ( nTextHeight )
7817         {
7818             ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice );
7819             nMaxTextWidth = OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout );
7820             nLines = (xub_StrLen)(nHeight/nTextHeight);
7821             nFormatLines = aMultiLineInfo.Count();
7822             if ( !nLines )
7823                 nLines = 1;
7824             if ( nFormatLines > nLines )
7825             {
7826                 if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
7827                 {
7828                     // handle last line
7829                     nFormatLines = nLines-1;
7830 
7831                     pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
7832                     aLastLine = aStr.Copy( pLineInfo->GetIndex() );
7833                     aLastLine.ConvertLineEnd( LINEEND_LF );
7834                     // replace line feed by space
7835                     xub_StrLen nLastLineLen = aLastLine.Len();
7836                     for ( i = 0; i < nLastLineLen; i++ )
7837                     {
7838                         if ( aLastLine.GetChar( i ) == _LF )
7839                             aLastLine.SetChar( i, ' ' );
7840                     }
7841                     aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
7842                     nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
7843                     nStyle |= TEXT_DRAW_TOP;
7844                 }
7845             }
7846 
7847             // vertical alignment
7848             if ( nStyle & TEXT_DRAW_BOTTOM )
7849                 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
7850             else if ( nStyle & TEXT_DRAW_VCENTER )
7851                 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
7852 
7853             // draw all lines excluding the last
7854             for ( i = 0; i < nFormatLines; i++ )
7855             {
7856                 pLineInfo = aMultiLineInfo.GetLine( i );
7857                 if ( nStyle & TEXT_DRAW_RIGHT )
7858                     aPos.X() += nWidth-pLineInfo->GetWidth();
7859                 else if ( nStyle & TEXT_DRAW_CENTER )
7860                     aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
7861                 xub_StrLen nIndex   = pLineInfo->GetIndex();
7862                 xub_StrLen nLineLen = pLineInfo->GetLen();
7863                 drawText( aPos, aStr, nIndex, nLineLen, bTextLines );
7864                 // mnemonics should not appear in documents,
7865                 // if the need arises, put them in here
7866                 aPos.Y() += nTextHeight;
7867                 aPos.X() = rRect.Left();
7868             }
7869 
7870 
7871             // output last line left adjusted since it was shortened
7872             if ( aLastLine.Len() )
7873                 drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines );
7874         }
7875     }
7876     else
7877     {
7878         long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7879 
7880         // Evt. Text kuerzen
7881         if ( nTextWidth > nWidth )
7882         {
7883             if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) )
7884             {
7885                 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
7886                 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
7887                 nStyle |= TEXT_DRAW_LEFT;
7888                 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7889             }
7890         }
7891 
7892         // vertical alignment
7893         if ( nStyle & TEXT_DRAW_RIGHT )
7894             aPos.X() += nWidth-nTextWidth;
7895         else if ( nStyle & TEXT_DRAW_CENTER )
7896             aPos.X() += (nWidth-nTextWidth)/2;
7897 
7898         if ( nStyle & TEXT_DRAW_BOTTOM )
7899             aPos.Y() += nHeight-nTextHeight;
7900         else if ( nStyle & TEXT_DRAW_VCENTER )
7901             aPos.Y() += (nHeight-nTextHeight)/2;
7902 
7903         // mnemonics should be inserted here if the need arises
7904 
7905         // draw the actual text
7906         drawText( aPos, aStr, 0, STRING_LEN, bTextLines );
7907     }
7908 
7909     // reset clip region to original value
7910     aLine.setLength( 0 );
7911     aLine.append( "Q\n" );
7912     writeBuffer( aLine.getStr(), aLine.getLength() );
7913 }
7914 
7915 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
7916 {
7917     MARK( "drawLine" );
7918 
7919     updateGraphicsState();
7920 
7921     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7922         return;
7923 
7924     OStringBuffer aLine;
7925     m_aPages.back().appendPoint( rStart, aLine );
7926     aLine.append( " m " );
7927     m_aPages.back().appendPoint( rStop, aLine );
7928     aLine.append( " l S\n" );
7929 
7930     writeBuffer( aLine.getStr(), aLine.getLength() );
7931 }
7932 
7933 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
7934 {
7935     MARK( "drawLine with LineInfo" );
7936     updateGraphicsState();
7937 
7938     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7939         return;
7940 
7941     if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 )
7942     {
7943         drawLine( rStart, rStop );
7944         return;
7945     }
7946 
7947     OStringBuffer aLine;
7948 
7949     aLine.append( "q " );
7950     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
7951     {
7952         m_aPages.back().appendPoint( rStart, aLine );
7953         aLine.append( " m " );
7954         m_aPages.back().appendPoint( rStop, aLine );
7955         aLine.append( " l S Q\n" );
7956 
7957         writeBuffer( aLine.getStr(), aLine.getLength() );
7958     }
7959     else
7960     {
7961         PDFWriter::ExtLineInfo aInfo;
7962         convertLineInfoToExtLineInfo( rInfo, aInfo );
7963         Point aPolyPoints[2] = { rStart, rStop };
7964         Polygon aPoly( 2, aPolyPoints );
7965         drawPolyLine( aPoly, aInfo );
7966     }
7967 }
7968 
7969 void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth )
7970 {
7971     Point aDiff( rStop-rStart );
7972     double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) );
7973     if( fLen < 1.0 )
7974         return;
7975 
7976     MARK( "drawWaveLine" );
7977     updateGraphicsState();
7978 
7979     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7980         return;
7981 
7982     OStringBuffer aLine( 512 );
7983     aLine.append( "q " );
7984     m_aPages.back().appendMappedLength( nLineWidth, aLine, true );
7985     aLine.append( " w " );
7986 
7987     appendDouble( (double)aDiff.X()/fLen, aLine );
7988     aLine.append( ' ' );
7989     appendDouble( -(double)aDiff.Y()/fLen, aLine );
7990     aLine.append( ' ' );
7991     appendDouble( (double)aDiff.Y()/fLen, aLine );
7992     aLine.append( ' ' );
7993     appendDouble( (double)aDiff.X()/fLen, aLine );
7994     aLine.append( ' ' );
7995     m_aPages.back().appendPoint( rStart, aLine );
7996     aLine.append( " cm " );
7997     m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine );
7998     aLine.append( "Q\n" );
7999     writeBuffer( aLine.getStr(), aLine.getLength() );
8000 }
8001 
8002 #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x )
8003 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
8004 
8005 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8006 {
8007     // note: units in pFontEntry are ref device pixel
8008     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8009     long			nLineHeight = 0;
8010     long			nLinePos = 0;
8011 
8012     appendStrokingColor( aColor, aLine );
8013     aLine.append( "\n" );
8014 
8015     if ( bIsAbove )
8016     {
8017         if ( !pFontEntry->maMetric.mnAboveWUnderlineSize )
8018             m_pReferenceDevice->ImplInitAboveTextLineSize();
8019         nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize );
8020         nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset );
8021     }
8022     else
8023     {
8024         if ( !pFontEntry->maMetric.mnWUnderlineSize )
8025             m_pReferenceDevice->ImplInitTextLineSize();
8026         nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize );
8027         nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset );
8028     }
8029     if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
8030         nLineHeight = 3;
8031 
8032     long nLineWidth = getReferenceDevice()->mnDPIX/450;
8033     if ( ! nLineWidth )
8034         nLineWidth = 1;
8035 
8036     if ( eTextLine == UNDERLINE_BOLDWAVE )
8037         nLineWidth = 3*nLineWidth;
8038 
8039     m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
8040     aLine.append( " w " );
8041 
8042     if ( eTextLine == UNDERLINE_DOUBLEWAVE )
8043     {
8044         long nOrgLineHeight = nLineHeight;
8045         nLineHeight /= 3;
8046         if ( nLineHeight < 2 )
8047         {
8048             if ( nOrgLineHeight > 1 )
8049                 nLineHeight = 2;
8050             else
8051                 nLineHeight = 1;
8052         }
8053         long nLineDY = nOrgLineHeight-(nLineHeight*2);
8054         if ( nLineDY < nLineWidth )
8055             nLineDY = nLineWidth;
8056         long nLineDY2 = nLineDY/2;
8057         if ( !nLineDY2 )
8058             nLineDY2 = 1;
8059 
8060         nLinePos -= nLineWidth-nLineDY2;
8061 
8062         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8063 
8064         nLinePos += nLineWidth+nLineDY;
8065         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8066     }
8067     else
8068     {
8069         if ( eTextLine != UNDERLINE_BOLDWAVE )
8070             nLinePos -= nLineWidth/2;
8071         m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
8072     }
8073 }
8074 
8075 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8076 {
8077     // note: units in pFontEntry are ref device pixel
8078     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8079     long			nLineHeight = 0;
8080     long			nLinePos  = 0;
8081     long			nLinePos2 = 0;
8082 
8083     if ( eTextLine > UNDERLINE_BOLDWAVE )
8084         eTextLine = UNDERLINE_SINGLE;
8085 
8086     switch ( eTextLine )
8087     {
8088         case UNDERLINE_SINGLE:
8089         case UNDERLINE_DOTTED:
8090         case UNDERLINE_DASH:
8091         case UNDERLINE_LONGDASH:
8092         case UNDERLINE_DASHDOT:
8093         case UNDERLINE_DASHDOTDOT:
8094             if ( bIsAbove )
8095             {
8096                 if ( !pFontEntry->maMetric.mnAboveUnderlineSize )
8097                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8098                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize );
8099                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset );
8100             }
8101             else
8102             {
8103                 if ( !pFontEntry->maMetric.mnUnderlineSize )
8104                     m_pReferenceDevice->ImplInitTextLineSize();
8105                 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize );
8106                 nLinePos    = HCONV( pFontEntry->maMetric.mnUnderlineOffset );
8107             }
8108             break;
8109         case UNDERLINE_BOLD:
8110         case UNDERLINE_BOLDDOTTED:
8111         case UNDERLINE_BOLDDASH:
8112         case UNDERLINE_BOLDLONGDASH:
8113         case UNDERLINE_BOLDDASHDOT:
8114         case UNDERLINE_BOLDDASHDOTDOT:
8115             if ( bIsAbove )
8116             {
8117                 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize )
8118                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8119                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize );
8120                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset );
8121             }
8122             else
8123             {
8124                 if ( !pFontEntry->maMetric.mnBUnderlineSize )
8125                     m_pReferenceDevice->ImplInitTextLineSize();
8126                 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize );
8127                 nLinePos    = HCONV( pFontEntry->maMetric.mnBUnderlineOffset );
8128                 nLinePos += nLineHeight/2;
8129             }
8130             break;
8131         case UNDERLINE_DOUBLE:
8132             if ( bIsAbove )
8133             {
8134                 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize )
8135                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8136                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize );
8137                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 );
8138                 nLinePos2   = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 );
8139             }
8140             else
8141             {
8142                 if ( !pFontEntry->maMetric.mnDUnderlineSize )
8143                     m_pReferenceDevice->ImplInitTextLineSize();
8144                 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize );
8145                 nLinePos    = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 );
8146                 nLinePos2   = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 );
8147             }
8148         default:
8149             break;
8150     }
8151 
8152     if ( nLineHeight )
8153     {
8154         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8155         aLine.append( " w " );
8156         appendStrokingColor( aColor, aLine );
8157         aLine.append( "\n" );
8158 
8159         switch ( eTextLine )
8160         {
8161             case UNDERLINE_DOTTED:
8162             case UNDERLINE_BOLDDOTTED:
8163                 aLine.append( "[ " );
8164                 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8165                 aLine.append( " ] 0 d\n" );
8166                 break;
8167             case UNDERLINE_DASH:
8168             case UNDERLINE_LONGDASH:
8169             case UNDERLINE_BOLDDASH:
8170             case UNDERLINE_BOLDLONGDASH:
8171                 {
8172                     sal_Int32 nDashLength = 4*nLineHeight;
8173                     sal_Int32 nVoidLength = 2*nLineHeight;
8174                     if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) )
8175                         nDashLength = 8*nLineHeight;
8176 
8177                     aLine.append( "[ " );
8178                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8179                     aLine.append( ' ' );
8180                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8181                     aLine.append( " ] 0 d\n" );
8182                 }
8183                 break;
8184             case UNDERLINE_DASHDOT:
8185             case UNDERLINE_BOLDDASHDOT:
8186                 {
8187                     sal_Int32 nDashLength = 4*nLineHeight;
8188                     sal_Int32 nVoidLength = 2*nLineHeight;
8189                     aLine.append( "[ " );
8190                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8191                     aLine.append( ' ' );
8192                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8193                     aLine.append( ' ' );
8194                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8195                     aLine.append( ' ' );
8196                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8197                     aLine.append( " ] 0 d\n" );
8198                 }
8199                 break;
8200             case UNDERLINE_DASHDOTDOT:
8201             case UNDERLINE_BOLDDASHDOTDOT:
8202                 {
8203                     sal_Int32 nDashLength = 4*nLineHeight;
8204                     sal_Int32 nVoidLength = 2*nLineHeight;
8205                     aLine.append( "[ " );
8206                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8207                     aLine.append( ' ' );
8208                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8209                     aLine.append( ' ' );
8210                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8211                     aLine.append( ' ' );
8212                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8213                     aLine.append( ' ' );
8214                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8215                     aLine.append( ' ' );
8216                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8217                     aLine.append( " ] 0 d\n" );
8218                 }
8219                 break;
8220             default:
8221                 break;
8222         }
8223 
8224         aLine.append( "0 " );
8225         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8226         aLine.append( " m " );
8227         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8228         aLine.append( ' ' );
8229         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8230         aLine.append( " l S\n" );
8231         if ( eTextLine == UNDERLINE_DOUBLE )
8232         {
8233             aLine.append( "0 " );
8234             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8235             aLine.append( " m " );
8236             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8237             aLine.append( ' ' );
8238             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8239             aLine.append( " l S\n" );
8240         }
8241     }
8242 }
8243 
8244 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
8245 {
8246     // note: units in pFontEntry are ref device pixel
8247     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8248     long			nLineHeight = 0;
8249     long			nLinePos  = 0;
8250     long			nLinePos2 = 0;
8251 
8252     if ( eStrikeout > STRIKEOUT_X )
8253         eStrikeout = STRIKEOUT_SINGLE;
8254 
8255     switch ( eStrikeout )
8256     {
8257         case STRIKEOUT_SINGLE:
8258             if ( !pFontEntry->maMetric.mnStrikeoutSize )
8259                 m_pReferenceDevice->ImplInitTextLineSize();
8260             nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize );
8261             nLinePos    = HCONV( pFontEntry->maMetric.mnStrikeoutOffset );
8262             break;
8263         case STRIKEOUT_BOLD:
8264             if ( !pFontEntry->maMetric.mnBStrikeoutSize )
8265                 m_pReferenceDevice->ImplInitTextLineSize();
8266             nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize );
8267             nLinePos    = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset );
8268             break;
8269         case STRIKEOUT_DOUBLE:
8270             if ( !pFontEntry->maMetric.mnDStrikeoutSize )
8271                 m_pReferenceDevice->ImplInitTextLineSize();
8272             nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize );
8273             nLinePos    = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 );
8274             nLinePos2   = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 );
8275             break;
8276         default:
8277             break;
8278     }
8279 
8280     if ( nLineHeight )
8281     {
8282         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8283         aLine.append( " w " );
8284         appendStrokingColor( aColor, aLine );
8285         aLine.append( "\n" );
8286 
8287         aLine.append( "0 " );
8288         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8289         aLine.append( " m " );
8290         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8291         aLine.append( ' ' );
8292         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8293         aLine.append( " l S\n" );
8294 
8295         if ( eStrikeout == STRIKEOUT_DOUBLE )
8296         {
8297             aLine.append( "0 " );
8298             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8299             aLine.append( " m " );
8300             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8301             aLine.append( ' ' );
8302             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8303             aLine.append( " l S\n" );
8304         }
8305     }
8306 }
8307 
8308 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
8309 {
8310     String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" );
8311     String aStrikeout = aStrikeoutChar;
8312     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
8313         aStrikeout.Append( aStrikeout );
8314 
8315     // do not get broader than nWidth modulo 1 character
8316     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
8317         aStrikeout.Erase( 0, 1 );
8318     aStrikeout.Append( aStrikeoutChar );
8319     sal_Bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
8320     if ( bShadow )
8321     {
8322         Font aFont = m_aCurrentPDFState.m_aFont;
8323         aFont.SetShadow( sal_False );
8324         setFont( aFont );
8325         updateGraphicsState();
8326     }
8327 
8328     // strikeout string is left aligned non-CTL text
8329     sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode();
8330     m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED );
8331     drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false );
8332     m_pReferenceDevice->SetLayoutMode( nOrigTLM );
8333 
8334     if ( bShadow )
8335     {
8336         Font aFont = m_aCurrentPDFState.m_aFont;
8337         aFont.SetShadow( sal_True );
8338         setFont( aFont );
8339         updateGraphicsState();
8340     }
8341 }
8342 
8343 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove )
8344 {
8345     if ( !nWidth ||
8346          ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
8347            ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) &&
8348            ((eOverline  == UNDERLINE_NONE)||(eOverline  == UNDERLINE_DONTKNOW)) ) )
8349         return;
8350 
8351     MARK( "drawTextLine" );
8352     updateGraphicsState();
8353 
8354     // note: units in pFontEntry are ref device pixel
8355     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8356     Color			aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
8357     Color			aOverlineColor  = m_aCurrentPDFState.m_aOverlineColor;
8358     Color			aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
8359     bool			bStrikeoutDone = false;
8360     bool			bUnderlineDone = false;
8361     bool			bOverlineDone  = false;
8362 
8363     if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
8364     {
8365         drawStrikeoutChar( rPos, nWidth, eStrikeout );
8366         bStrikeoutDone = true;
8367     }
8368 
8369     Point aPos( rPos );
8370     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
8371     if( eAlign == ALIGN_TOP )
8372         aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent );
8373     else if( eAlign == ALIGN_BOTTOM )
8374         aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent );
8375 
8376     OStringBuffer aLine( 512 );
8377     // save GS
8378     aLine.append( "q " );
8379 
8380     // rotate and translate matrix
8381     double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
8382     Matrix3 aMat;
8383     aMat.rotate( fAngle );
8384     aMat.translate( aPos.X(), aPos.Y() );
8385     aMat.append( m_aPages.back(), aLine );
8386     aLine.append( " cm\n" );
8387 
8388     if ( aUnderlineColor.GetTransparency() != 0 )
8389         aUnderlineColor = aStrikeoutColor;
8390 
8391     if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
8392          (eUnderline == UNDERLINE_WAVE) ||
8393          (eUnderline == UNDERLINE_DOUBLEWAVE) ||
8394          (eUnderline == UNDERLINE_BOLDWAVE) )
8395     {
8396         drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8397         bUnderlineDone = true;
8398     }
8399 
8400     if ( (eOverline == UNDERLINE_SMALLWAVE) ||
8401          (eOverline == UNDERLINE_WAVE) ||
8402          (eOverline == UNDERLINE_DOUBLEWAVE) ||
8403          (eOverline == UNDERLINE_BOLDWAVE) )
8404     {
8405         drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8406         bOverlineDone = true;
8407     }
8408 
8409     if ( !bUnderlineDone )
8410     {
8411         drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8412     }
8413 
8414     if ( !bOverlineDone )
8415     {
8416         drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8417     }
8418 
8419     if ( !bStrikeoutDone )
8420     {
8421         drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
8422     }
8423 
8424     aLine.append( "Q\n" );
8425     writeBuffer( aLine.getStr(), aLine.getLength() );
8426 }
8427 
8428 void PDFWriterImpl::drawPolygon( const Polygon& rPoly )
8429 {
8430     MARK( "drawPolygon" );
8431 
8432     updateGraphicsState();
8433 
8434     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8435         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8436         return;
8437 
8438     int nPoints = rPoly.GetSize();
8439     OStringBuffer aLine( 20 * nPoints );
8440     m_aPages.back().appendPolygon( rPoly, aLine );
8441     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8442         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8443         aLine.append( "B*\n" );
8444     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8445         aLine.append( "S\n" );
8446     else
8447         aLine.append( "f*\n" );
8448 
8449     writeBuffer( aLine.getStr(), aLine.getLength() );
8450 }
8451 
8452 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly )
8453 {
8454     MARK( "drawPolyPolygon" );
8455 
8456     updateGraphicsState();
8457 
8458     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8459         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8460         return;
8461 
8462     int nPolygons = rPolyPoly.Count();
8463 
8464     OStringBuffer aLine( 40 * nPolygons );
8465     m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
8466     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8467         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8468         aLine.append( "B*\n" );
8469     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8470         aLine.append( "S\n" );
8471     else
8472         aLine.append( "f*\n" );
8473 
8474     writeBuffer( aLine.getStr(), aLine.getLength() );
8475 }
8476 
8477 void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
8478 {
8479     DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8480     nTransparentPercent = nTransparentPercent % 100;
8481 
8482     MARK( "drawTransparent" );
8483 
8484     updateGraphicsState();
8485 
8486     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8487         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8488         return;
8489 
8490     if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 )
8491     {
8492         m_aErrors.insert( m_bIsPDF_A1 ?
8493                           PDFWriter::Warning_Transparency_Omitted_PDFA :
8494                           PDFWriter::Warning_Transparency_Omitted_PDF13 );
8495 
8496         drawPolyPolygon( rPolyPoly );
8497         return;
8498     }
8499 
8500     // create XObject
8501     m_aTransparentObjects.push_back( TransparencyEmit() );
8502     // FIXME: polygons with beziers may yield incorrect bound rect
8503     m_aTransparentObjects.back().m_aBoundRect	  = rPolyPoly.GetBoundRect();
8504     // convert rectangle to default user space
8505     m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8506     m_aTransparentObjects.back().m_nObject		    = createObject();
8507     m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8508     m_aTransparentObjects.back().m_fAlpha		    = (double)(100-nTransparentPercent) / 100.0;
8509     m_aTransparentObjects.back().m_pContentStream   = new SvMemoryStream( 256, 256 );
8510     // create XObject's content stream
8511     OStringBuffer aContent( 256 );
8512     m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
8513     if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
8514         m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
8515         aContent.append( " B*\n" );
8516     else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
8517         aContent.append( " S\n" );
8518     else
8519         aContent.append( " f*\n" );
8520     m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() );
8521 
8522     OStringBuffer aObjName( 16 );
8523     aObjName.append( "Tr" );
8524     aObjName.append( m_aTransparentObjects.back().m_nObject );
8525     OString aTrName( aObjName.makeStringAndClear() );
8526     aObjName.append( "EGS" );
8527     aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8528     OString aExtName( aObjName.makeStringAndClear() );
8529 
8530     OStringBuffer aLine( 80 );
8531     // insert XObject
8532     aLine.append( "q /" );
8533     aLine.append( aExtName );
8534     aLine.append( " gs /" );
8535     aLine.append( aTrName );
8536     aLine.append( " Do Q\n" );
8537     writeBuffer( aLine.getStr(), aLine.getLength() );
8538 
8539     pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8540     pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8541 }
8542 
8543 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
8544 {
8545     if( nObject >= 0 )
8546     {
8547         switch( eKind )
8548         {
8549             case ResXObject:
8550                 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
8551                 if( ! m_aOutputStreams.empty() )
8552                     m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
8553                 break;
8554             case ResExtGState:
8555                 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
8556                 if( ! m_aOutputStreams.empty() )
8557                     m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
8558                 break;
8559             case ResShading:
8560                 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
8561                 if( ! m_aOutputStreams.empty() )
8562                     m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
8563                 break;
8564             case ResPattern:
8565                 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
8566                 if( ! m_aOutputStreams.empty() )
8567                     m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
8568                 break;
8569         }
8570     }
8571 }
8572 
8573 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect )
8574 {
8575     push( PUSH_ALL );
8576 
8577     // force reemitting clip region
8578     clearClipRegion();
8579     updateGraphicsState();
8580 
8581     m_aOutputStreams.push_front( StreamRedirect() );
8582     m_aOutputStreams.front().m_pStream = pStream;
8583     m_aOutputStreams.front().m_aMapMode = m_aMapMode;
8584 
8585     if( !rTargetRect.IsEmpty() )
8586     {
8587         m_aOutputStreams.front().m_aTargetRect =
8588             lcl_convert( m_aGraphicsStack.front().m_aMapMode,
8589                          m_aMapMode,
8590                          getReferenceDevice(),
8591                          rTargetRect );
8592         Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
8593         long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
8594         aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
8595         m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
8596     }
8597 
8598     // setup graphics state for independent object stream
8599 
8600     // force reemitting colors
8601     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8602     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8603 }
8604 
8605 Rectangle PDFWriterImpl::getRedirectTargetRect() const
8606 {
8607     return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect;
8608 }
8609 
8610 SvStream* PDFWriterImpl::endRedirect()
8611 {
8612     SvStream* pStream = NULL;
8613     if( ! m_aOutputStreams.empty() )
8614     {
8615         pStream		= m_aOutputStreams.front().m_pStream;
8616         m_aMapMode	= m_aOutputStreams.front().m_aMapMode;
8617         m_aOutputStreams.pop_front();
8618     }
8619 
8620     pop();
8621     // force reemitting colors and clip region
8622     clearClipRegion();
8623     m_aCurrentPDFState.m_bClipRegion = m_aGraphicsStack.front().m_bClipRegion;
8624     m_aCurrentPDFState.m_aClipRegion = m_aGraphicsStack.front().m_aClipRegion;
8625     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8626     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8627 
8628     updateGraphicsState();
8629 
8630     return pStream;
8631 }
8632 
8633 void PDFWriterImpl::beginTransparencyGroup()
8634 {
8635     updateGraphicsState();
8636     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8637         beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8638 }
8639 
8640 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
8641 {
8642     DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8643     nTransparentPercent = nTransparentPercent % 100;
8644 
8645     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8646     {
8647         // create XObject
8648         m_aTransparentObjects.push_back( TransparencyEmit() );
8649         m_aTransparentObjects.back().m_aBoundRect	= rBoundingBox;
8650         // convert rectangle to default user space
8651         m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8652         m_aTransparentObjects.back().m_nObject		= createObject();
8653         m_aTransparentObjects.back().m_fAlpha		= (double)(100-nTransparentPercent) / 100.0;
8654         // get XObject's content stream
8655         m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8656         m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8657 
8658         OStringBuffer aObjName( 16 );
8659         aObjName.append( "Tr" );
8660         aObjName.append( m_aTransparentObjects.back().m_nObject );
8661         OString aTrName( aObjName.makeStringAndClear() );
8662         aObjName.append( "EGS" );
8663         aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8664         OString aExtName( aObjName.makeStringAndClear() );
8665 
8666         OStringBuffer aLine( 80 );
8667         // insert XObject
8668         aLine.append( "q /" );
8669         aLine.append( aExtName );
8670         aLine.append( " gs /" );
8671         aLine.append( aTrName );
8672         aLine.append( " Do Q\n" );
8673         writeBuffer( aLine.getStr(), aLine.getLength() );
8674 
8675         pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8676         pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8677     }
8678 }
8679 
8680 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask )
8681 {
8682     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8683     {
8684         // create XObject
8685         m_aTransparentObjects.push_back( TransparencyEmit() );
8686         m_aTransparentObjects.back().m_aBoundRect	= rBoundingBox;
8687         // convert rectangle to default user space
8688         m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8689         m_aTransparentObjects.back().m_nObject		= createObject();
8690         m_aTransparentObjects.back().m_fAlpha		= 0.0;
8691         // get XObject's content stream
8692         m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8693         m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8694 
8695         // draw soft mask
8696         beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8697         drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask );
8698         m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect());
8699 
8700         OStringBuffer aObjName( 16 );
8701         aObjName.append( "Tr" );
8702         aObjName.append( m_aTransparentObjects.back().m_nObject );
8703         OString aTrName( aObjName.makeStringAndClear() );
8704         aObjName.append( "EGS" );
8705         aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8706         OString aExtName( aObjName.makeStringAndClear() );
8707 
8708         OStringBuffer aLine( 80 );
8709         // insert XObject
8710         aLine.append( "q /" );
8711         aLine.append( aExtName );
8712         aLine.append( " gs /" );
8713         aLine.append( aTrName );
8714         aLine.append( " Do Q\n" );
8715         writeBuffer( aLine.getStr(), aLine.getLength() );
8716 
8717         pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8718         pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8719     }
8720 }
8721 
8722 void PDFWriterImpl::drawRectangle( const Rectangle& rRect )
8723 {
8724     MARK( "drawRectangle" );
8725 
8726     updateGraphicsState();
8727 
8728     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8729         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8730         return;
8731 
8732     OStringBuffer aLine( 40 );
8733     m_aPages.back().appendRect( rRect, aLine );
8734 
8735     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8736         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8737         aLine.append( " B*\n" );
8738     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8739         aLine.append( " S\n" );
8740     else
8741         aLine.append( " f*\n" );
8742 
8743     writeBuffer( aLine.getStr(), aLine.getLength() );
8744 }
8745 
8746 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
8747 {
8748     MARK( "drawRectangle with rounded edges" );
8749 
8750     if( !nHorzRound && !nVertRound )
8751         drawRectangle( rRect );
8752 
8753     updateGraphicsState();
8754 
8755     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8756         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8757         return;
8758 
8759     if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
8760         nHorzRound = rRect.GetWidth()/2;
8761     if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
8762         nVertRound = rRect.GetHeight()/2;
8763 
8764     Point aPoints[16];
8765     const double kappa = 0.5522847498;
8766     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
8767     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
8768 
8769     aPoints[1]  = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
8770     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8771     aPoints[2]  = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
8772     aPoints[3]  = Point( aPoints[2].X()+kx, aPoints[2].Y() );
8773 
8774     aPoints[5]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
8775     aPoints[4]  = Point( aPoints[5].X(), aPoints[5].Y()-ky );
8776     aPoints[6]  = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
8777     aPoints[7]  = Point( aPoints[6].X(), aPoints[6].Y()+ky );
8778 
8779     aPoints[9]  = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
8780     aPoints[8]  = Point( aPoints[9].X()+kx, aPoints[9].Y() );
8781     aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
8782     aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
8783 
8784     aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
8785     aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
8786     aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
8787     aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
8788 
8789 
8790     OStringBuffer aLine( 80 );
8791     m_aPages.back().appendPoint( aPoints[1], aLine );
8792     aLine.append( " m " );
8793     m_aPages.back().appendPoint( aPoints[2], aLine );
8794     aLine.append( " l " );
8795     m_aPages.back().appendPoint( aPoints[3], aLine );
8796     aLine.append( ' ' );
8797     m_aPages.back().appendPoint( aPoints[4], aLine );
8798     aLine.append( ' ' );
8799     m_aPages.back().appendPoint( aPoints[5], aLine );
8800     aLine.append( " c\n" );
8801     m_aPages.back().appendPoint( aPoints[6], aLine );
8802     aLine.append( " l " );
8803     m_aPages.back().appendPoint( aPoints[7], aLine );
8804     aLine.append( ' ' );
8805     m_aPages.back().appendPoint( aPoints[8], aLine );
8806     aLine.append( ' ' );
8807     m_aPages.back().appendPoint( aPoints[9], aLine );
8808     aLine.append( " c\n" );
8809     m_aPages.back().appendPoint( aPoints[10], aLine );
8810     aLine.append( " l " );
8811     m_aPages.back().appendPoint( aPoints[11], aLine );
8812     aLine.append( ' ' );
8813     m_aPages.back().appendPoint( aPoints[12], aLine );
8814     aLine.append( ' ' );
8815     m_aPages.back().appendPoint( aPoints[13], aLine );
8816     aLine.append( " c\n" );
8817     m_aPages.back().appendPoint( aPoints[14], aLine );
8818     aLine.append( " l " );
8819     m_aPages.back().appendPoint( aPoints[15], aLine );
8820     aLine.append( ' ' );
8821     m_aPages.back().appendPoint( aPoints[0], aLine );
8822     aLine.append( ' ' );
8823     m_aPages.back().appendPoint( aPoints[1], aLine );
8824     aLine.append( " c " );
8825 
8826     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8827         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8828         aLine.append( "b*\n" );
8829     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8830         aLine.append( "s\n" );
8831     else
8832         aLine.append( "f*\n" );
8833 
8834     writeBuffer( aLine.getStr(), aLine.getLength() );
8835 }
8836 
8837 void PDFWriterImpl::drawEllipse( const Rectangle& rRect )
8838 {
8839     MARK( "drawEllipse" );
8840 
8841     updateGraphicsState();
8842 
8843     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8844         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8845         return;
8846 
8847     Point aPoints[12];
8848     const double kappa = 0.5522847498;
8849     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
8850     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
8851 
8852     aPoints[1]  = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
8853     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8854     aPoints[2]  = Point( aPoints[1].X() + kx, aPoints[1].Y() );
8855 
8856     aPoints[4]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
8857     aPoints[3]  = Point( aPoints[4].X(), aPoints[4].Y() - ky );
8858     aPoints[5]  = Point( aPoints[4].X(), aPoints[4].Y() + ky );
8859 
8860     aPoints[7]  = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
8861     aPoints[6]  = Point( aPoints[7].X() + kx, aPoints[7].Y() );
8862     aPoints[8]  = Point( aPoints[7].X() - kx, aPoints[7].Y() );
8863 
8864     aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
8865     aPoints[9]  = Point( aPoints[10].X(), aPoints[10].Y() + ky );
8866     aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
8867 
8868     OStringBuffer aLine( 80 );
8869     m_aPages.back().appendPoint( aPoints[1], aLine );
8870     aLine.append( " m " );
8871     m_aPages.back().appendPoint( aPoints[2], aLine );
8872     aLine.append( ' ' );
8873     m_aPages.back().appendPoint( aPoints[3], aLine );
8874     aLine.append( ' ' );
8875     m_aPages.back().appendPoint( aPoints[4], aLine );
8876     aLine.append( " c\n" );
8877     m_aPages.back().appendPoint( aPoints[5], aLine );
8878     aLine.append( ' ' );
8879     m_aPages.back().appendPoint( aPoints[6], aLine );
8880     aLine.append( ' ' );
8881     m_aPages.back().appendPoint( aPoints[7], aLine );
8882     aLine.append( " c\n" );
8883     m_aPages.back().appendPoint( aPoints[8], aLine );
8884     aLine.append( ' ' );
8885     m_aPages.back().appendPoint( aPoints[9], aLine );
8886     aLine.append( ' ' );
8887     m_aPages.back().appendPoint( aPoints[10], aLine );
8888     aLine.append( " c\n" );
8889     m_aPages.back().appendPoint( aPoints[11], aLine );
8890     aLine.append( ' ' );
8891     m_aPages.back().appendPoint( aPoints[0], aLine );
8892     aLine.append( ' ' );
8893     m_aPages.back().appendPoint( aPoints[1], aLine );
8894     aLine.append( " c " );
8895 
8896     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8897         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8898         aLine.append( "b*\n" );
8899     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8900         aLine.append( "s\n" );
8901     else
8902         aLine.append( "f*\n" );
8903 
8904     writeBuffer( aLine.getStr(), aLine.getLength() );
8905 }
8906 
8907 static double calcAngle( const Rectangle& rRect, const Point& rPoint )
8908 {
8909     Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
8910                   (rRect.Top()+rRect.Bottom()+1)/2);
8911     Point aPoint = rPoint - aOrigin;
8912 
8913     double fX = (double)aPoint.X();
8914     double fY = (double)-aPoint.Y();
8915 
8916     if( rRect.GetWidth() > rRect.GetHeight() )
8917         fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
8918     else if( rRect.GetHeight() > rRect.GetWidth() )
8919         fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
8920     return atan2( fY, fX );
8921 }
8922 
8923 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
8924 {
8925     MARK( "drawArc" );
8926 
8927     updateGraphicsState();
8928 
8929     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8930         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8931         return;
8932 
8933     // calculate start and stop angles
8934     const double fStartAngle = calcAngle( rRect, rStart );
8935     double fStopAngle  = calcAngle( rRect, rStop );
8936     while( fStopAngle < fStartAngle )
8937         fStopAngle += 2.0*M_PI;
8938     const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
8939     const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
8940     const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
8941     const double halfWidth = (double)rRect.GetWidth()/2.0;
8942     const double halfHeight = (double)rRect.GetHeight()/2.0;
8943 
8944     const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
8945                          (rRect.Top()+rRect.Bottom()+1)/2 );
8946 
8947     OStringBuffer aLine( 30*nFragments );
8948     Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
8949                   -(int)(halfHeight * sin(fStartAngle) ) );
8950     aPoint += aCenter;
8951     m_aPages.back().appendPoint( aPoint, aLine );
8952     aLine.append( " m " );
8953     if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
8954     {
8955         for( int i = 0; i < nFragments; i++ )
8956         {
8957             const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
8958             const double fStopFragment = fStartFragment + fFragmentDelta;
8959             aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
8960                             -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
8961             aPoint += aCenter;
8962             m_aPages.back().appendPoint( aPoint, aLine );
8963             aLine.append( ' ' );
8964 
8965             aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
8966                             -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
8967             aPoint += aCenter;
8968             m_aPages.back().appendPoint( aPoint, aLine );
8969             aLine.append( ' ' );
8970 
8971             aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
8972                             -(int)(halfHeight * sin(fStopFragment) ) );
8973             aPoint += aCenter;
8974             m_aPages.back().appendPoint( aPoint, aLine );
8975             aLine.append( " c\n" );
8976         }
8977     }
8978     if( bWithChord || bWithPie )
8979     {
8980         if( bWithPie )
8981         {
8982             m_aPages.back().appendPoint( aCenter, aLine );
8983             aLine.append( " l " );
8984         }
8985         aLine.append( "h " );
8986     }
8987     if( ! bWithChord && ! bWithPie )
8988         aLine.append( "S\n" );
8989     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8990         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8991         aLine.append( "B*\n" );
8992     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8993         aLine.append( "S\n" );
8994     else
8995         aLine.append( "f*\n" );
8996 
8997     writeBuffer( aLine.getStr(), aLine.getLength() );
8998 }
8999 
9000 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly )
9001 {
9002     MARK( "drawPolyLine" );
9003 
9004     sal_uInt16 nPoints = rPoly.GetSize();
9005     if( nPoints < 2 )
9006         return;
9007 
9008     updateGraphicsState();
9009 
9010     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9011         return;
9012 
9013     OStringBuffer aLine( 20 * nPoints );
9014     m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
9015     aLine.append( "S\n" );
9016 
9017     writeBuffer( aLine.getStr(), aLine.getLength() );
9018 }
9019 
9020 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo )
9021 {
9022     MARK( "drawPolyLine with LineInfo" );
9023 
9024     updateGraphicsState();
9025 
9026     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9027         return;
9028 
9029     OStringBuffer aLine;
9030     aLine.append( "q " );
9031     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
9032     {
9033         writeBuffer( aLine.getStr(), aLine.getLength() );
9034         drawPolyLine( rPoly );
9035         writeBuffer( "Q\n", 2 );
9036     }
9037     else
9038     {
9039         PDFWriter::ExtLineInfo aInfo;
9040         convertLineInfoToExtLineInfo( rInfo, aInfo );
9041         drawPolyLine( rPoly, aInfo );
9042     }
9043 }
9044 
9045 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
9046 {
9047     DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" );
9048     rOut.m_fLineWidth           = rIn.GetWidth();
9049     rOut.m_fTransparency        = 0.0;
9050     rOut.m_eCap                 = PDFWriter::capButt;
9051     rOut.m_eJoin                = PDFWriter::joinMiter;
9052     rOut.m_fMiterLimit          = 10;
9053     rOut.m_aDashArray.clear();
9054 
9055     int nDashes     = rIn.GetDashCount();
9056     int nDashLen    = rIn.GetDashLen();
9057     int nDistance   = rIn.GetDistance();
9058     for( int n  = 0; n < nDashes; n++ )
9059     {
9060         rOut.m_aDashArray.push_back( nDashLen );
9061         rOut.m_aDashArray.push_back( nDistance );
9062     }
9063     int nDots       = rIn.GetDotCount();
9064     int nDotLen     = rIn.GetDotLen();
9065     for( int n  = 0; n < nDots; n++ )
9066     {
9067         rOut.m_aDashArray.push_back( nDotLen );
9068         rOut.m_aDashArray.push_back( nDistance );
9069     }
9070 }
9071 
9072 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
9073 {
9074     MARK( "drawPolyLine with ExtLineInfo" );
9075 
9076     updateGraphicsState();
9077 
9078     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9079         return;
9080 
9081     if( rInfo.m_fTransparency >= 1.0 )
9082         return;
9083 
9084     if( rInfo.m_fTransparency != 0.0 )
9085         beginTransparencyGroup();
9086 
9087     OStringBuffer aLine;
9088     aLine.append( "q " );
9089     m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
9090     aLine.append( " w" );
9091     if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader
9092     {
9093         switch( rInfo.m_eCap )
9094         {
9095             default:
9096             case PDFWriter::capButt:   aLine.append( " 0 J" );break;
9097             case PDFWriter::capRound:  aLine.append( " 1 J" );break;
9098             case PDFWriter::capSquare: aLine.append( " 2 J" );break;
9099         }
9100         switch( rInfo.m_eJoin )
9101         {
9102             default:
9103             case PDFWriter::joinMiter:
9104             {
9105                 double fLimit = rInfo.m_fMiterLimit;
9106                 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
9107                     fLimit = fLimit / rInfo.m_fLineWidth;
9108                 if( fLimit < 1.0 )
9109                     fLimit = 1.0;
9110                 aLine.append( " 0 j " );
9111                 appendDouble( fLimit, aLine );
9112                 aLine.append( " M" );
9113             }
9114             break;
9115             case PDFWriter::joinRound:  aLine.append( " 1 j" );break;
9116             case PDFWriter::joinBevel:  aLine.append( " 2 j" );break;
9117         }
9118         if( rInfo.m_aDashArray.size() > 0 )
9119         {
9120             aLine.append( " [ " );
9121             for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
9122                  it != rInfo.m_aDashArray.end(); ++it )
9123             {
9124                 m_aPages.back().appendMappedLength( *it, aLine );
9125                 aLine.append( ' ' );
9126             }
9127             aLine.append( "] 0 d" );
9128         }
9129         aLine.append( "\n" );
9130         writeBuffer( aLine.getStr(), aLine.getLength() );
9131         drawPolyLine( rPoly );
9132     }
9133     else
9134     {
9135         basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
9136         basegfx::B2DPolyPolygon aPolyPoly;
9137 
9138 		basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
9139 
9140 		// Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
9141 		// To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
9142 		// this line needs to be removed and the loop below adapted accordingly
9143 		aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
9144 
9145 		const sal_uInt32 nPolygonCount(aPolyPoly.count());
9146 
9147 		for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
9148         {
9149             aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
9150             aPoly = aPolyPoly.getB2DPolygon( nPoly );
9151 			const sal_uInt32 nPointCount(aPoly.count());
9152 
9153 			if(nPointCount)
9154 			{
9155 				const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
9156 				basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
9157 
9158 				for(sal_uInt32 a(0); a < nEdgeCount; a++)
9159 				{
9160                     if( a > 0 )
9161                         aLine.append( " " );
9162 					const sal_uInt32 nNextIndex((a + 1) % nPointCount);
9163 					const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
9164 
9165 					m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
9166 														FRound(aCurrent.getY()) ),
9167 												 aLine );
9168 					aLine.append( " m " );
9169 					m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
9170 														FRound(aNext.getY()) ),
9171 												 aLine );
9172 					aLine.append( " l" );
9173 
9174 					// prepare next edge
9175 					aCurrent = aNext;
9176 				}
9177 			}
9178         }
9179         aLine.append( " S " );
9180         writeBuffer( aLine.getStr(), aLine.getLength() );
9181     }
9182     writeBuffer( "Q\n", 2 );
9183 
9184     if( rInfo.m_fTransparency != 0.0 )
9185     {
9186         // FIXME: actually this may be incorrect with bezier polygons
9187         Rectangle aBoundRect( rPoly.GetBoundRect() );
9188         // avoid clipping with thick lines
9189         if( rInfo.m_fLineWidth > 0.0 )
9190         {
9191             sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
9192             aBoundRect.Top()    -= nLW;
9193             aBoundRect.Left()   -= nLW;
9194             aBoundRect.Right()  += nLW;
9195             aBoundRect.Bottom() += nLW;
9196         }
9197         endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) );
9198     }
9199 }
9200 
9201 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
9202 {
9203     MARK( "drawPixel" );
9204 
9205     Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
9206 
9207     if( aColor == Color( COL_TRANSPARENT ) )
9208         return;
9209 
9210     // pixels are drawn in line color, so have to set
9211     // the nonstroking color to line color
9212     Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
9213     setFillColor( aColor );
9214 
9215     updateGraphicsState();
9216 
9217     OStringBuffer aLine( 20 );
9218     m_aPages.back().appendPoint( rPoint, aLine );
9219     aLine.append( ' ' );
9220     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine );
9221     aLine.append( ' ' );
9222     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine );
9223     aLine.append( " re f\n" );
9224     writeBuffer( aLine.getStr(), aLine.getLength() );
9225 
9226     setFillColor( aOldFillColor );
9227 }
9228 
9229 void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors )
9230 {
9231     MARK( "drawPixel with Polygon" );
9232 
9233     updateGraphicsState();
9234 
9235     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors )
9236         return;
9237 
9238     sal_uInt16 nPoints = rPoints.GetSize();
9239     OStringBuffer aLine( nPoints*40 );
9240     aLine.append( "q " );
9241     if( ! pColors )
9242     {
9243         appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine );
9244         aLine.append( ' ' );
9245     }
9246 
9247     OStringBuffer aPixel(32);
9248     aPixel.append( ' ' );
9249     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel );
9250     aPixel.append( ' ' );
9251     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel );
9252     OString aPixelStr = aPixel.makeStringAndClear();
9253     for( sal_uInt16 i = 0; i < nPoints; i++ )
9254     {
9255         if( pColors )
9256         {
9257             if( pColors[i] == Color( COL_TRANSPARENT ) )
9258                 continue;
9259 
9260             appendNonStrokingColor( pColors[i], aLine );
9261             aLine.append( ' ' );
9262         }
9263         m_aPages.back().appendPoint( rPoints[i], aLine );
9264         aLine.append( aPixelStr );
9265         aLine.append( " re f\n" );
9266     }
9267     aLine.append( "Q\n" );
9268     writeBuffer( aLine.getStr(), aLine.getLength() );
9269 }
9270 
9271 class AccessReleaser
9272 {
9273     BitmapReadAccess* m_pAccess;
9274 public:
9275     AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){}
9276     ~AccessReleaser() { delete m_pAccess; }
9277 };
9278 
9279 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
9280 {
9281     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9282 
9283     bool bFlateFilter = compressStream( rObject.m_pContentStream );
9284     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
9285     sal_uLong nSize = rObject.m_pContentStream->Tell();
9286     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
9287     #if OSL_DEBUG_LEVEL > 1
9288     emitComment( "PDFWriterImpl::writeTransparentObject" );
9289     #endif
9290     OStringBuffer aLine( 512 );
9291     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9292     aLine.append( rObject.m_nObject );
9293     aLine.append( " 0 obj\n"
9294                   "<</Type/XObject\n"
9295                   "/Subtype/Form\n"
9296                   "/BBox[ " );
9297     appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
9298     aLine.append( ' ' );
9299     appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
9300     aLine.append( ' ' );
9301     appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
9302     aLine.append( ' ' );
9303     appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
9304     aLine.append( " ]\n" );
9305     if( ! rObject.m_pSoftMaskStream )
9306     {
9307         if( ! m_bIsPDF_A1 )
9308         {
9309             aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
9310         }
9311     }
9312     /* #i42884# the PDF reference recommends that each Form XObject
9313     *  should have a resource dict; alas if that is the same object
9314     *  as the one of the page it triggers an endless recursion in
9315     *  acroread 5 (6 and up have that fixed). Since we have only one
9316     *  resource dict anyway, let's use the one from the page by NOT
9317     *  emitting a Resources entry.
9318     */
9319     #if 0
9320     aLine.append( "   /Resources " );
9321     aLine.append( getResourceDictObj() );
9322     aLine.append( " 0 R\n" );
9323     #endif
9324 
9325     aLine.append( "/Length " );
9326     aLine.append( (sal_Int32)(nSize) );
9327     aLine.append( "\n" );
9328     if( bFlateFilter )
9329         aLine.append( "/Filter/FlateDecode\n" );
9330     aLine.append( ">>\n"
9331                   "stream\n" );
9332     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9333     checkAndEnableStreamEncryption( rObject.m_nObject );
9334     CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
9335     disableStreamEncryption();
9336     aLine.setLength( 0 );
9337     aLine.append( "\n"
9338                   "endstream\n"
9339                   "endobj\n\n" );
9340     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9341 
9342     // write ExtGState dict for this XObject
9343     aLine.setLength( 0 );
9344     aLine.append( rObject.m_nExtGStateObject );
9345     aLine.append( " 0 obj\n"
9346                   "<<" );
9347     if( ! rObject.m_pSoftMaskStream )
9348     {
9349 //i59651
9350         if( m_bIsPDF_A1 )
9351         {
9352             aLine.append( "/CA 1.0/ca 1.0" );
9353             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9354         }
9355         else
9356         {
9357             aLine.append(  "/CA " );
9358             appendDouble( rObject.m_fAlpha, aLine );
9359             aLine.append( "\n"
9360                           "   /ca " );
9361             appendDouble( rObject.m_fAlpha, aLine );
9362         }
9363         aLine.append( "\n" );
9364     }
9365     else
9366     {
9367         if( m_bIsPDF_A1 )
9368         {
9369             aLine.append( "/SMask/None" );
9370             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9371         }
9372         else
9373         {
9374             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
9375             sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
9376             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
9377             sal_Int32 nMaskObject = createObject();
9378             aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
9379             aLine.append( nMaskObject );
9380             aLine.append( " 0 R>>\n" );
9381 
9382             OStringBuffer aMask;
9383             aMask.append( nMaskObject );
9384             aMask.append( " 0 obj\n"
9385                           "<</Type/XObject\n"
9386                           "/Subtype/Form\n"
9387                           "/BBox[" );
9388             appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
9389             aMask.append( ' ' );
9390             appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
9391             aMask.append( ' ' );
9392             appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
9393             aMask.append( ' ' );
9394             appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
9395             aMask.append( "]\n" );
9396 
9397             /* #i42884# see above */
9398 #if 0
9399             aLine.append( "/Resources " );
9400             aMask.append( getResourceDictObj() );
9401             aMask.append( " 0 R\n" );
9402 #endif
9403 
9404             aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
9405             aMask.append( "/Length " );
9406             aMask.append( nMaskSize );
9407             aMask.append( ">>\n"
9408                           "stream\n" );
9409             CHECK_RETURN( updateObject( nMaskObject ) );
9410             checkAndEnableStreamEncryption(  nMaskObject );
9411             CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9412             CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
9413             disableStreamEncryption();
9414             aMask.setLength( 0 );
9415             aMask.append( "\nendstream\n"
9416                           "endobj\n\n" );
9417             CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9418         }
9419     }
9420     aLine.append( ">>\n"
9421                   "endobj\n\n" );
9422     CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) );
9423     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9424 
9425     return true;
9426 }
9427 
9428 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
9429 {
9430     sal_Int32 nFunctionObject = createObject();
9431     CHECK_RETURN( updateObject( nFunctionObject ) );
9432 
9433     VirtualDevice aDev;
9434     aDev.SetOutputSizePixel( rObject.m_aSize );
9435     aDev.SetMapMode( MapMode( MAP_PIXEL ) );
9436     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9437         aDev.SetDrawMode( aDev.GetDrawMode() |
9438                           ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT |
9439                             DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) );
9440     aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
9441 
9442     Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize );
9443     BitmapReadAccess* pAccess = aSample.AcquireReadAccess();
9444     AccessReleaser aReleaser( pAccess );
9445 
9446     Size aSize = aSample.GetSizePixel();
9447 
9448     sal_Int32 nStreamLengthObject = createObject();
9449     #if OSL_DEBUG_LEVEL > 1
9450     emitComment( "PDFWriterImpl::writeGradientFunction" );
9451     #endif
9452     OStringBuffer aLine( 120 );
9453     aLine.append( nFunctionObject );
9454     aLine.append( " 0 obj\n"
9455                   "<</FunctionType 0\n"
9456                   "/Domain[ 0 1 0 1 ]\n"
9457                   "/Size[ " );
9458     aLine.append( (sal_Int32)aSize.Width() );
9459     aLine.append( ' ' );
9460     aLine.append( (sal_Int32)aSize.Height() );
9461     aLine.append( " ]\n"
9462                   "/BitsPerSample 8\n"
9463                   "/Range[ 0 1 0 1 0 1 ]\n"
9464                   "/Order 3\n"
9465                   "/Length " );
9466     aLine.append( nStreamLengthObject );
9467     aLine.append( " 0 R\n"
9468 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9469                   "/Filter/FlateDecode"
9470 #endif
9471                   ">>\n"
9472                   "stream\n" );
9473     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9474 
9475     sal_uInt64 nStartStreamPos = 0;
9476     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) );
9477 
9478     checkAndEnableStreamEncryption( nFunctionObject );
9479     beginCompression();
9480     for( int y = aSize.Height()-1; y >= 0; y-- )
9481     {
9482         for( int x = 0; x < aSize.Width(); x++ )
9483         {
9484             sal_uInt8 aCol[3];
9485             BitmapColor aColor = pAccess->GetColor( y, x );
9486             aCol[0] = aColor.GetRed();
9487             aCol[1] = aColor.GetGreen();
9488             aCol[2] = aColor.GetBlue();
9489             CHECK_RETURN( writeBuffer( aCol, 3 ) );
9490         }
9491     }
9492     endCompression();
9493     disableStreamEncryption();
9494 
9495     sal_uInt64 nEndStreamPos = 0;
9496     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) );
9497 
9498     aLine.setLength( 0 );
9499     aLine.append( "\nendstream\nendobj\n\n" );
9500     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9501 
9502     // write stream length
9503     CHECK_RETURN( updateObject( nStreamLengthObject ) );
9504     aLine.setLength( 0 );
9505     aLine.append( nStreamLengthObject );
9506     aLine.append( " 0 obj\n" );
9507     aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
9508     aLine.append( "\nendobj\n\n" );
9509     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9510 
9511     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9512     aLine.setLength( 0 );
9513     aLine.append( rObject.m_nObject );
9514     aLine.append( " 0 obj\n"
9515                   "<</ShadingType 1\n"
9516                   "/ColorSpace/DeviceRGB\n"
9517                   "/AntiAlias true\n"
9518                   "/Domain[ 0 1 0 1 ]\n"
9519                   "/Matrix[ " );
9520     aLine.append( (sal_Int32)aSize.Width() );
9521     aLine.append( " 0 0 " );
9522     aLine.append( (sal_Int32)aSize.Height() );
9523     aLine.append( " 0 0 ]\n"
9524                   "/Function " );
9525     aLine.append( nFunctionObject );
9526     aLine.append( " 0 R\n"
9527                   ">>\n"
9528                   "endobj\n\n" );
9529     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9530 
9531     return true;
9532 }
9533 
9534 bool PDFWriterImpl::writeJPG( JPGEmit& rObject )
9535 {
9536     CHECK_RETURN( rObject.m_pStream );
9537     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9538 
9539     sal_Int32 nLength = 0;
9540     rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
9541     nLength = rObject.m_pStream->Tell();
9542     rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
9543 
9544     sal_Int32 nMaskObject = 0;
9545     if( !!rObject.m_aMask )
9546     {
9547         if( rObject.m_aMask.GetBitCount() == 1 ||
9548             ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651
9549             )
9550         {
9551             nMaskObject = createObject();
9552         }
9553         else if( m_bIsPDF_A1 )
9554             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9555         else if( m_aContext.Version < PDFWriter::PDF_1_4 )
9556             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
9557 
9558     }
9559     #if OSL_DEBUG_LEVEL > 1
9560     emitComment( "PDFWriterImpl::writeJPG" );
9561     #endif
9562 
9563     OStringBuffer aLine(200);
9564     aLine.append( rObject.m_nObject );
9565     aLine.append( " 0 obj\n"
9566                   "<</Type/XObject/Subtype/Image/Width " );
9567     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
9568     aLine.append( " /Height " );
9569     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
9570     aLine.append( " /BitsPerComponent 8 " );
9571     if( rObject.m_bTrueColor )
9572         aLine.append( "/ColorSpace/DeviceRGB" );
9573     else
9574         aLine.append( "/ColorSpace/DeviceGray" );
9575     aLine.append( "/Filter/DCTDecode/Length " );
9576     aLine.append( nLength );
9577     if( nMaskObject )
9578     {
9579         aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
9580         aLine.append( nMaskObject );
9581         aLine.append( " 0 R " );
9582     }
9583     aLine.append( ">>\nstream\n" );
9584     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9585 
9586     checkAndEnableStreamEncryption( rObject.m_nObject );
9587     CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
9588     disableStreamEncryption();
9589 
9590     aLine.setLength( 0 );
9591     aLine.append( "\nendstream\nendobj\n\n" );
9592     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9593 
9594     if( nMaskObject )
9595     {
9596         BitmapEmit aEmit;
9597         aEmit.m_nObject = nMaskObject;
9598         if( rObject.m_aMask.GetBitCount() == 1 )
9599             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
9600         else if( rObject.m_aMask.GetBitCount() == 8 )
9601             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
9602         writeBitmapObject( aEmit, true );
9603     }
9604 
9605     return true;
9606 }
9607 
9608 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
9609 {
9610     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9611 
9612     Bitmap	aBitmap;
9613     Color	aTransparentColor( COL_TRANSPARENT );
9614     bool	bWriteMask = false;
9615     if( ! bMask )
9616     {
9617         aBitmap = rObject.m_aBitmap.GetBitmap();
9618         if( rObject.m_aBitmap.IsAlpha() )
9619         {
9620             if( m_aContext.Version >= PDFWriter::PDF_1_4 )
9621                 bWriteMask = true;
9622             // else draw without alpha channel
9623         }
9624         else
9625         {
9626             switch( rObject.m_aBitmap.GetTransparentType() )
9627             {
9628                 case TRANSPARENT_NONE:
9629                     // comes from drawMask function
9630                     if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask )
9631                         bMask = true;
9632                     break;
9633                 case TRANSPARENT_COLOR:
9634                     aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
9635                     break;
9636                 case TRANSPARENT_BITMAP:
9637                     bWriteMask = true;
9638                     break;
9639             }
9640         }
9641     }
9642     else
9643     {
9644         if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
9645         {
9646             aBitmap = rObject.m_aBitmap.GetMask();
9647             aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
9648             DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
9649         }
9650         else if( aBitmap.GetBitCount() != 8 )
9651         {
9652             aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap();
9653             aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
9654             DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" );
9655         }
9656     }
9657 
9658     BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();
9659     AccessReleaser aReleaser( pAccess );
9660 
9661     bool bTrueColor;
9662     sal_Int32 nBitsPerComponent;
9663     switch( aBitmap.GetBitCount() )
9664     {
9665         case 1:
9666         case 2:
9667         case 4:
9668         case 8:
9669             bTrueColor = false;
9670             nBitsPerComponent = aBitmap.GetBitCount();
9671             break;
9672         default:
9673             bTrueColor = true;
9674             nBitsPerComponent = 8;
9675             break;
9676     }
9677 
9678     sal_Int32 nStreamLengthObject	= createObject();
9679     sal_Int32 nMaskObject			= 0;
9680 
9681     #if OSL_DEBUG_LEVEL > 1
9682     emitComment( "PDFWriterImpl::writeBitmapObject" );
9683     #endif
9684     OStringBuffer aLine(1024);
9685     aLine.append( rObject.m_nObject );
9686     aLine.append( " 0 obj\n"
9687                   "<</Type/XObject/Subtype/Image/Width " );
9688     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9689     aLine.append( "/Height " );
9690     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
9691     aLine.append( "/BitsPerComponent " );
9692     aLine.append( nBitsPerComponent );
9693     aLine.append( "/Length " );
9694     aLine.append( nStreamLengthObject );
9695     aLine.append( " 0 R\n" );
9696 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9697     if( nBitsPerComponent != 1 )
9698     {
9699         aLine.append( "/Filter/FlateDecode" );
9700     }
9701     else
9702     {
9703         aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
9704         aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9705         aLine.append( ">>\n" );
9706     }
9707 #endif
9708     if( ! bMask )
9709     {
9710         aLine.append( "/ColorSpace" );
9711         if( bTrueColor )
9712             aLine.append( "/DeviceRGB\n" );
9713         else if( aBitmap.HasGreyPalette() )
9714         {
9715             aLine.append( "/DeviceGray\n" );
9716             if( aBitmap.GetBitCount() == 1 )
9717             {
9718                 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
9719                 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9720                 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9721                 if( nBlackIndex == 1 )
9722                     aLine.append( "/Decode[1 0]\n" );
9723             }
9724         }
9725         else
9726         {
9727             aLine.append( "[ /Indexed/DeviceRGB " );
9728             aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
9729             aLine.append( "\n<" );
9730 			if( m_aContext.Encryption.Encrypt() )
9731 			{
9732 				enableStringEncryption( rObject.m_nObject );
9733 				//check encryption buffer size
9734 				if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
9735 				{
9736 					int	nChar = 0;
9737 					//fill the encryption buffer
9738 					for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9739 					{
9740 						const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9741 						m_pEncryptionBuffer[nChar++] = rColor.GetRed();
9742 						m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
9743 						m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
9744 					}
9745 					//encrypt the colorspace lookup table
9746 					rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
9747 					//now queue the data for output
9748                     nChar = 0;
9749 					for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9750 					{
9751 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9752 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9753 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9754 					}
9755 				}
9756 			}
9757 			else //no encryption requested (PDF/A-1a program flow drops here)
9758 			{
9759 				for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9760 				{
9761 					const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9762 					appendHex( rColor.GetRed(), aLine );
9763 					appendHex( rColor.GetGreen(), aLine );
9764 					appendHex( rColor.GetBlue(), aLine );
9765 				}
9766 			}
9767             aLine.append( ">\n]\n" );
9768         }
9769     }
9770     else
9771     {
9772         if( aBitmap.GetBitCount() == 1 )
9773         {
9774             aLine.append( "/ImageMask true\n" );
9775             sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9776             DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9777             if( nBlackIndex )
9778                 aLine.append( "/Decode[ 1 0 ]\n" );
9779             else
9780                 aLine.append( "/Decode[ 0 1 ]\n" );
9781         }
9782         else if( aBitmap.GetBitCount() == 8 )
9783         {
9784             aLine.append( "/ColorSpace/DeviceGray\n"
9785                           "/Decode [ 1 0 ]\n" );
9786         }
9787     }
9788 
9789     if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651
9790     {
9791         if( bWriteMask )
9792         {
9793             nMaskObject = createObject();
9794             if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 )
9795                 aLine.append( "/SMask " );
9796             else
9797                 aLine.append( "/Mask " );
9798             aLine.append( nMaskObject );
9799             aLine.append( " 0 R\n" );
9800         }
9801         else if( aTransparentColor != Color( COL_TRANSPARENT ) )
9802         {
9803             aLine.append( "/Mask[ " );
9804             if( bTrueColor )
9805             {
9806                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9807                 aLine.append( ' ' );
9808                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9809                 aLine.append( ' ' );
9810                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9811                 aLine.append( ' ' );
9812                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9813                 aLine.append( ' ' );
9814                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9815                 aLine.append( ' ' );
9816                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9817             }
9818             else
9819             {
9820                 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
9821                 aLine.append( nIndex );
9822             }
9823             aLine.append( " ]\n" );
9824         }
9825     }
9826     else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) )
9827         m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9828 
9829     aLine.append( ">>\n"
9830                   "stream\n" );
9831     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9832     sal_uInt64 nStartPos = 0;
9833     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) );
9834 
9835     checkAndEnableStreamEncryption( rObject.m_nObject );
9836 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9837     if( nBitsPerComponent == 1 )
9838     {
9839         writeG4Stream( pAccess );
9840     }
9841     else
9842 #endif
9843     {
9844         beginCompression();
9845         if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
9846         {
9847             const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U );
9848 
9849             for( int i = 0; i < pAccess->Height(); i++ )
9850             {
9851                 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
9852             }
9853         }
9854         else
9855         {
9856             const int nScanLineBytes = pAccess->Width()*3;
9857             boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] );
9858             for( int y = 0; y < pAccess->Height(); y++ )
9859             {
9860                 for( int x = 0; x < pAccess->Width(); x++ )
9861                 {
9862                     BitmapColor aColor = pAccess->GetColor( y, x );
9863                     pCol[3*x+0] = aColor.GetRed();
9864                     pCol[3*x+1] = aColor.GetGreen();
9865                     pCol[3*x+2] = aColor.GetBlue();
9866                 }
9867                 CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) );
9868             }
9869         }
9870         endCompression();
9871     }
9872     disableStreamEncryption();
9873 
9874     sal_uInt64 nEndPos = 0;
9875     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) );
9876     aLine.setLength( 0 );
9877     aLine.append( "\nendstream\nendobj\n\n" );
9878     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9879     CHECK_RETURN( updateObject( nStreamLengthObject ) );
9880     aLine.setLength( 0 );
9881     aLine.append( nStreamLengthObject );
9882     aLine.append( " 0 obj\n" );
9883     aLine.append( (sal_Int64)(nEndPos-nStartPos) );
9884     aLine.append( "\nendobj\n\n" );
9885     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9886 
9887     if( nMaskObject )
9888     {
9889         BitmapEmit aEmit;
9890         aEmit.m_nObject				= nMaskObject;
9891         aEmit.m_aBitmap				= rObject.m_aBitmap;
9892         return writeBitmapObject( aEmit, true );
9893     }
9894 
9895     return true;
9896 }
9897 
9898 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask )
9899 {
9900     MARK( "drawJPGBitmap" );
9901 
9902     OStringBuffer aLine( 80 );
9903     updateGraphicsState();
9904 
9905     // #i40055# sanity check
9906     if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
9907         return;
9908     if( ! (rSizePixel.Width() && rSizePixel.Height()) )
9909         return;
9910 
9911     rDCTData.Seek( 0 );
9912     if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9913     {
9914         // need to convert to grayscale;
9915         // load stream to bitmap and draw the bitmap instead
9916         Graphic aGraphic;
9917         GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG );
9918         Bitmap aBmp( aGraphic.GetBitmap() );
9919         if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() )
9920         {
9921             BitmapEx aBmpEx( aBmp, rMask );
9922             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx );
9923         }
9924         else
9925             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp );
9926         return;
9927     }
9928 
9929     SvMemoryStream* pStream = new SvMemoryStream;
9930     *pStream << rDCTData;
9931     pStream->Seek( STREAM_SEEK_TO_END );
9932 
9933     BitmapID aID;
9934     aID.m_aPixelSize	= rSizePixel;
9935     aID.m_nSize			= pStream->Tell();
9936     pStream->Seek( STREAM_SEEK_TO_BEGIN );
9937     aID.m_nChecksum		= rtl_crc32( 0, pStream->GetData(), aID.m_nSize );
9938     if( ! rMask.IsEmpty() )
9939         aID.m_nMaskChecksum	= rMask.GetChecksum();
9940 
9941     std::list< JPGEmit >::const_iterator it;
9942     for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
9943         ;
9944     if( it == m_aJPGs.end() )
9945     {
9946         m_aJPGs.push_front( JPGEmit() );
9947         JPGEmit& rEmit = m_aJPGs.front();
9948         rEmit.m_nObject		= createObject();
9949         rEmit.m_aID			= aID;
9950         rEmit.m_pStream		= pStream;
9951         rEmit.m_bTrueColor  = bIsTrueColor;
9952         if( !! rMask && rMask.GetSizePixel() == rSizePixel )
9953             rEmit.m_aMask	= rMask;
9954 
9955         it = m_aJPGs.begin();
9956     }
9957     else
9958         delete pStream;
9959 
9960     aLine.append( "q " );
9961     sal_Int32 nCheckWidth = 0;
9962     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth );
9963     aLine.append( " 0 0 " );
9964     sal_Int32 nCheckHeight = 0;
9965     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight );
9966     aLine.append( ' ' );
9967     m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
9968     aLine.append( " cm\n/Im" );
9969     aLine.append( it->m_nObject );
9970     aLine.append( " Do Q\n" );
9971     if( nCheckWidth == 0 || nCheckHeight == 0 )
9972     {
9973         // #i97512# avoid invalid current matrix
9974         aLine.setLength( 0 );
9975         aLine.append( "\n%jpeg image /Im" );
9976         aLine.append( it->m_nObject );
9977         aLine.append( " scaled to zero size, omitted\n" );
9978     }
9979     writeBuffer( aLine.getStr(), aLine.getLength() );
9980 
9981     OStringBuffer aObjName( 16 );
9982     aObjName.append( "Im" );
9983     aObjName.append( it->m_nObject );
9984     pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
9985 
9986 }
9987 
9988 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
9989 {
9990     OStringBuffer aLine( 80 );
9991     updateGraphicsState();
9992 
9993     aLine.append( "q " );
9994     if( rFillColor != Color( COL_TRANSPARENT ) )
9995     {
9996         appendNonStrokingColor( rFillColor, aLine );
9997         aLine.append( ' ' );
9998     }
9999     sal_Int32 nCheckWidth = 0;
10000     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth );
10001     aLine.append( " 0 0 " );
10002     sal_Int32 nCheckHeight = 0;
10003     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight );
10004     aLine.append( ' ' );
10005     m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
10006     aLine.append( " cm\n/Im" );
10007     aLine.append( rBitmap.m_nObject );
10008     aLine.append( " Do Q\n" );
10009     if( nCheckWidth == 0 || nCheckHeight == 0 )
10010     {
10011         // #i97512# avoid invalid current matrix
10012         aLine.setLength( 0 );
10013         aLine.append( "\n%bitmap image /Im" );
10014         aLine.append( rBitmap.m_nObject );
10015         aLine.append( " scaled to zero size, omitted\n" );
10016     }
10017     writeBuffer( aLine.getStr(), aLine.getLength() );
10018 }
10019 
10020 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask )
10021 {
10022     BitmapEx aBitmap( i_rBitmap );
10023     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
10024     {
10025         BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS;
10026         int nDepth = aBitmap.GetBitmap().GetBitCount();
10027         if( nDepth <= 4 )
10028             eConv = BMP_CONVERSION_4BIT_GREYS;
10029         if( nDepth > 1 )
10030             aBitmap.Convert( eConv );
10031     }
10032     BitmapID aID;
10033     aID.m_aPixelSize		= aBitmap.GetSizePixel();
10034     aID.m_nSize				= aBitmap.GetBitCount();
10035     aID.m_nChecksum			= aBitmap.GetBitmap().GetChecksum();
10036     aID.m_nMaskChecksum		= 0;
10037     if( aBitmap.IsAlpha() )
10038         aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum();
10039     else
10040     {
10041         Bitmap aMask = aBitmap.GetMask();
10042         if( ! aMask.IsEmpty() )
10043             aID.m_nMaskChecksum = aMask.GetChecksum();
10044     }
10045     std::list< BitmapEmit >::const_iterator it;
10046     for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
10047     {
10048         if( aID == it->m_aID )
10049             break;
10050     }
10051     if( it == m_aBitmaps.end() )
10052     {
10053         m_aBitmaps.push_front( BitmapEmit() );
10054         m_aBitmaps.front().m_aID		= aID;
10055         m_aBitmaps.front().m_aBitmap	= aBitmap;
10056         m_aBitmaps.front().m_nObject	= createObject();
10057         m_aBitmaps.front().m_bDrawMask	= bDrawMask;
10058         it = m_aBitmaps.begin();
10059     }
10060 
10061     OStringBuffer aObjName( 16 );
10062     aObjName.append( "Im" );
10063     aObjName.append( it->m_nObject );
10064     pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10065 
10066     return *it;
10067 }
10068 
10069 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap )
10070 {
10071     MARK( "drawBitmap (Bitmap)" );
10072 
10073     // #i40055# sanity check
10074     if( ! (rDestSize.Width() && rDestSize.Height()) )
10075         return;
10076 
10077     const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) );
10078     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10079 }
10080 
10081 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
10082 {
10083     MARK( "drawBitmap (BitmapEx)" );
10084 
10085     // #i40055# sanity check
10086     if( ! (rDestSize.Width() && rDestSize.Height()) )
10087         return;
10088 
10089     const BitmapEmit& rEmit = createBitmapEmit( rBitmap );
10090     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10091 }
10092 
10093 void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor )
10094 {
10095     MARK( "drawMask" );
10096 
10097     // #i40055# sanity check
10098     if( ! (rDestSize.Width() && rDestSize.Height()) )
10099         return;
10100 
10101     Bitmap aBitmap( rBitmap );
10102     if( aBitmap.GetBitCount() > 1 )
10103         aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
10104     DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
10105 
10106     const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true );
10107     drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor );
10108 }
10109 
10110 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
10111 {
10112     Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10113                                MapMode( MAP_POINT ),
10114                                getReferenceDevice(),
10115                                rSize ) );
10116     // check if we already have this gradient
10117     std::list<GradientEmit>::iterator it;
10118     // rounding to point will generally lose some pixels
10119     // round up to point boundary
10120     aPtSize.Width()++;
10121     aPtSize.Height()++;
10122     for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
10123     {
10124         if( it->m_aGradient == rGradient )
10125         {
10126             if( it->m_aSize == aPtSize )
10127                 break;
10128         }
10129     }
10130     if( it == m_aGradients.end() )
10131     {
10132         m_aGradients.push_front( GradientEmit() );
10133         m_aGradients.front().m_aGradient	= rGradient;
10134         m_aGradients.front().m_nObject	    = createObject();
10135         m_aGradients.front().m_aSize		= aPtSize;
10136         it = m_aGradients.begin();
10137     }
10138 
10139     OStringBuffer aObjName( 16 );
10140     aObjName.append( 'P' );
10141     aObjName.append( it->m_nObject );
10142     pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
10143 
10144     return it->m_nObject;
10145 }
10146 
10147 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient )
10148 {
10149     MARK( "drawGradient (Rectangle)" );
10150 
10151     if( m_aContext.Version == PDFWriter::PDF_1_2 )
10152     {
10153         drawRectangle( rRect );
10154         return;
10155     }
10156 
10157     sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
10158 
10159     Point aTranslate( rRect.BottomLeft() );
10160     aTranslate += Point( 0, 1 );
10161 
10162     updateGraphicsState();
10163 
10164     OStringBuffer aLine( 80 );
10165     aLine.append( "q 1 0 0 1 " );
10166     m_aPages.back().appendPoint( aTranslate, aLine );
10167     aLine.append( " cm " );
10168     // if a stroke is appended reset the clip region before stroke
10169     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10170         aLine.append( "q " );
10171     aLine.append( "0 0 " );
10172     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10173     aLine.append( ' ' );
10174     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10175     aLine.append( " re W n\n" );
10176 
10177     aLine.append( "/P" );
10178     aLine.append( nGradient );
10179     aLine.append( " sh " );
10180     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10181     {
10182         aLine.append( "Q 0 0 " );
10183         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10184         aLine.append( ' ' );
10185         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10186         aLine.append( " re S " );
10187     }
10188     aLine.append( "Q\n" );
10189     writeBuffer( aLine.getStr(), aLine.getLength() );
10190 }
10191 
10192 void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
10193 {
10194     MARK( "drawGradient (PolyPolygon)" );
10195 
10196     if( m_aContext.Version == PDFWriter::PDF_1_2 )
10197     {
10198         drawPolyPolygon( rPolyPoly );
10199         return;
10200     }
10201 
10202     Rectangle aBoundRect = rPolyPoly.GetBoundRect();
10203     sal_Int32 nGradient = createGradient( rGradient, aBoundRect.GetSize() );
10204 
10205     updateGraphicsState();
10206 
10207     Point aTranslate = aBoundRect.BottomLeft();
10208     int nPolygons = rPolyPoly.Count();
10209 
10210     OStringBuffer aLine( 80*nPolygons );
10211     aLine.append( "q " );
10212     // set PolyPolygon as clip path
10213     m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
10214     aLine.append( "W* n\n" );
10215     aLine.append( "1 0 0 1 " );
10216     m_aPages.back().appendPoint( aTranslate, aLine );
10217     aLine.append( " cm\n" );
10218     aLine.append( "/P" );
10219     aLine.append( nGradient );
10220     aLine.append( " sh Q\n" );
10221     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10222     {
10223         // and draw the surrounding path
10224         m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
10225         aLine.append( "S\n" );
10226     }
10227     writeBuffer( aLine.getStr(), aLine.getLength() );
10228 }
10229 
10230 void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
10231 {
10232     MARK( "drawHatch" );
10233 
10234     updateGraphicsState();
10235 
10236 	if( rPolyPoly.Count() )
10237 	{
10238 		PolyPolygon		aPolyPoly( rPolyPoly );
10239 
10240 		aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
10241 		push( PUSH_LINECOLOR );
10242 		setLineColor( rHatch.GetColor() );
10243 		getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, sal_False );
10244 		pop();
10245 	}
10246 }
10247 
10248 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall )
10249 {
10250     MARK( "drawWallpaper" );
10251 
10252     bool bDrawColor			= false;
10253     bool bDrawGradient		= false;
10254     bool bDrawBitmap		= false;
10255 
10256     BitmapEx aBitmap;
10257     Point aBmpPos = rRect.TopLeft();
10258     Size aBmpSize;
10259     if( rWall.IsBitmap() )
10260     {
10261         aBitmap = rWall.GetBitmap();
10262 		aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
10263                                 getMapMode(),
10264                                 getReferenceDevice(),
10265                                 aBitmap.GetPrefSize() );
10266         Rectangle aRect( rRect );
10267         if( rWall.IsRect() )
10268         {
10269             aRect = rWall.GetRect();
10270             aBmpPos = aRect.TopLeft();
10271             aBmpSize = aRect.GetSize();
10272         }
10273         if( rWall.GetStyle() != WALLPAPER_SCALE )
10274         {
10275             if( rWall.GetStyle() != WALLPAPER_TILE )
10276             {
10277                 bDrawBitmap		= true;
10278                 if( rWall.IsGradient() )
10279                     bDrawGradient = true;
10280                 else
10281                     bDrawColor = true;
10282                 switch( rWall.GetStyle() )
10283                 {
10284                     case WALLPAPER_TOPLEFT:
10285                         break;
10286                     case WALLPAPER_TOP:
10287                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10288                         break;
10289                     case WALLPAPER_LEFT:
10290                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10291                         break;
10292                     case WALLPAPER_TOPRIGHT:
10293                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10294                         break;
10295                     case WALLPAPER_CENTER:
10296                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10297                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10298                         break;
10299                     case WALLPAPER_RIGHT:
10300                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10301                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10302                         break;
10303                     case WALLPAPER_BOTTOMLEFT:
10304                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10305                         break;
10306                     case WALLPAPER_BOTTOM:
10307                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10308                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10309                         break;
10310                     case WALLPAPER_BOTTOMRIGHT:
10311                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10312                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10313                         break;
10314                     default: ;
10315                 }
10316             }
10317             else
10318             {
10319                 // push the bitmap
10320                 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) );
10321 
10322                 // convert to page coordinates; this needs to be done here
10323                 // since the emit does not know the page anymore
10324                 Rectangle aConvertRect( aBmpPos, aBmpSize );
10325                 m_aPages.back().convertRect( aConvertRect );
10326 
10327                 OStringBuffer aNameBuf(16);
10328                 aNameBuf.append( "Im" );
10329                 aNameBuf.append( rEmit.m_nObject );
10330                 OString aImageName( aNameBuf.makeStringAndClear() );
10331 
10332                 // push the pattern
10333                 OStringBuffer aTilingStream( 32 );
10334                 appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
10335                 aTilingStream.append( " 0 0 " );
10336                 appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
10337                 aTilingStream.append( " 0 0 cm\n/" );
10338                 aTilingStream.append( aImageName );
10339                 aTilingStream.append( " Do\n" );
10340 
10341                 m_aTilings.push_back( TilingEmit() );
10342                 m_aTilings.back().m_nObject			= createObject();
10343                 m_aTilings.back().m_aRectangle		= Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
10344                 m_aTilings.back().m_pTilingStream   = new SvMemoryStream();
10345                 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() );
10346                 // phase the tiling so wallpaper begins on upper left
10347                 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
10348                 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
10349                 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
10350 
10351                 updateGraphicsState();
10352 
10353                 OStringBuffer aObjName( 16 );
10354                 aObjName.append( 'P' );
10355                 aObjName.append( m_aTilings.back().m_nObject );
10356                 OString aPatternName( aObjName.makeStringAndClear() );
10357                 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
10358 
10359                 // fill a rRect with the pattern
10360                 OStringBuffer aLine( 100 );
10361                 aLine.append( "q /Pattern cs /" );
10362                 aLine.append( aPatternName );
10363                 aLine.append( " scn " );
10364                 m_aPages.back().appendRect( rRect, aLine );
10365                 aLine.append( " f Q\n" );
10366                 writeBuffer( aLine.getStr(), aLine.getLength() );
10367             }
10368         }
10369         else
10370         {
10371             aBmpPos		= aRect.TopLeft();
10372             aBmpSize	= aRect.GetSize();
10373             bDrawBitmap	= true;
10374         }
10375 
10376         if( aBitmap.IsTransparent() )
10377         {
10378             if( rWall.IsGradient() )
10379                 bDrawGradient = true;
10380             else
10381                 bDrawColor = true;
10382         }
10383     }
10384     else if( rWall.IsGradient() )
10385         bDrawGradient = true;
10386     else
10387         bDrawColor = true;
10388 
10389     if( bDrawGradient )
10390     {
10391         drawGradient( rRect, rWall.GetGradient() );
10392     }
10393     if( bDrawColor )
10394     {
10395         Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
10396         Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
10397         setLineColor( Color( COL_TRANSPARENT ) );
10398         setFillColor( rWall.GetColor() );
10399         drawRectangle( rRect );
10400         setLineColor( aOldLineColor );
10401         setFillColor( aOldFillColor );
10402     }
10403     if( bDrawBitmap )
10404     {
10405         // set temporary clip region since aBmpPos and aBmpSize
10406         // may be outside rRect
10407         OStringBuffer aLine( 20 );
10408         aLine.append( "q " );
10409         m_aPages.back().appendRect( rRect, aLine );
10410         aLine.append( " W n\n" );
10411         writeBuffer( aLine.getStr(), aLine.getLength() );
10412         drawBitmap( aBmpPos, aBmpSize, aBitmap );
10413         writeBuffer( "Q\n", 2 );
10414     }
10415 }
10416 
10417 void PDFWriterImpl::beginPattern( const Rectangle& rCellRect )
10418 {
10419     beginRedirect( new SvMemoryStream(), rCellRect );
10420 }
10421 
10422 sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform )
10423 {
10424     Rectangle aConvertRect( getRedirectTargetRect() );
10425     DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" );
10426 
10427     // get scaling between current mapmode and PDF output
10428     Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) );
10429     double fSX = (double(aScaling.Width()) / 10000.0);
10430     double fSY = (double(aScaling.Height()) / 10000.0);
10431 
10432     // transform translation part of matrix
10433     Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] );
10434     aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation );
10435 
10436     sal_Int32 nTilingId = m_aTilings.size();
10437     m_aTilings.push_back( TilingEmit() );
10438     TilingEmit& rTile = m_aTilings.back();
10439     rTile.m_nObject         = createObject();
10440     rTile.m_aResources      = m_aOutputStreams.front().m_aResourceDict;
10441     rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX;
10442     rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY;
10443     rTile.m_aTransform.matrix[2] = aTranslation.Width();
10444     rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX;
10445     rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY;
10446     rTile.m_aTransform.matrix[5] = -aTranslation.Height();
10447     // caution: endRedirect pops the stream, so do this last
10448     rTile.m_pTilingStream   = dynamic_cast<SvMemoryStream*>(endRedirect());
10449     // FIXME: bound rect will not work with rotated matrix
10450     rTile.m_aRectangle      = Rectangle( Point(0,0), aConvertRect.GetSize() );
10451     rTile.m_aCellSize       = aConvertRect.GetSize();
10452 
10453     OStringBuffer aObjName( 16 );
10454     aObjName.append( 'P' );
10455     aObjName.append( rTile.m_nObject );
10456     pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject );
10457     return nTilingId;
10458 }
10459 
10460 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill )
10461 {
10462     if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() )
10463         return;
10464 
10465     m_aPages.back().endStream();
10466     sal_Int32 nXObject = createObject();
10467     OStringBuffer aNameBuf( 16 );
10468     aNameBuf.append( "Pol" );
10469     aNameBuf.append( nXObject );
10470     OString aObjName( aNameBuf.makeStringAndClear() );
10471     Rectangle aObjRect;
10472     if( updateObject( nXObject ) )
10473     {
10474         // get bounding rect of object
10475         PolyPolygon aSubDiv;
10476         rPolyPoly.AdaptiveSubdivide( aSubDiv );
10477         aObjRect = aSubDiv.GetBoundRect();
10478         Rectangle aConvObjRect( aObjRect );
10479         m_aPages.back().convertRect( aConvObjRect );
10480 
10481         // move polypolygon to bottom left of page
10482         PolyPolygon aLocalPath( rPolyPoly );
10483         sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72;
10484         sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72;
10485         Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode );
10486         sal_Int32 nXOff = aObjRect.Left();
10487         sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom();
10488         aLocalPath.Move( -nXOff, nYOff );
10489 
10490         // prepare XObject's content stream
10491         OStringBuffer aStream( 512 );
10492         aStream.append( "/Pattern cs /P" );
10493         aStream.append( m_aTilings[ nPattern ].m_nObject );
10494         aStream.append( " scn\n" );
10495         m_aPages.back().appendPolyPolygon( aLocalPath, aStream );
10496         aStream.append( bEOFill ? "f*" : "f" );
10497         SvMemoryStream aMemStream( aStream.getLength() );
10498         aMemStream.Write( aStream.getStr(), aStream.getLength() );
10499         bool bDeflate = compressStream( &aMemStream );
10500         aMemStream.Seek( STREAM_SEEK_TO_END );
10501         sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell();
10502         aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
10503 
10504         // add new XObject to global resource dict
10505         m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject;
10506 
10507         // write XObject
10508         OStringBuffer aLine( 512 );
10509         aLine.append( nXObject );
10510         aLine.append( " 0 obj\n"
10511                       "<</Type/XObject/Subtype/Form/BBox[0 0 " );
10512         appendFixedInt( aConvObjRect.GetWidth(), aLine );
10513         aLine.append( ' ' );
10514         appendFixedInt( aConvObjRect.GetHeight(), aLine );
10515         aLine.append( "]/Length " );
10516         aLine.append( nStreamLen );
10517         if( bDeflate )
10518             aLine.append( "/Filter/FlateDecode" );
10519         aLine.append( ">>\n"
10520                       "stream\n" );
10521         writeBuffer( aLine.getStr(), aLine.getLength() );
10522         checkAndEnableStreamEncryption( nXObject );
10523         writeBuffer( aMemStream.GetData(), nStreamLen );
10524         disableStreamEncryption();
10525         writeBuffer( "\nendstream\nendobj\n\n", 19 );
10526     }
10527     m_aPages.back().beginStream();
10528     OStringBuffer aLine( 80 );
10529     aLine.append( "q 1 0 0 1 " );
10530     m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine );
10531     aLine.append( " cm/" );
10532     aLine.append( aObjName );
10533     aLine.append( " Do Q\n" );
10534     writeBuffer( aLine.getStr(), aLine.getLength() );
10535 }
10536 
10537 void PDFWriterImpl::updateGraphicsState()
10538 {
10539     OStringBuffer aLine( 256 );
10540     GraphicsState& rNewState = m_aGraphicsStack.front();
10541     // first set clip region since it might invalidate everything else
10542 
10543     if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) )
10544     {
10545         rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion;
10546 
10547         if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion ||
10548             ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) )
10549         {
10550             if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() )
10551             {
10552                 aLine.append( "Q " );
10553                 // invalidate everything but the clip region
10554                 m_aCurrentPDFState = GraphicsState();
10555                 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion);
10556             }
10557             if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() )
10558             {
10559                 // clip region is always stored in private PDF mapmode
10560                 MapMode aNewMapMode = rNewState.m_aMapMode;
10561                 rNewState.m_aMapMode = m_aMapMode;
10562                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10563                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10564 
10565                 aLine.append( "q " );
10566                 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine );
10567                 aLine.append( "W* n\n" );
10568                 rNewState.m_aMapMode = aNewMapMode;
10569                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10570                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10571             }
10572         }
10573     }
10574 
10575     if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) )
10576     {
10577         rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode;
10578         getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10579     }
10580 
10581     if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) )
10582     {
10583         rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont;
10584         getReferenceDevice()->SetFont( rNewState.m_aFont );
10585         getReferenceDevice()->ImplNewFont();
10586     }
10587 
10588     if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) )
10589     {
10590         rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode;
10591         getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
10592     }
10593 
10594     if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) )
10595     {
10596         rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage;
10597         getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
10598     }
10599 
10600     if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) )
10601     {
10602         rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor;
10603         if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
10604             rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
10605         {
10606             appendStrokingColor( rNewState.m_aLineColor, aLine );
10607             aLine.append( "\n" );
10608         }
10609     }
10610 
10611     if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) )
10612     {
10613         rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor;
10614         if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
10615             rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
10616         {
10617             appendNonStrokingColor( rNewState.m_aFillColor, aLine );
10618             aLine.append( "\n" );
10619         }
10620     }
10621 
10622     if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) )
10623     {
10624         rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent;
10625         if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent )
10626         {
10627             // TODO: switch extended graphicsstate
10628         }
10629     }
10630 
10631     // everything is up to date now
10632     m_aCurrentPDFState = m_aGraphicsStack.front();
10633     if( aLine.getLength() )
10634         writeBuffer( aLine.getStr(), aLine.getLength() );
10635 }
10636 
10637 /* #i47544# imitate OutputDevice behaviour:
10638 *  if a font with a nontransparent color is set, it overwrites the current
10639 *  text color. OTOH setting the text color will overwrite the color of the font.
10640 */
10641 void PDFWriterImpl::setFont( const Font& rFont )
10642 {
10643     Color aColor = rFont.GetColor();
10644     if( aColor == Color( COL_TRANSPARENT ) )
10645         aColor = m_aGraphicsStack.front().m_aFont.GetColor();
10646     m_aGraphicsStack.front().m_aFont = rFont;
10647     m_aGraphicsStack.front().m_aFont.SetColor( aColor );
10648     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont;
10649 }
10650 
10651 void PDFWriterImpl::push( sal_uInt16 nFlags )
10652 {
10653     OSL_ENSURE( m_aGraphicsStack.size() > 0, "invalid graphics stack" );
10654     m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
10655     m_aGraphicsStack.front().m_nFlags = nFlags;
10656 }
10657 
10658 void PDFWriterImpl::pop()
10659 {
10660     OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" );
10661     if( m_aGraphicsStack.size() < 2 )
10662         return;
10663 
10664     GraphicsState aState = m_aGraphicsStack.front();
10665     m_aGraphicsStack.pop_front();
10666     GraphicsState& rOld = m_aGraphicsStack.front();
10667 
10668     // move those parameters back that were not pushed
10669     // in the first place
10670     if( ! (aState.m_nFlags & PUSH_LINECOLOR) )
10671         setLineColor( aState.m_aLineColor );
10672     if( ! (aState.m_nFlags & PUSH_FILLCOLOR) )
10673         setFillColor( aState.m_aFillColor );
10674     if( ! (aState.m_nFlags & PUSH_FONT) )
10675         setFont( aState.m_aFont );
10676     if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) )
10677         setTextColor( aState.m_aFont.GetColor() );
10678     if( ! (aState.m_nFlags & PUSH_MAPMODE) )
10679         setMapMode( aState.m_aMapMode );
10680     if( ! (aState.m_nFlags & PUSH_CLIPREGION) )
10681     {
10682         // do not use setClipRegion here
10683         // it would convert again assuming the current mapmode
10684         rOld.m_aClipRegion = aState.m_aClipRegion;
10685         rOld.m_bClipRegion = aState.m_bClipRegion;
10686     }
10687     if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) )
10688         setTextLineColor( aState.m_aTextLineColor );
10689     if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) )
10690         setOverlineColor( aState.m_aOverlineColor );
10691     if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) )
10692         setTextAlign( aState.m_aFont.GetAlign() );
10693     if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) )
10694         setTextFillColor( aState.m_aFont.GetFillColor() );
10695     if( ! (aState.m_nFlags & PUSH_REFPOINT) )
10696     {
10697         // what ?
10698     }
10699     // invalidate graphics state
10700     m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U);
10701 }
10702 
10703 void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
10704 {
10705     m_aGraphicsStack.front().m_aMapMode = rMapMode;
10706     getReferenceDevice()->SetMapMode( rMapMode );
10707     m_aCurrentPDFState.m_aMapMode = rMapMode;
10708 }
10709 
10710 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10711 {
10712     basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10713     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10714     m_aGraphicsStack.front().m_aClipRegion = aRegion;
10715     m_aGraphicsStack.front().m_bClipRegion = true;
10716     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10717 }
10718 
10719 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
10720 {
10721     if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() )
10722     {
10723         Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10724                                    m_aMapMode,
10725                                    getReferenceDevice(),
10726                                    Point( nX, nY ) ) );
10727         aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10728                                m_aMapMode,
10729                                getReferenceDevice(),
10730                                Point() );
10731         basegfx::B2DHomMatrix aMat;
10732         aMat.translate( aPoint.X(), aPoint.Y() );
10733         m_aGraphicsStack.front().m_aClipRegion.transform( aMat );
10734         m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10735     }
10736 }
10737 
10738 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect )
10739 {
10740     basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect(
10741         basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) );
10742     return intersectClipRegion( aRect );
10743 }
10744 
10745 
10746 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10747 {
10748     basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) );
10749     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10750     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10751     if( m_aGraphicsStack.front().m_bClipRegion )
10752     {
10753         basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) );
10754         aRegion = basegfx::tools::prepareForPolygonOperation( aRegion );
10755         m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion );
10756     }
10757     else
10758     {
10759         m_aGraphicsStack.front().m_aClipRegion = aRegion;
10760         m_aGraphicsStack.front().m_bClipRegion = true;
10761     }
10762     return true;
10763 }
10764 
10765 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
10766 {
10767     if( nPageNr < 0 )
10768         nPageNr = m_nCurrentPage;
10769 
10770     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10771         return;
10772 
10773     m_aNotes.push_back( PDFNoteEntry() );
10774     m_aNotes.back().m_nObject		= createObject();
10775     m_aNotes.back().m_aContents		= rNote;
10776     m_aNotes.back().m_aRect			= rRect;
10777     // convert to default user space now, since the mapmode may change
10778     m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
10779 
10780     // insert note to page's annotation list
10781     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
10782 }
10783 
10784 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr )
10785 {
10786     if( nPageNr < 0 )
10787         nPageNr = m_nCurrentPage;
10788 
10789     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10790         return -1;
10791 
10792     sal_Int32 nRet = m_aLinks.size();
10793 
10794     m_aLinks.push_back( PDFLink() );
10795     m_aLinks.back().m_nObject	= createObject();
10796     m_aLinks.back().m_nPage		= nPageNr;
10797     m_aLinks.back().m_aRect		= rRect;
10798     // convert to default user space now, since the mapmode may change
10799     m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
10800 
10801     // insert link to page's annotation list
10802     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
10803 
10804     return nRet;
10805 }
10806 
10807 //--->i56629
10808 sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10809 {
10810     if( nPageNr < 0 )
10811         nPageNr = m_nCurrentPage;
10812 
10813     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10814         return -1;
10815 
10816     sal_Int32 nRet = m_aNamedDests.size();
10817 
10818     m_aNamedDests.push_back( PDFNamedDest() );
10819     m_aNamedDests.back().m_aDestName = sDestName;
10820     m_aNamedDests.back().m_nPage = nPageNr;
10821     m_aNamedDests.back().m_eType = eType;
10822     m_aNamedDests.back().m_aRect = rRect;
10823     // convert to default user space now, since the mapmode may change
10824     m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
10825 
10826     return nRet;
10827 }
10828 //<---i56629
10829 
10830 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10831 {
10832     if( nPageNr < 0 )
10833         nPageNr = m_nCurrentPage;
10834 
10835     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10836         return -1;
10837 
10838     sal_Int32 nRet = m_aDests.size();
10839 
10840     m_aDests.push_back( PDFDest() );
10841     m_aDests.back().m_nPage = nPageNr;
10842     m_aDests.back().m_eType	= eType;
10843     m_aDests.back().m_aRect = rRect;
10844     // convert to default user space now, since the mapmode may change
10845     m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
10846 
10847     return nRet;
10848 }
10849 
10850 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10851 {
10852     return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType );
10853 }
10854 
10855 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
10856 {
10857     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10858         return -1;
10859     if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
10860         return -2;
10861 
10862     m_aLinks[ nLinkId ].m_nDest = nDestId;
10863 
10864     return 0;
10865 }
10866 
10867 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
10868 {
10869     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10870         return -1;
10871 
10872     m_aLinks[ nLinkId ].m_nDest	= -1;
10873 
10874     using namespace ::com::sun::star;
10875 
10876     if (!m_xTrans.is())
10877     {
10878         uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() );
10879         if( xFact.is() )
10880         {
10881             m_xTrans = uno::Reference < util::XURLTransformer >(
10882                 xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY );
10883         }
10884     }
10885 
10886     util::URL aURL;
10887     aURL.Complete = rURL;
10888 
10889     if (m_xTrans.is())
10890         m_xTrans->parseStrict( aURL );
10891 
10892     m_aLinks[ nLinkId ].m_aURL	= aURL.Complete;
10893 
10894     return 0;
10895 }
10896 
10897 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
10898 {
10899     m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
10900 }
10901 
10902 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
10903 {
10904     // create new item
10905     sal_Int32 nNewItem = m_aOutline.size();
10906     m_aOutline.push_back( PDFOutlineEntry() );
10907 
10908     // set item attributes
10909     setOutlineItemParent( nNewItem, nParent );
10910     setOutlineItemText( nNewItem, rText );
10911     setOutlineItemDest( nNewItem, nDestID );
10912 
10913     return nNewItem;
10914 }
10915 
10916 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
10917 {
10918     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10919         return -1;
10920 
10921     int nRet = 0;
10922 
10923     if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
10924     {
10925         nNewParent = 0;
10926         nRet = -2;
10927     }
10928     // remove item from previous parent
10929     sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID;
10930     if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() )
10931     {
10932         PDFOutlineEntry& rParent = m_aOutline[ nParentID ];
10933 
10934         for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin();
10935              it != rParent.m_aChildren.end(); ++it )
10936         {
10937             if( *it == nItem )
10938             {
10939                 rParent.m_aChildren.erase( it );
10940                 break;
10941             }
10942         }
10943     }
10944 
10945     // insert item to new parent's list of children
10946     m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
10947 
10948     return nRet;
10949 }
10950 
10951 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
10952 {
10953     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10954         return -1;
10955 
10956     m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText );
10957     return 0;
10958 }
10959 
10960 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
10961 {
10962     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
10963         return -1;
10964     if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
10965         return -2;
10966     m_aOutline[nItem].m_nDestID = nDestID;
10967     return 0;
10968 }
10969 
10970 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
10971 {
10972     static std::map< PDFWriter::StructElement, const char* > aTagStrings;
10973     if( aTagStrings.empty() )
10974     {
10975         aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
10976         aTagStrings[ PDFWriter::Document ]		= "Document";
10977         aTagStrings[ PDFWriter::Part ]			= "Part";
10978         aTagStrings[ PDFWriter::Article ]		= "Art";
10979         aTagStrings[ PDFWriter::Section ]		= "Sect";
10980         aTagStrings[ PDFWriter::Division ]		= "Div";
10981         aTagStrings[ PDFWriter::BlockQuote ]	= "BlockQuote";
10982         aTagStrings[ PDFWriter::Caption ]		= "Caption";
10983         aTagStrings[ PDFWriter::TOC ]			= "TOC";
10984         aTagStrings[ PDFWriter::TOCI ]			= "TOCI";
10985         aTagStrings[ PDFWriter::Index ]			= "Index";
10986         aTagStrings[ PDFWriter::Paragraph ]		= "P";
10987         aTagStrings[ PDFWriter::Heading ]		= "H";
10988         aTagStrings[ PDFWriter::H1 ]			= "H1";
10989         aTagStrings[ PDFWriter::H2 ]			= "H2";
10990         aTagStrings[ PDFWriter::H3 ]			= "H3";
10991         aTagStrings[ PDFWriter::H4 ]			= "H4";
10992         aTagStrings[ PDFWriter::H5 ]			= "H5";
10993         aTagStrings[ PDFWriter::H6 ]			= "H6";
10994         aTagStrings[ PDFWriter::List ]			= "L";
10995         aTagStrings[ PDFWriter::ListItem ]		= "LI";
10996         aTagStrings[ PDFWriter::LILabel ]		= "Lbl";
10997         aTagStrings[ PDFWriter::LIBody ]		= "LBody";
10998         aTagStrings[ PDFWriter::Table ]			= "Table";
10999         aTagStrings[ PDFWriter::TableRow ]		= "TR";
11000         aTagStrings[ PDFWriter::TableHeader ]	= "TH";
11001         aTagStrings[ PDFWriter::TableData ]		= "TD";
11002         aTagStrings[ PDFWriter::Span ]			= "Span";
11003         aTagStrings[ PDFWriter::Quote ]			= "Quote";
11004         aTagStrings[ PDFWriter::Note ]			= "Note";
11005         aTagStrings[ PDFWriter::Reference ]		= "Reference";
11006         aTagStrings[ PDFWriter::BibEntry ]		= "BibEntry";
11007         aTagStrings[ PDFWriter::Code ]			= "Code";
11008         aTagStrings[ PDFWriter::Link ]			= "Link";
11009         aTagStrings[ PDFWriter::Figure ]		= "Figure";
11010         aTagStrings[ PDFWriter::Formula ]		= "Formula";
11011         aTagStrings[ PDFWriter::Form ]			= "Form";
11012     }
11013 
11014     std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
11015 
11016     return it != aTagStrings.end() ? it->second : "Div";
11017 }
11018 
11019 void PDFWriterImpl::beginStructureElementMCSeq()
11020 {
11021     if(	m_bEmitStructure &&
11022         m_nCurrentStructElement > 0 && // StructTreeRoot
11023         ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11024         )
11025     {
11026         PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
11027         OStringBuffer aLine( 128 );
11028         sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
11029         aLine.append( "/" );
11030         if( rEle.m_aAlias.getLength() > 0 )
11031             aLine.append( rEle.m_aAlias );
11032         else
11033             aLine.append( getStructureTag( rEle.m_eType ) );
11034         aLine.append( "<</MCID " );
11035         aLine.append( nMCID );
11036         aLine.append( ">>BDC\n" );
11037         writeBuffer( aLine.getStr(), aLine.getLength() );
11038 
11039         // update the element's content list
11040 #if OSL_DEBUG_LEVEL > 1
11041         fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n",
11042                  nMCID,
11043                  m_aPages[ m_nCurrentPage ].m_nPageObject,
11044                  rEle.m_nFirstPageObject );
11045 #endif
11046         rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) );
11047         // update the page's mcid parent list
11048         m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
11049         // mark element MC sequence as open
11050         rEle.m_bOpenMCSeq = true;
11051     }
11052     // handle artifacts
11053     else if( ! m_bEmitStructure && m_aContext.Tagged &&
11054                m_nCurrentStructElement > 0 &&
11055                m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
11056              ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11057              )
11058     {
11059         OStringBuffer aLine( 128 );
11060         aLine.append( "/Artifact BMC\n" );
11061         writeBuffer( aLine.getStr(), aLine.getLength() );
11062         // mark element MC sequence as open
11063         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
11064     }
11065 }
11066 
11067 void PDFWriterImpl::endStructureElementMCSeq()
11068 {
11069     if( m_nCurrentStructElement > 0 && // StructTreeRoot
11070         ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
11071         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
11072         )
11073     {
11074         writeBuffer( "EMC\n", 4 );
11075         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
11076     }
11077 }
11078 
11079 bool PDFWriterImpl::checkEmitStructure()
11080 {
11081     bool bEmit = false;
11082     if( m_aContext.Tagged )
11083     {
11084         bEmit = true;
11085         sal_Int32 nEle = m_nCurrentStructElement;
11086         while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
11087         {
11088             if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
11089             {
11090                 bEmit = false;
11091                 break;
11092             }
11093             nEle = m_aStructure[ nEle ].m_nParentElement;
11094         }
11095     }
11096     return bEmit;
11097 }
11098 
11099 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias )
11100 {
11101     if( m_nCurrentPage < 0 )
11102         return -1;
11103 
11104     if( ! m_aContext.Tagged )
11105         return -1;
11106 
11107     // close eventual current MC sequence
11108     endStructureElementMCSeq();
11109 
11110     if( m_nCurrentStructElement == 0 &&
11111         eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
11112     {
11113         // struct tree root hit, but not beginning document
11114         // this might happen with setCurrentStructureElement
11115         // silently insert structure into document again if one properly exists
11116         if( ! m_aStructure[ 0 ].m_aChildren.empty() )
11117         {
11118             PDFWriter::StructElement childType = PDFWriter::NonStructElement;
11119             sal_Int32 nNewCurElement = 0;
11120             const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
11121             for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
11122                  childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
11123             {
11124                 nNewCurElement = *it;
11125                 childType = m_aStructure[ nNewCurElement ].m_eType;
11126             }
11127             if( childType == PDFWriter::Document )
11128             {
11129                 m_nCurrentStructElement = nNewCurElement;
11130                 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" );
11131             }
11132             else {
11133                 DBG_ERROR( "document structure in disorder !" );
11134             }
11135         }
11136         else {
11137             DBG_ERROR( "PDF document structure MUST be contained in a Document element" );
11138         }
11139     }
11140 
11141     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11142     m_aStructure.push_back( PDFStructureElement() );
11143     PDFStructureElement& rEle = m_aStructure.back();
11144     rEle.m_eType			= eType;
11145     rEle.m_nOwnElement		= nNewId;
11146     rEle.m_nParentElement	= m_nCurrentStructElement;
11147     rEle.m_nFirstPageObject	= m_aPages[ m_nCurrentPage ].m_nPageObject;
11148     m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
11149     m_nCurrentStructElement = nNewId;
11150 
11151     // handle alias names
11152     if( rAlias.getLength() && eType != PDFWriter::NonStructElement )
11153     {
11154         OStringBuffer aNameBuf( rAlias.getLength() );
11155         appendName( rAlias, aNameBuf );
11156         OString aAliasName( aNameBuf.makeStringAndClear() );
11157         rEle.m_aAlias = aAliasName;
11158         m_aRoleMap[ aAliasName ] = getStructureTag( eType );
11159     }
11160 
11161 #if OSL_DEBUG_LEVEL > 1
11162     OStringBuffer aLine( "beginStructureElement " );
11163     aLine.append( m_nCurrentStructElement );
11164     aLine.append( ": " );
11165     aLine.append( getStructureTag( eType ) );
11166     if( rEle.m_aAlias.getLength() )
11167     {
11168         aLine.append( " aliased as \"" );
11169         aLine.append( rEle.m_aAlias );
11170         aLine.append( '\"' );
11171     }
11172     emitComment( aLine.getStr() );
11173 #endif
11174 
11175     // check whether to emit structure henceforth
11176     m_bEmitStructure = checkEmitStructure();
11177 
11178     if( m_bEmitStructure ) // don't create nonexistant objects
11179     {
11180         rEle.m_nObject		= createObject();
11181         // update parent's kids list
11182         m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject );
11183     }
11184     return nNewId;
11185 }
11186 
11187 void PDFWriterImpl::endStructureElement()
11188 {
11189     if( m_nCurrentPage < 0 )
11190         return;
11191 
11192     if( ! m_aContext.Tagged )
11193         return;
11194 
11195     if( m_nCurrentStructElement == 0 )
11196     {
11197         // hit the struct tree root, that means there is an endStructureElement
11198         // without corresponding beginStructureElement
11199         return;
11200     }
11201 
11202     // end the marked content sequence
11203     endStructureElementMCSeq();
11204 
11205 #if OSL_DEBUG_LEVEL > 1
11206     OStringBuffer aLine( "endStructureElement " );
11207     aLine.append( m_nCurrentStructElement );
11208     aLine.append( ": " );
11209     aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11210     if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
11211     {
11212         aLine.append( " aliased as \"" );
11213         aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11214         aLine.append( '\"' );
11215     }
11216 #endif
11217 
11218     // "end" the structure element, the parent becomes current element
11219     m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
11220 
11221     // check whether to emit structure henceforth
11222     m_bEmitStructure = checkEmitStructure();
11223 
11224 #if OSL_DEBUG_LEVEL > 1
11225     if( m_bEmitStructure )
11226         emitComment( aLine.getStr() );
11227 #endif
11228 }
11229 
11230 //---> i94258
11231 /*
11232  * This function adds an internal structure list container to overcome the 8191 elements array limitation
11233  * in kids element emission.
11234  * Recursive function
11235  *
11236  */
11237 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
11238 {
11239     if( rEle.m_eType == PDFWriter::NonStructElement &&
11240         rEle.m_nOwnElement != rEle.m_nParentElement )
11241         return;
11242 
11243     for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
11244     {
11245         if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
11246         {
11247             PDFStructureElement& rChild = m_aStructure[ *it ];
11248             if( rChild.m_eType != PDFWriter::NonStructElement )
11249             {
11250                 //triggered when a child of the rEle element is found
11251                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
11252                     addInternalStructureContainer( rChild );//examine the child
11253                 else
11254                 {
11255                     DBG_ERROR( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
11256 #if OSL_DEBUG_LEVEL > 1
11257                     fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
11258 #endif
11259                 }
11260             }
11261         }
11262         else
11263         {
11264             DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
11265 #if OSL_DEBUG_LEVEL > 1
11266             fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
11267 #endif
11268         }
11269     }
11270 
11271     if( rEle.m_nOwnElement != rEle.m_nParentElement )
11272     {
11273         if( !rEle.m_aKids.empty() )
11274         {
11275             if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
11276                 //then we need to add the containers for the kids elements
11277                 // a list to be used for the new kid element
11278                 std::list< PDFStructureElementKid > aNewKids;
11279                 std::list< sal_Int32 > aNewChildren;
11280 
11281                 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
11282                 OStringBuffer aNameBuf( "Div" );
11283                 OString aAliasName( aNameBuf.makeStringAndClear() );
11284                 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
11285 
11286                 while( rEle.m_aKids.size() > ncMaxPDFArraySize )
11287                 {
11288                     sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
11289                     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11290                     m_aStructure.push_back( PDFStructureElement() );
11291                     PDFStructureElement& rEleNew = m_aStructure.back();
11292                     rEleNew.m_aAlias            = aAliasName;
11293                     rEleNew.m_eType			    = PDFWriter::Division; // a new Div type container
11294                     rEleNew.m_nOwnElement		= nNewId;
11295                     rEleNew.m_nParentElement	= nCurrentStructElement;
11296                     //inherit the same page as the first child to be reparented
11297                     rEleNew.m_nFirstPageObject	= m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
11298                     rEleNew.m_nObject           = createObject();//assign a PDF object number
11299                     //add the object to the kid list of the parent
11300                     aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) );
11301                     aNewChildren.push_back( nNewId );
11302 
11303                     std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
11304                     std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
11305                     advance( aChildEndIt, ncMaxPDFArraySize );
11306                     advance( aKidEndIt, ncMaxPDFArraySize );
11307 
11308                     rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
11309                                             rEle.m_aKids,
11310                                             rEle.m_aKids.begin(),
11311                                             aKidEndIt );
11312                     rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
11313                                                 rEle.m_aChildren,
11314                                                 rEle.m_aChildren.begin(),
11315                                                 aChildEndIt );
11316                     // set the kid's new parent
11317                     for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
11318                          it != rEleNew.m_aChildren.end(); ++it )
11319                     {
11320                         m_aStructure[ *it ].m_nParentElement = nNewId;
11321                     }
11322                 }
11323                 //finally add the new kids resulting from the container added
11324                 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
11325                 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
11326             }
11327         }
11328     }
11329 }
11330 //<--- i94258
11331 
11332 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
11333 {
11334     bool bSuccess = false;
11335 
11336     if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
11337     {
11338         // end eventual previous marked content sequence
11339         endStructureElementMCSeq();
11340 
11341         m_nCurrentStructElement = nEle;
11342         m_bEmitStructure = checkEmitStructure();
11343 #if OSL_DEBUG_LEVEL > 1
11344         OStringBuffer aLine( "setCurrentStructureElement " );
11345         aLine.append( m_nCurrentStructElement );
11346         aLine.append( ": " );
11347         aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11348         if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
11349         {
11350             aLine.append( " aliased as \"" );
11351             aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11352             aLine.append( '\"' );
11353         }
11354         if( ! m_bEmitStructure )
11355             aLine.append( " (inside NonStruct)" );
11356         emitComment( aLine.getStr() );
11357 #endif
11358         bSuccess = true;
11359     }
11360 
11361     return bSuccess;
11362 }
11363 
11364 sal_Int32 PDFWriterImpl::getCurrentStructureElement()
11365 {
11366     return m_nCurrentStructElement;
11367 }
11368 
11369 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
11370 {
11371     if( !m_aContext.Tagged )
11372         return false;
11373 
11374     bool bInsert = false;
11375     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11376     {
11377         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11378         switch( eAttr )
11379         {
11380             case PDFWriter::Placement:
11381                 if( eVal == PDFWriter::Block		||
11382                     eVal == PDFWriter::Inline		||
11383                     eVal == PDFWriter::Before		||
11384                     eVal == PDFWriter::Start		||
11385                     eVal == PDFWriter::End )
11386                     bInsert = true;
11387                 break;
11388             case PDFWriter::WritingMode:
11389                 if( eVal == PDFWriter::LrTb			||
11390                     eVal == PDFWriter::RlTb			||
11391                     eVal == PDFWriter::TbRl )
11392                 {
11393                     bInsert = true;
11394                 }
11395                 break;
11396             case PDFWriter::TextAlign:
11397                 if( eVal == PDFWriter::Start		||
11398                     eVal == PDFWriter::Center		||
11399                     eVal == PDFWriter::End			||
11400                     eVal == PDFWriter::Justify )
11401                 {
11402                     if( eType == PDFWriter::Paragraph	||
11403                         eType == PDFWriter::Heading		||
11404                         eType == PDFWriter::H1			||
11405                         eType == PDFWriter::H2			||
11406                         eType == PDFWriter::H3			||
11407                         eType == PDFWriter::H4			||
11408                         eType == PDFWriter::H5			||
11409                         eType == PDFWriter::H6			||
11410                         eType == PDFWriter::List		||
11411                         eType == PDFWriter::ListItem	||
11412                         eType == PDFWriter::LILabel		||
11413                         eType == PDFWriter::LIBody		||
11414                         eType == PDFWriter::Table		||
11415                         eType == PDFWriter::TableRow	||
11416                         eType == PDFWriter::TableHeader	||
11417                         eType == PDFWriter::TableData )
11418                     {
11419                         bInsert = true;
11420                     }
11421                 }
11422                 break;
11423             case PDFWriter::Width:
11424             case PDFWriter::Height:
11425                 if( eVal == PDFWriter::Auto )
11426                 {
11427                     if( eType == PDFWriter::Figure		||
11428                         eType == PDFWriter::Formula		||
11429                         eType == PDFWriter::Form		||
11430                         eType == PDFWriter::Table		||
11431                         eType == PDFWriter::TableHeader	||
11432                         eType == PDFWriter::TableData )
11433                     {
11434                         bInsert = true;
11435                     }
11436                 }
11437                 break;
11438             case PDFWriter::BlockAlign:
11439                 if( eVal == PDFWriter::Before		||
11440                     eVal == PDFWriter::Middle		||
11441                     eVal == PDFWriter::After		||
11442                     eVal == PDFWriter::Justify )
11443                 {
11444                     if( eType == PDFWriter::TableHeader	||
11445                         eType == PDFWriter::TableData )
11446                     {
11447                         bInsert = true;
11448                     }
11449                 }
11450                 break;
11451             case PDFWriter::InlineAlign:
11452                 if( eVal == PDFWriter::Start		||
11453                     eVal == PDFWriter::Center		||
11454                     eVal == PDFWriter::End )
11455                 {
11456                     if( eType == PDFWriter::TableHeader	||
11457                         eType == PDFWriter::TableData )
11458                     {
11459                         bInsert = true;
11460                     }
11461                 }
11462                 break;
11463             case PDFWriter::LineHeight:
11464                 if( eVal == PDFWriter::Normal		||
11465                     eVal == PDFWriter::Auto )
11466                 {
11467                     // only for ILSE and BLSE
11468                     if( eType == PDFWriter::Paragraph	||
11469                         eType == PDFWriter::Heading		||
11470                         eType == PDFWriter::H1			||
11471                         eType == PDFWriter::H2			||
11472                         eType == PDFWriter::H3			||
11473                         eType == PDFWriter::H4			||
11474                         eType == PDFWriter::H5			||
11475                         eType == PDFWriter::H6			||
11476                         eType == PDFWriter::List		||
11477                         eType == PDFWriter::ListItem	||
11478                         eType == PDFWriter::LILabel		||
11479                         eType == PDFWriter::LIBody		||
11480                         eType == PDFWriter::Table		||
11481                         eType == PDFWriter::TableRow	||
11482                         eType == PDFWriter::TableHeader	||
11483                         eType == PDFWriter::TableData	||
11484                         eType == PDFWriter::Span		||
11485                         eType == PDFWriter::Quote		||
11486                         eType == PDFWriter::Note		||
11487                         eType == PDFWriter::Reference	||
11488                         eType == PDFWriter::BibEntry	||
11489                         eType == PDFWriter::Code		||
11490                         eType == PDFWriter::Link )
11491                     {
11492                         bInsert = true;
11493                     }
11494                 }
11495                 break;
11496             case PDFWriter::TextDecorationType:
11497                 if( eVal == PDFWriter::NONE			||
11498                     eVal == PDFWriter::Underline	||
11499                     eVal == PDFWriter::Overline		||
11500                     eVal == PDFWriter::LineThrough )
11501                 {
11502                     // only for ILSE and BLSE
11503                     if( eType == PDFWriter::Paragraph	||
11504                         eType == PDFWriter::Heading		||
11505                         eType == PDFWriter::H1			||
11506                         eType == PDFWriter::H2			||
11507                         eType == PDFWriter::H3			||
11508                         eType == PDFWriter::H4			||
11509                         eType == PDFWriter::H5			||
11510                         eType == PDFWriter::H6			||
11511                         eType == PDFWriter::List		||
11512                         eType == PDFWriter::ListItem	||
11513                         eType == PDFWriter::LILabel		||
11514                         eType == PDFWriter::LIBody		||
11515                         eType == PDFWriter::Table		||
11516                         eType == PDFWriter::TableRow	||
11517                         eType == PDFWriter::TableHeader	||
11518                         eType == PDFWriter::TableData	||
11519                         eType == PDFWriter::Span		||
11520                         eType == PDFWriter::Quote		||
11521                         eType == PDFWriter::Note		||
11522                         eType == PDFWriter::Reference	||
11523                         eType == PDFWriter::BibEntry	||
11524                         eType == PDFWriter::Code		||
11525                         eType == PDFWriter::Link )
11526                     {
11527                         bInsert = true;
11528                     }
11529                 }
11530                 break;
11531             case PDFWriter::ListNumbering:
11532                 if( eVal == PDFWriter::NONE			||
11533                     eVal == PDFWriter::Disc			||
11534                     eVal == PDFWriter::Circle		||
11535                     eVal == PDFWriter::Square		||
11536                     eVal == PDFWriter::Decimal		||
11537                     eVal == PDFWriter::UpperRoman	||
11538                     eVal == PDFWriter::LowerRoman	||
11539                     eVal == PDFWriter::UpperAlpha	||
11540                     eVal == PDFWriter::LowerAlpha )
11541                 {
11542                     if( eType == PDFWriter::List )
11543                         bInsert = true;
11544                 }
11545                 break;
11546             default: break;
11547         }
11548     }
11549 
11550     if( bInsert )
11551         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
11552 #if OSL_DEBUG_LEVEL > 1
11553     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11554         fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
11555                  getAttributeTag( eAttr ),
11556                  getAttributeValueTag( eVal ),
11557                  getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11558                  m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
11559                  );
11560 #endif
11561 
11562     return bInsert;
11563 }
11564 
11565 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
11566 {
11567     if( ! m_aContext.Tagged )
11568         return false;
11569 
11570     bool bInsert = false;
11571     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11572     {
11573         if( eAttr == PDFWriter::Language )
11574         {
11575             m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue );
11576             return true;
11577         }
11578 
11579         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11580         switch( eAttr )
11581         {
11582             case PDFWriter::SpaceBefore:
11583             case PDFWriter::SpaceAfter:
11584             case PDFWriter::StartIndent:
11585             case PDFWriter::EndIndent:
11586                 // just for BLSE
11587                 if( eType == PDFWriter::Paragraph	||
11588                     eType == PDFWriter::Heading		||
11589                     eType == PDFWriter::H1			||
11590                     eType == PDFWriter::H2			||
11591                     eType == PDFWriter::H3			||
11592                     eType == PDFWriter::H4			||
11593                     eType == PDFWriter::H5			||
11594                     eType == PDFWriter::H6			||
11595                     eType == PDFWriter::List		||
11596                     eType == PDFWriter::ListItem	||
11597                     eType == PDFWriter::LILabel		||
11598                     eType == PDFWriter::LIBody		||
11599                     eType == PDFWriter::Table		||
11600                     eType == PDFWriter::TableRow	||
11601                     eType == PDFWriter::TableHeader	||
11602                     eType == PDFWriter::TableData )
11603                 {
11604                     bInsert = true;
11605                 }
11606                 break;
11607             case PDFWriter::TextIndent:
11608                 // paragraph like BLSE and additional elements
11609                 if( eType == PDFWriter::Paragraph	||
11610                     eType == PDFWriter::Heading		||
11611                     eType == PDFWriter::H1			||
11612                     eType == PDFWriter::H2			||
11613                     eType == PDFWriter::H3			||
11614                     eType == PDFWriter::H4			||
11615                     eType == PDFWriter::H5			||
11616                     eType == PDFWriter::H6			||
11617                     eType == PDFWriter::LILabel		||
11618                     eType == PDFWriter::LIBody		||
11619                     eType == PDFWriter::TableHeader	||
11620                     eType == PDFWriter::TableData )
11621                 {
11622                     bInsert = true;
11623                 }
11624                 break;
11625             case PDFWriter::Width:
11626             case PDFWriter::Height:
11627                 if( eType == PDFWriter::Figure		||
11628                     eType == PDFWriter::Formula		||
11629                     eType == PDFWriter::Form		||
11630                     eType == PDFWriter::Table		||
11631                     eType == PDFWriter::TableHeader	||
11632                     eType == PDFWriter::TableData )
11633                 {
11634                     bInsert = true;
11635                 }
11636                 break;
11637             case PDFWriter::LineHeight:
11638             case PDFWriter::BaselineShift:
11639                 // only for ILSE and BLSE
11640                 if( eType == PDFWriter::Paragraph	||
11641                     eType == PDFWriter::Heading		||
11642                     eType == PDFWriter::H1			||
11643                     eType == PDFWriter::H2			||
11644                     eType == PDFWriter::H3			||
11645                     eType == PDFWriter::H4			||
11646                     eType == PDFWriter::H5			||
11647                     eType == PDFWriter::H6			||
11648                     eType == PDFWriter::List		||
11649                     eType == PDFWriter::ListItem	||
11650                     eType == PDFWriter::LILabel		||
11651                     eType == PDFWriter::LIBody		||
11652                     eType == PDFWriter::Table		||
11653                     eType == PDFWriter::TableRow	||
11654                     eType == PDFWriter::TableHeader	||
11655                     eType == PDFWriter::TableData	||
11656                     eType == PDFWriter::Span		||
11657                     eType == PDFWriter::Quote		||
11658                     eType == PDFWriter::Note		||
11659                     eType == PDFWriter::Reference	||
11660                     eType == PDFWriter::BibEntry	||
11661                     eType == PDFWriter::Code		||
11662                     eType == PDFWriter::Link )
11663                 {
11664                         bInsert = true;
11665                 }
11666                 break;
11667             case PDFWriter::RowSpan:
11668             case PDFWriter::ColSpan:
11669                 // only for table cells
11670                 if( eType == PDFWriter::TableHeader	||
11671                     eType == PDFWriter::TableData )
11672                 {
11673                     bInsert = true;
11674                 }
11675                 break;
11676             case PDFWriter::LinkAnnotation:
11677                 if( eType == PDFWriter::Link )
11678                     bInsert = true;
11679                 break;
11680             default: break;
11681         }
11682     }
11683 
11684     if( bInsert )
11685         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
11686 #if OSL_DEBUG_LEVEL > 1
11687     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11688         fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
11689                  getAttributeTag( eAttr ),
11690                  (int)nValue,
11691                  getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11692                  m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() );
11693 #endif
11694 
11695     return bInsert;
11696 }
11697 
11698 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect )
11699 {
11700     sal_Int32 nPageNr = m_nCurrentPage;
11701     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
11702         return;
11703 
11704 
11705     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11706     {
11707         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11708         if( eType == PDFWriter::Figure		||
11709             eType == PDFWriter::Formula		||
11710             eType == PDFWriter::Form		||
11711             eType == PDFWriter::Table )
11712         {
11713             m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
11714             // convert to default user space now, since the mapmode may change
11715             m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
11716         }
11717     }
11718 }
11719 
11720 void PDFWriterImpl::setActualText( const String& rText )
11721 {
11722     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11723     {
11724         m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
11725     }
11726 }
11727 
11728 void PDFWriterImpl::setAlternateText( const String& rText )
11729 {
11730     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11731     {
11732         m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
11733     }
11734 }
11735 
11736 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
11737 {
11738     if( nPageNr < 0 )
11739         nPageNr = m_nCurrentPage;
11740 
11741     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11742         return;
11743 
11744     m_aPages[ nPageNr ].m_nDuration = nSeconds;
11745 }
11746 
11747 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
11748 {
11749     if( nPageNr < 0 )
11750         nPageNr = m_nCurrentPage;
11751 
11752     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11753         return;
11754 
11755     m_aPages[ nPageNr ].m_eTransition	= eType;
11756     m_aPages[ nPageNr ].m_nTransTime	= nMilliSec;
11757 }
11758 
11759 void PDFWriterImpl::ensureUniqueRadioOnValues()
11760 {
11761     // loop over radio groups
11762     for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
11763          group != m_aRadioGroupWidgets.end(); ++group )
11764     {
11765         PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
11766         // check whether all kids have a unique OnValue
11767         std::hash_map< OUString, sal_Int32, OUStringHash > aOnValues;
11768         int nChildren = rGroupWidget.m_aKidsIndex.size();
11769         bool bIsUnique = true;
11770         for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
11771         {
11772             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11773             const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
11774             #if OSL_DEBUG_LEVEL > 1
11775             fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() );
11776             #endif
11777             if( aOnValues.find( rVal ) == aOnValues.end() )
11778             {
11779                 aOnValues[ rVal ] = 1;
11780             }
11781             else
11782             {
11783                 bIsUnique = false;
11784             }
11785         }
11786         if( ! bIsUnique )
11787         {
11788             #if OSL_DEBUG_LEVEL > 1
11789             fprintf( stderr, "enforcing unique OnValues\n" );
11790             #endif
11791             // make unique by using ascending OnValues
11792             for( int nKid = 0; nKid < nChildren; nKid++ )
11793             {
11794                 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11795                 PDFWidget& rKid = m_aWidgets[nKidIndex];
11796                 rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) );
11797                 if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11798                     rKid.m_aValue = rKid.m_aOnValue;
11799             }
11800         }
11801         // finally move the "Yes" appearance to the OnValue appearance
11802         for( int nKid = 0; nKid < nChildren; nKid++ )
11803         {
11804             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11805             PDFWidget& rKid = m_aWidgets[nKidIndex];
11806             PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
11807             if( app_it != rKid.m_aAppearances.end() )
11808             {
11809                 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
11810                 if( stream_it != app_it->second.end() )
11811                 {
11812                     SvMemoryStream* pStream = stream_it->second;
11813                     app_it->second.erase( stream_it );
11814                     OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
11815                     appendName( rKid.m_aOnValue, aBuf );
11816                     (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11817                 }
11818                 #if OSL_DEBUG_LEVEL > 1
11819                 else
11820                     fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" );
11821                 #endif
11822             }
11823             // update selected radio button
11824             if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11825             {
11826                 rGroupWidget.m_aValue = rKid.m_aValue;
11827             }
11828         }
11829     }
11830 }
11831 
11832 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
11833 {
11834     sal_Int32 nRadioGroupWidget = -1;
11835 
11836     std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
11837 
11838     if( it == m_aRadioGroupWidgets.end() )
11839     {
11840         m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
11841             sal_Int32(m_aWidgets.size());
11842 
11843         // new group, insert the radiobutton
11844         m_aWidgets.push_back( PDFWidget() );
11845         m_aWidgets.back().m_nObject		= createObject();
11846         m_aWidgets.back().m_nPage		= m_nCurrentPage;
11847         m_aWidgets.back().m_eType		= PDFWriter::RadioButton;
11848         m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
11849         m_aWidgets.back().m_nFlags |= 0x0000C000;   // NoToggleToOff and Radio bits
11850 
11851         createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn );
11852     }
11853     else
11854         nRadioGroupWidget = it->second;
11855 
11856     return nRadioGroupWidget;
11857 }
11858 
11859 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
11860 {
11861     if( nPageNr < 0 )
11862         nPageNr = m_nCurrentPage;
11863 
11864     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11865         return -1;
11866 
11867     sal_Int32 nNewWidget = m_aWidgets.size();
11868     m_aWidgets.push_back( PDFWidget() );
11869 
11870     m_aWidgets.back().m_nObject			= createObject();
11871     m_aWidgets.back().m_aRect				= rControl.Location;
11872     m_aWidgets.back().m_nPage				= nPageNr;
11873     m_aWidgets.back().m_eType				= rControl.getType();
11874 
11875     sal_Int32 nRadioGroupWidget = -1;
11876     // for unknown reasons the radio buttons of a radio group must not have a
11877     // field name, else the buttons are in fact check boxes -
11878     // that is multiple buttons of the radio group can be selected
11879     if( rControl.getType() == PDFWriter::RadioButton )
11880         nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
11881     else
11882     {
11883         createWidgetFieldName( nNewWidget, rControl );
11884     }
11885 
11886     // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
11887     PDFWidget& rNewWidget			= m_aWidgets[nNewWidget];
11888     rNewWidget.m_aDescription		= rControl.Description;
11889     rNewWidget.m_aText				= rControl.Text;
11890     rNewWidget.m_nTextStyle			= rControl.TextStyle &
11891         (  TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP |
11892            TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM |
11893            TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK  );
11894     rNewWidget.m_nTabOrder          = rControl.TabOrder;
11895 
11896     // various properties are set via the flags (/Ff) property of the field dict
11897     if( rControl.ReadOnly )
11898         rNewWidget.m_nFlags |= 1;
11899     if( rControl.getType() == PDFWriter::PushButton )
11900     {
11901         const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
11902         if( rNewWidget.m_nTextStyle == 0 )
11903             rNewWidget.m_nTextStyle =
11904                 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER |
11905                 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11906 
11907         rNewWidget.m_nFlags |= 0x00010000;
11908         if( rBtn.URL.getLength() )
11909             rNewWidget.m_aListEntries.push_back( rBtn.URL );
11910         rNewWidget.m_bSubmit    = rBtn.Submit;
11911         rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
11912         rNewWidget.m_nDest      = rBtn.Dest;
11913         createDefaultPushButtonAppearance( rNewWidget, rBtn );
11914     }
11915     else if( rControl.getType() == PDFWriter::RadioButton )
11916     {
11917         const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
11918         if( rNewWidget.m_nTextStyle == 0 )
11919             rNewWidget.m_nTextStyle =
11920                 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11921         /*  PDF sees a RadioButton group as one radio button with
11922          *  children which are in turn check boxes
11923          *
11924          *  so we need to create a radio button on demand for a new group
11925          *  and insert a checkbox for each RadioButtonWidget as its child
11926          */
11927         rNewWidget.m_eType			= PDFWriter::CheckBox;
11928         rNewWidget.m_nRadioGroup	= rBtn.RadioGroup;
11929 
11930         DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" );
11931 
11932         PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
11933         rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
11934         rRadioButton.m_aKidsIndex.push_back( nNewWidget );
11935         rNewWidget.m_nParent = rRadioButton.m_nObject;
11936 
11937         rNewWidget.m_aValue     = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) );
11938         rNewWidget.m_aOnValue   = rBtn.OnValue;
11939         if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected )
11940         {
11941             rNewWidget.m_aValue		= rNewWidget.m_aOnValue;
11942             rRadioButton.m_aValue	= rNewWidget.m_aOnValue;
11943         }
11944         createDefaultRadioButtonAppearance( rNewWidget, rBtn );
11945 
11946         // union rect of radio group
11947         Rectangle aRect = rNewWidget.m_aRect;
11948         m_aPages[ nPageNr ].convertRect( aRect );
11949         rRadioButton.m_aRect.Union( aRect );
11950     }
11951     else if( rControl.getType() == PDFWriter::CheckBox )
11952     {
11953         const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
11954         if( rNewWidget.m_nTextStyle == 0 )
11955             rNewWidget.m_nTextStyle =
11956                 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11957 
11958         rNewWidget.m_aValue = OUString::createFromAscii( rBox.Checked ? "Yes" : "Off" );
11959         // create default appearance before m_aRect gets transformed
11960         createDefaultCheckBoxAppearance( rNewWidget, rBox );
11961     }
11962     else if( rControl.getType() == PDFWriter::ListBox )
11963     {
11964         if( rNewWidget.m_nTextStyle == 0 )
11965             rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
11966 
11967         const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
11968         rNewWidget.m_aListEntries	  = rLstBox.Entries;
11969         rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
11970         rNewWidget.m_aValue			  = rLstBox.Text;
11971         if( rLstBox.DropDown )
11972             rNewWidget.m_nFlags |= 0x00020000;
11973         if( rLstBox.Sort )
11974             rNewWidget.m_nFlags |= 0x00080000;
11975         if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 )
11976             rNewWidget.m_nFlags |= 0x00200000;
11977 
11978         createDefaultListBoxAppearance( rNewWidget, rLstBox );
11979     }
11980     else if( rControl.getType() == PDFWriter::ComboBox )
11981     {
11982         if( rNewWidget.m_nTextStyle == 0 )
11983             rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
11984 
11985         const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
11986         rNewWidget.m_aValue			= rBox.Text;
11987         rNewWidget.m_aListEntries	= rBox.Entries;
11988         rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
11989         if( rBox.Sort )
11990             rNewWidget.m_nFlags |= 0x00080000;
11991 
11992         PDFWriter::ListBoxWidget aLBox;
11993         aLBox.Name				= rBox.Name;
11994         aLBox.Description		= rBox.Description;
11995         aLBox.Text				= rBox.Text;
11996         aLBox.TextStyle			= rBox.TextStyle;
11997         aLBox.ReadOnly			= rBox.ReadOnly;
11998         aLBox.Border			= rBox.Border;
11999         aLBox.BorderColor		= rBox.BorderColor;
12000         aLBox.Background		= rBox.Background;
12001         aLBox.BackgroundColor	= rBox.BackgroundColor;
12002         aLBox.TextFont			= rBox.TextFont;
12003         aLBox.TextColor			= rBox.TextColor;
12004         aLBox.DropDown			= true;
12005         aLBox.Sort				= rBox.Sort;
12006         aLBox.MultiSelect		= false;
12007         aLBox.Entries			= rBox.Entries;
12008 
12009         createDefaultListBoxAppearance( rNewWidget, aLBox );
12010     }
12011     else if( rControl.getType() == PDFWriter::Edit )
12012     {
12013         if( rNewWidget.m_nTextStyle == 0 )
12014             rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
12015 
12016         const PDFWriter::EditWidget& rEdit = static_cast<const  PDFWriter::EditWidget&>(rControl);
12017         if( rEdit.MultiLine )
12018         {
12019             rNewWidget.m_nFlags |= 0x00001000;
12020             rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12021         }
12022         if( rEdit.Password )
12023             rNewWidget.m_nFlags |= 0x00002000;
12024         if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 )
12025             rNewWidget.m_nFlags |= 0x00100000;
12026         rNewWidget.m_nMaxLen = rEdit.MaxLen;
12027         rNewWidget.m_aValue = rEdit.Text;
12028 
12029         createDefaultEditAppearance( rNewWidget, rEdit );
12030     }
12031 
12032     // convert to default user space now, since the mapmode may change
12033     // note: create default appearances before m_aRect gets transformed
12034     m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
12035 
12036     // insert widget to page's annotation list
12037     m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
12038 
12039     // mark page as having widgets
12040     m_aPages[ nPageNr ].m_bHasWidgets = true;
12041 
12042     return nNewWidget;
12043 }
12044 
12045 void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl )
12046 {
12047     if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() )
12048         return;
12049 
12050     PDFWidget& rWidget = m_aWidgets[ nControl ];
12051     m_nCurrentControl = nControl;
12052 
12053     SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 );
12054     // back conversion of control rect to current MapMode; necessary because
12055     // MapMode between createControl and beginControlAppearance
12056     // could have changed; therefore the widget rectangle is
12057     // already converted
12058     Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ),
12059                      rWidget.m_aRect.GetSize() );
12060     aBack = lcl_convert( m_aMapMode,
12061                          m_aGraphicsStack.front().m_aMapMode,
12062                          getReferenceDevice(),
12063                          aBack );
12064     beginRedirect( pControlStream, aBack );
12065     writeBuffer( "/Tx BMC\n", 8 );
12066 }
12067 
12068 bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState )
12069 {
12070     bool bRet = false;
12071     if( ! m_aOutputStreams.empty() )
12072         writeBuffer( "\nEMC\n", 5 );
12073     SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect());
12074     if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() )
12075     {
12076         PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ];
12077         OString aState, aStyle;
12078         switch( rWidget.m_eType )
12079         {
12080             case PDFWriter::PushButton:
12081                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12082                 {
12083                     aState = (eState == PDFWriter::Up) ? "N" : "D";
12084                     aStyle = "Standard";
12085                 }
12086                 break;
12087             case PDFWriter::CheckBox:
12088                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12089                 {
12090                     aState = "N";
12091                     aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes";
12092                     /* cf PDFReference 3rd ed. V1.4 p539:
12093                        recommended name for on state is "Yes",
12094                        recommended name for off state is "Off"
12095                      */
12096                 }
12097                 break;
12098             case PDFWriter::RadioButton:
12099                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12100                 {
12101                     aState = "N";
12102                     if( eState == PDFWriter::Up )
12103                         aStyle = "Off";
12104                     else
12105                     {
12106                         OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 );
12107                         appendName( rWidget.m_aOnValue, aBuf );
12108                         aStyle = aBuf.makeStringAndClear();
12109                     }
12110                 }
12111                 break;
12112             case PDFWriter::Edit:
12113                 aState = "N";
12114                 aStyle = "Standard";
12115                 break;
12116             case PDFWriter::ListBox:
12117             case PDFWriter::ComboBox:
12118             case PDFWriter::Hierarchy:
12119                 break;
12120         }
12121         if( aState.getLength() && aStyle.getLength() )
12122         {
12123             // delete eventual existing stream
12124             PDFAppearanceStreams::iterator it =
12125                 rWidget.m_aAppearances[ aState ].find( aStyle );
12126             if( it != rWidget.m_aAppearances[ aState ].end() )
12127                 delete it->second;
12128             rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance;
12129             bRet = true;
12130         }
12131     }
12132 
12133     if( ! bRet )
12134         delete pAppearance;
12135 
12136     m_nCurrentControl = -1;
12137 
12138     return bRet;
12139 }
12140 
12141 void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress )
12142 {
12143     if( pStream )
12144     {
12145         m_aAdditionalStreams.push_back( PDFAddStream() );
12146         PDFAddStream& rStream = m_aAdditionalStreams.back();
12147         rStream.m_aMimeType = rMimeType.Len()
12148                               ? OUString( rMimeType )
12149                               : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) );
12150         rStream.m_pStream = pStream;
12151         rStream.m_bCompress = bCompress;
12152     }
12153 }
12154 
12155 
12156 
12157