xref: /aoo41x/main/vcl/source/gdi/pdfwriter_impl.cxx (revision 457fd29f)
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                 // special case: nIndex inside a section marker
3379                 if( nIndex >= (*it) && (*it)+6 > nIndex )
3380                     nLength3 -= (*it)+6 - nIndex;
3381                 else if( *it >= nIndex  )
3382                 {
3383                     if( *it < nFontLen - 6 )
3384                         nLength3 -= 6;
3385                     else // the last section 0x8003 is only 2 bytes after all
3386                         nLength3 -= (nFontLen - *it);
3387                 }
3388             }
3389 
3390             // there may be whitespace to ignore before the 512 '0'
3391             while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' )
3392             {
3393                 nIndex--;
3394                 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3395                     ;
3396                 if( it != aSections.end() )
3397                 {
3398                     nIndex = (*it)-1;
3399                     break; // this is surely a binary boundary, in ascii case it wouldn't matter
3400                 }
3401             }
3402             nEndBinaryIndex = nIndex;
3403 
3404             // search for beginning of binary section
3405             nBeginBinaryIndex = nEndAsciiIndex;
3406             do
3407             {
3408                 nBeginBinaryIndex++;
3409                 for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it )
3410                     ;
3411                     } while( nBeginBinaryIndex < nEndBinaryIndex &&
3412                         ( pFontData[nBeginBinaryIndex] == '\r'	||
3413                             pFontData[nBeginBinaryIndex] == '\n'	||
3414                             it != aSections.end() ) );
3415 
3416                     // it seems to be vital to copy the exact whitespace between binary data
3417                     // and eexec, else a invalid font results. so make nEndAsciiIndex
3418                     // always immediate in front of nBeginBinaryIndex
3419                     nEndAsciiIndex = nBeginBinaryIndex-1;
3420                     for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it )
3421                         ;
3422                     if( it != aSections.end() )
3423                         nEndAsciiIndex = (*it)-1;
3424 
3425                     nLength1 = nEndAsciiIndex+1; // including the last character
3426                     for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it )
3427                         nLength1 -= 6; // decrease by pfb section size
3428 
3429                     // if the first four bytes are all ascii hex characters, then binary data
3430                     // has to be converted to real binary data
3431                     for( nIndex = 0; nIndex < 4 &&
3432                         ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) ||
3433                             ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) ||
3434                             ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' )
3435                             ); ++nIndex )
3436                     ;
3437                     bool bConvertHexData = true;
3438                     if( nIndex < 4 )
3439                     {
3440                         bConvertHexData = false;
3441                         nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte
3442                         for( it = aSections.begin(); it != aSections.end(); ++it )
3443                             if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex )
3444                             nLength2 -= 6;
3445                     }
3446                     else
3447                     {
3448                         // count the hex ascii characters to get nLength2
3449                         nLength2 = 0;
3450                         int nNextSectionIndex = 0;
3451                         for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3452                             ;
3453                         if( it != aSections.end() )
3454                             nNextSectionIndex = *it;
3455                         for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3456                         {
3457                             if( nIndex == nNextSectionIndex )
3458                             {
3459                                 nIndex += 6;
3460                                 ++it;
3461                                 nNextSectionIndex = (it == aSections.end() ? 0 : *it );
3462                             }
3463                             if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) ||
3464                                 ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) ||
3465                             ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) )
3466                             nLength2++;
3467                         }
3468                         DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" );
3469                         nLength2 /= 2;
3470                     }
3471 
3472                     // now we can actually write the font stream !
3473                     #if OSL_DEBUG_LEVEL > 1
3474                     emitComment( " PDFWriterImpl::emitEmbeddedFont" );
3475                     #endif
3476                     OStringBuffer aLine( 512 );
3477                     nStreamObject = createObject();
3478                     if( !updateObject(nStreamObject))
3479                         throw FontException();
3480                     sal_Int32 nStreamLengthObject = createObject();
3481                     aLine.append( nStreamObject );
3482                     aLine.append( " 0 obj\n"
3483                         "<</Length " );
3484                     aLine.append( nStreamLengthObject );
3485                     aLine.append( " 0 R"
3486                         #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3487                         "/Filter/FlateDecode"
3488                         #endif
3489                         "/Length1 " );
3490                     aLine.append( nLength1 );
3491                     aLine.append( " /Length2 " );
3492                     aLine.append( nLength2 );
3493                     aLine.append( " /Length3 ");
3494                     aLine.append( nLength3 );
3495                     aLine.append( ">>\n"
3496                         "stream\n" );
3497                     if( !writeBuffer( aLine.getStr(), aLine.getLength() ) )
3498                         throw FontException();
3499 
3500                     sal_uInt64 nBeginStreamPos = 0;
3501                     osl_getFilePos( m_aFile, &nBeginStreamPos );
3502 
3503                     beginCompression();
3504                     checkAndEnableStreamEncryption( nStreamObject );
3505 
3506                     // write ascii section
3507                     if( aSections.begin() == aSections.end() )
3508                     {
3509                         if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) )
3510                             throw FontException();
3511                     }
3512                     else
3513                     {
3514                         // first section always starts at 0
3515                         it = aSections.begin();
3516                         nIndex = (*it)+6;
3517                         ++it;
3518                         while( *it < nEndAsciiIndex )
3519                         {
3520                             if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) )
3521                                 throw FontException();
3522                             nIndex = (*it)+6;
3523                             ++it;
3524                         }
3525                         // write partial last section
3526                         if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) )
3527                             throw FontException();
3528                     }
3529 
3530                     // write binary section
3531                     if( ! bConvertHexData )
3532                     {
3533                         if( aSections.begin() == aSections.end() )
3534                         {
3535                             if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) )
3536                                 throw FontException();
3537                         }
3538                         else
3539                         {
3540                             for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it )
3541                                 ;
3542                             // write first partial section
3543                             if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) )
3544                                 throw FontException();
3545                             // write following sections
3546                             while( it != aSections.end() )
3547                             {
3548                                 nIndex = (*it)+6;
3549                                 ++it;
3550                                 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3551                                 {
3552                                     sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3553                                     if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3554                                         throw FontException();
3555                                 }
3556                             }
3557                         }
3558                     }
3559                     else
3560                     {
3561                         boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] );
3562                         rtl_zeroMemory( pWriteBuffer.get(), nLength2 );
3563                         int nWriteIndex = 0;
3564 
3565                         int nNextSectionIndex = 0;
3566                         for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3567                             ;
3568                         if( it != aSections.end() )
3569                             nNextSectionIndex = *it;
3570                         for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3571                         {
3572                             if( nIndex == nNextSectionIndex )
3573                             {
3574                                 nIndex += 6;
3575                                 ++it;
3576                                 nNextSectionIndex = (it == aSections.end() ? nFontLen : *it );
3577                             }
3578                             unsigned char cNibble = 0x80;
3579                             if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' )
3580                                 cNibble = pFontData[nIndex] - '0';
3581                             else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' )
3582                                 cNibble = pFontData[nIndex] - 'a' + 10;
3583                             else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' )
3584                                 cNibble = pFontData[nIndex] - 'A' + 10;
3585                             if( cNibble != 0x80 )
3586                             {
3587                                 if( !(nWriteIndex & 1 ) )
3588                                     cNibble <<= 4;
3589                                 pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble;
3590                                 nWriteIndex++;
3591                             }
3592                         }
3593                         if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) )
3594                             throw FontException();
3595                         if( aSections.empty() )
3596                         {
3597                             if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) )
3598                                 throw FontException();
3599                         }
3600                         else
3601                         {
3602                             // write rest of this section
3603                             if( nIndex < nNextSectionIndex )
3604                             {
3605                                 if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) )
3606                                     throw FontException();
3607                             }
3608                             // write following sections
3609                             while( it != aSections.end() )
3610                             {
3611                                 nIndex = (*it)+6;
3612                                 ++it;
3613                                 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3614                                 {
3615                                     sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3616                                     if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3617                                         throw FontException();
3618                                 }
3619                             }
3620                         }
3621                     }
3622                     endCompression();
3623                     disableStreamEncryption();
3624 
3625 
3626                     sal_uInt64 nEndStreamPos = 0;
3627                     osl_getFilePos( m_aFile, &nEndStreamPos );
3628 
3629                     // and finally close the stream
3630                     aLine.setLength( 0 );
3631                     aLine.append( "\nendstream\nendobj\n\n" );
3632                     if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3633                         throw FontException();
3634 
3635                     // write stream length object
3636                     aLine.setLength( 0 );
3637                     if( ! updateObject( nStreamLengthObject ) )
3638                         throw FontException();
3639                     aLine.append( nStreamLengthObject );
3640                     aLine.append( " 0 obj\n" );
3641                     aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) );
3642                     aLine.append( "\nendobj\n\n" );
3643                     if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3644                         throw FontException();
3645         }
3646         else
3647         {
3648             rtl::OStringBuffer aErrorComment( 256 );
3649             aErrorComment.append( "GetEmbedFontData failed for font \"" );
3650             aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3651             aErrorComment.append( '\"' );
3652             if( pFont->GetSlant() == ITALIC_NORMAL )
3653                 aErrorComment.append( " italic" );
3654             else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3655                 aErrorComment.append( " oblique" );
3656             aErrorComment.append( " weight=" );
3657             aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3658             emitComment( aErrorComment.getStr() );
3659         }
3660 
3661         if( nStreamObject )
3662             // write font descriptor
3663         nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject );
3664 
3665         if( nFontDescriptor )
3666         {
3667             if( pEncoding )
3668                 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, sizeof(nEncoding)/sizeof(nEncoding[0]) );
3669 
3670             // write font object
3671             sal_Int32 nObject = createObject();
3672             if( ! updateObject( nObject ) )
3673                 throw FontException();
3674 
3675             OStringBuffer aLine( 1024 );
3676             aLine.append( nObject );
3677             aLine.append( " 0 obj\n"
3678                 "<</Type/Font/Subtype/Type1/BaseFont/" );
3679             appendName( aInfo.m_aPSName, aLine );
3680             aLine.append( "\n" );
3681             if( !pFont->mbSymbolFlag &&  pEncoding == 0 )
3682                 aLine.append( "/Encoding/WinAnsiEncoding\n" );
3683             if( nToUnicodeStream )
3684             {
3685                 aLine.append( "/ToUnicode " );
3686                 aLine.append( nToUnicodeStream );
3687                 aLine.append( " 0 R\n" );
3688             }
3689             aLine.append( "/FirstChar 0 /LastChar 255\n"
3690                 "/Widths[" );
3691             for( int i = 0; i < 256; i++ )
3692             {
3693                 aLine.append( pWidths[i] );
3694                 aLine.append( ((i&15) == 15) ? "\n" : " " );
3695             }
3696             aLine.append( "]\n"
3697                 "/FontDescriptor " );
3698             aLine.append( nFontDescriptor );
3699             aLine.append( " 0 R>>\n"
3700                 "endobj\n\n" );
3701             if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3702                 throw FontException();
3703 
3704             nFontObject = nObject;
3705 
3706             aRet[ rEmbed.m_nNormalFontID ] = nObject;
3707 
3708             // write additional encodings
3709             for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it )
3710             {
3711                 sal_Int32 aEncWidths[ 256 ];
3712                 // emit encoding dict
3713                 sal_Int32 nEncObject = createObject();
3714                 if( ! updateObject( nEncObject ) )
3715                     throw FontException();
3716 
3717                 OutputDevice* pRef = getReferenceDevice();
3718                 pRef->Push( PUSH_FONT | PUSH_MAPMODE );
3719                 pRef->SetMapMode( MapMode( MAP_PIXEL ) );
3720                 Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) );
3721                 aFont.SetWeight( pFont->GetWeight() );
3722                 aFont.SetItalic( pFont->GetSlant() );
3723                 aFont.SetPitch( pFont->GetPitch() );
3724                 pRef->SetFont( aFont );
3725                 pRef->ImplNewFont();
3726 
3727                 aLine.setLength( 0 );
3728                 aLine.append( nEncObject );
3729                 aLine.append( " 0 obj\n"
3730                     "<</Type/Encoding/Differences[ 0\n" );
3731                 int nEncoded = 0;
3732                 aUnicodes.clear();
3733                 for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it )
3734                 {
3735                     String aStr( str_it->m_aUnicode );
3736                     aEncWidths[nEncoded] = pRef->GetTextWidth( aStr );
3737                     nEncodedCodes[nEncoded] = str_it->m_aUnicode;
3738                     nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded);
3739                     pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size());
3740                     aUnicodes.push_back( nEncodedCodes[nEncoded] );
3741                     pUnicodesPerGlyph[nEncoded] = 1;
3742 
3743                     aLine.append( " /" );
3744                     aLine.append( str_it->m_aName );
3745                     if( !((++nEncoded) & 15) )
3746                         aLine.append( "\n" );
3747                 }
3748                 aLine.append( "]>>\n"
3749                     "endobj\n\n" );
3750 
3751                 pRef->Pop();
3752 
3753                 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3754                     throw FontException();
3755 
3756                 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded );
3757 
3758                 nObject = createObject();
3759                 if( ! updateObject( nObject ) )
3760                     throw FontException();
3761 
3762                 aLine.setLength( 0 );
3763                 aLine.append( nObject );
3764                 aLine.append( " 0 obj\n"
3765                     "<</Type/Font/Subtype/Type1/BaseFont/" );
3766                 appendName( aInfo.m_aPSName, aLine );
3767                 aLine.append( "\n" );
3768                 aLine.append( "/Encoding " );
3769                 aLine.append( nEncObject );
3770                 aLine.append( " 0 R\n" );
3771                 if( nToUnicodeStream )
3772                 {
3773                     aLine.append( "/ToUnicode " );
3774                     aLine.append( nToUnicodeStream );
3775                     aLine.append( " 0 R\n" );
3776                 }
3777                 aLine.append( "/FirstChar 0\n"
3778                     "/LastChar " );
3779                 aLine.append( (sal_Int32)(nEncoded-1) );
3780                 aLine.append( "\n"
3781                     "/Widths[" );
3782                 for( int i = 0; i < nEncoded; i++ )
3783                 {
3784                     aLine.append( aEncWidths[i] );
3785                     aLine.append( ((i&15) == 15) ? "\n" : " " );
3786                 }
3787                 aLine.append( " ]\n"
3788                     "/FontDescriptor " );
3789                 aLine.append( nFontDescriptor );
3790                 aLine.append( " 0 R>>\n"
3791                     "endobj\n\n" );
3792                 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3793                     throw FontException();
3794 
3795                 aRet[ enc_it->m_nFontID ] = nObject;
3796             }
3797         }
3798     }
3799     catch( FontException& )
3800     {
3801         // these do nothing in case there was no compression or encryption ongoing
3802         endCompression();
3803         disableStreamEncryption();
3804     }
3805 
3806     if( pFontData )
3807         m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3808 
3809     return aRet;
3810 }
3811 
3812 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
3813 {
3814     if( nSubsetID )
3815     {
3816         for( int i = 0; i < 6; i++ )
3817         {
3818             int nOffset = (nSubsetID % 26);
3819             nSubsetID /= 26;
3820             rBuffer.append( (sal_Char)('A'+nOffset) );
3821         }
3822         rBuffer.append( '+' );
3823     }
3824     appendName( rPSName, rBuffer );
3825 }
3826 
3827 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding,
3828                                               sal_Ucs* pUnicodes,
3829                                               sal_Int32* pUnicodesPerGlyph,
3830                                               sal_Int32* pEncToUnicodeIndex,
3831                                               int nGlyphs )
3832 {
3833     int nMapped = 0, n = 0;
3834     for( n = 0; n < nGlyphs; n++ )
3835         if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3836             nMapped++;
3837 
3838     if( nMapped == 0 )
3839         return 0;
3840 
3841     sal_Int32 nStream = createObject();
3842     CHECK_RETURN( updateObject( nStream ) );
3843 
3844     OStringBuffer aContents( 1024 );
3845     aContents.append(
3846                      "/CIDInit/ProcSet findresource begin\n"
3847                      "12 dict begin\n"
3848                      "begincmap\n"
3849                      "/CIDSystemInfo<<\n"
3850                      "/Registry (Adobe)\n"
3851                      "/Ordering (UCS)\n"
3852                      "/Supplement 0\n"
3853                      ">> def\n"
3854                      "/CMapName/Adobe-Identity-UCS def\n"
3855                      "/CMapType 2 def\n"
3856                      "1 begincodespacerange\n"
3857                      "<00> <FF>\n"
3858                      "endcodespacerange\n"
3859                      );
3860     int nCount = 0;
3861     for( n = 0; n < nGlyphs; n++ )
3862     {
3863         if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3864         {
3865             if( (nCount % 100) == 0 )
3866             {
3867                 if( nCount )
3868                     aContents.append( "endbfchar\n" );
3869                 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) );
3870                 aContents.append( " beginbfchar\n" );
3871             }
3872             aContents.append( '<' );
3873             appendHex( (sal_Int8)pEncoding[n], aContents );
3874             aContents.append( "> <" );
3875             // TODO: handle unicodes>U+FFFF
3876             sal_Int32 nIndex = pEncToUnicodeIndex[n];
3877 	        for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ )
3878 	        {
3879                 appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents );
3880                 appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents );
3881             }
3882             aContents.append( ">\n" );
3883             nCount++;
3884         }
3885     }
3886     aContents.append( "endbfchar\n"
3887                       "endcmap\n"
3888                       "CMapName currentdict /CMap defineresource pop\n"
3889                       "end\n"
3890                       "end\n" );
3891 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3892     ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
3893     SvMemoryStream aStream;
3894     pCodec->BeginCompression();
3895     pCodec->Write( aStream, (const sal_uInt8*)aContents.getStr(), aContents.getLength() );
3896     pCodec->EndCompression();
3897     delete pCodec;
3898 #endif
3899 
3900     #if OSL_DEBUG_LEVEL > 1
3901     emitComment( "PDFWriterImpl::createToUnicodeCMap" );
3902     #endif
3903     OStringBuffer aLine( 40 );
3904 
3905     aLine.append( nStream );
3906     aLine.append( " 0 obj\n<</Length " );
3907 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3908     sal_Int32 nLen = (sal_Int32)aStream.Tell();
3909     aStream.Seek( 0 );
3910     aLine.append( nLen );
3911     aLine.append( "/Filter/FlateDecode" );
3912 #else
3913     aLine.append( aContents.getLength() );
3914 #endif
3915     aLine.append( ">>\nstream\n" );
3916     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3917     checkAndEnableStreamEncryption( nStream );
3918 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3919     CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
3920 #else
3921     CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
3922 #endif
3923     disableStreamEncryption();
3924     aLine.setLength( 0 );
3925     aLine.append( "\nendstream\n"
3926                   "endobj\n\n" );
3927     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3928     return nStream;
3929 }
3930 
3931 sal_Int32 PDFWriterImpl::emitFontDescriptor( const ImplFontData* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
3932 {
3933     OStringBuffer aLine( 1024 );
3934     // get font flags, see PDF reference 1.4 p. 358
3935     // possibly characters outside Adobe standard encoding
3936     // so set Symbolic flag
3937     sal_Int32 nFontFlags = (1<<2);
3938     if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE )
3939         nFontFlags |= (1 << 6);
3940     if( pFont->GetPitch() == PITCH_FIXED )
3941         nFontFlags |= 1;
3942     if( pFont->GetFamilyType() == FAMILY_SCRIPT )
3943         nFontFlags |= (1 << 3);
3944     else if( pFont->GetFamilyType() == FAMILY_ROMAN )
3945         nFontFlags |= (1 << 1);
3946 
3947     sal_Int32 nFontDescriptor = createObject();
3948     CHECK_RETURN( updateObject( nFontDescriptor ) );
3949     aLine.setLength( 0 );
3950     aLine.append( nFontDescriptor );
3951     aLine.append( " 0 obj\n"
3952                   "<</Type/FontDescriptor/FontName/" );
3953     appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
3954     aLine.append( "\n"
3955                   "/Flags " );
3956     aLine.append( nFontFlags );
3957     aLine.append( "\n"
3958                   "/FontBBox[" );
3959     // note: Top and Bottom are reversed in VCL and PDF rectangles
3960     aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() );
3961     aLine.append( ' ' );
3962     aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() );
3963     aLine.append( ' ' );
3964     aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() );
3965     aLine.append( ' ' );
3966     aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) );
3967     aLine.append( "]/ItalicAngle " );
3968     if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL )
3969         aLine.append( "-30" );
3970     else
3971         aLine.append( "0" );
3972     aLine.append( "\n"
3973                   "/Ascent " );
3974     aLine.append( (sal_Int32)rInfo.m_nAscent );
3975     aLine.append( "\n"
3976                   "/Descent " );
3977     aLine.append( (sal_Int32)-rInfo.m_nDescent );
3978     aLine.append( "\n"
3979                   "/CapHeight " );
3980     aLine.append( (sal_Int32)rInfo.m_nCapHeight );
3981     // According to PDF reference 1.4 StemV is required
3982     // seems a tad strange to me, but well ...
3983     aLine.append( "\n"
3984                   "/StemV 80\n" );
3985     if( nFontStream )
3986     {
3987         aLine.append( "/FontFile" );
3988         switch( rInfo.m_nFontType )
3989         {
3990             case FontSubsetInfo::SFNT_TTF:
3991                 aLine.append( '2' );
3992                 break;
3993             case FontSubsetInfo::TYPE1_PFA:
3994             case FontSubsetInfo::TYPE1_PFB:
3995             case FontSubsetInfo::ANY_TYPE1:
3996                 break;
3997             default:
3998                 DBG_ERROR( "unknown fonttype in PDF font descriptor" );
3999                 return 0;
4000         }
4001         aLine.append( ' ' );
4002         aLine.append( nFontStream );
4003         aLine.append( " 0 R\n" );
4004     }
4005     aLine.append( ">>\n"
4006                   "endobj\n\n" );
4007     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4008 
4009     return nFontDescriptor;
4010 }
4011 
4012 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const
4013 {
4014     for( std::map< sal_Int32, sal_Int32 >::const_iterator it =
4015          m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it )
4016     {
4017         rDict.append( m_aBuiltinFonts[it->first].getNameObject() );
4018         rDict.append( ' ' );
4019         rDict.append( it->second );
4020         rDict.append( " 0 R" );
4021     }
4022 }
4023 
4024 bool PDFWriterImpl::emitFonts()
4025 {
4026     if( ! m_pReferenceDevice->ImplGetGraphics() )
4027         return false;
4028 
4029     OStringBuffer aLine( 1024 );
4030 
4031     std::map< sal_Int32, sal_Int32 > aFontIDToObject;
4032 
4033     OUString aTmpName;
4034     osl_createTempFile( NULL, NULL, &aTmpName.pData );
4035     for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it )
4036     {
4037         for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit )
4038         {
4039             sal_Int32 pGlyphIDs[ 256 ];
4040             sal_Int32 pWidths[ 256 ];
4041             sal_uInt8 pEncoding[ 256 ];
4042             sal_Int32 pEncToUnicodeIndex[ 256 ];
4043             sal_Int32 pUnicodesPerGlyph[ 256 ];
4044             std::vector<sal_Ucs> aUnicodes;
4045             aUnicodes.reserve( 256 );
4046             int nGlyphs = 1;
4047             // fill arrays and prepare encoding index map
4048             sal_Int32 nToUnicodeStream = 0;
4049 
4050             rtl_zeroMemory( pGlyphIDs, sizeof( pGlyphIDs ) );
4051             rtl_zeroMemory( pEncoding, sizeof( pEncoding ) );
4052             rtl_zeroMemory( pUnicodesPerGlyph, sizeof( pUnicodesPerGlyph ) );
4053             rtl_zeroMemory( pEncToUnicodeIndex, sizeof( pEncToUnicodeIndex ) );
4054             for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit )
4055             {
4056                 sal_uInt8 nEnc = fit->second.getGlyphId();
4057 
4058                 DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" );
4059                 DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" );
4060 
4061                 pGlyphIDs[ nEnc ] = fit->first;
4062                 pEncoding[ nEnc ] = nEnc;
4063                 pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size());
4064                 pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes();
4065                 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ )
4066                     aUnicodes.push_back( fit->second.getCode( n ) );
4067                 if( fit->second.getCode(0) )
4068                     nToUnicodeStream = 1;
4069                 if( nGlyphs < 256 )
4070                     nGlyphs++;
4071                 else
4072                 {
4073                     DBG_ERROR( "too many glyphs for subset" );
4074                 }
4075             }
4076             FontSubsetInfo aSubsetInfo;
4077             if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
4078             {
4079                 // create font stream
4080                 oslFileHandle aFontFile;
4081                 CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) );
4082                 // get file size
4083                 sal_uInt64 nLength1;
4084                 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) );
4085                 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength1 ) ) );
4086                 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
4087 
4088                 #if OSL_DEBUG_LEVEL > 1
4089                 emitComment( "PDFWriterImpl::emitFonts" );
4090                 #endif
4091                 sal_Int32 nFontStream = createObject();
4092                 sal_Int32 nStreamLengthObject = createObject();
4093                 CHECK_RETURN( updateObject( nFontStream ) );
4094                 aLine.setLength( 0 );
4095                 aLine.append( nFontStream );
4096                 aLine.append( " 0 obj\n"
4097                              "<</Length " );
4098                 aLine.append( (sal_Int32)nStreamLengthObject );
4099                 aLine.append( " 0 R"
4100                              #ifndef DEBUG_DISABLE_PDFCOMPRESSION
4101                              "/Filter/FlateDecode"
4102                              #endif
4103                              "/Length1 " );
4104 
4105                 sal_uInt64 nStartPos = 0;
4106                 if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF )
4107                 {
4108                     aLine.append( (sal_Int32)nLength1 );
4109 
4110                     aLine.append( ">>\n"
4111                                  "stream\n" );
4112                     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4113                     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
4114 
4115                     // copy font file
4116                     beginCompression();
4117                     checkAndEnableStreamEncryption( nFontStream );
4118                     sal_Bool bEOF = sal_False;
4119                     do
4120                     {
4121                         char buf[8192];
4122                         sal_uInt64 nRead;
4123                         CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) );
4124                         CHECK_RETURN( writeBuffer( buf, nRead ) );
4125                         CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) );
4126                     } while( ! bEOF );
4127                 }
4128                 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 )
4129                 {
4130                     // TODO: implement
4131                     DBG_ERROR( "PDFWriterImpl does not support CFF-font subsets yet!" );
4132                 }
4133                 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA?
4134                 {
4135                     boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] );
4136 
4137                     sal_uInt64 nBytesRead = 0;
4138                     CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer.get(), nLength1, &nBytesRead ) ) );
4139                     DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" );
4140                     CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
4141                     // get the PFB-segment lengths
4142                     ThreeInts aSegmentLengths = {0,0,0};
4143                     getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths );
4144                     // the lengths below are mandatory for PDF-exported Type1 fonts
4145                     // because the PFB segment headers get stripped! WhyOhWhy.
4146                     aLine.append( (sal_Int32)aSegmentLengths[0] );
4147                     aLine.append( "/Length2 " );
4148                     aLine.append( (sal_Int32)aSegmentLengths[1] );
4149                     aLine.append( "/Length3 " );
4150                     aLine.append( (sal_Int32)aSegmentLengths[2] );
4151 
4152                     aLine.append( ">>\n"
4153                                  "stream\n" );
4154                     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4155                     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
4156 
4157                     // emit PFB-sections without section headers
4158                     beginCompression();
4159                     checkAndEnableStreamEncryption( nFontStream );
4160                     CHECK_RETURN( writeBuffer( &pBuffer[6], aSegmentLengths[0] ) );
4161                     CHECK_RETURN( writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) );
4162                     CHECK_RETURN( writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) );
4163                 }
4164                 else
4165                 {
4166                     fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType);
4167                     aLine.append( "0 >>\nstream\n" );
4168                 }
4169 
4170                 endCompression();
4171                 disableStreamEncryption();
4172                 // close the file
4173                 osl_closeFile( aFontFile );
4174 
4175                 sal_uInt64 nEndPos = 0;
4176                 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) );
4177                 // end the stream
4178                 aLine.setLength( 0 );
4179                 aLine.append( "\nendstream\nendobj\n\n" );
4180                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4181 
4182                 // emit stream length object
4183                 CHECK_RETURN( updateObject( nStreamLengthObject ) );
4184                 aLine.setLength( 0 );
4185                 aLine.append( nStreamLengthObject );
4186                 aLine.append( " 0 obj\n" );
4187                 aLine.append( (sal_Int64)(nEndPos-nStartPos) );
4188                 aLine.append( "\nendobj\n\n" );
4189                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4190 
4191                 // write font descriptor
4192                 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream );
4193 
4194                 if( nToUnicodeStream )
4195                     nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs );
4196 
4197                 sal_Int32 nFontObject = createObject();
4198                 CHECK_RETURN( updateObject( nFontObject ) );
4199                 aLine.setLength( 0 );
4200                 aLine.append( nFontObject );
4201 
4202                 aLine.append( " 0 obj\n" );
4203                 aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ?
4204                              "<</Type/Font/Subtype/Type1/BaseFont/" :
4205                              "<</Type/Font/Subtype/TrueType/BaseFont/" );
4206                 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine );
4207                 aLine.append( "\n"
4208                              "/FirstChar 0\n"
4209                              "/LastChar " );
4210                 aLine.append( (sal_Int32)(nGlyphs-1) );
4211                 aLine.append( "\n"
4212                              "/Widths[" );
4213                 for( int i = 0; i < nGlyphs; i++ )
4214                 {
4215                     aLine.append( pWidths[ i ] );
4216                     aLine.append( ((i & 15) == 15) ? "\n" : " " );
4217                 }
4218                 aLine.append( "]\n"
4219                              "/FontDescriptor " );
4220                 aLine.append( nFontDescriptor );
4221                 aLine.append( " 0 R\n" );
4222                 if( nToUnicodeStream )
4223                 {
4224                     aLine.append( "/ToUnicode " );
4225                     aLine.append( nToUnicodeStream );
4226                     aLine.append( " 0 R\n" );
4227                 }
4228                 aLine.append( ">>\n"
4229                              "endobj\n\n" );
4230                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4231 
4232                 aFontIDToObject[ lit->m_nFontID ] = nFontObject;
4233             }
4234             else
4235             {
4236                 const ImplFontData* pFont = it->first;
4237                 rtl::OStringBuffer aErrorComment( 256 );
4238                 aErrorComment.append( "CreateFontSubset failed for font \"" );
4239                 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
4240                 aErrorComment.append( '\"' );
4241                 if( pFont->GetSlant() == ITALIC_NORMAL )
4242                     aErrorComment.append( " italic" );
4243                 else if( pFont->GetSlant() == ITALIC_OBLIQUE )
4244                     aErrorComment.append( " oblique" );
4245                 aErrorComment.append( " weight=" );
4246                 aErrorComment.append( sal_Int32(pFont->GetWeight()) );
4247                 emitComment( aErrorComment.getStr() );
4248             }
4249         }
4250     }
4251     osl_removeFile( aTmpName.pData );
4252 
4253     // emit embedded fonts
4254     for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit )
4255     {
4256         std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second );
4257         for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4258         {
4259             CHECK_RETURN( fit->second );
4260             aFontIDToObject[ fit->first ] = fit->second;
4261         }
4262     }
4263 
4264     // emit system fonts
4265     for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit )
4266     {
4267         std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second );
4268         for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4269         {
4270             CHECK_RETURN( fit->second );
4271             aFontIDToObject[ fit->first ] = fit->second;
4272         }
4273     }
4274 
4275     OStringBuffer aFontDict( 1024 );
4276     aFontDict.append( getFontDictObject() );
4277     aFontDict.append( " 0 obj\n"
4278                      "<<" );
4279     int ni = 0;
4280     for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit )
4281     {
4282         aFontDict.append( "/F" );
4283         aFontDict.append( mit->first );
4284         aFontDict.append( ' ' );
4285         aFontDict.append( mit->second );
4286         aFontDict.append( " 0 R" );
4287         if( ((++ni) & 7) == 0 )
4288             aFontDict.append( '\n' );
4289     }
4290     // emit builtin font for widget apperances / variable text
4291     for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin();
4292         it != m_aBuiltinFontToObjectMap.end(); ++it )
4293     {
4294         ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]);
4295         it->second = emitBuiltinFont( &aData, it->second );
4296     }
4297     appendBuiltinFontsToDict( aFontDict );
4298     aFontDict.append( "\n>>\nendobj\n\n" );
4299 
4300     CHECK_RETURN( updateObject( getFontDictObject() ) );
4301     CHECK_RETURN( writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) );
4302     return true;
4303 }
4304 
4305 sal_Int32 PDFWriterImpl::emitResources()
4306 {
4307     // emit shadings
4308     if( ! m_aGradients.empty() )
4309         CHECK_RETURN( emitGradients() );
4310     // emit tilings
4311     if( ! m_aTilings.empty() )
4312         CHECK_RETURN( emitTilings() );
4313 
4314     // emit font dict
4315     CHECK_RETURN( emitFonts() );
4316 
4317     // emit Resource dict
4318     OStringBuffer aLine( 512 );
4319     sal_Int32 nResourceDict = getResourceDictObj();
4320     CHECK_RETURN( updateObject( nResourceDict ) );
4321     aLine.setLength( 0 );
4322     aLine.append( nResourceDict );
4323     aLine.append( " 0 obj\n" );
4324     m_aGlobalResourceDict.append( aLine, getFontDictObject() );
4325     aLine.append( "endobj\n\n" );
4326     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4327     return nResourceDict;
4328 }
4329 
4330 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
4331                                                  sal_Int32 nItemLevel,
4332                                                  sal_Int32 nCurrentItemId )
4333 {
4334     /* The /Count number of an item is
4335        positive: the number of visible subitems
4336        negative: the negative number of subitems that will become visible if
4337                  the item gets opened
4338        see PDF ref 1.4 p 478
4339     */
4340 
4341     sal_Int32 nCount = 0;
4342 
4343     if( m_aContext.OpenBookmarkLevels < 0           || // all levels arevisible
4344         m_aContext.OpenBookmarkLevels >= nItemLevel    // this level is visible
4345       )
4346     {
4347         PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4348         sal_Int32 nChildren = rItem.m_aChildren.size();
4349         for( sal_Int32 i = 0; i < nChildren; i++ )
4350             nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4351         rCounts[nCurrentItemId] = nCount;
4352         // return 1 (this item) + visible sub items
4353         if( nCount < 0 )
4354             nCount = 0;
4355         nCount++;
4356     }
4357     else
4358     {
4359         // this bookmark level is invisible
4360         PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4361         sal_Int32 nChildren = rItem.m_aChildren.size();
4362         rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
4363         for( sal_Int32 i = 0; i < nChildren; i++ )
4364             updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4365         nCount = -1;
4366     }
4367 
4368     return nCount;
4369 }
4370 
4371 sal_Int32 PDFWriterImpl::emitOutline()
4372 {
4373     int i, nItems = m_aOutline.size();
4374 
4375     // do we have an outline at all ?
4376     if( nItems < 2 )
4377         return 0;
4378 
4379     // reserve object numbers for all outline items
4380     for( i = 0; i < nItems; ++i )
4381         m_aOutline[i].m_nObject = createObject();
4382 
4383     // update all parent, next and prev object ids
4384     for( i = 0; i < nItems; ++i )
4385     {
4386         PDFOutlineEntry& rItem = m_aOutline[i];
4387         int nChildren = rItem.m_aChildren.size();
4388 
4389         if( nChildren )
4390         {
4391             for( int n = 0; n < nChildren; ++n )
4392             {
4393                 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
4394 
4395                 rChild.m_nParentObject = rItem.m_nObject;
4396                 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
4397                 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
4398             }
4399 
4400         }
4401     }
4402 
4403     // calculate Count entries for all items
4404     std::vector< sal_Int32 > aCounts( nItems );
4405     updateOutlineItemCount( aCounts, 0, 0 );
4406 
4407     // emit hierarchy
4408     for( i = 0; i < nItems; ++i )
4409     {
4410         PDFOutlineEntry& rItem = m_aOutline[i];
4411         OStringBuffer aLine( 1024 );
4412 
4413         CHECK_RETURN( updateObject( rItem.m_nObject ) );
4414         aLine.append( rItem.m_nObject );
4415         aLine.append( " 0 obj\n" );
4416         aLine.append( "<<" );
4417         // number of visible children (all levels)
4418         if( i > 0 || aCounts[0] > 0 )
4419         {
4420             aLine.append( "/Count " );
4421             aLine.append( aCounts[i] );
4422         }
4423         if( ! rItem.m_aChildren.empty() )
4424         {
4425             // children list: First, Last
4426             aLine.append( "/First " );
4427             aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
4428             aLine.append( " 0 R/Last " );
4429             aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
4430             aLine.append( " 0 R\n" );
4431         }
4432         if( i > 0 )
4433         {
4434             // Title, Dest, Parent, Prev, Next
4435             aLine.append( "/Title" );
4436             appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
4437             aLine.append( "\n" );
4438             // Dest is not required
4439             if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() )
4440             {
4441                 aLine.append( "/Dest" );
4442                 appendDest( rItem.m_nDestID, aLine );
4443             }
4444             aLine.append( "/Parent " );
4445             aLine.append( rItem.m_nParentObject );
4446             aLine.append( " 0 R" );
4447             if( rItem.m_nPrevObject )
4448             {
4449                 aLine.append( "/Prev " );
4450                 aLine.append( rItem.m_nPrevObject );
4451                 aLine.append( " 0 R" );
4452             }
4453             if( rItem.m_nNextObject )
4454             {
4455                 aLine.append( "/Next " );
4456                 aLine.append( rItem.m_nNextObject );
4457                 aLine.append( " 0 R" );
4458             }
4459         }
4460         aLine.append( ">>\nendobj\n\n" );
4461         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4462     }
4463 
4464     return m_aOutline[0].m_nObject;
4465 }
4466 
4467 #undef CHECK_RETURN
4468 #define CHECK_RETURN( x ) if( !x ) return false
4469 
4470 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
4471 {
4472     if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() )
4473     {
4474 #if OSL_DEBUG_LEVEL > 1
4475         fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID );
4476 #endif
4477         return false;
4478     }
4479 
4480 
4481     const PDFDest& rDest		= m_aDests[ nDestID ];
4482     const PDFPage& rDestPage	= m_aPages[ rDest.m_nPage ];
4483 
4484     rBuffer.append( '[' );
4485     rBuffer.append( rDestPage.m_nPageObject );
4486     rBuffer.append( " 0 R" );
4487 
4488     switch( rDest.m_eType )
4489     {
4490         case PDFWriter::XYZ:
4491         default:
4492             rBuffer.append( "/XYZ " );
4493             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4494             rBuffer.append( ' ' );
4495             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4496             rBuffer.append( " 0" );
4497             break;
4498         case PDFWriter::Fit:
4499             rBuffer.append( "/Fit" );
4500             break;
4501         case PDFWriter::FitRectangle:
4502             rBuffer.append( "/FitR " );
4503             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4504             rBuffer.append( ' ' );
4505             appendFixedInt( rDest.m_aRect.Top(), rBuffer );
4506             rBuffer.append( ' ' );
4507             appendFixedInt( rDest.m_aRect.Right(), rBuffer );
4508             rBuffer.append( ' ' );
4509             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4510             break;
4511         case PDFWriter::FitHorizontal:
4512             rBuffer.append( "/FitH " );
4513             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4514             break;
4515         case PDFWriter::FitVertical:
4516             rBuffer.append( "/FitV " );
4517             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4518             break;
4519         case PDFWriter::FitPageBoundingBox:
4520             rBuffer.append( "/FitB" );
4521             break;
4522         case PDFWriter::FitPageBoundingBoxHorizontal:
4523             rBuffer.append( "/FitBH " );
4524             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4525             break;
4526         case PDFWriter::FitPageBoundingBoxVertical:
4527             rBuffer.append( "/FitBV " );
4528             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4529             break;
4530     }
4531     rBuffer.append( ']' );
4532 
4533     return true;
4534 }
4535 
4536 bool PDFWriterImpl::emitLinkAnnotations()
4537 {
4538     int nAnnots = m_aLinks.size();
4539     for( int i = 0; i < nAnnots; i++ )
4540     {
4541         const PDFLink& rLink			= m_aLinks[i];
4542         if( ! updateObject( rLink.m_nObject ) )
4543             continue;
4544 
4545         OStringBuffer aLine( 1024 );
4546         aLine.append( rLink.m_nObject );
4547         aLine.append( " 0 obj\n" );
4548 //i59651  key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4549 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4550         aLine.append( "<</Type/Annot" );
4551         if( m_bIsPDF_A1 )
4552             aLine.append( "/F 4" );
4553         aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
4554 
4555         appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
4556         aLine.append( ' ' );
4557         appendFixedInt( rLink.m_aRect.Top(), aLine );
4558         aLine.append( ' ' );
4559         appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
4560         aLine.append( ' ' );
4561         appendFixedInt( rLink.m_aRect.Bottom(), aLine );
4562         aLine.append( "]" );
4563         if( rLink.m_nDest >= 0 )
4564         {
4565             aLine.append( "/Dest" );
4566             appendDest( rLink.m_nDest, aLine );
4567         }
4568         else
4569         {
4570 /*--->i56629
4571 destination is external to the document, so
4572 we check in the following sequence:
4573 
4574  if target type is neither .pdf, nor .od[tpgs], then
4575           check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
4576                              end processing
4577  else if target is .od[tpgs]: then
4578       if conversion of type from od[tpgs]  to pdf is requested, convert it and this becomes the new target file
4579       processing continue
4580 
4581  if (new)target is .pdf : then
4582      if GotToR is requested, then
4583            convert the target in GoToR where the fragment of the URI is
4584            considered the named destination in the target file, set relative or absolute as requested
4585      else strip the fragment from URL and then set URI or 'launch application' as requested
4586 */
4587 //
4588 // FIXME: check if the decode mechanisms for URL processing throughout this implementation
4589 // are the correct one!!
4590 //
4591 // extract target file type
4592             INetURLObject aDocumentURL( m_aContext.BaseURL );
4593             INetURLObject aTargetURL( rLink.m_aURL );
4594             sal_Int32   nChangeFileExtensionToPDF = 0;
4595             sal_Int32   nSetGoToRMode = 0;
4596             sal_Bool    bTargetHasPDFExtension = sal_False;
4597             INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
4598 			sal_Bool    bIsUNCPath = sal_False;
4599 // check if the protocol is a known one, or if there is no protocol at all (on target only)
4600 // if there is no protocol, make the target relative to the current document directory
4601 // getting the needed URL information from the current document path
4602             if( eTargetProtocol == INET_PROT_NOT_VALID )
4603             {
4604 				if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.compareToAscii( "\\\\\\\\", 4 ) == 0)
4605 				{
4606 					bIsUNCPath = sal_True;
4607 				}
4608 				else
4609 				{
4610 					INetURLObject aNewBase( aDocumentURL );//duplicate document URL
4611 					aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
4612 											  //target document
4613 					aNewBase.insertName( rLink.m_aURL );
4614 					aTargetURL = aNewBase;//reassign the new target URL
4615 //recompute the target protocol, with the new URL
4616 //normal URL processing resumes
4617 					eTargetProtocol = aTargetURL.GetProtocol();
4618 				}
4619             }
4620 
4621             rtl::OUString aFileExtension = aTargetURL.GetFileExtension();
4622 
4623 // Check if the URL ends in '/': if yes it's a directory,
4624 // it will be forced to a URI link.
4625 // possibly a malformed URI, leave it as it is, force as URI
4626             if( aTargetURL.hasFinalSlash() )
4627                 m_aContext.DefaultLinkAction = PDFWriter::URIAction;
4628 
4629             if( aFileExtension.getLength() > 0 )
4630             {
4631                 if( m_aContext.ConvertOOoTargetToPDFTarget )
4632                 {
4633 //examine the file type (.odm .odt. .odp, odg, ods)
4634                     if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odm" ) ) ) )
4635                         nChangeFileExtensionToPDF++;
4636                     if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odt" ) ) ) )
4637                         nChangeFileExtensionToPDF++;
4638                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odp" ) ) ) )
4639                         nChangeFileExtensionToPDF++;
4640                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odg" ) ) ) )
4641                         nChangeFileExtensionToPDF++;
4642                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ods" ) ) ) )
4643                         nChangeFileExtensionToPDF++;
4644                     if( nChangeFileExtensionToPDF )
4645                         aTargetURL.setExtension(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4646                 }
4647 //check if extension is pdf, see if GoToR should be forced
4648                 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4649                 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
4650                     nSetGoToRMode++;
4651             }
4652 //prepare the URL, if relative or not
4653             INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
4654 //queue the string common to all types of actions
4655             aLine.append( "/A<</Type/Action/S");
4656 			if( bIsUNCPath ) // handle Win UNC paths
4657 			{
4658 				aLine.append( "/Launch/Win<</F" );
4659 				// INetURLObject is not good with UNC paths, use original path
4660 				appendLiteralStringEncrypt(  rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4661 				aLine.append( ">>" );
4662 			}
4663 			else
4664 			{
4665 			    bool bSetRelative = false;
4666 			    bool bFileSpec = false;
4667 //check if relative file link is requested and if the protocol is 'file://'
4668 				if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE )
4669 					bSetRelative = true;
4670 
4671 				rtl::OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is,
4672 				if( nSetGoToRMode == 0 )
4673 				{
4674 					switch( m_aContext.DefaultLinkAction )
4675 					{
4676 					default:
4677 					case PDFWriter::URIAction :
4678 					case PDFWriter::URIActionDestination :
4679 						aLine.append( "/URI/URI" );
4680 						break;
4681 					case PDFWriter::LaunchAction:
4682 // now:
4683 // if a launch action is requested and the hyperlink target has a fragment
4684 // and the target file does not have a pdf extension, or it's not a 'file:://' protocol
4685 // then force the uri action on it
4686 // This code will permit the correct opening of application on web pages, the one that
4687 // normally have fragments (but I may be wrong...)
4688 // and will force the use of URI when the protocol is not file://
4689 						if( (aFragment.getLength() > 0 && !bTargetHasPDFExtension) ||
4690 										eTargetProtocol != INET_PROT_FILE )
4691 							aLine.append( "/URI/URI" );
4692 						else
4693 						{
4694 							aLine.append( "/Launch/F" );
4695 							bFileSpec = true;
4696 						}
4697 						break;
4698 					}
4699 				}
4700 //fragment are encoded in the same way as in the named destination processing
4701 				if( nSetGoToRMode )
4702 				{//add the fragment
4703 				    rtl::OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET );
4704 					aLine.append("/GoToR");
4705 					aLine.append("/F");
4706 					bFileSpec = true;
4707 					appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark,
4708 																						 INetURLObject::WAS_ENCODED,
4709 																						 INetURLObject::DECODE_WITH_CHARSET ) :
4710 																   aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4711 					if( aFragment.getLength() > 0 )
4712 					{
4713 						aLine.append("/D/");
4714 						appendDestinationName( aFragment , aLine );
4715 					}
4716 				}
4717 				else
4718 				{
4719 // change the fragment to accomodate the bookmark (only if the file extension is PDF and
4720 // the requested action is of the correct type)
4721 					if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination &&
4722 							   bTargetHasPDFExtension && aFragment.getLength() > 0 )
4723 					{
4724 						OStringBuffer aLineLoc( 1024 );
4725 						appendDestinationName( aFragment , aLineLoc );
4726 //substitute the fragment
4727 						aTargetURL.SetMark( aLineLoc.getStr() );
4728 					}
4729 					rtl::OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE );
4730 // check if we have a URL available, if the string is empty, set it as the original one
4731 //                 if( aURL.getLength() == 0 )
4732 //                     appendLiteralStringEncrypt( rLink.m_aURL , rLink.m_nObject, aLine );
4733 //                 else
4734 						appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL,
4735 						                                                                    INetURLObject::WAS_ENCODED,
4736 						                                                                    bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE
4737 						                                                                    ) :
4738 																   aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4739 				}
4740 //<--- i56629
4741 			}
4742             aLine.append( ">>\n" );
4743         }
4744         if( rLink.m_nStructParent > 0 )
4745         {
4746             aLine.append( "/StructParent " );
4747             aLine.append( rLink.m_nStructParent );
4748         }
4749         aLine.append( ">>\nendobj\n\n" );
4750         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4751     }
4752 
4753     return true;
4754 }
4755 
4756 bool PDFWriterImpl::emitNoteAnnotations()
4757 {
4758     // emit note annotations
4759     int nAnnots = m_aNotes.size();
4760     for( int i = 0; i < nAnnots; i++ )
4761     {
4762         const PDFNoteEntry& rNote		= m_aNotes[i];
4763         if( ! updateObject( rNote.m_nObject ) )
4764             return false;
4765 
4766         OStringBuffer aLine( 1024 );
4767         aLine.append( rNote.m_nObject );
4768         aLine.append( " 0 obj\n" );
4769 //i59651  key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4770 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4771         aLine.append( "<</Type/Annot" );
4772         if( m_bIsPDF_A1 )
4773             aLine.append( "/F 4" );
4774         aLine.append( "/Subtype/Text/Rect[" );
4775 
4776         appendFixedInt( rNote.m_aRect.Left(), aLine );
4777         aLine.append( ' ' );
4778         appendFixedInt( rNote.m_aRect.Top(), aLine );
4779         aLine.append( ' ' );
4780         appendFixedInt( rNote.m_aRect.Right(), aLine );
4781         aLine.append( ' ' );
4782         appendFixedInt( rNote.m_aRect.Bottom(), aLine );
4783         aLine.append( "]" );
4784 
4785         // contents of the note (type text string)
4786         aLine.append( "/Contents\n" );
4787         appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
4788         aLine.append( "\n" );
4789 
4790         // optional title
4791         if( rNote.m_aContents.Title.Len() )
4792         {
4793             aLine.append( "/T" );
4794             appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
4795             aLine.append( "\n" );
4796         }
4797 
4798         aLine.append( ">>\nendobj\n\n" );
4799         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4800     }
4801     return true;
4802 }
4803 
4804 Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font&  rAppSetFont )
4805 {
4806     bool bAdjustSize = false;
4807 
4808     Font aFont( rControlFont );
4809     if( ! aFont.GetName().Len() )
4810     {
4811         aFont = rAppSetFont;
4812         if( rControlFont.GetHeight() )
4813             aFont.SetSize( Size( 0, rControlFont.GetHeight() ) );
4814         else
4815             bAdjustSize = true;
4816         if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
4817             aFont.SetItalic( rControlFont.GetItalic() );
4818         if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
4819             aFont.SetWeight( rControlFont.GetWeight() );
4820     }
4821     else if( ! aFont.GetHeight() )
4822     {
4823         aFont.SetSize( rAppSetFont.GetSize() );
4824         bAdjustSize = true;
4825     }
4826     if( bAdjustSize )
4827     {
4828         Size aFontSize = aFont.GetSize();
4829         OutputDevice* pDefDev = Application::GetDefaultDevice();
4830         aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
4831         aFont.SetSize( aFontSize );
4832     }
4833     return aFont;
4834 }
4835 
4836 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont )
4837 {
4838     sal_Int32 nBest = 4; // default to Helvetica
4839     OUString aFontName( rFont.GetName() );
4840     aFontName = aFontName.toAsciiLowerCase();
4841 
4842     if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "times" ) ) ) != -1 )
4843         nBest = 8;
4844     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "courier" ) ) ) != -1 )
4845         nBest = 0;
4846     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "dingbats" ) ) ) != -1 )
4847         nBest = 13;
4848     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "symbol" ) ) ) != -1 )
4849         nBest = 12;
4850     if( nBest < 12 )
4851     {
4852         if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
4853             nBest += 1;
4854         if( rFont.GetWeight() > WEIGHT_MEDIUM )
4855             nBest += 2;
4856     }
4857 
4858     if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() )
4859         m_aBuiltinFontToObjectMap[ nBest ] = createObject();
4860 
4861     return nBest;
4862 }
4863 
4864 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
4865 {
4866     return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1;
4867 }
4868 
4869 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget )
4870 {
4871     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4872 
4873     // save graphics state
4874     push( sal::static_int_cast<sal_uInt16>(~0U) );
4875 
4876     // transform relative to control's coordinates since an
4877     // appearance stream is a form XObject
4878     // this relies on the m_aRect member of rButton NOT already being transformed
4879     // to default user space
4880     if( rWidget.Background || rWidget.Border )
4881     {
4882         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) );
4883         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) );
4884         drawRectangle( rWidget.Location );
4885     }
4886     // prepare font to use
4887     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
4888     setFont( aFont );
4889     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
4890 
4891     drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
4892 
4893     // create DA string while local mapmode is still in place
4894     // (that is before endRedirect())
4895     OStringBuffer aDA( 256 );
4896     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
4897     Font aDummyFont( String( RTL_CONSTASCII_USTRINGPARAM( "Helvetica" ) ), aFont.GetSize() );
4898     sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont );
4899     aDA.append( ' ' );
4900     aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() );
4901     aDA.append( ' ' );
4902     m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4903     aDA.append( " Tf" );
4904     rButton.m_aDAString = aDA.makeStringAndClear();
4905 
4906     pop();
4907 
4908     rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
4909 
4910     /* seems like a bad hack but at least works in both AR5 and 6:
4911        we draw the button ourselves and tell AR
4912        the button would be totally transparent with no text
4913 
4914        One would expect that simply setting a normal appearance
4915        should suffice, but no, as soon as the user actually presses
4916        the button and an action is tied to it (gasp! a button that
4917        does something) the appearance gets replaced by some crap that AR
4918        creates on the fly even if no DA or MK is given. On AR6 at least
4919        the DA and MK work as expected, but on AR5 this creates a region
4920        filled with the background color but nor text. Urgh.
4921     */
4922     rButton.m_aMKDict = "/BC [] /BG [] /CA";
4923     rButton.m_aMKDictCAString = "";
4924 }
4925 
4926 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern,
4927                                      const PDFWriter::AnyWidget& rWidget,
4928                                      const StyleSettings& rSettings )
4929 {
4930     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
4931 
4932     if( rWidget.Background || rWidget.Border )
4933     {
4934         if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) )
4935         {
4936             sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500;
4937             if( nDelta < 1 )
4938                 nDelta = 1;
4939             setLineColor( Color( COL_TRANSPARENT ) );
4940             Rectangle aRect = rIntern.m_aRect;
4941             setFillColor( rSettings.GetLightBorderColor() );
4942             drawRectangle( aRect );
4943             aRect.Left()  += nDelta; aRect.Top()     += nDelta;
4944             aRect.Right() -= nDelta; aRect.Bottom()  -= nDelta;
4945             setFillColor( rSettings.GetFieldColor() );
4946             drawRectangle( aRect );
4947             setFillColor( rSettings.GetLightColor() );
4948             drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
4949             drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
4950             setFillColor( rSettings.GetDarkShadowColor() );
4951             drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
4952             drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
4953         }
4954         else
4955         {
4956             setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) );
4957             setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4958             drawRectangle( rIntern.m_aRect );
4959         }
4960 
4961         if( rWidget.Border )
4962         {
4963             // adjust edit area accounting for border
4964             sal_Int32 nDelta = aFont.GetHeight()/4;
4965             if( nDelta < 1 )
4966                 nDelta = 1;
4967             rIntern.m_aRect.Left()	+= nDelta;
4968             rIntern.m_aRect.Top()	+= nDelta;
4969             rIntern.m_aRect.Right()	-= nDelta;
4970             rIntern.m_aRect.Bottom()-= nDelta;
4971         }
4972     }
4973     return aFont;
4974 }
4975 
4976 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget )
4977 {
4978     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4979     SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
4980 
4981     push( sal::static_int_cast<sal_uInt16>(~0U) );
4982 
4983     // prepare font to use, draw field border
4984     Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
4985     sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
4986 
4987     // prepare DA string
4988     OStringBuffer aDA( 32 );
4989     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4990     aDA.append( ' ' );
4991     if( m_aContext.FieldsUseSystemFonts )
4992     {
4993         aDA.append( "/F" );
4994         aDA.append( nBest );
4995 
4996         OStringBuffer aDR( 32 );
4997         aDR.append( "/Font " );
4998         aDR.append( getFontDictObject() );
4999         aDR.append( " 0 R" );
5000         rEdit.m_aDRDict = aDR.makeStringAndClear();
5001     }
5002     else
5003         aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5004     aDA.append( ' ' );
5005     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
5006     aDA.append( " Tf" );
5007 
5008     /*  create an empty appearance stream, let the viewer create
5009         the appearance at runtime. This is because AR5 seems to
5010         paint the widget appearance always, and a dynamically created
5011         appearance on top of it. AR6 is well behaved in that regard, so
5012         that behaviour seems to be a bug. Anyway this empty appearance
5013         relies on /NeedAppearances in the AcroForm dictionary set to "true"
5014      */
5015     beginRedirect( pEditStream, rEdit.m_aRect );
5016     OStringBuffer aAppearance( 32 );
5017     aAppearance.append( "/Tx BMC\nEMC\n" );
5018     writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5019 
5020     endRedirect();
5021     pop();
5022 
5023     rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
5024 
5025     rEdit.m_aDAString = aDA.makeStringAndClear();
5026 }
5027 
5028 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget )
5029 {
5030     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5031     SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
5032 
5033     push( sal::static_int_cast<sal_uInt16>(~0U) );
5034 
5035     // prepare font to use, draw field border
5036     Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
5037     sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
5038 
5039     beginRedirect( pListBoxStream, rBox.m_aRect );
5040     OStringBuffer aAppearance( 64 );
5041 
5042 #if 0
5043     if( ! rWidget.DropDown )
5044     {
5045         // prepare linewidth for DA string hack, see below
5046         Size aFontSize = lcl_convert( m_aGraphicsStack.front().m_aMapMode,
5047                                       m_aMapMode,
5048                                       getReferenceDevice(),
5049                                       Size( 0, aFont.GetHeight() ) );
5050         sal_Int32 nLW = aFontSize.Height() / 40;
5051         appendFixedInt( nLW > 0 ? nLW : 1, aAppearance );
5052         aAppearance.append( " w\n" );
5053         writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5054         aAppearance.setLength( 0 );
5055     }
5056 #endif
5057 
5058     setLineColor( Color( COL_TRANSPARENT ) );
5059     setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
5060     drawRectangle( rBox.m_aRect );
5061 
5062     // empty appearance, see createDefaultEditAppearance for reference
5063     aAppearance.append( "/Tx BMC\nEMC\n" );
5064     writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5065 
5066     endRedirect();
5067     pop();
5068 
5069     rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
5070 
5071     // prepare DA string
5072     OStringBuffer aDA( 256 );
5073 #if 0
5074     if( !rWidget.DropDown )
5075     {
5076         /* another of AR5's peculiarities: the selected item of a choice
5077            field is highlighted using the non stroking color - same as the
5078            text color. so workaround that by using text rendering mode 2
5079            (fill, then stroke) and set the stroking color
5080          */
5081         appendStrokingColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ), aDA );
5082         aDA.append( " 2 Tr " );
5083     }
5084 #endif
5085     // prepare DA string
5086     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
5087     aDA.append( ' ' );
5088     if( m_aContext.FieldsUseSystemFonts )
5089     {
5090         aDA.append( "/F" );
5091         aDA.append( nBest );
5092 
5093         OStringBuffer aDR( 32 );
5094         aDR.append( "/Font " );
5095         aDR.append( getFontDictObject() );
5096         aDR.append( " 0 R" );
5097         rBox.m_aDRDict = aDR.makeStringAndClear();
5098     }
5099     else
5100         aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5101     aDA.append( ' ' );
5102     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
5103     aDA.append( " Tf" );
5104     rBox.m_aDAString = aDA.makeStringAndClear();
5105 }
5106 
5107 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget )
5108 {
5109     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5110 
5111     // save graphics state
5112     push( sal::static_int_cast<sal_uInt16>(~0U) );
5113 
5114     if( rWidget.Background || rWidget.Border )
5115     {
5116         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5117         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5118         drawRectangle( rBox.m_aRect );
5119     }
5120 
5121     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5122     setFont( aFont );
5123     Size aFontSize = aFont.GetSize();
5124     if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5125         aFontSize.Height() = rBox.m_aRect.GetHeight();
5126     sal_Int32 nDelta = aFontSize.Height()/10;
5127     if( nDelta < 1 )
5128         nDelta = 1;
5129 
5130     Rectangle aCheckRect, aTextRect;
5131     if( rWidget.ButtonIsLeft )
5132     {
5133         aCheckRect.Left()	= rBox.m_aRect.Left() + nDelta;
5134         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5135         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5136         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5137 
5138         // #i74206# handle small controls without text area
5139         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5140         {
5141             aCheckRect.Right()  -= nDelta;
5142             aCheckRect.Top()    += nDelta/2;
5143             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5144         }
5145 
5146         aTextRect.Left()	= rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5147         aTextRect.Top()		= rBox.m_aRect.Top();
5148         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5149         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5150     }
5151     else
5152     {
5153         aCheckRect.Left()	= rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5154         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5155         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5156         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5157 
5158         // #i74206# handle small controls without text area
5159         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5160         {
5161             aCheckRect.Left()   += nDelta;
5162             aCheckRect.Top()    += nDelta/2;
5163             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5164         }
5165 
5166         aTextRect.Left()	= rBox.m_aRect.Left();
5167         aTextRect.Top()		= rBox.m_aRect.Top();
5168         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5169         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5170     }
5171     setLineColor( Color( COL_BLACK ) );
5172     setFillColor( Color( COL_TRANSPARENT ) );
5173     OStringBuffer aLW( 32 );
5174     aLW.append( "q " );
5175     m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
5176     aLW.append( " w " );
5177     writeBuffer( aLW.getStr(), aLW.getLength() );
5178     drawRectangle( aCheckRect );
5179     writeBuffer( " Q\n", 3 );
5180     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5181     drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5182 
5183     pop();
5184 
5185     OStringBuffer aDA( 256 );
5186     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5187     sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
5188     aDA.append( ' ' );
5189     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5190     aDA.append( " 0 Tf" );
5191     rBox.m_aDAString = aDA.makeStringAndClear();
5192     rBox.m_aMKDict = "/CA";
5193     rBox.m_aMKDictCAString = "8";
5194     rBox.m_aRect = aCheckRect;
5195 
5196     // create appearance streams
5197     sal_Char cMark = '8';
5198     sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)];
5199     nCharXOffset *= aCheckRect.GetHeight();
5200     nCharXOffset /= 2000;
5201     sal_Int32 nCharYOffset = 1000-
5202         (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative
5203     nCharYOffset *= aCheckRect.GetHeight();
5204     nCharYOffset /= 2000;
5205 
5206     SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5207     beginRedirect( pCheckStream, aCheckRect );
5208     aDA.append( "/Tx BMC\nq BT\n" );
5209     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5210     aDA.append( ' ' );
5211     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5212     aDA.append( ' ' );
5213     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5214     aDA.append( " Tf\n" );
5215     m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
5216 	aDA.append( " " );
5217 	m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
5218 	aDA.append( " Td (" );
5219 	aDA.append( cMark );
5220 	aDA.append( ") Tj\nET\nQ\nEMC\n" );
5221     writeBuffer( aDA.getStr(), aDA.getLength() );
5222     endRedirect();
5223     rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5224 
5225     SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5226     beginRedirect( pUncheckStream, aCheckRect );
5227     writeBuffer( "/Tx BMC\nEMC\n", 12 );
5228     endRedirect();
5229     rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5230 }
5231 
5232 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget )
5233 {
5234     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5235 
5236     // save graphics state
5237     push( sal::static_int_cast<sal_uInt16>(~0U) );
5238 
5239     if( rWidget.Background || rWidget.Border )
5240     {
5241         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5242         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5243         drawRectangle( rBox.m_aRect );
5244     }
5245 
5246     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5247     setFont( aFont );
5248     Size aFontSize = aFont.GetSize();
5249     if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5250         aFontSize.Height() = rBox.m_aRect.GetHeight();
5251     sal_Int32 nDelta = aFontSize.Height()/10;
5252     if( nDelta < 1 )
5253         nDelta = 1;
5254 
5255     Rectangle aCheckRect, aTextRect;
5256     if( rWidget.ButtonIsLeft )
5257     {
5258         aCheckRect.Left()	= rBox.m_aRect.Left() + nDelta;
5259         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5260         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5261         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5262 
5263         // #i74206# handle small controls without text area
5264         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5265         {
5266             aCheckRect.Right()  -= nDelta;
5267             aCheckRect.Top()    += nDelta/2;
5268             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5269         }
5270 
5271         aTextRect.Left()	= rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5272         aTextRect.Top()		= rBox.m_aRect.Top();
5273         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5274         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5275     }
5276     else
5277     {
5278         aCheckRect.Left()	= rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5279         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5280         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5281         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5282 
5283         // #i74206# handle small controls without text area
5284         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5285         {
5286             aCheckRect.Left()   += nDelta;
5287             aCheckRect.Top()    += nDelta/2;
5288             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5289         }
5290 
5291         aTextRect.Left()	= rBox.m_aRect.Left();
5292         aTextRect.Top()		= rBox.m_aRect.Top();
5293         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5294         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5295     }
5296     setLineColor( Color( COL_BLACK ) );
5297     setFillColor( Color( COL_TRANSPARENT ) );
5298     OStringBuffer aLW( 32 );
5299     aLW.append( "q " );
5300     m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
5301     aLW.append( " w " );
5302     writeBuffer( aLW.getStr(), aLW.getLength() );
5303     drawEllipse( aCheckRect );
5304     writeBuffer( " Q\n", 3 );
5305     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5306     drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5307 
5308     pop();
5309 
5310     OStringBuffer aDA( 256 );
5311     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5312     sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
5313     aDA.append( ' ' );
5314     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5315     aDA.append( " 0 Tf" );
5316     rBox.m_aDAString = aDA.makeStringAndClear();
5317 //to encrypt this (el)
5318     rBox.m_aMKDict = "/CA";
5319 //after this assignement, to m_aMKDic cannot be added anything
5320     rBox.m_aMKDictCAString = "l";
5321 
5322     rBox.m_aRect = aCheckRect;
5323 
5324     // create appearance streams
5325     push( sal::static_int_cast<sal_uInt16>(~0U) );
5326     SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5327 
5328     beginRedirect( pCheckStream, aCheckRect );
5329     aDA.append( "/Tx BMC\nq BT\n" );
5330     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5331     aDA.append( ' ' );
5332     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5333     aDA.append( ' ' );
5334     m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5335     aDA.append( " Tf\n0 0 Td\nET\nQ\n" );
5336     writeBuffer( aDA.getStr(), aDA.getLength() );
5337     setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5338     setLineColor( Color( COL_TRANSPARENT ) );
5339     aCheckRect.Left()	+= 3*nDelta;
5340     aCheckRect.Top()	+= 3*nDelta;
5341     aCheckRect.Bottom()	-= 3*nDelta;
5342     aCheckRect.Right()	-= 3*nDelta;
5343     drawEllipse( aCheckRect );
5344     writeBuffer( "\nEMC\n", 5 );
5345     endRedirect();
5346 
5347     pop();
5348     rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5349 
5350     SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5351     beginRedirect( pUncheckStream, aCheckRect );
5352     writeBuffer( "/Tx BMC\nEMC\n", 12 );
5353     endRedirect();
5354     rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5355 }
5356 
5357 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
5358 {
5359 
5360     // TODO: check and insert default streams
5361     rtl::OString aStandardAppearance;
5362     switch( rWidget.m_eType )
5363     {
5364         case PDFWriter::CheckBox:
5365             aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
5366             break;
5367         default:
5368             break;
5369     }
5370 
5371     if( rWidget.m_aAppearances.size() )
5372     {
5373         rAnnotDict.append( "/AP<<\n" );
5374         for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it )
5375         {
5376             rAnnotDict.append( "/" );
5377             rAnnotDict.append( dict_it->first );
5378             bool bUseSubDict = (dict_it->second.size() > 1);
5379             rAnnotDict.append( bUseSubDict ? "<<" : " " );
5380 
5381             for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin();
5382                  stream_it != dict_it->second.end(); ++stream_it )
5383             {
5384                 SvMemoryStream* pApppearanceStream = stream_it->second;
5385                 dict_it->second[ stream_it->first ] = NULL;
5386 
5387                 bool bDeflate = compressStream( pApppearanceStream );
5388 
5389                 pApppearanceStream->Seek( STREAM_SEEK_TO_END );
5390                 sal_Int64 nStreamLen = pApppearanceStream->Tell();
5391                 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
5392                 sal_Int32 nObject = createObject();
5393                 CHECK_RETURN( updateObject( nObject ) );
5394                 #if OSL_DEBUG_LEVEL > 1
5395                 emitComment( "PDFWriterImpl::emitAppearances" );
5396                 #endif
5397                 OStringBuffer aLine;
5398                 aLine.append( nObject );
5399 
5400                 aLine.append( " 0 obj\n"
5401                               "<</Type/XObject\n"
5402                               "/Subtype/Form\n"
5403                               "/BBox[0 0 " );
5404                 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
5405                 aLine.append( " " );
5406                 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
5407                 aLine.append( "]\n"
5408                               "/Resources " );
5409                 aLine.append( getResourceDictObj() );
5410                 aLine.append( " 0 R\n"
5411                               "/Length " );
5412                 aLine.append( nStreamLen );
5413                 aLine.append( "\n" );
5414                 if( bDeflate )
5415                     aLine.append( "/Filter/FlateDecode\n" );
5416                 aLine.append( ">>\nstream\n" );
5417                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5418                 checkAndEnableStreamEncryption( nObject );
5419                 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
5420                 disableStreamEncryption();
5421                 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
5422 
5423                 if( bUseSubDict )
5424                 {
5425                     rAnnotDict.append( " /" );
5426                     rAnnotDict.append( stream_it->first );
5427                     rAnnotDict.append( " " );
5428                 }
5429                 rAnnotDict.append( nObject );
5430                 rAnnotDict.append( " 0 R" );
5431 
5432                 delete pApppearanceStream;
5433             }
5434 
5435             rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
5436         }
5437         rAnnotDict.append( ">>\n" );
5438         if( aStandardAppearance.getLength() )
5439         {
5440             rAnnotDict.append( "/AS /" );
5441             rAnnotDict.append( aStandardAppearance );
5442             rAnnotDict.append( "\n" );
5443         }
5444     }
5445 
5446     return true;
5447 }
5448 
5449 bool PDFWriterImpl::emitWidgetAnnotations()
5450 {
5451     ensureUniqueRadioOnValues();
5452 
5453     int nAnnots = m_aWidgets.size();
5454     for( int a = 0; a < nAnnots; a++ )
5455     {
5456         PDFWidget& rWidget = m_aWidgets[a];
5457 
5458         OStringBuffer aLine( 1024 );
5459         OStringBuffer aValue( 256 );
5460         aLine.append( rWidget.m_nObject );
5461         aLine.append( " 0 obj\n"
5462                       "<<" );
5463         if( rWidget.m_eType != PDFWriter::Hierarchy )
5464         {
5465             // emit widget annotation only for terminal fields
5466             if( rWidget.m_aKids.empty() )
5467             {
5468                 aLine.append( "/Type/Annot/Subtype/Widget/F 4\n"
5469                               "/Rect[" );
5470                 appendFixedInt( rWidget.m_aRect.Left()-1, aLine );
5471                 aLine.append( ' ' );
5472                 appendFixedInt( rWidget.m_aRect.Top()+1, aLine );
5473                 aLine.append( ' ' );
5474                 appendFixedInt( rWidget.m_aRect.Right()+1, aLine );
5475                 aLine.append( ' ' );
5476                 appendFixedInt( rWidget.m_aRect.Bottom()-1, aLine );
5477                 aLine.append( "]\n" );
5478             }
5479             aLine.append( "/FT/" );
5480             switch( rWidget.m_eType )
5481             {
5482                 case PDFWriter::RadioButton:
5483                 case PDFWriter::CheckBox:
5484                     // for radio buttons only the RadioButton field, not the
5485                     // CheckBox children should have a value, else acrobat reader
5486                     // does not always check the right button
5487                     // of course real check boxes (not belonging to a readio group)
5488                     // need their values, too
5489                     if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
5490                     {
5491                         aValue.append( "/" );
5492                         // check for radio group with all buttons unpressed
5493                         if( rWidget.m_aValue.getLength() == 0 )
5494                             aValue.append( "Off" );
5495                         else
5496                             appendName( rWidget.m_aValue, aValue );
5497                     }
5498                 case PDFWriter::PushButton:
5499                     aLine.append( "Btn" );
5500                     break;
5501                 case PDFWriter::ListBox:
5502                     if( rWidget.m_nFlags & 0x200000 ) // multiselect
5503                     {
5504                         aValue.append( "[" );
5505                         for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
5506                         {
5507                             sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
5508                             if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
5509                                 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
5510                         }
5511                         aValue.append( "]" );
5512                     }
5513                     else if( rWidget.m_aSelectedEntries.size() > 0 &&
5514                              rWidget.m_aSelectedEntries[0] >= 0 &&
5515                              rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
5516                     {
5517                         appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
5518                     }
5519                     else
5520                         appendUnicodeTextStringEncrypt( rtl::OUString(), rWidget.m_nObject, aValue );
5521                     aLine.append( "Ch" );
5522                     break;
5523                 case PDFWriter::ComboBox:
5524                     appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5525                     aLine.append( "Ch" );
5526                     break;
5527                 case PDFWriter::Edit:
5528                     aLine.append( "Tx" );
5529                     appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5530                     break;
5531                 case PDFWriter::Hierarchy: // make the compiler happy
5532                     break;
5533             }
5534             aLine.append( "\n" );
5535             aLine.append( "/P " );
5536             aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
5537             aLine.append( " 0 R\n" );
5538         }
5539         if( rWidget.m_nParent )
5540         {
5541             aLine.append( "/Parent " );
5542             aLine.append( rWidget.m_nParent );
5543             aLine.append( " 0 R\n" );
5544         }
5545         if( rWidget.m_aKids.size() )
5546         {
5547             aLine.append( "/Kids[" );
5548             for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ )
5549             {
5550                 aLine.append( rWidget.m_aKids[i] );
5551                 aLine.append( " 0 R" );
5552                 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5553             }
5554             aLine.append( "]\n" );
5555         }
5556         if( rWidget.m_aName.getLength() )
5557         {
5558             aLine.append( "/T" );
5559             appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
5560             aLine.append( "\n" );
5561         }
5562         if( m_aContext.Version > PDFWriter::PDF_1_2 && rWidget.m_aDescription.getLength() )
5563         {
5564             // the alternate field name should be unicode able since it is
5565             // supposed to be used in UI
5566             aLine.append( "/TU" );
5567             appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
5568             aLine.append( "\n" );
5569         }
5570 
5571         if( rWidget.m_nFlags )
5572         {
5573             aLine.append( "/Ff " );
5574             aLine.append( rWidget.m_nFlags );
5575             aLine.append( "\n" );
5576         }
5577         if( aValue.getLength() )
5578         {
5579             OString aVal = aValue.makeStringAndClear();
5580             aLine.append( "/V " );
5581             aLine.append( aVal );
5582             aLine.append( "\n"
5583                           "/DV " );
5584             aLine.append( aVal );
5585             aLine.append( "\n" );
5586         }
5587         if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
5588         {
5589             sal_Int32 nTI = -1;
5590             aLine.append( "/Opt[\n" );
5591             sal_Int32 i = 0;
5592             for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i )
5593             {
5594                 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine );
5595                 aLine.append( "\n" );
5596                 if( *it == rWidget.m_aValue )
5597                     nTI = i;
5598             }
5599             aLine.append( "]\n" );
5600             if( nTI > 0 )
5601             {
5602                 aLine.append( "/TI " );
5603                 aLine.append( nTI );
5604                 aLine.append( "\n" );
5605                 if( rWidget.m_nFlags & 0x200000 ) // Multiselect
5606                 {
5607                     aLine.append( "/I [" );
5608                     aLine.append( nTI );
5609                     aLine.append( "]\n" );
5610                 }
5611             }
5612         }
5613         if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
5614         {
5615             aLine.append( "/MaxLen " );
5616             aLine.append( rWidget.m_nMaxLen );
5617             aLine.append( "\n" );
5618         }
5619         if( rWidget.m_eType == PDFWriter::PushButton )
5620         {
5621             if(!m_bIsPDF_A1)
5622             {
5623                 OStringBuffer aDest;
5624                 if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) )
5625                 {
5626                     aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
5627                     aLine.append( aDest.makeStringAndClear() );
5628                     aLine.append( ">>>>\n" );
5629                 }
5630                 else if( rWidget.m_aListEntries.empty() )
5631                 {
5632                     // create a reset form action
5633                     aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
5634                 }
5635                 else if( rWidget.m_bSubmit )
5636                 {
5637                     // create a submit form action
5638                     aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
5639                     appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() );
5640                     aLine.append( "/Flags " );
5641 
5642                     sal_Int32 nFlags = 0;
5643                     switch( m_aContext.SubmitFormat )
5644                     {
5645                     case PDFWriter::HTML:
5646                         nFlags |= 4;
5647                         break;
5648                     case PDFWriter::XML:
5649                         if( m_aContext.Version > PDFWriter::PDF_1_3 )
5650                             nFlags |= 32;
5651                         break;
5652                     case PDFWriter::PDF:
5653                         if( m_aContext.Version > PDFWriter::PDF_1_3 )
5654                             nFlags |= 256;
5655                         break;
5656                     case PDFWriter::FDF:
5657                     default:
5658                         break;
5659                     }
5660                     if( rWidget.m_bSubmitGet )
5661                         nFlags |= 8;
5662                     aLine.append( nFlags );
5663                     aLine.append( ">>>>\n" );
5664                 }
5665                 else
5666                 {
5667                     // create a URI action
5668                     aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
5669                     aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
5670                     aLine.append( ")>>>>\n" );
5671                 }
5672             }
5673             else
5674                 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA );
5675         }
5676         if( rWidget.m_aDAString.getLength() )
5677         {
5678             if( rWidget.m_aDRDict.getLength() )
5679             {
5680                 aLine.append( "/DR<<" );
5681                 aLine.append( rWidget.m_aDRDict );
5682                 aLine.append( ">>\n" );
5683             }
5684             else
5685             {
5686                 aLine.append( "/DR<</Font<<" );
5687                 appendBuiltinFontsToDict( aLine );
5688                 aLine.append( ">>>>\n" );
5689             }
5690             aLine.append( "/DA" );
5691             appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
5692             aLine.append( "\n" );
5693             if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER )
5694                 aLine.append( "/Q 1\n" );
5695             else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT )
5696                 aLine.append( "/Q 2\n" );
5697         }
5698         // appearance charactristics for terminal fields
5699         // which are supposed to have an appearance constructed
5700         // by the viewer application
5701         if( rWidget.m_aMKDict.getLength() )
5702         {
5703             aLine.append( "/MK<<" );
5704             aLine.append( rWidget.m_aMKDict );
5705 //add the CA string, encrypting it
5706             appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
5707             aLine.append( ">>\n" );
5708         }
5709 
5710         CHECK_RETURN( emitAppearances( rWidget, aLine ) );
5711 
5712         aLine.append( ">>\n"
5713                       "endobj\n\n" );
5714         CHECK_RETURN( updateObject( rWidget.m_nObject ) );
5715         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5716     }
5717     return true;
5718 }
5719 
5720 bool PDFWriterImpl::emitAnnotations()
5721 {
5722     if( m_aPages.size() < 1 )
5723         return false;
5724 
5725     CHECK_RETURN( emitLinkAnnotations() );
5726 
5727     CHECK_RETURN( emitNoteAnnotations() );
5728 
5729     CHECK_RETURN( emitWidgetAnnotations() );
5730 
5731     return true;
5732 }
5733 
5734 #undef CHECK_RETURN
5735 #define CHECK_RETURN( x ) if( !x ) return false
5736 
5737 bool PDFWriterImpl::emitCatalog()
5738 {
5739     // build page tree
5740     // currently there is only one node that contains all leaves
5741 
5742     // first create a page tree node id
5743     sal_Int32 nTreeNode = createObject();
5744 
5745     // emit global resource dictionary (page emit needs it)
5746     CHECK_RETURN( emitResources() );
5747 
5748     // emit all pages
5749     for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it )
5750         if( ! it->emit( nTreeNode ) )
5751             return false;
5752 
5753     sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
5754 
5755     sal_Int32 nOutlineDict = emitOutline();
5756 
5757     //emit Output intent i59651
5758     sal_Int32 nOutputIntentObject = emitOutputIntent();
5759 
5760     //emit metadata
5761     sal_Int32 nMetadataObject = emitDocumentMetadata();
5762 
5763     sal_Int32 nStructureDict = 0;
5764     if(m_aStructure.size() > 1)
5765     {
5766 ///check if dummy structure containers are needed
5767         addInternalStructureContainer(m_aStructure[0]);
5768         nStructureDict = m_aStructure[0].m_nObject = createObject();
5769         emitStructure( m_aStructure[ 0 ] );
5770     }
5771 
5772     // adjust tree node file offset
5773     if( ! updateObject( nTreeNode ) )
5774         return false;
5775 
5776     // emit tree node
5777     OStringBuffer aLine( 2048 );
5778     aLine.append( nTreeNode );
5779     aLine.append( " 0 obj\n" );
5780     aLine.append( "<</Type/Pages\n" );
5781     aLine.append( "/Resources " );
5782     aLine.append( getResourceDictObj() );
5783     aLine.append( " 0 R\n" );
5784 
5785     switch( m_eInheritedOrientation )
5786     {
5787         case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
5788         case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
5789 
5790         case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant
5791         case PDFWriter::Portrait:
5792         default:
5793             break;
5794     }
5795     sal_Int32 nMediaBoxWidth = 0;
5796     sal_Int32 nMediaBoxHeight = 0;
5797     if( m_aPages.empty() ) // sanity check, this should not happen
5798     {
5799         nMediaBoxWidth = m_nInheritedPageWidth;
5800         nMediaBoxHeight = m_nInheritedPageHeight;
5801     }
5802     else
5803     {
5804         for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter )
5805         {
5806             if( iter->m_nPageWidth > nMediaBoxWidth )
5807                 nMediaBoxWidth = iter->m_nPageWidth;
5808             if( iter->m_nPageHeight > nMediaBoxHeight )
5809                 nMediaBoxHeight = iter->m_nPageHeight;
5810         }
5811     }
5812     aLine.append( "/MediaBox[ 0 0 " );
5813     aLine.append( nMediaBoxWidth );
5814     aLine.append( ' ' );
5815     aLine.append( nMediaBoxHeight );
5816     aLine.append( " ]\n"
5817                   "/Kids[ " );
5818     unsigned int i = 0;
5819     for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ )
5820     {
5821         aLine.append( iter->m_nPageObject );
5822         aLine.append( " 0 R" );
5823         aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5824     }
5825     aLine.append( "]\n"
5826                   "/Count " );
5827     aLine.append( (sal_Int32)m_aPages.size() );
5828     aLine.append( ">>\n"
5829                   "endobj\n\n" );
5830     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5831 
5832     // emit annotation objects
5833     CHECK_RETURN( emitAnnotations() );
5834 
5835     // emit Catalog
5836     m_nCatalogObject = createObject();
5837     if( ! updateObject( m_nCatalogObject ) )
5838         return false;
5839     aLine.setLength( 0 );
5840     aLine.append( m_nCatalogObject );
5841     aLine.append( " 0 obj\n"
5842                   "<</Type/Catalog/Pages " );
5843     aLine.append( nTreeNode );
5844     aLine.append( " 0 R\n" );
5845 //--->i56629
5846 //check if there are named destinations to emit (root must be inside the catalog)
5847     if( nNamedDestinationsDictionary )
5848     {
5849         aLine.append("/Dests ");
5850         aLine.append( nNamedDestinationsDictionary );
5851         aLine.append( " 0 R\n" );
5852     }
5853 //<----
5854     if( m_aContext.PageLayout != PDFWriter::DefaultLayout )
5855         switch(  m_aContext.PageLayout )
5856         {
5857         default :
5858         case  PDFWriter::SinglePage :
5859             aLine.append( "/PageLayout/SinglePage\n" );
5860             break;
5861         case  PDFWriter::Continuous :
5862             aLine.append( "/PageLayout/OneColumn\n" );
5863             break;
5864         case  PDFWriter::ContinuousFacing :
5865 //the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5866             aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5867             break;
5868         }
5869     if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode )
5870         switch(  m_aContext.PDFDocumentMode )
5871         {
5872         default :
5873             aLine.append( "/PageMode/UseNone\n" );
5874             break;
5875         case PDFWriter::UseOutlines :
5876             aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5877             break;
5878         case PDFWriter::UseThumbs :
5879             aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5880             break;
5881         }
5882     else if( m_aContext.OpenInFullScreenMode )
5883         aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5884 
5885     OStringBuffer aInitPageRef;
5886     if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() )
5887     {
5888         aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5889         aInitPageRef.append( " 0 R" );
5890     }
5891     else
5892         aInitPageRef.append( "0" );
5893     switch( m_aContext.PDFDocumentAction )
5894     {
5895     case PDFWriter::ActionDefault :     //do nothing, this is the Acrobat default
5896     default:
5897         if( aInitPageRef.getLength() > 1 )
5898         {
5899             aLine.append( "/OpenAction[" );
5900             aLine.append( aInitPageRef );
5901             aLine.append( " /XYZ null null 0]\n" );
5902         }
5903         break;
5904     case PDFWriter::FitInWindow :
5905         aLine.append( "/OpenAction[" );
5906         aLine.append( aInitPageRef );
5907         aLine.append( " /Fit]\n" ); //Open fit page
5908         break;
5909     case PDFWriter::FitWidth :
5910         aLine.append( "/OpenAction[" );
5911         aLine.append( aInitPageRef );
5912         aLine.append( " /FitH " );
5913         aLine.append( m_nInheritedPageHeight );//Open fit width
5914         aLine.append( "]\n" );
5915         break;
5916     case PDFWriter::FitVisible :
5917         aLine.append( "/OpenAction[" );
5918         aLine.append( aInitPageRef );
5919         aLine.append( " /FitBH " );
5920         aLine.append( m_nInheritedPageHeight );//Open fit visible
5921         aLine.append( "]\n" );
5922         break;
5923     case PDFWriter::ActionZoom :
5924         aLine.append( "/OpenAction[" );
5925         aLine.append( aInitPageRef );
5926         aLine.append( " /XYZ null null " );
5927         if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5928             aLine.append( (double)m_aContext.Zoom/100.0 );
5929         else
5930             aLine.append( "0" );
5931         aLine.append( "]\n" );
5932         break;
5933     }
5934 // viewer preferences, if we had some, then emit
5935     if( m_aContext.HideViewerToolbar ||
5936         ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) ||
5937         m_aContext.HideViewerMenubar ||
5938         m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
5939         m_aContext.CenterWindow || (m_aContext.FirstPageLeft  &&  m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
5940         m_aContext.OpenInFullScreenMode )
5941     {
5942         aLine.append( "/ViewerPreferences<<" );
5943         if( m_aContext.HideViewerToolbar )
5944             aLine.append( "/HideToolbar true\n" );
5945         if( m_aContext.HideViewerMenubar )
5946             aLine.append( "/HideMenubar true\n" );
5947         if( m_aContext.HideViewerWindowControls )
5948             aLine.append( "/HideWindowUI true\n" );
5949         if( m_aContext.FitWindow )
5950             aLine.append( "/FitWindow true\n" );
5951         if( m_aContext.CenterWindow )
5952             aLine.append( "/CenterWindow true\n" );
5953         if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle )
5954             aLine.append( "/DisplayDocTitle true\n" );
5955         if( m_aContext.FirstPageLeft &&  m_aContext.PageLayout == PDFWriter::ContinuousFacing )
5956             aLine.append( "/Direction/R2L\n" );
5957         if( m_aContext.OpenInFullScreenMode )
5958             switch( m_aContext.PDFDocumentMode )
5959             {
5960             default :
5961             case PDFWriter::ModeDefault :
5962                 aLine.append( "/NonFullScreenPageMode/UseNone\n" );
5963                 break;
5964             case PDFWriter::UseOutlines :
5965                 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
5966                 break;
5967             case PDFWriter::UseThumbs :
5968                 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
5969                 break;
5970             }
5971         aLine.append( ">>\n" );
5972     }
5973 
5974     if( nOutlineDict )
5975     {
5976         aLine.append( "/Outlines " );
5977         aLine.append( nOutlineDict );
5978         aLine.append( " 0 R\n" );
5979     }
5980     if( nStructureDict )
5981     {
5982         aLine.append( "/StructTreeRoot " );
5983         aLine.append( nStructureDict );
5984         aLine.append( " 0 R\n" );
5985     }
5986     if( m_aContext.DocumentLocale.Language.getLength() > 0 )
5987     {
5988         OUStringBuffer aLocBuf( 16 );
5989         aLocBuf.append( m_aContext.DocumentLocale.Language.toAsciiLowerCase() );
5990         if( m_aContext.DocumentLocale.Country.getLength() > 0 )
5991         {
5992             aLocBuf.append( sal_Unicode('-') );
5993             aLocBuf.append( m_aContext.DocumentLocale.Country );
5994         }
5995         aLine.append( "/Lang" );
5996         appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
5997         aLine.append( "\n" );
5998     }
5999     if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 )
6000     {
6001         aLine.append( "/MarkInfo<</Marked true>>\n" );
6002     }
6003     if( m_aWidgets.size() > 0 )
6004     {
6005         aLine.append( "/AcroForm<</Fields[\n" );
6006         int nWidgets = m_aWidgets.size();
6007         int nOut = 0;
6008         for( int j = 0; j < nWidgets; j++ )
6009         {
6010             // output only root fields
6011             if( m_aWidgets[j].m_nParent < 1 )
6012             {
6013                 aLine.append( m_aWidgets[j].m_nObject );
6014                 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
6015             }
6016         }
6017         aLine.append( "\n]/DR " );
6018         aLine.append( getResourceDictObj() );
6019         aLine.append( " 0 R" );
6020         if( m_bIsPDF_A1 )
6021             aLine.append( ">>\n" );
6022         else
6023             aLine.append( "/NeedAppearances true>>\n" );
6024     }
6025 //--->i59651
6026 //check if there is a Metadata object
6027     if( nOutputIntentObject )
6028     {
6029         aLine.append("/OutputIntents[");
6030         aLine.append( nOutputIntentObject );
6031         aLine.append( " 0 R]" );
6032     }
6033     if( nMetadataObject )
6034     {
6035         aLine.append("/Metadata ");
6036         aLine.append( nMetadataObject );
6037         aLine.append( " 0 R" );
6038     }
6039 //<----
6040     aLine.append( ">>\n"
6041                   "endobj\n\n" );
6042     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6043 
6044     return true;
6045 }
6046 
6047 sal_Int32 PDFWriterImpl::emitInfoDict( )
6048 {
6049     sal_Int32 nObject = createObject();
6050 
6051     if( updateObject( nObject ) )
6052     {
6053         OStringBuffer aLine( 1024 );
6054         aLine.append( nObject );
6055         aLine.append( " 0 obj\n"
6056                       "<<" );
6057         if( m_aContext.DocumentInfo.Title.Len() )
6058         {
6059             aLine.append( "/Title" );
6060             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine );
6061             aLine.append( "\n" );
6062         }
6063         if( m_aContext.DocumentInfo.Author.Len() )
6064         {
6065             aLine.append( "/Author" );
6066             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine );
6067             aLine.append( "\n" );
6068         }
6069         if( m_aContext.DocumentInfo.Subject.Len() )
6070         {
6071             aLine.append( "/Subject" );
6072             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine );
6073             aLine.append( "\n" );
6074         }
6075         if( m_aContext.DocumentInfo.Keywords.Len() )
6076         {
6077             aLine.append( "/Keywords" );
6078             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine );
6079             aLine.append( "\n" );
6080         }
6081         if( m_aContext.DocumentInfo.Creator.Len() )
6082         {
6083             aLine.append( "/Creator" );
6084             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine );
6085             aLine.append( "\n" );
6086         }
6087         if( m_aContext.DocumentInfo.Producer.Len() )
6088         {
6089             aLine.append( "/Producer" );
6090             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine );
6091             aLine.append( "\n" );
6092         }
6093 
6094          aLine.append( "/CreationDate" );
6095          appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine );
6096         aLine.append( ">>\nendobj\n\n" );
6097         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6098             nObject = 0;
6099     }
6100     else
6101         nObject = 0;
6102 
6103     return nObject;
6104 }
6105 
6106 //--->i56629
6107 // Part of this function may be shared with method appendDest.
6108 //
6109 sal_Int32 PDFWriterImpl::emitNamedDestinations()
6110 {
6111     sal_Int32  nCount = m_aNamedDests.size();
6112     if( nCount <= 0 )
6113         return 0;//define internal error
6114 
6115 //get the object number for all the destinations
6116     sal_Int32 nObject = createObject();
6117 
6118     if( updateObject( nObject ) )
6119     {
6120 //emit the dictionary
6121         OStringBuffer aLine( 1024 );
6122         aLine.append( nObject );
6123         aLine.append( " 0 obj\n"
6124                       "<<" );
6125 
6126         sal_Int32  nDestID;
6127         for( nDestID = 0; nDestID < nCount; nDestID++ )
6128         {
6129             const PDFNamedDest& rDest   = m_aNamedDests[ nDestID ];
6130 // In order to correctly function both under an Internet browser and
6131 // directly with a reader (provided the reader has the feature) we
6132 // need to set the name of the destination the same way it will be encoded
6133 // in an Internet link
6134             INetURLObject aLocalURL(
6135                 OUString( RTL_CONSTASCII_USTRINGPARAM( "http://ahost.ax" ) ) ); //dummy location, won't be used
6136             aLocalURL.SetMark( rDest.m_aDestName );
6137 
6138             const rtl::OUString aName   = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as
6139             // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
6140             const PDFPage& rDestPage    = m_aPages[ rDest.m_nPage ];
6141 
6142             aLine.append( '/' );
6143             appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
6144             aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
6145                                  //maps the preceeding character properly
6146             aLine.append( rDestPage.m_nPageObject );
6147             aLine.append( " 0 R" );
6148 
6149             switch( rDest.m_eType )
6150             {
6151             case PDFWriter::XYZ:
6152             default:
6153                 aLine.append( "/XYZ " );
6154                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6155                 aLine.append( ' ' );
6156                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6157                 aLine.append( " 0" );
6158                 break;
6159             case PDFWriter::Fit:
6160                 aLine.append( "/Fit" );
6161                 break;
6162             case PDFWriter::FitRectangle:
6163                 aLine.append( "/FitR " );
6164                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6165                 aLine.append( ' ' );
6166                 appendFixedInt( rDest.m_aRect.Top(), aLine );
6167                 aLine.append( ' ' );
6168                 appendFixedInt( rDest.m_aRect.Right(), aLine );
6169                 aLine.append( ' ' );
6170                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6171                 break;
6172             case PDFWriter::FitHorizontal:
6173                 aLine.append( "/FitH " );
6174                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6175                 break;
6176             case PDFWriter::FitVertical:
6177                 aLine.append( "/FitV " );
6178                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6179                 break;
6180             case PDFWriter::FitPageBoundingBox:
6181                 aLine.append( "/FitB" );
6182                 break;
6183             case PDFWriter::FitPageBoundingBoxHorizontal:
6184                 aLine.append( "/FitBH " );
6185                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6186                 break;
6187             case PDFWriter::FitPageBoundingBoxVertical:
6188                 aLine.append( "/FitBV " );
6189                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6190                 break;
6191             }
6192             aLine.append( "]\n" );
6193         }
6194 //close
6195 
6196         aLine.append( ">>\nendobj\n\n" );
6197         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6198             nObject = 0;
6199     }
6200     else
6201         nObject = 0;
6202 
6203     return nObject;
6204 }
6205 //<--- i56629
6206 
6207 //--->i59651
6208 // emits the output intent dictionary
6209 
6210 sal_Int32 PDFWriterImpl::emitOutputIntent()
6211 {
6212     if( !m_bIsPDF_A1 )
6213         return 0;
6214 
6215 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
6216 
6217     OStringBuffer aLine( 1024 );
6218     sal_Int32 nICCObject = createObject();
6219     sal_Int32 nStreamLengthObject = createObject();
6220 
6221     aLine.append( nICCObject );
6222 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
6223     aLine.append( " 0 obj\n<</N 3/Length " );
6224     aLine.append( nStreamLengthObject );
6225     aLine.append( " 0 R" );
6226 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
6227     aLine.append( "/Filter/FlateDecode" );
6228 #endif
6229     aLine.append( ">>\nstream\n" );
6230     CHECK_RETURN( updateObject( nICCObject ) );
6231     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6232 //get file position
6233     sal_uInt64 nBeginStreamPos = 0;
6234     osl_getFilePos( m_aFile, &nBeginStreamPos );
6235     beginCompression();
6236     checkAndEnableStreamEncryption( nICCObject );
6237     sal_Int32 nStreamSize = writeBuffer( nsRGB_ICC_profile, (sal_Int32) sizeof( nsRGB_ICC_profile ) );
6238     disableStreamEncryption();
6239     endCompression();
6240     sal_uInt64 nEndStreamPos = 0;
6241     osl_getFilePos( m_aFile, &nEndStreamPos );
6242 
6243     if( nStreamSize == 0 )
6244         return 0;
6245     if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6246         return 0 ;
6247     aLine.setLength( 0 );
6248 
6249 //emit the stream length   object
6250     CHECK_RETURN( updateObject( nStreamLengthObject ) );
6251     aLine.setLength( 0 );
6252     aLine.append( nStreamLengthObject );
6253     aLine.append( " 0 obj\n" );
6254     aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6255     aLine.append( "\nendobj\n\n" );
6256     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6257     aLine.setLength( 0 );
6258 
6259 //emit the OutputIntent dictionary
6260     sal_Int32 nOIObject = createObject();
6261     CHECK_RETURN( updateObject( nOIObject ) );
6262     aLine.append( nOIObject );
6263     aLine.append( " 0 obj\n"
6264                   "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
6265 
6266     rtl::OUString aComment( RTL_CONSTASCII_USTRINGPARAM( "sRGB IEC61966-2.1" ) );
6267     appendLiteralStringEncrypt( aComment ,nOIObject, aLine );
6268     aLine.append("/DestOutputProfile ");
6269     aLine.append( nICCObject );
6270     aLine.append( " 0 R>>\nendobj\n\n" );;
6271     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6272 
6273     return nOIObject;
6274 }
6275 
6276 // formats the string for the XML stream
6277 static void escapeStringXML( const rtl::OUString& rStr, rtl::OUString &rValue)
6278 {
6279     const sal_Unicode* pUni = rStr.getStr();
6280     int nLen = rStr.getLength();
6281     for( ; nLen; nLen--, pUni++ )
6282     {
6283         switch( *pUni )
6284         {
6285         case sal_Unicode('&'):
6286             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&amp;" ) );
6287         break;
6288         case sal_Unicode('<'):
6289             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&lt;" ) );
6290         break;
6291         case sal_Unicode('>'):
6292             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&gt;" ) );
6293         break;
6294         case sal_Unicode('\''):
6295             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&apos;" ) );
6296         break;
6297         case sal_Unicode('"'):
6298             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&quot;" ) );
6299         break;
6300         default:
6301             rValue += rtl::OUString( *pUni );
6302             break;
6303         }
6304     }
6305 }
6306 
6307 // emits the document metadata
6308 //
6309 sal_Int32 PDFWriterImpl::emitDocumentMetadata()
6310 {
6311     if( !m_bIsPDF_A1 )
6312         return 0;
6313 
6314     //get the object number for all the destinations
6315     sal_Int32 nObject = createObject();
6316 
6317     if( updateObject( nObject ) )
6318     {
6319 // the following string are written in UTF-8 unicode
6320         OStringBuffer aMetadataStream( 8192 );
6321 
6322         aMetadataStream.append( "<?xpacket begin=\"" );
6323 // this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used
6324 // as a byte-order marker.
6325         aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) );
6326         aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
6327         aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
6328         aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
6329 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
6330         aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6331         aMetadataStream.append( "      xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
6332         aMetadataStream.append( "   <pdfaid:part>1</pdfaid:part>\n" );
6333         aMetadataStream.append( "   <pdfaid:conformance>A</pdfaid:conformance>\n" );
6334         aMetadataStream.append( "  </rdf:Description>\n" );
6335 //... Dublin Core properties go here
6336         if( m_aContext.DocumentInfo.Title.Len() ||
6337             m_aContext.DocumentInfo.Author.Len() ||
6338             m_aContext.DocumentInfo.Subject.Len() )
6339         {
6340             aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6341             aMetadataStream.append( "      xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
6342             if( m_aContext.DocumentInfo.Title.Len() )
6343             {
6344 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6345                 aMetadataStream.append( "   <dc:title>\n" );
6346                 aMetadataStream.append( "    <rdf:Alt>\n" );
6347                 aMetadataStream.append( "     <rdf:li xml:lang=\"x-default\">" );
6348                 rtl::OUString aTitle;
6349                 escapeStringXML( m_aContext.DocumentInfo.Title, aTitle );
6350                 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 )  );
6351                 aMetadataStream.append( "</rdf:li>\n" );
6352                 aMetadataStream.append( "    </rdf:Alt>\n" );
6353                 aMetadataStream.append( "   </dc:title>\n" );
6354             }
6355             if( m_aContext.DocumentInfo.Author.Len() )
6356             {
6357                 aMetadataStream.append( "   <dc:creator>\n" );
6358                 aMetadataStream.append( "    <rdf:Seq>\n" );
6359                 aMetadataStream.append( "     <rdf:li>" );
6360                 rtl::OUString aAuthor;
6361                 escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor );
6362                 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 )  );
6363                 aMetadataStream.append( "</rdf:li>\n" );
6364                 aMetadataStream.append( "    </rdf:Seq>\n" );
6365                 aMetadataStream.append( "   </dc:creator>\n" );
6366             }
6367             if( m_aContext.DocumentInfo.Subject.Len() )
6368             {
6369 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6370                 aMetadataStream.append( "   <dc:description>\n" );
6371                 aMetadataStream.append( "    <rdf:Alt>\n" );
6372                 aMetadataStream.append( "     <rdf:li xml:lang=\"x-default\">" );
6373                 rtl::OUString aSubject;
6374                 escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject );
6375                 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 )  );
6376                 aMetadataStream.append( "</rdf:li>\n" );
6377                 aMetadataStream.append( "    </rdf:Alt>\n" );
6378                 aMetadataStream.append( "   </dc:description>\n" );
6379             }
6380             aMetadataStream.append( "  </rdf:Description>\n" );
6381         }
6382 
6383 //... PDF properties go here
6384         if( m_aContext.DocumentInfo.Producer.Len() ||
6385             m_aContext.DocumentInfo.Keywords.Len() )
6386         {
6387             aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6388             aMetadataStream.append( "     xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
6389             if( m_aContext.DocumentInfo.Producer.Len() )
6390             {
6391                 aMetadataStream.append( "   <pdf:Producer>" );
6392                 rtl::OUString aProducer;
6393                 escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer );
6394                 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 )  );
6395                 aMetadataStream.append( "</pdf:Producer>\n" );
6396             }
6397             if( m_aContext.DocumentInfo.Keywords.Len() )
6398             {
6399                 aMetadataStream.append( "   <pdf:Keywords>" );
6400                 rtl::OUString aKeywords;
6401                 escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords );
6402                 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 )  );
6403                 aMetadataStream.append( "</pdf:Keywords>\n" );
6404             }
6405             aMetadataStream.append( "  </rdf:Description>\n" );
6406         }
6407 
6408         aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6409         aMetadataStream.append( "    xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
6410         if( m_aContext.DocumentInfo.Creator.Len() )
6411         {
6412             aMetadataStream.append( "   <xmp:CreatorTool>" );
6413             rtl::OUString aCreator;
6414             escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator );
6415             aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 )  );
6416             aMetadataStream.append( "</xmp:CreatorTool>\n" );
6417         }
6418 //creation date
6419         aMetadataStream.append( "   <xmp:CreateDate>" );
6420         aMetadataStream.append( m_aCreationMetaDateString );
6421         aMetadataStream.append( "</xmp:CreateDate>\n" );
6422 
6423         aMetadataStream.append( "  </rdf:Description>\n" );
6424         aMetadataStream.append( " </rdf:RDF>\n" );
6425         aMetadataStream.append( "</x:xmpmeta>\n" );
6426 
6427 //add the padding
6428         for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ )
6429         {
6430             aMetadataStream.append( " " );
6431             if( nSpaces % 100 == 0 )
6432                 aMetadataStream.append( "\n" );
6433         }
6434 
6435         aMetadataStream.append( "<?xpacket end=\"w\"?>\n" );
6436 
6437         OStringBuffer aMetadataObj( 1024 );
6438 
6439         aMetadataObj.append( nObject );
6440         aMetadataObj.append( " 0 obj\n" );
6441 
6442         aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
6443 
6444         aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() );
6445         aMetadataObj.append( ">>\nstream\n" );
6446         CHECK_RETURN( writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) );
6447 //emit the stream
6448         CHECK_RETURN( writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) );
6449 
6450         aMetadataObj.setLength( 0 );
6451         aMetadataObj.append( "\nendstream\nendobj\n\n" );
6452         if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
6453             nObject = 0;
6454     }
6455     else
6456         nObject = 0;
6457 
6458     return nObject;
6459 }
6460 //<---i59651
6461 
6462 bool PDFWriterImpl::emitTrailer()
6463 {
6464     // emit doc info
6465     OString aInfoValuesOut;
6466     sal_Int32 nDocInfoObject = emitInfoDict( );
6467 
6468     sal_Int32 nSecObject = 0;
6469 
6470 	if( m_aContext.Encryption.Encrypt() )
6471 	{
6472 //emit the security information
6473 //must be emitted as indirect dictionary object, since
6474 //Acrobat Reader 5 works only with this kind of implementation
6475 		nSecObject = createObject();
6476 
6477 		if( updateObject( nSecObject ) )
6478 		{
6479 			OStringBuffer aLineS( 1024 );
6480 			aLineS.append( nSecObject );
6481 			aLineS.append( " 0 obj\n"
6482 						   "<</Filter/Standard/V " );
6483 			// check the version
6484 			if( m_aContext.Encryption.Security128bit )
6485 				aLineS.append( "2/Length 128/R 3" );
6486 			else
6487 				aLineS.append( "1/R 2" );
6488 
6489 			// emit the owner password, must not be encrypted
6490 			aLineS.append( "/O(" );
6491 			appendLiteralString( (const sal_Char*)&m_aContext.Encryption.OValue[0], sal_Int32(m_aContext.Encryption.OValue.size()), aLineS );
6492 			aLineS.append( ")/U(" );
6493 			appendLiteralString( (const sal_Char*)&m_aContext.Encryption.UValue[0], sal_Int32(m_aContext.Encryption.UValue.size()), aLineS );
6494 			aLineS.append( ")/P " );// the permission set
6495 			aLineS.append( m_nAccessPermissions );
6496 			aLineS.append( ">>\nendobj\n\n" );
6497 			if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) )
6498 				nSecObject = 0;
6499 		}
6500 		else
6501 			nSecObject = 0;
6502 	}
6503     // emit xref table
6504     // remember start
6505     sal_uInt64 nXRefOffset = 0;
6506     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) );
6507     CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
6508 
6509     sal_Int32 nObjects = m_aObjects.size();
6510     OStringBuffer aLine;
6511     aLine.append( "0 " );
6512     aLine.append( (sal_Int32)(nObjects+1) );
6513     aLine.append( "\n" );
6514     aLine.append( "0000000000 65535 f \n" );
6515     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6516 
6517     for( sal_Int32 i = 0; i < nObjects; i++ )
6518     {
6519         aLine.setLength( 0 );
6520         OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] );
6521         for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
6522             aLine.append( '0' );
6523         aLine.append( aOffset );
6524         aLine.append( " 00000 n \n" );
6525         DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" );
6526         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6527     }
6528 
6529     // prepare document checksum
6530     OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
6531     if( m_aDocDigest )
6532     {
6533         sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
6534         rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) );
6535         for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ )
6536             appendHex( nMD5Sum[i], aDocChecksum );
6537     }
6538     // document id set in setDocInfo method
6539     // emit trailer
6540     aLine.setLength( 0 );
6541     aLine.append( "trailer\n"
6542                   "<</Size " );
6543     aLine.append( (sal_Int32)(nObjects+1) );
6544     aLine.append( "/Root " );
6545     aLine.append( m_nCatalogObject );
6546     aLine.append( " 0 R\n" );
6547     if( nSecObject |= 0 )
6548     {
6549         aLine.append( "/Encrypt ");
6550         aLine.append( nSecObject );
6551         aLine.append( " 0 R\n" );
6552     }
6553     if( nDocInfoObject )
6554     {
6555         aLine.append( "/Info " );
6556         aLine.append( nDocInfoObject );
6557         aLine.append( " 0 R\n" );
6558     }
6559     if( ! m_aContext.Encryption.DocumentIdentifier.empty() )
6560     {
6561         aLine.append( "/ID [ <" );
6562         for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6563              it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6564         {
6565             appendHex( sal_Int8(*it), aLine );
6566         }
6567         aLine.append( ">\n"
6568                       "<" );
6569         for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6570              it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6571         {
6572             appendHex( sal_Int8(*it), aLine );
6573         }
6574         aLine.append( "> ]\n" );
6575     }
6576     if( aDocChecksum.getLength() )
6577     {
6578         aLine.append( "/DocChecksum /" );
6579         aLine.append( aDocChecksum );
6580         aLine.append( "\n" );
6581     }
6582     if( m_aAdditionalStreams.size() > 0 )
6583     {
6584         aLine.append( "/AdditionalStreams [" );
6585         for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ )
6586         {
6587             aLine.append( "/" );
6588             appendName( m_aAdditionalStreams[i].m_aMimeType, aLine );
6589             aLine.append( " " );
6590             aLine.append( m_aAdditionalStreams[i].m_nStreamObject );
6591             aLine.append( " 0 R\n" );
6592         }
6593         aLine.append( "]\n" );
6594     }
6595     aLine.append( ">>\n"
6596                   "startxref\n" );
6597     aLine.append( (sal_Int64)nXRefOffset );
6598     aLine.append( "\n"
6599                   "%%EOF\n" );
6600     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6601 
6602     return true;
6603 }
6604 
6605 struct AnnotationSortEntry
6606 {
6607     sal_Int32 nTabOrder;
6608     sal_Int32 nObject;
6609     sal_Int32 nWidgetIndex;
6610 
6611     AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
6612         nTabOrder( nTab ),
6613         nObject( nObj ),
6614         nWidgetIndex( nI )
6615     {}
6616 };
6617 
6618 struct AnnotSortContainer
6619 {
6620     std::set< sal_Int32 >               aObjects;
6621     std::vector< AnnotationSortEntry >    aSortedAnnots;
6622 };
6623 
6624 struct AnnotSorterLess
6625 {
6626     std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets;
6627 
6628     AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {}
6629 
6630     bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
6631     {
6632         if( rLeft.nTabOrder < rRight.nTabOrder )
6633             return true;
6634         if( rRight.nTabOrder < rLeft.nTabOrder )
6635             return false;
6636         if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
6637             return false;
6638         if( rRight.nWidgetIndex < 0 )
6639             return true;
6640         if( rLeft.nWidgetIndex < 0 )
6641             return false;
6642         // remember: widget rects are in PDF coordinates, so they are ordered down up
6643         if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
6644             m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
6645             return true;
6646         if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
6647             m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
6648             return false;
6649         if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
6650             m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
6651             return true;
6652         return false;
6653     }
6654 };
6655 
6656 void PDFWriterImpl::sortWidgets()
6657 {
6658     // sort widget annotations on each page as per their
6659     // TabOrder attribute
6660     std::hash_map< sal_Int32, AnnotSortContainer > sorted;
6661     int nWidgets = m_aWidgets.size();
6662     for( int nW = 0; nW < nWidgets; nW++ )
6663     {
6664         const PDFWidget& rWidget = m_aWidgets[nW];
6665         if( rWidget.m_nPage >= 0 )
6666         {
6667             AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
6668             // optimize vector allocation
6669             if( rCont.aSortedAnnots.empty() )
6670                 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
6671             // insert widget to tab sorter
6672             // RadioButtons are not page annotations, only their individual check boxes are
6673             if( rWidget.m_eType != PDFWriter::RadioButton )
6674             {
6675                 rCont.aObjects.insert( rWidget.m_nObject );
6676                 rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) );
6677             }
6678         }
6679     }
6680     for( std::hash_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it )
6681     {
6682         // append entries for non widget annotations
6683         PDFPage& rPage = m_aPages[ it->first ];
6684         unsigned int nAnnots = rPage.m_aAnnotations.size();
6685         for( unsigned int nA = 0; nA < nAnnots; nA++ )
6686             if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end())
6687                 it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) );
6688 
6689         AnnotSorterLess aLess( m_aWidgets );
6690         std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess );
6691         // sanity check
6692         if( it->second.aSortedAnnots.size() == nAnnots)
6693         {
6694             for( unsigned int nA = 0; nA < nAnnots; nA++ )
6695                 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject;
6696         }
6697         else
6698         {
6699             DBG_ASSERT( 0, "wrong number of sorted annotations" );
6700             #if OSL_DEBUG_LEVEL > 0
6701             fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n"
6702 					 "    %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots );
6703             #endif
6704         }
6705     }
6706 
6707     // FIXME: implement tab order in structure tree for PDF 1.5
6708 }
6709 
6710 namespace vcl {
6711 class PDFStreamIf :
6712 		public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream	>
6713 {
6714     PDFWriterImpl*  m_pWriter;
6715     bool            m_bWrite;
6716     public:
6717     PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
6718     virtual ~PDFStreamIf();
6719 
6720     virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw();
6721     virtual void SAL_CALL flush() throw();
6722     virtual void SAL_CALL closeOutput() throw();
6723 };
6724 }
6725 
6726 PDFStreamIf::~PDFStreamIf()
6727 {
6728 }
6729 
6730 void SAL_CALL  PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw()
6731 {
6732     if( m_bWrite )
6733     {
6734         sal_Int32 nBytes = aData.getLength();
6735         if( nBytes > 0 )
6736             m_pWriter->writeBuffer( aData.getConstArray(), nBytes );
6737     }
6738 }
6739 
6740 void SAL_CALL PDFStreamIf::flush() throw()
6741 {
6742 }
6743 
6744 void SAL_CALL PDFStreamIf::closeOutput() throw()
6745 {
6746     m_bWrite = false;
6747 }
6748 
6749 bool PDFWriterImpl::emitAdditionalStreams()
6750 {
6751     unsigned int nStreams = m_aAdditionalStreams.size();
6752     for( unsigned int i = 0; i < nStreams; i++ )
6753     {
6754         PDFAddStream& rStream = m_aAdditionalStreams[i];
6755         rStream.m_nStreamObject = createObject();
6756         sal_Int32 nSizeObject = createObject();
6757 
6758         if( ! updateObject( rStream.m_nStreamObject ) )
6759             return false;
6760 
6761         OStringBuffer aLine;
6762         aLine.append( rStream.m_nStreamObject );
6763         aLine.append( " 0 obj\n<</Length " );
6764         aLine.append( nSizeObject );
6765         aLine.append( " 0 R" );
6766         if( rStream.m_bCompress )
6767             aLine.append( "/Filter/FlateDecode" );
6768         aLine.append( ">>\nstream\n" );
6769         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6770             return false;
6771         sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0;
6772         if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) )
6773         {
6774             osl_closeFile( m_aFile );
6775             m_bOpen = false;
6776         }
6777         if( rStream.m_bCompress )
6778             beginCompression();
6779 
6780         checkAndEnableStreamEncryption( rStream.m_nStreamObject );
6781         com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) );
6782         rStream.m_pStream->write( xStream );
6783         xStream.clear();
6784         delete rStream.m_pStream;
6785         rStream.m_pStream = NULL;
6786         disableStreamEncryption();
6787 
6788         if( rStream.m_bCompress )
6789             endCompression();
6790 
6791         if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) )
6792         {
6793             osl_closeFile( m_aFile );
6794             m_bOpen = false;
6795             return false;
6796         }
6797         if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6798             return false ;
6799         // emit stream length object
6800         if( ! updateObject( nSizeObject ) )
6801             return false;
6802         aLine.setLength( 0 );
6803         aLine.append( nSizeObject );
6804         aLine.append( " 0 obj\n" );
6805         aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6806         aLine.append( "\nendobj\n\n" );
6807         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6808             return false;
6809     }
6810     return true;
6811 }
6812 
6813 bool PDFWriterImpl::emit()
6814 {
6815     endPage();
6816 
6817     // resort structure tree and annotations if necessary
6818     // needed for widget tab order
6819     sortWidgets();
6820 
6821     // emit additional streams
6822     CHECK_RETURN( emitAdditionalStreams() );
6823 
6824     // emit catalog
6825     CHECK_RETURN( emitCatalog() );
6826 
6827     // emit trailer
6828     CHECK_RETURN( emitTrailer() );
6829 
6830     osl_closeFile( m_aFile );
6831     m_bOpen = false;
6832 
6833     return true;
6834 }
6835 
6836 std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors()
6837 {
6838     return m_aErrors;
6839 }
6840 
6841 sal_Int32 PDFWriterImpl::getSystemFont( const Font& i_rFont )
6842 {
6843     getReferenceDevice()->Push();
6844     getReferenceDevice()->SetFont( i_rFont );
6845     getReferenceDevice()->ImplNewFont();
6846 
6847     const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6848     sal_Int32 nFontID = 0;
6849     FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont );
6850     if( it != m_aSystemFonts.end() )
6851         nFontID = it->second.m_nNormalFontID;
6852     else
6853     {
6854         nFontID = m_nNextFID++;
6855         m_aSystemFonts[ pDevFont ] = EmbedFont();
6856         m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID;
6857     }
6858 
6859     getReferenceDevice()->Pop();
6860     getReferenceDevice()->ImplNewFont();
6861 
6862     return nFontID;
6863 }
6864 
6865 void PDFWriterImpl::registerGlyphs( int nGlyphs,
6866                                     sal_GlyphId* pGlyphs,
6867                                     sal_Int32* pGlyphWidths,
6868                                     sal_Ucs* pUnicodes,
6869                                     sal_Int32* pUnicodesPerGlyph,
6870                                     sal_uInt8* pMappedGlyphs,
6871                                     sal_Int32* pMappedFontObjects,
6872                                     const ImplFontData* pFallbackFonts[] )
6873 {
6874     const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6875     sal_Ucs* pCurUnicode = pUnicodes;
6876     for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ )
6877     {
6878         const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB);
6879         const ImplFontData* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont;
6880 
6881         if( isBuiltinFont( pCurrentFont ) )
6882         {
6883             sal_Int32 nFontID = 0;
6884             FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6885             if( it != m_aEmbeddedFonts.end() )
6886                 nFontID = it->second.m_nNormalFontID;
6887             else
6888             {
6889                 nFontID = m_nNextFID++;
6890                 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6891                 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6892             }
6893 
6894             pGlyphWidths[ i ] = 0;
6895             pMappedGlyphs[ i ] = sal::static_int_cast<sal_Int8>( nFontGlyphId );
6896             pMappedFontObjects[ i ] = nFontID;
6897             const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pCurrentFont );
6898             if( pFD )
6899             {
6900                 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
6901                 pGlyphWidths[i] = pBuiltinFont->m_aWidths[ nFontGlyphId & 0x00ff ];
6902             }
6903         }
6904         else if( pCurrentFont->mbSubsettable )
6905         {
6906             FontSubset& rSubset = m_aSubsets[ pCurrentFont ];
6907             // search for font specific glyphID
6908             FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId );
6909             if( it != rSubset.m_aMapping.end() )
6910             {
6911                 pMappedFontObjects[i] = it->second.m_nFontID;
6912                 pMappedGlyphs[i] = it->second.m_nSubsetGlyphID;
6913             }
6914             else
6915             {
6916                 // create new subset if necessary
6917                 if( rSubset.m_aSubsets.empty()
6918                 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
6919                 {
6920                     rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) );
6921                 }
6922 
6923                 // copy font id
6924                 pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID;
6925                 // create new glyph in subset
6926                 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
6927                 pMappedGlyphs[i] = nNewId;
6928 
6929                 // add new glyph to emitted font subset
6930                 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
6931                 rNewGlyphEmit.setGlyphId( nNewId );
6932                 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ )
6933                     rNewGlyphEmit.addCode( pCurUnicode[n] );
6934 
6935                 // add new glyph to font mapping
6936                 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
6937                 rNewGlyph.m_nFontID = pMappedFontObjects[i];
6938                 rNewGlyph.m_nSubsetGlyphID = nNewId;
6939             }
6940             getReferenceDevice()->ImplGetGraphics();
6941             const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0);
6942             pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont,
6943                                                           nFontGlyphId,
6944                                                           bVertical,
6945                                                           m_pReferenceDevice->mpGraphics );
6946         }
6947         else if( pCurrentFont->IsEmbeddable() )
6948         {
6949             sal_Int32 nFontID = 0;
6950             FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6951             if( it != m_aEmbeddedFonts.end() )
6952                 nFontID = it->second.m_nNormalFontID;
6953             else
6954             {
6955                 nFontID = m_nNextFID++;
6956                 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6957                 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6958             }
6959             EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont];
6960 
6961             const Ucs2SIntMap* pEncoding = NULL;
6962             const Ucs2OStrMap* pNonEncoded = NULL;
6963             getReferenceDevice()->ImplGetGraphics();
6964             pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded );
6965 
6966             Ucs2SIntMap::const_iterator enc_it;
6967             Ucs2OStrMap::const_iterator nonenc_it;
6968 
6969             sal_Int32 nCurFontID = nFontID;
6970             sal_Ucs cChar = *pCurUnicode;
6971             if( pEncoding )
6972             {
6973                 enc_it = pEncoding->find( cChar );
6974                 if( enc_it != pEncoding->end() && enc_it->second > 0 )
6975                 {
6976                     DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" );
6977                     cChar = (sal_Ucs)enc_it->second;
6978                 }
6979                 else if( (enc_it == pEncoding->end() || enc_it->second == -1) &&
6980                          pNonEncoded &&
6981                          (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() )
6982                 {
6983                     nCurFontID = 0;
6984                     // find non encoded glyph
6985                     for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it )
6986                     {
6987                         if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() )
6988                         {
6989                             nCurFontID = nec_it->m_nFontID;
6990                             cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ];
6991                             break;
6992                         }
6993                     }
6994                     if( nCurFontID == 0 ) // new nonencoded glyph
6995                     {
6996                         if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 )
6997                         {
6998                             rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() );
6999                             rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++;
7000                         }
7001                         EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back();
7002                         rEncoding.m_aEncVector.push_back( EmbedCode() );
7003                         rEncoding.m_aEncVector.back().m_aUnicode = cChar;
7004                         rEncoding.m_aEncVector.back().m_aName = nonenc_it->second;
7005                         rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1);
7006                         nCurFontID = rEncoding.m_nFontID;
7007                         cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ];
7008                     }
7009                 }
7010                 else
7011                     pEncoding = NULL;
7012             }
7013             if( ! pEncoding )
7014             {
7015                 if( cChar & 0xff00 )
7016                 {
7017                     // some characters can be used by conversion
7018                     if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area
7019                         cChar -= 0xf000;
7020                     else
7021                     {
7022                         String aString(cChar);
7023                         ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 );
7024                         cChar = ((sal_Ucs)aChar.GetChar( 0 )) & 0x00ff;
7025                     }
7026                 }
7027             }
7028 
7029             pMappedGlyphs[ i ] = (sal_Int8)cChar;
7030             pMappedFontObjects[ i ] = nCurFontID;
7031             pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont,
7032                                                             (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR,
7033                                                             false,
7034                                                             m_pReferenceDevice->mpGraphics );
7035         }
7036     }
7037 }
7038 
7039 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines )
7040 {
7041     push( PUSH_ALL );
7042 
7043     FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief();
7044 
7045     Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor();
7046     Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7047     Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7048     Color aReliefColor( COL_LIGHTGRAY );
7049     if( aTextColor == COL_BLACK )
7050         aTextColor = Color( COL_WHITE );
7051     if( aTextLineColor == COL_BLACK )
7052         aTextLineColor = Color( COL_WHITE );
7053     if( aOverlineColor == COL_BLACK )
7054         aOverlineColor = Color( COL_WHITE );
7055     if( aTextColor == COL_WHITE )
7056         aReliefColor = Color( COL_BLACK );
7057 
7058     Font aSetFont = m_aCurrentPDFState.m_aFont;
7059     aSetFont.SetRelief( RELIEF_NONE );
7060     aSetFont.SetShadow( sal_False );
7061 
7062     aSetFont.SetColor( aReliefColor );
7063     setTextLineColor( aReliefColor );
7064     setOverlineColor( aReliefColor );
7065     setFont( aSetFont );
7066     long nOff = 1 + getReferenceDevice()->mnDPIX/300;
7067     if( eRelief == RELIEF_ENGRAVED )
7068         nOff = -nOff;
7069 
7070     rLayout.DrawOffset() += Point( nOff, nOff );
7071     updateGraphicsState();
7072     drawLayout( rLayout, rText, bTextLines );
7073 
7074     rLayout.DrawOffset() -= Point( nOff, nOff );
7075     setTextLineColor( aTextLineColor );
7076     setOverlineColor( aOverlineColor );
7077     aSetFont.SetColor( aTextColor );
7078     setFont( aSetFont );
7079     updateGraphicsState();
7080     drawLayout( rLayout, rText, bTextLines );
7081 
7082     // clean up the mess
7083     pop();
7084 }
7085 
7086 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines )
7087 {
7088     Font aSaveFont = m_aCurrentPDFState.m_aFont;
7089     Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7090     Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7091 
7092     Font& rFont = m_aCurrentPDFState.m_aFont;
7093     if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 )
7094         rFont.SetColor( Color( COL_LIGHTGRAY ) );
7095     else
7096         rFont.SetColor( Color( COL_BLACK ) );
7097     rFont.SetShadow( sal_False );
7098     rFont.SetOutline( sal_False );
7099     setFont( rFont );
7100     setTextLineColor( rFont.GetColor() );
7101     setOverlineColor( rFont.GetColor() );
7102     updateGraphicsState();
7103 
7104     long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24);
7105     if( rFont.IsOutline() )
7106         nOff++;
7107     rLayout.DrawBase() += Point( nOff, nOff );
7108     drawLayout( rLayout, rText, bTextLines );
7109     rLayout.DrawBase() -= Point( nOff, nOff );
7110 
7111     setFont( aSaveFont );
7112     setTextLineColor( aSaveTextLineColor );
7113     setOverlineColor( aSaveOverlineColor );
7114     updateGraphicsState();
7115 }
7116 
7117 void PDFWriterImpl::drawVerticalGlyphs(
7118         const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7119         OStringBuffer& rLine,
7120         const Point& rAlignOffset,
7121         const Matrix3& rRotScale,
7122         double fAngle,
7123         double fXScale,
7124         double fSkew,
7125         sal_Int32 nFontHeight )
7126 {
7127     long nXOffset = 0;
7128     Point aCurPos( rGlyphs[0].m_aPos );
7129     aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7130     aCurPos += rAlignOffset;
7131     for( size_t i = 0; i < rGlyphs.size(); i++ )
7132     {
7133         // have to emit each glyph on its own
7134         double fDeltaAngle = 0.0;
7135         double fYScale = 1.0;
7136         double fTempXScale = fXScale;
7137         double fSkewB = fSkew;
7138         double fSkewA = 0.0;
7139 
7140         Point aDeltaPos;
7141         if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL )
7142         {
7143             fDeltaAngle = M_PI/2.0;
7144             aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent();
7145             aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale);
7146             fYScale = fXScale;
7147             fTempXScale = 1.0;
7148             fSkewA = -fSkewB;
7149             fSkewB = 0.0;
7150         }
7151         else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR )
7152         {
7153             fDeltaAngle = -M_PI/2.0;
7154             aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale);
7155             aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent();
7156             fYScale = fXScale;
7157             fTempXScale = 1.0;
7158             fSkewA = fSkewB;
7159             fSkewB = 0.0;
7160         }
7161         aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) );
7162         if( i < rGlyphs.size()-1 )
7163             nXOffset += rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
7164         if( ! rGlyphs[i].m_nGlyphId )
7165             continue;
7166 
7167         aDeltaPos = rRotScale.transform( aDeltaPos );
7168 
7169         Matrix3 aMat;
7170         if( fSkewB != 0.0 || fSkewA != 0.0 )
7171             aMat.skew( fSkewA, fSkewB );
7172         aMat.scale( fTempXScale, fYScale );
7173         aMat.rotate( fAngle+fDeltaAngle );
7174         aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
7175         aMat.append( m_aPages.back(), rLine );
7176         rLine.append( " Tm" );
7177         if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
7178         {
7179             rLine.append( " /F" );
7180             rLine.append( rGlyphs[i].m_nMappedFontId );
7181             rLine.append( ' ' );
7182             m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7183             rLine.append( " Tf" );
7184         }
7185         rLine.append( "<" );
7186         appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
7187         rLine.append( ">Tj\n" );
7188     }
7189 }
7190 
7191 void PDFWriterImpl::drawHorizontalGlyphs(
7192         const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7193         OStringBuffer& rLine,
7194         const Point& rAlignOffset,
7195         double fAngle,
7196         double fXScale,
7197         double fSkew,
7198         sal_Int32 nFontHeight,
7199         sal_Int32 nPixelFontHeight
7200         )
7201 {
7202     // horizontal (= normal) case
7203 
7204     // fill in  run end indices
7205     // end is marked by index of the first glyph of the next run
7206     // a run is marked by same mapped font id and same Y position
7207     std::vector< sal_uInt32 > aRunEnds;
7208     aRunEnds.reserve( rGlyphs.size() );
7209     for( size_t i = 1; i < rGlyphs.size(); i++ )
7210     {
7211         if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
7212             rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
7213         {
7214             aRunEnds.push_back(i);
7215         }
7216     }
7217     // last run ends at last glyph
7218     aRunEnds.push_back( rGlyphs.size() );
7219 
7220     // loop over runs of the same font
7221     sal_uInt32 nBeginRun = 0;
7222     for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
7223     {
7224         // setup text matrix
7225         Point aCurPos = rGlyphs[nBeginRun].m_aPos;
7226         // back transformation to current coordinate system
7227         aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7228         aCurPos += rAlignOffset;
7229         // the first run can be set with "Td" operator
7230         // subsequent use of that operator would move
7231         // the texline matrix relative to what was set before
7232         // making use of that would drive us into rounding issues
7233         Matrix3 aMat;
7234         if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
7235         {
7236             m_aPages.back().appendPoint( aCurPos, rLine, false );
7237             rLine.append( " Td " );
7238         }
7239         else
7240         {
7241             if( fSkew != 0.0 )
7242                 aMat.skew( 0.0, fSkew );
7243             aMat.scale( fXScale, 1.0 );
7244             aMat.rotate( fAngle );
7245             aMat.translate( aCurPos.X(), aCurPos.Y() );
7246             aMat.append( m_aPages.back(), rLine );
7247             rLine.append( " Tm\n" );
7248         }
7249         // set up correct font
7250         rLine.append( "/F" );
7251         rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
7252         rLine.append( ' ' );
7253         m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7254         rLine.append( " Tf" );
7255 
7256         // output glyphs using Tj or TJ
7257         OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
7258         aKernedLine.append( "[<" );
7259         aUnkernedLine.append( '<' );
7260         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
7261         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
7262 
7263         aMat.invert();
7264         bool bNeedKern = false;
7265         for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
7266         {
7267             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
7268             // check if default glyph positioning is sufficient
7269             const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
7270             const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
7271             double fAdvance = aThisPos.X() - aPrevPos.X();
7272             fAdvance *= 1000.0 / nPixelFontHeight;
7273             const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5);
7274             if( nAdjustment != 0 )
7275             {
7276                 // apply individual glyph positioning
7277                 bNeedKern = true;
7278                 aKernedLine.append( ">" );
7279                 aKernedLine.append( nAdjustment );
7280                 aKernedLine.append( "<" );
7281             }
7282             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
7283         }
7284         aKernedLine.append( ">]TJ\n" );
7285         aUnkernedLine.append( ">Tj\n" );
7286         rLine.append( bNeedKern ? aKernedLine : aUnkernedLine );
7287 
7288         // set beginning of next run
7289         nBeginRun = aRunEnds[nRun];
7290     }
7291 }
7292 
7293 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines )
7294 {
7295     // relief takes precedence over shadow (see outdev3.cxx)
7296     if(  m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE )
7297     {
7298         drawRelief( rLayout, rText, bTextLines );
7299         return;
7300     }
7301     else if( m_aCurrentPDFState.m_aFont.IsShadow() )
7302         drawShadow( rLayout, rText, bTextLines );
7303 
7304     OStringBuffer aLine( 512 );
7305 
7306     const int nMaxGlyphs = 256;
7307 
7308     sal_GlyphId pGlyphs[nMaxGlyphs];
7309     sal_Int32 pGlyphWidths[nMaxGlyphs];
7310     sal_uInt8 pMappedGlyphs[nMaxGlyphs];
7311     sal_Int32 pMappedFontObjects[nMaxGlyphs];
7312     std::vector<sal_Ucs> aUnicodes;
7313     aUnicodes.reserve( nMaxGlyphs );
7314     sal_Int32 pUnicodesPerGlyph[nMaxGlyphs];
7315     int pCharPosAry[nMaxGlyphs];
7316     sal_Int32 nAdvanceWidths[nMaxGlyphs];
7317     const ImplFontData* pFallbackFonts[nMaxGlyphs];
7318     bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
7319     int nGlyphs;
7320     int nIndex = 0;
7321     int nMinCharPos = 0, nMaxCharPos = rText.Len()-1;
7322     double fXScale = 1.0;
7323     double fSkew = 0.0;
7324     sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight;
7325     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
7326 
7327     // transform font height back to current units
7328     // note: the layout calculates in outdevs device pixel !!
7329     sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
7330     if( m_aCurrentPDFState.m_aFont.GetWidth() )
7331     {
7332         Font aFont( m_aCurrentPDFState.m_aFont );
7333         aFont.SetWidth( 0 );
7334         FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
7335         if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() )
7336         {
7337             fXScale =
7338                 (double)m_aCurrentPDFState.m_aFont.GetWidth() /
7339                 (double)aMetric.GetWidth();
7340         }
7341         // force state before GetFontMetric
7342         m_pReferenceDevice->ImplNewFont();
7343     }
7344 
7345     // perform artificial italics if necessary
7346     if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
7347           m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
7348         !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL ||
7349            m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE )
7350         )
7351     {
7352         fSkew = M_PI/12.0;
7353     }
7354 
7355     // if the mapmode is distorted we need to adjust for that also
7356     if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
7357     {
7358         fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
7359     }
7360 
7361     int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
7362     // normalize angles
7363     while( nAngle < 0 )
7364         nAngle += 3600;
7365     nAngle = nAngle % 3600;
7366     double fAngle = (double)nAngle * M_PI / 1800.0;
7367 
7368     Matrix3 aRotScale;
7369     aRotScale.scale( fXScale, 1.0 );
7370     if( fAngle != 0.0 )
7371         aRotScale.rotate( -fAngle );
7372 
7373     bool bPop = false;
7374     bool bABold = false;
7375     // artificial bold necessary ?
7376     if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
7377         m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
7378     {
7379         if( ! bPop )
7380             aLine.append( "q " );
7381         bPop = true;
7382         bABold = true;
7383     }
7384     // setup text colors (if necessary)
7385     Color aStrokeColor( COL_TRANSPARENT );
7386     Color aNonStrokeColor( COL_TRANSPARENT );
7387 
7388     if( m_aCurrentPDFState.m_aFont.IsOutline() )
7389     {
7390         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7391         aNonStrokeColor = Color( COL_WHITE );
7392     }
7393     else
7394         aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7395     if( bABold )
7396         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7397 
7398     if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
7399     {
7400         if( ! bPop )
7401             aLine.append( "q " );
7402         bPop = true;
7403         appendStrokingColor( aStrokeColor, aLine );
7404         aLine.append( "\n" );
7405     }
7406     if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
7407     {
7408         if( ! bPop )
7409             aLine.append( "q " );
7410         bPop = true;
7411         appendNonStrokingColor( aNonStrokeColor, aLine );
7412         aLine.append( "\n" );
7413     }
7414 
7415     // begin text object
7416     aLine.append( "BT\n" );
7417     // outline attribute ?
7418     if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
7419     {
7420         // set correct text mode, set stroke width
7421         aLine.append( "2 Tr " ); // fill, then stroke
7422 
7423         if( m_aCurrentPDFState.m_aFont.IsOutline() )
7424         {
7425             // unclear what to do in case of outline and artificial bold
7426             // for the time being outline wins
7427             aLine.append( "0.25 w \n" );
7428         }
7429         else
7430         {
7431             double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0;
7432             m_aPages.back().appendMappedLength( fW, aLine );
7433             aLine.append ( " w\n" );
7434         }
7435     }
7436 
7437     FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
7438 
7439     // collect the glyphs into a single array
7440     const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
7441     std::vector< PDFGlyph > aGlyphs;
7442     aGlyphs.reserve( nTmpMaxGlyphs );
7443     // first get all the glyphs and register them; coordinates still in Pixel
7444     Point aGNGlyphPos;
7445     while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 )
7446     {
7447         aUnicodes.clear();
7448         for( int i = 0; i < nGlyphs; i++ )
7449         {
7450             pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] );
7451 
7452             // default case: 1 glyph is one unicode
7453             pUnicodesPerGlyph[i] = 1;
7454             if( (pGlyphs[i] & GF_ISCHAR) )
7455             {
7456                 aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) );
7457             }
7458             else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
7459             {
7460                 int nChars = 1;
7461                 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) );
7462                 pUnicodesPerGlyph[i] = 1;
7463                 // try to handle ligatures and such
7464                 if( i < nGlyphs-1 )
7465                 {
7466                     nChars = pCharPosAry[i+1] - pCharPosAry[i];
7467                     // #i115618# fix for simple RTL+CTL cases
7468                     // TODO: sanitize for RTL ligatures, more complex CTL, etc.
7469                     if( nChars < 0 )
7470                         nChars = -nChars;
7471 		    else if( nChars == 0 )
7472                         nChars = 1;
7473                     pUnicodesPerGlyph[i] = nChars;
7474                     for( int n = 1; n < nChars; n++ )
7475                         aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) );
7476                 }
7477                 // #i36691# hack that is needed because currently the pGlyphs[]
7478                 // argument is ignored for embeddable fonts and so the layout
7479                 // engine's glyph work is ignored (i.e. char mirroring)
7480                 // TODO: a real solution would be to map the layout engine's
7481                 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
7482                 // back to unicode and then to embeddable font's encoding
7483                 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL )
7484                 {
7485                     size_t nI = aUnicodes.size()-1;
7486                     for( int n = 0; n < nChars; n++, nI-- )
7487                         aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI]));
7488                 }
7489             }
7490             else
7491                 aUnicodes.push_back( 0 );
7492             // note: in case of ctl one character may result
7493             // in multiple glyphs. The current SalLayout
7494             // implementations set -1 then to indicate that no direct
7495             // mapping is possible
7496         }
7497 
7498         registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
7499 
7500         for( int i = 0; i < nGlyphs; i++ )
7501         {
7502             aGlyphs.push_back( PDFGlyph( aGNGlyphPos,
7503                                          pGlyphWidths[i],
7504                                          pGlyphs[i],
7505                                          pMappedFontObjects[i],
7506                                          pMappedGlyphs[i] ) );
7507             if( bVertical )
7508                 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7509             else
7510                 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7511         }
7512     }
7513 
7514     Point aAlignOffset;
7515     if ( eAlign == ALIGN_BOTTOM )
7516         aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
7517     else if ( eAlign == ALIGN_TOP )
7518         aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
7519     if( aAlignOffset.X() || aAlignOffset.Y() )
7520         aAlignOffset = aRotScale.transform( aAlignOffset );
7521 
7522     /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
7523        string contained only on of the UTF16 BOMs
7524     */
7525     if( ! aGlyphs.empty() )
7526     {
7527         if( bVertical )
7528             drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
7529         else
7530             drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
7531     }
7532 
7533     // end textobject
7534     aLine.append( "ET\n" );
7535     if( bPop )
7536         aLine.append( "Q\n" );
7537 
7538     writeBuffer( aLine.getStr(), aLine.getLength() );
7539 
7540     // draw eventual textlines
7541     FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
7542     FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
7543     FontUnderline eOverline  = m_aCurrentPDFState.m_aFont.GetOverline();
7544     if( bTextLines &&
7545         (
7546          ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) ||
7547          ( eOverline  != UNDERLINE_NONE && eOverline  != UNDERLINE_DONTKNOW ) ||
7548          ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
7549          )
7550         )
7551     {
7552         sal_Bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
7553         if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
7554         {
7555             Point aPos, aStartPt;
7556             sal_Int32 nWidth = 0, nAdvance=0;
7557             for( int nStart = 0;;)
7558             {
7559                 sal_GlyphId nGlyphIndex;
7560                 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7561                     break;
7562 
7563                 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7564                 {
7565                     if( !nWidth )
7566                         aStartPt = aPos;
7567 
7568                     nWidth += nAdvance;
7569                 }
7570                 else if( nWidth > 0 )
7571                 {
7572                     drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7573                                   m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7574                                   eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7575                     nWidth = 0;
7576                 }
7577             }
7578 
7579             if( nWidth > 0 )
7580             {
7581                 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7582                               m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7583                               eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7584             }
7585         }
7586         else
7587         {
7588             Point aStartPt = rLayout.GetDrawPosition();
7589             int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
7590             drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7591                           m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7592                           eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7593         }
7594     }
7595 
7596     // write eventual emphasis marks
7597     if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
7598     {
7599         PolyPolygon 			aEmphPoly;
7600         Rectangle				aEmphRect1;
7601         Rectangle				aEmphRect2;
7602         long					nEmphYOff;
7603         long					nEmphWidth;
7604         long					nEmphHeight;
7605         sal_Bool					bEmphPolyLine;
7606         FontEmphasisMark		nEmphMark;
7607 
7608         push( PUSH_ALL );
7609 
7610         aLine.setLength( 0 );
7611         aLine.append( "q\n" );
7612 
7613         nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
7614         if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7615             nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
7616         else
7617             nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
7618         m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
7619                                                  bEmphPolyLine,
7620                                                  aEmphRect1,
7621                                                  aEmphRect2,
7622                                                  nEmphYOff,
7623                                                  nEmphWidth,
7624                                                  nEmphMark,
7625                                                  m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight),
7626                                                  m_pReferenceDevice->mpFontEntry->mnOrientation );
7627         if ( bEmphPolyLine )
7628         {
7629             setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
7630             setFillColor( Color( COL_TRANSPARENT ) );
7631         }
7632         else
7633         {
7634             setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
7635             setLineColor( Color( COL_TRANSPARENT ) );
7636         }
7637         writeBuffer( aLine.getStr(), aLine.getLength() );
7638 
7639         Point aOffset = Point(0,0);
7640 
7641         if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7642             aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff;
7643         else
7644             aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff;
7645 
7646         long nEmphWidth2     = nEmphWidth / 2;
7647         long nEmphHeight2    = nEmphHeight / 2;
7648         aOffset += Point( nEmphWidth2, nEmphHeight2 );
7649 
7650         if ( eAlign == ALIGN_BOTTOM )
7651             aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent;
7652         else if ( eAlign == ALIGN_TOP )
7653             aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent;
7654 
7655         for( int nStart = 0;;)
7656         {
7657             Point aPos;
7658             sal_GlyphId nGlyphIndex;
7659             sal_Int32 nAdvance;
7660             if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7661                 break;
7662 
7663             if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7664             {
7665                 Point aAdjOffset = aOffset;
7666                 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2;
7667                 aAdjOffset = aRotScale.transform( aAdjOffset );
7668 
7669                 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
7670 
7671                 aPos += aAdjOffset;
7672                 aPos = m_pReferenceDevice->PixelToLogic( aPos );
7673                 drawEmphasisMark( aPos.X(), aPos.Y(),
7674                                   aEmphPoly, bEmphPolyLine,
7675                                   aEmphRect1, aEmphRect2 );
7676             }
7677         }
7678 
7679         writeBuffer( "Q\n", 2 );
7680         pop();
7681     }
7682 
7683 }
7684 
7685 void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
7686                                       const PolyPolygon& rPolyPoly, sal_Bool bPolyLine,
7687                                       const Rectangle& rRect1, const Rectangle& rRect2 )
7688 {
7689     // TODO: pass nWidth as width of this mark
7690     // long nWidth = 0;
7691 
7692     if ( rPolyPoly.Count() )
7693     {
7694         if ( bPolyLine )
7695         {
7696             Polygon aPoly = rPolyPoly.GetObject( 0 );
7697             aPoly.Move( nX, nY );
7698             drawPolyLine( aPoly );
7699         }
7700         else
7701         {
7702             PolyPolygon aPolyPoly = rPolyPoly;
7703             aPolyPoly.Move( nX, nY );
7704             drawPolyPolygon( aPolyPoly );
7705         }
7706     }
7707 
7708     if ( !rRect1.IsEmpty() )
7709     {
7710         Rectangle aRect( Point( nX+rRect1.Left(),
7711                                 nY+rRect1.Top() ), rRect1.GetSize() );
7712         drawRectangle( aRect );
7713     }
7714 
7715     if ( !rRect2.IsEmpty() )
7716     {
7717         Rectangle aRect( Point( nX+rRect2.Left(),
7718                                 nY+rRect2.Top() ), rRect2.GetSize() );
7719 
7720         drawRectangle( aRect );
7721     }
7722 }
7723 
7724 void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7725 {
7726     MARK( "drawText" );
7727 
7728     updateGraphicsState();
7729 
7730     // get a layout from the OuputDevice's SalGraphics
7731     // this also enforces font substitution and sets the font on SalGraphics
7732     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
7733     if( pLayout )
7734     {
7735         drawLayout( *pLayout, rText, bTextLines );
7736         pLayout->Release();
7737     }
7738 }
7739 
7740 void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7741 {
7742     MARK( "drawText with array" );
7743 
7744     updateGraphicsState();
7745 
7746     // get a layout from the OuputDevice's SalGraphics
7747     // this also enforces font substitution and sets the font on SalGraphics
7748     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
7749     if( pLayout )
7750     {
7751         drawLayout( *pLayout, rText, bTextLines );
7752         pLayout->Release();
7753     }
7754 }
7755 
7756 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7757 {
7758     MARK( "drawStretchText" );
7759 
7760     updateGraphicsState();
7761 
7762     // get a layout from the OuputDevice's SalGraphics
7763     // this also enforces font substitution and sets the font on SalGraphics
7764     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
7765     if( pLayout )
7766     {
7767         drawLayout( *pLayout, rText, bTextLines );
7768         pLayout->Release();
7769     }
7770 }
7771 
7772 void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines )
7773 {
7774     long        nWidth          = rRect.GetWidth();
7775     long        nHeight         = rRect.GetHeight();
7776 
7777     if ( nWidth <= 0 || nHeight <= 0 )
7778         return;
7779 
7780     MARK( "drawText with rectangle" );
7781 
7782     updateGraphicsState();
7783 
7784     // clip with rectangle
7785     OStringBuffer aLine;
7786     aLine.append( "q " );
7787     m_aPages.back().appendRect( rRect, aLine );
7788     aLine.append( " W* n\n" );
7789     writeBuffer( aLine.getStr(), aLine.getLength() );
7790 
7791     // if disabled text is needed, put in here
7792 
7793     Point       aPos            = rRect.TopLeft();
7794 
7795     long		nTextHeight		= m_pReferenceDevice->GetTextHeight();
7796     xub_StrLen  nMnemonicPos    = STRING_NOTFOUND;
7797 
7798     String aStr = rOrigStr;
7799     if ( nStyle & TEXT_DRAW_MNEMONIC )
7800         aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos );
7801 
7802     // multiline text
7803     if ( nStyle & TEXT_DRAW_MULTILINE )
7804     {
7805         XubString               aLastLine;
7806         ImplMultiTextLineInfo   aMultiLineInfo;
7807         ImplTextLineInfo*       pLineInfo;
7808         long                    nMaxTextWidth;
7809         xub_StrLen              i;
7810         xub_StrLen              nLines;
7811         xub_StrLen              nFormatLines;
7812 
7813         if ( nTextHeight )
7814         {
7815             ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice );
7816             nMaxTextWidth = OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout );
7817             nLines = (xub_StrLen)(nHeight/nTextHeight);
7818             nFormatLines = aMultiLineInfo.Count();
7819             if ( !nLines )
7820                 nLines = 1;
7821             if ( nFormatLines > nLines )
7822             {
7823                 if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
7824                 {
7825                     // handle last line
7826                     nFormatLines = nLines-1;
7827 
7828                     pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
7829                     aLastLine = aStr.Copy( pLineInfo->GetIndex() );
7830                     aLastLine.ConvertLineEnd( LINEEND_LF );
7831                     // replace line feed by space
7832                     xub_StrLen nLastLineLen = aLastLine.Len();
7833                     for ( i = 0; i < nLastLineLen; i++ )
7834                     {
7835                         if ( aLastLine.GetChar( i ) == _LF )
7836                             aLastLine.SetChar( i, ' ' );
7837                     }
7838                     aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
7839                     nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
7840                     nStyle |= TEXT_DRAW_TOP;
7841                 }
7842             }
7843 
7844             // vertical alignment
7845             if ( nStyle & TEXT_DRAW_BOTTOM )
7846                 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
7847             else if ( nStyle & TEXT_DRAW_VCENTER )
7848                 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
7849 
7850             // draw all lines excluding the last
7851             for ( i = 0; i < nFormatLines; i++ )
7852             {
7853                 pLineInfo = aMultiLineInfo.GetLine( i );
7854                 if ( nStyle & TEXT_DRAW_RIGHT )
7855                     aPos.X() += nWidth-pLineInfo->GetWidth();
7856                 else if ( nStyle & TEXT_DRAW_CENTER )
7857                     aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
7858                 xub_StrLen nIndex   = pLineInfo->GetIndex();
7859                 xub_StrLen nLineLen = pLineInfo->GetLen();
7860                 drawText( aPos, aStr, nIndex, nLineLen, bTextLines );
7861                 // mnemonics should not appear in documents,
7862                 // if the need arises, put them in here
7863                 aPos.Y() += nTextHeight;
7864                 aPos.X() = rRect.Left();
7865             }
7866 
7867 
7868             // output last line left adjusted since it was shortened
7869             if ( aLastLine.Len() )
7870                 drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines );
7871         }
7872     }
7873     else
7874     {
7875         long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7876 
7877         // Evt. Text kuerzen
7878         if ( nTextWidth > nWidth )
7879         {
7880             if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) )
7881             {
7882                 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
7883                 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
7884                 nStyle |= TEXT_DRAW_LEFT;
7885                 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7886             }
7887         }
7888 
7889         // vertical alignment
7890         if ( nStyle & TEXT_DRAW_RIGHT )
7891             aPos.X() += nWidth-nTextWidth;
7892         else if ( nStyle & TEXT_DRAW_CENTER )
7893             aPos.X() += (nWidth-nTextWidth)/2;
7894 
7895         if ( nStyle & TEXT_DRAW_BOTTOM )
7896             aPos.Y() += nHeight-nTextHeight;
7897         else if ( nStyle & TEXT_DRAW_VCENTER )
7898             aPos.Y() += (nHeight-nTextHeight)/2;
7899 
7900         // mnemonics should be inserted here if the need arises
7901 
7902         // draw the actual text
7903         drawText( aPos, aStr, 0, STRING_LEN, bTextLines );
7904     }
7905 
7906     // reset clip region to original value
7907     aLine.setLength( 0 );
7908     aLine.append( "Q\n" );
7909     writeBuffer( aLine.getStr(), aLine.getLength() );
7910 }
7911 
7912 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
7913 {
7914     MARK( "drawLine" );
7915 
7916     updateGraphicsState();
7917 
7918     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7919         return;
7920 
7921     OStringBuffer aLine;
7922     m_aPages.back().appendPoint( rStart, aLine );
7923     aLine.append( " m " );
7924     m_aPages.back().appendPoint( rStop, aLine );
7925     aLine.append( " l S\n" );
7926 
7927     writeBuffer( aLine.getStr(), aLine.getLength() );
7928 }
7929 
7930 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
7931 {
7932     MARK( "drawLine with LineInfo" );
7933     updateGraphicsState();
7934 
7935     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7936         return;
7937 
7938     if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 )
7939     {
7940         drawLine( rStart, rStop );
7941         return;
7942     }
7943 
7944     OStringBuffer aLine;
7945 
7946     aLine.append( "q " );
7947     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
7948     {
7949         m_aPages.back().appendPoint( rStart, aLine );
7950         aLine.append( " m " );
7951         m_aPages.back().appendPoint( rStop, aLine );
7952         aLine.append( " l S Q\n" );
7953 
7954         writeBuffer( aLine.getStr(), aLine.getLength() );
7955     }
7956     else
7957     {
7958         PDFWriter::ExtLineInfo aInfo;
7959         convertLineInfoToExtLineInfo( rInfo, aInfo );
7960         Point aPolyPoints[2] = { rStart, rStop };
7961         Polygon aPoly( 2, aPolyPoints );
7962         drawPolyLine( aPoly, aInfo );
7963     }
7964 }
7965 
7966 void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth )
7967 {
7968     Point aDiff( rStop-rStart );
7969     double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) );
7970     if( fLen < 1.0 )
7971         return;
7972 
7973     MARK( "drawWaveLine" );
7974     updateGraphicsState();
7975 
7976     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7977         return;
7978 
7979     OStringBuffer aLine( 512 );
7980     aLine.append( "q " );
7981     m_aPages.back().appendMappedLength( nLineWidth, aLine, true );
7982     aLine.append( " w " );
7983 
7984     appendDouble( (double)aDiff.X()/fLen, aLine );
7985     aLine.append( ' ' );
7986     appendDouble( -(double)aDiff.Y()/fLen, aLine );
7987     aLine.append( ' ' );
7988     appendDouble( (double)aDiff.Y()/fLen, aLine );
7989     aLine.append( ' ' );
7990     appendDouble( (double)aDiff.X()/fLen, aLine );
7991     aLine.append( ' ' );
7992     m_aPages.back().appendPoint( rStart, aLine );
7993     aLine.append( " cm " );
7994     m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine );
7995     aLine.append( "Q\n" );
7996     writeBuffer( aLine.getStr(), aLine.getLength() );
7997 }
7998 
7999 #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x )
8000 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
8001 
8002 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8003 {
8004     // note: units in pFontEntry are ref device pixel
8005     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8006     long			nLineHeight = 0;
8007     long			nLinePos = 0;
8008 
8009     appendStrokingColor( aColor, aLine );
8010     aLine.append( "\n" );
8011 
8012     if ( bIsAbove )
8013     {
8014         if ( !pFontEntry->maMetric.mnAboveWUnderlineSize )
8015             m_pReferenceDevice->ImplInitAboveTextLineSize();
8016         nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize );
8017         nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset );
8018     }
8019     else
8020     {
8021         if ( !pFontEntry->maMetric.mnWUnderlineSize )
8022             m_pReferenceDevice->ImplInitTextLineSize();
8023         nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize );
8024         nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset );
8025     }
8026     if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
8027         nLineHeight = 3;
8028 
8029     long nLineWidth = getReferenceDevice()->mnDPIX/450;
8030     if ( ! nLineWidth )
8031         nLineWidth = 1;
8032 
8033     if ( eTextLine == UNDERLINE_BOLDWAVE )
8034         nLineWidth = 3*nLineWidth;
8035 
8036     m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
8037     aLine.append( " w " );
8038 
8039     if ( eTextLine == UNDERLINE_DOUBLEWAVE )
8040     {
8041         long nOrgLineHeight = nLineHeight;
8042         nLineHeight /= 3;
8043         if ( nLineHeight < 2 )
8044         {
8045             if ( nOrgLineHeight > 1 )
8046                 nLineHeight = 2;
8047             else
8048                 nLineHeight = 1;
8049         }
8050         long nLineDY = nOrgLineHeight-(nLineHeight*2);
8051         if ( nLineDY < nLineWidth )
8052             nLineDY = nLineWidth;
8053         long nLineDY2 = nLineDY/2;
8054         if ( !nLineDY2 )
8055             nLineDY2 = 1;
8056 
8057         nLinePos -= nLineWidth-nLineDY2;
8058 
8059         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8060 
8061         nLinePos += nLineWidth+nLineDY;
8062         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8063     }
8064     else
8065     {
8066         if ( eTextLine != UNDERLINE_BOLDWAVE )
8067             nLinePos -= nLineWidth/2;
8068         m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
8069     }
8070 }
8071 
8072 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8073 {
8074     // note: units in pFontEntry are ref device pixel
8075     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8076     long			nLineHeight = 0;
8077     long			nLinePos  = 0;
8078     long			nLinePos2 = 0;
8079 
8080     if ( eTextLine > UNDERLINE_BOLDWAVE )
8081         eTextLine = UNDERLINE_SINGLE;
8082 
8083     switch ( eTextLine )
8084     {
8085         case UNDERLINE_SINGLE:
8086         case UNDERLINE_DOTTED:
8087         case UNDERLINE_DASH:
8088         case UNDERLINE_LONGDASH:
8089         case UNDERLINE_DASHDOT:
8090         case UNDERLINE_DASHDOTDOT:
8091             if ( bIsAbove )
8092             {
8093                 if ( !pFontEntry->maMetric.mnAboveUnderlineSize )
8094                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8095                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize );
8096                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset );
8097             }
8098             else
8099             {
8100                 if ( !pFontEntry->maMetric.mnUnderlineSize )
8101                     m_pReferenceDevice->ImplInitTextLineSize();
8102                 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize );
8103                 nLinePos    = HCONV( pFontEntry->maMetric.mnUnderlineOffset );
8104             }
8105             break;
8106         case UNDERLINE_BOLD:
8107         case UNDERLINE_BOLDDOTTED:
8108         case UNDERLINE_BOLDDASH:
8109         case UNDERLINE_BOLDLONGDASH:
8110         case UNDERLINE_BOLDDASHDOT:
8111         case UNDERLINE_BOLDDASHDOTDOT:
8112             if ( bIsAbove )
8113             {
8114                 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize )
8115                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8116                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize );
8117                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset );
8118             }
8119             else
8120             {
8121                 if ( !pFontEntry->maMetric.mnBUnderlineSize )
8122                     m_pReferenceDevice->ImplInitTextLineSize();
8123                 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize );
8124                 nLinePos    = HCONV( pFontEntry->maMetric.mnBUnderlineOffset );
8125                 nLinePos += nLineHeight/2;
8126             }
8127             break;
8128         case UNDERLINE_DOUBLE:
8129             if ( bIsAbove )
8130             {
8131                 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize )
8132                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8133                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize );
8134                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 );
8135                 nLinePos2   = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 );
8136             }
8137             else
8138             {
8139                 if ( !pFontEntry->maMetric.mnDUnderlineSize )
8140                     m_pReferenceDevice->ImplInitTextLineSize();
8141                 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize );
8142                 nLinePos    = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 );
8143                 nLinePos2   = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 );
8144             }
8145         default:
8146             break;
8147     }
8148 
8149     if ( nLineHeight )
8150     {
8151         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8152         aLine.append( " w " );
8153         appendStrokingColor( aColor, aLine );
8154         aLine.append( "\n" );
8155 
8156         switch ( eTextLine )
8157         {
8158             case UNDERLINE_DOTTED:
8159             case UNDERLINE_BOLDDOTTED:
8160                 aLine.append( "[ " );
8161                 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8162                 aLine.append( " ] 0 d\n" );
8163                 break;
8164             case UNDERLINE_DASH:
8165             case UNDERLINE_LONGDASH:
8166             case UNDERLINE_BOLDDASH:
8167             case UNDERLINE_BOLDLONGDASH:
8168                 {
8169                     sal_Int32 nDashLength = 4*nLineHeight;
8170                     sal_Int32 nVoidLength = 2*nLineHeight;
8171                     if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) )
8172                         nDashLength = 8*nLineHeight;
8173 
8174                     aLine.append( "[ " );
8175                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8176                     aLine.append( ' ' );
8177                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8178                     aLine.append( " ] 0 d\n" );
8179                 }
8180                 break;
8181             case UNDERLINE_DASHDOT:
8182             case UNDERLINE_BOLDDASHDOT:
8183                 {
8184                     sal_Int32 nDashLength = 4*nLineHeight;
8185                     sal_Int32 nVoidLength = 2*nLineHeight;
8186                     aLine.append( "[ " );
8187                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8188                     aLine.append( ' ' );
8189                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8190                     aLine.append( ' ' );
8191                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8192                     aLine.append( ' ' );
8193                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8194                     aLine.append( " ] 0 d\n" );
8195                 }
8196                 break;
8197             case UNDERLINE_DASHDOTDOT:
8198             case UNDERLINE_BOLDDASHDOTDOT:
8199                 {
8200                     sal_Int32 nDashLength = 4*nLineHeight;
8201                     sal_Int32 nVoidLength = 2*nLineHeight;
8202                     aLine.append( "[ " );
8203                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8204                     aLine.append( ' ' );
8205                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8206                     aLine.append( ' ' );
8207                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8208                     aLine.append( ' ' );
8209                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8210                     aLine.append( ' ' );
8211                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8212                     aLine.append( ' ' );
8213                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8214                     aLine.append( " ] 0 d\n" );
8215                 }
8216                 break;
8217             default:
8218                 break;
8219         }
8220 
8221         aLine.append( "0 " );
8222         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8223         aLine.append( " m " );
8224         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8225         aLine.append( ' ' );
8226         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8227         aLine.append( " l S\n" );
8228         if ( eTextLine == UNDERLINE_DOUBLE )
8229         {
8230             aLine.append( "0 " );
8231             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8232             aLine.append( " m " );
8233             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8234             aLine.append( ' ' );
8235             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8236             aLine.append( " l S\n" );
8237         }
8238     }
8239 }
8240 
8241 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
8242 {
8243     // note: units in pFontEntry are ref device pixel
8244     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8245     long			nLineHeight = 0;
8246     long			nLinePos  = 0;
8247     long			nLinePos2 = 0;
8248 
8249     if ( eStrikeout > STRIKEOUT_X )
8250         eStrikeout = STRIKEOUT_SINGLE;
8251 
8252     switch ( eStrikeout )
8253     {
8254         case STRIKEOUT_SINGLE:
8255             if ( !pFontEntry->maMetric.mnStrikeoutSize )
8256                 m_pReferenceDevice->ImplInitTextLineSize();
8257             nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize );
8258             nLinePos    = HCONV( pFontEntry->maMetric.mnStrikeoutOffset );
8259             break;
8260         case STRIKEOUT_BOLD:
8261             if ( !pFontEntry->maMetric.mnBStrikeoutSize )
8262                 m_pReferenceDevice->ImplInitTextLineSize();
8263             nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize );
8264             nLinePos    = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset );
8265             break;
8266         case STRIKEOUT_DOUBLE:
8267             if ( !pFontEntry->maMetric.mnDStrikeoutSize )
8268                 m_pReferenceDevice->ImplInitTextLineSize();
8269             nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize );
8270             nLinePos    = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 );
8271             nLinePos2   = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 );
8272             break;
8273         default:
8274             break;
8275     }
8276 
8277     if ( nLineHeight )
8278     {
8279         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8280         aLine.append( " w " );
8281         appendStrokingColor( aColor, aLine );
8282         aLine.append( "\n" );
8283 
8284         aLine.append( "0 " );
8285         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8286         aLine.append( " m " );
8287         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8288         aLine.append( ' ' );
8289         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8290         aLine.append( " l S\n" );
8291 
8292         if ( eStrikeout == STRIKEOUT_DOUBLE )
8293         {
8294             aLine.append( "0 " );
8295             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8296             aLine.append( " m " );
8297             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8298             aLine.append( ' ' );
8299             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8300             aLine.append( " l S\n" );
8301         }
8302     }
8303 }
8304 
8305 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
8306 {
8307     String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" );
8308     String aStrikeout = aStrikeoutChar;
8309     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
8310         aStrikeout.Append( aStrikeout );
8311 
8312     // do not get broader than nWidth modulo 1 character
8313     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
8314         aStrikeout.Erase( 0, 1 );
8315     aStrikeout.Append( aStrikeoutChar );
8316     sal_Bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
8317     if ( bShadow )
8318     {
8319         Font aFont = m_aCurrentPDFState.m_aFont;
8320         aFont.SetShadow( sal_False );
8321         setFont( aFont );
8322         updateGraphicsState();
8323     }
8324 
8325     // strikeout string is left aligned non-CTL text
8326     sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode();
8327     m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED );
8328     drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false );
8329     m_pReferenceDevice->SetLayoutMode( nOrigTLM );
8330 
8331     if ( bShadow )
8332     {
8333         Font aFont = m_aCurrentPDFState.m_aFont;
8334         aFont.SetShadow( sal_True );
8335         setFont( aFont );
8336         updateGraphicsState();
8337     }
8338 }
8339 
8340 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove )
8341 {
8342     if ( !nWidth ||
8343          ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
8344            ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) &&
8345            ((eOverline  == UNDERLINE_NONE)||(eOverline  == UNDERLINE_DONTKNOW)) ) )
8346         return;
8347 
8348     MARK( "drawTextLine" );
8349     updateGraphicsState();
8350 
8351     // note: units in pFontEntry are ref device pixel
8352     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8353     Color			aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
8354     Color			aOverlineColor  = m_aCurrentPDFState.m_aOverlineColor;
8355     Color			aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
8356     bool			bStrikeoutDone = false;
8357     bool			bUnderlineDone = false;
8358     bool			bOverlineDone  = false;
8359 
8360     if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
8361     {
8362         drawStrikeoutChar( rPos, nWidth, eStrikeout );
8363         bStrikeoutDone = true;
8364     }
8365 
8366     Point aPos( rPos );
8367     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
8368     if( eAlign == ALIGN_TOP )
8369         aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent );
8370     else if( eAlign == ALIGN_BOTTOM )
8371         aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent );
8372 
8373     OStringBuffer aLine( 512 );
8374     // save GS
8375     aLine.append( "q " );
8376 
8377     // rotate and translate matrix
8378     double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
8379     Matrix3 aMat;
8380     aMat.rotate( fAngle );
8381     aMat.translate( aPos.X(), aPos.Y() );
8382     aMat.append( m_aPages.back(), aLine );
8383     aLine.append( " cm\n" );
8384 
8385     if ( aUnderlineColor.GetTransparency() != 0 )
8386         aUnderlineColor = aStrikeoutColor;
8387 
8388     if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
8389          (eUnderline == UNDERLINE_WAVE) ||
8390          (eUnderline == UNDERLINE_DOUBLEWAVE) ||
8391          (eUnderline == UNDERLINE_BOLDWAVE) )
8392     {
8393         drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8394         bUnderlineDone = true;
8395     }
8396 
8397     if ( (eOverline == UNDERLINE_SMALLWAVE) ||
8398          (eOverline == UNDERLINE_WAVE) ||
8399          (eOverline == UNDERLINE_DOUBLEWAVE) ||
8400          (eOverline == UNDERLINE_BOLDWAVE) )
8401     {
8402         drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8403         bOverlineDone = true;
8404     }
8405 
8406     if ( !bUnderlineDone )
8407     {
8408         drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8409     }
8410 
8411     if ( !bOverlineDone )
8412     {
8413         drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8414     }
8415 
8416     if ( !bStrikeoutDone )
8417     {
8418         drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
8419     }
8420 
8421     aLine.append( "Q\n" );
8422     writeBuffer( aLine.getStr(), aLine.getLength() );
8423 }
8424 
8425 void PDFWriterImpl::drawPolygon( const Polygon& rPoly )
8426 {
8427     MARK( "drawPolygon" );
8428 
8429     updateGraphicsState();
8430 
8431     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8432         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8433         return;
8434 
8435     int nPoints = rPoly.GetSize();
8436     OStringBuffer aLine( 20 * nPoints );
8437     m_aPages.back().appendPolygon( rPoly, aLine );
8438     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8439         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8440         aLine.append( "B*\n" );
8441     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8442         aLine.append( "S\n" );
8443     else
8444         aLine.append( "f*\n" );
8445 
8446     writeBuffer( aLine.getStr(), aLine.getLength() );
8447 }
8448 
8449 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly )
8450 {
8451     MARK( "drawPolyPolygon" );
8452 
8453     updateGraphicsState();
8454 
8455     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8456         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8457         return;
8458 
8459     int nPolygons = rPolyPoly.Count();
8460 
8461     OStringBuffer aLine( 40 * nPolygons );
8462     m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
8463     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8464         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8465         aLine.append( "B*\n" );
8466     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8467         aLine.append( "S\n" );
8468     else
8469         aLine.append( "f*\n" );
8470 
8471     writeBuffer( aLine.getStr(), aLine.getLength() );
8472 }
8473 
8474 void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
8475 {
8476     DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8477     nTransparentPercent = nTransparentPercent % 100;
8478 
8479     MARK( "drawTransparent" );
8480 
8481     updateGraphicsState();
8482 
8483     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8484         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8485         return;
8486 
8487     if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 )
8488     {
8489         m_aErrors.insert( m_bIsPDF_A1 ?
8490                           PDFWriter::Warning_Transparency_Omitted_PDFA :
8491                           PDFWriter::Warning_Transparency_Omitted_PDF13 );
8492 
8493         drawPolyPolygon( rPolyPoly );
8494         return;
8495     }
8496 
8497     // create XObject
8498     m_aTransparentObjects.push_back( TransparencyEmit() );
8499     // FIXME: polygons with beziers may yield incorrect bound rect
8500     m_aTransparentObjects.back().m_aBoundRect	  = rPolyPoly.GetBoundRect();
8501     // convert rectangle to default user space
8502     m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8503     m_aTransparentObjects.back().m_nObject		    = createObject();
8504     m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8505     m_aTransparentObjects.back().m_fAlpha		    = (double)(100-nTransparentPercent) / 100.0;
8506     m_aTransparentObjects.back().m_pContentStream   = new SvMemoryStream( 256, 256 );
8507     // create XObject's content stream
8508     OStringBuffer aContent( 256 );
8509     m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
8510     if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
8511         m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
8512         aContent.append( " B*\n" );
8513     else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
8514         aContent.append( " S\n" );
8515     else
8516         aContent.append( " f*\n" );
8517     m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() );
8518 
8519     OStringBuffer aObjName( 16 );
8520     aObjName.append( "Tr" );
8521     aObjName.append( m_aTransparentObjects.back().m_nObject );
8522     OString aTrName( aObjName.makeStringAndClear() );
8523     aObjName.append( "EGS" );
8524     aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8525     OString aExtName( aObjName.makeStringAndClear() );
8526 
8527     OStringBuffer aLine( 80 );
8528     // insert XObject
8529     aLine.append( "q /" );
8530     aLine.append( aExtName );
8531     aLine.append( " gs /" );
8532     aLine.append( aTrName );
8533     aLine.append( " Do Q\n" );
8534     writeBuffer( aLine.getStr(), aLine.getLength() );
8535 
8536     pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8537     pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8538 }
8539 
8540 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
8541 {
8542     if( nObject >= 0 )
8543     {
8544         switch( eKind )
8545         {
8546             case ResXObject:
8547                 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
8548                 if( ! m_aOutputStreams.empty() )
8549                     m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
8550                 break;
8551             case ResExtGState:
8552                 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
8553                 if( ! m_aOutputStreams.empty() )
8554                     m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
8555                 break;
8556             case ResShading:
8557                 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
8558                 if( ! m_aOutputStreams.empty() )
8559                     m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
8560                 break;
8561             case ResPattern:
8562                 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
8563                 if( ! m_aOutputStreams.empty() )
8564                     m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
8565                 break;
8566         }
8567     }
8568 }
8569 
8570 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect )
8571 {
8572     push( PUSH_ALL );
8573 
8574     // force reemitting clip region
8575     clearClipRegion();
8576     updateGraphicsState();
8577 
8578     m_aOutputStreams.push_front( StreamRedirect() );
8579     m_aOutputStreams.front().m_pStream = pStream;
8580     m_aOutputStreams.front().m_aMapMode = m_aMapMode;
8581 
8582     if( !rTargetRect.IsEmpty() )
8583     {
8584         m_aOutputStreams.front().m_aTargetRect =
8585             lcl_convert( m_aGraphicsStack.front().m_aMapMode,
8586                          m_aMapMode,
8587                          getReferenceDevice(),
8588                          rTargetRect );
8589         Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
8590         long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
8591         aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
8592         m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
8593     }
8594 
8595     // setup graphics state for independent object stream
8596 
8597     // force reemitting colors
8598     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8599     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8600 }
8601 
8602 Rectangle PDFWriterImpl::getRedirectTargetRect() const
8603 {
8604     return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect;
8605 }
8606 
8607 SvStream* PDFWriterImpl::endRedirect()
8608 {
8609     SvStream* pStream = NULL;
8610     if( ! m_aOutputStreams.empty() )
8611     {
8612         pStream		= m_aOutputStreams.front().m_pStream;
8613         m_aMapMode	= m_aOutputStreams.front().m_aMapMode;
8614         m_aOutputStreams.pop_front();
8615     }
8616 
8617     pop();
8618     // force reemitting colors and clip region
8619     clearClipRegion();
8620     m_aCurrentPDFState.m_bClipRegion = m_aGraphicsStack.front().m_bClipRegion;
8621     m_aCurrentPDFState.m_aClipRegion = m_aGraphicsStack.front().m_aClipRegion;
8622     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8623     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8624 
8625     updateGraphicsState();
8626 
8627     return pStream;
8628 }
8629 
8630 void PDFWriterImpl::beginTransparencyGroup()
8631 {
8632     updateGraphicsState();
8633     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8634         beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8635 }
8636 
8637 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
8638 {
8639     DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8640     nTransparentPercent = nTransparentPercent % 100;
8641 
8642     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8643     {
8644         // create XObject
8645         m_aTransparentObjects.push_back( TransparencyEmit() );
8646         m_aTransparentObjects.back().m_aBoundRect	= rBoundingBox;
8647         // convert rectangle to default user space
8648         m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8649         m_aTransparentObjects.back().m_nObject		= createObject();
8650         m_aTransparentObjects.back().m_fAlpha		= (double)(100-nTransparentPercent) / 100.0;
8651         // get XObject's content stream
8652         m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8653         m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8654 
8655         OStringBuffer aObjName( 16 );
8656         aObjName.append( "Tr" );
8657         aObjName.append( m_aTransparentObjects.back().m_nObject );
8658         OString aTrName( aObjName.makeStringAndClear() );
8659         aObjName.append( "EGS" );
8660         aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8661         OString aExtName( aObjName.makeStringAndClear() );
8662 
8663         OStringBuffer aLine( 80 );
8664         // insert XObject
8665         aLine.append( "q /" );
8666         aLine.append( aExtName );
8667         aLine.append( " gs /" );
8668         aLine.append( aTrName );
8669         aLine.append( " Do Q\n" );
8670         writeBuffer( aLine.getStr(), aLine.getLength() );
8671 
8672         pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8673         pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8674     }
8675 }
8676 
8677 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask )
8678 {
8679     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8680     {
8681         // create XObject
8682         m_aTransparentObjects.push_back( TransparencyEmit() );
8683         m_aTransparentObjects.back().m_aBoundRect	= rBoundingBox;
8684         // convert rectangle to default user space
8685         m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8686         m_aTransparentObjects.back().m_nObject		= createObject();
8687         m_aTransparentObjects.back().m_fAlpha		= 0.0;
8688         // get XObject's content stream
8689         m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8690         m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8691 
8692         // draw soft mask
8693         beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8694         drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask );
8695         m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect());
8696 
8697         OStringBuffer aObjName( 16 );
8698         aObjName.append( "Tr" );
8699         aObjName.append( m_aTransparentObjects.back().m_nObject );
8700         OString aTrName( aObjName.makeStringAndClear() );
8701         aObjName.append( "EGS" );
8702         aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8703         OString aExtName( aObjName.makeStringAndClear() );
8704 
8705         OStringBuffer aLine( 80 );
8706         // insert XObject
8707         aLine.append( "q /" );
8708         aLine.append( aExtName );
8709         aLine.append( " gs /" );
8710         aLine.append( aTrName );
8711         aLine.append( " Do Q\n" );
8712         writeBuffer( aLine.getStr(), aLine.getLength() );
8713 
8714         pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8715         pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8716     }
8717 }
8718 
8719 void PDFWriterImpl::drawRectangle( const Rectangle& rRect )
8720 {
8721     MARK( "drawRectangle" );
8722 
8723     updateGraphicsState();
8724 
8725     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8726         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8727         return;
8728 
8729     OStringBuffer aLine( 40 );
8730     m_aPages.back().appendRect( rRect, aLine );
8731 
8732     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8733         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8734         aLine.append( " B*\n" );
8735     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8736         aLine.append( " S\n" );
8737     else
8738         aLine.append( " f*\n" );
8739 
8740     writeBuffer( aLine.getStr(), aLine.getLength() );
8741 }
8742 
8743 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
8744 {
8745     MARK( "drawRectangle with rounded edges" );
8746 
8747     if( !nHorzRound && !nVertRound )
8748         drawRectangle( rRect );
8749 
8750     updateGraphicsState();
8751 
8752     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8753         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8754         return;
8755 
8756     if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
8757         nHorzRound = rRect.GetWidth()/2;
8758     if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
8759         nVertRound = rRect.GetHeight()/2;
8760 
8761     Point aPoints[16];
8762     const double kappa = 0.5522847498;
8763     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
8764     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
8765 
8766     aPoints[1]  = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
8767     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8768     aPoints[2]  = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
8769     aPoints[3]  = Point( aPoints[2].X()+kx, aPoints[2].Y() );
8770 
8771     aPoints[5]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
8772     aPoints[4]  = Point( aPoints[5].X(), aPoints[5].Y()-ky );
8773     aPoints[6]  = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
8774     aPoints[7]  = Point( aPoints[6].X(), aPoints[6].Y()+ky );
8775 
8776     aPoints[9]  = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
8777     aPoints[8]  = Point( aPoints[9].X()+kx, aPoints[9].Y() );
8778     aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
8779     aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
8780 
8781     aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
8782     aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
8783     aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
8784     aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
8785 
8786 
8787     OStringBuffer aLine( 80 );
8788     m_aPages.back().appendPoint( aPoints[1], aLine );
8789     aLine.append( " m " );
8790     m_aPages.back().appendPoint( aPoints[2], aLine );
8791     aLine.append( " l " );
8792     m_aPages.back().appendPoint( aPoints[3], aLine );
8793     aLine.append( ' ' );
8794     m_aPages.back().appendPoint( aPoints[4], aLine );
8795     aLine.append( ' ' );
8796     m_aPages.back().appendPoint( aPoints[5], aLine );
8797     aLine.append( " c\n" );
8798     m_aPages.back().appendPoint( aPoints[6], aLine );
8799     aLine.append( " l " );
8800     m_aPages.back().appendPoint( aPoints[7], aLine );
8801     aLine.append( ' ' );
8802     m_aPages.back().appendPoint( aPoints[8], aLine );
8803     aLine.append( ' ' );
8804     m_aPages.back().appendPoint( aPoints[9], aLine );
8805     aLine.append( " c\n" );
8806     m_aPages.back().appendPoint( aPoints[10], aLine );
8807     aLine.append( " l " );
8808     m_aPages.back().appendPoint( aPoints[11], aLine );
8809     aLine.append( ' ' );
8810     m_aPages.back().appendPoint( aPoints[12], aLine );
8811     aLine.append( ' ' );
8812     m_aPages.back().appendPoint( aPoints[13], aLine );
8813     aLine.append( " c\n" );
8814     m_aPages.back().appendPoint( aPoints[14], aLine );
8815     aLine.append( " l " );
8816     m_aPages.back().appendPoint( aPoints[15], aLine );
8817     aLine.append( ' ' );
8818     m_aPages.back().appendPoint( aPoints[0], aLine );
8819     aLine.append( ' ' );
8820     m_aPages.back().appendPoint( aPoints[1], aLine );
8821     aLine.append( " c " );
8822 
8823     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8824         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8825         aLine.append( "b*\n" );
8826     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8827         aLine.append( "s\n" );
8828     else
8829         aLine.append( "f*\n" );
8830 
8831     writeBuffer( aLine.getStr(), aLine.getLength() );
8832 }
8833 
8834 void PDFWriterImpl::drawEllipse( const Rectangle& rRect )
8835 {
8836     MARK( "drawEllipse" );
8837 
8838     updateGraphicsState();
8839 
8840     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8841         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8842         return;
8843 
8844     Point aPoints[12];
8845     const double kappa = 0.5522847498;
8846     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
8847     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
8848 
8849     aPoints[1]  = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
8850     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8851     aPoints[2]  = Point( aPoints[1].X() + kx, aPoints[1].Y() );
8852 
8853     aPoints[4]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
8854     aPoints[3]  = Point( aPoints[4].X(), aPoints[4].Y() - ky );
8855     aPoints[5]  = Point( aPoints[4].X(), aPoints[4].Y() + ky );
8856 
8857     aPoints[7]  = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
8858     aPoints[6]  = Point( aPoints[7].X() + kx, aPoints[7].Y() );
8859     aPoints[8]  = Point( aPoints[7].X() - kx, aPoints[7].Y() );
8860 
8861     aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
8862     aPoints[9]  = Point( aPoints[10].X(), aPoints[10].Y() + ky );
8863     aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
8864 
8865     OStringBuffer aLine( 80 );
8866     m_aPages.back().appendPoint( aPoints[1], aLine );
8867     aLine.append( " m " );
8868     m_aPages.back().appendPoint( aPoints[2], aLine );
8869     aLine.append( ' ' );
8870     m_aPages.back().appendPoint( aPoints[3], aLine );
8871     aLine.append( ' ' );
8872     m_aPages.back().appendPoint( aPoints[4], aLine );
8873     aLine.append( " c\n" );
8874     m_aPages.back().appendPoint( aPoints[5], aLine );
8875     aLine.append( ' ' );
8876     m_aPages.back().appendPoint( aPoints[6], aLine );
8877     aLine.append( ' ' );
8878     m_aPages.back().appendPoint( aPoints[7], aLine );
8879     aLine.append( " c\n" );
8880     m_aPages.back().appendPoint( aPoints[8], aLine );
8881     aLine.append( ' ' );
8882     m_aPages.back().appendPoint( aPoints[9], aLine );
8883     aLine.append( ' ' );
8884     m_aPages.back().appendPoint( aPoints[10], aLine );
8885     aLine.append( " c\n" );
8886     m_aPages.back().appendPoint( aPoints[11], aLine );
8887     aLine.append( ' ' );
8888     m_aPages.back().appendPoint( aPoints[0], aLine );
8889     aLine.append( ' ' );
8890     m_aPages.back().appendPoint( aPoints[1], aLine );
8891     aLine.append( " c " );
8892 
8893     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8894         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8895         aLine.append( "b*\n" );
8896     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8897         aLine.append( "s\n" );
8898     else
8899         aLine.append( "f*\n" );
8900 
8901     writeBuffer( aLine.getStr(), aLine.getLength() );
8902 }
8903 
8904 static double calcAngle( const Rectangle& rRect, const Point& rPoint )
8905 {
8906     Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
8907                   (rRect.Top()+rRect.Bottom()+1)/2);
8908     Point aPoint = rPoint - aOrigin;
8909 
8910     double fX = (double)aPoint.X();
8911     double fY = (double)-aPoint.Y();
8912 
8913     if( rRect.GetWidth() > rRect.GetHeight() )
8914         fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
8915     else if( rRect.GetHeight() > rRect.GetWidth() )
8916         fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
8917     return atan2( fY, fX );
8918 }
8919 
8920 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
8921 {
8922     MARK( "drawArc" );
8923 
8924     updateGraphicsState();
8925 
8926     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8927         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8928         return;
8929 
8930     // calculate start and stop angles
8931     const double fStartAngle = calcAngle( rRect, rStart );
8932     double fStopAngle  = calcAngle( rRect, rStop );
8933     while( fStopAngle < fStartAngle )
8934         fStopAngle += 2.0*M_PI;
8935     const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
8936     const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
8937     const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
8938     const double halfWidth = (double)rRect.GetWidth()/2.0;
8939     const double halfHeight = (double)rRect.GetHeight()/2.0;
8940 
8941     const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
8942                          (rRect.Top()+rRect.Bottom()+1)/2 );
8943 
8944     OStringBuffer aLine( 30*nFragments );
8945     Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
8946                   -(int)(halfHeight * sin(fStartAngle) ) );
8947     aPoint += aCenter;
8948     m_aPages.back().appendPoint( aPoint, aLine );
8949     aLine.append( " m " );
8950     if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
8951     {
8952         for( int i = 0; i < nFragments; i++ )
8953         {
8954             const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
8955             const double fStopFragment = fStartFragment + fFragmentDelta;
8956             aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
8957                             -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
8958             aPoint += aCenter;
8959             m_aPages.back().appendPoint( aPoint, aLine );
8960             aLine.append( ' ' );
8961 
8962             aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
8963                             -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
8964             aPoint += aCenter;
8965             m_aPages.back().appendPoint( aPoint, aLine );
8966             aLine.append( ' ' );
8967 
8968             aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
8969                             -(int)(halfHeight * sin(fStopFragment) ) );
8970             aPoint += aCenter;
8971             m_aPages.back().appendPoint( aPoint, aLine );
8972             aLine.append( " c\n" );
8973         }
8974     }
8975     if( bWithChord || bWithPie )
8976     {
8977         if( bWithPie )
8978         {
8979             m_aPages.back().appendPoint( aCenter, aLine );
8980             aLine.append( " l " );
8981         }
8982         aLine.append( "h " );
8983     }
8984     if( ! bWithChord && ! bWithPie )
8985         aLine.append( "S\n" );
8986     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8987         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8988         aLine.append( "B*\n" );
8989     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8990         aLine.append( "S\n" );
8991     else
8992         aLine.append( "f*\n" );
8993 
8994     writeBuffer( aLine.getStr(), aLine.getLength() );
8995 }
8996 
8997 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly )
8998 {
8999     MARK( "drawPolyLine" );
9000 
9001     sal_uInt16 nPoints = rPoly.GetSize();
9002     if( nPoints < 2 )
9003         return;
9004 
9005     updateGraphicsState();
9006 
9007     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9008         return;
9009 
9010     OStringBuffer aLine( 20 * nPoints );
9011     m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
9012     aLine.append( "S\n" );
9013 
9014     writeBuffer( aLine.getStr(), aLine.getLength() );
9015 }
9016 
9017 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo )
9018 {
9019     MARK( "drawPolyLine with LineInfo" );
9020 
9021     updateGraphicsState();
9022 
9023     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9024         return;
9025 
9026     OStringBuffer aLine;
9027     aLine.append( "q " );
9028     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
9029     {
9030         writeBuffer( aLine.getStr(), aLine.getLength() );
9031         drawPolyLine( rPoly );
9032         writeBuffer( "Q\n", 2 );
9033     }
9034     else
9035     {
9036         PDFWriter::ExtLineInfo aInfo;
9037         convertLineInfoToExtLineInfo( rInfo, aInfo );
9038         drawPolyLine( rPoly, aInfo );
9039     }
9040 }
9041 
9042 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
9043 {
9044     DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" );
9045     rOut.m_fLineWidth           = rIn.GetWidth();
9046     rOut.m_fTransparency        = 0.0;
9047     rOut.m_eCap                 = PDFWriter::capButt;
9048     rOut.m_eJoin                = PDFWriter::joinMiter;
9049     rOut.m_fMiterLimit          = 10;
9050     rOut.m_aDashArray.clear();
9051 
9052     int nDashes     = rIn.GetDashCount();
9053     int nDashLen    = rIn.GetDashLen();
9054     int nDistance   = rIn.GetDistance();
9055     for( int n  = 0; n < nDashes; n++ )
9056     {
9057         rOut.m_aDashArray.push_back( nDashLen );
9058         rOut.m_aDashArray.push_back( nDistance );
9059     }
9060     int nDots       = rIn.GetDotCount();
9061     int nDotLen     = rIn.GetDotLen();
9062     for( int n  = 0; n < nDots; n++ )
9063     {
9064         rOut.m_aDashArray.push_back( nDotLen );
9065         rOut.m_aDashArray.push_back( nDistance );
9066     }
9067 }
9068 
9069 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
9070 {
9071     MARK( "drawPolyLine with ExtLineInfo" );
9072 
9073     updateGraphicsState();
9074 
9075     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9076         return;
9077 
9078     if( rInfo.m_fTransparency >= 1.0 )
9079         return;
9080 
9081     if( rInfo.m_fTransparency != 0.0 )
9082         beginTransparencyGroup();
9083 
9084     OStringBuffer aLine;
9085     aLine.append( "q " );
9086     m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
9087     aLine.append( " w" );
9088     if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader
9089     {
9090         switch( rInfo.m_eCap )
9091         {
9092             default:
9093             case PDFWriter::capButt:   aLine.append( " 0 J" );break;
9094             case PDFWriter::capRound:  aLine.append( " 1 J" );break;
9095             case PDFWriter::capSquare: aLine.append( " 2 J" );break;
9096         }
9097         switch( rInfo.m_eJoin )
9098         {
9099             default:
9100             case PDFWriter::joinMiter:
9101             {
9102                 double fLimit = rInfo.m_fMiterLimit;
9103                 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
9104                     fLimit = fLimit / rInfo.m_fLineWidth;
9105                 if( fLimit < 1.0 )
9106                     fLimit = 1.0;
9107                 aLine.append( " 0 j " );
9108                 appendDouble( fLimit, aLine );
9109                 aLine.append( " M" );
9110             }
9111             break;
9112             case PDFWriter::joinRound:  aLine.append( " 1 j" );break;
9113             case PDFWriter::joinBevel:  aLine.append( " 2 j" );break;
9114         }
9115         if( rInfo.m_aDashArray.size() > 0 )
9116         {
9117             aLine.append( " [ " );
9118             for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
9119                  it != rInfo.m_aDashArray.end(); ++it )
9120             {
9121                 m_aPages.back().appendMappedLength( *it, aLine );
9122                 aLine.append( ' ' );
9123             }
9124             aLine.append( "] 0 d" );
9125         }
9126         aLine.append( "\n" );
9127         writeBuffer( aLine.getStr(), aLine.getLength() );
9128         drawPolyLine( rPoly );
9129     }
9130     else
9131     {
9132         basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
9133         basegfx::B2DPolyPolygon aPolyPoly;
9134 
9135 		basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
9136 
9137 		// Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
9138 		// To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
9139 		// this line needs to be removed and the loop below adapted accordingly
9140 		aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
9141 
9142 		const sal_uInt32 nPolygonCount(aPolyPoly.count());
9143 
9144 		for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
9145         {
9146             aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
9147             aPoly = aPolyPoly.getB2DPolygon( nPoly );
9148 			const sal_uInt32 nPointCount(aPoly.count());
9149 
9150 			if(nPointCount)
9151 			{
9152 				const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
9153 				basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
9154 
9155 				for(sal_uInt32 a(0); a < nEdgeCount; a++)
9156 				{
9157                     if( a > 0 )
9158                         aLine.append( " " );
9159 					const sal_uInt32 nNextIndex((a + 1) % nPointCount);
9160 					const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
9161 
9162 					m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
9163 														FRound(aCurrent.getY()) ),
9164 												 aLine );
9165 					aLine.append( " m " );
9166 					m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
9167 														FRound(aNext.getY()) ),
9168 												 aLine );
9169 					aLine.append( " l" );
9170 
9171 					// prepare next edge
9172 					aCurrent = aNext;
9173 				}
9174 			}
9175         }
9176         aLine.append( " S " );
9177         writeBuffer( aLine.getStr(), aLine.getLength() );
9178     }
9179     writeBuffer( "Q\n", 2 );
9180 
9181     if( rInfo.m_fTransparency != 0.0 )
9182     {
9183         // FIXME: actually this may be incorrect with bezier polygons
9184         Rectangle aBoundRect( rPoly.GetBoundRect() );
9185         // avoid clipping with thick lines
9186         if( rInfo.m_fLineWidth > 0.0 )
9187         {
9188             sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
9189             aBoundRect.Top()    -= nLW;
9190             aBoundRect.Left()   -= nLW;
9191             aBoundRect.Right()  += nLW;
9192             aBoundRect.Bottom() += nLW;
9193         }
9194         endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) );
9195     }
9196 }
9197 
9198 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
9199 {
9200     MARK( "drawPixel" );
9201 
9202     Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
9203 
9204     if( aColor == Color( COL_TRANSPARENT ) )
9205         return;
9206 
9207     // pixels are drawn in line color, so have to set
9208     // the nonstroking color to line color
9209     Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
9210     setFillColor( aColor );
9211 
9212     updateGraphicsState();
9213 
9214     OStringBuffer aLine( 20 );
9215     m_aPages.back().appendPoint( rPoint, aLine );
9216     aLine.append( ' ' );
9217     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine );
9218     aLine.append( ' ' );
9219     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine );
9220     aLine.append( " re f\n" );
9221     writeBuffer( aLine.getStr(), aLine.getLength() );
9222 
9223     setFillColor( aOldFillColor );
9224 }
9225 
9226 void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors )
9227 {
9228     MARK( "drawPixel with Polygon" );
9229 
9230     updateGraphicsState();
9231 
9232     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors )
9233         return;
9234 
9235     sal_uInt16 nPoints = rPoints.GetSize();
9236     OStringBuffer aLine( nPoints*40 );
9237     aLine.append( "q " );
9238     if( ! pColors )
9239     {
9240         appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine );
9241         aLine.append( ' ' );
9242     }
9243 
9244     OStringBuffer aPixel(32);
9245     aPixel.append( ' ' );
9246     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel );
9247     aPixel.append( ' ' );
9248     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel );
9249     OString aPixelStr = aPixel.makeStringAndClear();
9250     for( sal_uInt16 i = 0; i < nPoints; i++ )
9251     {
9252         if( pColors )
9253         {
9254             if( pColors[i] == Color( COL_TRANSPARENT ) )
9255                 continue;
9256 
9257             appendNonStrokingColor( pColors[i], aLine );
9258             aLine.append( ' ' );
9259         }
9260         m_aPages.back().appendPoint( rPoints[i], aLine );
9261         aLine.append( aPixelStr );
9262         aLine.append( " re f\n" );
9263     }
9264     aLine.append( "Q\n" );
9265     writeBuffer( aLine.getStr(), aLine.getLength() );
9266 }
9267 
9268 class AccessReleaser
9269 {
9270     BitmapReadAccess* m_pAccess;
9271 public:
9272     AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){}
9273     ~AccessReleaser() { delete m_pAccess; }
9274 };
9275 
9276 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
9277 {
9278     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9279 
9280     bool bFlateFilter = compressStream( rObject.m_pContentStream );
9281     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
9282     sal_uLong nSize = rObject.m_pContentStream->Tell();
9283     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
9284     #if OSL_DEBUG_LEVEL > 1
9285     emitComment( "PDFWriterImpl::writeTransparentObject" );
9286     #endif
9287     OStringBuffer aLine( 512 );
9288     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9289     aLine.append( rObject.m_nObject );
9290     aLine.append( " 0 obj\n"
9291                   "<</Type/XObject\n"
9292                   "/Subtype/Form\n"
9293                   "/BBox[ " );
9294     appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
9295     aLine.append( ' ' );
9296     appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
9297     aLine.append( ' ' );
9298     appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
9299     aLine.append( ' ' );
9300     appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
9301     aLine.append( " ]\n" );
9302     if( ! rObject.m_pSoftMaskStream )
9303     {
9304         if( ! m_bIsPDF_A1 )
9305         {
9306             aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
9307         }
9308     }
9309     /* #i42884# the PDF reference recommends that each Form XObject
9310     *  should have a resource dict; alas if that is the same object
9311     *  as the one of the page it triggers an endless recursion in
9312     *  acroread 5 (6 and up have that fixed). Since we have only one
9313     *  resource dict anyway, let's use the one from the page by NOT
9314     *  emitting a Resources entry.
9315     */
9316     #if 0
9317     aLine.append( "   /Resources " );
9318     aLine.append( getResourceDictObj() );
9319     aLine.append( " 0 R\n" );
9320     #endif
9321 
9322     aLine.append( "/Length " );
9323     aLine.append( (sal_Int32)(nSize) );
9324     aLine.append( "\n" );
9325     if( bFlateFilter )
9326         aLine.append( "/Filter/FlateDecode\n" );
9327     aLine.append( ">>\n"
9328                   "stream\n" );
9329     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9330     checkAndEnableStreamEncryption( rObject.m_nObject );
9331     CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
9332     disableStreamEncryption();
9333     aLine.setLength( 0 );
9334     aLine.append( "\n"
9335                   "endstream\n"
9336                   "endobj\n\n" );
9337     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9338 
9339     // write ExtGState dict for this XObject
9340     aLine.setLength( 0 );
9341     aLine.append( rObject.m_nExtGStateObject );
9342     aLine.append( " 0 obj\n"
9343                   "<<" );
9344     if( ! rObject.m_pSoftMaskStream )
9345     {
9346 //i59651
9347         if( m_bIsPDF_A1 )
9348         {
9349             aLine.append( "/CA 1.0/ca 1.0" );
9350             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9351         }
9352         else
9353         {
9354             aLine.append(  "/CA " );
9355             appendDouble( rObject.m_fAlpha, aLine );
9356             aLine.append( "\n"
9357                           "   /ca " );
9358             appendDouble( rObject.m_fAlpha, aLine );
9359         }
9360         aLine.append( "\n" );
9361     }
9362     else
9363     {
9364         if( m_bIsPDF_A1 )
9365         {
9366             aLine.append( "/SMask/None" );
9367             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9368         }
9369         else
9370         {
9371             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
9372             sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
9373             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
9374             sal_Int32 nMaskObject = createObject();
9375             aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
9376             aLine.append( nMaskObject );
9377             aLine.append( " 0 R>>\n" );
9378 
9379             OStringBuffer aMask;
9380             aMask.append( nMaskObject );
9381             aMask.append( " 0 obj\n"
9382                           "<</Type/XObject\n"
9383                           "/Subtype/Form\n"
9384                           "/BBox[" );
9385             appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
9386             aMask.append( ' ' );
9387             appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
9388             aMask.append( ' ' );
9389             appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
9390             aMask.append( ' ' );
9391             appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
9392             aMask.append( "]\n" );
9393 
9394             /* #i42884# see above */
9395 #if 0
9396             aLine.append( "/Resources " );
9397             aMask.append( getResourceDictObj() );
9398             aMask.append( " 0 R\n" );
9399 #endif
9400 
9401             aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
9402             aMask.append( "/Length " );
9403             aMask.append( nMaskSize );
9404             aMask.append( ">>\n"
9405                           "stream\n" );
9406             CHECK_RETURN( updateObject( nMaskObject ) );
9407             checkAndEnableStreamEncryption(  nMaskObject );
9408             CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9409             CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
9410             disableStreamEncryption();
9411             aMask.setLength( 0 );
9412             aMask.append( "\nendstream\n"
9413                           "endobj\n\n" );
9414             CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9415         }
9416     }
9417     aLine.append( ">>\n"
9418                   "endobj\n\n" );
9419     CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) );
9420     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9421 
9422     return true;
9423 }
9424 
9425 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
9426 {
9427     sal_Int32 nFunctionObject = createObject();
9428     CHECK_RETURN( updateObject( nFunctionObject ) );
9429 
9430     VirtualDevice aDev;
9431     aDev.SetOutputSizePixel( rObject.m_aSize );
9432     aDev.SetMapMode( MapMode( MAP_PIXEL ) );
9433     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9434         aDev.SetDrawMode( aDev.GetDrawMode() |
9435                           ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT |
9436                             DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) );
9437     aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
9438 
9439     Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize );
9440     BitmapReadAccess* pAccess = aSample.AcquireReadAccess();
9441     AccessReleaser aReleaser( pAccess );
9442 
9443     Size aSize = aSample.GetSizePixel();
9444 
9445     sal_Int32 nStreamLengthObject = createObject();
9446     #if OSL_DEBUG_LEVEL > 1
9447     emitComment( "PDFWriterImpl::writeGradientFunction" );
9448     #endif
9449     OStringBuffer aLine( 120 );
9450     aLine.append( nFunctionObject );
9451     aLine.append( " 0 obj\n"
9452                   "<</FunctionType 0\n"
9453                   "/Domain[ 0 1 0 1 ]\n"
9454                   "/Size[ " );
9455     aLine.append( (sal_Int32)aSize.Width() );
9456     aLine.append( ' ' );
9457     aLine.append( (sal_Int32)aSize.Height() );
9458     aLine.append( " ]\n"
9459                   "/BitsPerSample 8\n"
9460                   "/Range[ 0 1 0 1 0 1 ]\n"
9461                   "/Order 3\n"
9462                   "/Length " );
9463     aLine.append( nStreamLengthObject );
9464     aLine.append( " 0 R\n"
9465 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9466                   "/Filter/FlateDecode"
9467 #endif
9468                   ">>\n"
9469                   "stream\n" );
9470     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9471 
9472     sal_uInt64 nStartStreamPos = 0;
9473     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) );
9474 
9475     checkAndEnableStreamEncryption( nFunctionObject );
9476     beginCompression();
9477     for( int y = aSize.Height()-1; y >= 0; y-- )
9478     {
9479         for( int x = 0; x < aSize.Width(); x++ )
9480         {
9481             sal_uInt8 aCol[3];
9482             BitmapColor aColor = pAccess->GetColor( y, x );
9483             aCol[0] = aColor.GetRed();
9484             aCol[1] = aColor.GetGreen();
9485             aCol[2] = aColor.GetBlue();
9486             CHECK_RETURN( writeBuffer( aCol, 3 ) );
9487         }
9488     }
9489     endCompression();
9490     disableStreamEncryption();
9491 
9492     sal_uInt64 nEndStreamPos = 0;
9493     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) );
9494 
9495     aLine.setLength( 0 );
9496     aLine.append( "\nendstream\nendobj\n\n" );
9497     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9498 
9499     // write stream length
9500     CHECK_RETURN( updateObject( nStreamLengthObject ) );
9501     aLine.setLength( 0 );
9502     aLine.append( nStreamLengthObject );
9503     aLine.append( " 0 obj\n" );
9504     aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
9505     aLine.append( "\nendobj\n\n" );
9506     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9507 
9508     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9509     aLine.setLength( 0 );
9510     aLine.append( rObject.m_nObject );
9511     aLine.append( " 0 obj\n"
9512                   "<</ShadingType 1\n"
9513                   "/ColorSpace/DeviceRGB\n"
9514                   "/AntiAlias true\n"
9515                   "/Domain[ 0 1 0 1 ]\n"
9516                   "/Matrix[ " );
9517     aLine.append( (sal_Int32)aSize.Width() );
9518     aLine.append( " 0 0 " );
9519     aLine.append( (sal_Int32)aSize.Height() );
9520     aLine.append( " 0 0 ]\n"
9521                   "/Function " );
9522     aLine.append( nFunctionObject );
9523     aLine.append( " 0 R\n"
9524                   ">>\n"
9525                   "endobj\n\n" );
9526     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9527 
9528     return true;
9529 }
9530 
9531 bool PDFWriterImpl::writeJPG( JPGEmit& rObject )
9532 {
9533     CHECK_RETURN( rObject.m_pStream );
9534     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9535 
9536     sal_Int32 nLength = 0;
9537     rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
9538     nLength = rObject.m_pStream->Tell();
9539     rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
9540 
9541     sal_Int32 nMaskObject = 0;
9542     if( !!rObject.m_aMask )
9543     {
9544         if( rObject.m_aMask.GetBitCount() == 1 ||
9545             ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651
9546             )
9547         {
9548             nMaskObject = createObject();
9549         }
9550         else if( m_bIsPDF_A1 )
9551             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9552         else if( m_aContext.Version < PDFWriter::PDF_1_4 )
9553             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
9554 
9555     }
9556     #if OSL_DEBUG_LEVEL > 1
9557     emitComment( "PDFWriterImpl::writeJPG" );
9558     #endif
9559 
9560     OStringBuffer aLine(200);
9561     aLine.append( rObject.m_nObject );
9562     aLine.append( " 0 obj\n"
9563                   "<</Type/XObject/Subtype/Image/Width " );
9564     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
9565     aLine.append( " /Height " );
9566     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
9567     aLine.append( " /BitsPerComponent 8 " );
9568     if( rObject.m_bTrueColor )
9569         aLine.append( "/ColorSpace/DeviceRGB" );
9570     else
9571         aLine.append( "/ColorSpace/DeviceGray" );
9572     aLine.append( "/Filter/DCTDecode/Length " );
9573     aLine.append( nLength );
9574     if( nMaskObject )
9575     {
9576         aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
9577         aLine.append( nMaskObject );
9578         aLine.append( " 0 R " );
9579     }
9580     aLine.append( ">>\nstream\n" );
9581     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9582 
9583     checkAndEnableStreamEncryption( rObject.m_nObject );
9584     CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
9585     disableStreamEncryption();
9586 
9587     aLine.setLength( 0 );
9588     aLine.append( "\nendstream\nendobj\n\n" );
9589     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9590 
9591     if( nMaskObject )
9592     {
9593         BitmapEmit aEmit;
9594         aEmit.m_nObject = nMaskObject;
9595         if( rObject.m_aMask.GetBitCount() == 1 )
9596             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
9597         else if( rObject.m_aMask.GetBitCount() == 8 )
9598             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
9599         writeBitmapObject( aEmit, true );
9600     }
9601 
9602     return true;
9603 }
9604 
9605 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
9606 {
9607     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9608 
9609     Bitmap	aBitmap;
9610     Color	aTransparentColor( COL_TRANSPARENT );
9611     bool	bWriteMask = false;
9612     if( ! bMask )
9613     {
9614         aBitmap = rObject.m_aBitmap.GetBitmap();
9615         if( rObject.m_aBitmap.IsAlpha() )
9616         {
9617             if( m_aContext.Version >= PDFWriter::PDF_1_4 )
9618                 bWriteMask = true;
9619             // else draw without alpha channel
9620         }
9621         else
9622         {
9623             switch( rObject.m_aBitmap.GetTransparentType() )
9624             {
9625                 case TRANSPARENT_NONE:
9626                     // comes from drawMask function
9627                     if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask )
9628                         bMask = true;
9629                     break;
9630                 case TRANSPARENT_COLOR:
9631                     aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
9632                     break;
9633                 case TRANSPARENT_BITMAP:
9634                     bWriteMask = true;
9635                     break;
9636             }
9637         }
9638     }
9639     else
9640     {
9641         if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
9642         {
9643             aBitmap = rObject.m_aBitmap.GetMask();
9644             aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
9645             DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
9646         }
9647         else if( aBitmap.GetBitCount() != 8 )
9648         {
9649             aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap();
9650             aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
9651             DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" );
9652         }
9653     }
9654 
9655     BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();
9656     AccessReleaser aReleaser( pAccess );
9657 
9658     bool bTrueColor;
9659     sal_Int32 nBitsPerComponent;
9660     switch( aBitmap.GetBitCount() )
9661     {
9662         case 1:
9663         case 2:
9664         case 4:
9665         case 8:
9666             bTrueColor = false;
9667             nBitsPerComponent = aBitmap.GetBitCount();
9668             break;
9669         default:
9670             bTrueColor = true;
9671             nBitsPerComponent = 8;
9672             break;
9673     }
9674 
9675     sal_Int32 nStreamLengthObject	= createObject();
9676     sal_Int32 nMaskObject			= 0;
9677 
9678     #if OSL_DEBUG_LEVEL > 1
9679     emitComment( "PDFWriterImpl::writeBitmapObject" );
9680     #endif
9681     OStringBuffer aLine(1024);
9682     aLine.append( rObject.m_nObject );
9683     aLine.append( " 0 obj\n"
9684                   "<</Type/XObject/Subtype/Image/Width " );
9685     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9686     aLine.append( "/Height " );
9687     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
9688     aLine.append( "/BitsPerComponent " );
9689     aLine.append( nBitsPerComponent );
9690     aLine.append( "/Length " );
9691     aLine.append( nStreamLengthObject );
9692     aLine.append( " 0 R\n" );
9693 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9694     if( nBitsPerComponent != 1 )
9695     {
9696         aLine.append( "/Filter/FlateDecode" );
9697     }
9698     else
9699     {
9700         aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
9701         aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9702         aLine.append( ">>\n" );
9703     }
9704 #endif
9705     if( ! bMask )
9706     {
9707         aLine.append( "/ColorSpace" );
9708         if( bTrueColor )
9709             aLine.append( "/DeviceRGB\n" );
9710         else if( aBitmap.HasGreyPalette() )
9711         {
9712             aLine.append( "/DeviceGray\n" );
9713             if( aBitmap.GetBitCount() == 1 )
9714             {
9715                 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
9716                 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9717                 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9718                 if( nBlackIndex == 1 )
9719                     aLine.append( "/Decode[1 0]\n" );
9720             }
9721         }
9722         else
9723         {
9724             aLine.append( "[ /Indexed/DeviceRGB " );
9725             aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
9726             aLine.append( "\n<" );
9727 			if( m_aContext.Encryption.Encrypt() )
9728 			{
9729 				enableStringEncryption( rObject.m_nObject );
9730 				//check encryption buffer size
9731 				if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
9732 				{
9733 					int	nChar = 0;
9734 					//fill the encryption buffer
9735 					for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9736 					{
9737 						const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9738 						m_pEncryptionBuffer[nChar++] = rColor.GetRed();
9739 						m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
9740 						m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
9741 					}
9742 					//encrypt the colorspace lookup table
9743 					rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
9744 					//now queue the data for output
9745                     nChar = 0;
9746 					for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9747 					{
9748 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9749 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9750 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9751 					}
9752 				}
9753 			}
9754 			else //no encryption requested (PDF/A-1a program flow drops here)
9755 			{
9756 				for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9757 				{
9758 					const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9759 					appendHex( rColor.GetRed(), aLine );
9760 					appendHex( rColor.GetGreen(), aLine );
9761 					appendHex( rColor.GetBlue(), aLine );
9762 				}
9763 			}
9764             aLine.append( ">\n]\n" );
9765         }
9766     }
9767     else
9768     {
9769         if( aBitmap.GetBitCount() == 1 )
9770         {
9771             aLine.append( "/ImageMask true\n" );
9772             sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9773             DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9774             if( nBlackIndex )
9775                 aLine.append( "/Decode[ 1 0 ]\n" );
9776             else
9777                 aLine.append( "/Decode[ 0 1 ]\n" );
9778         }
9779         else if( aBitmap.GetBitCount() == 8 )
9780         {
9781             aLine.append( "/ColorSpace/DeviceGray\n"
9782                           "/Decode [ 1 0 ]\n" );
9783         }
9784     }
9785 
9786     if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651
9787     {
9788         if( bWriteMask )
9789         {
9790             nMaskObject = createObject();
9791             if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 )
9792                 aLine.append( "/SMask " );
9793             else
9794                 aLine.append( "/Mask " );
9795             aLine.append( nMaskObject );
9796             aLine.append( " 0 R\n" );
9797         }
9798         else if( aTransparentColor != Color( COL_TRANSPARENT ) )
9799         {
9800             aLine.append( "/Mask[ " );
9801             if( bTrueColor )
9802             {
9803                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9804                 aLine.append( ' ' );
9805                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9806                 aLine.append( ' ' );
9807                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9808                 aLine.append( ' ' );
9809                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9810                 aLine.append( ' ' );
9811                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9812                 aLine.append( ' ' );
9813                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9814             }
9815             else
9816             {
9817                 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
9818                 aLine.append( nIndex );
9819             }
9820             aLine.append( " ]\n" );
9821         }
9822     }
9823     else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) )
9824         m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9825 
9826     aLine.append( ">>\n"
9827                   "stream\n" );
9828     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9829     sal_uInt64 nStartPos = 0;
9830     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) );
9831 
9832     checkAndEnableStreamEncryption( rObject.m_nObject );
9833 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9834     if( nBitsPerComponent == 1 )
9835     {
9836         writeG4Stream( pAccess );
9837     }
9838     else
9839 #endif
9840     {
9841         beginCompression();
9842         if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
9843         {
9844             const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U );
9845 
9846             for( int i = 0; i < pAccess->Height(); i++ )
9847             {
9848                 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
9849             }
9850         }
9851         else
9852         {
9853             const int nScanLineBytes = pAccess->Width()*3;
9854             boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] );
9855             for( int y = 0; y < pAccess->Height(); y++ )
9856             {
9857                 for( int x = 0; x < pAccess->Width(); x++ )
9858                 {
9859                     BitmapColor aColor = pAccess->GetColor( y, x );
9860                     pCol[3*x+0] = aColor.GetRed();
9861                     pCol[3*x+1] = aColor.GetGreen();
9862                     pCol[3*x+2] = aColor.GetBlue();
9863                 }
9864                 CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) );
9865             }
9866         }
9867         endCompression();
9868     }
9869     disableStreamEncryption();
9870 
9871     sal_uInt64 nEndPos = 0;
9872     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) );
9873     aLine.setLength( 0 );
9874     aLine.append( "\nendstream\nendobj\n\n" );
9875     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9876     CHECK_RETURN( updateObject( nStreamLengthObject ) );
9877     aLine.setLength( 0 );
9878     aLine.append( nStreamLengthObject );
9879     aLine.append( " 0 obj\n" );
9880     aLine.append( (sal_Int64)(nEndPos-nStartPos) );
9881     aLine.append( "\nendobj\n\n" );
9882     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9883 
9884     if( nMaskObject )
9885     {
9886         BitmapEmit aEmit;
9887         aEmit.m_nObject				= nMaskObject;
9888         aEmit.m_aBitmap				= rObject.m_aBitmap;
9889         return writeBitmapObject( aEmit, true );
9890     }
9891 
9892     return true;
9893 }
9894 
9895 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask )
9896 {
9897     MARK( "drawJPGBitmap" );
9898 
9899     OStringBuffer aLine( 80 );
9900     updateGraphicsState();
9901 
9902     // #i40055# sanity check
9903     if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
9904         return;
9905     if( ! (rSizePixel.Width() && rSizePixel.Height()) )
9906         return;
9907 
9908     rDCTData.Seek( 0 );
9909     if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9910     {
9911         // need to convert to grayscale;
9912         // load stream to bitmap and draw the bitmap instead
9913         Graphic aGraphic;
9914         GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG );
9915         Bitmap aBmp( aGraphic.GetBitmap() );
9916         if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() )
9917         {
9918             BitmapEx aBmpEx( aBmp, rMask );
9919             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx );
9920         }
9921         else
9922             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp );
9923         return;
9924     }
9925 
9926     SvMemoryStream* pStream = new SvMemoryStream;
9927     *pStream << rDCTData;
9928     pStream->Seek( STREAM_SEEK_TO_END );
9929 
9930     BitmapID aID;
9931     aID.m_aPixelSize	= rSizePixel;
9932     aID.m_nSize			= pStream->Tell();
9933     pStream->Seek( STREAM_SEEK_TO_BEGIN );
9934     aID.m_nChecksum		= rtl_crc32( 0, pStream->GetData(), aID.m_nSize );
9935     if( ! rMask.IsEmpty() )
9936         aID.m_nMaskChecksum	= rMask.GetChecksum();
9937 
9938     std::list< JPGEmit >::const_iterator it;
9939     for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
9940         ;
9941     if( it == m_aJPGs.end() )
9942     {
9943         m_aJPGs.push_front( JPGEmit() );
9944         JPGEmit& rEmit = m_aJPGs.front();
9945         rEmit.m_nObject		= createObject();
9946         rEmit.m_aID			= aID;
9947         rEmit.m_pStream		= pStream;
9948         rEmit.m_bTrueColor  = bIsTrueColor;
9949         if( !! rMask && rMask.GetSizePixel() == rSizePixel )
9950             rEmit.m_aMask	= rMask;
9951 
9952         it = m_aJPGs.begin();
9953     }
9954     else
9955         delete pStream;
9956 
9957     aLine.append( "q " );
9958     sal_Int32 nCheckWidth = 0;
9959     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth );
9960     aLine.append( " 0 0 " );
9961     sal_Int32 nCheckHeight = 0;
9962     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight );
9963     aLine.append( ' ' );
9964     m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
9965     aLine.append( " cm\n/Im" );
9966     aLine.append( it->m_nObject );
9967     aLine.append( " Do Q\n" );
9968     if( nCheckWidth == 0 || nCheckHeight == 0 )
9969     {
9970         // #i97512# avoid invalid current matrix
9971         aLine.setLength( 0 );
9972         aLine.append( "\n%jpeg image /Im" );
9973         aLine.append( it->m_nObject );
9974         aLine.append( " scaled to zero size, omitted\n" );
9975     }
9976     writeBuffer( aLine.getStr(), aLine.getLength() );
9977 
9978     OStringBuffer aObjName( 16 );
9979     aObjName.append( "Im" );
9980     aObjName.append( it->m_nObject );
9981     pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
9982 
9983 }
9984 
9985 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
9986 {
9987     OStringBuffer aLine( 80 );
9988     updateGraphicsState();
9989 
9990     aLine.append( "q " );
9991     if( rFillColor != Color( COL_TRANSPARENT ) )
9992     {
9993         appendNonStrokingColor( rFillColor, aLine );
9994         aLine.append( ' ' );
9995     }
9996     sal_Int32 nCheckWidth = 0;
9997     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth );
9998     aLine.append( " 0 0 " );
9999     sal_Int32 nCheckHeight = 0;
10000     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight );
10001     aLine.append( ' ' );
10002     m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
10003     aLine.append( " cm\n/Im" );
10004     aLine.append( rBitmap.m_nObject );
10005     aLine.append( " Do Q\n" );
10006     if( nCheckWidth == 0 || nCheckHeight == 0 )
10007     {
10008         // #i97512# avoid invalid current matrix
10009         aLine.setLength( 0 );
10010         aLine.append( "\n%bitmap image /Im" );
10011         aLine.append( rBitmap.m_nObject );
10012         aLine.append( " scaled to zero size, omitted\n" );
10013     }
10014     writeBuffer( aLine.getStr(), aLine.getLength() );
10015 }
10016 
10017 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask )
10018 {
10019     BitmapEx aBitmap( i_rBitmap );
10020     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
10021     {
10022         BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS;
10023         int nDepth = aBitmap.GetBitmap().GetBitCount();
10024         if( nDepth <= 4 )
10025             eConv = BMP_CONVERSION_4BIT_GREYS;
10026         if( nDepth > 1 )
10027             aBitmap.Convert( eConv );
10028     }
10029     BitmapID aID;
10030     aID.m_aPixelSize		= aBitmap.GetSizePixel();
10031     aID.m_nSize				= aBitmap.GetBitCount();
10032     aID.m_nChecksum			= aBitmap.GetBitmap().GetChecksum();
10033     aID.m_nMaskChecksum		= 0;
10034     if( aBitmap.IsAlpha() )
10035         aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum();
10036     else
10037     {
10038         Bitmap aMask = aBitmap.GetMask();
10039         if( ! aMask.IsEmpty() )
10040             aID.m_nMaskChecksum = aMask.GetChecksum();
10041     }
10042     std::list< BitmapEmit >::const_iterator it;
10043     for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
10044     {
10045         if( aID == it->m_aID )
10046             break;
10047     }
10048     if( it == m_aBitmaps.end() )
10049     {
10050         m_aBitmaps.push_front( BitmapEmit() );
10051         m_aBitmaps.front().m_aID		= aID;
10052         m_aBitmaps.front().m_aBitmap	= aBitmap;
10053         m_aBitmaps.front().m_nObject	= createObject();
10054         m_aBitmaps.front().m_bDrawMask	= bDrawMask;
10055         it = m_aBitmaps.begin();
10056     }
10057 
10058     OStringBuffer aObjName( 16 );
10059     aObjName.append( "Im" );
10060     aObjName.append( it->m_nObject );
10061     pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10062 
10063     return *it;
10064 }
10065 
10066 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap )
10067 {
10068     MARK( "drawBitmap (Bitmap)" );
10069 
10070     // #i40055# sanity check
10071     if( ! (rDestSize.Width() && rDestSize.Height()) )
10072         return;
10073 
10074     const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) );
10075     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10076 }
10077 
10078 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
10079 {
10080     MARK( "drawBitmap (BitmapEx)" );
10081 
10082     // #i40055# sanity check
10083     if( ! (rDestSize.Width() && rDestSize.Height()) )
10084         return;
10085 
10086     const BitmapEmit& rEmit = createBitmapEmit( rBitmap );
10087     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10088 }
10089 
10090 void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor )
10091 {
10092     MARK( "drawMask" );
10093 
10094     // #i40055# sanity check
10095     if( ! (rDestSize.Width() && rDestSize.Height()) )
10096         return;
10097 
10098     Bitmap aBitmap( rBitmap );
10099     if( aBitmap.GetBitCount() > 1 )
10100         aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
10101     DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
10102 
10103     const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true );
10104     drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor );
10105 }
10106 
10107 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
10108 {
10109     Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10110                                MapMode( MAP_POINT ),
10111                                getReferenceDevice(),
10112                                rSize ) );
10113     // check if we already have this gradient
10114     std::list<GradientEmit>::iterator it;
10115     // rounding to point will generally lose some pixels
10116     // round up to point boundary
10117     aPtSize.Width()++;
10118     aPtSize.Height()++;
10119     for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
10120     {
10121         if( it->m_aGradient == rGradient )
10122         {
10123             if( it->m_aSize == aPtSize )
10124                 break;
10125         }
10126     }
10127     if( it == m_aGradients.end() )
10128     {
10129         m_aGradients.push_front( GradientEmit() );
10130         m_aGradients.front().m_aGradient	= rGradient;
10131         m_aGradients.front().m_nObject	    = createObject();
10132         m_aGradients.front().m_aSize		= aPtSize;
10133         it = m_aGradients.begin();
10134     }
10135 
10136     OStringBuffer aObjName( 16 );
10137     aObjName.append( 'P' );
10138     aObjName.append( it->m_nObject );
10139     pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
10140 
10141     return it->m_nObject;
10142 }
10143 
10144 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient )
10145 {
10146     MARK( "drawGradient (Rectangle)" );
10147 
10148     if( m_aContext.Version == PDFWriter::PDF_1_2 )
10149     {
10150         drawRectangle( rRect );
10151         return;
10152     }
10153 
10154     sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
10155 
10156     Point aTranslate( rRect.BottomLeft() );
10157     aTranslate += Point( 0, 1 );
10158 
10159     updateGraphicsState();
10160 
10161     OStringBuffer aLine( 80 );
10162     aLine.append( "q 1 0 0 1 " );
10163     m_aPages.back().appendPoint( aTranslate, aLine );
10164     aLine.append( " cm " );
10165     // if a stroke is appended reset the clip region before stroke
10166     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10167         aLine.append( "q " );
10168     aLine.append( "0 0 " );
10169     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10170     aLine.append( ' ' );
10171     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10172     aLine.append( " re W n\n" );
10173 
10174     aLine.append( "/P" );
10175     aLine.append( nGradient );
10176     aLine.append( " sh " );
10177     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10178     {
10179         aLine.append( "Q 0 0 " );
10180         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10181         aLine.append( ' ' );
10182         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10183         aLine.append( " re S " );
10184     }
10185     aLine.append( "Q\n" );
10186     writeBuffer( aLine.getStr(), aLine.getLength() );
10187 }
10188 
10189 void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
10190 {
10191     MARK( "drawGradient (PolyPolygon)" );
10192 
10193     if( m_aContext.Version == PDFWriter::PDF_1_2 )
10194     {
10195         drawPolyPolygon( rPolyPoly );
10196         return;
10197     }
10198 
10199     Rectangle aBoundRect = rPolyPoly.GetBoundRect();
10200     sal_Int32 nGradient = createGradient( rGradient, aBoundRect.GetSize() );
10201 
10202     updateGraphicsState();
10203 
10204     Point aTranslate = aBoundRect.BottomLeft();
10205     int nPolygons = rPolyPoly.Count();
10206 
10207     OStringBuffer aLine( 80*nPolygons );
10208     aLine.append( "q " );
10209     // set PolyPolygon as clip path
10210     m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
10211     aLine.append( "W* n\n" );
10212     aLine.append( "1 0 0 1 " );
10213     m_aPages.back().appendPoint( aTranslate, aLine );
10214     aLine.append( " cm\n" );
10215     aLine.append( "/P" );
10216     aLine.append( nGradient );
10217     aLine.append( " sh Q\n" );
10218     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10219     {
10220         // and draw the surrounding path
10221         m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
10222         aLine.append( "S\n" );
10223     }
10224     writeBuffer( aLine.getStr(), aLine.getLength() );
10225 }
10226 
10227 void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
10228 {
10229     MARK( "drawHatch" );
10230 
10231     updateGraphicsState();
10232 
10233 	if( rPolyPoly.Count() )
10234 	{
10235 		PolyPolygon		aPolyPoly( rPolyPoly );
10236 
10237 		aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
10238 		push( PUSH_LINECOLOR );
10239 		setLineColor( rHatch.GetColor() );
10240 		getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, sal_False );
10241 		pop();
10242 	}
10243 }
10244 
10245 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall )
10246 {
10247     MARK( "drawWallpaper" );
10248 
10249     bool bDrawColor			= false;
10250     bool bDrawGradient		= false;
10251     bool bDrawBitmap		= false;
10252 
10253     BitmapEx aBitmap;
10254     Point aBmpPos = rRect.TopLeft();
10255     Size aBmpSize;
10256     if( rWall.IsBitmap() )
10257     {
10258         aBitmap = rWall.GetBitmap();
10259 		aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
10260                                 getMapMode(),
10261                                 getReferenceDevice(),
10262                                 aBitmap.GetPrefSize() );
10263         Rectangle aRect( rRect );
10264         if( rWall.IsRect() )
10265         {
10266             aRect = rWall.GetRect();
10267             aBmpPos = aRect.TopLeft();
10268             aBmpSize = aRect.GetSize();
10269         }
10270         if( rWall.GetStyle() != WALLPAPER_SCALE )
10271         {
10272             if( rWall.GetStyle() != WALLPAPER_TILE )
10273             {
10274                 bDrawBitmap		= true;
10275                 if( rWall.IsGradient() )
10276                     bDrawGradient = true;
10277                 else
10278                     bDrawColor = true;
10279                 switch( rWall.GetStyle() )
10280                 {
10281                     case WALLPAPER_TOPLEFT:
10282                         break;
10283                     case WALLPAPER_TOP:
10284                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10285                         break;
10286                     case WALLPAPER_LEFT:
10287                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10288                         break;
10289                     case WALLPAPER_TOPRIGHT:
10290                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10291                         break;
10292                     case WALLPAPER_CENTER:
10293                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10294                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10295                         break;
10296                     case WALLPAPER_RIGHT:
10297                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10298                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10299                         break;
10300                     case WALLPAPER_BOTTOMLEFT:
10301                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10302                         break;
10303                     case WALLPAPER_BOTTOM:
10304                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10305                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10306                         break;
10307                     case WALLPAPER_BOTTOMRIGHT:
10308                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10309                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10310                         break;
10311                     default: ;
10312                 }
10313             }
10314             else
10315             {
10316                 // push the bitmap
10317                 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) );
10318 
10319                 // convert to page coordinates; this needs to be done here
10320                 // since the emit does not know the page anymore
10321                 Rectangle aConvertRect( aBmpPos, aBmpSize );
10322                 m_aPages.back().convertRect( aConvertRect );
10323 
10324                 OStringBuffer aNameBuf(16);
10325                 aNameBuf.append( "Im" );
10326                 aNameBuf.append( rEmit.m_nObject );
10327                 OString aImageName( aNameBuf.makeStringAndClear() );
10328 
10329                 // push the pattern
10330                 OStringBuffer aTilingStream( 32 );
10331                 appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
10332                 aTilingStream.append( " 0 0 " );
10333                 appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
10334                 aTilingStream.append( " 0 0 cm\n/" );
10335                 aTilingStream.append( aImageName );
10336                 aTilingStream.append( " Do\n" );
10337 
10338                 m_aTilings.push_back( TilingEmit() );
10339                 m_aTilings.back().m_nObject			= createObject();
10340                 m_aTilings.back().m_aRectangle		= Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
10341                 m_aTilings.back().m_pTilingStream   = new SvMemoryStream();
10342                 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() );
10343                 // phase the tiling so wallpaper begins on upper left
10344                 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
10345                 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
10346                 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
10347 
10348                 updateGraphicsState();
10349 
10350                 OStringBuffer aObjName( 16 );
10351                 aObjName.append( 'P' );
10352                 aObjName.append( m_aTilings.back().m_nObject );
10353                 OString aPatternName( aObjName.makeStringAndClear() );
10354                 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
10355 
10356                 // fill a rRect with the pattern
10357                 OStringBuffer aLine( 100 );
10358                 aLine.append( "q /Pattern cs /" );
10359                 aLine.append( aPatternName );
10360                 aLine.append( " scn " );
10361                 m_aPages.back().appendRect( rRect, aLine );
10362                 aLine.append( " f Q\n" );
10363                 writeBuffer( aLine.getStr(), aLine.getLength() );
10364             }
10365         }
10366         else
10367         {
10368             aBmpPos		= aRect.TopLeft();
10369             aBmpSize	= aRect.GetSize();
10370             bDrawBitmap	= true;
10371         }
10372 
10373         if( aBitmap.IsTransparent() )
10374         {
10375             if( rWall.IsGradient() )
10376                 bDrawGradient = true;
10377             else
10378                 bDrawColor = true;
10379         }
10380     }
10381     else if( rWall.IsGradient() )
10382         bDrawGradient = true;
10383     else
10384         bDrawColor = true;
10385 
10386     if( bDrawGradient )
10387     {
10388         drawGradient( rRect, rWall.GetGradient() );
10389     }
10390     if( bDrawColor )
10391     {
10392         Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
10393         Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
10394         setLineColor( Color( COL_TRANSPARENT ) );
10395         setFillColor( rWall.GetColor() );
10396         drawRectangle( rRect );
10397         setLineColor( aOldLineColor );
10398         setFillColor( aOldFillColor );
10399     }
10400     if( bDrawBitmap )
10401     {
10402         // set temporary clip region since aBmpPos and aBmpSize
10403         // may be outside rRect
10404         OStringBuffer aLine( 20 );
10405         aLine.append( "q " );
10406         m_aPages.back().appendRect( rRect, aLine );
10407         aLine.append( " W n\n" );
10408         writeBuffer( aLine.getStr(), aLine.getLength() );
10409         drawBitmap( aBmpPos, aBmpSize, aBitmap );
10410         writeBuffer( "Q\n", 2 );
10411     }
10412 }
10413 
10414 void PDFWriterImpl::beginPattern( const Rectangle& rCellRect )
10415 {
10416     beginRedirect( new SvMemoryStream(), rCellRect );
10417 }
10418 
10419 sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform )
10420 {
10421     Rectangle aConvertRect( getRedirectTargetRect() );
10422     DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" );
10423 
10424     // get scaling between current mapmode and PDF output
10425     Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) );
10426     double fSX = (double(aScaling.Width()) / 10000.0);
10427     double fSY = (double(aScaling.Height()) / 10000.0);
10428 
10429     // transform translation part of matrix
10430     Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] );
10431     aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation );
10432 
10433     sal_Int32 nTilingId = m_aTilings.size();
10434     m_aTilings.push_back( TilingEmit() );
10435     TilingEmit& rTile = m_aTilings.back();
10436     rTile.m_nObject         = createObject();
10437     rTile.m_aResources      = m_aOutputStreams.front().m_aResourceDict;
10438     rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX;
10439     rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY;
10440     rTile.m_aTransform.matrix[2] = aTranslation.Width();
10441     rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX;
10442     rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY;
10443     rTile.m_aTransform.matrix[5] = -aTranslation.Height();
10444     // caution: endRedirect pops the stream, so do this last
10445     rTile.m_pTilingStream   = dynamic_cast<SvMemoryStream*>(endRedirect());
10446     // FIXME: bound rect will not work with rotated matrix
10447     rTile.m_aRectangle      = Rectangle( Point(0,0), aConvertRect.GetSize() );
10448     rTile.m_aCellSize       = aConvertRect.GetSize();
10449 
10450     OStringBuffer aObjName( 16 );
10451     aObjName.append( 'P' );
10452     aObjName.append( rTile.m_nObject );
10453     pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject );
10454     return nTilingId;
10455 }
10456 
10457 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill )
10458 {
10459     if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() )
10460         return;
10461 
10462     m_aPages.back().endStream();
10463     sal_Int32 nXObject = createObject();
10464     OStringBuffer aNameBuf( 16 );
10465     aNameBuf.append( "Pol" );
10466     aNameBuf.append( nXObject );
10467     OString aObjName( aNameBuf.makeStringAndClear() );
10468     Rectangle aObjRect;
10469     if( updateObject( nXObject ) )
10470     {
10471         // get bounding rect of object
10472         PolyPolygon aSubDiv;
10473         rPolyPoly.AdaptiveSubdivide( aSubDiv );
10474         aObjRect = aSubDiv.GetBoundRect();
10475         Rectangle aConvObjRect( aObjRect );
10476         m_aPages.back().convertRect( aConvObjRect );
10477 
10478         // move polypolygon to bottom left of page
10479         PolyPolygon aLocalPath( rPolyPoly );
10480         sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72;
10481         sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72;
10482         Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode );
10483         sal_Int32 nXOff = aObjRect.Left();
10484         sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom();
10485         aLocalPath.Move( -nXOff, nYOff );
10486 
10487         // prepare XObject's content stream
10488         OStringBuffer aStream( 512 );
10489         aStream.append( "/Pattern cs /P" );
10490         aStream.append( m_aTilings[ nPattern ].m_nObject );
10491         aStream.append( " scn\n" );
10492         m_aPages.back().appendPolyPolygon( aLocalPath, aStream );
10493         aStream.append( bEOFill ? "f*" : "f" );
10494         SvMemoryStream aMemStream( aStream.getLength() );
10495         aMemStream.Write( aStream.getStr(), aStream.getLength() );
10496         bool bDeflate = compressStream( &aMemStream );
10497         aMemStream.Seek( STREAM_SEEK_TO_END );
10498         sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell();
10499         aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
10500 
10501         // add new XObject to global resource dict
10502         m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject;
10503 
10504         // write XObject
10505         OStringBuffer aLine( 512 );
10506         aLine.append( nXObject );
10507         aLine.append( " 0 obj\n"
10508                       "<</Type/XObject/Subtype/Form/BBox[0 0 " );
10509         appendFixedInt( aConvObjRect.GetWidth(), aLine );
10510         aLine.append( ' ' );
10511         appendFixedInt( aConvObjRect.GetHeight(), aLine );
10512         aLine.append( "]/Length " );
10513         aLine.append( nStreamLen );
10514         if( bDeflate )
10515             aLine.append( "/Filter/FlateDecode" );
10516         aLine.append( ">>\n"
10517                       "stream\n" );
10518         writeBuffer( aLine.getStr(), aLine.getLength() );
10519         checkAndEnableStreamEncryption( nXObject );
10520         writeBuffer( aMemStream.GetData(), nStreamLen );
10521         disableStreamEncryption();
10522         writeBuffer( "\nendstream\nendobj\n\n", 19 );
10523     }
10524     m_aPages.back().beginStream();
10525     OStringBuffer aLine( 80 );
10526     aLine.append( "q 1 0 0 1 " );
10527     m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine );
10528     aLine.append( " cm/" );
10529     aLine.append( aObjName );
10530     aLine.append( " Do Q\n" );
10531     writeBuffer( aLine.getStr(), aLine.getLength() );
10532 }
10533 
10534 void PDFWriterImpl::updateGraphicsState()
10535 {
10536     OStringBuffer aLine( 256 );
10537     GraphicsState& rNewState = m_aGraphicsStack.front();
10538     // first set clip region since it might invalidate everything else
10539 
10540     if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) )
10541     {
10542         rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion;
10543 
10544         if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion ||
10545             ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) )
10546         {
10547             if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() )
10548             {
10549                 aLine.append( "Q " );
10550                 // invalidate everything but the clip region
10551                 m_aCurrentPDFState = GraphicsState();
10552                 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion);
10553             }
10554             if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() )
10555             {
10556                 // clip region is always stored in private PDF mapmode
10557                 MapMode aNewMapMode = rNewState.m_aMapMode;
10558                 rNewState.m_aMapMode = m_aMapMode;
10559                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10560                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10561 
10562                 aLine.append( "q " );
10563                 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine );
10564                 aLine.append( "W* n\n" );
10565                 rNewState.m_aMapMode = aNewMapMode;
10566                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10567                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10568             }
10569         }
10570     }
10571 
10572     if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) )
10573     {
10574         rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode;
10575         getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10576     }
10577 
10578     if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) )
10579     {
10580         rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont;
10581         getReferenceDevice()->SetFont( rNewState.m_aFont );
10582         getReferenceDevice()->ImplNewFont();
10583     }
10584 
10585     if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) )
10586     {
10587         rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode;
10588         getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
10589     }
10590 
10591     if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) )
10592     {
10593         rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage;
10594         getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
10595     }
10596 
10597     if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) )
10598     {
10599         rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor;
10600         if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
10601             rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
10602         {
10603             appendStrokingColor( rNewState.m_aLineColor, aLine );
10604             aLine.append( "\n" );
10605         }
10606     }
10607 
10608     if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) )
10609     {
10610         rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor;
10611         if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
10612             rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
10613         {
10614             appendNonStrokingColor( rNewState.m_aFillColor, aLine );
10615             aLine.append( "\n" );
10616         }
10617     }
10618 
10619     if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) )
10620     {
10621         rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent;
10622         if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent )
10623         {
10624             // TODO: switch extended graphicsstate
10625         }
10626     }
10627 
10628     // everything is up to date now
10629     m_aCurrentPDFState = m_aGraphicsStack.front();
10630     if( aLine.getLength() )
10631         writeBuffer( aLine.getStr(), aLine.getLength() );
10632 }
10633 
10634 /* #i47544# imitate OutputDevice behaviour:
10635 *  if a font with a nontransparent color is set, it overwrites the current
10636 *  text color. OTOH setting the text color will overwrite the color of the font.
10637 */
10638 void PDFWriterImpl::setFont( const Font& rFont )
10639 {
10640     Color aColor = rFont.GetColor();
10641     if( aColor == Color( COL_TRANSPARENT ) )
10642         aColor = m_aGraphicsStack.front().m_aFont.GetColor();
10643     m_aGraphicsStack.front().m_aFont = rFont;
10644     m_aGraphicsStack.front().m_aFont.SetColor( aColor );
10645     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont;
10646 }
10647 
10648 void PDFWriterImpl::push( sal_uInt16 nFlags )
10649 {
10650     OSL_ENSURE( m_aGraphicsStack.size() > 0, "invalid graphics stack" );
10651     m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
10652     m_aGraphicsStack.front().m_nFlags = nFlags;
10653 }
10654 
10655 void PDFWriterImpl::pop()
10656 {
10657     OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" );
10658     if( m_aGraphicsStack.size() < 2 )
10659         return;
10660 
10661     GraphicsState aState = m_aGraphicsStack.front();
10662     m_aGraphicsStack.pop_front();
10663     GraphicsState& rOld = m_aGraphicsStack.front();
10664 
10665     // move those parameters back that were not pushed
10666     // in the first place
10667     if( ! (aState.m_nFlags & PUSH_LINECOLOR) )
10668         setLineColor( aState.m_aLineColor );
10669     if( ! (aState.m_nFlags & PUSH_FILLCOLOR) )
10670         setFillColor( aState.m_aFillColor );
10671     if( ! (aState.m_nFlags & PUSH_FONT) )
10672         setFont( aState.m_aFont );
10673     if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) )
10674         setTextColor( aState.m_aFont.GetColor() );
10675     if( ! (aState.m_nFlags & PUSH_MAPMODE) )
10676         setMapMode( aState.m_aMapMode );
10677     if( ! (aState.m_nFlags & PUSH_CLIPREGION) )
10678     {
10679         // do not use setClipRegion here
10680         // it would convert again assuming the current mapmode
10681         rOld.m_aClipRegion = aState.m_aClipRegion;
10682         rOld.m_bClipRegion = aState.m_bClipRegion;
10683     }
10684     if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) )
10685         setTextLineColor( aState.m_aTextLineColor );
10686     if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) )
10687         setOverlineColor( aState.m_aOverlineColor );
10688     if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) )
10689         setTextAlign( aState.m_aFont.GetAlign() );
10690     if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) )
10691         setTextFillColor( aState.m_aFont.GetFillColor() );
10692     if( ! (aState.m_nFlags & PUSH_REFPOINT) )
10693     {
10694         // what ?
10695     }
10696     // invalidate graphics state
10697     m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U);
10698 }
10699 
10700 void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
10701 {
10702     m_aGraphicsStack.front().m_aMapMode = rMapMode;
10703     getReferenceDevice()->SetMapMode( rMapMode );
10704     m_aCurrentPDFState.m_aMapMode = rMapMode;
10705 }
10706 
10707 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10708 {
10709     basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10710     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10711     m_aGraphicsStack.front().m_aClipRegion = aRegion;
10712     m_aGraphicsStack.front().m_bClipRegion = true;
10713     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10714 }
10715 
10716 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
10717 {
10718     if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() )
10719     {
10720         Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10721                                    m_aMapMode,
10722                                    getReferenceDevice(),
10723                                    Point( nX, nY ) ) );
10724         aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10725                                m_aMapMode,
10726                                getReferenceDevice(),
10727                                Point() );
10728         basegfx::B2DHomMatrix aMat;
10729         aMat.translate( aPoint.X(), aPoint.Y() );
10730         m_aGraphicsStack.front().m_aClipRegion.transform( aMat );
10731         m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10732     }
10733 }
10734 
10735 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect )
10736 {
10737     basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect(
10738         basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) );
10739     return intersectClipRegion( aRect );
10740 }
10741 
10742 
10743 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10744 {
10745     basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) );
10746     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10747     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10748     if( m_aGraphicsStack.front().m_bClipRegion )
10749     {
10750         basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) );
10751         aRegion = basegfx::tools::prepareForPolygonOperation( aRegion );
10752         m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion );
10753     }
10754     else
10755     {
10756         m_aGraphicsStack.front().m_aClipRegion = aRegion;
10757         m_aGraphicsStack.front().m_bClipRegion = true;
10758     }
10759     return true;
10760 }
10761 
10762 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
10763 {
10764     if( nPageNr < 0 )
10765         nPageNr = m_nCurrentPage;
10766 
10767     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10768         return;
10769 
10770     m_aNotes.push_back( PDFNoteEntry() );
10771     m_aNotes.back().m_nObject		= createObject();
10772     m_aNotes.back().m_aContents		= rNote;
10773     m_aNotes.back().m_aRect			= rRect;
10774     // convert to default user space now, since the mapmode may change
10775     m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
10776 
10777     // insert note to page's annotation list
10778     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
10779 }
10780 
10781 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr )
10782 {
10783     if( nPageNr < 0 )
10784         nPageNr = m_nCurrentPage;
10785 
10786     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10787         return -1;
10788 
10789     sal_Int32 nRet = m_aLinks.size();
10790 
10791     m_aLinks.push_back( PDFLink() );
10792     m_aLinks.back().m_nObject	= createObject();
10793     m_aLinks.back().m_nPage		= nPageNr;
10794     m_aLinks.back().m_aRect		= rRect;
10795     // convert to default user space now, since the mapmode may change
10796     m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
10797 
10798     // insert link to page's annotation list
10799     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
10800 
10801     return nRet;
10802 }
10803 
10804 //--->i56629
10805 sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10806 {
10807     if( nPageNr < 0 )
10808         nPageNr = m_nCurrentPage;
10809 
10810     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10811         return -1;
10812 
10813     sal_Int32 nRet = m_aNamedDests.size();
10814 
10815     m_aNamedDests.push_back( PDFNamedDest() );
10816     m_aNamedDests.back().m_aDestName = sDestName;
10817     m_aNamedDests.back().m_nPage = nPageNr;
10818     m_aNamedDests.back().m_eType = eType;
10819     m_aNamedDests.back().m_aRect = rRect;
10820     // convert to default user space now, since the mapmode may change
10821     m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
10822 
10823     return nRet;
10824 }
10825 //<---i56629
10826 
10827 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10828 {
10829     if( nPageNr < 0 )
10830         nPageNr = m_nCurrentPage;
10831 
10832     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10833         return -1;
10834 
10835     sal_Int32 nRet = m_aDests.size();
10836 
10837     m_aDests.push_back( PDFDest() );
10838     m_aDests.back().m_nPage = nPageNr;
10839     m_aDests.back().m_eType	= eType;
10840     m_aDests.back().m_aRect = rRect;
10841     // convert to default user space now, since the mapmode may change
10842     m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
10843 
10844     return nRet;
10845 }
10846 
10847 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10848 {
10849     return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType );
10850 }
10851 
10852 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
10853 {
10854     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10855         return -1;
10856     if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
10857         return -2;
10858 
10859     m_aLinks[ nLinkId ].m_nDest = nDestId;
10860 
10861     return 0;
10862 }
10863 
10864 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
10865 {
10866     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10867         return -1;
10868 
10869     m_aLinks[ nLinkId ].m_nDest	= -1;
10870 
10871     using namespace ::com::sun::star;
10872 
10873     if (!m_xTrans.is())
10874     {
10875         uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() );
10876         if( xFact.is() )
10877         {
10878             m_xTrans = uno::Reference < util::XURLTransformer >(
10879                 xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY );
10880         }
10881     }
10882 
10883     util::URL aURL;
10884     aURL.Complete = rURL;
10885 
10886     if (m_xTrans.is())
10887         m_xTrans->parseStrict( aURL );
10888 
10889     m_aLinks[ nLinkId ].m_aURL	= aURL.Complete;
10890 
10891     return 0;
10892 }
10893 
10894 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
10895 {
10896     m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
10897 }
10898 
10899 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
10900 {
10901     // create new item
10902     sal_Int32 nNewItem = m_aOutline.size();
10903     m_aOutline.push_back( PDFOutlineEntry() );
10904 
10905     // set item attributes
10906     setOutlineItemParent( nNewItem, nParent );
10907     setOutlineItemText( nNewItem, rText );
10908     setOutlineItemDest( nNewItem, nDestID );
10909 
10910     return nNewItem;
10911 }
10912 
10913 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
10914 {
10915     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10916         return -1;
10917 
10918     int nRet = 0;
10919 
10920     if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
10921     {
10922         nNewParent = 0;
10923         nRet = -2;
10924     }
10925     // remove item from previous parent
10926     sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID;
10927     if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() )
10928     {
10929         PDFOutlineEntry& rParent = m_aOutline[ nParentID ];
10930 
10931         for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin();
10932              it != rParent.m_aChildren.end(); ++it )
10933         {
10934             if( *it == nItem )
10935             {
10936                 rParent.m_aChildren.erase( it );
10937                 break;
10938             }
10939         }
10940     }
10941 
10942     // insert item to new parent's list of children
10943     m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
10944 
10945     return nRet;
10946 }
10947 
10948 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
10949 {
10950     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10951         return -1;
10952 
10953     m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText );
10954     return 0;
10955 }
10956 
10957 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
10958 {
10959     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
10960         return -1;
10961     if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
10962         return -2;
10963     m_aOutline[nItem].m_nDestID = nDestID;
10964     return 0;
10965 }
10966 
10967 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
10968 {
10969     static std::map< PDFWriter::StructElement, const char* > aTagStrings;
10970     if( aTagStrings.empty() )
10971     {
10972         aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
10973         aTagStrings[ PDFWriter::Document ]		= "Document";
10974         aTagStrings[ PDFWriter::Part ]			= "Part";
10975         aTagStrings[ PDFWriter::Article ]		= "Art";
10976         aTagStrings[ PDFWriter::Section ]		= "Sect";
10977         aTagStrings[ PDFWriter::Division ]		= "Div";
10978         aTagStrings[ PDFWriter::BlockQuote ]	= "BlockQuote";
10979         aTagStrings[ PDFWriter::Caption ]		= "Caption";
10980         aTagStrings[ PDFWriter::TOC ]			= "TOC";
10981         aTagStrings[ PDFWriter::TOCI ]			= "TOCI";
10982         aTagStrings[ PDFWriter::Index ]			= "Index";
10983         aTagStrings[ PDFWriter::Paragraph ]		= "P";
10984         aTagStrings[ PDFWriter::Heading ]		= "H";
10985         aTagStrings[ PDFWriter::H1 ]			= "H1";
10986         aTagStrings[ PDFWriter::H2 ]			= "H2";
10987         aTagStrings[ PDFWriter::H3 ]			= "H3";
10988         aTagStrings[ PDFWriter::H4 ]			= "H4";
10989         aTagStrings[ PDFWriter::H5 ]			= "H5";
10990         aTagStrings[ PDFWriter::H6 ]			= "H6";
10991         aTagStrings[ PDFWriter::List ]			= "L";
10992         aTagStrings[ PDFWriter::ListItem ]		= "LI";
10993         aTagStrings[ PDFWriter::LILabel ]		= "Lbl";
10994         aTagStrings[ PDFWriter::LIBody ]		= "LBody";
10995         aTagStrings[ PDFWriter::Table ]			= "Table";
10996         aTagStrings[ PDFWriter::TableRow ]		= "TR";
10997         aTagStrings[ PDFWriter::TableHeader ]	= "TH";
10998         aTagStrings[ PDFWriter::TableData ]		= "TD";
10999         aTagStrings[ PDFWriter::Span ]			= "Span";
11000         aTagStrings[ PDFWriter::Quote ]			= "Quote";
11001         aTagStrings[ PDFWriter::Note ]			= "Note";
11002         aTagStrings[ PDFWriter::Reference ]		= "Reference";
11003         aTagStrings[ PDFWriter::BibEntry ]		= "BibEntry";
11004         aTagStrings[ PDFWriter::Code ]			= "Code";
11005         aTagStrings[ PDFWriter::Link ]			= "Link";
11006         aTagStrings[ PDFWriter::Figure ]		= "Figure";
11007         aTagStrings[ PDFWriter::Formula ]		= "Formula";
11008         aTagStrings[ PDFWriter::Form ]			= "Form";
11009     }
11010 
11011     std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
11012 
11013     return it != aTagStrings.end() ? it->second : "Div";
11014 }
11015 
11016 void PDFWriterImpl::beginStructureElementMCSeq()
11017 {
11018     if(	m_bEmitStructure &&
11019         m_nCurrentStructElement > 0 && // StructTreeRoot
11020         ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11021         )
11022     {
11023         PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
11024         OStringBuffer aLine( 128 );
11025         sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
11026         aLine.append( "/" );
11027         if( rEle.m_aAlias.getLength() > 0 )
11028             aLine.append( rEle.m_aAlias );
11029         else
11030             aLine.append( getStructureTag( rEle.m_eType ) );
11031         aLine.append( "<</MCID " );
11032         aLine.append( nMCID );
11033         aLine.append( ">>BDC\n" );
11034         writeBuffer( aLine.getStr(), aLine.getLength() );
11035 
11036         // update the element's content list
11037 #if OSL_DEBUG_LEVEL > 1
11038         fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n",
11039                  nMCID,
11040                  m_aPages[ m_nCurrentPage ].m_nPageObject,
11041                  rEle.m_nFirstPageObject );
11042 #endif
11043         rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) );
11044         // update the page's mcid parent list
11045         m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
11046         // mark element MC sequence as open
11047         rEle.m_bOpenMCSeq = true;
11048     }
11049     // handle artifacts
11050     else if( ! m_bEmitStructure && m_aContext.Tagged &&
11051                m_nCurrentStructElement > 0 &&
11052                m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
11053              ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11054              )
11055     {
11056         OStringBuffer aLine( 128 );
11057         aLine.append( "/Artifact BMC\n" );
11058         writeBuffer( aLine.getStr(), aLine.getLength() );
11059         // mark element MC sequence as open
11060         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
11061     }
11062 }
11063 
11064 void PDFWriterImpl::endStructureElementMCSeq()
11065 {
11066     if( m_nCurrentStructElement > 0 && // StructTreeRoot
11067         ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
11068         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
11069         )
11070     {
11071         writeBuffer( "EMC\n", 4 );
11072         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
11073     }
11074 }
11075 
11076 bool PDFWriterImpl::checkEmitStructure()
11077 {
11078     bool bEmit = false;
11079     if( m_aContext.Tagged )
11080     {
11081         bEmit = true;
11082         sal_Int32 nEle = m_nCurrentStructElement;
11083         while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
11084         {
11085             if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
11086             {
11087                 bEmit = false;
11088                 break;
11089             }
11090             nEle = m_aStructure[ nEle ].m_nParentElement;
11091         }
11092     }
11093     return bEmit;
11094 }
11095 
11096 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias )
11097 {
11098     if( m_nCurrentPage < 0 )
11099         return -1;
11100 
11101     if( ! m_aContext.Tagged )
11102         return -1;
11103 
11104     // close eventual current MC sequence
11105     endStructureElementMCSeq();
11106 
11107     if( m_nCurrentStructElement == 0 &&
11108         eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
11109     {
11110         // struct tree root hit, but not beginning document
11111         // this might happen with setCurrentStructureElement
11112         // silently insert structure into document again if one properly exists
11113         if( ! m_aStructure[ 0 ].m_aChildren.empty() )
11114         {
11115             PDFWriter::StructElement childType = PDFWriter::NonStructElement;
11116             sal_Int32 nNewCurElement = 0;
11117             const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
11118             for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
11119                  childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
11120             {
11121                 nNewCurElement = *it;
11122                 childType = m_aStructure[ nNewCurElement ].m_eType;
11123             }
11124             if( childType == PDFWriter::Document )
11125             {
11126                 m_nCurrentStructElement = nNewCurElement;
11127                 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" );
11128             }
11129             else {
11130                 DBG_ERROR( "document structure in disorder !" );
11131             }
11132         }
11133         else {
11134             DBG_ERROR( "PDF document structure MUST be contained in a Document element" );
11135         }
11136     }
11137 
11138     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11139     m_aStructure.push_back( PDFStructureElement() );
11140     PDFStructureElement& rEle = m_aStructure.back();
11141     rEle.m_eType			= eType;
11142     rEle.m_nOwnElement		= nNewId;
11143     rEle.m_nParentElement	= m_nCurrentStructElement;
11144     rEle.m_nFirstPageObject	= m_aPages[ m_nCurrentPage ].m_nPageObject;
11145     m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
11146     m_nCurrentStructElement = nNewId;
11147 
11148     // handle alias names
11149     if( rAlias.getLength() && eType != PDFWriter::NonStructElement )
11150     {
11151         OStringBuffer aNameBuf( rAlias.getLength() );
11152         appendName( rAlias, aNameBuf );
11153         OString aAliasName( aNameBuf.makeStringAndClear() );
11154         rEle.m_aAlias = aAliasName;
11155         m_aRoleMap[ aAliasName ] = getStructureTag( eType );
11156     }
11157 
11158 #if OSL_DEBUG_LEVEL > 1
11159     OStringBuffer aLine( "beginStructureElement " );
11160     aLine.append( m_nCurrentStructElement );
11161     aLine.append( ": " );
11162     aLine.append( getStructureTag( eType ) );
11163     if( rEle.m_aAlias.getLength() )
11164     {
11165         aLine.append( " aliased as \"" );
11166         aLine.append( rEle.m_aAlias );
11167         aLine.append( '\"' );
11168     }
11169     emitComment( aLine.getStr() );
11170 #endif
11171 
11172     // check whether to emit structure henceforth
11173     m_bEmitStructure = checkEmitStructure();
11174 
11175     if( m_bEmitStructure ) // don't create nonexistant objects
11176     {
11177         rEle.m_nObject		= createObject();
11178         // update parent's kids list
11179         m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject );
11180     }
11181     return nNewId;
11182 }
11183 
11184 void PDFWriterImpl::endStructureElement()
11185 {
11186     if( m_nCurrentPage < 0 )
11187         return;
11188 
11189     if( ! m_aContext.Tagged )
11190         return;
11191 
11192     if( m_nCurrentStructElement == 0 )
11193     {
11194         // hit the struct tree root, that means there is an endStructureElement
11195         // without corresponding beginStructureElement
11196         return;
11197     }
11198 
11199     // end the marked content sequence
11200     endStructureElementMCSeq();
11201 
11202 #if OSL_DEBUG_LEVEL > 1
11203     OStringBuffer aLine( "endStructureElement " );
11204     aLine.append( m_nCurrentStructElement );
11205     aLine.append( ": " );
11206     aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11207     if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
11208     {
11209         aLine.append( " aliased as \"" );
11210         aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11211         aLine.append( '\"' );
11212     }
11213 #endif
11214 
11215     // "end" the structure element, the parent becomes current element
11216     m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
11217 
11218     // check whether to emit structure henceforth
11219     m_bEmitStructure = checkEmitStructure();
11220 
11221 #if OSL_DEBUG_LEVEL > 1
11222     if( m_bEmitStructure )
11223         emitComment( aLine.getStr() );
11224 #endif
11225 }
11226 
11227 //---> i94258
11228 /*
11229  * This function adds an internal structure list container to overcome the 8191 elements array limitation
11230  * in kids element emission.
11231  * Recursive function
11232  *
11233  */
11234 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
11235 {
11236     if( rEle.m_eType == PDFWriter::NonStructElement &&
11237         rEle.m_nOwnElement != rEle.m_nParentElement )
11238         return;
11239 
11240     for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
11241     {
11242         if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
11243         {
11244             PDFStructureElement& rChild = m_aStructure[ *it ];
11245             if( rChild.m_eType != PDFWriter::NonStructElement )
11246             {
11247                 //triggered when a child of the rEle element is found
11248                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
11249                     addInternalStructureContainer( rChild );//examine the child
11250                 else
11251                 {
11252                     DBG_ERROR( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
11253 #if OSL_DEBUG_LEVEL > 1
11254                     fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
11255 #endif
11256                 }
11257             }
11258         }
11259         else
11260         {
11261             DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
11262 #if OSL_DEBUG_LEVEL > 1
11263             fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
11264 #endif
11265         }
11266     }
11267 
11268     if( rEle.m_nOwnElement != rEle.m_nParentElement )
11269     {
11270         if( !rEle.m_aKids.empty() )
11271         {
11272             if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
11273                 //then we need to add the containers for the kids elements
11274                 // a list to be used for the new kid element
11275                 std::list< PDFStructureElementKid > aNewKids;
11276                 std::list< sal_Int32 > aNewChildren;
11277 
11278                 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
11279                 OStringBuffer aNameBuf( "Div" );
11280                 OString aAliasName( aNameBuf.makeStringAndClear() );
11281                 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
11282 
11283                 while( rEle.m_aKids.size() > ncMaxPDFArraySize )
11284                 {
11285                     sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
11286                     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11287                     m_aStructure.push_back( PDFStructureElement() );
11288                     PDFStructureElement& rEleNew = m_aStructure.back();
11289                     rEleNew.m_aAlias            = aAliasName;
11290                     rEleNew.m_eType			    = PDFWriter::Division; // a new Div type container
11291                     rEleNew.m_nOwnElement		= nNewId;
11292                     rEleNew.m_nParentElement	= nCurrentStructElement;
11293                     //inherit the same page as the first child to be reparented
11294                     rEleNew.m_nFirstPageObject	= m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
11295                     rEleNew.m_nObject           = createObject();//assign a PDF object number
11296                     //add the object to the kid list of the parent
11297                     aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) );
11298                     aNewChildren.push_back( nNewId );
11299 
11300                     std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
11301                     std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
11302                     advance( aChildEndIt, ncMaxPDFArraySize );
11303                     advance( aKidEndIt, ncMaxPDFArraySize );
11304 
11305                     rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
11306                                             rEle.m_aKids,
11307                                             rEle.m_aKids.begin(),
11308                                             aKidEndIt );
11309                     rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
11310                                                 rEle.m_aChildren,
11311                                                 rEle.m_aChildren.begin(),
11312                                                 aChildEndIt );
11313                     // set the kid's new parent
11314                     for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
11315                          it != rEleNew.m_aChildren.end(); ++it )
11316                     {
11317                         m_aStructure[ *it ].m_nParentElement = nNewId;
11318                     }
11319                 }
11320                 //finally add the new kids resulting from the container added
11321                 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
11322                 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
11323             }
11324         }
11325     }
11326 }
11327 //<--- i94258
11328 
11329 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
11330 {
11331     bool bSuccess = false;
11332 
11333     if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
11334     {
11335         // end eventual previous marked content sequence
11336         endStructureElementMCSeq();
11337 
11338         m_nCurrentStructElement = nEle;
11339         m_bEmitStructure = checkEmitStructure();
11340 #if OSL_DEBUG_LEVEL > 1
11341         OStringBuffer aLine( "setCurrentStructureElement " );
11342         aLine.append( m_nCurrentStructElement );
11343         aLine.append( ": " );
11344         aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11345         if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
11346         {
11347             aLine.append( " aliased as \"" );
11348             aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11349             aLine.append( '\"' );
11350         }
11351         if( ! m_bEmitStructure )
11352             aLine.append( " (inside NonStruct)" );
11353         emitComment( aLine.getStr() );
11354 #endif
11355         bSuccess = true;
11356     }
11357 
11358     return bSuccess;
11359 }
11360 
11361 sal_Int32 PDFWriterImpl::getCurrentStructureElement()
11362 {
11363     return m_nCurrentStructElement;
11364 }
11365 
11366 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
11367 {
11368     if( !m_aContext.Tagged )
11369         return false;
11370 
11371     bool bInsert = false;
11372     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11373     {
11374         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11375         switch( eAttr )
11376         {
11377             case PDFWriter::Placement:
11378                 if( eVal == PDFWriter::Block		||
11379                     eVal == PDFWriter::Inline		||
11380                     eVal == PDFWriter::Before		||
11381                     eVal == PDFWriter::Start		||
11382                     eVal == PDFWriter::End )
11383                     bInsert = true;
11384                 break;
11385             case PDFWriter::WritingMode:
11386                 if( eVal == PDFWriter::LrTb			||
11387                     eVal == PDFWriter::RlTb			||
11388                     eVal == PDFWriter::TbRl )
11389                 {
11390                     bInsert = true;
11391                 }
11392                 break;
11393             case PDFWriter::TextAlign:
11394                 if( eVal == PDFWriter::Start		||
11395                     eVal == PDFWriter::Center		||
11396                     eVal == PDFWriter::End			||
11397                     eVal == PDFWriter::Justify )
11398                 {
11399                     if( eType == PDFWriter::Paragraph	||
11400                         eType == PDFWriter::Heading		||
11401                         eType == PDFWriter::H1			||
11402                         eType == PDFWriter::H2			||
11403                         eType == PDFWriter::H3			||
11404                         eType == PDFWriter::H4			||
11405                         eType == PDFWriter::H5			||
11406                         eType == PDFWriter::H6			||
11407                         eType == PDFWriter::List		||
11408                         eType == PDFWriter::ListItem	||
11409                         eType == PDFWriter::LILabel		||
11410                         eType == PDFWriter::LIBody		||
11411                         eType == PDFWriter::Table		||
11412                         eType == PDFWriter::TableRow	||
11413                         eType == PDFWriter::TableHeader	||
11414                         eType == PDFWriter::TableData )
11415                     {
11416                         bInsert = true;
11417                     }
11418                 }
11419                 break;
11420             case PDFWriter::Width:
11421             case PDFWriter::Height:
11422                 if( eVal == PDFWriter::Auto )
11423                 {
11424                     if( eType == PDFWriter::Figure		||
11425                         eType == PDFWriter::Formula		||
11426                         eType == PDFWriter::Form		||
11427                         eType == PDFWriter::Table		||
11428                         eType == PDFWriter::TableHeader	||
11429                         eType == PDFWriter::TableData )
11430                     {
11431                         bInsert = true;
11432                     }
11433                 }
11434                 break;
11435             case PDFWriter::BlockAlign:
11436                 if( eVal == PDFWriter::Before		||
11437                     eVal == PDFWriter::Middle		||
11438                     eVal == PDFWriter::After		||
11439                     eVal == PDFWriter::Justify )
11440                 {
11441                     if( eType == PDFWriter::TableHeader	||
11442                         eType == PDFWriter::TableData )
11443                     {
11444                         bInsert = true;
11445                     }
11446                 }
11447                 break;
11448             case PDFWriter::InlineAlign:
11449                 if( eVal == PDFWriter::Start		||
11450                     eVal == PDFWriter::Center		||
11451                     eVal == PDFWriter::End )
11452                 {
11453                     if( eType == PDFWriter::TableHeader	||
11454                         eType == PDFWriter::TableData )
11455                     {
11456                         bInsert = true;
11457                     }
11458                 }
11459                 break;
11460             case PDFWriter::LineHeight:
11461                 if( eVal == PDFWriter::Normal		||
11462                     eVal == PDFWriter::Auto )
11463                 {
11464                     // only for ILSE and BLSE
11465                     if( eType == PDFWriter::Paragraph	||
11466                         eType == PDFWriter::Heading		||
11467                         eType == PDFWriter::H1			||
11468                         eType == PDFWriter::H2			||
11469                         eType == PDFWriter::H3			||
11470                         eType == PDFWriter::H4			||
11471                         eType == PDFWriter::H5			||
11472                         eType == PDFWriter::H6			||
11473                         eType == PDFWriter::List		||
11474                         eType == PDFWriter::ListItem	||
11475                         eType == PDFWriter::LILabel		||
11476                         eType == PDFWriter::LIBody		||
11477                         eType == PDFWriter::Table		||
11478                         eType == PDFWriter::TableRow	||
11479                         eType == PDFWriter::TableHeader	||
11480                         eType == PDFWriter::TableData	||
11481                         eType == PDFWriter::Span		||
11482                         eType == PDFWriter::Quote		||
11483                         eType == PDFWriter::Note		||
11484                         eType == PDFWriter::Reference	||
11485                         eType == PDFWriter::BibEntry	||
11486                         eType == PDFWriter::Code		||
11487                         eType == PDFWriter::Link )
11488                     {
11489                         bInsert = true;
11490                     }
11491                 }
11492                 break;
11493             case PDFWriter::TextDecorationType:
11494                 if( eVal == PDFWriter::NONE			||
11495                     eVal == PDFWriter::Underline	||
11496                     eVal == PDFWriter::Overline		||
11497                     eVal == PDFWriter::LineThrough )
11498                 {
11499                     // only for ILSE and BLSE
11500                     if( eType == PDFWriter::Paragraph	||
11501                         eType == PDFWriter::Heading		||
11502                         eType == PDFWriter::H1			||
11503                         eType == PDFWriter::H2			||
11504                         eType == PDFWriter::H3			||
11505                         eType == PDFWriter::H4			||
11506                         eType == PDFWriter::H5			||
11507                         eType == PDFWriter::H6			||
11508                         eType == PDFWriter::List		||
11509                         eType == PDFWriter::ListItem	||
11510                         eType == PDFWriter::LILabel		||
11511                         eType == PDFWriter::LIBody		||
11512                         eType == PDFWriter::Table		||
11513                         eType == PDFWriter::TableRow	||
11514                         eType == PDFWriter::TableHeader	||
11515                         eType == PDFWriter::TableData	||
11516                         eType == PDFWriter::Span		||
11517                         eType == PDFWriter::Quote		||
11518                         eType == PDFWriter::Note		||
11519                         eType == PDFWriter::Reference	||
11520                         eType == PDFWriter::BibEntry	||
11521                         eType == PDFWriter::Code		||
11522                         eType == PDFWriter::Link )
11523                     {
11524                         bInsert = true;
11525                     }
11526                 }
11527                 break;
11528             case PDFWriter::ListNumbering:
11529                 if( eVal == PDFWriter::NONE			||
11530                     eVal == PDFWriter::Disc			||
11531                     eVal == PDFWriter::Circle		||
11532                     eVal == PDFWriter::Square		||
11533                     eVal == PDFWriter::Decimal		||
11534                     eVal == PDFWriter::UpperRoman	||
11535                     eVal == PDFWriter::LowerRoman	||
11536                     eVal == PDFWriter::UpperAlpha	||
11537                     eVal == PDFWriter::LowerAlpha )
11538                 {
11539                     if( eType == PDFWriter::List )
11540                         bInsert = true;
11541                 }
11542                 break;
11543             default: break;
11544         }
11545     }
11546 
11547     if( bInsert )
11548         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
11549 #if OSL_DEBUG_LEVEL > 1
11550     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11551         fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
11552                  getAttributeTag( eAttr ),
11553                  getAttributeValueTag( eVal ),
11554                  getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11555                  m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
11556                  );
11557 #endif
11558 
11559     return bInsert;
11560 }
11561 
11562 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
11563 {
11564     if( ! m_aContext.Tagged )
11565         return false;
11566 
11567     bool bInsert = false;
11568     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11569     {
11570         if( eAttr == PDFWriter::Language )
11571         {
11572             m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue );
11573             return true;
11574         }
11575 
11576         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11577         switch( eAttr )
11578         {
11579             case PDFWriter::SpaceBefore:
11580             case PDFWriter::SpaceAfter:
11581             case PDFWriter::StartIndent:
11582             case PDFWriter::EndIndent:
11583                 // just for BLSE
11584                 if( eType == PDFWriter::Paragraph	||
11585                     eType == PDFWriter::Heading		||
11586                     eType == PDFWriter::H1			||
11587                     eType == PDFWriter::H2			||
11588                     eType == PDFWriter::H3			||
11589                     eType == PDFWriter::H4			||
11590                     eType == PDFWriter::H5			||
11591                     eType == PDFWriter::H6			||
11592                     eType == PDFWriter::List		||
11593                     eType == PDFWriter::ListItem	||
11594                     eType == PDFWriter::LILabel		||
11595                     eType == PDFWriter::LIBody		||
11596                     eType == PDFWriter::Table		||
11597                     eType == PDFWriter::TableRow	||
11598                     eType == PDFWriter::TableHeader	||
11599                     eType == PDFWriter::TableData )
11600                 {
11601                     bInsert = true;
11602                 }
11603                 break;
11604             case PDFWriter::TextIndent:
11605                 // paragraph like BLSE and additional elements
11606                 if( eType == PDFWriter::Paragraph	||
11607                     eType == PDFWriter::Heading		||
11608                     eType == PDFWriter::H1			||
11609                     eType == PDFWriter::H2			||
11610                     eType == PDFWriter::H3			||
11611                     eType == PDFWriter::H4			||
11612                     eType == PDFWriter::H5			||
11613                     eType == PDFWriter::H6			||
11614                     eType == PDFWriter::LILabel		||
11615                     eType == PDFWriter::LIBody		||
11616                     eType == PDFWriter::TableHeader	||
11617                     eType == PDFWriter::TableData )
11618                 {
11619                     bInsert = true;
11620                 }
11621                 break;
11622             case PDFWriter::Width:
11623             case PDFWriter::Height:
11624                 if( eType == PDFWriter::Figure		||
11625                     eType == PDFWriter::Formula		||
11626                     eType == PDFWriter::Form		||
11627                     eType == PDFWriter::Table		||
11628                     eType == PDFWriter::TableHeader	||
11629                     eType == PDFWriter::TableData )
11630                 {
11631                     bInsert = true;
11632                 }
11633                 break;
11634             case PDFWriter::LineHeight:
11635             case PDFWriter::BaselineShift:
11636                 // only for ILSE and BLSE
11637                 if( eType == PDFWriter::Paragraph	||
11638                     eType == PDFWriter::Heading		||
11639                     eType == PDFWriter::H1			||
11640                     eType == PDFWriter::H2			||
11641                     eType == PDFWriter::H3			||
11642                     eType == PDFWriter::H4			||
11643                     eType == PDFWriter::H5			||
11644                     eType == PDFWriter::H6			||
11645                     eType == PDFWriter::List		||
11646                     eType == PDFWriter::ListItem	||
11647                     eType == PDFWriter::LILabel		||
11648                     eType == PDFWriter::LIBody		||
11649                     eType == PDFWriter::Table		||
11650                     eType == PDFWriter::TableRow	||
11651                     eType == PDFWriter::TableHeader	||
11652                     eType == PDFWriter::TableData	||
11653                     eType == PDFWriter::Span		||
11654                     eType == PDFWriter::Quote		||
11655                     eType == PDFWriter::Note		||
11656                     eType == PDFWriter::Reference	||
11657                     eType == PDFWriter::BibEntry	||
11658                     eType == PDFWriter::Code		||
11659                     eType == PDFWriter::Link )
11660                 {
11661                         bInsert = true;
11662                 }
11663                 break;
11664             case PDFWriter::RowSpan:
11665             case PDFWriter::ColSpan:
11666                 // only for table cells
11667                 if( eType == PDFWriter::TableHeader	||
11668                     eType == PDFWriter::TableData )
11669                 {
11670                     bInsert = true;
11671                 }
11672                 break;
11673             case PDFWriter::LinkAnnotation:
11674                 if( eType == PDFWriter::Link )
11675                     bInsert = true;
11676                 break;
11677             default: break;
11678         }
11679     }
11680 
11681     if( bInsert )
11682         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
11683 #if OSL_DEBUG_LEVEL > 1
11684     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11685         fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
11686                  getAttributeTag( eAttr ),
11687                  (int)nValue,
11688                  getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11689                  m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() );
11690 #endif
11691 
11692     return bInsert;
11693 }
11694 
11695 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect )
11696 {
11697     sal_Int32 nPageNr = m_nCurrentPage;
11698     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
11699         return;
11700 
11701 
11702     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11703     {
11704         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11705         if( eType == PDFWriter::Figure		||
11706             eType == PDFWriter::Formula		||
11707             eType == PDFWriter::Form		||
11708             eType == PDFWriter::Table )
11709         {
11710             m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
11711             // convert to default user space now, since the mapmode may change
11712             m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
11713         }
11714     }
11715 }
11716 
11717 void PDFWriterImpl::setActualText( const String& rText )
11718 {
11719     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11720     {
11721         m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
11722     }
11723 }
11724 
11725 void PDFWriterImpl::setAlternateText( const String& rText )
11726 {
11727     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11728     {
11729         m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
11730     }
11731 }
11732 
11733 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
11734 {
11735     if( nPageNr < 0 )
11736         nPageNr = m_nCurrentPage;
11737 
11738     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11739         return;
11740 
11741     m_aPages[ nPageNr ].m_nDuration = nSeconds;
11742 }
11743 
11744 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
11745 {
11746     if( nPageNr < 0 )
11747         nPageNr = m_nCurrentPage;
11748 
11749     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11750         return;
11751 
11752     m_aPages[ nPageNr ].m_eTransition	= eType;
11753     m_aPages[ nPageNr ].m_nTransTime	= nMilliSec;
11754 }
11755 
11756 void PDFWriterImpl::ensureUniqueRadioOnValues()
11757 {
11758     // loop over radio groups
11759     for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
11760          group != m_aRadioGroupWidgets.end(); ++group )
11761     {
11762         PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
11763         // check whether all kids have a unique OnValue
11764         std::hash_map< OUString, sal_Int32, OUStringHash > aOnValues;
11765         int nChildren = rGroupWidget.m_aKidsIndex.size();
11766         bool bIsUnique = true;
11767         for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
11768         {
11769             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11770             const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
11771             #if OSL_DEBUG_LEVEL > 1
11772             fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() );
11773             #endif
11774             if( aOnValues.find( rVal ) == aOnValues.end() )
11775             {
11776                 aOnValues[ rVal ] = 1;
11777             }
11778             else
11779             {
11780                 bIsUnique = false;
11781             }
11782         }
11783         if( ! bIsUnique )
11784         {
11785             #if OSL_DEBUG_LEVEL > 1
11786             fprintf( stderr, "enforcing unique OnValues\n" );
11787             #endif
11788             // make unique by using ascending OnValues
11789             for( int nKid = 0; nKid < nChildren; nKid++ )
11790             {
11791                 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11792                 PDFWidget& rKid = m_aWidgets[nKidIndex];
11793                 rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) );
11794                 if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11795                     rKid.m_aValue = rKid.m_aOnValue;
11796             }
11797         }
11798         // finally move the "Yes" appearance to the OnValue appearance
11799         for( int nKid = 0; nKid < nChildren; nKid++ )
11800         {
11801             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11802             PDFWidget& rKid = m_aWidgets[nKidIndex];
11803             PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
11804             if( app_it != rKid.m_aAppearances.end() )
11805             {
11806                 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
11807                 if( stream_it != app_it->second.end() )
11808                 {
11809                     SvMemoryStream* pStream = stream_it->second;
11810                     app_it->second.erase( stream_it );
11811                     OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
11812                     appendName( rKid.m_aOnValue, aBuf );
11813                     (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11814                 }
11815                 #if OSL_DEBUG_LEVEL > 1
11816                 else
11817                     fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" );
11818                 #endif
11819             }
11820             // update selected radio button
11821             if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11822             {
11823                 rGroupWidget.m_aValue = rKid.m_aValue;
11824             }
11825         }
11826     }
11827 }
11828 
11829 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
11830 {
11831     sal_Int32 nRadioGroupWidget = -1;
11832 
11833     std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
11834 
11835     if( it == m_aRadioGroupWidgets.end() )
11836     {
11837         m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
11838             sal_Int32(m_aWidgets.size());
11839 
11840         // new group, insert the radiobutton
11841         m_aWidgets.push_back( PDFWidget() );
11842         m_aWidgets.back().m_nObject		= createObject();
11843         m_aWidgets.back().m_nPage		= m_nCurrentPage;
11844         m_aWidgets.back().m_eType		= PDFWriter::RadioButton;
11845         m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
11846         m_aWidgets.back().m_nFlags |= 0x0000C000;   // NoToggleToOff and Radio bits
11847 
11848         createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn );
11849     }
11850     else
11851         nRadioGroupWidget = it->second;
11852 
11853     return nRadioGroupWidget;
11854 }
11855 
11856 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
11857 {
11858     if( nPageNr < 0 )
11859         nPageNr = m_nCurrentPage;
11860 
11861     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11862         return -1;
11863 
11864     sal_Int32 nNewWidget = m_aWidgets.size();
11865     m_aWidgets.push_back( PDFWidget() );
11866 
11867     m_aWidgets.back().m_nObject			= createObject();
11868     m_aWidgets.back().m_aRect				= rControl.Location;
11869     m_aWidgets.back().m_nPage				= nPageNr;
11870     m_aWidgets.back().m_eType				= rControl.getType();
11871 
11872     sal_Int32 nRadioGroupWidget = -1;
11873     // for unknown reasons the radio buttons of a radio group must not have a
11874     // field name, else the buttons are in fact check boxes -
11875     // that is multiple buttons of the radio group can be selected
11876     if( rControl.getType() == PDFWriter::RadioButton )
11877         nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
11878     else
11879     {
11880         createWidgetFieldName( nNewWidget, rControl );
11881     }
11882 
11883     // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
11884     PDFWidget& rNewWidget			= m_aWidgets[nNewWidget];
11885     rNewWidget.m_aDescription		= rControl.Description;
11886     rNewWidget.m_aText				= rControl.Text;
11887     rNewWidget.m_nTextStyle			= rControl.TextStyle &
11888         (  TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP |
11889            TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM |
11890            TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK  );
11891     rNewWidget.m_nTabOrder          = rControl.TabOrder;
11892 
11893     // various properties are set via the flags (/Ff) property of the field dict
11894     if( rControl.ReadOnly )
11895         rNewWidget.m_nFlags |= 1;
11896     if( rControl.getType() == PDFWriter::PushButton )
11897     {
11898         const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
11899         if( rNewWidget.m_nTextStyle == 0 )
11900             rNewWidget.m_nTextStyle =
11901                 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER |
11902                 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11903 
11904         rNewWidget.m_nFlags |= 0x00010000;
11905         if( rBtn.URL.getLength() )
11906             rNewWidget.m_aListEntries.push_back( rBtn.URL );
11907         rNewWidget.m_bSubmit    = rBtn.Submit;
11908         rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
11909         rNewWidget.m_nDest      = rBtn.Dest;
11910         createDefaultPushButtonAppearance( rNewWidget, rBtn );
11911     }
11912     else if( rControl.getType() == PDFWriter::RadioButton )
11913     {
11914         const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
11915         if( rNewWidget.m_nTextStyle == 0 )
11916             rNewWidget.m_nTextStyle =
11917                 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11918         /*  PDF sees a RadioButton group as one radio button with
11919          *  children which are in turn check boxes
11920          *
11921          *  so we need to create a radio button on demand for a new group
11922          *  and insert a checkbox for each RadioButtonWidget as its child
11923          */
11924         rNewWidget.m_eType			= PDFWriter::CheckBox;
11925         rNewWidget.m_nRadioGroup	= rBtn.RadioGroup;
11926 
11927         DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" );
11928 
11929         PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
11930         rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
11931         rRadioButton.m_aKidsIndex.push_back( nNewWidget );
11932         rNewWidget.m_nParent = rRadioButton.m_nObject;
11933 
11934         rNewWidget.m_aValue     = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) );
11935         rNewWidget.m_aOnValue   = rBtn.OnValue;
11936         if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected )
11937         {
11938             rNewWidget.m_aValue		= rNewWidget.m_aOnValue;
11939             rRadioButton.m_aValue	= rNewWidget.m_aOnValue;
11940         }
11941         createDefaultRadioButtonAppearance( rNewWidget, rBtn );
11942 
11943         // union rect of radio group
11944         Rectangle aRect = rNewWidget.m_aRect;
11945         m_aPages[ nPageNr ].convertRect( aRect );
11946         rRadioButton.m_aRect.Union( aRect );
11947     }
11948     else if( rControl.getType() == PDFWriter::CheckBox )
11949     {
11950         const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
11951         if( rNewWidget.m_nTextStyle == 0 )
11952             rNewWidget.m_nTextStyle =
11953                 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11954 
11955         rNewWidget.m_aValue = OUString::createFromAscii( rBox.Checked ? "Yes" : "Off" );
11956         // create default appearance before m_aRect gets transformed
11957         createDefaultCheckBoxAppearance( rNewWidget, rBox );
11958     }
11959     else if( rControl.getType() == PDFWriter::ListBox )
11960     {
11961         if( rNewWidget.m_nTextStyle == 0 )
11962             rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
11963 
11964         const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
11965         rNewWidget.m_aListEntries	  = rLstBox.Entries;
11966         rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
11967         rNewWidget.m_aValue			  = rLstBox.Text;
11968         if( rLstBox.DropDown )
11969             rNewWidget.m_nFlags |= 0x00020000;
11970         if( rLstBox.Sort )
11971             rNewWidget.m_nFlags |= 0x00080000;
11972         if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 )
11973             rNewWidget.m_nFlags |= 0x00200000;
11974 
11975         createDefaultListBoxAppearance( rNewWidget, rLstBox );
11976     }
11977     else if( rControl.getType() == PDFWriter::ComboBox )
11978     {
11979         if( rNewWidget.m_nTextStyle == 0 )
11980             rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
11981 
11982         const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
11983         rNewWidget.m_aValue			= rBox.Text;
11984         rNewWidget.m_aListEntries	= rBox.Entries;
11985         rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
11986         if( rBox.Sort )
11987             rNewWidget.m_nFlags |= 0x00080000;
11988 
11989         PDFWriter::ListBoxWidget aLBox;
11990         aLBox.Name				= rBox.Name;
11991         aLBox.Description		= rBox.Description;
11992         aLBox.Text				= rBox.Text;
11993         aLBox.TextStyle			= rBox.TextStyle;
11994         aLBox.ReadOnly			= rBox.ReadOnly;
11995         aLBox.Border			= rBox.Border;
11996         aLBox.BorderColor		= rBox.BorderColor;
11997         aLBox.Background		= rBox.Background;
11998         aLBox.BackgroundColor	= rBox.BackgroundColor;
11999         aLBox.TextFont			= rBox.TextFont;
12000         aLBox.TextColor			= rBox.TextColor;
12001         aLBox.DropDown			= true;
12002         aLBox.Sort				= rBox.Sort;
12003         aLBox.MultiSelect		= false;
12004         aLBox.Entries			= rBox.Entries;
12005 
12006         createDefaultListBoxAppearance( rNewWidget, aLBox );
12007     }
12008     else if( rControl.getType() == PDFWriter::Edit )
12009     {
12010         if( rNewWidget.m_nTextStyle == 0 )
12011             rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
12012 
12013         const PDFWriter::EditWidget& rEdit = static_cast<const  PDFWriter::EditWidget&>(rControl);
12014         if( rEdit.MultiLine )
12015         {
12016             rNewWidget.m_nFlags |= 0x00001000;
12017             rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12018         }
12019         if( rEdit.Password )
12020             rNewWidget.m_nFlags |= 0x00002000;
12021         if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 )
12022             rNewWidget.m_nFlags |= 0x00100000;
12023         rNewWidget.m_nMaxLen = rEdit.MaxLen;
12024         rNewWidget.m_aValue = rEdit.Text;
12025 
12026         createDefaultEditAppearance( rNewWidget, rEdit );
12027     }
12028 
12029     // convert to default user space now, since the mapmode may change
12030     // note: create default appearances before m_aRect gets transformed
12031     m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
12032 
12033     // insert widget to page's annotation list
12034     m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
12035 
12036     // mark page as having widgets
12037     m_aPages[ nPageNr ].m_bHasWidgets = true;
12038 
12039     return nNewWidget;
12040 }
12041 
12042 void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl )
12043 {
12044     if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() )
12045         return;
12046 
12047     PDFWidget& rWidget = m_aWidgets[ nControl ];
12048     m_nCurrentControl = nControl;
12049 
12050     SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 );
12051     // back conversion of control rect to current MapMode; necessary because
12052     // MapMode between createControl and beginControlAppearance
12053     // could have changed; therefore the widget rectangle is
12054     // already converted
12055     Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ),
12056                      rWidget.m_aRect.GetSize() );
12057     aBack = lcl_convert( m_aMapMode,
12058                          m_aGraphicsStack.front().m_aMapMode,
12059                          getReferenceDevice(),
12060                          aBack );
12061     beginRedirect( pControlStream, aBack );
12062     writeBuffer( "/Tx BMC\n", 8 );
12063 }
12064 
12065 bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState )
12066 {
12067     bool bRet = false;
12068     if( ! m_aOutputStreams.empty() )
12069         writeBuffer( "\nEMC\n", 5 );
12070     SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect());
12071     if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() )
12072     {
12073         PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ];
12074         OString aState, aStyle;
12075         switch( rWidget.m_eType )
12076         {
12077             case PDFWriter::PushButton:
12078                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12079                 {
12080                     aState = (eState == PDFWriter::Up) ? "N" : "D";
12081                     aStyle = "Standard";
12082                 }
12083                 break;
12084             case PDFWriter::CheckBox:
12085                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12086                 {
12087                     aState = "N";
12088                     aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes";
12089                     /* cf PDFReference 3rd ed. V1.4 p539:
12090                        recommended name for on state is "Yes",
12091                        recommended name for off state is "Off"
12092                      */
12093                 }
12094                 break;
12095             case PDFWriter::RadioButton:
12096                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12097                 {
12098                     aState = "N";
12099                     if( eState == PDFWriter::Up )
12100                         aStyle = "Off";
12101                     else
12102                     {
12103                         OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 );
12104                         appendName( rWidget.m_aOnValue, aBuf );
12105                         aStyle = aBuf.makeStringAndClear();
12106                     }
12107                 }
12108                 break;
12109             case PDFWriter::Edit:
12110                 aState = "N";
12111                 aStyle = "Standard";
12112                 break;
12113             case PDFWriter::ListBox:
12114             case PDFWriter::ComboBox:
12115             case PDFWriter::Hierarchy:
12116                 break;
12117         }
12118         if( aState.getLength() && aStyle.getLength() )
12119         {
12120             // delete eventual existing stream
12121             PDFAppearanceStreams::iterator it =
12122                 rWidget.m_aAppearances[ aState ].find( aStyle );
12123             if( it != rWidget.m_aAppearances[ aState ].end() )
12124                 delete it->second;
12125             rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance;
12126             bRet = true;
12127         }
12128     }
12129 
12130     if( ! bRet )
12131         delete pAppearance;
12132 
12133     m_nCurrentControl = -1;
12134 
12135     return bRet;
12136 }
12137 
12138 void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress )
12139 {
12140     if( pStream )
12141     {
12142         m_aAdditionalStreams.push_back( PDFAddStream() );
12143         PDFAddStream& rStream = m_aAdditionalStreams.back();
12144         rStream.m_aMimeType = rMimeType.Len()
12145                               ? OUString( rMimeType )
12146                               : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) );
12147         rStream.m_pStream = pStream;
12148         rStream.m_bCompress = bCompress;
12149     }
12150 }
12151 
12152 
12153 
12154