xref: /aoo41x/main/vcl/source/gdi/pdfwriter_impl.cxx (revision 3c19fd8c)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #define _USE_MATH_DEFINES
28 #include <math.h>
29 #include <algorithm>
30 
31 #include <tools/urlobj.hxx>
32 
33 #include <pdfwriter_impl.hxx>
34 
35 #include <basegfx/polygon/b2dpolygon.hxx>
36 #include <basegfx/polygon/b2dpolypolygon.hxx>
37 #include <basegfx/polygon/b2dpolygontools.hxx>
38 #include <basegfx/polygon/b2dpolypolygontools.hxx>
39 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
40 #include <basegfx/matrix/b2dhommatrix.hxx>
41 
42 #include <osl/thread.h>
43 #include <osl/file.h>
44 
45 #include <rtl/crc.h>
46 #include <rtl/digest.h>
47 #include <rtl/ustrbuf.hxx>
48 
49 #include <tools/debug.hxx>
50 #include <tools/zcodec.hxx>
51 #include <tools/stream.hxx>
52 
53 #include <i18npool/mslangid.hxx>
54 
55 #include <vcl/virdev.hxx>
56 #include <vcl/bmpacc.hxx>
57 #include <vcl/bitmapex.hxx>
58 #include <vcl/image.hxx>
59 #include <vcl/metric.hxx>
60 #include <vcl/svapp.hxx>
61 #include <vcl/lineinfo.hxx>
62 #include "vcl/cvtgrf.hxx"
63 #include "vcl/strhelper.hxx"
64 
65 #include <fontsubset.hxx>
66 #include <outdev.h>
67 #include <sallayout.hxx>
68 #include <textlayout.hxx>
69 #include <salgdi.hxx>
70 
71 #include <icc/sRGB-IEC61966-2.1.hxx>
72 
73 #include <comphelper/processfactory.hxx>
74 
75 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
76 #include <com/sun/star/util/URL.hpp>
77 
78 #include "cppuhelper/implbase1.hxx"
79 
80 using namespace vcl;
81 using namespace rtl;
82 
83 #if (OSL_DEBUG_LEVEL < 2)
84 #define COMPRESS_PAGES
85 #else
86 #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams
87 #endif
88 
89 #ifdef DO_TEST_PDF
90 class PDFTestOutputStream : public PDFOutputStream
91 {
92     public:
93     virtual ~PDFTestOutputStream();
94     virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream );
95 };
96 
97 PDFTestOutputStream::~PDFTestOutputStream()
98 {
99 }
100 
101 void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream )
102 {
103     OString aStr( "lalala\ntest\ntest\ntest" );
104     com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() );
105     rtl_copyMemory( aData.getArray(), aStr.getStr(), aStr.getLength() );
106     xStream->writeBytes( aData );
107 }
108 
109 // this test code cannot be used to test PDF/A-1 because it forces
110 // control item (widgets) to bypass the structure controlling
111 // the embedding of such elements in actual run
112 void doTestCode()
113 {
114     static const char* pHome = getenv( "HOME"  );
115     rtl::OUString aTestFile( RTL_CONSTASCII_USTRINGPARAM( "file://" ) );
116     aTestFile += rtl::OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 );
117     aTestFile += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/pdf_export_test.pdf" ) );
118 
119     PDFWriter::PDFWriterContext aContext;
120     aContext.URL			= aTestFile;
121     aContext.Version		= PDFWriter::PDF_1_4;
122     aContext.Tagged			= true;
123     aContext.InitialPage    = 2;
124     aContext.DocumentInfo.Title = OUString( RTL_CONSTASCII_USTRINGPARAM( "PDF export test document" ) );
125     aContext.DocumentInfo.Producer = OUString( RTL_CONSTASCII_USTRINGPARAM( "VCL" ) );
126 
127     PDFWriter aWriter( aContext );
128     aWriter.NewPage( 595, 842 );
129     aWriter.BeginStructureElement( PDFWriter::Document );
130     // set duration of 3 sec for first page
131     aWriter.SetAutoAdvanceTime( 3 );
132     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
133 
134     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
135     aWriter.SetLineColor( Color( COL_LIGHTGREEN ) );
136     aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
137 
138     aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
139     aWriter.SetTextColor( Color( COL_BLACK ) );
140     aWriter.SetLineColor( Color( COL_BLACK ) );
141     aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
142 
143     Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
144     aWriter.DrawRect( aRect );
145     aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 1" ) ) );
146     sal_Int32 nFirstLink = aWriter.CreateLink( aRect );
147     PDFNote aNote;
148     aNote.Title = String( RTL_CONSTASCII_USTRINGPARAM( "A small test note" ) );
149     aNote.Contents = String( RTL_CONSTASCII_USTRINGPARAM( "There is no business like show business like no business i know. Everything about it is appealing." ) );
150     aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote );
151 
152     Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
153     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
154     aWriter.DrawRect( aTargetRect );
155     aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest second link" ) ) );
156     sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect );
157 
158     aWriter.BeginStructureElement( PDFWriter::Section );
159     aWriter.BeginStructureElement( PDFWriter::Heading );
160     aWriter.DrawText( Point(4500, 9000), String( RTL_CONSTASCII_USTRINGPARAM( "A small structure test" ) ) );
161     aWriter.EndStructureElement();
162     aWriter.BeginStructureElement( PDFWriter::Paragraph );
163     aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
164     aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline );
165     aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
166                       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." ) ),
167                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
168                       );
169     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." ) ) );
170     aWriter.SetAlternateText( String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." ) ) );
171     aWriter.EndStructureElement();
172     sal_Int32 nLongPara = aWriter.BeginStructureElement( PDFWriter::Paragraph );
173     aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
174     aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
175                       String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph is nothing special either but ends on the next page structurewise" ) ),
176                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
177                       );
178 
179     aWriter.NewPage( 595, 842 );
180     // test AddStream interface
181     aWriter.AddStream( String( RTL_CONSTASCII_USTRINGPARAM( "text/plain" ) ), new PDFTestOutputStream(), true );
182     // set transitional mode
183     aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 );
184     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
185     aWriter.SetTextColor( Color( COL_BLACK ) );
186     aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
187     aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
188                       String( RTL_CONSTASCII_USTRINGPARAM( "Here's where all things come to an end ... well at least the paragaph from the last page." ) ),
189                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
190                       );
191     aWriter.EndStructureElement();
192 
193     aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
194     // disable structure
195     aWriter.BeginStructureElement( PDFWriter::NonStructElement );
196     aWriter.DrawRect( aRect );
197     aWriter.BeginStructureElement( PDFWriter::Paragraph );
198     aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 2" ) ) );
199     sal_Int32 nSecondLink = aWriter.CreateLink( aRect );
200 
201     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
202     aWriter.BeginStructureElement( PDFWriter::ListItem );
203     aWriter.DrawRect( aTargetRect );
204     aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest first link" ) ) );
205     sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect );
206     // enable structure
207     aWriter.EndStructureElement();
208     // add something to the long paragraph as an afterthought
209     sal_Int32 nSaveStruct = aWriter.GetCurrentStructureElement();
210     aWriter.SetCurrentStructureElement( nLongPara );
211     aWriter.DrawText( Rectangle( Point( 4500,4500 ),  Size( 12000, 1000 ) ),
212                       String( RTL_CONSTASCII_USTRINGPARAM( "Add something to the longish paragraph above." ) ),
213                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
214     aWriter.SetCurrentStructureElement( nSaveStruct );
215     aWriter.EndStructureElement();
216     aWriter.EndStructureElement();
217     aWriter.BeginStructureElement( PDFWriter::Figure );
218     aWriter.BeginStructureElement( PDFWriter::Caption );
219     aWriter.DrawText( Point( 4500, 9000 ), String( RTL_CONSTASCII_USTRINGPARAM( "Some drawing stuff inside the structure" ) ) );
220     aWriter.EndStructureElement();
221 
222     // test clipping
223     basegfx::B2DPolyPolygon aClip;
224     basegfx::B2DPolygon aClipPoly;
225     aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) );
226     aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) );
227     aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) );
228     aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) );
229     aClipPoly.setClosed( true );
230     //aClipPoly.flip();
231     aClip.append( aClipPoly );
232 
233     aWriter.Push( PUSH_CLIPREGION | PUSH_FILLCOLOR );
234     aWriter.SetClipRegion( aClip );
235     aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
236     aWriter.MoveClipRegion( 1000, 500 );
237     aWriter.SetFillColor( Color( COL_RED ) );
238     aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
239     aWriter.Pop();
240     // test transparency
241     // draw background
242     Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
243     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
244     aWriter.DrawRect( aTranspRect );
245     aWriter.BeginTransparencyGroup();
246 
247     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
248     aWriter.DrawEllipse( aTranspRect );
249     aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
250     aWriter.DrawText( aTranspRect,
251                       String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
252                       TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
253 
254     aWriter.EndTransparencyGroup( aTranspRect, 50 );
255 
256     // prepare an alpha mask
257     Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
258     BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess();
259     for( int nX = 0; nX < 256; nX++ )
260         for( int nY = 0; nY < 256; nY++ )
261             pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) );
262     aTransMask.ReleaseAccess( pAcc );
263     aTransMask.SetPrefMapMode( MAP_MM );
264     aTransMask.SetPrefSize( Size( 10, 10 ) );
265 
266     aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask );
267 
268     aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
269     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
270     aWriter.DrawRect( aTranspRect );
271     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
272     aWriter.DrawEllipse( aTranspRect );
273     aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
274     aWriter.DrawText( aTranspRect,
275                       String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
276                       TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
277     aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
278     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
279     aWriter.DrawRect( aTranspRect );
280     aWriter.BeginTransparencyGroup();
281     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
282     aWriter.DrawEllipse( aTranspRect );
283     aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
284     aWriter.DrawText( aTranspRect,
285                       String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
286                       TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
287     aWriter.EndTransparencyGroup( aTranspRect, aTransMask );
288 
289     Bitmap aImageBmp( Size( 256, 256 ), 24 );
290     pAcc = aImageBmp.AcquireWriteAccess();
291     pAcc->SetFillColor( Color( 0xff, 0, 0xff ) );
292     pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
293     aImageBmp.ReleaseAccess( pAcc );
294     BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) );
295     aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx );
296 
297 
298     aWriter.EndStructureElement();
299     aWriter.EndStructureElement();
300 
301     LineInfo aLI( LINE_DASH, 3 );
302     aLI.SetDashCount( 2 );
303     aLI.SetDashLen( 50 );
304     aLI.SetDotCount( 2 );
305     aLI.SetDotLen( 25 );
306     aLI.SetDistance( 15 );
307     Point aLIPoints[] = { Point( 4000, 10000 ),
308                           Point( 8000, 12000 ),
309                           Point( 3000, 19000 ) };
310     Polygon aLIPoly( 3, aLIPoints );
311     aWriter.SetLineColor( Color( COL_BLUE ) );
312     aWriter.SetFillColor();
313     aWriter.DrawPolyLine( aLIPoly, aLI );
314 
315     aLI.SetDashCount( 4 );
316     aLIPoly.Move( 1000, 1000 );
317     aWriter.DrawPolyLine( aLIPoly, aLI );
318 
319     aWriter.NewPage( 595, 842 );
320     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
321     Wallpaper aWall( aTransMask );
322     aWall.SetStyle( WALLPAPER_TILE );
323     aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall );
324 
325     aWriter.Push( PUSH_ALL );
326     aWriter.BeginPattern(Rectangle(Point(0,0),Size(2000,1000)));
327     aWriter.SetFillColor( Color( COL_RED ) );
328     aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
329     Point aFillPoints[] = { Point( 1000, 0 ),
330                             Point( 0, 1000 ),
331                             Point( 2000, 1000 ) };
332     aWriter.DrawPolygon( Polygon( 3, aFillPoints ) );
333     aWriter.DrawBitmap( Point( 200, 200 ), Size( 1600, 600 ), aTransMask );
334     aWriter.DrawText( Rectangle( Point( 200, 200 ), Size( 1600, 600 ) ), String( RTL_CONSTASCII_USTRINGPARAM( "Pattern" ) ) );
335     sal_Int32 nPattern = aWriter.EndPattern( SvtGraphicFill::Transform() );
336     aWriter.Pop();
337     Rectangle aPolyRect( Point( 3800, 11200 ), Size( 10200, 6300 ) );
338     aWriter.DrawPolyPolygon( PolyPolygon( Polygon( aPolyRect ) ), nPattern, true );
339     aWriter.SetFillColor();
340     aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
341     aWriter.DrawRect( aPolyRect );
342 
343     aWriter.NewPage( 595, 842 );
344     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
345     aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
346     aWriter.SetTextColor( Color( COL_BLACK ) );
347     aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
348     aWriter.DrawRect( aRect );
349     aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "www.heise.de" ) ) );
350     sal_Int32 nURILink = aWriter.CreateLink( aRect );
351     aWriter.SetLinkURL( nURILink, OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) ) );
352 
353     aWriter.SetLinkDest( nFirstLink, nFirstDest );
354     aWriter.SetLinkDest( nSecondLink, nSecondDest );
355 
356     // include a button
357     PDFWriter::PushButtonWidget aBtn;
358     aBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testButton" ) );
359     aBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test button" ) );
360     aBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "hit me" ) );
361     aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
362     aBtn.Border = aBtn.Background = true;
363     aWriter.CreateControl( aBtn );
364 
365     // include a uri button
366     PDFWriter::PushButtonWidget aUriBtn;
367     aUriBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "wwwButton" ) );
368     aUriBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A URI button" ) );
369     aUriBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to www" ) );
370     aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
371     aUriBtn.Border = aUriBtn.Background = true;
372     aUriBtn.URL = OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) );
373     aWriter.CreateControl( aUriBtn );
374 
375     // include a dest button
376     PDFWriter::PushButtonWidget aDstBtn;
377     aDstBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "destButton" ) );
378     aDstBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A Dest button" ) );
379     aDstBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to paragraph" ) );
380     aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
381     aDstBtn.Border = aDstBtn.Background = true;
382     aDstBtn.Dest = nFirstDest;
383     aWriter.CreateControl( aDstBtn );
384 
385     PDFWriter::CheckBoxWidget aCBox;
386     aCBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox" ) );
387     aCBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test check box" ) );
388     aCBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me" ) );
389     aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
390     aCBox.Checked = true;
391     aCBox.Border = aCBox.Background = false;
392     aWriter.CreateControl( aCBox );
393 
394     PDFWriter::CheckBoxWidget aCBox2;
395     aCBox2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox2" ) );
396     aCBox2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "Another test check box" ) );
397     aCBox2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me right" ) );
398     aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
399     aCBox2.Checked = true;
400     aCBox2.Border = aCBox2.Background = false;
401     aCBox2.ButtonIsLeft = false;
402     aWriter.CreateControl( aCBox2 );
403 
404     PDFWriter::RadioButtonWidget aRB1;
405     aRB1.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_1" ) );
406     aRB1.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 1" ) );
407     aRB1.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Despair" ) );
408     aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
409     aRB1.Selected = true;
410     aRB1.RadioGroup = 1;
411     aRB1.Border = aRB1.Background = true;
412     aRB1.ButtonIsLeft = false;
413     aRB1.BorderColor = Color( COL_LIGHTGREEN );
414     aRB1.BackgroundColor = Color( COL_LIGHTBLUE );
415     aRB1.TextColor = Color( COL_LIGHTRED );
416     aRB1.TextFont = Font( String( RTL_CONSTASCII_USTRINGPARAM( "Courier" ) ), Size( 0, 800 ) );
417     aWriter.CreateControl( aRB1 );
418 
419     PDFWriter::RadioButtonWidget aRB2;
420     aRB2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb2_1" ) );
421     aRB2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 2 button 1" ) );
422     aRB2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Joy" ) );
423     aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
424     aRB2.Selected = true;
425     aRB2.RadioGroup = 2;
426     aWriter.CreateControl( aRB2 );
427 
428     PDFWriter::RadioButtonWidget aRB3;
429     aRB3.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_2" ) );
430     aRB3.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 2" ) );
431     aRB3.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Desperation" ) );
432     aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
433     aRB3.Selected = true;
434     aRB3.RadioGroup = 1;
435     aWriter.CreateControl( aRB3 );
436 
437     PDFWriter::EditWidget aEditBox;
438     aEditBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testEdit" ) );
439     aEditBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test edit field" ) );
440     aEditBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "A little test text" ) );
441     aEditBox.TextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
442     aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
443     aEditBox.MaxLen = 100;
444     aEditBox.Border = aEditBox.Background = true;
445     aEditBox.BorderColor = Color( COL_BLACK );
446     aWriter.CreateControl( aEditBox );
447 
448     // normal list box
449     PDFWriter::ListBoxWidget aLstBox;
450     aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testListBox" ) );
451     aLstBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) );
452     aLstBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "select me" ) );
453     aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
454     aLstBox.Sort = true;
455     aLstBox.MultiSelect = true;
456     aLstBox.Border = aLstBox.Background = true;
457     aLstBox.BorderColor = Color( COL_BLACK );
458     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) ) );
459     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Two" ) ) );
460     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Three" ) ) );
461     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Four" ) ) );
462     aLstBox.SelectedEntries.push_back( 1 );
463     aLstBox.SelectedEntries.push_back( 2 );
464     aWriter.CreateControl( aLstBox );
465 
466     // dropdown list box
467     aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testDropDownListBox" ) );
468     aLstBox.DropDown = true;
469     aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
470     aWriter.CreateControl( aLstBox );
471 
472     // combo box
473     PDFWriter::ComboBoxWidget aComboBox;
474     aComboBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testComboBox" ) );
475     aComboBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "test a combobox" ) );
476     aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Larry" ) ) );
477     aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Curly" ) ) );
478     aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Moe" ) ) );
479     aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
480     aWriter.CreateControl( aComboBox );
481 
482     // test outlines
483     sal_Int32 nPage1OL = aWriter.CreateOutlineItem();
484     aWriter.SetOutlineItemText( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 1" ) ) );
485     aWriter.SetOutlineItemDest( nPage1OL, nSecondDest );
486     aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2" ) ), nSecondDest );
487     aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 revisited" ) ), nSecondDest );
488     aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 again" ) ), nSecondDest );
489     sal_Int32 nPage2OL = aWriter.CreateOutlineItem();
490     aWriter.SetOutlineItemText( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 2" ) ) );
491     aWriter.CreateOutlineItem( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 1" ) ), nFirstDest );
492 
493     aWriter.EndStructureElement(); // close document
494     aWriter.Emit();
495 }
496 #endif
497 
498 static const sal_Int32 nLog10Divisor = 1;
499 static const double fDivisor = 10.0;
500 
501 static inline double pixelToPoint( sal_Int32 px ) { return double(px)/fDivisor; }
502 static inline double pixelToPoint( double px ) { return px/fDivisor; }
503 static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); }
504 
505 const sal_uInt8 PDFWriterImpl::s_nPadString[32] =
506 {
507     0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
508     0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
509 };
510 
511 static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
512 {
513     static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
514                                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
515     rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
516     rBuffer.append( pHexDigits[ nInt & 15 ] );
517 }
518 
519 static void appendName( const OUString& rStr, OStringBuffer& rBuffer )
520 {
521 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
522 // I guess than when reading the #xx sequence it will count for a single character.
523     OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) );
524     const sal_Char* pStr = aStr.getStr();
525     int nLen = aStr.getLength();
526     for( int i = 0; i < nLen; i++ )
527     {
528         /*  #i16920# PDF recommendation: output UTF8, any byte
529          *  outside the interval [33(=ASCII'!');126(=ASCII'~')]
530          *  should be escaped hexadecimal
531          *  for the sake of ghostscript which also reads PDF
532          *  but has a narrower acceptance rate we only pass
533          *  alphanumerics and '-' literally.
534          */
535         if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) ||
536             (pStr[i] >= 'a' && pStr[i] <= 'z' ) ||
537             (pStr[i] >= '0' && pStr[i] <= '9' ) ||
538             pStr[i] == '-' )
539         {
540             rBuffer.append( pStr[i] );
541         }
542         else
543         {
544             rBuffer.append( '#' );
545             appendHex( (sal_Int8)pStr[i], rBuffer );
546         }
547     }
548 }
549 
550 static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer )
551 {
552 //FIXME i59651 see above
553     while( pStr && *pStr )
554     {
555         if( (*pStr >= 'A' && *pStr <= 'Z' ) ||
556             (*pStr >= 'a' && *pStr <= 'z' ) ||
557             (*pStr >= '0' && *pStr <= '9' ) ||
558             *pStr == '-' )
559         {
560             rBuffer.append( *pStr );
561         }
562         else
563         {
564             rBuffer.append( '#' );
565             appendHex( (sal_Int8)*pStr, rBuffer );
566         }
567         pStr++;
568     }
569 }
570 
571 //used only to emit encoded passwords
572 static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer )
573 {
574 	while( nLength )
575 	{
576 		switch( *pStr )
577 		{
578 		case '\n' :
579 			rBuffer.append( "\\n" );
580 			break;
581 		case '\r' :
582 			rBuffer.append( "\\r" );
583 			break;
584 		case '\t' :
585 			rBuffer.append( "\\t" );
586 			break;
587 		case '\b' :
588 			rBuffer.append( "\\b" );
589 			break;
590 		case '\f' :
591 			rBuffer.append( "\\f" );
592 			break;
593 		case '(' :
594 		case ')' :
595 		case '\\' :
596 			rBuffer.append( "\\" );
597 			rBuffer.append( (sal_Char) *pStr );
598 			break;
599 		default:
600 			rBuffer.append( (sal_Char) *pStr );
601 			break;
602 		}
603 		pStr++;
604 		nLength--;
605 	}
606 }
607 
608 /**--->i56629
609  * Convert a string before using it.
610  *
611  * This string conversion function is needed because the destination name
612  * in a PDF file seen through an Internet browser should be
613  * specially crafted, in order to be used directly by the browser.
614  * In this way the fragment part of a hyperlink to a PDF file (e.g. something
615  * as 'test1/test2/a-file.pdf#thefragment) will be (hopefully) interpreted by the
616  * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
617  * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
618  * and go to named destination thefragment using default zoom'.
619  * The conversion is needed because in case of a fragment in the form: Slide%201
620  * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
621  * using this conversion, in both the generated named destinations, fragment and GoToR
622  * destination.
623  *
624  * The names for destinations are name objects and so they don't need to be encrypted
625  * even though they expose the content of PDF file (e.g. guessing the PDF content from the
626  * destination name).
627  *
628  * Fhurter limitation: it is advisable to use standard ASCII characters for
629  * OOo bookmarks.
630 */
631 static void appendDestinationName( const rtl::OUString& rString, OStringBuffer& rBuffer )
632 {
633     const sal_Unicode* pStr = rString.getStr();
634     sal_Int32 nLen = rString.getLength();
635     for( int i = 0; i < nLen; i++ )
636     {
637         sal_Unicode aChar = pStr[i];
638         if( (aChar >= '0' && aChar <= '9' ) ||
639             (aChar >= 'a' && aChar <= 'z' ) ||
640             (aChar >= 'A' && aChar <= 'Z' ) ||
641             aChar == '-' )
642         {
643             rBuffer.append((sal_Char)aChar);
644         }
645         else
646         {
647             sal_Int8 aValueHigh = sal_Int8(aChar >> 8);
648             if(aValueHigh > 0)
649                 appendHex( aValueHigh, rBuffer );
650             appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
651         }
652     }
653 }
654 //<--- i56629
655 
656 static void appendUnicodeTextString( const rtl::OUString& rString, OStringBuffer& rBuffer )
657 {
658 	rBuffer.append( "FEFF" );
659 	const sal_Unicode* pStr = rString.getStr();
660 	sal_Int32 nLen = rString.getLength();
661 	for( int i = 0; i < nLen; i++ )
662 	{
663 		sal_Unicode aChar = pStr[i];
664 		appendHex( (sal_Int8)(aChar >> 8), rBuffer );
665 		appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
666 	}
667 }
668 
669 void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl )
670 {
671     /* #i80258# previously we use appendName here
672        however we need a slightly different coding scheme than the normal
673        name encoding for field names
674     */
675     const OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ? i_rControl.Name : i_rControl.Text;
676     OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) );
677     const sal_Char* pStr = aStr.getStr();
678     int nLen = aStr.getLength();
679 
680     OStringBuffer aBuffer( rName.getLength()+64 );
681     for( int i = 0; i < nLen; i++ )
682     {
683         /*  #i16920# PDF recommendation: output UTF8, any byte
684          *  outside the interval [32(=ASCII' ');126(=ASCII'~')]
685          *  should be escaped hexadecimal
686          */
687         if( (pStr[i] >= 32 && pStr[i] <= 126 ) )
688             aBuffer.append( pStr[i] );
689         else
690         {
691             aBuffer.append( '#' );
692             appendHex( (sal_Int8)pStr[i], aBuffer );
693         }
694     }
695 
696     OString aFullName( aBuffer.makeStringAndClear() );
697 
698     /* #i82785# create hierarchical fields down to the for each dot in i_rName */
699     sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0;
700     OString aPartialName;
701     OString aDomain;
702     do
703     {
704         nLastTokenIndex = nTokenIndex;
705         aPartialName = aFullName.getToken( 0, '.', nTokenIndex );
706         if( nTokenIndex != -1 )
707         {
708             // find or create a hierarchical field
709             // first find the fully qualified name up to this field
710             aDomain = aFullName.copy( 0, nTokenIndex-1 );
711             std::hash_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
712             if( it == m_aFieldNameMap.end() )
713             {
714                  // create new hierarchy field
715                 sal_Int32 nNewWidget = m_aWidgets.size();
716                 m_aWidgets.push_back( PDFWidget() );
717                 m_aWidgets[nNewWidget].m_nObject = createObject();
718                 m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy;
719                 m_aWidgets[nNewWidget].m_aName = aPartialName;
720                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
721                 m_aFieldNameMap[aDomain] = nNewWidget;
722                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
723                 if( nLastTokenIndex > 0 )
724                 {
725                     // this field is not a root field and
726                     // needs to be inserted to its parent
727                     OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) );
728                     it = m_aFieldNameMap.find( aParentDomain );
729                     OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" );
730                     if( it != m_aFieldNameMap.end()  )
731                     {
732                         OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" );
733                         if( it->second < sal_Int32(m_aWidgets.size()) )
734                         {
735                             PDFWidget& rParentField( m_aWidgets[it->second] );
736                             rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject );
737                             rParentField.m_aKidsIndex.push_back( nNewWidget );
738                             m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject;
739                         }
740                     }
741                 }
742             }
743             else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy )
744             {
745                 // this is invalid, someone tries to have a terminal field as parent
746                 // example: a button with the name foo.bar exists and
747                 // another button is named foo.bar.no
748                 // workaround: put the second terminal field as much up in the hierarchy as
749                 // necessary to have a non-terminal field as parent (or none at all)
750                 // since it->second already is terminal, we just need to use its parent
751                 aDomain = OString();
752                 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
753                 if( nLastTokenIndex > 0 )
754                 {
755                     aDomain = aFullName.copy( 0, nLastTokenIndex-1 );
756                     OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() );
757                     aBuf.append( aDomain );
758                     aBuf.append( '.' );
759                     aBuf.append( aPartialName );
760                     aFullName = aBuf.makeStringAndClear();
761                 }
762                 else
763                     aFullName = aPartialName;
764                 break;
765             }
766         }
767     } while( nTokenIndex != -1 );
768 
769     // insert widget into its hierarchy field
770     if( aDomain.getLength() )
771     {
772         std::hash_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
773         if( it != m_aFieldNameMap.end() )
774         {
775             OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" );
776             if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) )
777             {
778                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject;
779                 m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject);
780                 m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex );
781             }
782         }
783     }
784 
785     if( aPartialName.getLength() == 0 )
786     {
787         // how funny, an empty field name
788         if( i_rControl.getType() == PDFWriter::RadioButton )
789         {
790             aPartialName  = "RadioGroup";
791             aPartialName += OString::valueOf( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup );
792         }
793         else
794             aPartialName = OString( "Widget" );
795     }
796 
797     if( ! m_aContext.AllowDuplicateFieldNames )
798     {
799         std::hash_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aFullName );
800 
801         if( it != m_aFieldNameMap.end() ) // not unique
802         {
803             std::hash_map< OString, sal_Int32, OStringHash >::const_iterator check_it;
804             OString aTry;
805             sal_Int32 nTry = 2;
806             do
807             {
808                 OStringBuffer aUnique( aFullName.getLength() + 16 );
809                 aUnique.append( aFullName );
810                 aUnique.append( '_' );
811                 aUnique.append( nTry++ );
812                 aTry = aUnique.makeStringAndClear();
813                 check_it = m_aFieldNameMap.find( aTry );
814             } while( check_it != m_aFieldNameMap.end() );
815             aFullName = aTry;
816             m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
817             aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
818         }
819         else
820             m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
821     }
822 
823     // finally
824     m_aWidgets[i_nWidgetIndex].m_aName = aPartialName;
825 }
826 
827 static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor )
828 {
829     if( nValue < 0 )
830     {
831         rBuffer.append( '-' );
832         nValue = -nValue;
833     }
834     sal_Int32 nFactor = 1, nDiv = nPrecision;
835     while( nDiv-- )
836         nFactor *= 10;
837 
838     sal_Int32 nInt		= nValue / nFactor;
839     rBuffer.append( nInt );
840     if( nFactor > 1 )
841     {
842         sal_Int32 nDecimal	= nValue % nFactor;
843         if( nDecimal )
844         {
845             rBuffer.append( '.' );
846             // omit trailing zeros
847             while( (nDecimal % 10) == 0 )
848                 nDecimal /= 10;
849             rBuffer.append( nDecimal );
850         }
851     }
852 }
853 
854 
855 // appends a double. PDF does not accept exponential format, only fixed point
856 static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 )
857 {
858     bool bNeg = false;
859     if( fValue < 0.0 )
860     {
861         bNeg = true;
862         fValue=-fValue;
863     }
864 
865     sal_Int64 nInt = (sal_Int64)fValue;
866     fValue -= (double)nInt;
867     // optimizing hardware may lead to a value of 1.0 after the subtraction
868     if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision )
869     {
870         nInt++;
871         fValue = 0.0;
872     }
873     sal_Int64 nFrac = 0;
874     if( fValue )
875     {
876         fValue *= pow( 10.0, (double)nPrecision );
877         nFrac = (sal_Int64)fValue;
878     }
879     if( bNeg && ( nInt || nFrac ) )
880         rBuffer.append( '-' );
881     rBuffer.append( nInt );
882     if( nFrac )
883     {
884 		int i;
885         rBuffer.append( '.' );
886 		sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5);
887 		for ( i = 0; ( i < nPrecision ) && nFrac; i++ )
888 		{
889 			sal_Int64 nNumb = nFrac / nBound;
890 			nFrac -= nNumb * nBound;
891 			rBuffer.append( nNumb );
892 			nBound /= 10;
893 		}
894     }
895 }
896 
897 
898 static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey = false )
899 {
900 
901     if( rColor != Color( COL_TRANSPARENT ) )
902     {
903         if( bConvertToGrey )
904         {
905             sal_uInt8 cByte = rColor.GetLuminance();
906             appendDouble( (double)cByte / 255.0, rBuffer );
907         }
908         else
909         {
910             appendDouble( (double)rColor.GetRed() / 255.0, rBuffer );
911             rBuffer.append( ' ' );
912             appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer );
913             rBuffer.append( ' ' );
914             appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer );
915         }
916     }
917 }
918 
919 void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
920 {
921     if( rColor != Color( COL_TRANSPARENT ) )
922     {
923         bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
924         appendColor( rColor, rBuffer, bGrey );
925         rBuffer.append( bGrey ? " G" : " RG" );
926     }
927 }
928 
929 void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
930 {
931     if( rColor != Color( COL_TRANSPARENT ) )
932     {
933         bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
934         appendColor( rColor, rBuffer, bGrey );
935         rBuffer.append( bGrey ? " g" : " rg" );
936     }
937 }
938 
939 // matrix helper class
940 // TODO: use basegfx matrix class instead or derive from it
941 namespace vcl // TODO: use anonymous namespace to keep this class local
942 {
943 /*	for sparse matrices of the form (2D linear transformations)
944  *  f[0] f[1] 0
945  *  f[2] f[3] 0
946  *  f[4] f[5] 1
947  */
948 class Matrix3
949 {
950     double f[6];
951 
952     void set( double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; }
953 public:
954     Matrix3();
955     ~Matrix3() {}
956 
957     void skew( double alpha, double beta );
958     void scale( double sx, double sy );
959     void rotate( double angle );
960     void translate( double tx, double ty );
961     bool invert();
962 
963     void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL );
964 
965     Point transform( const Point& rPoint ) const;
966 };
967 }
968 
969 Matrix3::Matrix3()
970 {
971     // initialize to unity
972     f[0] = 1.0;
973     f[1] = 0.0;
974     f[2] = 0.0;
975     f[3] = 1.0;
976     f[4] = 0.0;
977     f[5] = 0.0;
978 }
979 
980 Point Matrix3::transform( const Point& rOrig ) const
981 {
982     double x = (double)rOrig.X(), y = (double)rOrig.Y();
983     return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) );
984 }
985 
986 void Matrix3::skew( double alpha, double beta )
987 {
988     double fn[6];
989     double tb = tan( beta );
990     fn[0] = f[0] + f[2]*tb;
991     fn[1] = f[1];
992     fn[2] = f[2] + f[3]*tb;
993     fn[3] = f[3];
994     fn[4] = f[4] + f[5]*tb;
995     fn[5] = f[5];
996     if( alpha != 0.0 )
997     {
998         double ta = tan( alpha );
999         fn[1] += f[0]*ta;
1000         fn[3] += f[2]*ta;
1001         fn[5] += f[4]*ta;
1002     }
1003     set( fn );
1004 }
1005 
1006 void Matrix3::scale( double sx, double sy )
1007 {
1008     double fn[6];
1009     fn[0] = sx*f[0];
1010     fn[1] = sy*f[1];
1011     fn[2] = sx*f[2];
1012     fn[3] = sy*f[3];
1013     fn[4] = sx*f[4];
1014     fn[5] = sy*f[5];
1015     set( fn );
1016 }
1017 
1018 void Matrix3::rotate( double angle )
1019 {
1020     double fn[6];
1021     double fSin = sin(angle);
1022     double fCos = cos(angle);
1023     fn[0] = f[0]*fCos - f[1]*fSin;
1024     fn[1] = f[0]*fSin + f[1]*fCos;
1025     fn[2] = f[2]*fCos - f[3]*fSin;
1026     fn[3] = f[2]*fSin + f[3]*fCos;
1027     fn[4] = f[4]*fCos - f[5]*fSin;
1028     fn[5] = f[4]*fSin + f[5]*fCos;
1029     set( fn );
1030 }
1031 
1032 void Matrix3::translate( double tx, double ty )
1033 {
1034     f[4] += tx;
1035     f[5] += ty;
1036 }
1037 
1038 bool Matrix3::invert()
1039 {
1040 	// short circuit trivial cases
1041 	if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 )
1042 	{
1043 		f[4] = -f[4];
1044 		f[5] = -f[5];
1045 		return true;
1046 	}
1047 
1048 	// check determinant
1049 	const double fDet = f[0]*f[3]-f[1]*f[2];
1050 	if( fDet == 0.0 )
1051 		return false;
1052 
1053 	// invert the matrix
1054 	double fn[6];
1055 	fn[0] = +f[3] / fDet;
1056 	fn[1] = -f[1] / fDet;
1057 	fn[2] = -f[2] / fDet;
1058 	fn[3] = +f[0] / fDet;
1059 
1060 	// apply inversion to translation
1061 	fn[4] = -(f[4]*fn[0] + f[5]*fn[2]);
1062 	fn[5] = -(f[4]*fn[1] + f[5]*fn[3]);
1063 
1064 	set( fn );
1065 	return true;
1066 }
1067 
1068 void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack )
1069 {
1070     appendDouble( f[0], rBuffer );
1071     rBuffer.append( ' ' );
1072     appendDouble( f[1], rBuffer );
1073     rBuffer.append( ' ' );
1074     appendDouble( f[2], rBuffer );
1075     rBuffer.append( ' ' );
1076     appendDouble( f[3], rBuffer );
1077     rBuffer.append( ' ' );
1078     rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack );
1079 }
1080 
1081 static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList )
1082 {
1083     if( rList.empty() )
1084         return;
1085     rBuf.append( '/' );
1086     rBuf.append( pPrefix );
1087     rBuf.append( "<<" );
1088     int ni = 0;
1089     for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it )
1090     {
1091         if( it->first.getLength() && it->second > 0 )
1092         {
1093             rBuf.append( '/' );
1094             rBuf.append( it->first );
1095             rBuf.append( ' ' );
1096             rBuf.append( it->second );
1097             rBuf.append( " 0 R" );
1098             if( ((++ni) & 7) == 0 )
1099                 rBuf.append( '\n' );
1100         }
1101     }
1102     rBuf.append( ">>\n" );
1103 }
1104 
1105 void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject )
1106 {
1107     rBuf.append( "<</Font " );
1108     rBuf.append( nFontDictObject );
1109     rBuf.append( " 0 R\n" );
1110     appendResourceMap( rBuf, "XObject", m_aXObjects );
1111     appendResourceMap( rBuf, "ExtGState", m_aExtGStates );
1112     appendResourceMap( rBuf, "Shading", m_aShadings );
1113     appendResourceMap( rBuf, "Pattern", m_aPatterns );
1114     rBuf.append( "/ProcSet[/PDF/Text" );
1115     if( !m_aXObjects.empty() )
1116         rBuf.append( "/ImageC/ImageI/ImageB" );
1117     rBuf.append( "]\n>>\n" );
1118 };
1119 
1120 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
1121         :
1122         m_pWriter( pWriter ),
1123         m_nPageWidth( nPageWidth ),
1124         m_nPageHeight( nPageHeight ),
1125         m_eOrientation( eOrientation ),
1126         m_nPageObject( 0 ),  // invalid object number
1127         m_nPageIndex( -1 ), // invalid index
1128         m_nStreamLengthObject( 0 ),
1129         m_nBeginStreamPos( 0 ),
1130         m_eTransition( PDFWriter::Regular ),
1131         m_nTransTime( 0 ),
1132         m_nDuration( 0 ),
1133         m_bHasWidgets( false )
1134 {
1135     // object ref must be only ever updated in emit()
1136     m_nPageObject = m_pWriter->createObject();
1137 }
1138 
1139 PDFWriterImpl::PDFPage::~PDFPage()
1140 {
1141 }
1142 
1143 void PDFWriterImpl::PDFPage::beginStream()
1144 {
1145 #if OSL_DEBUG_LEVEL > 1
1146     {
1147         OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" );
1148          m_pWriter->emitComment( aLine.getStr() );
1149     }
1150 #endif
1151     m_aStreamObjects.push_back(m_pWriter->createObject());
1152     if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) )
1153         return;
1154 
1155     m_nStreamLengthObject = m_pWriter->createObject();
1156     // write content stream header
1157     OStringBuffer aLine;
1158     aLine.append( m_aStreamObjects.back() );
1159     aLine.append( " 0 obj\n<</Length " );
1160     aLine.append( m_nStreamLengthObject );
1161     aLine.append( " 0 R" );
1162 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1163     aLine.append( "/Filter/FlateDecode" );
1164 #endif
1165     aLine.append( ">>\nstream\n" );
1166     if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) )
1167         return;
1168     if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &m_nBeginStreamPos ) )
1169     {
1170         osl_closeFile( m_pWriter->m_aFile );
1171         m_pWriter->m_bOpen = false;
1172     }
1173 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1174     m_pWriter->beginCompression();
1175 #endif
1176     m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() );
1177 }
1178 
1179 void PDFWriterImpl::PDFPage::endStream()
1180 {
1181 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1182     m_pWriter->endCompression();
1183 #endif
1184     sal_uInt64 nEndStreamPos;
1185     if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &nEndStreamPos ) )
1186     {
1187         osl_closeFile( m_pWriter->m_aFile );
1188         m_pWriter->m_bOpen = false;
1189         return;
1190     }
1191     m_pWriter->disableStreamEncryption();
1192     if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1193         return;
1194     // emit stream length object
1195     if( ! m_pWriter->updateObject( m_nStreamLengthObject ) )
1196         return;
1197     OStringBuffer aLine;
1198     aLine.append( m_nStreamLengthObject );
1199     aLine.append( " 0 obj\n" );
1200     aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) );
1201     aLine.append( "\nendobj\n\n" );
1202     m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1203 }
1204 
1205 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject )
1206 {
1207     // emit page object
1208     if( ! m_pWriter->updateObject( m_nPageObject ) )
1209         return false;
1210     OStringBuffer aLine;
1211 
1212     aLine.append( m_nPageObject );
1213     aLine.append( " 0 obj\n"
1214                   "<</Type/Page/Parent " );
1215     aLine.append( nParentObject );
1216     aLine.append( " 0 R" );
1217     aLine.append( "/Resources " );
1218     aLine.append( m_pWriter->getResourceDictObj() );
1219     aLine.append( " 0 R" );
1220     if( m_nPageWidth && m_nPageHeight )
1221     {
1222         aLine.append( "/MediaBox[0 0 " );
1223         aLine.append( m_nPageWidth );
1224         aLine.append( ' ' );
1225         aLine.append( m_nPageHeight );
1226         aLine.append( "]" );
1227     }
1228     switch( m_eOrientation )
1229     {
1230         case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
1231         case PDFWriter::Seascape:  aLine.append( "/Rotate -90\n" );break;
1232         case PDFWriter::Portrait:  aLine.append( "/Rotate 0\n" );break;
1233 
1234         case PDFWriter::Inherit:
1235         default:
1236             break;
1237     }
1238     int nAnnots = m_aAnnotations.size();
1239     if( nAnnots > 0 )
1240     {
1241         aLine.append( "/Annots[\n" );
1242         for( int i = 0; i < nAnnots; i++ )
1243         {
1244             aLine.append( m_aAnnotations[i] );
1245             aLine.append( " 0 R" );
1246             aLine.append( ((i+1)%15) ? " " : "\n" );
1247         }
1248         aLine.append( "]\n" );
1249     }
1250     #if 0
1251     // FIXME: implement tab order as Structure Tree
1252     if( m_bHasWidgets && m_pWriter->getVersion() >= PDFWriter::PDF_1_5 )
1253         aLine.append( "   /Tabs /S\n" );
1254     #endif
1255     if( m_aMCIDParents.size() > 0 )
1256     {
1257         OStringBuffer aStructParents( 1024 );
1258         aStructParents.append( "[ " );
1259         int nParents = m_aMCIDParents.size();
1260         for( int i = 0; i < nParents; i++ )
1261         {
1262             aStructParents.append( m_aMCIDParents[i] );
1263             aStructParents.append( " 0 R" );
1264             aStructParents.append( ((i%10) == 9) ? "\n" : " " );
1265         }
1266         aStructParents.append( "]" );
1267         m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() );
1268 
1269         aLine.append( "/StructParents " );
1270         aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) );
1271         aLine.append( "\n" );
1272     }
1273     if( m_nDuration > 0 )
1274     {
1275         aLine.append( "/Dur " );
1276         aLine.append( (sal_Int32)m_nDuration );
1277         aLine.append( "\n" );
1278     }
1279     if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 )
1280     {
1281         // transition duration
1282         aLine.append( "/Trans<</D " );
1283         appendDouble( (double)m_nTransTime/1000.0, aLine, 3 );
1284         aLine.append( "\n" );
1285         const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL;
1286         switch( m_eTransition )
1287         {
1288             case PDFWriter::SplitHorizontalInward:
1289                 pStyle = "Split"; pDm = "H"; pM = "I"; break;
1290             case PDFWriter::SplitHorizontalOutward:
1291                 pStyle = "Split"; pDm = "H"; pM = "O"; break;
1292             case PDFWriter::SplitVerticalInward:
1293                 pStyle = "Split"; pDm = "V"; pM = "I"; break;
1294             case PDFWriter::SplitVerticalOutward:
1295                 pStyle = "Split"; pDm = "V"; pM = "O"; break;
1296             case PDFWriter::BlindsHorizontal:
1297                 pStyle = "Blinds"; pDm = "H"; break;
1298             case PDFWriter::BlindsVertical:
1299                 pStyle = "Blinds"; pDm = "V"; break;
1300             case PDFWriter::BoxInward:
1301                 pStyle = "Box"; pM = "I"; break;
1302             case PDFWriter::BoxOutward:
1303                 pStyle = "Box"; pM = "O"; break;
1304             case PDFWriter::WipeLeftToRight:
1305                 pStyle = "Wipe"; pDi = "0"; break;
1306             case PDFWriter::WipeBottomToTop:
1307                 pStyle = "Wipe"; pDi = "90"; break;
1308             case PDFWriter::WipeRightToLeft:
1309                 pStyle = "Wipe"; pDi = "180"; break;
1310             case PDFWriter::WipeTopToBottom:
1311                 pStyle = "Wipe"; pDi = "270"; break;
1312             case PDFWriter::Dissolve:
1313                 pStyle = "Dissolve"; break;
1314             case PDFWriter::GlitterLeftToRight:
1315                 pStyle = "Glitter"; pDi = "0"; break;
1316             case PDFWriter::GlitterTopToBottom:
1317                 pStyle = "Glitter"; pDi = "270"; break;
1318             case PDFWriter::GlitterTopLeftToBottomRight:
1319                 pStyle = "Glitter"; pDi = "315"; break;
1320             case PDFWriter::Regular:
1321                 break;
1322         }
1323         // transition style
1324         if( pStyle )
1325         {
1326             aLine.append( "/S/" );
1327             aLine.append( pStyle );
1328             aLine.append( "\n" );
1329         }
1330         if( pDm )
1331         {
1332             aLine.append( "/Dm/" );
1333             aLine.append( pDm );
1334             aLine.append( "\n" );
1335         }
1336         if( pM )
1337         {
1338             aLine.append( "/M/" );
1339             aLine.append( pM );
1340             aLine.append( "\n" );
1341         }
1342         if( pDi  )
1343         {
1344             aLine.append( "/Di " );
1345             aLine.append( pDi );
1346             aLine.append( "\n" );
1347         }
1348         aLine.append( ">>\n" );
1349     }
1350     if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 )
1351     {
1352         aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1353     }
1354     aLine.append( "/Contents" );
1355     unsigned int nStreamObjects = m_aStreamObjects.size();
1356     if( nStreamObjects > 1 )
1357         aLine.append( '[' );
1358     for( unsigned int i = 0; i < m_aStreamObjects.size(); i++ )
1359     {
1360         aLine.append( ' ' );
1361         aLine.append( m_aStreamObjects[i] );
1362         aLine.append( " 0 R" );
1363     }
1364     if( nStreamObjects > 1 )
1365         aLine.append( ']' );
1366     aLine.append( ">>\nendobj\n\n" );
1367     return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1368 }
1369 
1370 namespace vcl
1371 {
1372 template < class GEOMETRY >
1373 GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
1374 {
1375     GEOMETRY aPoint;
1376     if ( MAP_PIXEL == _rSource.GetMapUnit() )
1377     {
1378         aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
1379     }
1380     else
1381     {
1382         aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
1383     }
1384     return aPoint;
1385 }
1386 }
1387 
1388 void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const
1389 {
1390     if( pOutPoint )
1391     {
1392         Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1393                                    m_pWriter->m_aMapMode,
1394                                    m_pWriter->getReferenceDevice(),
1395                                    rPoint ) );
1396         *pOutPoint = aPoint;
1397     }
1398 
1399     Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1400                                m_pWriter->m_aMapMode,
1401                                m_pWriter->getReferenceDevice(),
1402                                rPoint ) );
1403 
1404     sal_Int32 nValue	= aPoint.X();
1405     if( bNeg )
1406         nValue = -nValue;
1407 
1408     appendFixedInt( nValue, rBuffer );
1409 
1410     rBuffer.append( ' ' );
1411 
1412     nValue		= pointToPixel(getHeight()) - aPoint.Y();
1413     if( bNeg )
1414         nValue = -nValue;
1415 
1416     appendFixedInt( nValue, rBuffer );
1417 }
1418 
1419 void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const
1420 {
1421     double fValue	= pixelToPoint(rPoint.getX());
1422 
1423     appendDouble( fValue, rBuffer, nLog10Divisor );
1424 
1425     rBuffer.append( ' ' );
1426 
1427     fValue		= double(getHeight()) - pixelToPoint(rPoint.getY());
1428 
1429     appendDouble( fValue, rBuffer, nLog10Divisor );
1430 }
1431 
1432 void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const
1433 {
1434     appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer );
1435     rBuffer.append( ' ' );
1436     appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false );
1437     rBuffer.append( ' ' );
1438     appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true );
1439     rBuffer.append( " re" );
1440 }
1441 
1442 void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const
1443 {
1444     Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1445                              m_pWriter->m_aMapMode,
1446                              m_pWriter->getReferenceDevice(),
1447                              rRect.BottomLeft() + Point( 0, 1 )
1448                              );
1449     Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1450                               m_pWriter->m_aMapMode,
1451                               m_pWriter->getReferenceDevice(),
1452                               rRect.GetSize() );
1453     rRect.Left()	= aLL.X();
1454     rRect.Right()	= aLL.X() + aSize.Width();
1455     rRect.Top()		= pointToPixel(getHeight()) - aLL.Y();
1456     rRect.Bottom()	= rRect.Top() + aSize.Height();
1457 }
1458 
1459 void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1460 {
1461     sal_uInt16 nPoints = rPoly.GetSize();
1462     /*
1463      *  #108582# applications do weird things
1464      */
1465     sal_uInt32 nBufLen = rBuffer.getLength();
1466     if( nPoints > 0 )
1467     {
1468         const sal_uInt8* pFlagArray = rPoly.GetConstFlagAry();
1469         appendPoint( rPoly[0], rBuffer );
1470         rBuffer.append( " m\n" );
1471         for( sal_uInt16 i = 1; i < nPoints; i++ )
1472         {
1473             if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 )
1474             {
1475                 // bezier
1476                 DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" );
1477                 appendPoint( rPoly[i], rBuffer );
1478                 rBuffer.append( " " );
1479                 appendPoint( rPoly[i+1], rBuffer );
1480                 rBuffer.append( " " );
1481                 appendPoint( rPoly[i+2], rBuffer );
1482                 rBuffer.append( " c" );
1483                 i += 2; // add additionally consumed points
1484             }
1485             else
1486             {
1487                 // line
1488                 appendPoint( rPoly[i], rBuffer );
1489                 rBuffer.append( " l" );
1490             }
1491             if( (rBuffer.getLength() - nBufLen) > 65 )
1492             {
1493                 rBuffer.append( "\n" );
1494                 nBufLen = rBuffer.getLength();
1495             }
1496             else
1497                 rBuffer.append( " " );
1498         }
1499         if( bClose )
1500             rBuffer.append( "h\n" );
1501     }
1502 }
1503 
1504 void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1505 {
1506     basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1507                                             m_pWriter->m_aMapMode,
1508                                             m_pWriter->getReferenceDevice(),
1509                                             rPoly ) );
1510 
1511     if( basegfx::tools::isRectangle( aPoly ) )
1512     {
1513         basegfx::B2DRange aRange( aPoly.getB2DRange() );
1514         basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() );
1515         appendPixelPoint( aBL, rBuffer );
1516         rBuffer.append( ' ' );
1517         appendMappedLength( aRange.getWidth(), rBuffer, false, NULL, nLog10Divisor );
1518         rBuffer.append( ' ' );
1519         appendMappedLength( aRange.getHeight(), rBuffer, true, NULL, nLog10Divisor );
1520         rBuffer.append( " re\n" );
1521         return;
1522     }
1523     sal_uInt32 nPoints = aPoly.count();
1524     if( nPoints > 0 )
1525     {
1526         sal_uInt32 nBufLen = rBuffer.getLength();
1527         basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) );
1528         appendPixelPoint( aLastPoint, rBuffer );
1529         rBuffer.append( " m\n" );
1530         for( sal_uInt32 i = 1; i <= nPoints; i++ )
1531         {
1532             if( i != nPoints || aPoly.isClosed() )
1533             {
1534                 sal_uInt32 nCurPoint  = i % nPoints;
1535                 sal_uInt32 nLastPoint = i-1;
1536                 basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) );
1537                 if( aPoly.isNextControlPointUsed( nLastPoint ) &&
1538                     aPoly.isPrevControlPointUsed( nCurPoint ) )
1539                 {
1540                     appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1541                     rBuffer.append( ' ' );
1542                     appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1543                     rBuffer.append( ' ' );
1544                     appendPixelPoint( aPoint, rBuffer );
1545                     rBuffer.append( " c" );
1546                 }
1547                 else if( aPoly.isNextControlPointUsed( nLastPoint ) )
1548                 {
1549                     appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1550                     rBuffer.append( ' ' );
1551                     appendPixelPoint( aPoint, rBuffer );
1552                     rBuffer.append( " y" );
1553                 }
1554                 else if( aPoly.isPrevControlPointUsed( nCurPoint ) )
1555                 {
1556                     appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1557                     rBuffer.append( ' ' );
1558                     appendPixelPoint( aPoint, rBuffer );
1559                     rBuffer.append( " v" );
1560                 }
1561                 else
1562                 {
1563                     appendPixelPoint( aPoint, rBuffer );
1564                     rBuffer.append( " l" );
1565                 }
1566                 if( (rBuffer.getLength() - nBufLen) > 65 )
1567                 {
1568                     rBuffer.append( "\n" );
1569                     nBufLen = rBuffer.getLength();
1570                 }
1571                 else
1572                     rBuffer.append( " " );
1573             }
1574         }
1575         if( bClose )
1576             rBuffer.append( "h\n" );
1577     }
1578 }
1579 
1580 void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1581 {
1582     sal_uInt16 nPolygons = rPolyPoly.Count();
1583     for( sal_uInt16 n = 0; n < nPolygons; n++ )
1584         appendPolygon( rPolyPoly[n], rBuffer, bClose );
1585 }
1586 
1587 void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1588 {
1589     sal_uInt32 nPolygons = rPolyPoly.count();
1590     for( sal_uInt32 n = 0; n < nPolygons; n++ )
1591         appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer, bClose );
1592 }
1593 
1594 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1595 {
1596     sal_Int32 nValue = nLength;
1597     if ( nLength < 0 )
1598     {
1599         rBuffer.append( '-' );
1600         nValue = -nLength;
1601     }
1602     Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1603                              m_pWriter->m_aMapMode,
1604                              m_pWriter->getReferenceDevice(),
1605                              Size( nValue, nValue ) ) );
1606     nValue = bVertical ? aSize.Height() : aSize.Width();
1607     if( pOutLength )
1608         *pOutLength = ((nLength < 0 ) ? -nValue : nValue);
1609 
1610     appendFixedInt( nValue, rBuffer, 1 );
1611 }
1612 
1613 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength, sal_Int32 nPrecision ) const
1614 {
1615     Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1616                              m_pWriter->m_aMapMode,
1617                              m_pWriter->getReferenceDevice(),
1618                              Size( 1000, 1000 )	) );
1619     if( pOutLength )
1620         *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0);
1621     fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0);
1622     appendDouble( fLength, rBuffer, nPrecision );
1623 }
1624 
1625 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const
1626 {
1627     if(LINE_DASH == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen())
1628     {
1629         // dashed and non-degraded case, check for implementation limits of dash array
1630         // in PDF reader apps (e.g. acroread)
1631         if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10)
1632         {
1633             return false;
1634         }
1635     }
1636 
1637     if(basegfx::B2DLINEJOIN_NONE != rInfo.GetLineJoin())
1638     {
1639         // LineJoin used, ExtLineInfo required
1640         return false;
1641     }
1642 
1643     if(com::sun::star::drawing::LineCap_BUTT != rInfo.GetLineCap())
1644     {
1645         // LineCap used, ExtLineInfo required
1646         return false;
1647     }
1648 
1649     if( rInfo.GetStyle() == LINE_DASH )
1650     {
1651         rBuffer.append( "[ " );
1652         if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case
1653         {
1654             appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1655             rBuffer.append( ' ' );
1656             appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1657             rBuffer.append( ' ' );
1658         }
1659         else
1660         {
1661             for( int n = 0; n < rInfo.GetDashCount(); n++ )
1662             {
1663                 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1664                 rBuffer.append( ' ' );
1665                 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1666                 rBuffer.append( ' ' );
1667             }
1668             for( int m = 0; m < rInfo.GetDotCount(); m++ )
1669             {
1670                 appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer );
1671                 rBuffer.append( ' ' );
1672                 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1673                 rBuffer.append( ' ' );
1674             }
1675         }
1676         rBuffer.append( "] 0 d\n" );
1677     }
1678 
1679     if( rInfo.GetWidth() > 1 )
1680     {
1681         appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer );
1682         rBuffer.append( " w\n" );
1683     }
1684     else if( rInfo.GetWidth() == 0 )
1685     {
1686         // "pixel" line
1687         appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->ImplGetDPIX()), rBuffer );
1688         rBuffer.append( " w\n" );
1689     }
1690 
1691     return true;
1692 }
1693 
1694 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const
1695 {
1696     if( nWidth <= 0 )
1697         return;
1698     if( nDelta < 1 )
1699         nDelta = 1;
1700 
1701     rBuffer.append( "0 " );
1702     appendMappedLength( nY, rBuffer, true );
1703     rBuffer.append( " m\n" );
1704     for( sal_Int32 n = 0; n < nWidth; )
1705     {
1706         n += nDelta;
1707         appendMappedLength( n, rBuffer, false );
1708         rBuffer.append( ' ' );
1709         appendMappedLength( nDelta+nY, rBuffer, true );
1710         rBuffer.append( ' ' );
1711         n += nDelta;
1712         appendMappedLength( n, rBuffer, false );
1713         rBuffer.append( ' ' );
1714         appendMappedLength( nY, rBuffer, true );
1715         rBuffer.append( " v " );
1716         if( n < nWidth )
1717         {
1718             n += nDelta;
1719             appendMappedLength( n, rBuffer, false );
1720             rBuffer.append( ' ' );
1721             appendMappedLength( nY-nDelta, rBuffer, true );
1722             rBuffer.append( ' ' );
1723             n += nDelta;
1724             appendMappedLength( n, rBuffer, false );
1725             rBuffer.append( ' ' );
1726             appendMappedLength( nY, rBuffer, true );
1727             rBuffer.append( " v\n" );
1728         }
1729     }
1730     rBuffer.append( "S\n" );
1731 }
1732 
1733 /*
1734  *  class PDFWriterImpl
1735  */
1736 
1737  PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext,
1738                                const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc,
1739                                PDFWriter& i_rOuterFace)
1740         :
1741         m_pReferenceDevice( NULL ),
1742         m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ),
1743         m_nCurrentStructElement( 0 ),
1744         m_bEmitStructure( true ),
1745         m_bNewMCID( false ),
1746         m_nCurrentControl( -1 ),
1747         m_bEmbedStandardFonts( false ),
1748         m_nNextFID( 1 ),
1749         m_nInheritedPageWidth( 595 ),  // default A4
1750         m_nInheritedPageHeight( 842 ), // default A4
1751         m_eInheritedOrientation( PDFWriter::Portrait ),
1752         m_nCurrentPage( -1 ),
1753         m_nResourceDict( -1 ),
1754         m_nFontDictObject( -1 ),
1755         m_pCodec( NULL ),
1756         m_aDocDigest( rtl_digest_createMD5() ),
1757 		m_aCipher( (rtlCipher)NULL ),
1758 		m_aDigest( NULL ),
1759 		m_bEncryptThisStream( false ),
1760 		m_pEncryptionBuffer( NULL ),
1761 		m_nEncryptionBufferSize( 0 ),
1762         m_bIsPDF_A1( false ),
1763         m_rOuterFace( i_rOuterFace )
1764 {
1765 #ifdef DO_TEST_PDF
1766     static bool bOnce = true;
1767     if( bOnce )
1768     {
1769         bOnce = false;
1770         doTestCode();
1771     }
1772 #endif
1773     m_aContext = rContext;
1774     m_aStructure.push_back( PDFStructureElement() );
1775     m_aStructure[0].m_nOwnElement		= 0;
1776     m_aStructure[0].m_nParentElement	= 0;
1777 
1778     Font aFont;
1779     aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
1780     aFont.SetSize( Size( 0, 12 ) );
1781 
1782     GraphicsState aState;
1783     aState.m_aMapMode		= m_aMapMode;
1784     aState.m_aFont			= aFont;
1785     m_aGraphicsStack.push_front( aState );
1786 
1787     oslFileError  aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
1788     if( aError != osl_File_E_None )
1789     {
1790         if( aError == osl_File_E_EXIST )
1791         {
1792             aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write );
1793             if( aError == osl_File_E_None )
1794                 aError = osl_setFileSize( m_aFile, 0 );
1795         }
1796     }
1797     if( aError != osl_File_E_None )
1798         return;
1799 
1800     m_bOpen = true;
1801 
1802     // setup DocInfo
1803     setupDocInfo();
1804 
1805     /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1806 	m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1807 	m_aDigest = rtl_digest_createMD5();
1808 
1809 	/* the size of the Codec default maximum */
1810 	checkEncryptionBufferSize( 0x4000 );
1811 
1812 	if( xEnc.is() )
1813 	    prepareEncryption( xEnc );
1814 
1815 	if( m_aContext.Encryption.Encrypt() )
1816 	{
1817 	    // sanity check
1818 	    if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE ||
1819 	        m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE ||
1820 	        m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH
1821 	       )
1822 	    {
1823 	        // the field lengths are invalid ? This was not setup by initEncryption.
1824 	        // do not encrypt after all
1825 	        m_aContext.Encryption.OValue.clear();
1826 	        m_aContext.Encryption.UValue.clear();
1827 	        OSL_ENSURE( 0, "encryption data failed sanity check, encryption disabled" );
1828 	    }
1829 	    else // setup key lengths
1830 	        m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength );
1831 	}
1832 
1833     // write header
1834     OStringBuffer aBuffer( 20 );
1835     aBuffer.append( "%PDF-" );
1836     switch( m_aContext.Version )
1837     {
1838         case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break;
1839         case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break;
1840         case PDFWriter::PDF_A_1:
1841         default:
1842         case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break;
1843         case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break;
1844     }
1845     // append something binary as comment (suggested in PDF Reference)
1846     aBuffer.append( "\n%äüöß\n" );
1847     if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) )
1848     {
1849         osl_closeFile( m_aFile );
1850         m_bOpen = false;
1851         return;
1852     }
1853 
1854     // insert outline root
1855     m_aOutline.push_back( PDFOutlineEntry() );
1856 
1857     m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1);
1858     if( m_bIsPDF_A1 )
1859         m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour
1860 
1861     m_bEmbedStandardFonts = m_aContext.EmbedStandardFonts;
1862 }
1863 
1864 PDFWriterImpl::~PDFWriterImpl()
1865 {
1866     if( m_aDocDigest )
1867         rtl_digest_destroyMD5( m_aDocDigest );
1868     delete static_cast<VirtualDevice*>(m_pReferenceDevice);
1869 
1870 	if( m_aCipher )
1871 		rtl_cipher_destroyARCFOUR( m_aCipher );
1872 	if( m_aDigest )
1873 		rtl_digest_destroyMD5( m_aDigest );
1874 
1875     rtl_freeMemory( m_pEncryptionBuffer );
1876 }
1877 
1878 void PDFWriterImpl::setupDocInfo()
1879 {
1880     std::vector< sal_uInt8 > aId;
1881     computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString );
1882     if( m_aContext.Encryption.DocumentIdentifier.empty() )
1883         m_aContext.Encryption.DocumentIdentifier = aId;
1884 }
1885 
1886 void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier,
1887                                                const vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
1888                                                rtl::OString& o_rCString1,
1889                                                rtl::OString& o_rCString2
1890                                                )
1891 {
1892     o_rIdentifier.clear();
1893 
1894     //build the document id
1895 	rtl::OString aInfoValuesOut;
1896 	OStringBuffer aID( 1024 );
1897 	if( i_rDocInfo.Title.Len() )
1898 		appendUnicodeTextString( i_rDocInfo.Title, aID );
1899 	if( i_rDocInfo.Author.Len() )
1900 		appendUnicodeTextString( i_rDocInfo.Author, aID );
1901 	if( i_rDocInfo.Subject.Len() )
1902 		appendUnicodeTextString( i_rDocInfo.Subject, aID );
1903 	if( i_rDocInfo.Keywords.Len() )
1904 		appendUnicodeTextString( i_rDocInfo.Keywords, aID );
1905 	if( i_rDocInfo.Creator.Len() )
1906 		appendUnicodeTextString( i_rDocInfo.Creator, aID );
1907 	if( i_rDocInfo.Producer.Len() )
1908 		appendUnicodeTextString( i_rDocInfo.Producer, aID );
1909 
1910 	TimeValue aTVal, aGMT;
1911 	oslDateTime aDT;
1912 	osl_getSystemTime( &aGMT );
1913 	osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
1914 	osl_getDateTimeFromTimeValue( &aTVal, &aDT );
1915 	rtl::OStringBuffer aCreationDateString(64), aCreationMetaDateString(64);
1916 	aCreationDateString.append( "D:" );
1917 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1918 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1919 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1920 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1921 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1922 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1923 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1924 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1925 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1926 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1927 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1928 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1929 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1930 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1931 
1932 	//--> i59651, we fill the Metadata date string as well, if PDF/A is requested
1933     // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1934     // local time zone offset UTC only, whereas Acrobat 8 seems
1935     // to use the localtime notation only
1936     // according to a raccomandation in XMP Specification (Jan 2004, page 75)
1937     // the Acrobat way seems the right approach
1938     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1939     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1940     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1941     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1942     aCreationMetaDateString.append( "-" );
1943     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1944     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1945     aCreationMetaDateString.append( "-" );
1946     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1947     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1948     aCreationMetaDateString.append( "T" );
1949     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1950     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1951     aCreationMetaDateString.append( ":" );
1952     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1953     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1954     aCreationMetaDateString.append( ":" );
1955     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1956     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1957 
1958 	sal_uInt32 nDelta = 0;
1959 	if( aGMT.Seconds > aTVal.Seconds )
1960 	{
1961 		aCreationDateString.append( "-" );
1962 		nDelta = aGMT.Seconds-aTVal.Seconds;
1963 		aCreationMetaDateString.append( "-" );
1964 	}
1965 	else if( aGMT.Seconds < aTVal.Seconds )
1966 	{
1967 		aCreationDateString.append( "+" );
1968 		nDelta = aTVal.Seconds-aGMT.Seconds;
1969 		aCreationMetaDateString.append( "+" );
1970 	}
1971 	else
1972     {
1973 		aCreationDateString.append( "Z" );
1974 		aCreationMetaDateString.append( "Z" );
1975 
1976     }
1977 	if( nDelta )
1978 	{
1979 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1980 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1981 		aCreationDateString.append( "'" );
1982 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1983 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1984 
1985 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1986 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1987 		aCreationMetaDateString.append( ":" );
1988 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1989 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1990 	}
1991 	aCreationDateString.append( "'" );
1992 	aID.append( aCreationDateString.getStr(), aCreationDateString.getLength() );
1993 
1994 	aInfoValuesOut = aID.makeStringAndClear();
1995 	o_rCString1 = aCreationDateString.makeStringAndClear();
1996 	o_rCString2 = aCreationMetaDateString.makeStringAndClear();
1997 
1998 	rtlDigest aDigest = rtl_digest_createMD5();
1999 	OSL_ENSURE( aDigest != NULL, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" );
2000 	if( aDigest )
2001 	{
2002 		rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) );
2003 		if( nError == rtl_Digest_E_None )
2004 			nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() );
2005 		if( nError == rtl_Digest_E_None )
2006 		{
2007 		    o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 );
2008 		    //the binary form of the doc id is needed for encryption stuff
2009 			rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 );
2010 		}
2011 	}
2012 }
2013 
2014 /* i12626 methods */
2015 /*
2016 check if the Unicode string must be encrypted or not, perform the requested task,
2017 append the string as unicode hex, encrypted if needed
2018  */
2019 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2020 {
2021 	rOutBuffer.append( "<" );
2022 	if( m_aContext.Encryption.Encrypt() )
2023 	{
2024 		const sal_Unicode* pStr = rInString.getStr();
2025 		sal_Int32 nLen = rInString.getLength();
2026 //prepare a unicode string, encrypt it
2027 		if( checkEncryptionBufferSize( nLen*2 ) )
2028 		{
2029 			enableStringEncryption( nInObjectNumber );
2030 			register sal_uInt8 *pCopy = m_pEncryptionBuffer;
2031 			sal_Int32 nChars = 2;
2032 			*pCopy++ = 0xFE;
2033 			*pCopy++ = 0xFF;
2034 // we need to prepare a byte stream from the unicode string buffer
2035 			for( register int i = 0; i < nLen; i++ )
2036 			{
2037 				register sal_Unicode aUnChar = pStr[i];
2038 				*pCopy++ = (sal_uInt8)( aUnChar >> 8 );
2039 				*pCopy++ = (sal_uInt8)( aUnChar & 255 );
2040 				nChars += 2;
2041 			}
2042 //encrypt in place
2043 			rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars );
2044 //now append, hexadecimal (appendHex), the encrypted result
2045 			for(register int i = 0; i < nChars; i++)
2046 				appendHex( m_pEncryptionBuffer[i], rOutBuffer );
2047 		}
2048 	}
2049 	else
2050 		appendUnicodeTextString( rInString, rOutBuffer );
2051 	rOutBuffer.append( ">" );
2052 }
2053 
2054 inline void PDFWriterImpl::appendLiteralStringEncrypt( rtl::OStringBuffer& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
2055 {
2056 	rOutBuffer.append( "(" );
2057 	sal_Int32 nChars = rInString.getLength();
2058 //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
2059 	if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) )
2060 	{
2061 //encrypt the string in a buffer, then append it
2062 		enableStringEncryption( nInObjectNumber );
2063 		rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars );
2064 		appendLiteralString( (const sal_Char*)m_pEncryptionBuffer, nChars, rOutBuffer );
2065 	}
2066 	else
2067         appendLiteralString( rInString.getStr(), nChars , rOutBuffer );
2068 	rOutBuffer.append( ")" );
2069 }
2070 
2071 inline void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
2072 {
2073 	rtl::OStringBuffer aBufferString( rInString );
2074 	appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2075 }
2076 
2077 void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc )
2078 {
2079 	rtl::OString aBufferString( rtl::OUStringToOString( rInString, nEnc ) );
2080 	sal_Int32 nLen = aBufferString.getLength();
2081 	rtl::OStringBuffer aBuf( nLen );
2082 	const sal_Char* pT = aBufferString.getStr();
2083 
2084 	for( sal_Int32 i = 0; i < nLen; i++, pT++ )
2085 	{
2086 	    if( (*pT & 0x80) == 0 )
2087 	        aBuf.append( *pT );
2088 	    else
2089 	    {
2090 	        aBuf.append( '<' );
2091 	        appendHex( *pT, aBuf );
2092 	        aBuf.append( '>' );
2093 	    }
2094 	}
2095 	aBufferString = aBuf.makeStringAndClear();
2096 	appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2097 }
2098 
2099 /* end i12626 methods */
2100 
2101 void PDFWriterImpl::emitComment( const char* pComment )
2102 {
2103     OStringBuffer aLine( 64 );
2104     aLine.append( "% " );
2105     aLine.append( (const sal_Char*)pComment );
2106     aLine.append( "\n" );
2107     writeBuffer( aLine.getStr(), aLine.getLength() );
2108 }
2109 
2110 bool PDFWriterImpl::compressStream( SvMemoryStream* pStream )
2111 {
2112 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2113     pStream->Seek( STREAM_SEEK_TO_END );
2114     sal_uLong nEndPos = pStream->Tell();
2115     pStream->Seek( STREAM_SEEK_TO_BEGIN );
2116     ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
2117     SvMemoryStream aStream;
2118     pCodec->BeginCompression();
2119     pCodec->Write( aStream, (const sal_uInt8*)pStream->GetData(), nEndPos );
2120     pCodec->EndCompression();
2121     delete pCodec;
2122     nEndPos = aStream.Tell();
2123     pStream->Seek( STREAM_SEEK_TO_BEGIN );
2124     aStream.Seek( STREAM_SEEK_TO_BEGIN );
2125     pStream->SetStreamSize( nEndPos );
2126     pStream->Write( aStream.GetData(), nEndPos );
2127     return true;
2128 #else
2129     (void)pStream;
2130     return false;
2131 #endif
2132 }
2133 
2134 void PDFWriterImpl::beginCompression()
2135 {
2136 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2137     m_pCodec = new ZCodec( 0x4000, 0x4000 );
2138     m_pMemStream = new SvMemoryStream();
2139     m_pCodec->BeginCompression();
2140 #endif
2141 }
2142 
2143 void PDFWriterImpl::endCompression()
2144 {
2145 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2146     if( m_pCodec )
2147     {
2148         m_pCodec->EndCompression();
2149         delete m_pCodec;
2150         m_pCodec = NULL;
2151         sal_uInt64 nLen = m_pMemStream->Tell();
2152         m_pMemStream->Seek( 0 );
2153         writeBuffer( m_pMemStream->GetData(), nLen );
2154         delete m_pMemStream;
2155         m_pMemStream = NULL;
2156     }
2157 #endif
2158 }
2159 
2160 bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes )
2161 {
2162     if( ! m_bOpen ) // we are already down the drain
2163         return false;
2164 
2165     if( ! nBytes ) // huh ?
2166         return true;
2167 
2168     if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2169     {
2170         m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END );
2171         m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) );
2172         return true;
2173     }
2174 
2175     sal_uInt64 nWritten;
2176     if( m_pCodec )
2177     {
2178         m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes );
2179         nWritten = nBytes;
2180     }
2181     else
2182     {
2183 		sal_Bool  buffOK = sal_True;
2184 		if( m_bEncryptThisStream )
2185 		{
2186 /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
2187 			if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) != sal_False )
2188 				rtl_cipher_encodeARCFOUR( m_aCipher,
2189                                           (sal_uInt8*)pBuffer, static_cast<sal_Size>(nBytes),
2190                                           m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) );
2191 		}
2192 
2193         const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer  : pBuffer;
2194         if( m_aDocDigest )
2195             rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) );
2196 
2197         if( osl_writeFile( m_aFile,
2198                            pWriteBuffer,
2199                            nBytes, &nWritten ) != osl_File_E_None )
2200             nWritten = 0;
2201 
2202         if( nWritten != nBytes )
2203         {
2204             osl_closeFile( m_aFile );
2205             m_bOpen = false;
2206         }
2207     }
2208 
2209     return nWritten == nBytes;
2210 }
2211 
2212 OutputDevice* PDFWriterImpl::getReferenceDevice()
2213 {
2214     if( ! m_pReferenceDevice )
2215     {
2216         VirtualDevice*  pVDev = new VirtualDevice( 0 );
2217 
2218         m_pReferenceDevice = pVDev;
2219 
2220         if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 )
2221             pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 );
2222         else
2223             pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy );
2224 
2225         pVDev->SetOutputSizePixel( Size( 640, 480 ) );
2226         pVDev->SetMapMode( MAP_MM );
2227 
2228         m_pReferenceDevice->mpPDFWriter = this;
2229         m_pReferenceDevice->ImplUpdateFontData( sal_True );
2230     }
2231     return m_pReferenceDevice;
2232 }
2233 
2234 class ImplPdfBuiltinFontData : public ImplFontData
2235 {
2236 private:
2237     const PDFWriterImpl::BuiltinFont& mrBuiltin;
2238 
2239 public:
2240     enum {PDF_FONT_MAGIC = 0xBDFF0A1C };
2241                                         ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& );
2242     const PDFWriterImpl::BuiltinFont*   GetBuiltinFont() const  { return &mrBuiltin; }
2243 
2244     virtual ImplFontData*               Clone() const { return new ImplPdfBuiltinFontData(*this); }
2245     virtual ImplFontEntry*              CreateFontInstance( ImplFontSelectData& ) const;
2246     virtual sal_IntPtr                  GetFontId() const { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); }
2247 };
2248 
2249 inline const ImplPdfBuiltinFontData* GetPdfFontData( const ImplFontData* pFontData )
2250 {
2251     const ImplPdfBuiltinFontData* pFD = NULL;
2252     if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) )
2253         pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData );
2254     return pFD;
2255 }
2256 
2257 static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin )
2258 {
2259     ImplDevFontAttributes aDFA;
2260     aDFA.maName         = String::CreateFromAscii( rBuiltin.m_pName );
2261     aDFA.maStyleName    = String::CreateFromAscii( rBuiltin.m_pStyleName );
2262     aDFA.meFamily       = rBuiltin.m_eFamily;
2263     aDFA.mbSymbolFlag   = (rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 );
2264     aDFA.mePitch        = rBuiltin.m_ePitch;
2265     aDFA.meWeight       = rBuiltin.m_eWeight;
2266     aDFA.meItalic       = rBuiltin.m_eItalic;
2267     aDFA.meWidthType    = rBuiltin.m_eWidthType;
2268 
2269     aDFA.mbOrientation  = true;
2270     aDFA.mbDevice       = true;
2271     aDFA.mnQuality      = 50000;
2272     aDFA.mbSubsettable  = false;
2273     aDFA.mbEmbeddable   = false;
2274     return aDFA;
2275 }
2276 
2277 ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin )
2278 :   ImplFontData( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ),
2279     mrBuiltin( rBuiltin )
2280 {}
2281 
2282 ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
2283 {
2284     ImplFontEntry* pEntry = new ImplFontEntry( rFSD );
2285     return pEntry;
2286 }
2287 
2288 ImplDevFontList* PDFWriterImpl::filterDevFontList( ImplDevFontList* pFontList )
2289 {
2290     DBG_ASSERT( m_aSubsets.size() == 0, "Fonts changing during PDF generation, document will be invalid" );
2291     ImplDevFontList* pFiltered = pFontList->Clone( true, true );
2292 
2293     // append the PDF builtin fonts
2294     if( !m_bIsPDF_A1 && !m_bEmbedStandardFonts)
2295         for( unsigned int i = 0; i < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); i++ )
2296         {
2297             ImplFontData* pNewData = new ImplPdfBuiltinFontData( m_aBuiltinFonts[i] );
2298             pFiltered->Add( pNewData );
2299         }
2300     return pFiltered;
2301 }
2302 
2303 bool PDFWriterImpl::isBuiltinFont( const ImplFontData* pFont ) const
2304 {
2305     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
2306     return (pFD != NULL);
2307 }
2308 
2309 void PDFWriterImpl::getFontMetric( ImplFontSelectData* pSelect, ImplFontMetricData* pMetric ) const
2310 {
2311     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2312     if( !pFD )
2313         return;
2314     const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2315 
2316     pMetric->mnOrientation	= sal::static_int_cast<short>(pSelect->mnOrientation);
2317     pMetric->meFamily		= pBuiltinFont->m_eFamily;
2318     pMetric->mePitch		= pBuiltinFont->m_ePitch;
2319     pMetric->meWeight		= pBuiltinFont->m_eWeight;
2320     pMetric->meItalic		= pBuiltinFont->m_eItalic;
2321     pMetric->mbSymbolFlag	= pFD->IsSymbolFont();
2322     pMetric->mnWidth		= pSelect->mnHeight;
2323     pMetric->mnAscent		= ( pSelect->mnHeight * +pBuiltinFont->m_nAscent + 500 ) / 1000;
2324     pMetric->mnDescent		= ( pSelect->mnHeight * -pBuiltinFont->m_nDescent + 500 ) / 1000;
2325     pMetric->mnIntLeading	= 0;
2326     pMetric->mnExtLeading	= 0;
2327     pMetric->mnSlant		= 0;
2328     pMetric->mbScalableFont	= true;
2329     pMetric->mbDevice		= true;
2330 }
2331 
2332 // -----------------------------------------------------------------------
2333 
2334 namespace vcl {
2335 
2336 class PDFSalLayout : public GenericSalLayout
2337 {
2338     PDFWriterImpl&  mrPDFWriterImpl;
2339     const PDFWriterImpl::BuiltinFont& mrBuiltinFont;
2340     bool            mbIsSymbolFont;
2341     long            mnPixelPerEM;
2342     String          maOrigText;
2343 
2344 public:
2345                     PDFSalLayout( PDFWriterImpl&,
2346                                   const PDFWriterImpl::BuiltinFont&,
2347                                   long nPixelPerEM, int nOrientation );
2348 
2349     void            SetText( const String& rText )  { maOrigText = rText; }
2350     virtual bool    LayoutText( ImplLayoutArgs& );
2351     virtual void    InitFont() const;
2352     virtual void    DrawText( SalGraphics& ) const;
2353 };
2354 
2355 }
2356 
2357 // -----------------------------------------------------------------------
2358 
2359 PDFSalLayout::PDFSalLayout( PDFWriterImpl& rPDFWriterImpl,
2360     const PDFWriterImpl::BuiltinFont& rBuiltinFont,
2361     long nPixelPerEM, int nOrientation )
2362 :   mrPDFWriterImpl( rPDFWriterImpl ),
2363     mrBuiltinFont( rBuiltinFont ),
2364     mnPixelPerEM( nPixelPerEM )
2365 {
2366     mbIsSymbolFont = (rBuiltinFont.m_eCharSet != RTL_TEXTENCODING_MS_1252);
2367     SetOrientation( nOrientation );
2368 }
2369 
2370 // -----------------------------------------------------------------------
2371 
2372 bool PDFSalLayout::LayoutText( ImplLayoutArgs& rArgs )
2373 {
2374     const String aText( rArgs.mpStr+rArgs.mnMinCharPos, sal::static_int_cast<xub_StrLen>(rArgs.mnEndCharPos-rArgs.mnMinCharPos) );
2375     SetText( aText );
2376     SetUnitsPerPixel( 1000 );
2377 
2378     rtl_UnicodeToTextConverter aConv = rtl_createTextToUnicodeConverter( mrBuiltinFont.m_eCharSet );
2379 
2380     Point aNewPos( 0, 0 );
2381     bool bRightToLeft;
2382     for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); )
2383     {
2384         // TODO: handle unicode surrogates
2385         // on the other hand the PDF builtin fonts don't support them anyway
2386         sal_Unicode cChar = rArgs.mpStr[ nCharPos ];
2387         if( bRightToLeft )
2388             cChar = static_cast<sal_Unicode>(GetMirroredChar( cChar ));
2389 
2390         if( 1 ) // TODO: shortcut for ASCII?
2391         {
2392             sal_Char aBuf[4];
2393             sal_uInt32 nInfo;
2394             sal_Size nSrcCvtChars;
2395 
2396             sal_Size nConv = rtl_convertUnicodeToText( aConv,
2397                                                        NULL,
2398                                                        &cChar, 1,
2399                                                        aBuf, sizeof(aBuf)/sizeof(*aBuf),
2400                                                        RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR,
2401                                                        &nInfo, &nSrcCvtChars );
2402             // check whether conversion was possible
2403             // else fallback font is needed as the standard fonts
2404             // are handled via WinAnsi encoding
2405             if( nConv > 0 )
2406                 cChar = ((sal_Unicode)aBuf[0]) & 0x00ff;
2407         }
2408         if( cChar & 0xff00 )
2409         {
2410             cChar = 0;   // NotDef glyph
2411             rArgs.NeedFallback( nCharPos, bRightToLeft );
2412         }
2413 
2414         long nGlyphWidth = (long)mrBuiltinFont.m_aWidths[cChar] * mnPixelPerEM;
2415         long nGlyphFlags = 0; // builtin fonts don't have diacritic glyphs
2416         if( bRightToLeft )
2417             nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
2418         // TODO: get kerning from builtin fonts
2419         GlyphItem aGI( nCharPos, cChar, aNewPos, nGlyphFlags, nGlyphWidth );
2420         AppendGlyph( aGI );
2421 
2422         aNewPos.X() += nGlyphWidth;
2423     }
2424 
2425     rtl_destroyUnicodeToTextConverter( aConv );
2426 
2427     return true;
2428 }
2429 
2430 // -----------------------------------------------------------------------
2431 
2432 void PDFSalLayout::InitFont() const
2433 {
2434     // TODO: recreate font with all its attributes
2435 }
2436 
2437 // -----------------------------------------------------------------------
2438 
2439 void PDFSalLayout::DrawText( SalGraphics& ) const
2440 {
2441     mrPDFWriterImpl.drawLayout( *const_cast<PDFSalLayout*>(this), maOrigText, true );
2442 }
2443 
2444 // -----------------------------------------------------------------------
2445 
2446 SalLayout* PDFWriterImpl::GetTextLayout( ImplLayoutArgs& rArgs, ImplFontSelectData* pSelect )
2447 {
2448     DBG_ASSERT( (pSelect->mpFontData != NULL),
2449         "PDFWriterImpl::GetTextLayout mpFontData is NULL" );
2450 
2451     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2452     if( !pFD )
2453         return NULL;
2454     const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2455 
2456     long nPixelPerEM = pSelect->mnWidth ? pSelect->mnWidth : pSelect->mnHeight;
2457     int nOrientation = pSelect->mnOrientation;
2458     PDFSalLayout* pLayout = new PDFSalLayout( *this, *pBuiltinFont, nPixelPerEM, nOrientation );
2459     pLayout->SetText( rArgs.mpStr );
2460     return pLayout;
2461 }
2462 
2463 sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
2464 {
2465     endPage();
2466     m_nCurrentPage = m_aPages.size();
2467     m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) );
2468     m_aPages.back().m_nPageIndex = m_nCurrentPage;
2469     m_aPages.back().beginStream();
2470 
2471     // setup global graphics state
2472     // linewidth is "1 pixel" by default
2473     OStringBuffer aBuf( 16 );
2474     appendDouble( 72.0/double(getReferenceDevice()->ImplGetDPIX()), aBuf );
2475     aBuf.append( " w\n" );
2476     writeBuffer( aBuf.getStr(), aBuf.getLength() );
2477 
2478     return m_nCurrentPage;
2479 }
2480 
2481 void PDFWriterImpl::endPage()
2482 {
2483     if( m_aPages.begin() != m_aPages.end() )
2484     {
2485         // close eventual MC sequence
2486         endStructureElementMCSeq();
2487 
2488         // sanity check
2489         if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2490         {
2491             DBG_ERROR( "redirection across pages !!!" );
2492             m_aOutputStreams.clear(); // leak !
2493             m_aMapMode.SetOrigin( Point() );
2494         }
2495 
2496         m_aGraphicsStack.clear();
2497         m_aGraphicsStack.push_back( GraphicsState() );
2498 
2499         // this should pop the PDF graphics stack if necessary
2500         updateGraphicsState();
2501 
2502         m_aPages.back().endStream();
2503 
2504         // reset the default font
2505         Font aFont;
2506         aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
2507         aFont.SetSize( Size( 0, 12 ) );
2508 
2509         m_aCurrentPDFState = m_aGraphicsStack.front();
2510         m_aGraphicsStack.front().m_aFont =  aFont;
2511 
2512         for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin();
2513              it != m_aBitmaps.end(); ++it )
2514         {
2515             if( ! it->m_aBitmap.IsEmpty() )
2516             {
2517                 writeBitmapObject( *it );
2518                 it->m_aBitmap = BitmapEx();
2519             }
2520         }
2521         for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg )
2522         {
2523             if( jpeg->m_pStream )
2524             {
2525                 writeJPG( *jpeg );
2526                 delete jpeg->m_pStream;
2527                 jpeg->m_pStream = NULL;
2528                 jpeg->m_aMask = Bitmap();
2529             }
2530         }
2531         for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin();
2532              t != m_aTransparentObjects.end(); ++t )
2533         {
2534             if( t->m_pContentStream )
2535             {
2536                 writeTransparentObject( *t );
2537                 delete t->m_pContentStream;
2538                 t->m_pContentStream = NULL;
2539             }
2540         }
2541     }
2542 }
2543 
2544 sal_Int32 PDFWriterImpl::createObject()
2545 {
2546     m_aObjects.push_back( ~0U );
2547     return m_aObjects.size();
2548 }
2549 
2550 bool PDFWriterImpl::updateObject( sal_Int32 n )
2551 {
2552     if( ! m_bOpen )
2553         return false;
2554 
2555     sal_uInt64 nOffset = ~0U;
2556     oslFileError aError = osl_getFilePos( m_aFile, &nOffset );
2557     DBG_ASSERT( aError == osl_File_E_None, "could not register object" );
2558     if( aError != osl_File_E_None )
2559     {
2560         osl_closeFile( m_aFile );
2561         m_bOpen = false;
2562     }
2563     m_aObjects[ n-1 ] = nOffset;
2564     return aError == osl_File_E_None;
2565 }
2566 
2567 #define CHECK_RETURN( x ) if( !(x) ) return 0
2568 
2569 sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject )
2570 {
2571     if( nObject > 0 )
2572     {
2573         OStringBuffer aLine( 1024 );
2574 
2575         aLine.append( nObject );
2576         aLine.append( " 0 obj\n"
2577                       "<</Nums[\n" );
2578         sal_Int32 nTreeItems = m_aStructParentTree.size();
2579         for( sal_Int32 n = 0; n < nTreeItems; n++ )
2580         {
2581             aLine.append( n );
2582             aLine.append( ' ' );
2583             aLine.append( m_aStructParentTree[n] );
2584             aLine.append( "\n" );
2585         }
2586         aLine.append( "]>>\nendobj\n\n" );
2587         CHECK_RETURN( updateObject( nObject ) );
2588         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2589     }
2590     return nObject;
2591 }
2592 
2593 const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr )
2594 {
2595     static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings;
2596     // fill maps once
2597     if( aAttributeStrings.empty() )
2598     {
2599         aAttributeStrings[ PDFWriter::Placement ]			= "Placement";
2600         aAttributeStrings[ PDFWriter::WritingMode ]			= "WritingMode";
2601         aAttributeStrings[ PDFWriter::SpaceBefore ]			= "SpaceBefore";
2602         aAttributeStrings[ PDFWriter::SpaceAfter ]			= "SpaceAfter";
2603         aAttributeStrings[ PDFWriter::StartIndent ]			= "StartIndent";
2604         aAttributeStrings[ PDFWriter::EndIndent ]			= "EndIndent";
2605         aAttributeStrings[ PDFWriter::TextIndent ]			= "TextIndent";
2606         aAttributeStrings[ PDFWriter::TextAlign ]			= "TextAlign";
2607         aAttributeStrings[ PDFWriter::Width ]				= "Width";
2608         aAttributeStrings[ PDFWriter::Height ]				= "Height";
2609         aAttributeStrings[ PDFWriter::BlockAlign ]			= "BlockAlign";
2610         aAttributeStrings[ PDFWriter::InlineAlign ]			= "InlineAlign";
2611         aAttributeStrings[ PDFWriter::LineHeight ]			= "LineHeight";
2612         aAttributeStrings[ PDFWriter::BaselineShift ]		= "BaselineShift";
2613         aAttributeStrings[ PDFWriter::TextDecorationType ]	= "TextDecorationType";
2614         aAttributeStrings[ PDFWriter::ListNumbering ]		= "ListNumbering";
2615         aAttributeStrings[ PDFWriter::RowSpan ]				= "RowSpan";
2616         aAttributeStrings[ PDFWriter::ColSpan ]				= "ColSpan";
2617         aAttributeStrings[ PDFWriter::LinkAnnotation ]      = "LinkAnnotation";
2618     }
2619 
2620     std::map< PDFWriter::StructAttribute, const char* >::const_iterator it =
2621         aAttributeStrings.find( eAttr );
2622 
2623 #if OSL_DEBUG_LEVEL > 1
2624     if( it == aAttributeStrings.end() )
2625         fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr );
2626 #endif
2627 
2628     return it != aAttributeStrings.end() ? it->second : "";
2629 }
2630 
2631 const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal )
2632 {
2633     static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings;
2634 
2635     if( aValueStrings.empty() )
2636     {
2637         aValueStrings[ PDFWriter::NONE ]					= "None";
2638         aValueStrings[ PDFWriter::Block ]					= "Block";
2639         aValueStrings[ PDFWriter::Inline ]					= "Inline";
2640         aValueStrings[ PDFWriter::Before ]					= "Before";
2641         aValueStrings[ PDFWriter::After ]					= "After";
2642         aValueStrings[ PDFWriter::Start ]					= "Start";
2643         aValueStrings[ PDFWriter::End ]						= "End";
2644         aValueStrings[ PDFWriter::LrTb ]					= "LrTb";
2645         aValueStrings[ PDFWriter::RlTb ]					= "RlTb";
2646         aValueStrings[ PDFWriter::TbRl ]					= "TbRl";
2647         aValueStrings[ PDFWriter::Center ]					= "Center";
2648         aValueStrings[ PDFWriter::Justify ]					= "Justify";
2649         aValueStrings[ PDFWriter::Auto ]					= "Auto";
2650         aValueStrings[ PDFWriter::Middle ]					= "Middle";
2651         aValueStrings[ PDFWriter::Normal ]					= "Normal";
2652         aValueStrings[ PDFWriter::Underline ]				= "Underline";
2653 		aValueStrings[ PDFWriter::Overline ]				= "Overline";
2654         aValueStrings[ PDFWriter::LineThrough ]				= "LineThrough";
2655         aValueStrings[ PDFWriter::Disc ]					= "Disc";
2656         aValueStrings[ PDFWriter::Circle ]					= "Circle";
2657         aValueStrings[ PDFWriter::Square ]					= "Square";
2658         aValueStrings[ PDFWriter::Decimal ]					= "Decimal";
2659         aValueStrings[ PDFWriter::UpperRoman ]				= "UpperRoman";
2660         aValueStrings[ PDFWriter::LowerRoman ]				= "LowerRoman";
2661         aValueStrings[ PDFWriter::UpperAlpha ]				= "UpperAlpha";
2662         aValueStrings[ PDFWriter::LowerAlpha ]				= "LowerAlpha";
2663     }
2664 
2665     std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it =
2666         aValueStrings.find( eVal );
2667 
2668 #if OSL_DEBUG_LEVEL > 1
2669     if( it == aValueStrings.end() )
2670         fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal );
2671 #endif
2672 
2673     return it != aValueStrings.end() ? it->second : "";
2674 }
2675 
2676 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt )
2677 {
2678     o_rLine.append( "/" );
2679     o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) );
2680 
2681     if( i_rVal.eValue != PDFWriter::Invalid )
2682     {
2683         o_rLine.append( "/" );
2684         o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) );
2685     }
2686     else
2687     {
2688         // numerical value
2689         o_rLine.append( " " );
2690         if( i_bIsFixedInt )
2691             appendFixedInt( i_rVal.nValue, o_rLine );
2692         else
2693             o_rLine.append( i_rVal.nValue );
2694     }
2695     o_rLine.append( "\n" );
2696 }
2697 
2698 OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
2699 {
2700     // create layout, list and table attribute sets
2701     OStringBuffer aLayout(256), aList(64), aTable(64);
2702     for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin();
2703          it != i_rEle.m_aAttributes.end(); ++it )
2704     {
2705         if( it->first == PDFWriter::ListNumbering )
2706             appendStructureAttributeLine( it->first, it->second, aList, true );
2707         else if( it->first == PDFWriter::RowSpan ||
2708                  it->first == PDFWriter::ColSpan )
2709             appendStructureAttributeLine( it->first, it->second, aTable, false );
2710         else if( it->first == PDFWriter::LinkAnnotation )
2711         {
2712             sal_Int32 nLink = it->second.nValue;
2713             std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
2714                 m_aLinkPropertyMap.find( nLink );
2715             if( link_it != m_aLinkPropertyMap.end() )
2716                 nLink = link_it->second;
2717             if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() )
2718             {
2719                 // update struct parent of link
2720                 OStringBuffer aStructParentEntry( 32 );
2721                 aStructParentEntry.append( i_rEle.m_nObject );
2722                 aStructParentEntry.append( " 0 R" );
2723                 m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() );
2724                 m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1;
2725 
2726                 sal_Int32 nRefObject = createObject();
2727                 OStringBuffer aRef( 256 );
2728                 aRef.append( nRefObject );
2729                 aRef.append( " 0 obj\n"
2730                              "<</Type/OBJR/Obj " );
2731                 aRef.append( m_aLinks[ nLink ].m_nObject );
2732                 aRef.append( " 0 R>>\n"
2733                              "endobj\n\n"
2734                              );
2735                 updateObject( nRefObject );
2736                 writeBuffer( aRef.getStr(), aRef.getLength() );
2737 
2738                 i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) );
2739             }
2740             else
2741             {
2742                 DBG_ERROR( "unresolved link id for Link structure" );
2743 #if OSL_DEBUG_LEVEL > 1
2744                 fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink );
2745                 {
2746                     OStringBuffer aLine( "unresolved link id " );
2747                     aLine.append( nLink );
2748                     aLine.append( " for Link structure" );
2749                     emitComment( aLine.getStr() );
2750                 }
2751 #endif
2752             }
2753         }
2754         else
2755             appendStructureAttributeLine( it->first, it->second, aLayout, true );
2756     }
2757     if( ! i_rEle.m_aBBox.IsEmpty() )
2758     {
2759         aLayout.append( "/BBox[" );
2760         appendFixedInt( i_rEle.m_aBBox.Left(), aLayout );
2761         aLayout.append( " " );
2762         appendFixedInt( i_rEle.m_aBBox.Top(), aLayout );
2763         aLayout.append( " " );
2764         appendFixedInt( i_rEle.m_aBBox.Right(), aLayout );
2765         aLayout.append( " " );
2766         appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout );
2767         aLayout.append( "]\n" );
2768     }
2769 
2770     std::vector< sal_Int32 > aAttribObjects;
2771     if( aLayout.getLength() )
2772     {
2773         aAttribObjects.push_back( createObject() );
2774         updateObject( aAttribObjects.back() );
2775         OStringBuffer aObj( 64 );
2776         aObj.append( aAttribObjects.back() );
2777         aObj.append( " 0 obj\n"
2778                      "<</O/Layout\n" );
2779         aLayout.append( ">>\nendobj\n\n" );
2780         writeBuffer( aObj.getStr(), aObj.getLength() );
2781         writeBuffer( aLayout.getStr(), aLayout.getLength() );
2782     }
2783     if( aList.getLength() )
2784     {
2785         aAttribObjects.push_back( createObject() );
2786         updateObject( aAttribObjects.back() );
2787         OStringBuffer aObj( 64 );
2788         aObj.append( aAttribObjects.back() );
2789         aObj.append( " 0 obj\n"
2790                      "<</O/List\n" );
2791         aList.append( ">>\nendobj\n\n" );
2792         writeBuffer( aObj.getStr(), aObj.getLength() );
2793         writeBuffer( aList.getStr(), aList.getLength() );
2794     }
2795     if( aTable.getLength() )
2796     {
2797         aAttribObjects.push_back( createObject() );
2798         updateObject( aAttribObjects.back() );
2799         OStringBuffer aObj( 64 );
2800         aObj.append( aAttribObjects.back() );
2801         aObj.append( " 0 obj\n"
2802                      "<</O/Table\n" );
2803         aTable.append( ">>\nendobj\n\n" );
2804         writeBuffer( aObj.getStr(), aObj.getLength() );
2805         writeBuffer( aTable.getStr(), aTable.getLength() );
2806     }
2807 
2808     OStringBuffer aRet( 64 );
2809     if( aAttribObjects.size() > 1 )
2810         aRet.append( " [" );
2811     for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin();
2812          at_it != aAttribObjects.end(); ++at_it )
2813     {
2814         aRet.append( " " );
2815         aRet.append( *at_it );
2816         aRet.append( " 0 R" );
2817     }
2818     if( aAttribObjects.size() > 1 )
2819         aRet.append( " ]" );
2820     return aRet.makeStringAndClear();
2821 }
2822 
2823 sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
2824 {
2825     if(
2826        // do not emit NonStruct and its children
2827        rEle.m_eType == PDFWriter::NonStructElement &&
2828        rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root
2829        )
2830         return 0;
2831 
2832     for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
2833     {
2834         if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
2835         {
2836             PDFStructureElement& rChild = m_aStructure[ *it ];
2837             if( rChild.m_eType != PDFWriter::NonStructElement )
2838             {
2839                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
2840                     emitStructure( rChild );
2841                 else
2842                 {
2843                     DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure element" );
2844 #if OSL_DEBUG_LEVEL > 1
2845                     fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
2846 #endif
2847                 }
2848             }
2849         }
2850         else
2851         {
2852             DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
2853 #if OSL_DEBUG_LEVEL > 1
2854             fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
2855 #endif
2856         }
2857     }
2858 
2859     OStringBuffer aLine( 512 );
2860     aLine.append( rEle.m_nObject );
2861     aLine.append( " 0 obj\n"
2862                   "<</Type" );
2863     sal_Int32 nParentTree = -1;
2864     if( rEle.m_nOwnElement == rEle.m_nParentElement )
2865     {
2866         nParentTree = createObject();
2867         CHECK_RETURN( nParentTree );
2868         aLine.append( "/StructTreeRoot\n" );
2869         aLine.append( "/ParentTree " );
2870         aLine.append( nParentTree );
2871         aLine.append( " 0 R\n" );
2872         if( ! m_aRoleMap.empty() )
2873         {
2874             aLine.append( "/RoleMap<<" );
2875             for( std::hash_map<OString,OString,OStringHash>::const_iterator
2876                  it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it )
2877             {
2878                 aLine.append( '/' );
2879                 aLine.append(it->first);
2880                 aLine.append( '/' );
2881                 aLine.append( it->second );
2882                 aLine.append( '\n' );
2883             }
2884             aLine.append( ">>\n" );
2885         }
2886     }
2887     else
2888     {
2889         aLine.append( "/StructElem\n"
2890                       "/S/" );
2891         if( rEle.m_aAlias.getLength() > 0 )
2892             aLine.append( rEle.m_aAlias );
2893         else
2894             aLine.append( getStructureTag( rEle.m_eType ) );
2895         aLine.append( "\n"
2896                       "/P " );
2897         aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject );
2898         aLine.append( " 0 R\n"
2899                       "/Pg " );
2900         aLine.append( rEle.m_nFirstPageObject );
2901         aLine.append( " 0 R\n" );
2902         if( rEle.m_aActualText.getLength() )
2903         {
2904             aLine.append( "/ActualText" );
2905             appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine );
2906             aLine.append( "\n" );
2907         }
2908         if( rEle.m_aAltText.getLength() )
2909         {
2910             aLine.append( "/Alt" );
2911             appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine );
2912             aLine.append( "\n" );
2913         }
2914     }
2915     if( ! rEle.m_aBBox.IsEmpty() || rEle.m_aAttributes.size() )
2916     {
2917         OString aAttribs =  emitStructureAttributes( rEle );
2918         if( aAttribs.getLength() )
2919         {
2920             aLine.append( "/A" );
2921             aLine.append( aAttribs );
2922             aLine.append( "\n" );
2923         }
2924     }
2925     if( rEle.m_aLocale.Language.getLength() > 0 )
2926     {
2927         OUStringBuffer aLocBuf( 16 );
2928         aLocBuf.append( rEle.m_aLocale.Language.toAsciiLowerCase() );
2929         if( rEle.m_aLocale.Country.getLength() > 0 )
2930         {
2931             aLocBuf.append( sal_Unicode('-') );
2932             aLocBuf.append( rEle.m_aLocale.Country );
2933         }
2934         aLine.append( "/Lang" );
2935         appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine );
2936         aLine.append( "\n" );
2937     }
2938     if( ! rEle.m_aKids.empty() )
2939     {
2940         unsigned int i = 0;
2941         aLine.append( "/K[" );
2942         for( std::list< PDFStructureElementKid >::const_iterator it =
2943                  rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ )
2944         {
2945             if( it->nMCID == -1 )
2946             {
2947                 aLine.append( it->nObject );
2948                 aLine.append( " 0 R" );
2949                 aLine.append( ( (i & 15) == 15 ) ? "\n" : " " );
2950             }
2951             else
2952             {
2953                 if( it->nObject == rEle.m_nFirstPageObject )
2954                 {
2955                     aLine.append( it->nMCID );
2956                     aLine.append( " " );
2957                 }
2958                 else
2959                 {
2960                     aLine.append( "<</Type/MCR/Pg " );
2961                     aLine.append( it->nObject );
2962                     aLine.append( " 0 R /MCID " );
2963                     aLine.append( it->nMCID );
2964                     aLine.append( ">>\n" );
2965                 }
2966             }
2967         }
2968         aLine.append( "]\n" );
2969     }
2970     aLine.append( ">>\nendobj\n\n" );
2971 
2972     CHECK_RETURN( updateObject( rEle.m_nObject ) );
2973     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2974 
2975     CHECK_RETURN( emitStructParentTree( nParentTree ) );
2976 
2977     return rEle.m_nObject;
2978 }
2979 
2980 bool PDFWriterImpl::emitGradients()
2981 {
2982     for( std::list<GradientEmit>::iterator it = m_aGradients.begin();
2983          it != m_aGradients.end(); ++it )
2984     {
2985         CHECK_RETURN( writeGradientFunction( *it ) );
2986     }
2987     return true;
2988 }
2989 
2990 bool PDFWriterImpl::emitTilings()
2991 {
2992     OStringBuffer aTilingObj( 1024 );
2993 
2994     for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it )
2995     {
2996         DBG_ASSERT( it->m_pTilingStream, "tiling without stream" );
2997         if( ! it->m_pTilingStream )
2998             continue;
2999 
3000         aTilingObj.setLength( 0 );
3001 
3002         #if OSL_DEBUG_LEVEL > 1
3003         emitComment( "PDFWriterImpl::emitTilings" );
3004         #endif
3005 
3006         sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left();
3007         sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top();
3008         sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth();
3009         sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight();
3010         if( it->m_aCellSize.Width() == 0 )
3011             it->m_aCellSize.Width() = nW;
3012         if( it->m_aCellSize.Height() == 0 )
3013             it->m_aCellSize.Height() = nH;
3014 
3015         bool bDeflate = compressStream( it->m_pTilingStream );
3016         it->m_pTilingStream->Seek( STREAM_SEEK_TO_END );
3017         sal_Size nTilingStreamSize = it->m_pTilingStream->Tell();
3018         it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN );
3019 
3020         // write pattern object
3021         aTilingObj.append( it->m_nObject );
3022         aTilingObj.append( " 0 obj\n" );
3023         aTilingObj.append( "<</Type/Pattern/PatternType 1\n"
3024                            "/PaintType 1\n"
3025                            "/TilingType 2\n"
3026                            "/BBox[" );
3027         appendFixedInt( nX, aTilingObj );
3028         aTilingObj.append( ' ' );
3029         appendFixedInt( nY, aTilingObj );
3030         aTilingObj.append( ' ' );
3031         appendFixedInt( nX+nW, aTilingObj );
3032         aTilingObj.append( ' ' );
3033         appendFixedInt( nY+nH, aTilingObj );
3034         aTilingObj.append( "]\n"
3035                            "/XStep " );
3036         appendFixedInt( it->m_aCellSize.Width(), aTilingObj );
3037         aTilingObj.append( "\n"
3038                            "/YStep " );
3039         appendFixedInt( it->m_aCellSize.Height(), aTilingObj );
3040         aTilingObj.append( "\n" );
3041         if( it->m_aTransform.matrix[0] != 1.0 ||
3042             it->m_aTransform.matrix[1] != 0.0 ||
3043             it->m_aTransform.matrix[3] != 0.0 ||
3044             it->m_aTransform.matrix[4] != 1.0 ||
3045             it->m_aTransform.matrix[2] != 0.0 ||
3046             it->m_aTransform.matrix[5] != 0.0 )
3047         {
3048             aTilingObj.append( "/Matrix [" );
3049             // TODO: scaling, mirroring on y, etc
3050             appendDouble( it->m_aTransform.matrix[0], aTilingObj );
3051             aTilingObj.append( ' ' );
3052             appendDouble( it->m_aTransform.matrix[1], aTilingObj );
3053             aTilingObj.append( ' ' );
3054             appendDouble( it->m_aTransform.matrix[3], aTilingObj );
3055             aTilingObj.append( ' ' );
3056             appendDouble( it->m_aTransform.matrix[4], aTilingObj );
3057             aTilingObj.append( ' ' );
3058             appendDouble( it->m_aTransform.matrix[2], aTilingObj );
3059             aTilingObj.append( ' ' );
3060             appendDouble( it->m_aTransform.matrix[5], aTilingObj );
3061             aTilingObj.append( "]\n" );
3062         }
3063         aTilingObj.append( "/Resources" );
3064         it->m_aResources.append( aTilingObj, getFontDictObject() );
3065         if( bDeflate )
3066             aTilingObj.append( "/Filter/FlateDecode" );
3067         aTilingObj.append( "/Length " );
3068         aTilingObj.append( (sal_Int32)nTilingStreamSize );
3069         aTilingObj.append( ">>\nstream\n" );
3070         CHECK_RETURN( updateObject( it->m_nObject ) );
3071         CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
3072         checkAndEnableStreamEncryption( it->m_nObject );
3073         nTilingStreamSize = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize );
3074         delete it->m_pTilingStream;
3075         it->m_pTilingStream = NULL;
3076         if( nTilingStreamSize == 0 )
3077             return false;
3078         disableStreamEncryption();
3079         aTilingObj.setLength( 0 );
3080         aTilingObj.append( "\nendstream\nendobj\n\n" );
3081         CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
3082     }
3083     return true;
3084 }
3085 
3086 sal_Int32 PDFWriterImpl::emitBuiltinFont( const ImplFontData* pFont, sal_Int32 nFontObject )
3087 {
3088     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
3089     if( !pFD )
3090         return 0;
3091     const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
3092 
3093     OStringBuffer aLine( 1024 );
3094 
3095     if( nFontObject <= 0 )
3096         nFontObject = createObject();
3097     CHECK_RETURN( updateObject( nFontObject ) );
3098     aLine.append( nFontObject );
3099     aLine.append( " 0 obj\n"
3100                   "<</Type/Font/Subtype/Type1/BaseFont/" );
3101     appendName( pBuiltinFont->m_pPSName, aLine );
3102     aLine.append( "\n" );
3103     if( pBuiltinFont->m_eCharSet == RTL_TEXTENCODING_MS_1252 )
3104          aLine.append( "/Encoding/WinAnsiEncoding\n" );
3105     aLine.append( ">>\nendobj\n\n" );
3106     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3107     return nFontObject;
3108 }
3109 
3110 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const ImplFontData* pFont, EmbedFont& rEmbed )
3111 {
3112     std::map< sal_Int32, sal_Int32 > aRet;
3113     if( isBuiltinFont( pFont ) )
3114     {
3115         aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont );
3116         return aRet;
3117     }
3118 
3119     sal_Int32 nFontObject = 0;
3120     sal_Int32 nFontDescriptor = 0;
3121     rtl::OString aSubType( "/Type1" );
3122     FontSubsetInfo aInfo;
3123     // fill in dummy values
3124     aInfo.m_nAscent = 1000;
3125 	aInfo.m_nDescent = 200;
3126 	aInfo.m_nCapHeight = 1000;
3127 	aInfo.m_aFontBBox = Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) );
3128     aInfo.m_aPSName = pFont->maName;
3129     sal_Int32 pWidths[256];
3130     rtl_zeroMemory( pWidths, sizeof(pWidths) );
3131     if( pFont->IsEmbeddable() )
3132     {
3133         const unsigned char* pFontData = NULL;
3134         long nFontLen = 0;
3135         sal_Ucs nEncodedCodes[256];
3136         sal_Int32 pEncWidths[256];
3137         if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pEncWidths, aInfo, &nFontLen )) != NULL )
3138         {
3139             m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3140             for( int i = 0; i < 256; i++ )
3141             {
3142                 if( nEncodedCodes[i] >= 32 && nEncodedCodes[i] < 256 )
3143                 {
3144                     pWidths[i] = pEncWidths[ i ];
3145                 }
3146             }
3147         }
3148     }
3149     else if( pFont->mbSubsettable )
3150     {
3151         aSubType = rtl::OString( "/TrueType" );
3152         Int32Vector aGlyphWidths;
3153         Ucs2UIntMap aUnicodeMap;
3154         m_pReferenceDevice->mpGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap );
3155 
3156         OUString aTmpName;
3157         osl_createTempFile( NULL, NULL, &aTmpName.pData );
3158         sal_Int32 pGlyphIDs[ 256 ];
3159         sal_uInt8 pEncoding[ 256 ];
3160         sal_Ucs   pUnicodes[ 256 ];
3161         sal_Int32 pDuWidths[ 256 ];
3162 
3163         memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) );
3164         memset( pEncoding, 0, sizeof( pEncoding ) );
3165         memset( pUnicodes, 0, sizeof( pUnicodes ) );
3166         memset( pDuWidths, 0, sizeof( pDuWidths ) );
3167 
3168         for( sal_Ucs c = 32; c < 256; c++ )
3169         {
3170             pUnicodes[c] = c;
3171             pEncoding[c] = c;
3172             pGlyphIDs[c] = 0;
3173             if( aUnicodeMap.find( c ) != aUnicodeMap.end() )
3174                 pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ];
3175         }
3176 
3177         m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, pFont, pGlyphIDs, pEncoding, pDuWidths, 256, aInfo );
3178         osl_removeFile( aTmpName.pData );
3179     }
3180     else
3181     {
3182         DBG_ERROR( "system font neither embeddable nor subsettable" );
3183     }
3184 
3185     // write font descriptor
3186     nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 );
3187     if( nFontDescriptor )
3188     {
3189         // write font object
3190         sal_Int32 nObject = createObject();
3191         if( updateObject( nObject ) )
3192         {
3193             OStringBuffer aLine( 1024 );
3194             aLine.append( nObject );
3195             aLine.append( " 0 obj\n"
3196                           "<</Type/Font/Subtype" );
3197             aLine.append( aSubType );
3198             aLine.append( "/BaseFont/" );
3199             appendName( aInfo.m_aPSName, aLine );
3200             aLine.append( "\n" );
3201             if( !pFont->mbSymbolFlag )
3202                 aLine.append( "/Encoding/WinAnsiEncoding\n" );
3203             aLine.append( "/FirstChar 32 /LastChar 255\n"
3204                           "/Widths[" );
3205             for( int i = 32; i < 256; i++ )
3206             {
3207                 aLine.append( pWidths[i] );
3208                 aLine.append( ((i&15) == 15) ? "\n" : " " );
3209             }
3210             aLine.append( "]\n"
3211                           "/FontDescriptor " );
3212             aLine.append( nFontDescriptor );
3213             aLine.append( " 0 R>>\n"
3214                           "endobj\n\n" );
3215             writeBuffer( aLine.getStr(), aLine.getLength() );
3216 
3217             nFontObject = nObject;
3218             aRet[ rEmbed.m_nNormalFontID ] = nObject;
3219         }
3220     }
3221 
3222     return aRet;
3223 }
3224 
3225 typedef int ThreeInts[3];
3226 static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen,
3227 	ThreeInts& rSegmentLengths )
3228 {
3229 	if( !pFontBytes || (nByteLen < 0) )
3230 		return false;
3231 	const unsigned char* pPtr = pFontBytes;
3232 	const unsigned char* pEnd = pFontBytes + nByteLen;
3233 
3234 	for( int i = 0; i < 3; ++i) {
3235 		// read segment1 header
3236 		if( pPtr+6 >= pEnd )
3237 			return false;
3238 		if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) )
3239 			return false;
3240 		const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2];
3241 		if( nLen <= 0)
3242 			return false;
3243 		rSegmentLengths[i] = nLen;
3244 		pPtr += nLen + 6;
3245 	}
3246 
3247 	// read segment-end header
3248 	if( pPtr+2 >= pEnd )
3249 		return false;
3250 	if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) )
3251 		return false;
3252 
3253 	return true;
3254 }
3255 
3256 struct FontException : public std::exception
3257 {
3258 };
3259 
3260 // TODO: always subset instead of embedding the full font => this method becomes obsolete then
3261 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFontData* pFont, EmbedFont& rEmbed )
3262 {
3263     std::map< sal_Int32, sal_Int32 > aRet;
3264     if( isBuiltinFont( pFont ) )
3265     {
3266         aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont );
3267         return aRet;
3268     }
3269 
3270     sal_Int32 nFontObject = 0;
3271     sal_Int32 nStreamObject = 0;
3272     sal_Int32 nFontDescriptor = 0;
3273 
3274     // prepare font encoding
3275     const Ucs2SIntMap* pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pFont, NULL );
3276     sal_Int32 nToUnicodeStream = 0;
3277     sal_uInt8 nEncoding[256];
3278     sal_Ucs nEncodedCodes[256];
3279     std::vector<sal_Ucs> aUnicodes;
3280     aUnicodes.reserve( 256 );
3281     sal_Int32 pUnicodesPerGlyph[256];
3282     sal_Int32 pEncToUnicodeIndex[256];
3283     if( pEncoding )
3284     {
3285         rtl_zeroMemory( nEncoding, sizeof(nEncoding) );
3286         rtl_zeroMemory( nEncodedCodes, sizeof(nEncodedCodes) );
3287         rtl_zeroMemory( pUnicodesPerGlyph, sizeof(pUnicodesPerGlyph) );
3288         rtl_zeroMemory( pEncToUnicodeIndex, sizeof(pEncToUnicodeIndex) );
3289         for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it )
3290         {
3291             if( it->second != -1 )
3292             {
3293                 sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff);
3294                 nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode );
3295                 nEncodedCodes[ nCode ] = it->first;
3296                 pEncToUnicodeIndex[ nCode ] = static_cast<sal_Int32>(aUnicodes.size());
3297                 aUnicodes.push_back( it->first );
3298                 pUnicodesPerGlyph[ nCode ] = 1;
3299             }
3300         }
3301     }
3302 
3303     FontSubsetInfo aInfo;
3304     sal_Int32 pWidths[256];
3305     const unsigned char* pFontData = NULL;
3306     long nFontLen = 0;
3307     sal_Int32 nLength1, nLength2;
3308     try
3309     {
3310         if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL )
3311         {
3312             if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 )
3313                 throw FontException();
3314             // see whether it is pfb or pfa; if it is a pfb, fill ranges
3315             // of 6 bytes that are not part of the font program
3316             std::list< int > aSections;
3317             std::list< int >::const_iterator it;
3318             int nIndex = 0;
3319             while( pFontData[nIndex] == 0x80 && nIndex < nFontLen-1 )
3320             {
3321                 aSections.push_back( nIndex );
3322                 if( pFontData[nIndex+1] == 0x03 )
3323                     break;
3324                 sal_Int32 nBytes =
3325                 ((sal_Int32)pFontData[nIndex+2])			|
3326                 ((sal_Int32)pFontData[nIndex+3]) << 8		|
3327                 ((sal_Int32)pFontData[nIndex+4]) << 16		|
3328                 ((sal_Int32)pFontData[nIndex+5]) << 24;
3329                 nIndex += nBytes+6;
3330             }
3331 
3332             // search for eexec
3333             // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below
3334             nIndex = 0;
3335             int nEndAsciiIndex;
3336             int nBeginBinaryIndex;
3337             int nEndBinaryIndex;
3338             do
3339             {
3340                 while( nIndex < nFontLen-4 &&
3341                     ( pFontData[nIndex] != 'e'	||
3342                         pFontData[nIndex+1] != 'e' ||
3343                         pFontData[nIndex+2] != 'x' ||
3344                         pFontData[nIndex+3] != 'e' ||
3345                         pFontData[nIndex+4] != 'c'
3346                         )
3347                     )
3348                 nIndex++;
3349                 // check whether we are in a excluded section
3350                 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3351                     ;
3352             } while( it != aSections.end() && nIndex < nFontLen-4 );
3353             // this should end the ascii part
3354             if( nIndex > nFontLen-5 )
3355                 throw FontException();
3356 
3357             nEndAsciiIndex = nIndex+4;
3358             // now count backwards until we can account for 512 '0'
3359             // which is the endmarker of the (hopefully) binary data
3360             // do not count the pfb header sections
3361             int nFound = 0;
3362             nIndex =  nFontLen-1;
3363             while( nIndex > 0 && nFound < 512 )
3364             {
3365                 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3366                     ;
3367                 if( it == aSections.end() )
3368                 {
3369                     // inside the 512 '0' block there may only be whitespace
3370                     // according to T1 spec; probably it would be to simple
3371                     // if all fonts complied
3372                     if( pFontData[nIndex] == '0' )
3373                         nFound++;
3374                         else if( nFound > 0					&&
3375                             pFontData[nIndex] != '\r'		&&
3376                         pFontData[nIndex] != '\t'		&&
3377                         pFontData[nIndex] != '\n'		&&
3378                         pFontData[nIndex] != ' ' )
3379                         break;
3380                 }
3381                 nIndex--;
3382             }
3383 
3384             if( nIndex < 1 || nIndex <= nEndAsciiIndex )
3385                 throw FontException();
3386 
3387             // nLength3 is the rest of the file - excluding any section headers
3388             // nIndex now points to the first of the 512 '0' characters marking the
3389             // fixed content portion
3390             sal_Int32 nLength3 = nFontLen - nIndex;
3391             for( it = aSections.begin(); it != aSections.end(); ++it )
3392             {
3393                 // special case: nIndex inside a section marker
3394                 if( nIndex >= (*it) && (*it)+6 > nIndex )
3395                     nLength3 -= (*it)+6 - nIndex;
3396                 else if( *it >= nIndex  )
3397                 {
3398                     if( *it < nFontLen - 6 )
3399                         nLength3 -= 6;
3400                     else // the last section 0x8003 is only 2 bytes after all
3401                         nLength3 -= (nFontLen - *it);
3402                 }
3403             }
3404 
3405             // there may be whitespace to ignore before the 512 '0'
3406             while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' )
3407             {
3408                 nIndex--;
3409                 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3410                     ;
3411                 if( it != aSections.end() )
3412                 {
3413                     nIndex = (*it)-1;
3414                     break; // this is surely a binary boundary, in ascii case it wouldn't matter
3415                 }
3416             }
3417             nEndBinaryIndex = nIndex;
3418 
3419             // search for beginning of binary section
3420             nBeginBinaryIndex = nEndAsciiIndex;
3421             do
3422             {
3423                 nBeginBinaryIndex++;
3424                 for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it )
3425                     ;
3426                     } while( nBeginBinaryIndex < nEndBinaryIndex &&
3427                         ( pFontData[nBeginBinaryIndex] == '\r'	||
3428                             pFontData[nBeginBinaryIndex] == '\n'	||
3429                             it != aSections.end() ) );
3430 
3431                     // it seems to be vital to copy the exact whitespace between binary data
3432                     // and eexec, else a invalid font results. so make nEndAsciiIndex
3433                     // always immediate in front of nBeginBinaryIndex
3434                     nEndAsciiIndex = nBeginBinaryIndex-1;
3435                     for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it )
3436                         ;
3437                     if( it != aSections.end() )
3438                         nEndAsciiIndex = (*it)-1;
3439 
3440                     nLength1 = nEndAsciiIndex+1; // including the last character
3441                     for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it )
3442                         nLength1 -= 6; // decrease by pfb section size
3443 
3444                     // if the first four bytes are all ascii hex characters, then binary data
3445                     // has to be converted to real binary data
3446                     for( nIndex = 0; nIndex < 4 &&
3447                         ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) ||
3448                             ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) ||
3449                             ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' )
3450                             ); ++nIndex )
3451                     ;
3452                     bool bConvertHexData = true;
3453                     if( nIndex < 4 )
3454                     {
3455                         bConvertHexData = false;
3456                         nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte
3457                         for( it = aSections.begin(); it != aSections.end(); ++it )
3458                             if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex )
3459                             nLength2 -= 6;
3460                     }
3461                     else
3462                     {
3463                         // count the hex ascii characters to get nLength2
3464                         nLength2 = 0;
3465                         int nNextSectionIndex = 0;
3466                         for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3467                             ;
3468                         if( it != aSections.end() )
3469                             nNextSectionIndex = *it;
3470                         for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3471                         {
3472                             if( nIndex == nNextSectionIndex )
3473                             {
3474                                 nIndex += 6;
3475                                 ++it;
3476                                 nNextSectionIndex = (it == aSections.end() ? 0 : *it );
3477                             }
3478                             if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) ||
3479                                 ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) ||
3480                             ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) )
3481                             nLength2++;
3482                         }
3483                         DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" );
3484                         nLength2 /= 2;
3485                     }
3486 
3487                     // now we can actually write the font stream !
3488                     #if OSL_DEBUG_LEVEL > 1
3489                     emitComment( " PDFWriterImpl::emitEmbeddedFont" );
3490                     #endif
3491                     OStringBuffer aLine( 512 );
3492                     nStreamObject = createObject();
3493                     if( !updateObject(nStreamObject))
3494                         throw FontException();
3495                     sal_Int32 nStreamLengthObject = createObject();
3496                     aLine.append( nStreamObject );
3497                     aLine.append( " 0 obj\n"
3498                         "<</Length " );
3499                     aLine.append( nStreamLengthObject );
3500                     aLine.append( " 0 R"
3501                         #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3502                         "/Filter/FlateDecode"
3503                         #endif
3504                         "/Length1 " );
3505                     aLine.append( nLength1 );
3506                     aLine.append( " /Length2 " );
3507                     aLine.append( nLength2 );
3508                     aLine.append( " /Length3 ");
3509                     aLine.append( nLength3 );
3510                     aLine.append( ">>\n"
3511                         "stream\n" );
3512                     if( !writeBuffer( aLine.getStr(), aLine.getLength() ) )
3513                         throw FontException();
3514 
3515                     sal_uInt64 nBeginStreamPos = 0;
3516                     osl_getFilePos( m_aFile, &nBeginStreamPos );
3517 
3518                     beginCompression();
3519                     checkAndEnableStreamEncryption( nStreamObject );
3520 
3521                     // write ascii section
3522                     if( aSections.begin() == aSections.end() )
3523                     {
3524                         if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) )
3525                             throw FontException();
3526                     }
3527                     else
3528                     {
3529                         // first section always starts at 0
3530                         it = aSections.begin();
3531                         nIndex = (*it)+6;
3532                         ++it;
3533                         while( *it < nEndAsciiIndex )
3534                         {
3535                             if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) )
3536                                 throw FontException();
3537                             nIndex = (*it)+6;
3538                             ++it;
3539                         }
3540                         // write partial last section
3541                         if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) )
3542                             throw FontException();
3543                     }
3544 
3545                     // write binary section
3546                     if( ! bConvertHexData )
3547                     {
3548                         if( aSections.begin() == aSections.end() )
3549                         {
3550                             if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) )
3551                                 throw FontException();
3552                         }
3553                         else
3554                         {
3555                             for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it )
3556                                 ;
3557                             // write first partial section
3558                             if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) )
3559                                 throw FontException();
3560                             // write following sections
3561                             while( it != aSections.end() )
3562                             {
3563                                 nIndex = (*it)+6;
3564                                 ++it;
3565                                 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3566                                 {
3567                                     sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3568                                     if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3569                                         throw FontException();
3570                                 }
3571                             }
3572                         }
3573                     }
3574                     else
3575                     {
3576                         boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] );
3577                         rtl_zeroMemory( pWriteBuffer.get(), nLength2 );
3578                         int nWriteIndex = 0;
3579 
3580                         int nNextSectionIndex = 0;
3581                         for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3582                             ;
3583                         if( it != aSections.end() )
3584                             nNextSectionIndex = *it;
3585                         for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3586                         {
3587                             if( nIndex == nNextSectionIndex )
3588                             {
3589                                 nIndex += 6;
3590                                 ++it;
3591                                 nNextSectionIndex = (it == aSections.end() ? nFontLen : *it );
3592                             }
3593                             unsigned char cNibble = 0x80;
3594                             if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' )
3595                                 cNibble = pFontData[nIndex] - '0';
3596                             else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' )
3597                                 cNibble = pFontData[nIndex] - 'a' + 10;
3598                             else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' )
3599                                 cNibble = pFontData[nIndex] - 'A' + 10;
3600                             if( cNibble != 0x80 )
3601                             {
3602                                 if( !(nWriteIndex & 1 ) )
3603                                     cNibble <<= 4;
3604                                 pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble;
3605                                 nWriteIndex++;
3606                             }
3607                         }
3608                         if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) )
3609                             throw FontException();
3610                         if( aSections.empty() )
3611                         {
3612                             if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) )
3613                                 throw FontException();
3614                         }
3615                         else
3616                         {
3617                             // write rest of this section
3618                             if( nIndex < nNextSectionIndex )
3619                             {
3620                                 if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) )
3621                                     throw FontException();
3622                             }
3623                             // write following sections
3624                             while( it != aSections.end() )
3625                             {
3626                                 nIndex = (*it)+6;
3627                                 ++it;
3628                                 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3629                                 {
3630                                     sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3631                                     if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3632                                         throw FontException();
3633                                 }
3634                             }
3635                         }
3636                     }
3637                     endCompression();
3638                     disableStreamEncryption();
3639 
3640 
3641                     sal_uInt64 nEndStreamPos = 0;
3642                     osl_getFilePos( m_aFile, &nEndStreamPos );
3643 
3644                     // and finally close the stream
3645                     aLine.setLength( 0 );
3646                     aLine.append( "\nendstream\nendobj\n\n" );
3647                     if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3648                         throw FontException();
3649 
3650                     // write stream length object
3651                     aLine.setLength( 0 );
3652                     if( ! updateObject( nStreamLengthObject ) )
3653                         throw FontException();
3654                     aLine.append( nStreamLengthObject );
3655                     aLine.append( " 0 obj\n" );
3656                     aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) );
3657                     aLine.append( "\nendobj\n\n" );
3658                     if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3659                         throw FontException();
3660         }
3661         else
3662         {
3663             rtl::OStringBuffer aErrorComment( 256 );
3664             aErrorComment.append( "GetEmbedFontData failed for font \"" );
3665             aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3666             aErrorComment.append( '\"' );
3667             if( pFont->GetSlant() == ITALIC_NORMAL )
3668                 aErrorComment.append( " italic" );
3669             else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3670                 aErrorComment.append( " oblique" );
3671             aErrorComment.append( " weight=" );
3672             aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3673             emitComment( aErrorComment.getStr() );
3674         }
3675 
3676         if( nStreamObject )
3677             // write font descriptor
3678         nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject );
3679 
3680         if( nFontDescriptor )
3681         {
3682             if( pEncoding )
3683                 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, sizeof(nEncoding)/sizeof(nEncoding[0]) );
3684 
3685             // write font object
3686             sal_Int32 nObject = createObject();
3687             if( ! updateObject( nObject ) )
3688                 throw FontException();
3689 
3690             OStringBuffer aLine( 1024 );
3691             aLine.append( nObject );
3692             aLine.append( " 0 obj\n"
3693                 "<</Type/Font/Subtype/Type1/BaseFont/" );
3694             appendName( aInfo.m_aPSName, aLine );
3695             aLine.append( "\n" );
3696             if( !pFont->mbSymbolFlag &&  pEncoding == 0 )
3697                 aLine.append( "/Encoding/WinAnsiEncoding\n" );
3698             if( nToUnicodeStream )
3699             {
3700                 aLine.append( "/ToUnicode " );
3701                 aLine.append( nToUnicodeStream );
3702                 aLine.append( " 0 R\n" );
3703             }
3704             aLine.append( "/FirstChar 0 /LastChar 255\n"
3705                 "/Widths[" );
3706             for( int i = 0; i < 256; i++ )
3707             {
3708                 aLine.append( pWidths[i] );
3709                 aLine.append( ((i&15) == 15) ? "\n" : " " );
3710             }
3711             aLine.append( "]\n"
3712                 "/FontDescriptor " );
3713             aLine.append( nFontDescriptor );
3714             aLine.append( " 0 R>>\n"
3715                 "endobj\n\n" );
3716             if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3717                 throw FontException();
3718 
3719             nFontObject = nObject;
3720 
3721             aRet[ rEmbed.m_nNormalFontID ] = nObject;
3722 
3723             // write additional encodings
3724             for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it )
3725             {
3726                 sal_Int32 aEncWidths[ 256 ];
3727                 // emit encoding dict
3728                 sal_Int32 nEncObject = createObject();
3729                 if( ! updateObject( nEncObject ) )
3730                     throw FontException();
3731 
3732                 OutputDevice* pRef = getReferenceDevice();
3733                 pRef->Push( PUSH_FONT | PUSH_MAPMODE );
3734                 pRef->SetMapMode( MapMode( MAP_PIXEL ) );
3735                 Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) );
3736                 aFont.SetWeight( pFont->GetWeight() );
3737                 aFont.SetItalic( pFont->GetSlant() );
3738                 aFont.SetPitch( pFont->GetPitch() );
3739                 pRef->SetFont( aFont );
3740                 pRef->ImplNewFont();
3741 
3742                 aLine.setLength( 0 );
3743                 aLine.append( nEncObject );
3744                 aLine.append( " 0 obj\n"
3745                     "<</Type/Encoding/Differences[ 0\n" );
3746                 int nEncoded = 0;
3747                 aUnicodes.clear();
3748                 for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it )
3749                 {
3750                     String aStr( str_it->m_aUnicode );
3751                     aEncWidths[nEncoded] = pRef->GetTextWidth( aStr );
3752                     nEncodedCodes[nEncoded] = str_it->m_aUnicode;
3753                     nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded);
3754                     pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size());
3755                     aUnicodes.push_back( nEncodedCodes[nEncoded] );
3756                     pUnicodesPerGlyph[nEncoded] = 1;
3757 
3758                     aLine.append( " /" );
3759                     aLine.append( str_it->m_aName );
3760                     if( !((++nEncoded) & 15) )
3761                         aLine.append( "\n" );
3762                 }
3763                 aLine.append( "]>>\n"
3764                     "endobj\n\n" );
3765 
3766                 pRef->Pop();
3767 
3768                 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3769                     throw FontException();
3770 
3771                 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded );
3772 
3773                 nObject = createObject();
3774                 if( ! updateObject( nObject ) )
3775                     throw FontException();
3776 
3777                 aLine.setLength( 0 );
3778                 aLine.append( nObject );
3779                 aLine.append( " 0 obj\n"
3780                     "<</Type/Font/Subtype/Type1/BaseFont/" );
3781                 appendName( aInfo.m_aPSName, aLine );
3782                 aLine.append( "\n" );
3783                 aLine.append( "/Encoding " );
3784                 aLine.append( nEncObject );
3785                 aLine.append( " 0 R\n" );
3786                 if( nToUnicodeStream )
3787                 {
3788                     aLine.append( "/ToUnicode " );
3789                     aLine.append( nToUnicodeStream );
3790                     aLine.append( " 0 R\n" );
3791                 }
3792                 aLine.append( "/FirstChar 0\n"
3793                     "/LastChar " );
3794                 aLine.append( (sal_Int32)(nEncoded-1) );
3795                 aLine.append( "\n"
3796                     "/Widths[" );
3797                 for( int i = 0; i < nEncoded; i++ )
3798                 {
3799                     aLine.append( aEncWidths[i] );
3800                     aLine.append( ((i&15) == 15) ? "\n" : " " );
3801                 }
3802                 aLine.append( " ]\n"
3803                     "/FontDescriptor " );
3804                 aLine.append( nFontDescriptor );
3805                 aLine.append( " 0 R>>\n"
3806                     "endobj\n\n" );
3807                 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3808                     throw FontException();
3809 
3810                 aRet[ enc_it->m_nFontID ] = nObject;
3811             }
3812         }
3813     }
3814     catch( FontException& )
3815     {
3816         // these do nothing in case there was no compression or encryption ongoing
3817         endCompression();
3818         disableStreamEncryption();
3819     }
3820 
3821     if( pFontData )
3822         m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3823 
3824     return aRet;
3825 }
3826 
3827 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
3828 {
3829     if( nSubsetID )
3830     {
3831         for( int i = 0; i < 6; i++ )
3832         {
3833             int nOffset = (nSubsetID % 26);
3834             nSubsetID /= 26;
3835             rBuffer.append( (sal_Char)('A'+nOffset) );
3836         }
3837         rBuffer.append( '+' );
3838     }
3839     appendName( rPSName, rBuffer );
3840 }
3841 
3842 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding,
3843                                               sal_Ucs* pUnicodes,
3844                                               sal_Int32* pUnicodesPerGlyph,
3845                                               sal_Int32* pEncToUnicodeIndex,
3846                                               int nGlyphs )
3847 {
3848     int nMapped = 0, n = 0;
3849     for( n = 0; n < nGlyphs; n++ )
3850         if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3851             nMapped++;
3852 
3853     if( nMapped == 0 )
3854         return 0;
3855 
3856     sal_Int32 nStream = createObject();
3857     CHECK_RETURN( updateObject( nStream ) );
3858 
3859     OStringBuffer aContents( 1024 );
3860     aContents.append(
3861                      "/CIDInit/ProcSet findresource begin\n"
3862                      "12 dict begin\n"
3863                      "begincmap\n"
3864                      "/CIDSystemInfo<<\n"
3865                      "/Registry (Adobe)\n"
3866                      "/Ordering (UCS)\n"
3867                      "/Supplement 0\n"
3868                      ">> def\n"
3869                      "/CMapName/Adobe-Identity-UCS def\n"
3870                      "/CMapType 2 def\n"
3871                      "1 begincodespacerange\n"
3872                      "<00> <FF>\n"
3873                      "endcodespacerange\n"
3874                      );
3875     int nCount = 0;
3876     for( n = 0; n < nGlyphs; n++ )
3877     {
3878         if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3879         {
3880             if( (nCount % 100) == 0 )
3881             {
3882                 if( nCount )
3883                     aContents.append( "endbfchar\n" );
3884                 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) );
3885                 aContents.append( " beginbfchar\n" );
3886             }
3887             aContents.append( '<' );
3888             appendHex( (sal_Int8)pEncoding[n], aContents );
3889             aContents.append( "> <" );
3890             // TODO: handle unicodes>U+FFFF
3891             sal_Int32 nIndex = pEncToUnicodeIndex[n];
3892 	        for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ )
3893 	        {
3894                 appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents );
3895                 appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents );
3896             }
3897             aContents.append( ">\n" );
3898             nCount++;
3899         }
3900     }
3901     aContents.append( "endbfchar\n"
3902                       "endcmap\n"
3903                       "CMapName currentdict /CMap defineresource pop\n"
3904                       "end\n"
3905                       "end\n" );
3906 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3907     ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
3908     SvMemoryStream aStream;
3909     pCodec->BeginCompression();
3910     pCodec->Write( aStream, (const sal_uInt8*)aContents.getStr(), aContents.getLength() );
3911     pCodec->EndCompression();
3912     delete pCodec;
3913 #endif
3914 
3915     #if OSL_DEBUG_LEVEL > 1
3916     emitComment( "PDFWriterImpl::createToUnicodeCMap" );
3917     #endif
3918     OStringBuffer aLine( 40 );
3919 
3920     aLine.append( nStream );
3921     aLine.append( " 0 obj\n<</Length " );
3922 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3923     sal_Int32 nLen = (sal_Int32)aStream.Tell();
3924     aStream.Seek( 0 );
3925     aLine.append( nLen );
3926     aLine.append( "/Filter/FlateDecode" );
3927 #else
3928     aLine.append( aContents.getLength() );
3929 #endif
3930     aLine.append( ">>\nstream\n" );
3931     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3932     checkAndEnableStreamEncryption( nStream );
3933 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3934     CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
3935 #else
3936     CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
3937 #endif
3938     disableStreamEncryption();
3939     aLine.setLength( 0 );
3940     aLine.append( "\nendstream\n"
3941                   "endobj\n\n" );
3942     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3943     return nStream;
3944 }
3945 
3946 sal_Int32 PDFWriterImpl::emitFontDescriptor( const ImplFontData* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
3947 {
3948     OStringBuffer aLine( 1024 );
3949     // get font flags, see PDF reference 1.4 p. 358
3950     // possibly characters outside Adobe standard encoding
3951     // so set Symbolic flag
3952     sal_Int32 nFontFlags = (1<<2);
3953     if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE )
3954         nFontFlags |= (1 << 6);
3955     if( pFont->GetPitch() == PITCH_FIXED )
3956         nFontFlags |= 1;
3957     if( pFont->GetFamilyType() == FAMILY_SCRIPT )
3958         nFontFlags |= (1 << 3);
3959     else if( pFont->GetFamilyType() == FAMILY_ROMAN )
3960         nFontFlags |= (1 << 1);
3961 
3962     sal_Int32 nFontDescriptor = createObject();
3963     CHECK_RETURN( updateObject( nFontDescriptor ) );
3964     aLine.setLength( 0 );
3965     aLine.append( nFontDescriptor );
3966     aLine.append( " 0 obj\n"
3967                   "<</Type/FontDescriptor/FontName/" );
3968     appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
3969     aLine.append( "\n"
3970                   "/Flags " );
3971     aLine.append( nFontFlags );
3972     aLine.append( "\n"
3973                   "/FontBBox[" );
3974     // note: Top and Bottom are reversed in VCL and PDF rectangles
3975     aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() );
3976     aLine.append( ' ' );
3977     aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() );
3978     aLine.append( ' ' );
3979     aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() );
3980     aLine.append( ' ' );
3981     aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) );
3982     aLine.append( "]/ItalicAngle " );
3983     if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL )
3984         aLine.append( "-30" );
3985     else
3986         aLine.append( "0" );
3987     aLine.append( "\n"
3988                   "/Ascent " );
3989     aLine.append( (sal_Int32)rInfo.m_nAscent );
3990     aLine.append( "\n"
3991                   "/Descent " );
3992     aLine.append( (sal_Int32)-rInfo.m_nDescent );
3993     aLine.append( "\n"
3994                   "/CapHeight " );
3995     aLine.append( (sal_Int32)rInfo.m_nCapHeight );
3996     // According to PDF reference 1.4 StemV is required
3997     // seems a tad strange to me, but well ...
3998     aLine.append( "\n"
3999                   "/StemV 80\n" );
4000     if( nFontStream )
4001     {
4002         aLine.append( "/FontFile" );
4003         switch( rInfo.m_nFontType )
4004         {
4005             case FontSubsetInfo::SFNT_TTF:
4006                 aLine.append( '2' );
4007                 break;
4008             case FontSubsetInfo::TYPE1_PFA:
4009             case FontSubsetInfo::TYPE1_PFB:
4010             case FontSubsetInfo::ANY_TYPE1:
4011                 break;
4012             default:
4013                 DBG_ERROR( "unknown fonttype in PDF font descriptor" );
4014                 return 0;
4015         }
4016         aLine.append( ' ' );
4017         aLine.append( nFontStream );
4018         aLine.append( " 0 R\n" );
4019     }
4020     aLine.append( ">>\n"
4021                   "endobj\n\n" );
4022     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4023 
4024     return nFontDescriptor;
4025 }
4026 
4027 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const
4028 {
4029     for( std::map< sal_Int32, sal_Int32 >::const_iterator it =
4030          m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it )
4031     {
4032         rDict.append( m_aBuiltinFonts[it->first].getNameObject() );
4033         rDict.append( ' ' );
4034         rDict.append( it->second );
4035         rDict.append( " 0 R" );
4036     }
4037 }
4038 
4039 bool PDFWriterImpl::emitFonts()
4040 {
4041     if( ! m_pReferenceDevice->ImplGetGraphics() )
4042         return false;
4043 
4044     OStringBuffer aLine( 1024 );
4045 
4046     std::map< sal_Int32, sal_Int32 > aFontIDToObject;
4047 
4048     OUString aTmpName;
4049     osl_createTempFile( NULL, NULL, &aTmpName.pData );
4050     for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it )
4051     {
4052         for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit )
4053         {
4054             sal_Int32 pGlyphIDs[ 256 ];
4055             sal_Int32 pWidths[ 256 ];
4056             sal_uInt8 pEncoding[ 256 ];
4057             sal_Int32 pEncToUnicodeIndex[ 256 ];
4058             sal_Int32 pUnicodesPerGlyph[ 256 ];
4059             std::vector<sal_Ucs> aUnicodes;
4060             aUnicodes.reserve( 256 );
4061             int nGlyphs = 1;
4062             // fill arrays and prepare encoding index map
4063             sal_Int32 nToUnicodeStream = 0;
4064 
4065             rtl_zeroMemory( pGlyphIDs, sizeof( pGlyphIDs ) );
4066             rtl_zeroMemory( pEncoding, sizeof( pEncoding ) );
4067             rtl_zeroMemory( pUnicodesPerGlyph, sizeof( pUnicodesPerGlyph ) );
4068             rtl_zeroMemory( pEncToUnicodeIndex, sizeof( pEncToUnicodeIndex ) );
4069             for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit )
4070             {
4071                 sal_uInt8 nEnc = fit->second.getGlyphId();
4072 
4073                 DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" );
4074                 DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" );
4075 
4076                 pGlyphIDs[ nEnc ] = fit->first;
4077                 pEncoding[ nEnc ] = nEnc;
4078                 pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size());
4079                 pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes();
4080                 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ )
4081                     aUnicodes.push_back( fit->second.getCode( n ) );
4082                 if( fit->second.getCode(0) )
4083                     nToUnicodeStream = 1;
4084                 if( nGlyphs < 256 )
4085                     nGlyphs++;
4086                 else
4087                 {
4088                     DBG_ERROR( "too many glyphs for subset" );
4089                 }
4090             }
4091             FontSubsetInfo aSubsetInfo;
4092             if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
4093             {
4094                 // create font stream
4095                 oslFileHandle aFontFile;
4096                 CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) );
4097                 // get file size
4098                 sal_uInt64 nLength1;
4099                 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) );
4100                 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength1 ) ) );
4101                 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
4102 
4103                 #if OSL_DEBUG_LEVEL > 1
4104                 emitComment( "PDFWriterImpl::emitFonts" );
4105                 #endif
4106                 sal_Int32 nFontStream = createObject();
4107                 sal_Int32 nStreamLengthObject = createObject();
4108                 CHECK_RETURN( updateObject( nFontStream ) );
4109                 aLine.setLength( 0 );
4110                 aLine.append( nFontStream );
4111                 aLine.append( " 0 obj\n"
4112                              "<</Length " );
4113                 aLine.append( (sal_Int32)nStreamLengthObject );
4114                 aLine.append( " 0 R"
4115                              #ifndef DEBUG_DISABLE_PDFCOMPRESSION
4116                              "/Filter/FlateDecode"
4117                              #endif
4118                              "/Length1 " );
4119 
4120                 sal_uInt64 nStartPos = 0;
4121                 if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF )
4122                 {
4123                     aLine.append( (sal_Int32)nLength1 );
4124 
4125                     aLine.append( ">>\n"
4126                                  "stream\n" );
4127                     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4128                     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
4129 
4130                     // copy font file
4131                     beginCompression();
4132                     checkAndEnableStreamEncryption( nFontStream );
4133                     sal_Bool bEOF = sal_False;
4134                     do
4135                     {
4136                         char buf[8192];
4137                         sal_uInt64 nRead;
4138                         CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) );
4139                         CHECK_RETURN( writeBuffer( buf, nRead ) );
4140                         CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) );
4141                     } while( ! bEOF );
4142                 }
4143                 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 )
4144                 {
4145                     // TODO: implement
4146                     DBG_ERROR( "PDFWriterImpl does not support CFF-font subsets yet!" );
4147                 }
4148                 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA?
4149                 {
4150                     boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] );
4151 
4152                     sal_uInt64 nBytesRead = 0;
4153                     CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer.get(), nLength1, &nBytesRead ) ) );
4154                     DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" );
4155                     CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
4156                     // get the PFB-segment lengths
4157                     ThreeInts aSegmentLengths = {0,0,0};
4158                     getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths );
4159                     // the lengths below are mandatory for PDF-exported Type1 fonts
4160                     // because the PFB segment headers get stripped! WhyOhWhy.
4161                     aLine.append( (sal_Int32)aSegmentLengths[0] );
4162                     aLine.append( "/Length2 " );
4163                     aLine.append( (sal_Int32)aSegmentLengths[1] );
4164                     aLine.append( "/Length3 " );
4165                     aLine.append( (sal_Int32)aSegmentLengths[2] );
4166 
4167                     aLine.append( ">>\n"
4168                                  "stream\n" );
4169                     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4170                     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
4171 
4172                     // emit PFB-sections without section headers
4173                     beginCompression();
4174                     checkAndEnableStreamEncryption( nFontStream );
4175                     CHECK_RETURN( writeBuffer( &pBuffer[6], aSegmentLengths[0] ) );
4176                     CHECK_RETURN( writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) );
4177                     CHECK_RETURN( writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) );
4178                 }
4179                 else
4180                 {
4181                     fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType);
4182                     aLine.append( "0 >>\nstream\n" );
4183                 }
4184 
4185                 endCompression();
4186                 disableStreamEncryption();
4187                 // close the file
4188                 osl_closeFile( aFontFile );
4189 
4190                 sal_uInt64 nEndPos = 0;
4191                 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) );
4192                 // end the stream
4193                 aLine.setLength( 0 );
4194                 aLine.append( "\nendstream\nendobj\n\n" );
4195                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4196 
4197                 // emit stream length object
4198                 CHECK_RETURN( updateObject( nStreamLengthObject ) );
4199                 aLine.setLength( 0 );
4200                 aLine.append( nStreamLengthObject );
4201                 aLine.append( " 0 obj\n" );
4202                 aLine.append( (sal_Int64)(nEndPos-nStartPos) );
4203                 aLine.append( "\nendobj\n\n" );
4204                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4205 
4206                 // write font descriptor
4207                 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream );
4208 
4209                 if( nToUnicodeStream )
4210                     nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs );
4211 
4212                 sal_Int32 nFontObject = createObject();
4213                 CHECK_RETURN( updateObject( nFontObject ) );
4214                 aLine.setLength( 0 );
4215                 aLine.append( nFontObject );
4216 
4217                 aLine.append( " 0 obj\n" );
4218                 aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ?
4219                              "<</Type/Font/Subtype/Type1/BaseFont/" :
4220                              "<</Type/Font/Subtype/TrueType/BaseFont/" );
4221                 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine );
4222                 aLine.append( "\n"
4223                              "/FirstChar 0\n"
4224                              "/LastChar " );
4225                 aLine.append( (sal_Int32)(nGlyphs-1) );
4226                 aLine.append( "\n"
4227                              "/Widths[" );
4228                 for( int i = 0; i < nGlyphs; i++ )
4229                 {
4230                     aLine.append( pWidths[ i ] );
4231                     aLine.append( ((i & 15) == 15) ? "\n" : " " );
4232                 }
4233                 aLine.append( "]\n"
4234                              "/FontDescriptor " );
4235                 aLine.append( nFontDescriptor );
4236                 aLine.append( " 0 R\n" );
4237                 if( nToUnicodeStream )
4238                 {
4239                     aLine.append( "/ToUnicode " );
4240                     aLine.append( nToUnicodeStream );
4241                     aLine.append( " 0 R\n" );
4242                 }
4243                 aLine.append( ">>\n"
4244                              "endobj\n\n" );
4245                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4246 
4247                 aFontIDToObject[ lit->m_nFontID ] = nFontObject;
4248             }
4249             else
4250             {
4251                 const ImplFontData* pFont = it->first;
4252                 rtl::OStringBuffer aErrorComment( 256 );
4253                 aErrorComment.append( "CreateFontSubset failed for font \"" );
4254                 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
4255                 aErrorComment.append( '\"' );
4256                 if( pFont->GetSlant() == ITALIC_NORMAL )
4257                     aErrorComment.append( " italic" );
4258                 else if( pFont->GetSlant() == ITALIC_OBLIQUE )
4259                     aErrorComment.append( " oblique" );
4260                 aErrorComment.append( " weight=" );
4261                 aErrorComment.append( sal_Int32(pFont->GetWeight()) );
4262                 emitComment( aErrorComment.getStr() );
4263             }
4264         }
4265     }
4266     osl_removeFile( aTmpName.pData );
4267 
4268     // emit embedded fonts
4269     for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit )
4270     {
4271         std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second );
4272         for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4273         {
4274             CHECK_RETURN( fit->second );
4275             aFontIDToObject[ fit->first ] = fit->second;
4276         }
4277     }
4278 
4279     // emit system fonts
4280     for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit )
4281     {
4282         std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second );
4283         for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4284         {
4285             CHECK_RETURN( fit->second );
4286             aFontIDToObject[ fit->first ] = fit->second;
4287         }
4288     }
4289 
4290     OStringBuffer aFontDict( 1024 );
4291     aFontDict.append( getFontDictObject() );
4292     aFontDict.append( " 0 obj\n"
4293                      "<<" );
4294     int ni = 0;
4295     for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit )
4296     {
4297         aFontDict.append( "/F" );
4298         aFontDict.append( mit->first );
4299         aFontDict.append( ' ' );
4300         aFontDict.append( mit->second );
4301         aFontDict.append( " 0 R" );
4302         if( ((++ni) & 7) == 0 )
4303             aFontDict.append( '\n' );
4304     }
4305     // emit builtin font for widget apperances / variable text
4306     for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin();
4307         it != m_aBuiltinFontToObjectMap.end(); ++it )
4308     {
4309         ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]);
4310         it->second = emitBuiltinFont( &aData, it->second );
4311     }
4312     appendBuiltinFontsToDict( aFontDict );
4313     aFontDict.append( "\n>>\nendobj\n\n" );
4314 
4315     CHECK_RETURN( updateObject( getFontDictObject() ) );
4316     CHECK_RETURN( writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) );
4317     return true;
4318 }
4319 
4320 sal_Int32 PDFWriterImpl::emitResources()
4321 {
4322     // emit shadings
4323     if( ! m_aGradients.empty() )
4324         CHECK_RETURN( emitGradients() );
4325     // emit tilings
4326     if( ! m_aTilings.empty() )
4327         CHECK_RETURN( emitTilings() );
4328 
4329     // emit font dict
4330     CHECK_RETURN( emitFonts() );
4331 
4332     // emit Resource dict
4333     OStringBuffer aLine( 512 );
4334     sal_Int32 nResourceDict = getResourceDictObj();
4335     CHECK_RETURN( updateObject( nResourceDict ) );
4336     aLine.setLength( 0 );
4337     aLine.append( nResourceDict );
4338     aLine.append( " 0 obj\n" );
4339     m_aGlobalResourceDict.append( aLine, getFontDictObject() );
4340     aLine.append( "endobj\n\n" );
4341     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4342     return nResourceDict;
4343 }
4344 
4345 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
4346                                                  sal_Int32 nItemLevel,
4347                                                  sal_Int32 nCurrentItemId )
4348 {
4349     /* The /Count number of an item is
4350        positive: the number of visible subitems
4351        negative: the negative number of subitems that will become visible if
4352                  the item gets opened
4353        see PDF ref 1.4 p 478
4354     */
4355 
4356     sal_Int32 nCount = 0;
4357 
4358     if( m_aContext.OpenBookmarkLevels < 0           || // all levels arevisible
4359         m_aContext.OpenBookmarkLevels >= nItemLevel    // this level is visible
4360       )
4361     {
4362         PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4363         sal_Int32 nChildren = rItem.m_aChildren.size();
4364         for( sal_Int32 i = 0; i < nChildren; i++ )
4365             nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4366         rCounts[nCurrentItemId] = nCount;
4367         // return 1 (this item) + visible sub items
4368         if( nCount < 0 )
4369             nCount = 0;
4370         nCount++;
4371     }
4372     else
4373     {
4374         // this bookmark level is invisible
4375         PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4376         sal_Int32 nChildren = rItem.m_aChildren.size();
4377         rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
4378         for( sal_Int32 i = 0; i < nChildren; i++ )
4379             updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4380         nCount = -1;
4381     }
4382 
4383     return nCount;
4384 }
4385 
4386 sal_Int32 PDFWriterImpl::emitOutline()
4387 {
4388     int i, nItems = m_aOutline.size();
4389 
4390     // do we have an outline at all ?
4391     if( nItems < 2 )
4392         return 0;
4393 
4394     // reserve object numbers for all outline items
4395     for( i = 0; i < nItems; ++i )
4396         m_aOutline[i].m_nObject = createObject();
4397 
4398     // update all parent, next and prev object ids
4399     for( i = 0; i < nItems; ++i )
4400     {
4401         PDFOutlineEntry& rItem = m_aOutline[i];
4402         int nChildren = rItem.m_aChildren.size();
4403 
4404         if( nChildren )
4405         {
4406             for( int n = 0; n < nChildren; ++n )
4407             {
4408                 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
4409 
4410                 rChild.m_nParentObject = rItem.m_nObject;
4411                 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
4412                 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
4413             }
4414 
4415         }
4416     }
4417 
4418     // calculate Count entries for all items
4419     std::vector< sal_Int32 > aCounts( nItems );
4420     updateOutlineItemCount( aCounts, 0, 0 );
4421 
4422     // emit hierarchy
4423     for( i = 0; i < nItems; ++i )
4424     {
4425         PDFOutlineEntry& rItem = m_aOutline[i];
4426         OStringBuffer aLine( 1024 );
4427 
4428         CHECK_RETURN( updateObject( rItem.m_nObject ) );
4429         aLine.append( rItem.m_nObject );
4430         aLine.append( " 0 obj\n" );
4431         aLine.append( "<<" );
4432         // number of visible children (all levels)
4433         if( i > 0 || aCounts[0] > 0 )
4434         {
4435             aLine.append( "/Count " );
4436             aLine.append( aCounts[i] );
4437         }
4438         if( ! rItem.m_aChildren.empty() )
4439         {
4440             // children list: First, Last
4441             aLine.append( "/First " );
4442             aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
4443             aLine.append( " 0 R/Last " );
4444             aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
4445             aLine.append( " 0 R\n" );
4446         }
4447         if( i > 0 )
4448         {
4449             // Title, Dest, Parent, Prev, Next
4450             aLine.append( "/Title" );
4451             appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
4452             aLine.append( "\n" );
4453             // Dest is not required
4454             if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() )
4455             {
4456                 aLine.append( "/Dest" );
4457                 appendDest( rItem.m_nDestID, aLine );
4458             }
4459             aLine.append( "/Parent " );
4460             aLine.append( rItem.m_nParentObject );
4461             aLine.append( " 0 R" );
4462             if( rItem.m_nPrevObject )
4463             {
4464                 aLine.append( "/Prev " );
4465                 aLine.append( rItem.m_nPrevObject );
4466                 aLine.append( " 0 R" );
4467             }
4468             if( rItem.m_nNextObject )
4469             {
4470                 aLine.append( "/Next " );
4471                 aLine.append( rItem.m_nNextObject );
4472                 aLine.append( " 0 R" );
4473             }
4474         }
4475         aLine.append( ">>\nendobj\n\n" );
4476         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4477     }
4478 
4479     return m_aOutline[0].m_nObject;
4480 }
4481 
4482 #undef CHECK_RETURN
4483 #define CHECK_RETURN( x ) if( !x ) return false
4484 
4485 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
4486 {
4487     if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() )
4488     {
4489 #if OSL_DEBUG_LEVEL > 1
4490         fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID );
4491 #endif
4492         return false;
4493     }
4494 
4495 
4496     const PDFDest& rDest		= m_aDests[ nDestID ];
4497     const PDFPage& rDestPage	= m_aPages[ rDest.m_nPage ];
4498 
4499     rBuffer.append( '[' );
4500     rBuffer.append( rDestPage.m_nPageObject );
4501     rBuffer.append( " 0 R" );
4502 
4503     switch( rDest.m_eType )
4504     {
4505         case PDFWriter::XYZ:
4506         default:
4507             rBuffer.append( "/XYZ " );
4508             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4509             rBuffer.append( ' ' );
4510             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4511             rBuffer.append( " 0" );
4512             break;
4513         case PDFWriter::Fit:
4514             rBuffer.append( "/Fit" );
4515             break;
4516         case PDFWriter::FitRectangle:
4517             rBuffer.append( "/FitR " );
4518             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4519             rBuffer.append( ' ' );
4520             appendFixedInt( rDest.m_aRect.Top(), rBuffer );
4521             rBuffer.append( ' ' );
4522             appendFixedInt( rDest.m_aRect.Right(), rBuffer );
4523             rBuffer.append( ' ' );
4524             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4525             break;
4526         case PDFWriter::FitHorizontal:
4527             rBuffer.append( "/FitH " );
4528             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4529             break;
4530         case PDFWriter::FitVertical:
4531             rBuffer.append( "/FitV " );
4532             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4533             break;
4534         case PDFWriter::FitPageBoundingBox:
4535             rBuffer.append( "/FitB" );
4536             break;
4537         case PDFWriter::FitPageBoundingBoxHorizontal:
4538             rBuffer.append( "/FitBH " );
4539             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4540             break;
4541         case PDFWriter::FitPageBoundingBoxVertical:
4542             rBuffer.append( "/FitBV " );
4543             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4544             break;
4545     }
4546     rBuffer.append( ']' );
4547 
4548     return true;
4549 }
4550 
4551 bool PDFWriterImpl::emitLinkAnnotations()
4552 {
4553     int nAnnots = m_aLinks.size();
4554     for( int i = 0; i < nAnnots; i++ )
4555     {
4556         const PDFLink& rLink			= m_aLinks[i];
4557         if( ! updateObject( rLink.m_nObject ) )
4558             continue;
4559 
4560         OStringBuffer aLine( 1024 );
4561         aLine.append( rLink.m_nObject );
4562         aLine.append( " 0 obj\n" );
4563 //i59651  key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4564 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4565         aLine.append( "<</Type/Annot" );
4566         if( m_bIsPDF_A1 )
4567             aLine.append( "/F 4" );
4568         aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
4569 
4570         appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
4571         aLine.append( ' ' );
4572         appendFixedInt( rLink.m_aRect.Top(), aLine );
4573         aLine.append( ' ' );
4574         appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
4575         aLine.append( ' ' );
4576         appendFixedInt( rLink.m_aRect.Bottom(), aLine );
4577         aLine.append( "]" );
4578         if( rLink.m_nDest >= 0 )
4579         {
4580             aLine.append( "/Dest" );
4581             appendDest( rLink.m_nDest, aLine );
4582         }
4583         else
4584         {
4585 /*--->i56629
4586 destination is external to the document, so
4587 we check in the following sequence:
4588 
4589  if target type is neither .pdf, nor .od[tpgs], then
4590           check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
4591                              end processing
4592  else if target is .od[tpgs]: then
4593       if conversion of type from od[tpgs]  to pdf is requested, convert it and this becomes the new target file
4594       processing continue
4595 
4596  if (new)target is .pdf : then
4597      if GotToR is requested, then
4598            convert the target in GoToR where the fragment of the URI is
4599            considered the named destination in the target file, set relative or absolute as requested
4600      else strip the fragment from URL and then set URI or 'launch application' as requested
4601 */
4602 //
4603 // FIXME: check if the decode mechanisms for URL processing throughout this implementation
4604 // are the correct one!!
4605 //
4606 // extract target file type
4607             INetURLObject aDocumentURL( m_aContext.BaseURL );
4608             INetURLObject aTargetURL( rLink.m_aURL );
4609             sal_Int32   nChangeFileExtensionToPDF = 0;
4610             sal_Int32   nSetGoToRMode = 0;
4611             sal_Bool    bTargetHasPDFExtension = sal_False;
4612             INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
4613 			sal_Bool    bIsUNCPath = sal_False;
4614 // check if the protocol is a known one, or if there is no protocol at all (on target only)
4615 // if there is no protocol, make the target relative to the current document directory
4616 // getting the needed URL information from the current document path
4617             if( eTargetProtocol == INET_PROT_NOT_VALID )
4618             {
4619 				if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.compareToAscii( "\\\\\\\\", 4 ) == 0)
4620 				{
4621 					bIsUNCPath = sal_True;
4622 				}
4623 				else
4624 				{
4625 					INetURLObject aNewBase( aDocumentURL );//duplicate document URL
4626 					aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
4627 											  //target document
4628 					aNewBase.insertName( rLink.m_aURL );
4629 					aTargetURL = aNewBase;//reassign the new target URL
4630 //recompute the target protocol, with the new URL
4631 //normal URL processing resumes
4632 					eTargetProtocol = aTargetURL.GetProtocol();
4633 				}
4634             }
4635 
4636             rtl::OUString aFileExtension = aTargetURL.GetFileExtension();
4637 
4638 // Check if the URL ends in '/': if yes it's a directory,
4639 // it will be forced to a URI link.
4640 // possibly a malformed URI, leave it as it is, force as URI
4641             if( aTargetURL.hasFinalSlash() )
4642                 m_aContext.DefaultLinkAction = PDFWriter::URIAction;
4643 
4644             if( aFileExtension.getLength() > 0 )
4645             {
4646                 if( m_aContext.ConvertOOoTargetToPDFTarget )
4647                 {
4648 //examine the file type (.odm .odt. .odp, odg, ods)
4649                     if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odm" ) ) ) )
4650                         nChangeFileExtensionToPDF++;
4651                     if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odt" ) ) ) )
4652                         nChangeFileExtensionToPDF++;
4653                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odp" ) ) ) )
4654                         nChangeFileExtensionToPDF++;
4655                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odg" ) ) ) )
4656                         nChangeFileExtensionToPDF++;
4657                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ods" ) ) ) )
4658                         nChangeFileExtensionToPDF++;
4659                     if( nChangeFileExtensionToPDF )
4660                         aTargetURL.setExtension(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4661                 }
4662 //check if extension is pdf, see if GoToR should be forced
4663                 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4664                 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
4665                     nSetGoToRMode++;
4666             }
4667 //prepare the URL, if relative or not
4668             INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
4669 //queue the string common to all types of actions
4670             aLine.append( "/A<</Type/Action/S");
4671 			if( bIsUNCPath ) // handle Win UNC paths
4672 			{
4673 				aLine.append( "/Launch/Win<</F" );
4674 				// INetURLObject is not good with UNC paths, use original path
4675 				appendLiteralStringEncrypt(  rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4676 				aLine.append( ">>" );
4677 			}
4678 			else
4679 			{
4680 			    bool bSetRelative = false;
4681 			    bool bFileSpec = false;
4682 //check if relative file link is requested and if the protocol is 'file://'
4683 				if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE )
4684 					bSetRelative = true;
4685 
4686 				rtl::OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is,
4687 				if( nSetGoToRMode == 0 )
4688 				{
4689 					switch( m_aContext.DefaultLinkAction )
4690 					{
4691 					default:
4692 					case PDFWriter::URIAction :
4693 					case PDFWriter::URIActionDestination :
4694 						aLine.append( "/URI/URI" );
4695 						break;
4696 					case PDFWriter::LaunchAction:
4697 // now:
4698 // if a launch action is requested and the hyperlink target has a fragment
4699 // and the target file does not have a pdf extension, or it's not a 'file:://' protocol
4700 // then force the uri action on it
4701 // This code will permit the correct opening of application on web pages, the one that
4702 // normally have fragments (but I may be wrong...)
4703 // and will force the use of URI when the protocol is not file://
4704 						if( (aFragment.getLength() > 0 && !bTargetHasPDFExtension) ||
4705 										eTargetProtocol != INET_PROT_FILE )
4706 							aLine.append( "/URI/URI" );
4707 						else
4708 						{
4709 							aLine.append( "/Launch/F" );
4710 							bFileSpec = true;
4711 						}
4712 						break;
4713 					}
4714 				}
4715 //fragment are encoded in the same way as in the named destination processing
4716 				if( nSetGoToRMode )
4717 				{//add the fragment
4718 				    rtl::OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET );
4719 					aLine.append("/GoToR");
4720 					aLine.append("/F");
4721 					bFileSpec = true;
4722 					appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark,
4723 																						 INetURLObject::WAS_ENCODED,
4724 																						 INetURLObject::DECODE_WITH_CHARSET ) :
4725 																   aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4726 					if( aFragment.getLength() > 0 )
4727 					{
4728 						aLine.append("/D/");
4729 						appendDestinationName( aFragment , aLine );
4730 					}
4731 				}
4732 				else
4733 				{
4734 // change the fragment to accomodate the bookmark (only if the file extension is PDF and
4735 // the requested action is of the correct type)
4736 					if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination &&
4737 							   bTargetHasPDFExtension && aFragment.getLength() > 0 )
4738 					{
4739 						OStringBuffer aLineLoc( 1024 );
4740 						appendDestinationName( aFragment , aLineLoc );
4741 //substitute the fragment
4742 						aTargetURL.SetMark( aLineLoc.getStr() );
4743 					}
4744 					rtl::OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE );
4745 // check if we have a URL available, if the string is empty, set it as the original one
4746 //                 if( aURL.getLength() == 0 )
4747 //                     appendLiteralStringEncrypt( rLink.m_aURL , rLink.m_nObject, aLine );
4748 //                 else
4749 						appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL,
4750 						                                                                    INetURLObject::WAS_ENCODED,
4751 						                                                                    bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE
4752 						                                                                    ) :
4753 																   aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4754 				}
4755 //<--- i56629
4756 			}
4757             aLine.append( ">>\n" );
4758         }
4759         if( rLink.m_nStructParent > 0 )
4760         {
4761             aLine.append( "/StructParent " );
4762             aLine.append( rLink.m_nStructParent );
4763         }
4764         aLine.append( ">>\nendobj\n\n" );
4765         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4766     }
4767 
4768     return true;
4769 }
4770 
4771 bool PDFWriterImpl::emitNoteAnnotations()
4772 {
4773     // emit note annotations
4774     int nAnnots = m_aNotes.size();
4775     for( int i = 0; i < nAnnots; i++ )
4776     {
4777         const PDFNoteEntry& rNote		= m_aNotes[i];
4778         if( ! updateObject( rNote.m_nObject ) )
4779             return false;
4780 
4781         OStringBuffer aLine( 1024 );
4782         aLine.append( rNote.m_nObject );
4783         aLine.append( " 0 obj\n" );
4784 //i59651  key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4785 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4786         aLine.append( "<</Type/Annot" );
4787         if( m_bIsPDF_A1 )
4788             aLine.append( "/F 4" );
4789         aLine.append( "/Subtype/Text/Rect[" );
4790 
4791         appendFixedInt( rNote.m_aRect.Left(), aLine );
4792         aLine.append( ' ' );
4793         appendFixedInt( rNote.m_aRect.Top(), aLine );
4794         aLine.append( ' ' );
4795         appendFixedInt( rNote.m_aRect.Right(), aLine );
4796         aLine.append( ' ' );
4797         appendFixedInt( rNote.m_aRect.Bottom(), aLine );
4798         aLine.append( "]" );
4799 
4800         // contents of the note (type text string)
4801         aLine.append( "/Contents\n" );
4802         appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
4803         aLine.append( "\n" );
4804 
4805         // optional title
4806         if( rNote.m_aContents.Title.Len() )
4807         {
4808             aLine.append( "/T" );
4809             appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
4810             aLine.append( "\n" );
4811         }
4812 
4813         aLine.append( ">>\nendobj\n\n" );
4814         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4815     }
4816     return true;
4817 }
4818 
4819 Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font&  rAppSetFont )
4820 {
4821     bool bAdjustSize = false;
4822 
4823     Font aFont( rControlFont );
4824     if( ! aFont.GetName().Len() )
4825     {
4826         aFont = rAppSetFont;
4827         if( rControlFont.GetHeight() )
4828             aFont.SetSize( Size( 0, rControlFont.GetHeight() ) );
4829         else
4830             bAdjustSize = true;
4831         if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
4832             aFont.SetItalic( rControlFont.GetItalic() );
4833         if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
4834             aFont.SetWeight( rControlFont.GetWeight() );
4835     }
4836     else if( ! aFont.GetHeight() )
4837     {
4838         aFont.SetSize( rAppSetFont.GetSize() );
4839         bAdjustSize = true;
4840     }
4841     if( bAdjustSize )
4842     {
4843         Size aFontSize = aFont.GetSize();
4844         OutputDevice* pDefDev = Application::GetDefaultDevice();
4845         aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
4846         aFont.SetSize( aFontSize );
4847     }
4848     return aFont;
4849 }
4850 
4851 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont )
4852 {
4853     sal_Int32 nBest = 4; // default to Helvetica
4854     OUString aFontName( rFont.GetName() );
4855     aFontName = aFontName.toAsciiLowerCase();
4856 
4857     if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "times" ) ) ) != -1 )
4858         nBest = 8;
4859     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "courier" ) ) ) != -1 )
4860         nBest = 0;
4861     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "dingbats" ) ) ) != -1 )
4862         nBest = 13;
4863     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "symbol" ) ) ) != -1 )
4864         nBest = 12;
4865     if( nBest < 12 )
4866     {
4867         if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
4868             nBest += 1;
4869         if( rFont.GetWeight() > WEIGHT_MEDIUM )
4870             nBest += 2;
4871     }
4872 
4873     if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() )
4874         m_aBuiltinFontToObjectMap[ nBest ] = createObject();
4875 
4876     return nBest;
4877 }
4878 
4879 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
4880 {
4881     return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1;
4882 }
4883 
4884 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget )
4885 {
4886     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4887 
4888     // save graphics state
4889     push( sal::static_int_cast<sal_uInt16>(~0U) );
4890 
4891     // transform relative to control's coordinates since an
4892     // appearance stream is a form XObject
4893     // this relies on the m_aRect member of rButton NOT already being transformed
4894     // to default user space
4895     if( rWidget.Background || rWidget.Border )
4896     {
4897         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) );
4898         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) );
4899         drawRectangle( rWidget.Location );
4900     }
4901     // prepare font to use
4902     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
4903     setFont( aFont );
4904     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
4905 
4906     drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
4907 
4908     // create DA string while local mapmode is still in place
4909     // (that is before endRedirect())
4910     OStringBuffer aDA( 256 );
4911     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
4912     Font aDummyFont( String( RTL_CONSTASCII_USTRINGPARAM( "Helvetica" ) ), aFont.GetSize() );
4913     sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont );
4914     aDA.append( ' ' );
4915     aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() );
4916     aDA.append( ' ' );
4917     m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4918     aDA.append( " Tf" );
4919     rButton.m_aDAString = aDA.makeStringAndClear();
4920 
4921     pop();
4922 
4923     rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
4924 
4925     /* seems like a bad hack but at least works in both AR5 and 6:
4926        we draw the button ourselves and tell AR
4927        the button would be totally transparent with no text
4928 
4929        One would expect that simply setting a normal appearance
4930        should suffice, but no, as soon as the user actually presses
4931        the button and an action is tied to it (gasp! a button that
4932        does something) the appearance gets replaced by some crap that AR
4933        creates on the fly even if no DA or MK is given. On AR6 at least
4934        the DA and MK work as expected, but on AR5 this creates a region
4935        filled with the background color but nor text. Urgh.
4936     */
4937     rButton.m_aMKDict = "/BC [] /BG [] /CA";
4938     rButton.m_aMKDictCAString = "";
4939 }
4940 
4941 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern,
4942                                      const PDFWriter::AnyWidget& rWidget,
4943                                      const StyleSettings& rSettings )
4944 {
4945     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
4946 
4947     if( rWidget.Background || rWidget.Border )
4948     {
4949         if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) )
4950         {
4951             sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500;
4952             if( nDelta < 1 )
4953                 nDelta = 1;
4954             setLineColor( Color( COL_TRANSPARENT ) );
4955             Rectangle aRect = rIntern.m_aRect;
4956             setFillColor( rSettings.GetLightBorderColor() );
4957             drawRectangle( aRect );
4958             aRect.Left()  += nDelta; aRect.Top()     += nDelta;
4959             aRect.Right() -= nDelta; aRect.Bottom()  -= nDelta;
4960             setFillColor( rSettings.GetFieldColor() );
4961             drawRectangle( aRect );
4962             setFillColor( rSettings.GetLightColor() );
4963             drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
4964             drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
4965             setFillColor( rSettings.GetDarkShadowColor() );
4966             drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
4967             drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
4968         }
4969         else
4970         {
4971             setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) );
4972             setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4973             drawRectangle( rIntern.m_aRect );
4974         }
4975 
4976         if( rWidget.Border )
4977         {
4978             // adjust edit area accounting for border
4979             sal_Int32 nDelta = aFont.GetHeight()/4;
4980             if( nDelta < 1 )
4981                 nDelta = 1;
4982             rIntern.m_aRect.Left()	+= nDelta;
4983             rIntern.m_aRect.Top()	+= nDelta;
4984             rIntern.m_aRect.Right()	-= nDelta;
4985             rIntern.m_aRect.Bottom()-= nDelta;
4986         }
4987     }
4988     return aFont;
4989 }
4990 
4991 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget )
4992 {
4993     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4994     SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
4995 
4996     push( sal::static_int_cast<sal_uInt16>(~0U) );
4997 
4998     // prepare font to use, draw field border
4999     Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
5000     sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
5001 
5002     // prepare DA string
5003     OStringBuffer aDA( 32 );
5004     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
5005     aDA.append( ' ' );
5006     if( m_aContext.FieldsUseSystemFonts )
5007     {
5008         aDA.append( "/F" );
5009         aDA.append( nBest );
5010 
5011         OStringBuffer aDR( 32 );
5012         aDR.append( "/Font " );
5013         aDR.append( getFontDictObject() );
5014         aDR.append( " 0 R" );
5015         rEdit.m_aDRDict = aDR.makeStringAndClear();
5016     }
5017     else
5018         aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5019     aDA.append( ' ' );
5020     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
5021     aDA.append( " Tf" );
5022 
5023     /*  create an empty appearance stream, let the viewer create
5024         the appearance at runtime. This is because AR5 seems to
5025         paint the widget appearance always, and a dynamically created
5026         appearance on top of it. AR6 is well behaved in that regard, so
5027         that behaviour seems to be a bug. Anyway this empty appearance
5028         relies on /NeedAppearances in the AcroForm dictionary set to "true"
5029      */
5030     beginRedirect( pEditStream, rEdit.m_aRect );
5031     OStringBuffer aAppearance( 32 );
5032     aAppearance.append( "/Tx BMC\nEMC\n" );
5033     writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5034 
5035     endRedirect();
5036     pop();
5037 
5038     rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
5039 
5040     rEdit.m_aDAString = aDA.makeStringAndClear();
5041 }
5042 
5043 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget )
5044 {
5045     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5046     SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
5047 
5048     push( sal::static_int_cast<sal_uInt16>(~0U) );
5049 
5050     // prepare font to use, draw field border
5051     Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
5052     sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
5053 
5054     beginRedirect( pListBoxStream, rBox.m_aRect );
5055     OStringBuffer aAppearance( 64 );
5056 
5057 #if 0
5058     if( ! rWidget.DropDown )
5059     {
5060         // prepare linewidth for DA string hack, see below
5061         Size aFontSize = lcl_convert( m_aGraphicsStack.front().m_aMapMode,
5062                                       m_aMapMode,
5063                                       getReferenceDevice(),
5064                                       Size( 0, aFont.GetHeight() ) );
5065         sal_Int32 nLW = aFontSize.Height() / 40;
5066         appendFixedInt( nLW > 0 ? nLW : 1, aAppearance );
5067         aAppearance.append( " w\n" );
5068         writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5069         aAppearance.setLength( 0 );
5070     }
5071 #endif
5072 
5073     setLineColor( Color( COL_TRANSPARENT ) );
5074     setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
5075     drawRectangle( rBox.m_aRect );
5076 
5077     // empty appearance, see createDefaultEditAppearance for reference
5078     aAppearance.append( "/Tx BMC\nEMC\n" );
5079     writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5080 
5081     endRedirect();
5082     pop();
5083 
5084     rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
5085 
5086     // prepare DA string
5087     OStringBuffer aDA( 256 );
5088 #if 0
5089     if( !rWidget.DropDown )
5090     {
5091         /* another of AR5's peculiarities: the selected item of a choice
5092            field is highlighted using the non stroking color - same as the
5093            text color. so workaround that by using text rendering mode 2
5094            (fill, then stroke) and set the stroking color
5095          */
5096         appendStrokingColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ), aDA );
5097         aDA.append( " 2 Tr " );
5098     }
5099 #endif
5100     // prepare DA string
5101     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
5102     aDA.append( ' ' );
5103     if( m_aContext.FieldsUseSystemFonts )
5104     {
5105         aDA.append( "/F" );
5106         aDA.append( nBest );
5107 
5108         OStringBuffer aDR( 32 );
5109         aDR.append( "/Font " );
5110         aDR.append( getFontDictObject() );
5111         aDR.append( " 0 R" );
5112         rBox.m_aDRDict = aDR.makeStringAndClear();
5113     }
5114     else
5115         aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5116     aDA.append( ' ' );
5117     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
5118     aDA.append( " Tf" );
5119     rBox.m_aDAString = aDA.makeStringAndClear();
5120 }
5121 
5122 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget )
5123 {
5124     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5125 
5126     // save graphics state
5127     push( sal::static_int_cast<sal_uInt16>(~0U) );
5128 
5129     if( rWidget.Background || rWidget.Border )
5130     {
5131         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5132         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5133         drawRectangle( rBox.m_aRect );
5134     }
5135 
5136     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5137     setFont( aFont );
5138     Size aFontSize = aFont.GetSize();
5139     if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5140         aFontSize.Height() = rBox.m_aRect.GetHeight();
5141     sal_Int32 nDelta = aFontSize.Height()/10;
5142     if( nDelta < 1 )
5143         nDelta = 1;
5144 
5145     Rectangle aCheckRect, aTextRect;
5146     if( rWidget.ButtonIsLeft )
5147     {
5148         aCheckRect.Left()	= rBox.m_aRect.Left() + nDelta;
5149         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5150         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5151         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5152 
5153         // #i74206# handle small controls without text area
5154         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5155         {
5156             aCheckRect.Right()  -= nDelta;
5157             aCheckRect.Top()    += nDelta/2;
5158             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5159         }
5160 
5161         aTextRect.Left()	= rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5162         aTextRect.Top()		= rBox.m_aRect.Top();
5163         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5164         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5165     }
5166     else
5167     {
5168         aCheckRect.Left()	= rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5169         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5170         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5171         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5172 
5173         // #i74206# handle small controls without text area
5174         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5175         {
5176             aCheckRect.Left()   += nDelta;
5177             aCheckRect.Top()    += nDelta/2;
5178             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5179         }
5180 
5181         aTextRect.Left()	= rBox.m_aRect.Left();
5182         aTextRect.Top()		= rBox.m_aRect.Top();
5183         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5184         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5185     }
5186     setLineColor( Color( COL_BLACK ) );
5187     setFillColor( Color( COL_TRANSPARENT ) );
5188     OStringBuffer aLW( 32 );
5189     aLW.append( "q " );
5190     m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
5191     aLW.append( " w " );
5192     writeBuffer( aLW.getStr(), aLW.getLength() );
5193     drawRectangle( aCheckRect );
5194     writeBuffer( " Q\n", 3 );
5195     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5196     drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5197 
5198     pop();
5199 
5200     OStringBuffer aDA( 256 );
5201     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5202     sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
5203     aDA.append( ' ' );
5204     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5205     aDA.append( " 0 Tf" );
5206     rBox.m_aDAString = aDA.makeStringAndClear();
5207     rBox.m_aMKDict = "/CA";
5208     rBox.m_aMKDictCAString = "8";
5209     rBox.m_aRect = aCheckRect;
5210 
5211     // create appearance streams
5212     sal_Char cMark = '8';
5213     sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)];
5214     nCharXOffset *= aCheckRect.GetHeight();
5215     nCharXOffset /= 2000;
5216     sal_Int32 nCharYOffset = 1000-
5217         (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative
5218     nCharYOffset *= aCheckRect.GetHeight();
5219     nCharYOffset /= 2000;
5220 
5221     SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5222     beginRedirect( pCheckStream, aCheckRect );
5223     aDA.append( "/Tx BMC\nq BT\n" );
5224     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5225     aDA.append( ' ' );
5226     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5227     aDA.append( ' ' );
5228     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5229     aDA.append( " Tf\n" );
5230     m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
5231 	aDA.append( " " );
5232 	m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
5233 	aDA.append( " Td (" );
5234 	aDA.append( cMark );
5235 	aDA.append( ") Tj\nET\nQ\nEMC\n" );
5236     writeBuffer( aDA.getStr(), aDA.getLength() );
5237     endRedirect();
5238     rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5239 
5240     SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5241     beginRedirect( pUncheckStream, aCheckRect );
5242     writeBuffer( "/Tx BMC\nEMC\n", 12 );
5243     endRedirect();
5244     rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5245 }
5246 
5247 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget )
5248 {
5249     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5250 
5251     // save graphics state
5252     push( sal::static_int_cast<sal_uInt16>(~0U) );
5253 
5254     if( rWidget.Background || rWidget.Border )
5255     {
5256         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5257         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5258         drawRectangle( rBox.m_aRect );
5259     }
5260 
5261     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5262     setFont( aFont );
5263     Size aFontSize = aFont.GetSize();
5264     if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5265         aFontSize.Height() = rBox.m_aRect.GetHeight();
5266     sal_Int32 nDelta = aFontSize.Height()/10;
5267     if( nDelta < 1 )
5268         nDelta = 1;
5269 
5270     Rectangle aCheckRect, aTextRect;
5271     if( rWidget.ButtonIsLeft )
5272     {
5273         aCheckRect.Left()	= rBox.m_aRect.Left() + nDelta;
5274         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5275         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5276         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5277 
5278         // #i74206# handle small controls without text area
5279         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5280         {
5281             aCheckRect.Right()  -= nDelta;
5282             aCheckRect.Top()    += nDelta/2;
5283             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5284         }
5285 
5286         aTextRect.Left()	= rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5287         aTextRect.Top()		= rBox.m_aRect.Top();
5288         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5289         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5290     }
5291     else
5292     {
5293         aCheckRect.Left()	= rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5294         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5295         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5296         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5297 
5298         // #i74206# handle small controls without text area
5299         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5300         {
5301             aCheckRect.Left()   += nDelta;
5302             aCheckRect.Top()    += nDelta/2;
5303             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5304         }
5305 
5306         aTextRect.Left()	= rBox.m_aRect.Left();
5307         aTextRect.Top()		= rBox.m_aRect.Top();
5308         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5309         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5310     }
5311     setLineColor( Color( COL_BLACK ) );
5312     setFillColor( Color( COL_TRANSPARENT ) );
5313     OStringBuffer aLW( 32 );
5314     aLW.append( "q " );
5315     m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
5316     aLW.append( " w " );
5317     writeBuffer( aLW.getStr(), aLW.getLength() );
5318     drawEllipse( aCheckRect );
5319     writeBuffer( " Q\n", 3 );
5320     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5321     drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5322 
5323     pop();
5324 
5325     OStringBuffer aDA( 256 );
5326     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5327     sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
5328     aDA.append( ' ' );
5329     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5330     aDA.append( " 0 Tf" );
5331     rBox.m_aDAString = aDA.makeStringAndClear();
5332 //to encrypt this (el)
5333     rBox.m_aMKDict = "/CA";
5334 //after this assignement, to m_aMKDic cannot be added anything
5335     rBox.m_aMKDictCAString = "l";
5336 
5337     rBox.m_aRect = aCheckRect;
5338 
5339     // create appearance streams
5340     push( sal::static_int_cast<sal_uInt16>(~0U) );
5341     SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5342 
5343     beginRedirect( pCheckStream, aCheckRect );
5344     aDA.append( "/Tx BMC\nq BT\n" );
5345     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5346     aDA.append( ' ' );
5347     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5348     aDA.append( ' ' );
5349     m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5350     aDA.append( " Tf\n0 0 Td\nET\nQ\n" );
5351     writeBuffer( aDA.getStr(), aDA.getLength() );
5352     setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5353     setLineColor( Color( COL_TRANSPARENT ) );
5354     aCheckRect.Left()	+= 3*nDelta;
5355     aCheckRect.Top()	+= 3*nDelta;
5356     aCheckRect.Bottom()	-= 3*nDelta;
5357     aCheckRect.Right()	-= 3*nDelta;
5358     drawEllipse( aCheckRect );
5359     writeBuffer( "\nEMC\n", 5 );
5360     endRedirect();
5361 
5362     pop();
5363     rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5364 
5365     SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5366     beginRedirect( pUncheckStream, aCheckRect );
5367     writeBuffer( "/Tx BMC\nEMC\n", 12 );
5368     endRedirect();
5369     rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5370 }
5371 
5372 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
5373 {
5374 
5375     // TODO: check and insert default streams
5376     rtl::OString aStandardAppearance;
5377     switch( rWidget.m_eType )
5378     {
5379         case PDFWriter::CheckBox:
5380             aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
5381             break;
5382         default:
5383             break;
5384     }
5385 
5386     if( rWidget.m_aAppearances.size() )
5387     {
5388         rAnnotDict.append( "/AP<<\n" );
5389         for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it )
5390         {
5391             rAnnotDict.append( "/" );
5392             rAnnotDict.append( dict_it->first );
5393             bool bUseSubDict = (dict_it->second.size() > 1);
5394             rAnnotDict.append( bUseSubDict ? "<<" : " " );
5395 
5396             for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin();
5397                  stream_it != dict_it->second.end(); ++stream_it )
5398             {
5399                 SvMemoryStream* pApppearanceStream = stream_it->second;
5400                 dict_it->second[ stream_it->first ] = NULL;
5401 
5402                 bool bDeflate = compressStream( pApppearanceStream );
5403 
5404                 pApppearanceStream->Seek( STREAM_SEEK_TO_END );
5405                 sal_Int64 nStreamLen = pApppearanceStream->Tell();
5406                 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
5407                 sal_Int32 nObject = createObject();
5408                 CHECK_RETURN( updateObject( nObject ) );
5409                 #if OSL_DEBUG_LEVEL > 1
5410                 emitComment( "PDFWriterImpl::emitAppearances" );
5411                 #endif
5412                 OStringBuffer aLine;
5413                 aLine.append( nObject );
5414 
5415                 aLine.append( " 0 obj\n"
5416                               "<</Type/XObject\n"
5417                               "/Subtype/Form\n"
5418                               "/BBox[0 0 " );
5419                 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
5420                 aLine.append( " " );
5421                 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
5422                 aLine.append( "]\n"
5423                               "/Resources " );
5424                 aLine.append( getResourceDictObj() );
5425                 aLine.append( " 0 R\n"
5426                               "/Length " );
5427                 aLine.append( nStreamLen );
5428                 aLine.append( "\n" );
5429                 if( bDeflate )
5430                     aLine.append( "/Filter/FlateDecode\n" );
5431                 aLine.append( ">>\nstream\n" );
5432                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5433                 checkAndEnableStreamEncryption( nObject );
5434                 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
5435                 disableStreamEncryption();
5436                 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
5437 
5438                 if( bUseSubDict )
5439                 {
5440                     rAnnotDict.append( " /" );
5441                     rAnnotDict.append( stream_it->first );
5442                     rAnnotDict.append( " " );
5443                 }
5444                 rAnnotDict.append( nObject );
5445                 rAnnotDict.append( " 0 R" );
5446 
5447                 delete pApppearanceStream;
5448             }
5449 
5450             rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
5451         }
5452         rAnnotDict.append( ">>\n" );
5453         if( aStandardAppearance.getLength() )
5454         {
5455             rAnnotDict.append( "/AS /" );
5456             rAnnotDict.append( aStandardAppearance );
5457             rAnnotDict.append( "\n" );
5458         }
5459     }
5460 
5461     return true;
5462 }
5463 
5464 bool PDFWriterImpl::emitWidgetAnnotations()
5465 {
5466     ensureUniqueRadioOnValues();
5467 
5468     int nAnnots = m_aWidgets.size();
5469     for( int a = 0; a < nAnnots; a++ )
5470     {
5471         PDFWidget& rWidget = m_aWidgets[a];
5472 
5473         OStringBuffer aLine( 1024 );
5474         OStringBuffer aValue( 256 );
5475         aLine.append( rWidget.m_nObject );
5476         aLine.append( " 0 obj\n"
5477                       "<<" );
5478         if( rWidget.m_eType != PDFWriter::Hierarchy )
5479         {
5480             // emit widget annotation only for terminal fields
5481             if( rWidget.m_aKids.empty() )
5482             {
5483                 aLine.append( "/Type/Annot/Subtype/Widget/F 4\n"
5484                               "/Rect[" );
5485                 appendFixedInt( rWidget.m_aRect.Left()-1, aLine );
5486                 aLine.append( ' ' );
5487                 appendFixedInt( rWidget.m_aRect.Top()+1, aLine );
5488                 aLine.append( ' ' );
5489                 appendFixedInt( rWidget.m_aRect.Right()+1, aLine );
5490                 aLine.append( ' ' );
5491                 appendFixedInt( rWidget.m_aRect.Bottom()-1, aLine );
5492                 aLine.append( "]\n" );
5493             }
5494             aLine.append( "/FT/" );
5495             switch( rWidget.m_eType )
5496             {
5497                 case PDFWriter::RadioButton:
5498                 case PDFWriter::CheckBox:
5499                     // for radio buttons only the RadioButton field, not the
5500                     // CheckBox children should have a value, else acrobat reader
5501                     // does not always check the right button
5502                     // of course real check boxes (not belonging to a readio group)
5503                     // need their values, too
5504                     if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
5505                     {
5506                         aValue.append( "/" );
5507                         // check for radio group with all buttons unpressed
5508                         if( rWidget.m_aValue.getLength() == 0 )
5509                             aValue.append( "Off" );
5510                         else
5511                             appendName( rWidget.m_aValue, aValue );
5512                     }
5513                 case PDFWriter::PushButton:
5514                     aLine.append( "Btn" );
5515                     break;
5516                 case PDFWriter::ListBox:
5517                     if( rWidget.m_nFlags & 0x200000 ) // multiselect
5518                     {
5519                         aValue.append( "[" );
5520                         for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
5521                         {
5522                             sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
5523                             if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
5524                                 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
5525                         }
5526                         aValue.append( "]" );
5527                     }
5528                     else if( rWidget.m_aSelectedEntries.size() > 0 &&
5529                              rWidget.m_aSelectedEntries[0] >= 0 &&
5530                              rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
5531                     {
5532                         appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
5533                     }
5534                     else
5535                         appendUnicodeTextStringEncrypt( rtl::OUString(), rWidget.m_nObject, aValue );
5536                     aLine.append( "Ch" );
5537                     break;
5538                 case PDFWriter::ComboBox:
5539                     appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5540                     aLine.append( "Ch" );
5541                     break;
5542                 case PDFWriter::Edit:
5543                     aLine.append( "Tx" );
5544                     appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5545                     break;
5546                 case PDFWriter::Hierarchy: // make the compiler happy
5547                     break;
5548             }
5549             aLine.append( "\n" );
5550             aLine.append( "/P " );
5551             aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
5552             aLine.append( " 0 R\n" );
5553         }
5554         if( rWidget.m_nParent )
5555         {
5556             aLine.append( "/Parent " );
5557             aLine.append( rWidget.m_nParent );
5558             aLine.append( " 0 R\n" );
5559         }
5560         if( rWidget.m_aKids.size() )
5561         {
5562             aLine.append( "/Kids[" );
5563             for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ )
5564             {
5565                 aLine.append( rWidget.m_aKids[i] );
5566                 aLine.append( " 0 R" );
5567                 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5568             }
5569             aLine.append( "]\n" );
5570         }
5571         if( rWidget.m_aName.getLength() )
5572         {
5573             aLine.append( "/T" );
5574             appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
5575             aLine.append( "\n" );
5576         }
5577         if( m_aContext.Version > PDFWriter::PDF_1_2 && rWidget.m_aDescription.getLength() )
5578         {
5579             // the alternate field name should be unicode able since it is
5580             // supposed to be used in UI
5581             aLine.append( "/TU" );
5582             appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
5583             aLine.append( "\n" );
5584         }
5585 
5586         if( rWidget.m_nFlags )
5587         {
5588             aLine.append( "/Ff " );
5589             aLine.append( rWidget.m_nFlags );
5590             aLine.append( "\n" );
5591         }
5592         if( aValue.getLength() )
5593         {
5594             OString aVal = aValue.makeStringAndClear();
5595             aLine.append( "/V " );
5596             aLine.append( aVal );
5597             aLine.append( "\n"
5598                           "/DV " );
5599             aLine.append( aVal );
5600             aLine.append( "\n" );
5601         }
5602         if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
5603         {
5604             sal_Int32 nTI = -1;
5605             aLine.append( "/Opt[\n" );
5606             sal_Int32 i = 0;
5607             for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i )
5608             {
5609                 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine );
5610                 aLine.append( "\n" );
5611                 if( *it == rWidget.m_aValue )
5612                     nTI = i;
5613             }
5614             aLine.append( "]\n" );
5615             if( nTI > 0 )
5616             {
5617                 aLine.append( "/TI " );
5618                 aLine.append( nTI );
5619                 aLine.append( "\n" );
5620                 if( rWidget.m_nFlags & 0x200000 ) // Multiselect
5621                 {
5622                     aLine.append( "/I [" );
5623                     aLine.append( nTI );
5624                     aLine.append( "]\n" );
5625                 }
5626             }
5627         }
5628         if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
5629         {
5630             aLine.append( "/MaxLen " );
5631             aLine.append( rWidget.m_nMaxLen );
5632             aLine.append( "\n" );
5633         }
5634         if( rWidget.m_eType == PDFWriter::PushButton )
5635         {
5636             if(!m_bIsPDF_A1)
5637             {
5638                 OStringBuffer aDest;
5639                 if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) )
5640                 {
5641                     aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
5642                     aLine.append( aDest.makeStringAndClear() );
5643                     aLine.append( ">>>>\n" );
5644                 }
5645                 else if( rWidget.m_aListEntries.empty() )
5646                 {
5647                     // create a reset form action
5648                     aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
5649                 }
5650                 else if( rWidget.m_bSubmit )
5651                 {
5652                     // create a submit form action
5653                     aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
5654                     appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() );
5655                     aLine.append( "/Flags " );
5656 
5657                     sal_Int32 nFlags = 0;
5658                     switch( m_aContext.SubmitFormat )
5659                     {
5660                     case PDFWriter::HTML:
5661                         nFlags |= 4;
5662                         break;
5663                     case PDFWriter::XML:
5664                         if( m_aContext.Version > PDFWriter::PDF_1_3 )
5665                             nFlags |= 32;
5666                         break;
5667                     case PDFWriter::PDF:
5668                         if( m_aContext.Version > PDFWriter::PDF_1_3 )
5669                             nFlags |= 256;
5670                         break;
5671                     case PDFWriter::FDF:
5672                     default:
5673                         break;
5674                     }
5675                     if( rWidget.m_bSubmitGet )
5676                         nFlags |= 8;
5677                     aLine.append( nFlags );
5678                     aLine.append( ">>>>\n" );
5679                 }
5680                 else
5681                 {
5682                     // create a URI action
5683                     aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
5684                     aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
5685                     aLine.append( ")>>>>\n" );
5686                 }
5687             }
5688             else
5689                 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA );
5690         }
5691         if( rWidget.m_aDAString.getLength() )
5692         {
5693             if( rWidget.m_aDRDict.getLength() )
5694             {
5695                 aLine.append( "/DR<<" );
5696                 aLine.append( rWidget.m_aDRDict );
5697                 aLine.append( ">>\n" );
5698             }
5699             else
5700             {
5701                 aLine.append( "/DR<</Font<<" );
5702                 appendBuiltinFontsToDict( aLine );
5703                 aLine.append( ">>>>\n" );
5704             }
5705             aLine.append( "/DA" );
5706             appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
5707             aLine.append( "\n" );
5708             if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER )
5709                 aLine.append( "/Q 1\n" );
5710             else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT )
5711                 aLine.append( "/Q 2\n" );
5712         }
5713         // appearance charactristics for terminal fields
5714         // which are supposed to have an appearance constructed
5715         // by the viewer application
5716         if( rWidget.m_aMKDict.getLength() )
5717         {
5718             aLine.append( "/MK<<" );
5719             aLine.append( rWidget.m_aMKDict );
5720 //add the CA string, encrypting it
5721             appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
5722             aLine.append( ">>\n" );
5723         }
5724 
5725         CHECK_RETURN( emitAppearances( rWidget, aLine ) );
5726 
5727         aLine.append( ">>\n"
5728                       "endobj\n\n" );
5729         CHECK_RETURN( updateObject( rWidget.m_nObject ) );
5730         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5731     }
5732     return true;
5733 }
5734 
5735 bool PDFWriterImpl::emitAnnotations()
5736 {
5737     if( m_aPages.size() < 1 )
5738         return false;
5739 
5740     CHECK_RETURN( emitLinkAnnotations() );
5741 
5742     CHECK_RETURN( emitNoteAnnotations() );
5743 
5744     CHECK_RETURN( emitWidgetAnnotations() );
5745 
5746     return true;
5747 }
5748 
5749 #undef CHECK_RETURN
5750 #define CHECK_RETURN( x ) if( !x ) return false
5751 
5752 bool PDFWriterImpl::emitCatalog()
5753 {
5754     // build page tree
5755     // currently there is only one node that contains all leaves
5756 
5757     // first create a page tree node id
5758     sal_Int32 nTreeNode = createObject();
5759 
5760     // emit global resource dictionary (page emit needs it)
5761     CHECK_RETURN( emitResources() );
5762 
5763     // emit all pages
5764     for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it )
5765         if( ! it->emit( nTreeNode ) )
5766             return false;
5767 
5768     sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
5769 
5770     sal_Int32 nOutlineDict = emitOutline();
5771 
5772     //emit Output intent i59651
5773     sal_Int32 nOutputIntentObject = emitOutputIntent();
5774 
5775     //emit metadata
5776     sal_Int32 nMetadataObject = emitDocumentMetadata();
5777 
5778     sal_Int32 nStructureDict = 0;
5779     if(m_aStructure.size() > 1)
5780     {
5781 ///check if dummy structure containers are needed
5782         addInternalStructureContainer(m_aStructure[0]);
5783         nStructureDict = m_aStructure[0].m_nObject = createObject();
5784         emitStructure( m_aStructure[ 0 ] );
5785     }
5786 
5787     // adjust tree node file offset
5788     if( ! updateObject( nTreeNode ) )
5789         return false;
5790 
5791     // emit tree node
5792     OStringBuffer aLine( 2048 );
5793     aLine.append( nTreeNode );
5794     aLine.append( " 0 obj\n" );
5795     aLine.append( "<</Type/Pages\n" );
5796     aLine.append( "/Resources " );
5797     aLine.append( getResourceDictObj() );
5798     aLine.append( " 0 R\n" );
5799 
5800     switch( m_eInheritedOrientation )
5801     {
5802         case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
5803         case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
5804 
5805         case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant
5806         case PDFWriter::Portrait:
5807         default:
5808             break;
5809     }
5810     sal_Int32 nMediaBoxWidth = 0;
5811     sal_Int32 nMediaBoxHeight = 0;
5812     if( m_aPages.empty() ) // sanity check, this should not happen
5813     {
5814         nMediaBoxWidth = m_nInheritedPageWidth;
5815         nMediaBoxHeight = m_nInheritedPageHeight;
5816     }
5817     else
5818     {
5819         for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter )
5820         {
5821             if( iter->m_nPageWidth > nMediaBoxWidth )
5822                 nMediaBoxWidth = iter->m_nPageWidth;
5823             if( iter->m_nPageHeight > nMediaBoxHeight )
5824                 nMediaBoxHeight = iter->m_nPageHeight;
5825         }
5826     }
5827     aLine.append( "/MediaBox[ 0 0 " );
5828     aLine.append( nMediaBoxWidth );
5829     aLine.append( ' ' );
5830     aLine.append( nMediaBoxHeight );
5831     aLine.append( " ]\n"
5832                   "/Kids[ " );
5833     unsigned int i = 0;
5834     for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ )
5835     {
5836         aLine.append( iter->m_nPageObject );
5837         aLine.append( " 0 R" );
5838         aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5839     }
5840     aLine.append( "]\n"
5841                   "/Count " );
5842     aLine.append( (sal_Int32)m_aPages.size() );
5843     aLine.append( ">>\n"
5844                   "endobj\n\n" );
5845     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5846 
5847     // emit annotation objects
5848     CHECK_RETURN( emitAnnotations() );
5849 
5850     // emit Catalog
5851     m_nCatalogObject = createObject();
5852     if( ! updateObject( m_nCatalogObject ) )
5853         return false;
5854     aLine.setLength( 0 );
5855     aLine.append( m_nCatalogObject );
5856     aLine.append( " 0 obj\n"
5857                   "<</Type/Catalog/Pages " );
5858     aLine.append( nTreeNode );
5859     aLine.append( " 0 R\n" );
5860 //--->i56629
5861 //check if there are named destinations to emit (root must be inside the catalog)
5862     if( nNamedDestinationsDictionary )
5863     {
5864         aLine.append("/Dests ");
5865         aLine.append( nNamedDestinationsDictionary );
5866         aLine.append( " 0 R\n" );
5867     }
5868 //<----
5869     if( m_aContext.PageLayout != PDFWriter::DefaultLayout )
5870         switch(  m_aContext.PageLayout )
5871         {
5872         default :
5873         case  PDFWriter::SinglePage :
5874             aLine.append( "/PageLayout/SinglePage\n" );
5875             break;
5876         case  PDFWriter::Continuous :
5877             aLine.append( "/PageLayout/OneColumn\n" );
5878             break;
5879         case  PDFWriter::ContinuousFacing :
5880 //the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5881             aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5882             break;
5883         }
5884     if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode )
5885         switch(  m_aContext.PDFDocumentMode )
5886         {
5887         default :
5888             aLine.append( "/PageMode/UseNone\n" );
5889             break;
5890         case PDFWriter::UseOutlines :
5891             aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5892             break;
5893         case PDFWriter::UseThumbs :
5894             aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5895             break;
5896         }
5897     else if( m_aContext.OpenInFullScreenMode )
5898         aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5899 
5900     OStringBuffer aInitPageRef;
5901     if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() )
5902     {
5903         aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5904         aInitPageRef.append( " 0 R" );
5905     }
5906     else
5907         aInitPageRef.append( "0" );
5908     switch( m_aContext.PDFDocumentAction )
5909     {
5910     case PDFWriter::ActionDefault :     //do nothing, this is the Acrobat default
5911     default:
5912         if( aInitPageRef.getLength() > 1 )
5913         {
5914             aLine.append( "/OpenAction[" );
5915             aLine.append( aInitPageRef );
5916             aLine.append( " /XYZ null null 0]\n" );
5917         }
5918         break;
5919     case PDFWriter::FitInWindow :
5920         aLine.append( "/OpenAction[" );
5921         aLine.append( aInitPageRef );
5922         aLine.append( " /Fit]\n" ); //Open fit page
5923         break;
5924     case PDFWriter::FitWidth :
5925         aLine.append( "/OpenAction[" );
5926         aLine.append( aInitPageRef );
5927         aLine.append( " /FitH " );
5928         aLine.append( m_nInheritedPageHeight );//Open fit width
5929         aLine.append( "]\n" );
5930         break;
5931     case PDFWriter::FitVisible :
5932         aLine.append( "/OpenAction[" );
5933         aLine.append( aInitPageRef );
5934         aLine.append( " /FitBH " );
5935         aLine.append( m_nInheritedPageHeight );//Open fit visible
5936         aLine.append( "]\n" );
5937         break;
5938     case PDFWriter::ActionZoom :
5939         aLine.append( "/OpenAction[" );
5940         aLine.append( aInitPageRef );
5941         aLine.append( " /XYZ null null " );
5942         if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5943             aLine.append( (double)m_aContext.Zoom/100.0 );
5944         else
5945             aLine.append( "0" );
5946         aLine.append( "]\n" );
5947         break;
5948     }
5949 // viewer preferences, if we had some, then emit
5950     if( m_aContext.HideViewerToolbar ||
5951         ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) ||
5952         m_aContext.HideViewerMenubar ||
5953         m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
5954         m_aContext.CenterWindow || (m_aContext.FirstPageLeft  &&  m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
5955         m_aContext.OpenInFullScreenMode )
5956     {
5957         aLine.append( "/ViewerPreferences<<" );
5958         if( m_aContext.HideViewerToolbar )
5959             aLine.append( "/HideToolbar true\n" );
5960         if( m_aContext.HideViewerMenubar )
5961             aLine.append( "/HideMenubar true\n" );
5962         if( m_aContext.HideViewerWindowControls )
5963             aLine.append( "/HideWindowUI true\n" );
5964         if( m_aContext.FitWindow )
5965             aLine.append( "/FitWindow true\n" );
5966         if( m_aContext.CenterWindow )
5967             aLine.append( "/CenterWindow true\n" );
5968         if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle )
5969             aLine.append( "/DisplayDocTitle true\n" );
5970         if( m_aContext.FirstPageLeft &&  m_aContext.PageLayout == PDFWriter::ContinuousFacing )
5971             aLine.append( "/Direction/R2L\n" );
5972         if( m_aContext.OpenInFullScreenMode )
5973             switch( m_aContext.PDFDocumentMode )
5974             {
5975             default :
5976             case PDFWriter::ModeDefault :
5977                 aLine.append( "/NonFullScreenPageMode/UseNone\n" );
5978                 break;
5979             case PDFWriter::UseOutlines :
5980                 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
5981                 break;
5982             case PDFWriter::UseThumbs :
5983                 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
5984                 break;
5985             }
5986         aLine.append( ">>\n" );
5987     }
5988 
5989     if( nOutlineDict )
5990     {
5991         aLine.append( "/Outlines " );
5992         aLine.append( nOutlineDict );
5993         aLine.append( " 0 R\n" );
5994     }
5995     if( nStructureDict )
5996     {
5997         aLine.append( "/StructTreeRoot " );
5998         aLine.append( nStructureDict );
5999         aLine.append( " 0 R\n" );
6000     }
6001     if( m_aContext.DocumentLocale.Language.getLength() > 0 )
6002     {
6003         OUStringBuffer aLocBuf( 16 );
6004         aLocBuf.append( m_aContext.DocumentLocale.Language.toAsciiLowerCase() );
6005         if( m_aContext.DocumentLocale.Country.getLength() > 0 )
6006         {
6007             aLocBuf.append( sal_Unicode('-') );
6008             aLocBuf.append( m_aContext.DocumentLocale.Country );
6009         }
6010         aLine.append( "/Lang" );
6011         appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
6012         aLine.append( "\n" );
6013     }
6014     if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 )
6015     {
6016         aLine.append( "/MarkInfo<</Marked true>>\n" );
6017     }
6018     if( m_aWidgets.size() > 0 )
6019     {
6020         aLine.append( "/AcroForm<</Fields[\n" );
6021         int nWidgets = m_aWidgets.size();
6022         int nOut = 0;
6023         for( int j = 0; j < nWidgets; j++ )
6024         {
6025             // output only root fields
6026             if( m_aWidgets[j].m_nParent < 1 )
6027             {
6028                 aLine.append( m_aWidgets[j].m_nObject );
6029                 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
6030             }
6031         }
6032         aLine.append( "\n]/DR " );
6033         aLine.append( getResourceDictObj() );
6034         aLine.append( " 0 R" );
6035         if( m_bIsPDF_A1 )
6036             aLine.append( ">>\n" );
6037         else
6038             aLine.append( "/NeedAppearances true>>\n" );
6039     }
6040 //--->i59651
6041 //check if there is a Metadata object
6042     if( nOutputIntentObject )
6043     {
6044         aLine.append("/OutputIntents[");
6045         aLine.append( nOutputIntentObject );
6046         aLine.append( " 0 R]" );
6047     }
6048     if( nMetadataObject )
6049     {
6050         aLine.append("/Metadata ");
6051         aLine.append( nMetadataObject );
6052         aLine.append( " 0 R" );
6053     }
6054 //<----
6055     aLine.append( ">>\n"
6056                   "endobj\n\n" );
6057     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6058 
6059     return true;
6060 }
6061 
6062 sal_Int32 PDFWriterImpl::emitInfoDict( )
6063 {
6064     sal_Int32 nObject = createObject();
6065 
6066     if( updateObject( nObject ) )
6067     {
6068         OStringBuffer aLine( 1024 );
6069         aLine.append( nObject );
6070         aLine.append( " 0 obj\n"
6071                       "<<" );
6072         if( m_aContext.DocumentInfo.Title.Len() )
6073         {
6074             aLine.append( "/Title" );
6075             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine );
6076             aLine.append( "\n" );
6077         }
6078         if( m_aContext.DocumentInfo.Author.Len() )
6079         {
6080             aLine.append( "/Author" );
6081             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine );
6082             aLine.append( "\n" );
6083         }
6084         if( m_aContext.DocumentInfo.Subject.Len() )
6085         {
6086             aLine.append( "/Subject" );
6087             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine );
6088             aLine.append( "\n" );
6089         }
6090         if( m_aContext.DocumentInfo.Keywords.Len() )
6091         {
6092             aLine.append( "/Keywords" );
6093             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine );
6094             aLine.append( "\n" );
6095         }
6096         if( m_aContext.DocumentInfo.Creator.Len() )
6097         {
6098             aLine.append( "/Creator" );
6099             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine );
6100             aLine.append( "\n" );
6101         }
6102         if( m_aContext.DocumentInfo.Producer.Len() )
6103         {
6104             aLine.append( "/Producer" );
6105             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine );
6106             aLine.append( "\n" );
6107         }
6108 
6109          aLine.append( "/CreationDate" );
6110          appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine );
6111         aLine.append( ">>\nendobj\n\n" );
6112         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6113             nObject = 0;
6114     }
6115     else
6116         nObject = 0;
6117 
6118     return nObject;
6119 }
6120 
6121 //--->i56629
6122 // Part of this function may be shared with method appendDest.
6123 //
6124 sal_Int32 PDFWriterImpl::emitNamedDestinations()
6125 {
6126     sal_Int32  nCount = m_aNamedDests.size();
6127     if( nCount <= 0 )
6128         return 0;//define internal error
6129 
6130 //get the object number for all the destinations
6131     sal_Int32 nObject = createObject();
6132 
6133     if( updateObject( nObject ) )
6134     {
6135 //emit the dictionary
6136         OStringBuffer aLine( 1024 );
6137         aLine.append( nObject );
6138         aLine.append( " 0 obj\n"
6139                       "<<" );
6140 
6141         sal_Int32  nDestID;
6142         for( nDestID = 0; nDestID < nCount; nDestID++ )
6143         {
6144             const PDFNamedDest& rDest   = m_aNamedDests[ nDestID ];
6145 // In order to correctly function both under an Internet browser and
6146 // directly with a reader (provided the reader has the feature) we
6147 // need to set the name of the destination the same way it will be encoded
6148 // in an Internet link
6149             INetURLObject aLocalURL(
6150                 OUString( RTL_CONSTASCII_USTRINGPARAM( "http://ahost.ax" ) ) ); //dummy location, won't be used
6151             aLocalURL.SetMark( rDest.m_aDestName );
6152 
6153             const rtl::OUString aName   = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as
6154             // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
6155             const PDFPage& rDestPage    = m_aPages[ rDest.m_nPage ];
6156 
6157             aLine.append( '/' );
6158             appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
6159             aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
6160                                  //maps the preceeding character properly
6161             aLine.append( rDestPage.m_nPageObject );
6162             aLine.append( " 0 R" );
6163 
6164             switch( rDest.m_eType )
6165             {
6166             case PDFWriter::XYZ:
6167             default:
6168                 aLine.append( "/XYZ " );
6169                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6170                 aLine.append( ' ' );
6171                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6172                 aLine.append( " 0" );
6173                 break;
6174             case PDFWriter::Fit:
6175                 aLine.append( "/Fit" );
6176                 break;
6177             case PDFWriter::FitRectangle:
6178                 aLine.append( "/FitR " );
6179                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6180                 aLine.append( ' ' );
6181                 appendFixedInt( rDest.m_aRect.Top(), aLine );
6182                 aLine.append( ' ' );
6183                 appendFixedInt( rDest.m_aRect.Right(), aLine );
6184                 aLine.append( ' ' );
6185                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6186                 break;
6187             case PDFWriter::FitHorizontal:
6188                 aLine.append( "/FitH " );
6189                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6190                 break;
6191             case PDFWriter::FitVertical:
6192                 aLine.append( "/FitV " );
6193                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6194                 break;
6195             case PDFWriter::FitPageBoundingBox:
6196                 aLine.append( "/FitB" );
6197                 break;
6198             case PDFWriter::FitPageBoundingBoxHorizontal:
6199                 aLine.append( "/FitBH " );
6200                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6201                 break;
6202             case PDFWriter::FitPageBoundingBoxVertical:
6203                 aLine.append( "/FitBV " );
6204                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6205                 break;
6206             }
6207             aLine.append( "]\n" );
6208         }
6209 //close
6210 
6211         aLine.append( ">>\nendobj\n\n" );
6212         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6213             nObject = 0;
6214     }
6215     else
6216         nObject = 0;
6217 
6218     return nObject;
6219 }
6220 //<--- i56629
6221 
6222 //--->i59651
6223 // emits the output intent dictionary
6224 
6225 sal_Int32 PDFWriterImpl::emitOutputIntent()
6226 {
6227     if( !m_bIsPDF_A1 )
6228         return 0;
6229 
6230 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
6231 
6232     OStringBuffer aLine( 1024 );
6233     sal_Int32 nICCObject = createObject();
6234     sal_Int32 nStreamLengthObject = createObject();
6235 
6236     aLine.append( nICCObject );
6237 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
6238     aLine.append( " 0 obj\n<</N 3/Length " );
6239     aLine.append( nStreamLengthObject );
6240     aLine.append( " 0 R" );
6241 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
6242     aLine.append( "/Filter/FlateDecode" );
6243 #endif
6244     aLine.append( ">>\nstream\n" );
6245     CHECK_RETURN( updateObject( nICCObject ) );
6246     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6247 //get file position
6248     sal_uInt64 nBeginStreamPos = 0;
6249     osl_getFilePos( m_aFile, &nBeginStreamPos );
6250     beginCompression();
6251     checkAndEnableStreamEncryption( nICCObject );
6252     sal_Int32 nStreamSize = writeBuffer( nsRGB_ICC_profile, (sal_Int32) sizeof( nsRGB_ICC_profile ) );
6253     disableStreamEncryption();
6254     endCompression();
6255     sal_uInt64 nEndStreamPos = 0;
6256     osl_getFilePos( m_aFile, &nEndStreamPos );
6257 
6258     if( nStreamSize == 0 )
6259         return 0;
6260     if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6261         return 0 ;
6262     aLine.setLength( 0 );
6263 
6264 //emit the stream length   object
6265     CHECK_RETURN( updateObject( nStreamLengthObject ) );
6266     aLine.setLength( 0 );
6267     aLine.append( nStreamLengthObject );
6268     aLine.append( " 0 obj\n" );
6269     aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6270     aLine.append( "\nendobj\n\n" );
6271     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6272     aLine.setLength( 0 );
6273 
6274 //emit the OutputIntent dictionary
6275     sal_Int32 nOIObject = createObject();
6276     CHECK_RETURN( updateObject( nOIObject ) );
6277     aLine.append( nOIObject );
6278     aLine.append( " 0 obj\n"
6279                   "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
6280 
6281     rtl::OUString aComment( RTL_CONSTASCII_USTRINGPARAM( "sRGB IEC61966-2.1" ) );
6282     appendLiteralStringEncrypt( aComment ,nOIObject, aLine );
6283     aLine.append("/DestOutputProfile ");
6284     aLine.append( nICCObject );
6285     aLine.append( " 0 R>>\nendobj\n\n" );;
6286     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6287 
6288     return nOIObject;
6289 }
6290 
6291 // formats the string for the XML stream
6292 static void escapeStringXML( const rtl::OUString& rStr, rtl::OUString &rValue)
6293 {
6294     const sal_Unicode* pUni = rStr.getStr();
6295     int nLen = rStr.getLength();
6296     for( ; nLen; nLen--, pUni++ )
6297     {
6298         switch( *pUni )
6299         {
6300         case sal_Unicode('&'):
6301             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&amp;" ) );
6302         break;
6303         case sal_Unicode('<'):
6304             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&lt;" ) );
6305         break;
6306         case sal_Unicode('>'):
6307             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&gt;" ) );
6308         break;
6309         case sal_Unicode('\''):
6310             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&apos;" ) );
6311         break;
6312         case sal_Unicode('"'):
6313             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&quot;" ) );
6314         break;
6315         default:
6316             rValue += rtl::OUString( *pUni );
6317             break;
6318         }
6319     }
6320 }
6321 
6322 // emits the document metadata
6323 //
6324 sal_Int32 PDFWriterImpl::emitDocumentMetadata()
6325 {
6326     if( !m_bIsPDF_A1 )
6327         return 0;
6328 
6329     //get the object number for all the destinations
6330     sal_Int32 nObject = createObject();
6331 
6332     if( updateObject( nObject ) )
6333     {
6334 // the following string are written in UTF-8 unicode
6335         OStringBuffer aMetadataStream( 8192 );
6336 
6337         aMetadataStream.append( "<?xpacket begin=\"" );
6338 // this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used
6339 // as a byte-order marker.
6340         aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) );
6341         aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
6342         aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
6343         aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
6344 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
6345         aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6346         aMetadataStream.append( "      xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
6347         aMetadataStream.append( "   <pdfaid:part>1</pdfaid:part>\n" );
6348         aMetadataStream.append( "   <pdfaid:conformance>A</pdfaid:conformance>\n" );
6349         aMetadataStream.append( "  </rdf:Description>\n" );
6350 //... Dublin Core properties go here
6351         if( m_aContext.DocumentInfo.Title.Len() ||
6352             m_aContext.DocumentInfo.Author.Len() ||
6353             m_aContext.DocumentInfo.Subject.Len() )
6354         {
6355             aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6356             aMetadataStream.append( "      xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
6357             if( m_aContext.DocumentInfo.Title.Len() )
6358             {
6359 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6360                 aMetadataStream.append( "   <dc:title>\n" );
6361                 aMetadataStream.append( "    <rdf:Alt>\n" );
6362                 aMetadataStream.append( "     <rdf:li xml:lang=\"x-default\">" );
6363                 rtl::OUString aTitle;
6364                 escapeStringXML( m_aContext.DocumentInfo.Title, aTitle );
6365                 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 )  );
6366                 aMetadataStream.append( "</rdf:li>\n" );
6367                 aMetadataStream.append( "    </rdf:Alt>\n" );
6368                 aMetadataStream.append( "   </dc:title>\n" );
6369             }
6370             if( m_aContext.DocumentInfo.Author.Len() )
6371             {
6372                 aMetadataStream.append( "   <dc:creator>\n" );
6373                 aMetadataStream.append( "    <rdf:Seq>\n" );
6374                 aMetadataStream.append( "     <rdf:li>" );
6375                 rtl::OUString aAuthor;
6376                 escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor );
6377                 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 )  );
6378                 aMetadataStream.append( "</rdf:li>\n" );
6379                 aMetadataStream.append( "    </rdf:Seq>\n" );
6380                 aMetadataStream.append( "   </dc:creator>\n" );
6381             }
6382             if( m_aContext.DocumentInfo.Subject.Len() )
6383             {
6384 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6385                 aMetadataStream.append( "   <dc:description>\n" );
6386                 aMetadataStream.append( "    <rdf:Alt>\n" );
6387                 aMetadataStream.append( "     <rdf:li xml:lang=\"x-default\">" );
6388                 rtl::OUString aSubject;
6389                 escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject );
6390                 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 )  );
6391                 aMetadataStream.append( "</rdf:li>\n" );
6392                 aMetadataStream.append( "    </rdf:Alt>\n" );
6393                 aMetadataStream.append( "   </dc:description>\n" );
6394             }
6395             aMetadataStream.append( "  </rdf:Description>\n" );
6396         }
6397 
6398 //... PDF properties go here
6399         if( m_aContext.DocumentInfo.Producer.Len() ||
6400             m_aContext.DocumentInfo.Keywords.Len() )
6401         {
6402             aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6403             aMetadataStream.append( "     xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
6404             if( m_aContext.DocumentInfo.Producer.Len() )
6405             {
6406                 aMetadataStream.append( "   <pdf:Producer>" );
6407                 rtl::OUString aProducer;
6408                 escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer );
6409                 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 )  );
6410                 aMetadataStream.append( "</pdf:Producer>\n" );
6411             }
6412             if( m_aContext.DocumentInfo.Keywords.Len() )
6413             {
6414                 aMetadataStream.append( "   <pdf:Keywords>" );
6415                 rtl::OUString aKeywords;
6416                 escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords );
6417                 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 )  );
6418                 aMetadataStream.append( "</pdf:Keywords>\n" );
6419             }
6420             aMetadataStream.append( "  </rdf:Description>\n" );
6421         }
6422 
6423         aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6424         aMetadataStream.append( "    xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
6425         if( m_aContext.DocumentInfo.Creator.Len() )
6426         {
6427             aMetadataStream.append( "   <xmp:CreatorTool>" );
6428             rtl::OUString aCreator;
6429             escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator );
6430             aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 )  );
6431             aMetadataStream.append( "</xmp:CreatorTool>\n" );
6432         }
6433 //creation date
6434         aMetadataStream.append( "   <xmp:CreateDate>" );
6435         aMetadataStream.append( m_aCreationMetaDateString );
6436         aMetadataStream.append( "</xmp:CreateDate>\n" );
6437 
6438         aMetadataStream.append( "  </rdf:Description>\n" );
6439         aMetadataStream.append( " </rdf:RDF>\n" );
6440         aMetadataStream.append( "</x:xmpmeta>\n" );
6441 
6442 //add the padding
6443         for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ )
6444         {
6445             aMetadataStream.append( " " );
6446             if( nSpaces % 100 == 0 )
6447                 aMetadataStream.append( "\n" );
6448         }
6449 
6450         aMetadataStream.append( "<?xpacket end=\"w\"?>\n" );
6451 
6452         OStringBuffer aMetadataObj( 1024 );
6453 
6454         aMetadataObj.append( nObject );
6455         aMetadataObj.append( " 0 obj\n" );
6456 
6457         aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
6458 
6459         aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() );
6460         aMetadataObj.append( ">>\nstream\n" );
6461         CHECK_RETURN( writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) );
6462 //emit the stream
6463         CHECK_RETURN( writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) );
6464 
6465         aMetadataObj.setLength( 0 );
6466         aMetadataObj.append( "\nendstream\nendobj\n\n" );
6467         if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
6468             nObject = 0;
6469     }
6470     else
6471         nObject = 0;
6472 
6473     return nObject;
6474 }
6475 //<---i59651
6476 
6477 bool PDFWriterImpl::emitTrailer()
6478 {
6479     // emit doc info
6480     OString aInfoValuesOut;
6481     sal_Int32 nDocInfoObject = emitInfoDict( );
6482 
6483     sal_Int32 nSecObject = 0;
6484 
6485 	if( m_aContext.Encryption.Encrypt() )
6486 	{
6487 //emit the security information
6488 //must be emitted as indirect dictionary object, since
6489 //Acrobat Reader 5 works only with this kind of implementation
6490 		nSecObject = createObject();
6491 
6492 		if( updateObject( nSecObject ) )
6493 		{
6494 			OStringBuffer aLineS( 1024 );
6495 			aLineS.append( nSecObject );
6496 			aLineS.append( " 0 obj\n"
6497 						   "<</Filter/Standard/V " );
6498 			// check the version
6499 			if( m_aContext.Encryption.Security128bit )
6500 				aLineS.append( "2/Length 128/R 3" );
6501 			else
6502 				aLineS.append( "1/R 2" );
6503 
6504 			// emit the owner password, must not be encrypted
6505 			aLineS.append( "/O(" );
6506 			appendLiteralString( (const sal_Char*)&m_aContext.Encryption.OValue[0], sal_Int32(m_aContext.Encryption.OValue.size()), aLineS );
6507 			aLineS.append( ")/U(" );
6508 			appendLiteralString( (const sal_Char*)&m_aContext.Encryption.UValue[0], sal_Int32(m_aContext.Encryption.UValue.size()), aLineS );
6509 			aLineS.append( ")/P " );// the permission set
6510 			aLineS.append( m_nAccessPermissions );
6511 			aLineS.append( ">>\nendobj\n\n" );
6512 			if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) )
6513 				nSecObject = 0;
6514 		}
6515 		else
6516 			nSecObject = 0;
6517 	}
6518     // emit xref table
6519     // remember start
6520     sal_uInt64 nXRefOffset = 0;
6521     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) );
6522     CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
6523 
6524     sal_Int32 nObjects = m_aObjects.size();
6525     OStringBuffer aLine;
6526     aLine.append( "0 " );
6527     aLine.append( (sal_Int32)(nObjects+1) );
6528     aLine.append( "\n" );
6529     aLine.append( "0000000000 65535 f \n" );
6530     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6531 
6532     for( sal_Int32 i = 0; i < nObjects; i++ )
6533     {
6534         aLine.setLength( 0 );
6535         OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] );
6536         for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
6537             aLine.append( '0' );
6538         aLine.append( aOffset );
6539         aLine.append( " 00000 n \n" );
6540         DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" );
6541         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6542     }
6543 
6544     // prepare document checksum
6545     OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
6546     if( m_aDocDigest )
6547     {
6548         sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
6549         rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) );
6550         for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ )
6551             appendHex( nMD5Sum[i], aDocChecksum );
6552     }
6553     // document id set in setDocInfo method
6554     // emit trailer
6555     aLine.setLength( 0 );
6556     aLine.append( "trailer\n"
6557                   "<</Size " );
6558     aLine.append( (sal_Int32)(nObjects+1) );
6559     aLine.append( "/Root " );
6560     aLine.append( m_nCatalogObject );
6561     aLine.append( " 0 R\n" );
6562     if( nSecObject |= 0 )
6563     {
6564         aLine.append( "/Encrypt ");
6565         aLine.append( nSecObject );
6566         aLine.append( " 0 R\n" );
6567     }
6568     if( nDocInfoObject )
6569     {
6570         aLine.append( "/Info " );
6571         aLine.append( nDocInfoObject );
6572         aLine.append( " 0 R\n" );
6573     }
6574     if( ! m_aContext.Encryption.DocumentIdentifier.empty() )
6575     {
6576         aLine.append( "/ID [ <" );
6577         for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6578              it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6579         {
6580             appendHex( sal_Int8(*it), aLine );
6581         }
6582         aLine.append( ">\n"
6583                       "<" );
6584         for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6585              it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6586         {
6587             appendHex( sal_Int8(*it), aLine );
6588         }
6589         aLine.append( "> ]\n" );
6590     }
6591     if( aDocChecksum.getLength() )
6592     {
6593         aLine.append( "/DocChecksum /" );
6594         aLine.append( aDocChecksum );
6595         aLine.append( "\n" );
6596     }
6597     if( m_aAdditionalStreams.size() > 0 )
6598     {
6599         aLine.append( "/AdditionalStreams [" );
6600         for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ )
6601         {
6602             aLine.append( "/" );
6603             appendName( m_aAdditionalStreams[i].m_aMimeType, aLine );
6604             aLine.append( " " );
6605             aLine.append( m_aAdditionalStreams[i].m_nStreamObject );
6606             aLine.append( " 0 R\n" );
6607         }
6608         aLine.append( "]\n" );
6609     }
6610     aLine.append( ">>\n"
6611                   "startxref\n" );
6612     aLine.append( (sal_Int64)nXRefOffset );
6613     aLine.append( "\n"
6614                   "%%EOF\n" );
6615     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6616 
6617     return true;
6618 }
6619 
6620 struct AnnotationSortEntry
6621 {
6622     sal_Int32 nTabOrder;
6623     sal_Int32 nObject;
6624     sal_Int32 nWidgetIndex;
6625 
6626     AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
6627         nTabOrder( nTab ),
6628         nObject( nObj ),
6629         nWidgetIndex( nI )
6630     {}
6631 };
6632 
6633 struct AnnotSortContainer
6634 {
6635     std::set< sal_Int32 >               aObjects;
6636     std::vector< AnnotationSortEntry >    aSortedAnnots;
6637 };
6638 
6639 struct AnnotSorterLess
6640 {
6641     std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets;
6642 
6643     AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {}
6644 
6645     bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
6646     {
6647         if( rLeft.nTabOrder < rRight.nTabOrder )
6648             return true;
6649         if( rRight.nTabOrder < rLeft.nTabOrder )
6650             return false;
6651         if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
6652             return false;
6653         if( rRight.nWidgetIndex < 0 )
6654             return true;
6655         if( rLeft.nWidgetIndex < 0 )
6656             return false;
6657         // remember: widget rects are in PDF coordinates, so they are ordered down up
6658         if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
6659             m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
6660             return true;
6661         if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
6662             m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
6663             return false;
6664         if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
6665             m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
6666             return true;
6667         return false;
6668     }
6669 };
6670 
6671 void PDFWriterImpl::sortWidgets()
6672 {
6673     // sort widget annotations on each page as per their
6674     // TabOrder attribute
6675     std::hash_map< sal_Int32, AnnotSortContainer > sorted;
6676     int nWidgets = m_aWidgets.size();
6677     for( int nW = 0; nW < nWidgets; nW++ )
6678     {
6679         const PDFWidget& rWidget = m_aWidgets[nW];
6680         if( rWidget.m_nPage >= 0 )
6681         {
6682             AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
6683             // optimize vector allocation
6684             if( rCont.aSortedAnnots.empty() )
6685                 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
6686             // insert widget to tab sorter
6687             // RadioButtons are not page annotations, only their individual check boxes are
6688             if( rWidget.m_eType != PDFWriter::RadioButton )
6689             {
6690                 rCont.aObjects.insert( rWidget.m_nObject );
6691                 rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) );
6692             }
6693         }
6694     }
6695     for( std::hash_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it )
6696     {
6697         // append entries for non widget annotations
6698         PDFPage& rPage = m_aPages[ it->first ];
6699         unsigned int nAnnots = rPage.m_aAnnotations.size();
6700         for( unsigned int nA = 0; nA < nAnnots; nA++ )
6701             if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end())
6702                 it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) );
6703 
6704         AnnotSorterLess aLess( m_aWidgets );
6705         std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess );
6706         // sanity check
6707         if( it->second.aSortedAnnots.size() == nAnnots)
6708         {
6709             for( unsigned int nA = 0; nA < nAnnots; nA++ )
6710                 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject;
6711         }
6712         else
6713         {
6714             DBG_ASSERT( 0, "wrong number of sorted annotations" );
6715             #if OSL_DEBUG_LEVEL > 0
6716             fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n"
6717 					 "    %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots );
6718             #endif
6719         }
6720     }
6721 
6722     // FIXME: implement tab order in structure tree for PDF 1.5
6723 }
6724 
6725 namespace vcl {
6726 class PDFStreamIf :
6727 		public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream	>
6728 {
6729     PDFWriterImpl*  m_pWriter;
6730     bool            m_bWrite;
6731     public:
6732     PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
6733     virtual ~PDFStreamIf();
6734 
6735     virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw();
6736     virtual void SAL_CALL flush() throw();
6737     virtual void SAL_CALL closeOutput() throw();
6738 };
6739 }
6740 
6741 PDFStreamIf::~PDFStreamIf()
6742 {
6743 }
6744 
6745 void SAL_CALL  PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw()
6746 {
6747     if( m_bWrite )
6748     {
6749         sal_Int32 nBytes = aData.getLength();
6750         if( nBytes > 0 )
6751             m_pWriter->writeBuffer( aData.getConstArray(), nBytes );
6752     }
6753 }
6754 
6755 void SAL_CALL PDFStreamIf::flush() throw()
6756 {
6757 }
6758 
6759 void SAL_CALL PDFStreamIf::closeOutput() throw()
6760 {
6761     m_bWrite = false;
6762 }
6763 
6764 bool PDFWriterImpl::emitAdditionalStreams()
6765 {
6766     unsigned int nStreams = m_aAdditionalStreams.size();
6767     for( unsigned int i = 0; i < nStreams; i++ )
6768     {
6769         PDFAddStream& rStream = m_aAdditionalStreams[i];
6770         rStream.m_nStreamObject = createObject();
6771         sal_Int32 nSizeObject = createObject();
6772 
6773         if( ! updateObject( rStream.m_nStreamObject ) )
6774             return false;
6775 
6776         OStringBuffer aLine;
6777         aLine.append( rStream.m_nStreamObject );
6778         aLine.append( " 0 obj\n<</Length " );
6779         aLine.append( nSizeObject );
6780         aLine.append( " 0 R" );
6781         if( rStream.m_bCompress )
6782             aLine.append( "/Filter/FlateDecode" );
6783         aLine.append( ">>\nstream\n" );
6784         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6785             return false;
6786         sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0;
6787         if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) )
6788         {
6789             osl_closeFile( m_aFile );
6790             m_bOpen = false;
6791         }
6792         if( rStream.m_bCompress )
6793             beginCompression();
6794 
6795         checkAndEnableStreamEncryption( rStream.m_nStreamObject );
6796         com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) );
6797         rStream.m_pStream->write( xStream );
6798         xStream.clear();
6799         delete rStream.m_pStream;
6800         rStream.m_pStream = NULL;
6801         disableStreamEncryption();
6802 
6803         if( rStream.m_bCompress )
6804             endCompression();
6805 
6806         if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) )
6807         {
6808             osl_closeFile( m_aFile );
6809             m_bOpen = false;
6810             return false;
6811         }
6812         if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6813             return false ;
6814         // emit stream length object
6815         if( ! updateObject( nSizeObject ) )
6816             return false;
6817         aLine.setLength( 0 );
6818         aLine.append( nSizeObject );
6819         aLine.append( " 0 obj\n" );
6820         aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6821         aLine.append( "\nendobj\n\n" );
6822         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6823             return false;
6824     }
6825     return true;
6826 }
6827 
6828 bool PDFWriterImpl::emit()
6829 {
6830     endPage();
6831 
6832     // resort structure tree and annotations if necessary
6833     // needed for widget tab order
6834     sortWidgets();
6835 
6836     // emit additional streams
6837     CHECK_RETURN( emitAdditionalStreams() );
6838 
6839     // emit catalog
6840     CHECK_RETURN( emitCatalog() );
6841 
6842     // emit trailer
6843     CHECK_RETURN( emitTrailer() );
6844 
6845     osl_closeFile( m_aFile );
6846     m_bOpen = false;
6847 
6848     return true;
6849 }
6850 
6851 std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors()
6852 {
6853     return m_aErrors;
6854 }
6855 
6856 sal_Int32 PDFWriterImpl::getSystemFont( const Font& i_rFont )
6857 {
6858     getReferenceDevice()->Push();
6859     getReferenceDevice()->SetFont( i_rFont );
6860     getReferenceDevice()->ImplNewFont();
6861 
6862     const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6863     sal_Int32 nFontID = 0;
6864     FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont );
6865     if( it != m_aSystemFonts.end() )
6866         nFontID = it->second.m_nNormalFontID;
6867     else
6868     {
6869         nFontID = m_nNextFID++;
6870         m_aSystemFonts[ pDevFont ] = EmbedFont();
6871         m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID;
6872     }
6873 
6874     getReferenceDevice()->Pop();
6875     getReferenceDevice()->ImplNewFont();
6876 
6877     return nFontID;
6878 }
6879 
6880 void PDFWriterImpl::registerGlyphs( int nGlyphs,
6881                                     sal_GlyphId* pGlyphs,
6882                                     sal_Int32* pGlyphWidths,
6883                                     sal_Ucs* pUnicodes,
6884                                     sal_Int32* pUnicodesPerGlyph,
6885                                     sal_uInt8* pMappedGlyphs,
6886                                     sal_Int32* pMappedFontObjects,
6887                                     const ImplFontData* pFallbackFonts[] )
6888 {
6889     const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6890     sal_Ucs* pCurUnicode = pUnicodes;
6891     for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ )
6892     {
6893         const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB);
6894         const ImplFontData* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont;
6895 
6896         if( isBuiltinFont( pCurrentFont ) )
6897         {
6898             sal_Int32 nFontID = 0;
6899             FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6900             if( it != m_aEmbeddedFonts.end() )
6901                 nFontID = it->second.m_nNormalFontID;
6902             else
6903             {
6904                 nFontID = m_nNextFID++;
6905                 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6906                 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6907             }
6908 
6909             pGlyphWidths[ i ] = 0;
6910             pMappedGlyphs[ i ] = sal::static_int_cast<sal_Int8>( nFontGlyphId );
6911             pMappedFontObjects[ i ] = nFontID;
6912             const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pCurrentFont );
6913             if( pFD )
6914             {
6915                 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
6916                 pGlyphWidths[i] = pBuiltinFont->m_aWidths[ nFontGlyphId & 0x00ff ];
6917             }
6918         }
6919         else if( pCurrentFont->mbSubsettable )
6920         {
6921             FontSubset& rSubset = m_aSubsets[ pCurrentFont ];
6922             // search for font specific glyphID
6923             FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId );
6924             if( it != rSubset.m_aMapping.end() )
6925             {
6926                 pMappedFontObjects[i] = it->second.m_nFontID;
6927                 pMappedGlyphs[i] = it->second.m_nSubsetGlyphID;
6928             }
6929             else
6930             {
6931                 // create new subset if necessary
6932                 if( rSubset.m_aSubsets.empty()
6933                 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
6934                 {
6935                     rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) );
6936                 }
6937 
6938                 // copy font id
6939                 pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID;
6940                 // create new glyph in subset
6941                 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
6942                 pMappedGlyphs[i] = nNewId;
6943 
6944                 // add new glyph to emitted font subset
6945                 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
6946                 rNewGlyphEmit.setGlyphId( nNewId );
6947                 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ )
6948                     rNewGlyphEmit.addCode( pCurUnicode[n] );
6949 
6950                 // add new glyph to font mapping
6951                 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
6952                 rNewGlyph.m_nFontID = pMappedFontObjects[i];
6953                 rNewGlyph.m_nSubsetGlyphID = nNewId;
6954             }
6955             getReferenceDevice()->ImplGetGraphics();
6956             const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0);
6957             pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont,
6958                                                           nFontGlyphId,
6959                                                           bVertical,
6960                                                           m_pReferenceDevice->mpGraphics );
6961         }
6962         else if( pCurrentFont->IsEmbeddable() )
6963         {
6964             sal_Int32 nFontID = 0;
6965             FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6966             if( it != m_aEmbeddedFonts.end() )
6967                 nFontID = it->second.m_nNormalFontID;
6968             else
6969             {
6970                 nFontID = m_nNextFID++;
6971                 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6972                 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6973             }
6974             EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont];
6975 
6976             const Ucs2SIntMap* pEncoding = NULL;
6977             const Ucs2OStrMap* pNonEncoded = NULL;
6978             getReferenceDevice()->ImplGetGraphics();
6979             pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded );
6980 
6981             Ucs2SIntMap::const_iterator enc_it;
6982             Ucs2OStrMap::const_iterator nonenc_it;
6983 
6984             sal_Int32 nCurFontID = nFontID;
6985             sal_Ucs cChar = *pCurUnicode;
6986             if( pEncoding )
6987             {
6988                 enc_it = pEncoding->find( cChar );
6989                 if( enc_it != pEncoding->end() && enc_it->second > 0 )
6990                 {
6991                     DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" );
6992                     cChar = (sal_Ucs)enc_it->second;
6993                 }
6994                 else if( (enc_it == pEncoding->end() || enc_it->second == -1) &&
6995                          pNonEncoded &&
6996                          (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() )
6997                 {
6998                     nCurFontID = 0;
6999                     // find non encoded glyph
7000                     for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it )
7001                     {
7002                         if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() )
7003                         {
7004                             nCurFontID = nec_it->m_nFontID;
7005                             cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ];
7006                             break;
7007                         }
7008                     }
7009                     if( nCurFontID == 0 ) // new nonencoded glyph
7010                     {
7011                         if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 )
7012                         {
7013                             rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() );
7014                             rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++;
7015                         }
7016                         EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back();
7017                         rEncoding.m_aEncVector.push_back( EmbedCode() );
7018                         rEncoding.m_aEncVector.back().m_aUnicode = cChar;
7019                         rEncoding.m_aEncVector.back().m_aName = nonenc_it->second;
7020                         rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1);
7021                         nCurFontID = rEncoding.m_nFontID;
7022                         cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ];
7023                     }
7024                 }
7025                 else
7026                     pEncoding = NULL;
7027             }
7028             if( ! pEncoding )
7029             {
7030                 if( cChar & 0xff00 )
7031                 {
7032                     // some characters can be used by conversion
7033                     if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area
7034                         cChar -= 0xf000;
7035                     else
7036                     {
7037                         String aString(cChar);
7038                         ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 );
7039                         cChar = ((sal_Ucs)aChar.GetChar( 0 )) & 0x00ff;
7040                     }
7041                 }
7042             }
7043 
7044             pMappedGlyphs[ i ] = (sal_Int8)cChar;
7045             pMappedFontObjects[ i ] = nCurFontID;
7046             pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont,
7047                                                             (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR,
7048                                                             false,
7049                                                             m_pReferenceDevice->mpGraphics );
7050         }
7051     }
7052 }
7053 
7054 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines )
7055 {
7056     push( PUSH_ALL );
7057 
7058     FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief();
7059 
7060     Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor();
7061     Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7062     Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7063     Color aReliefColor( COL_LIGHTGRAY );
7064     if( aTextColor == COL_BLACK )
7065         aTextColor = Color( COL_WHITE );
7066     if( aTextLineColor == COL_BLACK )
7067         aTextLineColor = Color( COL_WHITE );
7068     if( aOverlineColor == COL_BLACK )
7069         aOverlineColor = Color( COL_WHITE );
7070     if( aTextColor == COL_WHITE )
7071         aReliefColor = Color( COL_BLACK );
7072 
7073     Font aSetFont = m_aCurrentPDFState.m_aFont;
7074     aSetFont.SetRelief( RELIEF_NONE );
7075     aSetFont.SetShadow( sal_False );
7076 
7077     aSetFont.SetColor( aReliefColor );
7078     setTextLineColor( aReliefColor );
7079     setOverlineColor( aReliefColor );
7080     setFont( aSetFont );
7081     long nOff = 1 + getReferenceDevice()->mnDPIX/300;
7082     if( eRelief == RELIEF_ENGRAVED )
7083         nOff = -nOff;
7084 
7085     rLayout.DrawOffset() += Point( nOff, nOff );
7086     updateGraphicsState();
7087     drawLayout( rLayout, rText, bTextLines );
7088 
7089     rLayout.DrawOffset() -= Point( nOff, nOff );
7090     setTextLineColor( aTextLineColor );
7091     setOverlineColor( aOverlineColor );
7092     aSetFont.SetColor( aTextColor );
7093     setFont( aSetFont );
7094     updateGraphicsState();
7095     drawLayout( rLayout, rText, bTextLines );
7096 
7097     // clean up the mess
7098     pop();
7099 }
7100 
7101 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines )
7102 {
7103     Font aSaveFont = m_aCurrentPDFState.m_aFont;
7104     Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7105     Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7106 
7107     Font& rFont = m_aCurrentPDFState.m_aFont;
7108     if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 )
7109         rFont.SetColor( Color( COL_LIGHTGRAY ) );
7110     else
7111         rFont.SetColor( Color( COL_BLACK ) );
7112     rFont.SetShadow( sal_False );
7113     rFont.SetOutline( sal_False );
7114     setFont( rFont );
7115     setTextLineColor( rFont.GetColor() );
7116     setOverlineColor( rFont.GetColor() );
7117     updateGraphicsState();
7118 
7119     long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24);
7120     if( rFont.IsOutline() )
7121         nOff++;
7122     rLayout.DrawBase() += Point( nOff, nOff );
7123     drawLayout( rLayout, rText, bTextLines );
7124     rLayout.DrawBase() -= Point( nOff, nOff );
7125 
7126     setFont( aSaveFont );
7127     setTextLineColor( aSaveTextLineColor );
7128     setOverlineColor( aSaveOverlineColor );
7129     updateGraphicsState();
7130 }
7131 
7132 void PDFWriterImpl::drawVerticalGlyphs(
7133         const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7134         OStringBuffer& rLine,
7135         const Point& rAlignOffset,
7136         const Matrix3& rRotScale,
7137         double fAngle,
7138         double fXScale,
7139         double fSkew,
7140         sal_Int32 nFontHeight )
7141 {
7142     long nXOffset = 0;
7143     Point aCurPos( rGlyphs[0].m_aPos );
7144     aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7145     aCurPos += rAlignOffset;
7146     for( size_t i = 0; i < rGlyphs.size(); i++ )
7147     {
7148         // have to emit each glyph on its own
7149         double fDeltaAngle = 0.0;
7150         double fYScale = 1.0;
7151         double fTempXScale = fXScale;
7152         double fSkewB = fSkew;
7153         double fSkewA = 0.0;
7154 
7155         Point aDeltaPos;
7156         if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL )
7157         {
7158             fDeltaAngle = M_PI/2.0;
7159             aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent();
7160             aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale);
7161             fYScale = fXScale;
7162             fTempXScale = 1.0;
7163             fSkewA = -fSkewB;
7164             fSkewB = 0.0;
7165         }
7166         else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR )
7167         {
7168             fDeltaAngle = -M_PI/2.0;
7169             aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale);
7170             aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent();
7171             fYScale = fXScale;
7172             fTempXScale = 1.0;
7173             fSkewA = fSkewB;
7174             fSkewB = 0.0;
7175         }
7176         aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) );
7177         if( i < rGlyphs.size()-1 )
7178 		// [Bug 120627] the text on the Y axis is reversed when export ppt file to PDF format
7179 		{
7180 			long nOffsetX = rGlyphs[i+1].m_aPos.X() - rGlyphs[i].m_aPos.X();
7181 			long nOffsetY = rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
7182 			nXOffset += (int)sqrt(double(nOffsetX*nOffsetX + nOffsetY*nOffsetY));
7183 		}
7184         if( ! rGlyphs[i].m_nGlyphId )
7185             continue;
7186 
7187         aDeltaPos = rRotScale.transform( aDeltaPos );
7188 
7189         Matrix3 aMat;
7190         if( fSkewB != 0.0 || fSkewA != 0.0 )
7191             aMat.skew( fSkewA, fSkewB );
7192         aMat.scale( fTempXScale, fYScale );
7193         aMat.rotate( fAngle+fDeltaAngle );
7194         aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
7195         aMat.append( m_aPages.back(), rLine );
7196         rLine.append( " Tm" );
7197         if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
7198         {
7199             rLine.append( " /F" );
7200             rLine.append( rGlyphs[i].m_nMappedFontId );
7201             rLine.append( ' ' );
7202             m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7203             rLine.append( " Tf" );
7204         }
7205         rLine.append( "<" );
7206         appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
7207         rLine.append( ">Tj\n" );
7208     }
7209 }
7210 
7211 void PDFWriterImpl::drawHorizontalGlyphs(
7212         const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7213         OStringBuffer& rLine,
7214         const Point& rAlignOffset,
7215         double fAngle,
7216         double fXScale,
7217         double fSkew,
7218         sal_Int32 nFontHeight,
7219         sal_Int32 nPixelFontHeight
7220         )
7221 {
7222     // horizontal (= normal) case
7223 
7224     // fill in  run end indices
7225     // end is marked by index of the first glyph of the next run
7226     // a run is marked by same mapped font id and same Y position
7227     std::vector< sal_uInt32 > aRunEnds;
7228     aRunEnds.reserve( rGlyphs.size() );
7229     for( size_t i = 1; i < rGlyphs.size(); i++ )
7230     {
7231         if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
7232             rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
7233         {
7234             aRunEnds.push_back(i);
7235         }
7236     }
7237     // last run ends at last glyph
7238     aRunEnds.push_back( rGlyphs.size() );
7239 
7240     // loop over runs of the same font
7241     sal_uInt32 nBeginRun = 0;
7242     for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
7243     {
7244         // setup text matrix
7245         Point aCurPos = rGlyphs[nBeginRun].m_aPos;
7246         // back transformation to current coordinate system
7247         aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7248         aCurPos += rAlignOffset;
7249         // the first run can be set with "Td" operator
7250         // subsequent use of that operator would move
7251         // the texline matrix relative to what was set before
7252         // making use of that would drive us into rounding issues
7253         Matrix3 aMat;
7254         if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
7255         {
7256             m_aPages.back().appendPoint( aCurPos, rLine, false );
7257             rLine.append( " Td " );
7258         }
7259         else
7260         {
7261             if( fSkew != 0.0 )
7262                 aMat.skew( 0.0, fSkew );
7263             aMat.scale( fXScale, 1.0 );
7264             aMat.rotate( fAngle );
7265             aMat.translate( aCurPos.X(), aCurPos.Y() );
7266             aMat.append( m_aPages.back(), rLine );
7267             rLine.append( " Tm\n" );
7268         }
7269         // set up correct font
7270         rLine.append( "/F" );
7271         rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
7272         rLine.append( ' ' );
7273         m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7274         rLine.append( " Tf" );
7275 
7276         // output glyphs using Tj or TJ
7277         OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
7278         aKernedLine.append( "[<" );
7279         aUnkernedLine.append( '<' );
7280         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
7281         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
7282 
7283         aMat.invert();
7284         bool bNeedKern = false;
7285         for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
7286         {
7287             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
7288             // check if default glyph positioning is sufficient
7289             const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
7290             const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
7291             double fAdvance = aThisPos.X() - aPrevPos.X();
7292             fAdvance *= 1000.0 / nPixelFontHeight;
7293             const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5);
7294             if( nAdjustment != 0 )
7295             {
7296                 // apply individual glyph positioning
7297                 bNeedKern = true;
7298                 aKernedLine.append( ">" );
7299                 aKernedLine.append( nAdjustment );
7300                 aKernedLine.append( "<" );
7301             }
7302             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
7303         }
7304         aKernedLine.append( ">]TJ\n" );
7305         aUnkernedLine.append( ">Tj\n" );
7306         rLine.append( bNeedKern ? aKernedLine : aUnkernedLine );
7307 
7308         // set beginning of next run
7309         nBeginRun = aRunEnds[nRun];
7310     }
7311 }
7312 
7313 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines )
7314 {
7315     // relief takes precedence over shadow (see outdev3.cxx)
7316     if(  m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE )
7317     {
7318         drawRelief( rLayout, rText, bTextLines );
7319         return;
7320     }
7321     else if( m_aCurrentPDFState.m_aFont.IsShadow() )
7322         drawShadow( rLayout, rText, bTextLines );
7323 
7324     OStringBuffer aLine( 512 );
7325 
7326     const int nMaxGlyphs = 256;
7327 
7328     sal_GlyphId pGlyphs[nMaxGlyphs];
7329     sal_Int32 pGlyphWidths[nMaxGlyphs];
7330     sal_uInt8 pMappedGlyphs[nMaxGlyphs];
7331     sal_Int32 pMappedFontObjects[nMaxGlyphs];
7332     std::vector<sal_Ucs> aUnicodes;
7333     aUnicodes.reserve( nMaxGlyphs );
7334     sal_Int32 pUnicodesPerGlyph[nMaxGlyphs];
7335     int pCharPosAry[nMaxGlyphs];
7336     sal_Int32 nAdvanceWidths[nMaxGlyphs];
7337     const ImplFontData* pFallbackFonts[nMaxGlyphs];
7338     bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
7339     int nGlyphs;
7340     int nIndex = 0;
7341     int nMinCharPos = 0, nMaxCharPos = rText.Len()-1;
7342     double fXScale = 1.0;
7343     double fSkew = 0.0;
7344     sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight;
7345     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
7346 
7347     // transform font height back to current units
7348     // note: the layout calculates in outdevs device pixel !!
7349     sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
7350     if( m_aCurrentPDFState.m_aFont.GetWidth() )
7351     {
7352         Font aFont( m_aCurrentPDFState.m_aFont );
7353         aFont.SetWidth( 0 );
7354         FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
7355         if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() )
7356         {
7357             fXScale =
7358                 (double)m_aCurrentPDFState.m_aFont.GetWidth() /
7359                 (double)aMetric.GetWidth();
7360         }
7361         // force state before GetFontMetric
7362         m_pReferenceDevice->ImplNewFont();
7363     }
7364 
7365     // perform artificial italics if necessary
7366     if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
7367           m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
7368         !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL ||
7369            m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE )
7370         )
7371     {
7372         fSkew = M_PI/12.0;
7373     }
7374 
7375     // if the mapmode is distorted we need to adjust for that also
7376     if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
7377     {
7378         fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
7379     }
7380 
7381     int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
7382     // normalize angles
7383     while( nAngle < 0 )
7384         nAngle += 3600;
7385     nAngle = nAngle % 3600;
7386     double fAngle = (double)nAngle * M_PI / 1800.0;
7387 
7388     Matrix3 aRotScale;
7389     aRotScale.scale( fXScale, 1.0 );
7390     if( fAngle != 0.0 )
7391         aRotScale.rotate( -fAngle );
7392 
7393     bool bPop = false;
7394     bool bABold = false;
7395     // artificial bold necessary ?
7396     if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
7397         m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
7398     {
7399         if( ! bPop )
7400             aLine.append( "q " );
7401         bPop = true;
7402         bABold = true;
7403     }
7404     // setup text colors (if necessary)
7405     Color aStrokeColor( COL_TRANSPARENT );
7406     Color aNonStrokeColor( COL_TRANSPARENT );
7407 
7408     if( m_aCurrentPDFState.m_aFont.IsOutline() )
7409     {
7410         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7411         aNonStrokeColor = Color( COL_WHITE );
7412     }
7413     else
7414         aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7415     if( bABold )
7416         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7417 
7418     if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
7419     {
7420         if( ! bPop )
7421             aLine.append( "q " );
7422         bPop = true;
7423         appendStrokingColor( aStrokeColor, aLine );
7424         aLine.append( "\n" );
7425     }
7426     if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
7427     {
7428         if( ! bPop )
7429             aLine.append( "q " );
7430         bPop = true;
7431         appendNonStrokingColor( aNonStrokeColor, aLine );
7432         aLine.append( "\n" );
7433     }
7434 
7435     // begin text object
7436     aLine.append( "BT\n" );
7437     // outline attribute ?
7438     if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
7439     {
7440         // set correct text mode, set stroke width
7441         aLine.append( "2 Tr " ); // fill, then stroke
7442 
7443         if( m_aCurrentPDFState.m_aFont.IsOutline() )
7444         {
7445             // unclear what to do in case of outline and artificial bold
7446             // for the time being outline wins
7447             aLine.append( "0.25 w \n" );
7448         }
7449         else
7450         {
7451             double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0;
7452             m_aPages.back().appendMappedLength( fW, aLine );
7453             aLine.append ( " w\n" );
7454         }
7455     }
7456 
7457     FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
7458 
7459     // collect the glyphs into a single array
7460     const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
7461     std::vector< PDFGlyph > aGlyphs;
7462     aGlyphs.reserve( nTmpMaxGlyphs );
7463     // first get all the glyphs and register them; coordinates still in Pixel
7464     Point aGNGlyphPos;
7465     while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 )
7466     {
7467         aUnicodes.clear();
7468         for( int i = 0; i < nGlyphs; i++ )
7469         {
7470             pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] );
7471 
7472             // default case: 1 glyph is one unicode
7473             pUnicodesPerGlyph[i] = 1;
7474             if( (pGlyphs[i] & GF_ISCHAR) )
7475             {
7476                 aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) );
7477             }
7478             else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
7479             {
7480                 int nChars = 1;
7481                 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) );
7482                 pUnicodesPerGlyph[i] = 1;
7483                 // try to handle ligatures and such
7484                 if( i < nGlyphs-1 )
7485                 {
7486                     nChars = pCharPosAry[i+1] - pCharPosAry[i];
7487                     // #i115618# fix for simple RTL+CTL cases
7488                     // TODO: sanitize for RTL ligatures, more complex CTL, etc.
7489                     if( nChars < 0 )
7490                         nChars = -nChars;
7491 		    else if( nChars == 0 )
7492                         nChars = 1;
7493                     pUnicodesPerGlyph[i] = nChars;
7494                     for( int n = 1; n < nChars; n++ )
7495                         aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) );
7496                 }
7497                 // #i36691# hack that is needed because currently the pGlyphs[]
7498                 // argument is ignored for embeddable fonts and so the layout
7499                 // engine's glyph work is ignored (i.e. char mirroring)
7500                 // TODO: a real solution would be to map the layout engine's
7501                 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
7502                 // back to unicode and then to embeddable font's encoding
7503                 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL )
7504                 {
7505                     size_t nI = aUnicodes.size()-1;
7506                     for( int n = 0; n < nChars; n++, nI-- )
7507                         aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI]));
7508                 }
7509             }
7510             else
7511                 aUnicodes.push_back( 0 );
7512             // note: in case of ctl one character may result
7513             // in multiple glyphs. The current SalLayout
7514             // implementations set -1 then to indicate that no direct
7515             // mapping is possible
7516         }
7517 
7518         registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
7519 
7520         for( int i = 0; i < nGlyphs; i++ )
7521         {
7522             aGlyphs.push_back( PDFGlyph( aGNGlyphPos,
7523                                          pGlyphWidths[i],
7524                                          pGlyphs[i],
7525                                          pMappedFontObjects[i],
7526                                          pMappedGlyphs[i] ) );
7527             if( bVertical )
7528                 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7529             else
7530                 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7531         }
7532     }
7533 
7534     Point aAlignOffset;
7535     if ( eAlign == ALIGN_BOTTOM )
7536         aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
7537     else if ( eAlign == ALIGN_TOP )
7538         aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
7539     if( aAlignOffset.X() || aAlignOffset.Y() )
7540         aAlignOffset = aRotScale.transform( aAlignOffset );
7541 
7542     /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
7543        string contained only on of the UTF16 BOMs
7544     */
7545     if( ! aGlyphs.empty() )
7546     {
7547         if( bVertical )
7548             drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
7549         else
7550             drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
7551     }
7552 
7553     // end textobject
7554     aLine.append( "ET\n" );
7555     if( bPop )
7556         aLine.append( "Q\n" );
7557 
7558     writeBuffer( aLine.getStr(), aLine.getLength() );
7559 
7560     // draw eventual textlines
7561     FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
7562     FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
7563     FontUnderline eOverline  = m_aCurrentPDFState.m_aFont.GetOverline();
7564     if( bTextLines &&
7565         (
7566          ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) ||
7567          ( eOverline  != UNDERLINE_NONE && eOverline  != UNDERLINE_DONTKNOW ) ||
7568          ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
7569          )
7570         )
7571     {
7572         sal_Bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
7573         if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
7574         {
7575             Point aPos, aStartPt;
7576             sal_Int32 nWidth = 0, nAdvance=0;
7577             for( int nStart = 0;;)
7578             {
7579                 sal_GlyphId nGlyphIndex;
7580                 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7581                     break;
7582 
7583                 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7584                 {
7585                     if( !nWidth )
7586                         aStartPt = aPos;
7587 
7588                     nWidth += nAdvance;
7589                 }
7590                 else if( nWidth > 0 )
7591                 {
7592                     drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7593                                   m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7594                                   eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7595                     nWidth = 0;
7596                 }
7597             }
7598 
7599             if( nWidth > 0 )
7600             {
7601                 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7602                               m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7603                               eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7604             }
7605         }
7606         else
7607         {
7608             Point aStartPt = rLayout.GetDrawPosition();
7609             int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
7610             drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7611                           m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7612                           eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7613         }
7614     }
7615 
7616     // write eventual emphasis marks
7617     if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
7618     {
7619         PolyPolygon 			aEmphPoly;
7620         Rectangle				aEmphRect1;
7621         Rectangle				aEmphRect2;
7622         long					nEmphYOff;
7623         long					nEmphWidth;
7624         long					nEmphHeight;
7625         sal_Bool					bEmphPolyLine;
7626         FontEmphasisMark		nEmphMark;
7627 
7628         push( PUSH_ALL );
7629 
7630         aLine.setLength( 0 );
7631         aLine.append( "q\n" );
7632 
7633         nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
7634         if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7635             nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
7636         else
7637             nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
7638         m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
7639                                                  bEmphPolyLine,
7640                                                  aEmphRect1,
7641                                                  aEmphRect2,
7642                                                  nEmphYOff,
7643                                                  nEmphWidth,
7644                                                  nEmphMark,
7645                                                  m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight),
7646                                                  m_pReferenceDevice->mpFontEntry->mnOrientation );
7647         if ( bEmphPolyLine )
7648         {
7649             setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
7650             setFillColor( Color( COL_TRANSPARENT ) );
7651         }
7652         else
7653         {
7654             setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
7655             setLineColor( Color( COL_TRANSPARENT ) );
7656         }
7657         writeBuffer( aLine.getStr(), aLine.getLength() );
7658 
7659         Point aOffset = Point(0,0);
7660 
7661         if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7662             aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff;
7663         else
7664             aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff;
7665 
7666         long nEmphWidth2     = nEmphWidth / 2;
7667         long nEmphHeight2    = nEmphHeight / 2;
7668         aOffset += Point( nEmphWidth2, nEmphHeight2 );
7669 
7670         if ( eAlign == ALIGN_BOTTOM )
7671             aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent;
7672         else if ( eAlign == ALIGN_TOP )
7673             aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent;
7674 
7675         for( int nStart = 0;;)
7676         {
7677             Point aPos;
7678             sal_GlyphId nGlyphIndex;
7679             sal_Int32 nAdvance;
7680             if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7681                 break;
7682 
7683             if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7684             {
7685                 Point aAdjOffset = aOffset;
7686                 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2;
7687                 aAdjOffset = aRotScale.transform( aAdjOffset );
7688 
7689                 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
7690 
7691                 aPos += aAdjOffset;
7692                 aPos = m_pReferenceDevice->PixelToLogic( aPos );
7693                 drawEmphasisMark( aPos.X(), aPos.Y(),
7694                                   aEmphPoly, bEmphPolyLine,
7695                                   aEmphRect1, aEmphRect2 );
7696             }
7697         }
7698 
7699         writeBuffer( "Q\n", 2 );
7700         pop();
7701     }
7702 
7703 }
7704 
7705 void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
7706                                       const PolyPolygon& rPolyPoly, sal_Bool bPolyLine,
7707                                       const Rectangle& rRect1, const Rectangle& rRect2 )
7708 {
7709     // TODO: pass nWidth as width of this mark
7710     // long nWidth = 0;
7711 
7712     if ( rPolyPoly.Count() )
7713     {
7714         if ( bPolyLine )
7715         {
7716             Polygon aPoly = rPolyPoly.GetObject( 0 );
7717             aPoly.Move( nX, nY );
7718             drawPolyLine( aPoly );
7719         }
7720         else
7721         {
7722             PolyPolygon aPolyPoly = rPolyPoly;
7723             aPolyPoly.Move( nX, nY );
7724             drawPolyPolygon( aPolyPoly );
7725         }
7726     }
7727 
7728     if ( !rRect1.IsEmpty() )
7729     {
7730         Rectangle aRect( Point( nX+rRect1.Left(),
7731                                 nY+rRect1.Top() ), rRect1.GetSize() );
7732         drawRectangle( aRect );
7733     }
7734 
7735     if ( !rRect2.IsEmpty() )
7736     {
7737         Rectangle aRect( Point( nX+rRect2.Left(),
7738                                 nY+rRect2.Top() ), rRect2.GetSize() );
7739 
7740         drawRectangle( aRect );
7741     }
7742 }
7743 
7744 void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7745 {
7746     MARK( "drawText" );
7747 
7748     updateGraphicsState();
7749 
7750     // get a layout from the OuputDevice's SalGraphics
7751     // this also enforces font substitution and sets the font on SalGraphics
7752     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
7753     if( pLayout )
7754     {
7755         drawLayout( *pLayout, rText, bTextLines );
7756         pLayout->Release();
7757     }
7758 }
7759 
7760 void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7761 {
7762     MARK( "drawText with array" );
7763 
7764     updateGraphicsState();
7765 
7766     // get a layout from the OuputDevice's SalGraphics
7767     // this also enforces font substitution and sets the font on SalGraphics
7768     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
7769     if( pLayout )
7770     {
7771         drawLayout( *pLayout, rText, bTextLines );
7772         pLayout->Release();
7773     }
7774 }
7775 
7776 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7777 {
7778     MARK( "drawStretchText" );
7779 
7780     updateGraphicsState();
7781 
7782     // get a layout from the OuputDevice's SalGraphics
7783     // this also enforces font substitution and sets the font on SalGraphics
7784     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
7785     if( pLayout )
7786     {
7787         drawLayout( *pLayout, rText, bTextLines );
7788         pLayout->Release();
7789     }
7790 }
7791 
7792 void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines )
7793 {
7794     long        nWidth          = rRect.GetWidth();
7795     long        nHeight         = rRect.GetHeight();
7796 
7797     if ( nWidth <= 0 || nHeight <= 0 )
7798         return;
7799 
7800     MARK( "drawText with rectangle" );
7801 
7802     updateGraphicsState();
7803 
7804     // clip with rectangle
7805     OStringBuffer aLine;
7806     aLine.append( "q " );
7807     m_aPages.back().appendRect( rRect, aLine );
7808     aLine.append( " W* n\n" );
7809     writeBuffer( aLine.getStr(), aLine.getLength() );
7810 
7811     // if disabled text is needed, put in here
7812 
7813     Point       aPos            = rRect.TopLeft();
7814 
7815     long		nTextHeight		= m_pReferenceDevice->GetTextHeight();
7816     xub_StrLen  nMnemonicPos    = STRING_NOTFOUND;
7817 
7818     String aStr = rOrigStr;
7819     if ( nStyle & TEXT_DRAW_MNEMONIC )
7820         aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos );
7821 
7822     // multiline text
7823     if ( nStyle & TEXT_DRAW_MULTILINE )
7824     {
7825         XubString               aLastLine;
7826         ImplMultiTextLineInfo   aMultiLineInfo;
7827         ImplTextLineInfo*       pLineInfo;
7828         long                    nMaxTextWidth;
7829         xub_StrLen              i;
7830         xub_StrLen              nLines;
7831         xub_StrLen              nFormatLines;
7832 
7833         if ( nTextHeight )
7834         {
7835             ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice );
7836             nMaxTextWidth = OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout );
7837             nLines = (xub_StrLen)(nHeight/nTextHeight);
7838             nFormatLines = aMultiLineInfo.Count();
7839             if ( !nLines )
7840                 nLines = 1;
7841             if ( nFormatLines > nLines )
7842             {
7843                 if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
7844                 {
7845                     // handle last line
7846                     nFormatLines = nLines-1;
7847 
7848                     pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
7849                     aLastLine = aStr.Copy( pLineInfo->GetIndex() );
7850                     aLastLine.ConvertLineEnd( LINEEND_LF );
7851                     // replace line feed by space
7852                     xub_StrLen nLastLineLen = aLastLine.Len();
7853                     for ( i = 0; i < nLastLineLen; i++ )
7854                     {
7855                         if ( aLastLine.GetChar( i ) == _LF )
7856                             aLastLine.SetChar( i, ' ' );
7857                     }
7858                     aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
7859                     nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
7860                     nStyle |= TEXT_DRAW_TOP;
7861                 }
7862             }
7863 
7864             // vertical alignment
7865             if ( nStyle & TEXT_DRAW_BOTTOM )
7866                 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
7867             else if ( nStyle & TEXT_DRAW_VCENTER )
7868                 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
7869 
7870             // draw all lines excluding the last
7871             for ( i = 0; i < nFormatLines; i++ )
7872             {
7873                 pLineInfo = aMultiLineInfo.GetLine( i );
7874                 if ( nStyle & TEXT_DRAW_RIGHT )
7875                     aPos.X() += nWidth-pLineInfo->GetWidth();
7876                 else if ( nStyle & TEXT_DRAW_CENTER )
7877                     aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
7878                 xub_StrLen nIndex   = pLineInfo->GetIndex();
7879                 xub_StrLen nLineLen = pLineInfo->GetLen();
7880                 drawText( aPos, aStr, nIndex, nLineLen, bTextLines );
7881                 // mnemonics should not appear in documents,
7882                 // if the need arises, put them in here
7883                 aPos.Y() += nTextHeight;
7884                 aPos.X() = rRect.Left();
7885             }
7886 
7887 
7888             // output last line left adjusted since it was shortened
7889             if ( aLastLine.Len() )
7890                 drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines );
7891         }
7892     }
7893     else
7894     {
7895         long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7896 
7897         // Evt. Text kuerzen
7898         if ( nTextWidth > nWidth )
7899         {
7900             if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) )
7901             {
7902                 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
7903                 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
7904                 nStyle |= TEXT_DRAW_LEFT;
7905                 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7906             }
7907         }
7908 
7909         // vertical alignment
7910         if ( nStyle & TEXT_DRAW_RIGHT )
7911             aPos.X() += nWidth-nTextWidth;
7912         else if ( nStyle & TEXT_DRAW_CENTER )
7913             aPos.X() += (nWidth-nTextWidth)/2;
7914 
7915         if ( nStyle & TEXT_DRAW_BOTTOM )
7916             aPos.Y() += nHeight-nTextHeight;
7917         else if ( nStyle & TEXT_DRAW_VCENTER )
7918             aPos.Y() += (nHeight-nTextHeight)/2;
7919 
7920         // mnemonics should be inserted here if the need arises
7921 
7922         // draw the actual text
7923         drawText( aPos, aStr, 0, STRING_LEN, bTextLines );
7924     }
7925 
7926     // reset clip region to original value
7927     aLine.setLength( 0 );
7928     aLine.append( "Q\n" );
7929     writeBuffer( aLine.getStr(), aLine.getLength() );
7930 }
7931 
7932 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
7933 {
7934     MARK( "drawLine" );
7935 
7936     updateGraphicsState();
7937 
7938     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7939         return;
7940 
7941     OStringBuffer aLine;
7942     m_aPages.back().appendPoint( rStart, aLine );
7943     aLine.append( " m " );
7944     m_aPages.back().appendPoint( rStop, aLine );
7945     aLine.append( " l S\n" );
7946 
7947     writeBuffer( aLine.getStr(), aLine.getLength() );
7948 }
7949 
7950 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
7951 {
7952     MARK( "drawLine with LineInfo" );
7953     updateGraphicsState();
7954 
7955     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7956         return;
7957 
7958     if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 )
7959     {
7960         drawLine( rStart, rStop );
7961         return;
7962     }
7963 
7964     OStringBuffer aLine;
7965 
7966     aLine.append( "q " );
7967     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
7968     {
7969         m_aPages.back().appendPoint( rStart, aLine );
7970         aLine.append( " m " );
7971         m_aPages.back().appendPoint( rStop, aLine );
7972         aLine.append( " l S Q\n" );
7973 
7974         writeBuffer( aLine.getStr(), aLine.getLength() );
7975     }
7976     else
7977     {
7978         PDFWriter::ExtLineInfo aInfo;
7979         convertLineInfoToExtLineInfo( rInfo, aInfo );
7980         Point aPolyPoints[2] = { rStart, rStop };
7981         Polygon aPoly( 2, aPolyPoints );
7982         drawPolyLine( aPoly, aInfo );
7983     }
7984 }
7985 
7986 void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth )
7987 {
7988     Point aDiff( rStop-rStart );
7989     double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) );
7990     if( fLen < 1.0 )
7991         return;
7992 
7993     MARK( "drawWaveLine" );
7994     updateGraphicsState();
7995 
7996     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7997         return;
7998 
7999     OStringBuffer aLine( 512 );
8000     aLine.append( "q " );
8001     m_aPages.back().appendMappedLength( nLineWidth, aLine, true );
8002     aLine.append( " w " );
8003 
8004     appendDouble( (double)aDiff.X()/fLen, aLine );
8005     aLine.append( ' ' );
8006     appendDouble( -(double)aDiff.Y()/fLen, aLine );
8007     aLine.append( ' ' );
8008     appendDouble( (double)aDiff.Y()/fLen, aLine );
8009     aLine.append( ' ' );
8010     appendDouble( (double)aDiff.X()/fLen, aLine );
8011     aLine.append( ' ' );
8012     m_aPages.back().appendPoint( rStart, aLine );
8013     aLine.append( " cm " );
8014     m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine );
8015     aLine.append( "Q\n" );
8016     writeBuffer( aLine.getStr(), aLine.getLength() );
8017 }
8018 
8019 #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x )
8020 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
8021 
8022 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8023 {
8024     // note: units in pFontEntry are ref device pixel
8025     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8026     long			nLineHeight = 0;
8027     long			nLinePos = 0;
8028 
8029     appendStrokingColor( aColor, aLine );
8030     aLine.append( "\n" );
8031 
8032     if ( bIsAbove )
8033     {
8034         if ( !pFontEntry->maMetric.mnAboveWUnderlineSize )
8035             m_pReferenceDevice->ImplInitAboveTextLineSize();
8036         nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize );
8037         nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset );
8038     }
8039     else
8040     {
8041         if ( !pFontEntry->maMetric.mnWUnderlineSize )
8042             m_pReferenceDevice->ImplInitTextLineSize();
8043         nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize );
8044         nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset );
8045     }
8046     if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
8047         nLineHeight = 3;
8048 
8049     long nLineWidth = getReferenceDevice()->mnDPIX/450;
8050     if ( ! nLineWidth )
8051         nLineWidth = 1;
8052 
8053     if ( eTextLine == UNDERLINE_BOLDWAVE )
8054         nLineWidth = 3*nLineWidth;
8055 
8056     m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
8057     aLine.append( " w " );
8058 
8059     if ( eTextLine == UNDERLINE_DOUBLEWAVE )
8060     {
8061         long nOrgLineHeight = nLineHeight;
8062         nLineHeight /= 3;
8063         if ( nLineHeight < 2 )
8064         {
8065             if ( nOrgLineHeight > 1 )
8066                 nLineHeight = 2;
8067             else
8068                 nLineHeight = 1;
8069         }
8070         long nLineDY = nOrgLineHeight-(nLineHeight*2);
8071         if ( nLineDY < nLineWidth )
8072             nLineDY = nLineWidth;
8073         long nLineDY2 = nLineDY/2;
8074         if ( !nLineDY2 )
8075             nLineDY2 = 1;
8076 
8077         nLinePos -= nLineWidth-nLineDY2;
8078 
8079         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8080 
8081         nLinePos += nLineWidth+nLineDY;
8082         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8083     }
8084     else
8085     {
8086         if ( eTextLine != UNDERLINE_BOLDWAVE )
8087             nLinePos -= nLineWidth/2;
8088         m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
8089     }
8090 }
8091 
8092 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8093 {
8094     // note: units in pFontEntry are ref device pixel
8095     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8096     long			nLineHeight = 0;
8097     long			nLinePos  = 0;
8098     long			nLinePos2 = 0;
8099 
8100     if ( eTextLine > UNDERLINE_BOLDWAVE )
8101         eTextLine = UNDERLINE_SINGLE;
8102 
8103     switch ( eTextLine )
8104     {
8105         case UNDERLINE_SINGLE:
8106         case UNDERLINE_DOTTED:
8107         case UNDERLINE_DASH:
8108         case UNDERLINE_LONGDASH:
8109         case UNDERLINE_DASHDOT:
8110         case UNDERLINE_DASHDOTDOT:
8111             if ( bIsAbove )
8112             {
8113                 if ( !pFontEntry->maMetric.mnAboveUnderlineSize )
8114                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8115                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize );
8116                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset );
8117             }
8118             else
8119             {
8120                 if ( !pFontEntry->maMetric.mnUnderlineSize )
8121                     m_pReferenceDevice->ImplInitTextLineSize();
8122                 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize );
8123                 nLinePos    = HCONV( pFontEntry->maMetric.mnUnderlineOffset );
8124             }
8125             break;
8126         case UNDERLINE_BOLD:
8127         case UNDERLINE_BOLDDOTTED:
8128         case UNDERLINE_BOLDDASH:
8129         case UNDERLINE_BOLDLONGDASH:
8130         case UNDERLINE_BOLDDASHDOT:
8131         case UNDERLINE_BOLDDASHDOTDOT:
8132             if ( bIsAbove )
8133             {
8134                 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize )
8135                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8136                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize );
8137                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset );
8138             }
8139             else
8140             {
8141                 if ( !pFontEntry->maMetric.mnBUnderlineSize )
8142                     m_pReferenceDevice->ImplInitTextLineSize();
8143                 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize );
8144                 nLinePos    = HCONV( pFontEntry->maMetric.mnBUnderlineOffset );
8145                 nLinePos += nLineHeight/2;
8146             }
8147             break;
8148         case UNDERLINE_DOUBLE:
8149             if ( bIsAbove )
8150             {
8151                 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize )
8152                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8153                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize );
8154                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 );
8155                 nLinePos2   = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 );
8156             }
8157             else
8158             {
8159                 if ( !pFontEntry->maMetric.mnDUnderlineSize )
8160                     m_pReferenceDevice->ImplInitTextLineSize();
8161                 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize );
8162                 nLinePos    = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 );
8163                 nLinePos2   = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 );
8164             }
8165         default:
8166             break;
8167     }
8168 
8169     if ( nLineHeight )
8170     {
8171         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8172         aLine.append( " w " );
8173         appendStrokingColor( aColor, aLine );
8174         aLine.append( "\n" );
8175 
8176         switch ( eTextLine )
8177         {
8178             case UNDERLINE_DOTTED:
8179             case UNDERLINE_BOLDDOTTED:
8180                 aLine.append( "[ " );
8181                 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8182                 aLine.append( " ] 0 d\n" );
8183                 break;
8184             case UNDERLINE_DASH:
8185             case UNDERLINE_LONGDASH:
8186             case UNDERLINE_BOLDDASH:
8187             case UNDERLINE_BOLDLONGDASH:
8188                 {
8189                     sal_Int32 nDashLength = 4*nLineHeight;
8190                     sal_Int32 nVoidLength = 2*nLineHeight;
8191                     if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) )
8192                         nDashLength = 8*nLineHeight;
8193 
8194                     aLine.append( "[ " );
8195                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8196                     aLine.append( ' ' );
8197                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8198                     aLine.append( " ] 0 d\n" );
8199                 }
8200                 break;
8201             case UNDERLINE_DASHDOT:
8202             case UNDERLINE_BOLDDASHDOT:
8203                 {
8204                     sal_Int32 nDashLength = 4*nLineHeight;
8205                     sal_Int32 nVoidLength = 2*nLineHeight;
8206                     aLine.append( "[ " );
8207                     m_aPages.back().appendMappedLength( nDashLength, 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             case UNDERLINE_DASHDOTDOT:
8218             case UNDERLINE_BOLDDASHDOTDOT:
8219                 {
8220                     sal_Int32 nDashLength = 4*nLineHeight;
8221                     sal_Int32 nVoidLength = 2*nLineHeight;
8222                     aLine.append( "[ " );
8223                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8224                     aLine.append( ' ' );
8225                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8226                     aLine.append( ' ' );
8227                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8228                     aLine.append( ' ' );
8229                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8230                     aLine.append( ' ' );
8231                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8232                     aLine.append( ' ' );
8233                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8234                     aLine.append( " ] 0 d\n" );
8235                 }
8236                 break;
8237             default:
8238                 break;
8239         }
8240 
8241         aLine.append( "0 " );
8242         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8243         aLine.append( " m " );
8244         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8245         aLine.append( ' ' );
8246         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8247         aLine.append( " l S\n" );
8248         if ( eTextLine == UNDERLINE_DOUBLE )
8249         {
8250             aLine.append( "0 " );
8251             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8252             aLine.append( " m " );
8253             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8254             aLine.append( ' ' );
8255             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8256             aLine.append( " l S\n" );
8257         }
8258     }
8259 }
8260 
8261 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
8262 {
8263     // note: units in pFontEntry are ref device pixel
8264     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8265     long			nLineHeight = 0;
8266     long			nLinePos  = 0;
8267     long			nLinePos2 = 0;
8268 
8269     if ( eStrikeout > STRIKEOUT_X )
8270         eStrikeout = STRIKEOUT_SINGLE;
8271 
8272     switch ( eStrikeout )
8273     {
8274         case STRIKEOUT_SINGLE:
8275             if ( !pFontEntry->maMetric.mnStrikeoutSize )
8276                 m_pReferenceDevice->ImplInitTextLineSize();
8277             nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize );
8278             nLinePos    = HCONV( pFontEntry->maMetric.mnStrikeoutOffset );
8279             break;
8280         case STRIKEOUT_BOLD:
8281             if ( !pFontEntry->maMetric.mnBStrikeoutSize )
8282                 m_pReferenceDevice->ImplInitTextLineSize();
8283             nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize );
8284             nLinePos    = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset );
8285             break;
8286         case STRIKEOUT_DOUBLE:
8287             if ( !pFontEntry->maMetric.mnDStrikeoutSize )
8288                 m_pReferenceDevice->ImplInitTextLineSize();
8289             nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize );
8290             nLinePos    = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 );
8291             nLinePos2   = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 );
8292             break;
8293         default:
8294             break;
8295     }
8296 
8297     if ( nLineHeight )
8298     {
8299         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8300         aLine.append( " w " );
8301         appendStrokingColor( aColor, aLine );
8302         aLine.append( "\n" );
8303 
8304         aLine.append( "0 " );
8305         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8306         aLine.append( " m " );
8307         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8308         aLine.append( ' ' );
8309         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8310         aLine.append( " l S\n" );
8311 
8312         if ( eStrikeout == STRIKEOUT_DOUBLE )
8313         {
8314             aLine.append( "0 " );
8315             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8316             aLine.append( " m " );
8317             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8318             aLine.append( ' ' );
8319             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8320             aLine.append( " l S\n" );
8321         }
8322     }
8323 }
8324 
8325 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
8326 {
8327     String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" );
8328     String aStrikeout = aStrikeoutChar;
8329     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
8330         aStrikeout.Append( aStrikeout );
8331 
8332     // do not get broader than nWidth modulo 1 character
8333     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
8334         aStrikeout.Erase( 0, 1 );
8335     aStrikeout.Append( aStrikeoutChar );
8336     sal_Bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
8337     if ( bShadow )
8338     {
8339         Font aFont = m_aCurrentPDFState.m_aFont;
8340         aFont.SetShadow( sal_False );
8341         setFont( aFont );
8342         updateGraphicsState();
8343     }
8344 
8345     // strikeout string is left aligned non-CTL text
8346     sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode();
8347     m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED );
8348     drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false );
8349     m_pReferenceDevice->SetLayoutMode( nOrigTLM );
8350 
8351     if ( bShadow )
8352     {
8353         Font aFont = m_aCurrentPDFState.m_aFont;
8354         aFont.SetShadow( sal_True );
8355         setFont( aFont );
8356         updateGraphicsState();
8357     }
8358 }
8359 
8360 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove )
8361 {
8362     if ( !nWidth ||
8363          ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
8364            ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) &&
8365            ((eOverline  == UNDERLINE_NONE)||(eOverline  == UNDERLINE_DONTKNOW)) ) )
8366         return;
8367 
8368     MARK( "drawTextLine" );
8369     updateGraphicsState();
8370 
8371     // note: units in pFontEntry are ref device pixel
8372     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8373     Color			aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
8374     Color			aOverlineColor  = m_aCurrentPDFState.m_aOverlineColor;
8375     Color			aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
8376     bool			bStrikeoutDone = false;
8377     bool			bUnderlineDone = false;
8378     bool			bOverlineDone  = false;
8379 
8380     if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
8381     {
8382         drawStrikeoutChar( rPos, nWidth, eStrikeout );
8383         bStrikeoutDone = true;
8384     }
8385 
8386     Point aPos( rPos );
8387     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
8388     if( eAlign == ALIGN_TOP )
8389         aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent );
8390     else if( eAlign == ALIGN_BOTTOM )
8391         aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent );
8392 
8393     OStringBuffer aLine( 512 );
8394     // save GS
8395     aLine.append( "q " );
8396 
8397     // rotate and translate matrix
8398     double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
8399     Matrix3 aMat;
8400     aMat.rotate( fAngle );
8401     aMat.translate( aPos.X(), aPos.Y() );
8402     aMat.append( m_aPages.back(), aLine );
8403     aLine.append( " cm\n" );
8404 
8405     if ( aUnderlineColor.GetTransparency() != 0 )
8406         aUnderlineColor = aStrikeoutColor;
8407 
8408     if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
8409          (eUnderline == UNDERLINE_WAVE) ||
8410          (eUnderline == UNDERLINE_DOUBLEWAVE) ||
8411          (eUnderline == UNDERLINE_BOLDWAVE) )
8412     {
8413         drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8414         bUnderlineDone = true;
8415     }
8416 
8417     if ( (eOverline == UNDERLINE_SMALLWAVE) ||
8418          (eOverline == UNDERLINE_WAVE) ||
8419          (eOverline == UNDERLINE_DOUBLEWAVE) ||
8420          (eOverline == UNDERLINE_BOLDWAVE) )
8421     {
8422         drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8423         bOverlineDone = true;
8424     }
8425 
8426     if ( !bUnderlineDone )
8427     {
8428         drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8429     }
8430 
8431     if ( !bOverlineDone )
8432     {
8433         drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8434     }
8435 
8436     if ( !bStrikeoutDone )
8437     {
8438         drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
8439     }
8440 
8441     aLine.append( "Q\n" );
8442     writeBuffer( aLine.getStr(), aLine.getLength() );
8443 }
8444 
8445 void PDFWriterImpl::drawPolygon( const Polygon& rPoly )
8446 {
8447     MARK( "drawPolygon" );
8448 
8449     updateGraphicsState();
8450 
8451     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8452         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8453         return;
8454 
8455     int nPoints = rPoly.GetSize();
8456     OStringBuffer aLine( 20 * nPoints );
8457     m_aPages.back().appendPolygon( rPoly, aLine );
8458     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8459         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8460         aLine.append( "B*\n" );
8461     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8462         aLine.append( "S\n" );
8463     else
8464         aLine.append( "f*\n" );
8465 
8466     writeBuffer( aLine.getStr(), aLine.getLength() );
8467 }
8468 
8469 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly )
8470 {
8471     MARK( "drawPolyPolygon" );
8472 
8473     updateGraphicsState();
8474 
8475     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8476         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8477         return;
8478 
8479     int nPolygons = rPolyPoly.Count();
8480 
8481     OStringBuffer aLine( 40 * nPolygons );
8482     m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
8483     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8484         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8485         aLine.append( "B*\n" );
8486     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8487         aLine.append( "S\n" );
8488     else
8489         aLine.append( "f*\n" );
8490 
8491     writeBuffer( aLine.getStr(), aLine.getLength() );
8492 }
8493 
8494 void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
8495 {
8496     DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8497     nTransparentPercent = nTransparentPercent % 100;
8498 
8499     MARK( "drawTransparent" );
8500 
8501     updateGraphicsState();
8502 
8503     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8504         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8505         return;
8506 
8507     if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 )
8508     {
8509         m_aErrors.insert( m_bIsPDF_A1 ?
8510                           PDFWriter::Warning_Transparency_Omitted_PDFA :
8511                           PDFWriter::Warning_Transparency_Omitted_PDF13 );
8512 
8513         drawPolyPolygon( rPolyPoly );
8514         return;
8515     }
8516 
8517     // create XObject
8518     m_aTransparentObjects.push_back( TransparencyEmit() );
8519     // FIXME: polygons with beziers may yield incorrect bound rect
8520     m_aTransparentObjects.back().m_aBoundRect	  = rPolyPoly.GetBoundRect();
8521     // convert rectangle to default user space
8522     m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8523     m_aTransparentObjects.back().m_nObject		    = createObject();
8524     m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8525     m_aTransparentObjects.back().m_fAlpha		    = (double)(100-nTransparentPercent) / 100.0;
8526     m_aTransparentObjects.back().m_pContentStream   = new SvMemoryStream( 256, 256 );
8527     // create XObject's content stream
8528     OStringBuffer aContent( 256 );
8529     m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
8530     if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
8531         m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
8532         aContent.append( " B*\n" );
8533     else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
8534         aContent.append( " S\n" );
8535     else
8536         aContent.append( " f*\n" );
8537     m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() );
8538 
8539     OStringBuffer aObjName( 16 );
8540     aObjName.append( "Tr" );
8541     aObjName.append( m_aTransparentObjects.back().m_nObject );
8542     OString aTrName( aObjName.makeStringAndClear() );
8543     aObjName.append( "EGS" );
8544     aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8545     OString aExtName( aObjName.makeStringAndClear() );
8546 
8547     OStringBuffer aLine( 80 );
8548     // insert XObject
8549     aLine.append( "q /" );
8550     aLine.append( aExtName );
8551     aLine.append( " gs /" );
8552     aLine.append( aTrName );
8553     aLine.append( " Do Q\n" );
8554     writeBuffer( aLine.getStr(), aLine.getLength() );
8555 
8556     pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8557     pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8558 }
8559 
8560 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
8561 {
8562     if( nObject >= 0 )
8563     {
8564         switch( eKind )
8565         {
8566             case ResXObject:
8567                 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
8568                 if( ! m_aOutputStreams.empty() )
8569                     m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
8570                 break;
8571             case ResExtGState:
8572                 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
8573                 if( ! m_aOutputStreams.empty() )
8574                     m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
8575                 break;
8576             case ResShading:
8577                 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
8578                 if( ! m_aOutputStreams.empty() )
8579                     m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
8580                 break;
8581             case ResPattern:
8582                 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
8583                 if( ! m_aOutputStreams.empty() )
8584                     m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
8585                 break;
8586         }
8587     }
8588 }
8589 
8590 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect )
8591 {
8592     push( PUSH_ALL );
8593 
8594     // force reemitting clip region
8595     clearClipRegion();
8596     updateGraphicsState();
8597 
8598     m_aOutputStreams.push_front( StreamRedirect() );
8599     m_aOutputStreams.front().m_pStream = pStream;
8600     m_aOutputStreams.front().m_aMapMode = m_aMapMode;
8601 
8602     if( !rTargetRect.IsEmpty() )
8603     {
8604         m_aOutputStreams.front().m_aTargetRect =
8605             lcl_convert( m_aGraphicsStack.front().m_aMapMode,
8606                          m_aMapMode,
8607                          getReferenceDevice(),
8608                          rTargetRect );
8609         Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
8610         long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
8611         aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
8612         m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
8613     }
8614 
8615     // setup graphics state for independent object stream
8616 
8617     // force reemitting colors
8618     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8619     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8620 }
8621 
8622 Rectangle PDFWriterImpl::getRedirectTargetRect() const
8623 {
8624     return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect;
8625 }
8626 
8627 SvStream* PDFWriterImpl::endRedirect()
8628 {
8629     SvStream* pStream = NULL;
8630     if( ! m_aOutputStreams.empty() )
8631     {
8632         pStream		= m_aOutputStreams.front().m_pStream;
8633         m_aMapMode	= m_aOutputStreams.front().m_aMapMode;
8634         m_aOutputStreams.pop_front();
8635     }
8636 
8637     pop();
8638     // force reemitting colors and clip region
8639     clearClipRegion();
8640     m_aCurrentPDFState.m_bClipRegion = m_aGraphicsStack.front().m_bClipRegion;
8641     m_aCurrentPDFState.m_aClipRegion = m_aGraphicsStack.front().m_aClipRegion;
8642     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8643     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8644 
8645     updateGraphicsState();
8646 
8647     return pStream;
8648 }
8649 
8650 void PDFWriterImpl::beginTransparencyGroup()
8651 {
8652     updateGraphicsState();
8653     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8654         beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8655 }
8656 
8657 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
8658 {
8659     DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8660     nTransparentPercent = nTransparentPercent % 100;
8661 
8662     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8663     {
8664         // create XObject
8665         m_aTransparentObjects.push_back( TransparencyEmit() );
8666         m_aTransparentObjects.back().m_aBoundRect	= rBoundingBox;
8667         // convert rectangle to default user space
8668         m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8669         m_aTransparentObjects.back().m_nObject		= createObject();
8670         m_aTransparentObjects.back().m_fAlpha		= (double)(100-nTransparentPercent) / 100.0;
8671         // get XObject's content stream
8672         m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8673         m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8674 
8675         OStringBuffer aObjName( 16 );
8676         aObjName.append( "Tr" );
8677         aObjName.append( m_aTransparentObjects.back().m_nObject );
8678         OString aTrName( aObjName.makeStringAndClear() );
8679         aObjName.append( "EGS" );
8680         aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8681         OString aExtName( aObjName.makeStringAndClear() );
8682 
8683         OStringBuffer aLine( 80 );
8684         // insert XObject
8685         aLine.append( "q /" );
8686         aLine.append( aExtName );
8687         aLine.append( " gs /" );
8688         aLine.append( aTrName );
8689         aLine.append( " Do Q\n" );
8690         writeBuffer( aLine.getStr(), aLine.getLength() );
8691 
8692         pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8693         pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8694     }
8695 }
8696 
8697 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask )
8698 {
8699     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8700     {
8701         // create XObject
8702         m_aTransparentObjects.push_back( TransparencyEmit() );
8703         m_aTransparentObjects.back().m_aBoundRect	= rBoundingBox;
8704         // convert rectangle to default user space
8705         m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8706         m_aTransparentObjects.back().m_nObject		= createObject();
8707         m_aTransparentObjects.back().m_fAlpha		= 0.0;
8708         // get XObject's content stream
8709         m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8710         m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8711 
8712         // draw soft mask
8713         beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8714         drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask );
8715         m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect());
8716 
8717         OStringBuffer aObjName( 16 );
8718         aObjName.append( "Tr" );
8719         aObjName.append( m_aTransparentObjects.back().m_nObject );
8720         OString aTrName( aObjName.makeStringAndClear() );
8721         aObjName.append( "EGS" );
8722         aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8723         OString aExtName( aObjName.makeStringAndClear() );
8724 
8725         OStringBuffer aLine( 80 );
8726         // insert XObject
8727         aLine.append( "q /" );
8728         aLine.append( aExtName );
8729         aLine.append( " gs /" );
8730         aLine.append( aTrName );
8731         aLine.append( " Do Q\n" );
8732         writeBuffer( aLine.getStr(), aLine.getLength() );
8733 
8734         pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8735         pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8736     }
8737 }
8738 
8739 void PDFWriterImpl::drawRectangle( const Rectangle& rRect )
8740 {
8741     MARK( "drawRectangle" );
8742 
8743     updateGraphicsState();
8744 
8745     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8746         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8747         return;
8748 
8749     OStringBuffer aLine( 40 );
8750     m_aPages.back().appendRect( rRect, aLine );
8751 
8752     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8753         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8754         aLine.append( " B*\n" );
8755     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8756         aLine.append( " S\n" );
8757     else
8758         aLine.append( " f*\n" );
8759 
8760     writeBuffer( aLine.getStr(), aLine.getLength() );
8761 }
8762 
8763 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
8764 {
8765     MARK( "drawRectangle with rounded edges" );
8766 
8767     if( !nHorzRound && !nVertRound )
8768         drawRectangle( rRect );
8769 
8770     updateGraphicsState();
8771 
8772     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8773         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8774         return;
8775 
8776     if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
8777         nHorzRound = rRect.GetWidth()/2;
8778     if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
8779         nVertRound = rRect.GetHeight()/2;
8780 
8781     Point aPoints[16];
8782     const double kappa = 0.5522847498;
8783     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
8784     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
8785 
8786     aPoints[1]  = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
8787     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8788     aPoints[2]  = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
8789     aPoints[3]  = Point( aPoints[2].X()+kx, aPoints[2].Y() );
8790 
8791     aPoints[5]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
8792     aPoints[4]  = Point( aPoints[5].X(), aPoints[5].Y()-ky );
8793     aPoints[6]  = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
8794     aPoints[7]  = Point( aPoints[6].X(), aPoints[6].Y()+ky );
8795 
8796     aPoints[9]  = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
8797     aPoints[8]  = Point( aPoints[9].X()+kx, aPoints[9].Y() );
8798     aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
8799     aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
8800 
8801     aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
8802     aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
8803     aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
8804     aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
8805 
8806 
8807     OStringBuffer aLine( 80 );
8808     m_aPages.back().appendPoint( aPoints[1], aLine );
8809     aLine.append( " m " );
8810     m_aPages.back().appendPoint( aPoints[2], aLine );
8811     aLine.append( " l " );
8812     m_aPages.back().appendPoint( aPoints[3], aLine );
8813     aLine.append( ' ' );
8814     m_aPages.back().appendPoint( aPoints[4], aLine );
8815     aLine.append( ' ' );
8816     m_aPages.back().appendPoint( aPoints[5], aLine );
8817     aLine.append( " c\n" );
8818     m_aPages.back().appendPoint( aPoints[6], aLine );
8819     aLine.append( " l " );
8820     m_aPages.back().appendPoint( aPoints[7], aLine );
8821     aLine.append( ' ' );
8822     m_aPages.back().appendPoint( aPoints[8], aLine );
8823     aLine.append( ' ' );
8824     m_aPages.back().appendPoint( aPoints[9], aLine );
8825     aLine.append( " c\n" );
8826     m_aPages.back().appendPoint( aPoints[10], aLine );
8827     aLine.append( " l " );
8828     m_aPages.back().appendPoint( aPoints[11], aLine );
8829     aLine.append( ' ' );
8830     m_aPages.back().appendPoint( aPoints[12], aLine );
8831     aLine.append( ' ' );
8832     m_aPages.back().appendPoint( aPoints[13], aLine );
8833     aLine.append( " c\n" );
8834     m_aPages.back().appendPoint( aPoints[14], aLine );
8835     aLine.append( " l " );
8836     m_aPages.back().appendPoint( aPoints[15], aLine );
8837     aLine.append( ' ' );
8838     m_aPages.back().appendPoint( aPoints[0], aLine );
8839     aLine.append( ' ' );
8840     m_aPages.back().appendPoint( aPoints[1], aLine );
8841     aLine.append( " c " );
8842 
8843     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8844         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8845         aLine.append( "b*\n" );
8846     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8847         aLine.append( "s\n" );
8848     else
8849         aLine.append( "f*\n" );
8850 
8851     writeBuffer( aLine.getStr(), aLine.getLength() );
8852 }
8853 
8854 void PDFWriterImpl::drawEllipse( const Rectangle& rRect )
8855 {
8856     MARK( "drawEllipse" );
8857 
8858     updateGraphicsState();
8859 
8860     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8861         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8862         return;
8863 
8864     Point aPoints[12];
8865     const double kappa = 0.5522847498;
8866     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
8867     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
8868 
8869     aPoints[1]  = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
8870     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8871     aPoints[2]  = Point( aPoints[1].X() + kx, aPoints[1].Y() );
8872 
8873     aPoints[4]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
8874     aPoints[3]  = Point( aPoints[4].X(), aPoints[4].Y() - ky );
8875     aPoints[5]  = Point( aPoints[4].X(), aPoints[4].Y() + ky );
8876 
8877     aPoints[7]  = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
8878     aPoints[6]  = Point( aPoints[7].X() + kx, aPoints[7].Y() );
8879     aPoints[8]  = Point( aPoints[7].X() - kx, aPoints[7].Y() );
8880 
8881     aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
8882     aPoints[9]  = Point( aPoints[10].X(), aPoints[10].Y() + ky );
8883     aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
8884 
8885     OStringBuffer aLine( 80 );
8886     m_aPages.back().appendPoint( aPoints[1], aLine );
8887     aLine.append( " m " );
8888     m_aPages.back().appendPoint( aPoints[2], aLine );
8889     aLine.append( ' ' );
8890     m_aPages.back().appendPoint( aPoints[3], aLine );
8891     aLine.append( ' ' );
8892     m_aPages.back().appendPoint( aPoints[4], aLine );
8893     aLine.append( " c\n" );
8894     m_aPages.back().appendPoint( aPoints[5], aLine );
8895     aLine.append( ' ' );
8896     m_aPages.back().appendPoint( aPoints[6], aLine );
8897     aLine.append( ' ' );
8898     m_aPages.back().appendPoint( aPoints[7], aLine );
8899     aLine.append( " c\n" );
8900     m_aPages.back().appendPoint( aPoints[8], aLine );
8901     aLine.append( ' ' );
8902     m_aPages.back().appendPoint( aPoints[9], aLine );
8903     aLine.append( ' ' );
8904     m_aPages.back().appendPoint( aPoints[10], aLine );
8905     aLine.append( " c\n" );
8906     m_aPages.back().appendPoint( aPoints[11], aLine );
8907     aLine.append( ' ' );
8908     m_aPages.back().appendPoint( aPoints[0], aLine );
8909     aLine.append( ' ' );
8910     m_aPages.back().appendPoint( aPoints[1], aLine );
8911     aLine.append( " c " );
8912 
8913     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8914         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8915         aLine.append( "b*\n" );
8916     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8917         aLine.append( "s\n" );
8918     else
8919         aLine.append( "f*\n" );
8920 
8921     writeBuffer( aLine.getStr(), aLine.getLength() );
8922 }
8923 
8924 static double calcAngle( const Rectangle& rRect, const Point& rPoint )
8925 {
8926     Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
8927                   (rRect.Top()+rRect.Bottom()+1)/2);
8928     Point aPoint = rPoint - aOrigin;
8929 
8930     double fX = (double)aPoint.X();
8931     double fY = (double)-aPoint.Y();
8932 
8933     if( rRect.GetWidth() > rRect.GetHeight() )
8934         fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
8935     else if( rRect.GetHeight() > rRect.GetWidth() )
8936         fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
8937     return atan2( fY, fX );
8938 }
8939 
8940 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
8941 {
8942     MARK( "drawArc" );
8943 
8944     updateGraphicsState();
8945 
8946     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8947         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8948         return;
8949 
8950     // calculate start and stop angles
8951     const double fStartAngle = calcAngle( rRect, rStart );
8952     double fStopAngle  = calcAngle( rRect, rStop );
8953     while( fStopAngle < fStartAngle )
8954         fStopAngle += 2.0*M_PI;
8955     const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
8956     const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
8957     const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
8958     const double halfWidth = (double)rRect.GetWidth()/2.0;
8959     const double halfHeight = (double)rRect.GetHeight()/2.0;
8960 
8961     const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
8962                          (rRect.Top()+rRect.Bottom()+1)/2 );
8963 
8964     OStringBuffer aLine( 30*nFragments );
8965     Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
8966                   -(int)(halfHeight * sin(fStartAngle) ) );
8967     aPoint += aCenter;
8968     m_aPages.back().appendPoint( aPoint, aLine );
8969     aLine.append( " m " );
8970     if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
8971     {
8972         for( int i = 0; i < nFragments; i++ )
8973         {
8974             const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
8975             const double fStopFragment = fStartFragment + fFragmentDelta;
8976             aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
8977                             -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
8978             aPoint += aCenter;
8979             m_aPages.back().appendPoint( aPoint, aLine );
8980             aLine.append( ' ' );
8981 
8982             aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
8983                             -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
8984             aPoint += aCenter;
8985             m_aPages.back().appendPoint( aPoint, aLine );
8986             aLine.append( ' ' );
8987 
8988             aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
8989                             -(int)(halfHeight * sin(fStopFragment) ) );
8990             aPoint += aCenter;
8991             m_aPages.back().appendPoint( aPoint, aLine );
8992             aLine.append( " c\n" );
8993         }
8994     }
8995     if( bWithChord || bWithPie )
8996     {
8997         if( bWithPie )
8998         {
8999             m_aPages.back().appendPoint( aCenter, aLine );
9000             aLine.append( " l " );
9001         }
9002         aLine.append( "h " );
9003     }
9004     if( ! bWithChord && ! bWithPie )
9005         aLine.append( "S\n" );
9006     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
9007         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
9008         aLine.append( "B*\n" );
9009     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9010         aLine.append( "S\n" );
9011     else
9012         aLine.append( "f*\n" );
9013 
9014     writeBuffer( aLine.getStr(), aLine.getLength() );
9015 }
9016 
9017 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly )
9018 {
9019     MARK( "drawPolyLine" );
9020 
9021     sal_uInt16 nPoints = rPoly.GetSize();
9022     if( nPoints < 2 )
9023         return;
9024 
9025     updateGraphicsState();
9026 
9027     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9028         return;
9029 
9030     OStringBuffer aLine( 20 * nPoints );
9031     m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
9032     aLine.append( "S\n" );
9033 
9034     writeBuffer( aLine.getStr(), aLine.getLength() );
9035 }
9036 
9037 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo )
9038 {
9039     MARK( "drawPolyLine with LineInfo" );
9040 
9041     updateGraphicsState();
9042 
9043     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9044         return;
9045 
9046     OStringBuffer aLine;
9047     aLine.append( "q " );
9048     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
9049     {
9050         writeBuffer( aLine.getStr(), aLine.getLength() );
9051         drawPolyLine( rPoly );
9052         writeBuffer( "Q\n", 2 );
9053     }
9054     else
9055     {
9056         PDFWriter::ExtLineInfo aInfo;
9057         convertLineInfoToExtLineInfo( rInfo, aInfo );
9058         drawPolyLine( rPoly, aInfo );
9059     }
9060 }
9061 
9062 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
9063 {
9064     DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" );
9065     rOut.m_fLineWidth           = rIn.GetWidth();
9066     rOut.m_fTransparency        = 0.0;
9067     rOut.m_eCap                 = PDFWriter::capButt;
9068     rOut.m_eJoin                = PDFWriter::joinMiter;
9069     rOut.m_fMiterLimit          = 10;
9070     rOut.m_aDashArray.clear();
9071 
9072     // add DashDot to DashArray
9073     const int nDashes     = rIn.GetDashCount();
9074     const int nDashLen    = rIn.GetDashLen();
9075     const int nDistance   = rIn.GetDistance();
9076 
9077     for( int n  = 0; n < nDashes; n++ )
9078     {
9079         rOut.m_aDashArray.push_back( nDashLen );
9080         rOut.m_aDashArray.push_back( nDistance );
9081     }
9082 
9083     const int nDots       = rIn.GetDotCount();
9084     const int nDotLen     = rIn.GetDotLen();
9085 
9086     for( int n  = 0; n < nDots; n++ )
9087     {
9088         rOut.m_aDashArray.push_back( nDotLen );
9089         rOut.m_aDashArray.push_back( nDistance );
9090     }
9091 
9092     // add LineJoin
9093     switch(rIn.GetLineJoin())
9094     {
9095         case basegfx::B2DLINEJOIN_BEVEL :
9096         {
9097             rOut.m_eJoin = PDFWriter::joinBevel;
9098             break;
9099         }
9100         default : // basegfx::B2DLINEJOIN_NONE :
9101         // Pdf has no 'none' lineJoin, default is miter
9102         case basegfx::B2DLINEJOIN_MIDDLE :
9103         case basegfx::B2DLINEJOIN_MITER :
9104         {
9105             rOut.m_eJoin = PDFWriter::joinMiter;
9106             break;
9107         }
9108         case basegfx::B2DLINEJOIN_ROUND :
9109         {
9110             rOut.m_eJoin = PDFWriter::joinRound;
9111             break;
9112         }
9113     }
9114 
9115     // add LineCap
9116     switch(rIn.GetLineCap())
9117     {
9118         default: /* com::sun::star::drawing::LineCap_BUTT */
9119         {
9120             rOut.m_eCap = PDFWriter::capButt;
9121             break;
9122         }
9123         case com::sun::star::drawing::LineCap_ROUND:
9124         {
9125             rOut.m_eCap = PDFWriter::capRound;
9126             break;
9127         }
9128         case com::sun::star::drawing::LineCap_SQUARE:
9129         {
9130             rOut.m_eCap = PDFWriter::capSquare;
9131             break;
9132         }
9133     }
9134 }
9135 
9136 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
9137 {
9138     MARK( "drawPolyLine with ExtLineInfo" );
9139 
9140     updateGraphicsState();
9141 
9142     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9143         return;
9144 
9145     if( rInfo.m_fTransparency >= 1.0 )
9146         return;
9147 
9148     if( rInfo.m_fTransparency != 0.0 )
9149         beginTransparencyGroup();
9150 
9151     OStringBuffer aLine;
9152     aLine.append( "q " );
9153     m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
9154     aLine.append( " w" );
9155     if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader
9156     {
9157         switch( rInfo.m_eCap )
9158         {
9159             default:
9160             case PDFWriter::capButt:   aLine.append( " 0 J" );break;
9161             case PDFWriter::capRound:  aLine.append( " 1 J" );break;
9162             case PDFWriter::capSquare: aLine.append( " 2 J" );break;
9163         }
9164         switch( rInfo.m_eJoin )
9165         {
9166             default:
9167             case PDFWriter::joinMiter:
9168             {
9169                 double fLimit = rInfo.m_fMiterLimit;
9170                 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
9171                     fLimit = fLimit / rInfo.m_fLineWidth;
9172                 if( fLimit < 1.0 )
9173                     fLimit = 1.0;
9174                 aLine.append( " 0 j " );
9175                 appendDouble( fLimit, aLine );
9176                 aLine.append( " M" );
9177             }
9178             break;
9179             case PDFWriter::joinRound:  aLine.append( " 1 j" );break;
9180             case PDFWriter::joinBevel:  aLine.append( " 2 j" );break;
9181         }
9182         if( rInfo.m_aDashArray.size() > 0 )
9183         {
9184             aLine.append( " [ " );
9185             for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
9186                  it != rInfo.m_aDashArray.end(); ++it )
9187             {
9188                 m_aPages.back().appendMappedLength( *it, aLine );
9189                 aLine.append( ' ' );
9190             }
9191             aLine.append( "] 0 d" );
9192         }
9193         aLine.append( "\n" );
9194         writeBuffer( aLine.getStr(), aLine.getLength() );
9195         drawPolyLine( rPoly );
9196     }
9197     else
9198     {
9199         basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
9200         basegfx::B2DPolyPolygon aPolyPoly;
9201 
9202 		basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
9203 
9204 		// Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
9205 		// To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
9206 		// this line needs to be removed and the loop below adapted accordingly
9207 		aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
9208 
9209 		const sal_uInt32 nPolygonCount(aPolyPoly.count());
9210 
9211 		for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
9212         {
9213             aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
9214             aPoly = aPolyPoly.getB2DPolygon( nPoly );
9215 			const sal_uInt32 nPointCount(aPoly.count());
9216 
9217 			if(nPointCount)
9218 			{
9219 				const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
9220 				basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
9221 
9222 				for(sal_uInt32 a(0); a < nEdgeCount; a++)
9223 				{
9224                     if( a > 0 )
9225                         aLine.append( " " );
9226 					const sal_uInt32 nNextIndex((a + 1) % nPointCount);
9227 					const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
9228 
9229 					m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
9230 														FRound(aCurrent.getY()) ),
9231 												 aLine );
9232 					aLine.append( " m " );
9233 					m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
9234 														FRound(aNext.getY()) ),
9235 												 aLine );
9236 					aLine.append( " l" );
9237 
9238 					// prepare next edge
9239 					aCurrent = aNext;
9240 				}
9241 			}
9242         }
9243         aLine.append( " S " );
9244         writeBuffer( aLine.getStr(), aLine.getLength() );
9245     }
9246     writeBuffer( "Q\n", 2 );
9247 
9248     if( rInfo.m_fTransparency != 0.0 )
9249     {
9250         // FIXME: actually this may be incorrect with bezier polygons
9251         Rectangle aBoundRect( rPoly.GetBoundRect() );
9252         // avoid clipping with thick lines
9253         if( rInfo.m_fLineWidth > 0.0 )
9254         {
9255             sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
9256             aBoundRect.Top()    -= nLW;
9257             aBoundRect.Left()   -= nLW;
9258             aBoundRect.Right()  += nLW;
9259             aBoundRect.Bottom() += nLW;
9260         }
9261         endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) );
9262     }
9263 }
9264 
9265 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
9266 {
9267     MARK( "drawPixel" );
9268 
9269     Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
9270 
9271     if( aColor == Color( COL_TRANSPARENT ) )
9272         return;
9273 
9274     // pixels are drawn in line color, so have to set
9275     // the nonstroking color to line color
9276     Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
9277     setFillColor( aColor );
9278 
9279     updateGraphicsState();
9280 
9281     OStringBuffer aLine( 20 );
9282     m_aPages.back().appendPoint( rPoint, aLine );
9283     aLine.append( ' ' );
9284     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine );
9285     aLine.append( ' ' );
9286     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine );
9287     aLine.append( " re f\n" );
9288     writeBuffer( aLine.getStr(), aLine.getLength() );
9289 
9290     setFillColor( aOldFillColor );
9291 }
9292 
9293 void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors )
9294 {
9295     MARK( "drawPixel with Polygon" );
9296 
9297     updateGraphicsState();
9298 
9299     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors )
9300         return;
9301 
9302     sal_uInt16 nPoints = rPoints.GetSize();
9303     OStringBuffer aLine( nPoints*40 );
9304     aLine.append( "q " );
9305     if( ! pColors )
9306     {
9307         appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine );
9308         aLine.append( ' ' );
9309     }
9310 
9311     OStringBuffer aPixel(32);
9312     aPixel.append( ' ' );
9313     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel );
9314     aPixel.append( ' ' );
9315     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel );
9316     OString aPixelStr = aPixel.makeStringAndClear();
9317     for( sal_uInt16 i = 0; i < nPoints; i++ )
9318     {
9319         if( pColors )
9320         {
9321             if( pColors[i] == Color( COL_TRANSPARENT ) )
9322                 continue;
9323 
9324             appendNonStrokingColor( pColors[i], aLine );
9325             aLine.append( ' ' );
9326         }
9327         m_aPages.back().appendPoint( rPoints[i], aLine );
9328         aLine.append( aPixelStr );
9329         aLine.append( " re f\n" );
9330     }
9331     aLine.append( "Q\n" );
9332     writeBuffer( aLine.getStr(), aLine.getLength() );
9333 }
9334 
9335 class AccessReleaser
9336 {
9337     BitmapReadAccess* m_pAccess;
9338 public:
9339     AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){}
9340     ~AccessReleaser() { delete m_pAccess; }
9341 };
9342 
9343 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
9344 {
9345     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9346 
9347     bool bFlateFilter = compressStream( rObject.m_pContentStream );
9348     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
9349     sal_uLong nSize = rObject.m_pContentStream->Tell();
9350     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
9351     #if OSL_DEBUG_LEVEL > 1
9352     emitComment( "PDFWriterImpl::writeTransparentObject" );
9353     #endif
9354     OStringBuffer aLine( 512 );
9355     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9356     aLine.append( rObject.m_nObject );
9357     aLine.append( " 0 obj\n"
9358                   "<</Type/XObject\n"
9359                   "/Subtype/Form\n"
9360                   "/BBox[ " );
9361     appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
9362     aLine.append( ' ' );
9363     appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
9364     aLine.append( ' ' );
9365     appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
9366     aLine.append( ' ' );
9367     appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
9368     aLine.append( " ]\n" );
9369     if( ! rObject.m_pSoftMaskStream )
9370     {
9371         if( ! m_bIsPDF_A1 )
9372         {
9373             aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
9374         }
9375     }
9376     /* #i42884# the PDF reference recommends that each Form XObject
9377     *  should have a resource dict; alas if that is the same object
9378     *  as the one of the page it triggers an endless recursion in
9379     *  acroread 5 (6 and up have that fixed). Since we have only one
9380     *  resource dict anyway, let's use the one from the page by NOT
9381     *  emitting a Resources entry.
9382     */
9383     #if 0
9384     aLine.append( "   /Resources " );
9385     aLine.append( getResourceDictObj() );
9386     aLine.append( " 0 R\n" );
9387     #endif
9388 
9389     aLine.append( "/Length " );
9390     aLine.append( (sal_Int32)(nSize) );
9391     aLine.append( "\n" );
9392     if( bFlateFilter )
9393         aLine.append( "/Filter/FlateDecode\n" );
9394     aLine.append( ">>\n"
9395                   "stream\n" );
9396     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9397     checkAndEnableStreamEncryption( rObject.m_nObject );
9398     CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
9399     disableStreamEncryption();
9400     aLine.setLength( 0 );
9401     aLine.append( "\n"
9402                   "endstream\n"
9403                   "endobj\n\n" );
9404     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9405 
9406     // write ExtGState dict for this XObject
9407     aLine.setLength( 0 );
9408     aLine.append( rObject.m_nExtGStateObject );
9409     aLine.append( " 0 obj\n"
9410                   "<<" );
9411     if( ! rObject.m_pSoftMaskStream )
9412     {
9413 //i59651
9414         if( m_bIsPDF_A1 )
9415         {
9416             aLine.append( "/CA 1.0/ca 1.0" );
9417             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9418         }
9419         else
9420         {
9421             aLine.append(  "/CA " );
9422             appendDouble( rObject.m_fAlpha, aLine );
9423             aLine.append( "\n"
9424                           "   /ca " );
9425             appendDouble( rObject.m_fAlpha, aLine );
9426         }
9427         aLine.append( "\n" );
9428     }
9429     else
9430     {
9431         if( m_bIsPDF_A1 )
9432         {
9433             aLine.append( "/SMask/None" );
9434             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9435         }
9436         else
9437         {
9438             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
9439             sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
9440             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
9441             sal_Int32 nMaskObject = createObject();
9442             aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
9443             aLine.append( nMaskObject );
9444             aLine.append( " 0 R>>\n" );
9445 
9446             OStringBuffer aMask;
9447             aMask.append( nMaskObject );
9448             aMask.append( " 0 obj\n"
9449                           "<</Type/XObject\n"
9450                           "/Subtype/Form\n"
9451                           "/BBox[" );
9452             appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
9453             aMask.append( ' ' );
9454             appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
9455             aMask.append( ' ' );
9456             appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
9457             aMask.append( ' ' );
9458             appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
9459             aMask.append( "]\n" );
9460 
9461             /* #i42884# see above */
9462 #if 0
9463             aLine.append( "/Resources " );
9464             aMask.append( getResourceDictObj() );
9465             aMask.append( " 0 R\n" );
9466 #endif
9467 
9468             aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
9469             aMask.append( "/Length " );
9470             aMask.append( nMaskSize );
9471             aMask.append( ">>\n"
9472                           "stream\n" );
9473             CHECK_RETURN( updateObject( nMaskObject ) );
9474             checkAndEnableStreamEncryption(  nMaskObject );
9475             CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9476             CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
9477             disableStreamEncryption();
9478             aMask.setLength( 0 );
9479             aMask.append( "\nendstream\n"
9480                           "endobj\n\n" );
9481             CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9482         }
9483     }
9484     aLine.append( ">>\n"
9485                   "endobj\n\n" );
9486     CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) );
9487     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9488 
9489     return true;
9490 }
9491 
9492 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
9493 {
9494     sal_Int32 nFunctionObject = createObject();
9495     CHECK_RETURN( updateObject( nFunctionObject ) );
9496 
9497     VirtualDevice aDev;
9498     aDev.SetOutputSizePixel( rObject.m_aSize );
9499     aDev.SetMapMode( MapMode( MAP_PIXEL ) );
9500     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9501         aDev.SetDrawMode( aDev.GetDrawMode() |
9502                           ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT |
9503                             DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) );
9504     aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
9505 
9506     Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize );
9507     BitmapReadAccess* pAccess = aSample.AcquireReadAccess();
9508     AccessReleaser aReleaser( pAccess );
9509 
9510     Size aSize = aSample.GetSizePixel();
9511 
9512     sal_Int32 nStreamLengthObject = createObject();
9513     #if OSL_DEBUG_LEVEL > 1
9514     emitComment( "PDFWriterImpl::writeGradientFunction" );
9515     #endif
9516     OStringBuffer aLine( 120 );
9517     aLine.append( nFunctionObject );
9518     aLine.append( " 0 obj\n"
9519                   "<</FunctionType 0\n"
9520                   "/Domain[ 0 1 0 1 ]\n"
9521                   "/Size[ " );
9522     aLine.append( (sal_Int32)aSize.Width() );
9523     aLine.append( ' ' );
9524     aLine.append( (sal_Int32)aSize.Height() );
9525     aLine.append( " ]\n"
9526                   "/BitsPerSample 8\n"
9527                   "/Range[ 0 1 0 1 0 1 ]\n"
9528                   "/Order 3\n"
9529                   "/Length " );
9530     aLine.append( nStreamLengthObject );
9531     aLine.append( " 0 R\n"
9532 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9533                   "/Filter/FlateDecode"
9534 #endif
9535                   ">>\n"
9536                   "stream\n" );
9537     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9538 
9539     sal_uInt64 nStartStreamPos = 0;
9540     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) );
9541 
9542     checkAndEnableStreamEncryption( nFunctionObject );
9543     beginCompression();
9544     for( int y = aSize.Height()-1; y >= 0; y-- )
9545     {
9546         for( int x = 0; x < aSize.Width(); x++ )
9547         {
9548             sal_uInt8 aCol[3];
9549             BitmapColor aColor = pAccess->GetColor( y, x );
9550             aCol[0] = aColor.GetRed();
9551             aCol[1] = aColor.GetGreen();
9552             aCol[2] = aColor.GetBlue();
9553             CHECK_RETURN( writeBuffer( aCol, 3 ) );
9554         }
9555     }
9556     endCompression();
9557     disableStreamEncryption();
9558 
9559     sal_uInt64 nEndStreamPos = 0;
9560     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) );
9561 
9562     aLine.setLength( 0 );
9563     aLine.append( "\nendstream\nendobj\n\n" );
9564     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9565 
9566     // write stream length
9567     CHECK_RETURN( updateObject( nStreamLengthObject ) );
9568     aLine.setLength( 0 );
9569     aLine.append( nStreamLengthObject );
9570     aLine.append( " 0 obj\n" );
9571     aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
9572     aLine.append( "\nendobj\n\n" );
9573     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9574 
9575     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9576     aLine.setLength( 0 );
9577     aLine.append( rObject.m_nObject );
9578     aLine.append( " 0 obj\n"
9579                   "<</ShadingType 1\n"
9580                   "/ColorSpace/DeviceRGB\n"
9581                   "/AntiAlias true\n"
9582                   "/Domain[ 0 1 0 1 ]\n"
9583                   "/Matrix[ " );
9584     aLine.append( (sal_Int32)aSize.Width() );
9585     aLine.append( " 0 0 " );
9586     aLine.append( (sal_Int32)aSize.Height() );
9587     aLine.append( " 0 0 ]\n"
9588                   "/Function " );
9589     aLine.append( nFunctionObject );
9590     aLine.append( " 0 R\n"
9591                   ">>\n"
9592                   "endobj\n\n" );
9593     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9594 
9595     return true;
9596 }
9597 
9598 bool PDFWriterImpl::writeJPG( JPGEmit& rObject )
9599 {
9600     CHECK_RETURN( rObject.m_pStream );
9601     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9602 
9603     sal_Int32 nLength = 0;
9604     rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
9605     nLength = rObject.m_pStream->Tell();
9606     rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
9607 
9608     sal_Int32 nMaskObject = 0;
9609     if( !!rObject.m_aMask )
9610     {
9611         if( rObject.m_aMask.GetBitCount() == 1 ||
9612             ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651
9613             )
9614         {
9615             nMaskObject = createObject();
9616         }
9617         else if( m_bIsPDF_A1 )
9618             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9619         else if( m_aContext.Version < PDFWriter::PDF_1_4 )
9620             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
9621 
9622     }
9623     #if OSL_DEBUG_LEVEL > 1
9624     emitComment( "PDFWriterImpl::writeJPG" );
9625     #endif
9626 
9627     OStringBuffer aLine(200);
9628     aLine.append( rObject.m_nObject );
9629     aLine.append( " 0 obj\n"
9630                   "<</Type/XObject/Subtype/Image/Width " );
9631     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
9632     aLine.append( " /Height " );
9633     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
9634     aLine.append( " /BitsPerComponent 8 " );
9635     if( rObject.m_bTrueColor )
9636         aLine.append( "/ColorSpace/DeviceRGB" );
9637     else
9638         aLine.append( "/ColorSpace/DeviceGray" );
9639     aLine.append( "/Filter/DCTDecode/Length " );
9640     aLine.append( nLength );
9641     if( nMaskObject )
9642     {
9643         aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
9644         aLine.append( nMaskObject );
9645         aLine.append( " 0 R " );
9646     }
9647     aLine.append( ">>\nstream\n" );
9648     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9649 
9650     checkAndEnableStreamEncryption( rObject.m_nObject );
9651     CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
9652     disableStreamEncryption();
9653 
9654     aLine.setLength( 0 );
9655     aLine.append( "\nendstream\nendobj\n\n" );
9656     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9657 
9658     if( nMaskObject )
9659     {
9660         BitmapEmit aEmit;
9661         aEmit.m_nObject = nMaskObject;
9662         if( rObject.m_aMask.GetBitCount() == 1 )
9663             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
9664         else if( rObject.m_aMask.GetBitCount() == 8 )
9665             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
9666         writeBitmapObject( aEmit, true );
9667     }
9668 
9669     return true;
9670 }
9671 
9672 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
9673 {
9674     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9675 
9676     Bitmap	aBitmap;
9677     Color	aTransparentColor( COL_TRANSPARENT );
9678     bool	bWriteMask = false;
9679     if( ! bMask )
9680     {
9681         aBitmap = rObject.m_aBitmap.GetBitmap();
9682         if( rObject.m_aBitmap.IsAlpha() )
9683         {
9684             if( m_aContext.Version >= PDFWriter::PDF_1_4 )
9685                 bWriteMask = true;
9686             // else draw without alpha channel
9687         }
9688         else
9689         {
9690             switch( rObject.m_aBitmap.GetTransparentType() )
9691             {
9692                 case TRANSPARENT_NONE:
9693                     // comes from drawMask function
9694                     if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask )
9695                         bMask = true;
9696                     break;
9697                 case TRANSPARENT_COLOR:
9698                     aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
9699                     break;
9700                 case TRANSPARENT_BITMAP:
9701                     bWriteMask = true;
9702                     break;
9703             }
9704         }
9705     }
9706     else
9707     {
9708         if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
9709         {
9710             aBitmap = rObject.m_aBitmap.GetMask();
9711             aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
9712             DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
9713         }
9714         else if( aBitmap.GetBitCount() != 8 )
9715         {
9716             aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap();
9717             aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
9718             DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" );
9719         }
9720     }
9721 
9722     BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();
9723     AccessReleaser aReleaser( pAccess );
9724 
9725     bool bTrueColor;
9726     sal_Int32 nBitsPerComponent;
9727     switch( aBitmap.GetBitCount() )
9728     {
9729         case 1:
9730         case 2:
9731         case 4:
9732         case 8:
9733             bTrueColor = false;
9734             nBitsPerComponent = aBitmap.GetBitCount();
9735             break;
9736         default:
9737             bTrueColor = true;
9738             nBitsPerComponent = 8;
9739             break;
9740     }
9741 
9742     sal_Int32 nStreamLengthObject	= createObject();
9743     sal_Int32 nMaskObject			= 0;
9744 
9745     #if OSL_DEBUG_LEVEL > 1
9746     emitComment( "PDFWriterImpl::writeBitmapObject" );
9747     #endif
9748     OStringBuffer aLine(1024);
9749     aLine.append( rObject.m_nObject );
9750     aLine.append( " 0 obj\n"
9751                   "<</Type/XObject/Subtype/Image/Width " );
9752     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9753     aLine.append( "/Height " );
9754     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
9755     aLine.append( "/BitsPerComponent " );
9756     aLine.append( nBitsPerComponent );
9757     aLine.append( "/Length " );
9758     aLine.append( nStreamLengthObject );
9759     aLine.append( " 0 R\n" );
9760 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9761     if( nBitsPerComponent != 1 )
9762     {
9763         aLine.append( "/Filter/FlateDecode" );
9764     }
9765     else
9766     {
9767         aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
9768         aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9769         aLine.append( ">>\n" );
9770     }
9771 #endif
9772     if( ! bMask )
9773     {
9774         aLine.append( "/ColorSpace" );
9775         if( bTrueColor )
9776             aLine.append( "/DeviceRGB\n" );
9777         else if( aBitmap.HasGreyPalette() )
9778         {
9779             aLine.append( "/DeviceGray\n" );
9780             if( aBitmap.GetBitCount() == 1 )
9781             {
9782                 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
9783                 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9784                 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9785                 if( nBlackIndex == 1 )
9786                     aLine.append( "/Decode[1 0]\n" );
9787             }
9788         }
9789         else
9790         {
9791             aLine.append( "[ /Indexed/DeviceRGB " );
9792             aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
9793             aLine.append( "\n<" );
9794 			if( m_aContext.Encryption.Encrypt() )
9795 			{
9796 				enableStringEncryption( rObject.m_nObject );
9797 				//check encryption buffer size
9798 				if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
9799 				{
9800 					int	nChar = 0;
9801 					//fill the encryption buffer
9802 					for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9803 					{
9804 						const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9805 						m_pEncryptionBuffer[nChar++] = rColor.GetRed();
9806 						m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
9807 						m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
9808 					}
9809 					//encrypt the colorspace lookup table
9810 					rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
9811 					//now queue the data for output
9812                     nChar = 0;
9813 					for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9814 					{
9815 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9816 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9817 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9818 					}
9819 				}
9820 			}
9821 			else //no encryption requested (PDF/A-1a program flow drops here)
9822 			{
9823 				for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9824 				{
9825 					const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9826 					appendHex( rColor.GetRed(), aLine );
9827 					appendHex( rColor.GetGreen(), aLine );
9828 					appendHex( rColor.GetBlue(), aLine );
9829 				}
9830 			}
9831             aLine.append( ">\n]\n" );
9832         }
9833     }
9834     else
9835     {
9836         if( aBitmap.GetBitCount() == 1 )
9837         {
9838             aLine.append( "/ImageMask true\n" );
9839             sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9840             DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9841             if( nBlackIndex )
9842                 aLine.append( "/Decode[ 1 0 ]\n" );
9843             else
9844                 aLine.append( "/Decode[ 0 1 ]\n" );
9845         }
9846         else if( aBitmap.GetBitCount() == 8 )
9847         {
9848             aLine.append( "/ColorSpace/DeviceGray\n"
9849                           "/Decode [ 1 0 ]\n" );
9850         }
9851     }
9852 
9853     if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651
9854     {
9855         if( bWriteMask )
9856         {
9857             nMaskObject = createObject();
9858             if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 )
9859                 aLine.append( "/SMask " );
9860             else
9861                 aLine.append( "/Mask " );
9862             aLine.append( nMaskObject );
9863             aLine.append( " 0 R\n" );
9864         }
9865         else if( aTransparentColor != Color( COL_TRANSPARENT ) )
9866         {
9867             aLine.append( "/Mask[ " );
9868             if( bTrueColor )
9869             {
9870                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9871                 aLine.append( ' ' );
9872                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9873                 aLine.append( ' ' );
9874                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9875                 aLine.append( ' ' );
9876                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9877                 aLine.append( ' ' );
9878                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9879                 aLine.append( ' ' );
9880                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9881             }
9882             else
9883             {
9884                 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
9885                 aLine.append( nIndex );
9886             }
9887             aLine.append( " ]\n" );
9888         }
9889     }
9890     else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) )
9891         m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9892 
9893     aLine.append( ">>\n"
9894                   "stream\n" );
9895     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9896     sal_uInt64 nStartPos = 0;
9897     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) );
9898 
9899     checkAndEnableStreamEncryption( rObject.m_nObject );
9900 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9901     if( nBitsPerComponent == 1 )
9902     {
9903         writeG4Stream( pAccess );
9904     }
9905     else
9906 #endif
9907     {
9908         beginCompression();
9909         if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
9910         {
9911             const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U );
9912 
9913             for( int i = 0; i < pAccess->Height(); i++ )
9914             {
9915                 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
9916             }
9917         }
9918         else
9919         {
9920             const int nScanLineBytes = pAccess->Width()*3;
9921             boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] );
9922             for( int y = 0; y < pAccess->Height(); y++ )
9923             {
9924                 for( int x = 0; x < pAccess->Width(); x++ )
9925                 {
9926                     BitmapColor aColor = pAccess->GetColor( y, x );
9927                     pCol[3*x+0] = aColor.GetRed();
9928                     pCol[3*x+1] = aColor.GetGreen();
9929                     pCol[3*x+2] = aColor.GetBlue();
9930                 }
9931                 CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) );
9932             }
9933         }
9934         endCompression();
9935     }
9936     disableStreamEncryption();
9937 
9938     sal_uInt64 nEndPos = 0;
9939     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) );
9940     aLine.setLength( 0 );
9941     aLine.append( "\nendstream\nendobj\n\n" );
9942     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9943     CHECK_RETURN( updateObject( nStreamLengthObject ) );
9944     aLine.setLength( 0 );
9945     aLine.append( nStreamLengthObject );
9946     aLine.append( " 0 obj\n" );
9947     aLine.append( (sal_Int64)(nEndPos-nStartPos) );
9948     aLine.append( "\nendobj\n\n" );
9949     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9950 
9951     if( nMaskObject )
9952     {
9953         BitmapEmit aEmit;
9954         aEmit.m_nObject				= nMaskObject;
9955         aEmit.m_aBitmap				= rObject.m_aBitmap;
9956         return writeBitmapObject( aEmit, true );
9957     }
9958 
9959     return true;
9960 }
9961 
9962 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask )
9963 {
9964     MARK( "drawJPGBitmap" );
9965 
9966     OStringBuffer aLine( 80 );
9967     updateGraphicsState();
9968 
9969     // #i40055# sanity check
9970     if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
9971         return;
9972     if( ! (rSizePixel.Width() && rSizePixel.Height()) )
9973         return;
9974 
9975     rDCTData.Seek( 0 );
9976     if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9977     {
9978         // need to convert to grayscale;
9979         // load stream to bitmap and draw the bitmap instead
9980         Graphic aGraphic;
9981         GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG );
9982         Bitmap aBmp( aGraphic.GetBitmap() );
9983         if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() )
9984         {
9985             BitmapEx aBmpEx( aBmp, rMask );
9986             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx );
9987         }
9988         else
9989             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp );
9990         return;
9991     }
9992 
9993     SvMemoryStream* pStream = new SvMemoryStream;
9994     *pStream << rDCTData;
9995     pStream->Seek( STREAM_SEEK_TO_END );
9996 
9997     BitmapID aID;
9998     aID.m_aPixelSize	= rSizePixel;
9999     aID.m_nSize			= pStream->Tell();
10000     pStream->Seek( STREAM_SEEK_TO_BEGIN );
10001     aID.m_nChecksum		= rtl_crc32( 0, pStream->GetData(), aID.m_nSize );
10002     if( ! rMask.IsEmpty() )
10003         aID.m_nMaskChecksum	= rMask.GetChecksum();
10004 
10005     std::list< JPGEmit >::const_iterator it;
10006     for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
10007         ;
10008     if( it == m_aJPGs.end() )
10009     {
10010         m_aJPGs.push_front( JPGEmit() );
10011         JPGEmit& rEmit = m_aJPGs.front();
10012         rEmit.m_nObject		= createObject();
10013         rEmit.m_aID			= aID;
10014         rEmit.m_pStream		= pStream;
10015         rEmit.m_bTrueColor  = bIsTrueColor;
10016         if( !! rMask && rMask.GetSizePixel() == rSizePixel )
10017             rEmit.m_aMask	= rMask;
10018 
10019         it = m_aJPGs.begin();
10020     }
10021     else
10022         delete pStream;
10023 
10024     aLine.append( "q " );
10025     sal_Int32 nCheckWidth = 0;
10026     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth );
10027     aLine.append( " 0 0 " );
10028     sal_Int32 nCheckHeight = 0;
10029     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight );
10030     aLine.append( ' ' );
10031     m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
10032     aLine.append( " cm\n/Im" );
10033     aLine.append( it->m_nObject );
10034     aLine.append( " Do Q\n" );
10035     if( nCheckWidth == 0 || nCheckHeight == 0 )
10036     {
10037         // #i97512# avoid invalid current matrix
10038         aLine.setLength( 0 );
10039         aLine.append( "\n%jpeg image /Im" );
10040         aLine.append( it->m_nObject );
10041         aLine.append( " scaled to zero size, omitted\n" );
10042     }
10043     writeBuffer( aLine.getStr(), aLine.getLength() );
10044 
10045     OStringBuffer aObjName( 16 );
10046     aObjName.append( "Im" );
10047     aObjName.append( it->m_nObject );
10048     pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10049 
10050 }
10051 
10052 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
10053 {
10054     OStringBuffer aLine( 80 );
10055     updateGraphicsState();
10056 
10057     aLine.append( "q " );
10058     if( rFillColor != Color( COL_TRANSPARENT ) )
10059     {
10060         appendNonStrokingColor( rFillColor, aLine );
10061         aLine.append( ' ' );
10062     }
10063     sal_Int32 nCheckWidth = 0;
10064     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth );
10065     aLine.append( " 0 0 " );
10066     sal_Int32 nCheckHeight = 0;
10067     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight );
10068     aLine.append( ' ' );
10069     m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
10070     aLine.append( " cm\n/Im" );
10071     aLine.append( rBitmap.m_nObject );
10072     aLine.append( " Do Q\n" );
10073     if( nCheckWidth == 0 || nCheckHeight == 0 )
10074     {
10075         // #i97512# avoid invalid current matrix
10076         aLine.setLength( 0 );
10077         aLine.append( "\n%bitmap image /Im" );
10078         aLine.append( rBitmap.m_nObject );
10079         aLine.append( " scaled to zero size, omitted\n" );
10080     }
10081     writeBuffer( aLine.getStr(), aLine.getLength() );
10082 }
10083 
10084 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask )
10085 {
10086     BitmapEx aBitmap( i_rBitmap );
10087     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
10088     {
10089         BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS;
10090         int nDepth = aBitmap.GetBitmap().GetBitCount();
10091         if( nDepth <= 4 )
10092             eConv = BMP_CONVERSION_4BIT_GREYS;
10093         if( nDepth > 1 )
10094             aBitmap.Convert( eConv );
10095     }
10096     BitmapID aID;
10097     aID.m_aPixelSize		= aBitmap.GetSizePixel();
10098     aID.m_nSize				= aBitmap.GetBitCount();
10099     aID.m_nChecksum			= aBitmap.GetBitmap().GetChecksum();
10100     aID.m_nMaskChecksum		= 0;
10101     if( aBitmap.IsAlpha() )
10102         aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum();
10103     else
10104     {
10105         Bitmap aMask = aBitmap.GetMask();
10106         if( ! aMask.IsEmpty() )
10107             aID.m_nMaskChecksum = aMask.GetChecksum();
10108     }
10109     std::list< BitmapEmit >::const_iterator it;
10110     for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
10111     {
10112         if( aID == it->m_aID )
10113             break;
10114     }
10115     if( it == m_aBitmaps.end() )
10116     {
10117         m_aBitmaps.push_front( BitmapEmit() );
10118         m_aBitmaps.front().m_aID		= aID;
10119         m_aBitmaps.front().m_aBitmap	= aBitmap;
10120         m_aBitmaps.front().m_nObject	= createObject();
10121         m_aBitmaps.front().m_bDrawMask	= bDrawMask;
10122         it = m_aBitmaps.begin();
10123     }
10124 
10125     OStringBuffer aObjName( 16 );
10126     aObjName.append( "Im" );
10127     aObjName.append( it->m_nObject );
10128     pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10129 
10130     return *it;
10131 }
10132 
10133 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap )
10134 {
10135     MARK( "drawBitmap (Bitmap)" );
10136 
10137     // #i40055# sanity check
10138     if( ! (rDestSize.Width() && rDestSize.Height()) )
10139         return;
10140 
10141     const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) );
10142     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10143 }
10144 
10145 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
10146 {
10147     MARK( "drawBitmap (BitmapEx)" );
10148 
10149     // #i40055# sanity check
10150     if( ! (rDestSize.Width() && rDestSize.Height()) )
10151         return;
10152 
10153     const BitmapEmit& rEmit = createBitmapEmit( rBitmap );
10154     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10155 }
10156 
10157 void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor )
10158 {
10159     MARK( "drawMask" );
10160 
10161     // #i40055# sanity check
10162     if( ! (rDestSize.Width() && rDestSize.Height()) )
10163         return;
10164 
10165     Bitmap aBitmap( rBitmap );
10166     if( aBitmap.GetBitCount() > 1 )
10167         aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
10168     DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
10169 
10170     const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true );
10171     drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor );
10172 }
10173 
10174 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
10175 {
10176     Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10177                                MapMode( MAP_POINT ),
10178                                getReferenceDevice(),
10179                                rSize ) );
10180     // check if we already have this gradient
10181     std::list<GradientEmit>::iterator it;
10182     // rounding to point will generally lose some pixels
10183     // round up to point boundary
10184     aPtSize.Width()++;
10185     aPtSize.Height()++;
10186     for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
10187     {
10188         if( it->m_aGradient == rGradient )
10189         {
10190             if( it->m_aSize == aPtSize )
10191                 break;
10192         }
10193     }
10194     if( it == m_aGradients.end() )
10195     {
10196         m_aGradients.push_front( GradientEmit() );
10197         m_aGradients.front().m_aGradient	= rGradient;
10198         m_aGradients.front().m_nObject	    = createObject();
10199         m_aGradients.front().m_aSize		= aPtSize;
10200         it = m_aGradients.begin();
10201     }
10202 
10203     OStringBuffer aObjName( 16 );
10204     aObjName.append( 'P' );
10205     aObjName.append( it->m_nObject );
10206     pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
10207 
10208     return it->m_nObject;
10209 }
10210 
10211 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient )
10212 {
10213     MARK( "drawGradient (Rectangle)" );
10214 
10215     if( m_aContext.Version == PDFWriter::PDF_1_2 )
10216     {
10217         drawRectangle( rRect );
10218         return;
10219     }
10220 
10221     sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
10222 
10223     Point aTranslate( rRect.BottomLeft() );
10224     aTranslate += Point( 0, 1 );
10225 
10226     updateGraphicsState();
10227 
10228     OStringBuffer aLine( 80 );
10229     aLine.append( "q 1 0 0 1 " );
10230     m_aPages.back().appendPoint( aTranslate, aLine );
10231     aLine.append( " cm " );
10232     // if a stroke is appended reset the clip region before stroke
10233     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10234         aLine.append( "q " );
10235     aLine.append( "0 0 " );
10236     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10237     aLine.append( ' ' );
10238     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10239     aLine.append( " re W n\n" );
10240 
10241     aLine.append( "/P" );
10242     aLine.append( nGradient );
10243     aLine.append( " sh " );
10244     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10245     {
10246         aLine.append( "Q 0 0 " );
10247         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10248         aLine.append( ' ' );
10249         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10250         aLine.append( " re S " );
10251     }
10252     aLine.append( "Q\n" );
10253     writeBuffer( aLine.getStr(), aLine.getLength() );
10254 }
10255 
10256 void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
10257 {
10258     MARK( "drawGradient (PolyPolygon)" );
10259 
10260     if( m_aContext.Version == PDFWriter::PDF_1_2 )
10261     {
10262         drawPolyPolygon( rPolyPoly );
10263         return;
10264     }
10265 
10266     Rectangle aBoundRect = rPolyPoly.GetBoundRect();
10267     sal_Int32 nGradient = createGradient( rGradient, aBoundRect.GetSize() );
10268 
10269     updateGraphicsState();
10270 
10271     Point aTranslate = aBoundRect.BottomLeft();
10272     int nPolygons = rPolyPoly.Count();
10273 
10274     OStringBuffer aLine( 80*nPolygons );
10275     aLine.append( "q " );
10276     // set PolyPolygon as clip path
10277     m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
10278     aLine.append( "W* n\n" );
10279     aLine.append( "1 0 0 1 " );
10280     m_aPages.back().appendPoint( aTranslate, aLine );
10281     aLine.append( " cm\n" );
10282     aLine.append( "/P" );
10283     aLine.append( nGradient );
10284     aLine.append( " sh Q\n" );
10285     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10286     {
10287         // and draw the surrounding path
10288         m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
10289         aLine.append( "S\n" );
10290     }
10291     writeBuffer( aLine.getStr(), aLine.getLength() );
10292 }
10293 
10294 void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
10295 {
10296     MARK( "drawHatch" );
10297 
10298     updateGraphicsState();
10299 
10300 	if( rPolyPoly.Count() )
10301 	{
10302 		PolyPolygon		aPolyPoly( rPolyPoly );
10303 
10304 		aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
10305 		push( PUSH_LINECOLOR );
10306 		setLineColor( rHatch.GetColor() );
10307 		getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, sal_False );
10308 		pop();
10309 	}
10310 }
10311 
10312 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall )
10313 {
10314     MARK( "drawWallpaper" );
10315 
10316     bool bDrawColor			= false;
10317     bool bDrawGradient		= false;
10318     bool bDrawBitmap		= false;
10319 
10320     BitmapEx aBitmap;
10321     Point aBmpPos = rRect.TopLeft();
10322     Size aBmpSize;
10323     if( rWall.IsBitmap() )
10324     {
10325         aBitmap = rWall.GetBitmap();
10326 		aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
10327                                 getMapMode(),
10328                                 getReferenceDevice(),
10329                                 aBitmap.GetPrefSize() );
10330         Rectangle aRect( rRect );
10331         if( rWall.IsRect() )
10332         {
10333             aRect = rWall.GetRect();
10334             aBmpPos = aRect.TopLeft();
10335             aBmpSize = aRect.GetSize();
10336         }
10337         if( rWall.GetStyle() != WALLPAPER_SCALE )
10338         {
10339             if( rWall.GetStyle() != WALLPAPER_TILE )
10340             {
10341                 bDrawBitmap		= true;
10342                 if( rWall.IsGradient() )
10343                     bDrawGradient = true;
10344                 else
10345                     bDrawColor = true;
10346                 switch( rWall.GetStyle() )
10347                 {
10348                     case WALLPAPER_TOPLEFT:
10349                         break;
10350                     case WALLPAPER_TOP:
10351                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10352                         break;
10353                     case WALLPAPER_LEFT:
10354                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10355                         break;
10356                     case WALLPAPER_TOPRIGHT:
10357                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10358                         break;
10359                     case WALLPAPER_CENTER:
10360                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10361                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10362                         break;
10363                     case WALLPAPER_RIGHT:
10364                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10365                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10366                         break;
10367                     case WALLPAPER_BOTTOMLEFT:
10368                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10369                         break;
10370                     case WALLPAPER_BOTTOM:
10371                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10372                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10373                         break;
10374                     case WALLPAPER_BOTTOMRIGHT:
10375                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10376                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10377                         break;
10378                     default: ;
10379                 }
10380             }
10381             else
10382             {
10383                 // push the bitmap
10384                 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) );
10385 
10386                 // convert to page coordinates; this needs to be done here
10387                 // since the emit does not know the page anymore
10388                 Rectangle aConvertRect( aBmpPos, aBmpSize );
10389                 m_aPages.back().convertRect( aConvertRect );
10390 
10391                 OStringBuffer aNameBuf(16);
10392                 aNameBuf.append( "Im" );
10393                 aNameBuf.append( rEmit.m_nObject );
10394                 OString aImageName( aNameBuf.makeStringAndClear() );
10395 
10396                 // push the pattern
10397                 OStringBuffer aTilingStream( 32 );
10398                 appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
10399                 aTilingStream.append( " 0 0 " );
10400                 appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
10401                 aTilingStream.append( " 0 0 cm\n/" );
10402                 aTilingStream.append( aImageName );
10403                 aTilingStream.append( " Do\n" );
10404 
10405                 m_aTilings.push_back( TilingEmit() );
10406                 m_aTilings.back().m_nObject			= createObject();
10407                 m_aTilings.back().m_aRectangle		= Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
10408                 m_aTilings.back().m_pTilingStream   = new SvMemoryStream();
10409                 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() );
10410                 // phase the tiling so wallpaper begins on upper left
10411                 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
10412                 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
10413                 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
10414 
10415                 updateGraphicsState();
10416 
10417                 OStringBuffer aObjName( 16 );
10418                 aObjName.append( 'P' );
10419                 aObjName.append( m_aTilings.back().m_nObject );
10420                 OString aPatternName( aObjName.makeStringAndClear() );
10421                 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
10422 
10423                 // fill a rRect with the pattern
10424                 OStringBuffer aLine( 100 );
10425                 aLine.append( "q /Pattern cs /" );
10426                 aLine.append( aPatternName );
10427                 aLine.append( " scn " );
10428                 m_aPages.back().appendRect( rRect, aLine );
10429                 aLine.append( " f Q\n" );
10430                 writeBuffer( aLine.getStr(), aLine.getLength() );
10431             }
10432         }
10433         else
10434         {
10435             aBmpPos		= aRect.TopLeft();
10436             aBmpSize	= aRect.GetSize();
10437             bDrawBitmap	= true;
10438         }
10439 
10440         if( aBitmap.IsTransparent() )
10441         {
10442             if( rWall.IsGradient() )
10443                 bDrawGradient = true;
10444             else
10445                 bDrawColor = true;
10446         }
10447     }
10448     else if( rWall.IsGradient() )
10449         bDrawGradient = true;
10450     else
10451         bDrawColor = true;
10452 
10453     if( bDrawGradient )
10454     {
10455         drawGradient( rRect, rWall.GetGradient() );
10456     }
10457     if( bDrawColor )
10458     {
10459         Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
10460         Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
10461         setLineColor( Color( COL_TRANSPARENT ) );
10462         setFillColor( rWall.GetColor() );
10463         drawRectangle( rRect );
10464         setLineColor( aOldLineColor );
10465         setFillColor( aOldFillColor );
10466     }
10467     if( bDrawBitmap )
10468     {
10469         // set temporary clip region since aBmpPos and aBmpSize
10470         // may be outside rRect
10471         OStringBuffer aLine( 20 );
10472         aLine.append( "q " );
10473         m_aPages.back().appendRect( rRect, aLine );
10474         aLine.append( " W n\n" );
10475         writeBuffer( aLine.getStr(), aLine.getLength() );
10476         drawBitmap( aBmpPos, aBmpSize, aBitmap );
10477         writeBuffer( "Q\n", 2 );
10478     }
10479 }
10480 
10481 void PDFWriterImpl::beginPattern( const Rectangle& rCellRect )
10482 {
10483     beginRedirect( new SvMemoryStream(), rCellRect );
10484 }
10485 
10486 sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform )
10487 {
10488     Rectangle aConvertRect( getRedirectTargetRect() );
10489     DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" );
10490 
10491     // get scaling between current mapmode and PDF output
10492     Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) );
10493     double fSX = (double(aScaling.Width()) / 10000.0);
10494     double fSY = (double(aScaling.Height()) / 10000.0);
10495 
10496     // transform translation part of matrix
10497     Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] );
10498     aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation );
10499 
10500     sal_Int32 nTilingId = m_aTilings.size();
10501     m_aTilings.push_back( TilingEmit() );
10502     TilingEmit& rTile = m_aTilings.back();
10503     rTile.m_nObject         = createObject();
10504     rTile.m_aResources      = m_aOutputStreams.front().m_aResourceDict;
10505     rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX;
10506     rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY;
10507     rTile.m_aTransform.matrix[2] = aTranslation.Width();
10508     rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX;
10509     rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY;
10510     rTile.m_aTransform.matrix[5] = -aTranslation.Height();
10511     // caution: endRedirect pops the stream, so do this last
10512     rTile.m_pTilingStream   = dynamic_cast<SvMemoryStream*>(endRedirect());
10513     // FIXME: bound rect will not work with rotated matrix
10514     rTile.m_aRectangle      = Rectangle( Point(0,0), aConvertRect.GetSize() );
10515     rTile.m_aCellSize       = aConvertRect.GetSize();
10516 
10517     OStringBuffer aObjName( 16 );
10518     aObjName.append( 'P' );
10519     aObjName.append( rTile.m_nObject );
10520     pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject );
10521     return nTilingId;
10522 }
10523 
10524 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill )
10525 {
10526     if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() )
10527         return;
10528 
10529     m_aPages.back().endStream();
10530     sal_Int32 nXObject = createObject();
10531     OStringBuffer aNameBuf( 16 );
10532     aNameBuf.append( "Pol" );
10533     aNameBuf.append( nXObject );
10534     OString aObjName( aNameBuf.makeStringAndClear() );
10535     Rectangle aObjRect;
10536     if( updateObject( nXObject ) )
10537     {
10538         // get bounding rect of object
10539         PolyPolygon aSubDiv;
10540         rPolyPoly.AdaptiveSubdivide( aSubDiv );
10541         aObjRect = aSubDiv.GetBoundRect();
10542         Rectangle aConvObjRect( aObjRect );
10543         m_aPages.back().convertRect( aConvObjRect );
10544 
10545         // move polypolygon to bottom left of page
10546         PolyPolygon aLocalPath( rPolyPoly );
10547         sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72;
10548         sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72;
10549         Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode );
10550         sal_Int32 nXOff = aObjRect.Left();
10551         sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom();
10552         aLocalPath.Move( -nXOff, nYOff );
10553 
10554         // prepare XObject's content stream
10555         OStringBuffer aStream( 512 );
10556         aStream.append( "/Pattern cs /P" );
10557         aStream.append( m_aTilings[ nPattern ].m_nObject );
10558         aStream.append( " scn\n" );
10559         m_aPages.back().appendPolyPolygon( aLocalPath, aStream );
10560         aStream.append( bEOFill ? "f*" : "f" );
10561         SvMemoryStream aMemStream( aStream.getLength() );
10562         aMemStream.Write( aStream.getStr(), aStream.getLength() );
10563         bool bDeflate = compressStream( &aMemStream );
10564         aMemStream.Seek( STREAM_SEEK_TO_END );
10565         sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell();
10566         aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
10567 
10568         // add new XObject to global resource dict
10569         m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject;
10570 
10571         // write XObject
10572         OStringBuffer aLine( 512 );
10573         aLine.append( nXObject );
10574         aLine.append( " 0 obj\n"
10575                       "<</Type/XObject/Subtype/Form/BBox[0 0 " );
10576         appendFixedInt( aConvObjRect.GetWidth(), aLine );
10577         aLine.append( ' ' );
10578         appendFixedInt( aConvObjRect.GetHeight(), aLine );
10579         aLine.append( "]/Length " );
10580         aLine.append( nStreamLen );
10581         if( bDeflate )
10582             aLine.append( "/Filter/FlateDecode" );
10583         aLine.append( ">>\n"
10584                       "stream\n" );
10585         writeBuffer( aLine.getStr(), aLine.getLength() );
10586         checkAndEnableStreamEncryption( nXObject );
10587         writeBuffer( aMemStream.GetData(), nStreamLen );
10588         disableStreamEncryption();
10589         writeBuffer( "\nendstream\nendobj\n\n", 19 );
10590     }
10591     m_aPages.back().beginStream();
10592     OStringBuffer aLine( 80 );
10593     aLine.append( "q 1 0 0 1 " );
10594     m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine );
10595     aLine.append( " cm/" );
10596     aLine.append( aObjName );
10597     aLine.append( " Do Q\n" );
10598     writeBuffer( aLine.getStr(), aLine.getLength() );
10599 }
10600 
10601 void PDFWriterImpl::updateGraphicsState()
10602 {
10603     OStringBuffer aLine( 256 );
10604     GraphicsState& rNewState = m_aGraphicsStack.front();
10605     // first set clip region since it might invalidate everything else
10606 
10607     if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) )
10608     {
10609         rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion;
10610 
10611         if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion ||
10612             ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) )
10613         {
10614             if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() )
10615             {
10616                 aLine.append( "Q " );
10617                 // invalidate everything but the clip region
10618                 m_aCurrentPDFState = GraphicsState();
10619                 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion);
10620             }
10621             if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() )
10622             {
10623                 // clip region is always stored in private PDF mapmode
10624                 MapMode aNewMapMode = rNewState.m_aMapMode;
10625                 rNewState.m_aMapMode = m_aMapMode;
10626                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10627                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10628 
10629                 aLine.append( "q " );
10630                 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine );
10631                 aLine.append( "W* n\n" );
10632                 rNewState.m_aMapMode = aNewMapMode;
10633                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10634                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10635             }
10636         }
10637     }
10638 
10639     if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) )
10640     {
10641         rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode;
10642         getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10643     }
10644 
10645     if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) )
10646     {
10647         rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont;
10648         getReferenceDevice()->SetFont( rNewState.m_aFont );
10649         getReferenceDevice()->ImplNewFont();
10650     }
10651 
10652     if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) )
10653     {
10654         rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode;
10655         getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
10656     }
10657 
10658     if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) )
10659     {
10660         rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage;
10661         getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
10662     }
10663 
10664     if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) )
10665     {
10666         rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor;
10667         if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
10668             rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
10669         {
10670             appendStrokingColor( rNewState.m_aLineColor, aLine );
10671             aLine.append( "\n" );
10672         }
10673     }
10674 
10675     if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) )
10676     {
10677         rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor;
10678         if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
10679             rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
10680         {
10681             appendNonStrokingColor( rNewState.m_aFillColor, aLine );
10682             aLine.append( "\n" );
10683         }
10684     }
10685 
10686     if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) )
10687     {
10688         rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent;
10689         if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent )
10690         {
10691             // TODO: switch extended graphicsstate
10692         }
10693     }
10694 
10695     // everything is up to date now
10696     m_aCurrentPDFState = m_aGraphicsStack.front();
10697     if( aLine.getLength() )
10698         writeBuffer( aLine.getStr(), aLine.getLength() );
10699 }
10700 
10701 /* #i47544# imitate OutputDevice behaviour:
10702 *  if a font with a nontransparent color is set, it overwrites the current
10703 *  text color. OTOH setting the text color will overwrite the color of the font.
10704 */
10705 void PDFWriterImpl::setFont( const Font& rFont )
10706 {
10707     Color aColor = rFont.GetColor();
10708     if( aColor == Color( COL_TRANSPARENT ) )
10709         aColor = m_aGraphicsStack.front().m_aFont.GetColor();
10710     m_aGraphicsStack.front().m_aFont = rFont;
10711     m_aGraphicsStack.front().m_aFont.SetColor( aColor );
10712     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont;
10713 }
10714 
10715 void PDFWriterImpl::push( sal_uInt16 nFlags )
10716 {
10717     OSL_ENSURE( m_aGraphicsStack.size() > 0, "invalid graphics stack" );
10718     m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
10719     m_aGraphicsStack.front().m_nFlags = nFlags;
10720 }
10721 
10722 void PDFWriterImpl::pop()
10723 {
10724     OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" );
10725     if( m_aGraphicsStack.size() < 2 )
10726         return;
10727 
10728     GraphicsState aState = m_aGraphicsStack.front();
10729     m_aGraphicsStack.pop_front();
10730     GraphicsState& rOld = m_aGraphicsStack.front();
10731 
10732     // move those parameters back that were not pushed
10733     // in the first place
10734     if( ! (aState.m_nFlags & PUSH_LINECOLOR) )
10735         setLineColor( aState.m_aLineColor );
10736     if( ! (aState.m_nFlags & PUSH_FILLCOLOR) )
10737         setFillColor( aState.m_aFillColor );
10738     if( ! (aState.m_nFlags & PUSH_FONT) )
10739         setFont( aState.m_aFont );
10740     if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) )
10741         setTextColor( aState.m_aFont.GetColor() );
10742     if( ! (aState.m_nFlags & PUSH_MAPMODE) )
10743         setMapMode( aState.m_aMapMode );
10744     if( ! (aState.m_nFlags & PUSH_CLIPREGION) )
10745     {
10746         // do not use setClipRegion here
10747         // it would convert again assuming the current mapmode
10748         rOld.m_aClipRegion = aState.m_aClipRegion;
10749         rOld.m_bClipRegion = aState.m_bClipRegion;
10750     }
10751     if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) )
10752         setTextLineColor( aState.m_aTextLineColor );
10753     if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) )
10754         setOverlineColor( aState.m_aOverlineColor );
10755     if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) )
10756         setTextAlign( aState.m_aFont.GetAlign() );
10757     if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) )
10758         setTextFillColor( aState.m_aFont.GetFillColor() );
10759     if( ! (aState.m_nFlags & PUSH_REFPOINT) )
10760     {
10761         // what ?
10762     }
10763     // invalidate graphics state
10764     m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U);
10765 }
10766 
10767 void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
10768 {
10769     m_aGraphicsStack.front().m_aMapMode = rMapMode;
10770     getReferenceDevice()->SetMapMode( rMapMode );
10771     m_aCurrentPDFState.m_aMapMode = rMapMode;
10772 }
10773 
10774 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10775 {
10776     basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10777     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10778     m_aGraphicsStack.front().m_aClipRegion = aRegion;
10779     m_aGraphicsStack.front().m_bClipRegion = true;
10780     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10781 }
10782 
10783 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
10784 {
10785     if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() )
10786     {
10787         Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10788                                    m_aMapMode,
10789                                    getReferenceDevice(),
10790                                    Point( nX, nY ) ) );
10791         aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10792                                m_aMapMode,
10793                                getReferenceDevice(),
10794                                Point() );
10795         basegfx::B2DHomMatrix aMat;
10796         aMat.translate( aPoint.X(), aPoint.Y() );
10797         m_aGraphicsStack.front().m_aClipRegion.transform( aMat );
10798         m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10799     }
10800 }
10801 
10802 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect )
10803 {
10804     basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect(
10805         basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) );
10806     return intersectClipRegion( aRect );
10807 }
10808 
10809 
10810 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10811 {
10812     basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) );
10813     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10814     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10815     if( m_aGraphicsStack.front().m_bClipRegion )
10816     {
10817         basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) );
10818         aRegion = basegfx::tools::prepareForPolygonOperation( aRegion );
10819         m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion );
10820     }
10821     else
10822     {
10823         m_aGraphicsStack.front().m_aClipRegion = aRegion;
10824         m_aGraphicsStack.front().m_bClipRegion = true;
10825     }
10826     return true;
10827 }
10828 
10829 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
10830 {
10831     if( nPageNr < 0 )
10832         nPageNr = m_nCurrentPage;
10833 
10834     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10835         return;
10836 
10837     m_aNotes.push_back( PDFNoteEntry() );
10838     m_aNotes.back().m_nObject		= createObject();
10839     m_aNotes.back().m_aContents		= rNote;
10840     m_aNotes.back().m_aRect			= rRect;
10841     // convert to default user space now, since the mapmode may change
10842     m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
10843 
10844     // insert note to page's annotation list
10845     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
10846 }
10847 
10848 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr )
10849 {
10850     if( nPageNr < 0 )
10851         nPageNr = m_nCurrentPage;
10852 
10853     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10854         return -1;
10855 
10856     sal_Int32 nRet = m_aLinks.size();
10857 
10858     m_aLinks.push_back( PDFLink() );
10859     m_aLinks.back().m_nObject	= createObject();
10860     m_aLinks.back().m_nPage		= nPageNr;
10861     m_aLinks.back().m_aRect		= rRect;
10862     // convert to default user space now, since the mapmode may change
10863     m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
10864 
10865     // insert link to page's annotation list
10866     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
10867 
10868     return nRet;
10869 }
10870 
10871 //--->i56629
10872 sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10873 {
10874     if( nPageNr < 0 )
10875         nPageNr = m_nCurrentPage;
10876 
10877     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10878         return -1;
10879 
10880     sal_Int32 nRet = m_aNamedDests.size();
10881 
10882     m_aNamedDests.push_back( PDFNamedDest() );
10883     m_aNamedDests.back().m_aDestName = sDestName;
10884     m_aNamedDests.back().m_nPage = nPageNr;
10885     m_aNamedDests.back().m_eType = eType;
10886     m_aNamedDests.back().m_aRect = rRect;
10887     // convert to default user space now, since the mapmode may change
10888     m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
10889 
10890     return nRet;
10891 }
10892 //<---i56629
10893 
10894 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10895 {
10896     if( nPageNr < 0 )
10897         nPageNr = m_nCurrentPage;
10898 
10899     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10900         return -1;
10901 
10902     sal_Int32 nRet = m_aDests.size();
10903 
10904     m_aDests.push_back( PDFDest() );
10905     m_aDests.back().m_nPage = nPageNr;
10906     m_aDests.back().m_eType	= eType;
10907     m_aDests.back().m_aRect = rRect;
10908     // convert to default user space now, since the mapmode may change
10909     m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
10910 
10911     return nRet;
10912 }
10913 
10914 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10915 {
10916     return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType );
10917 }
10918 
10919 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
10920 {
10921     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10922         return -1;
10923     if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
10924         return -2;
10925 
10926     m_aLinks[ nLinkId ].m_nDest = nDestId;
10927 
10928     return 0;
10929 }
10930 
10931 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
10932 {
10933     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10934         return -1;
10935 
10936     m_aLinks[ nLinkId ].m_nDest	= -1;
10937 
10938     using namespace ::com::sun::star;
10939 
10940     if (!m_xTrans.is())
10941     {
10942         uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() );
10943         if( xFact.is() )
10944         {
10945             m_xTrans = uno::Reference < util::XURLTransformer >(
10946                 xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY );
10947         }
10948     }
10949 
10950     util::URL aURL;
10951     aURL.Complete = rURL;
10952 
10953     if (m_xTrans.is())
10954         m_xTrans->parseStrict( aURL );
10955 
10956     m_aLinks[ nLinkId ].m_aURL	= aURL.Complete;
10957 
10958     return 0;
10959 }
10960 
10961 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
10962 {
10963     m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
10964 }
10965 
10966 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
10967 {
10968     // create new item
10969     sal_Int32 nNewItem = m_aOutline.size();
10970     m_aOutline.push_back( PDFOutlineEntry() );
10971 
10972     // set item attributes
10973     setOutlineItemParent( nNewItem, nParent );
10974     setOutlineItemText( nNewItem, rText );
10975     setOutlineItemDest( nNewItem, nDestID );
10976 
10977     return nNewItem;
10978 }
10979 
10980 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
10981 {
10982     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10983         return -1;
10984 
10985     int nRet = 0;
10986 
10987     if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
10988     {
10989         nNewParent = 0;
10990         nRet = -2;
10991     }
10992     // remove item from previous parent
10993     sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID;
10994     if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() )
10995     {
10996         PDFOutlineEntry& rParent = m_aOutline[ nParentID ];
10997 
10998         for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin();
10999              it != rParent.m_aChildren.end(); ++it )
11000         {
11001             if( *it == nItem )
11002             {
11003                 rParent.m_aChildren.erase( it );
11004                 break;
11005             }
11006         }
11007     }
11008 
11009     // insert item to new parent's list of children
11010     m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
11011 
11012     return nRet;
11013 }
11014 
11015 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
11016 {
11017     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
11018         return -1;
11019 
11020     m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText );
11021     return 0;
11022 }
11023 
11024 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
11025 {
11026     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
11027         return -1;
11028     if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
11029         return -2;
11030     m_aOutline[nItem].m_nDestID = nDestID;
11031     return 0;
11032 }
11033 
11034 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
11035 {
11036     static std::map< PDFWriter::StructElement, const char* > aTagStrings;
11037     if( aTagStrings.empty() )
11038     {
11039         aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
11040         aTagStrings[ PDFWriter::Document ]		= "Document";
11041         aTagStrings[ PDFWriter::Part ]			= "Part";
11042         aTagStrings[ PDFWriter::Article ]		= "Art";
11043         aTagStrings[ PDFWriter::Section ]		= "Sect";
11044         aTagStrings[ PDFWriter::Division ]		= "Div";
11045         aTagStrings[ PDFWriter::BlockQuote ]	= "BlockQuote";
11046         aTagStrings[ PDFWriter::Caption ]		= "Caption";
11047         aTagStrings[ PDFWriter::TOC ]			= "TOC";
11048         aTagStrings[ PDFWriter::TOCI ]			= "TOCI";
11049         aTagStrings[ PDFWriter::Index ]			= "Index";
11050         aTagStrings[ PDFWriter::Paragraph ]		= "P";
11051         aTagStrings[ PDFWriter::Heading ]		= "H";
11052         aTagStrings[ PDFWriter::H1 ]			= "H1";
11053         aTagStrings[ PDFWriter::H2 ]			= "H2";
11054         aTagStrings[ PDFWriter::H3 ]			= "H3";
11055         aTagStrings[ PDFWriter::H4 ]			= "H4";
11056         aTagStrings[ PDFWriter::H5 ]			= "H5";
11057         aTagStrings[ PDFWriter::H6 ]			= "H6";
11058         aTagStrings[ PDFWriter::List ]			= "L";
11059         aTagStrings[ PDFWriter::ListItem ]		= "LI";
11060         aTagStrings[ PDFWriter::LILabel ]		= "Lbl";
11061         aTagStrings[ PDFWriter::LIBody ]		= "LBody";
11062         aTagStrings[ PDFWriter::Table ]			= "Table";
11063         aTagStrings[ PDFWriter::TableRow ]		= "TR";
11064         aTagStrings[ PDFWriter::TableHeader ]	= "TH";
11065         aTagStrings[ PDFWriter::TableData ]		= "TD";
11066         aTagStrings[ PDFWriter::Span ]			= "Span";
11067         aTagStrings[ PDFWriter::Quote ]			= "Quote";
11068         aTagStrings[ PDFWriter::Note ]			= "Note";
11069         aTagStrings[ PDFWriter::Reference ]		= "Reference";
11070         aTagStrings[ PDFWriter::BibEntry ]		= "BibEntry";
11071         aTagStrings[ PDFWriter::Code ]			= "Code";
11072         aTagStrings[ PDFWriter::Link ]			= "Link";
11073         aTagStrings[ PDFWriter::Figure ]		= "Figure";
11074         aTagStrings[ PDFWriter::Formula ]		= "Formula";
11075         aTagStrings[ PDFWriter::Form ]			= "Form";
11076     }
11077 
11078     std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
11079 
11080     return it != aTagStrings.end() ? it->second : "Div";
11081 }
11082 
11083 void PDFWriterImpl::beginStructureElementMCSeq()
11084 {
11085     if(	m_bEmitStructure &&
11086         m_nCurrentStructElement > 0 && // StructTreeRoot
11087         ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11088         )
11089     {
11090         PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
11091         OStringBuffer aLine( 128 );
11092         sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
11093         aLine.append( "/" );
11094         if( rEle.m_aAlias.getLength() > 0 )
11095             aLine.append( rEle.m_aAlias );
11096         else
11097             aLine.append( getStructureTag( rEle.m_eType ) );
11098         aLine.append( "<</MCID " );
11099         aLine.append( nMCID );
11100         aLine.append( ">>BDC\n" );
11101         writeBuffer( aLine.getStr(), aLine.getLength() );
11102 
11103         // update the element's content list
11104 #if OSL_DEBUG_LEVEL > 1
11105         fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n",
11106                  nMCID,
11107                  m_aPages[ m_nCurrentPage ].m_nPageObject,
11108                  rEle.m_nFirstPageObject );
11109 #endif
11110         rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) );
11111         // update the page's mcid parent list
11112         m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
11113         // mark element MC sequence as open
11114         rEle.m_bOpenMCSeq = true;
11115     }
11116     // handle artifacts
11117     else if( ! m_bEmitStructure && m_aContext.Tagged &&
11118                m_nCurrentStructElement > 0 &&
11119                m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
11120              ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11121              )
11122     {
11123         OStringBuffer aLine( 128 );
11124         aLine.append( "/Artifact BMC\n" );
11125         writeBuffer( aLine.getStr(), aLine.getLength() );
11126         // mark element MC sequence as open
11127         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
11128     }
11129 }
11130 
11131 void PDFWriterImpl::endStructureElementMCSeq()
11132 {
11133     if( m_nCurrentStructElement > 0 && // StructTreeRoot
11134         ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
11135         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
11136         )
11137     {
11138         writeBuffer( "EMC\n", 4 );
11139         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
11140     }
11141 }
11142 
11143 bool PDFWriterImpl::checkEmitStructure()
11144 {
11145     bool bEmit = false;
11146     if( m_aContext.Tagged )
11147     {
11148         bEmit = true;
11149         sal_Int32 nEle = m_nCurrentStructElement;
11150         while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
11151         {
11152             if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
11153             {
11154                 bEmit = false;
11155                 break;
11156             }
11157             nEle = m_aStructure[ nEle ].m_nParentElement;
11158         }
11159     }
11160     return bEmit;
11161 }
11162 
11163 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias )
11164 {
11165     if( m_nCurrentPage < 0 )
11166         return -1;
11167 
11168     if( ! m_aContext.Tagged )
11169         return -1;
11170 
11171     // close eventual current MC sequence
11172     endStructureElementMCSeq();
11173 
11174     if( m_nCurrentStructElement == 0 &&
11175         eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
11176     {
11177         // struct tree root hit, but not beginning document
11178         // this might happen with setCurrentStructureElement
11179         // silently insert structure into document again if one properly exists
11180         if( ! m_aStructure[ 0 ].m_aChildren.empty() )
11181         {
11182             PDFWriter::StructElement childType = PDFWriter::NonStructElement;
11183             sal_Int32 nNewCurElement = 0;
11184             const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
11185             for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
11186                  childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
11187             {
11188                 nNewCurElement = *it;
11189                 childType = m_aStructure[ nNewCurElement ].m_eType;
11190             }
11191             if( childType == PDFWriter::Document )
11192             {
11193                 m_nCurrentStructElement = nNewCurElement;
11194                 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" );
11195             }
11196             else {
11197                 DBG_ERROR( "document structure in disorder !" );
11198             }
11199         }
11200         else {
11201             DBG_ERROR( "PDF document structure MUST be contained in a Document element" );
11202         }
11203     }
11204 
11205     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11206     m_aStructure.push_back( PDFStructureElement() );
11207     PDFStructureElement& rEle = m_aStructure.back();
11208     rEle.m_eType			= eType;
11209     rEle.m_nOwnElement		= nNewId;
11210     rEle.m_nParentElement	= m_nCurrentStructElement;
11211     rEle.m_nFirstPageObject	= m_aPages[ m_nCurrentPage ].m_nPageObject;
11212     m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
11213     m_nCurrentStructElement = nNewId;
11214 
11215     // handle alias names
11216     if( rAlias.getLength() && eType != PDFWriter::NonStructElement )
11217     {
11218         OStringBuffer aNameBuf( rAlias.getLength() );
11219         appendName( rAlias, aNameBuf );
11220         OString aAliasName( aNameBuf.makeStringAndClear() );
11221         rEle.m_aAlias = aAliasName;
11222         m_aRoleMap[ aAliasName ] = getStructureTag( eType );
11223     }
11224 
11225 #if OSL_DEBUG_LEVEL > 1
11226     OStringBuffer aLine( "beginStructureElement " );
11227     aLine.append( m_nCurrentStructElement );
11228     aLine.append( ": " );
11229     aLine.append( getStructureTag( eType ) );
11230     if( rEle.m_aAlias.getLength() )
11231     {
11232         aLine.append( " aliased as \"" );
11233         aLine.append( rEle.m_aAlias );
11234         aLine.append( '\"' );
11235     }
11236     emitComment( aLine.getStr() );
11237 #endif
11238 
11239     // check whether to emit structure henceforth
11240     m_bEmitStructure = checkEmitStructure();
11241 
11242     if( m_bEmitStructure ) // don't create nonexistant objects
11243     {
11244         rEle.m_nObject		= createObject();
11245         // update parent's kids list
11246         m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject );
11247     }
11248     return nNewId;
11249 }
11250 
11251 void PDFWriterImpl::endStructureElement()
11252 {
11253     if( m_nCurrentPage < 0 )
11254         return;
11255 
11256     if( ! m_aContext.Tagged )
11257         return;
11258 
11259     if( m_nCurrentStructElement == 0 )
11260     {
11261         // hit the struct tree root, that means there is an endStructureElement
11262         // without corresponding beginStructureElement
11263         return;
11264     }
11265 
11266     // end the marked content sequence
11267     endStructureElementMCSeq();
11268 
11269 #if OSL_DEBUG_LEVEL > 1
11270     OStringBuffer aLine( "endStructureElement " );
11271     aLine.append( m_nCurrentStructElement );
11272     aLine.append( ": " );
11273     aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11274     if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
11275     {
11276         aLine.append( " aliased as \"" );
11277         aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11278         aLine.append( '\"' );
11279     }
11280 #endif
11281 
11282     // "end" the structure element, the parent becomes current element
11283     m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
11284 
11285     // check whether to emit structure henceforth
11286     m_bEmitStructure = checkEmitStructure();
11287 
11288 #if OSL_DEBUG_LEVEL > 1
11289     if( m_bEmitStructure )
11290         emitComment( aLine.getStr() );
11291 #endif
11292 }
11293 
11294 //---> i94258
11295 /*
11296  * This function adds an internal structure list container to overcome the 8191 elements array limitation
11297  * in kids element emission.
11298  * Recursive function
11299  *
11300  */
11301 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
11302 {
11303     if( rEle.m_eType == PDFWriter::NonStructElement &&
11304         rEle.m_nOwnElement != rEle.m_nParentElement )
11305         return;
11306 
11307     for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
11308     {
11309         if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
11310         {
11311             PDFStructureElement& rChild = m_aStructure[ *it ];
11312             if( rChild.m_eType != PDFWriter::NonStructElement )
11313             {
11314                 //triggered when a child of the rEle element is found
11315                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
11316                     addInternalStructureContainer( rChild );//examine the child
11317                 else
11318                 {
11319                     DBG_ERROR( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
11320 #if OSL_DEBUG_LEVEL > 1
11321                     fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
11322 #endif
11323                 }
11324             }
11325         }
11326         else
11327         {
11328             DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
11329 #if OSL_DEBUG_LEVEL > 1
11330             fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
11331 #endif
11332         }
11333     }
11334 
11335     if( rEle.m_nOwnElement != rEle.m_nParentElement )
11336     {
11337         if( !rEle.m_aKids.empty() )
11338         {
11339             if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
11340                 //then we need to add the containers for the kids elements
11341                 // a list to be used for the new kid element
11342                 std::list< PDFStructureElementKid > aNewKids;
11343                 std::list< sal_Int32 > aNewChildren;
11344 
11345                 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
11346                 OStringBuffer aNameBuf( "Div" );
11347                 OString aAliasName( aNameBuf.makeStringAndClear() );
11348                 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
11349 
11350                 while( rEle.m_aKids.size() > ncMaxPDFArraySize )
11351                 {
11352                     sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
11353                     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11354                     m_aStructure.push_back( PDFStructureElement() );
11355                     PDFStructureElement& rEleNew = m_aStructure.back();
11356                     rEleNew.m_aAlias            = aAliasName;
11357                     rEleNew.m_eType			    = PDFWriter::Division; // a new Div type container
11358                     rEleNew.m_nOwnElement		= nNewId;
11359                     rEleNew.m_nParentElement	= nCurrentStructElement;
11360                     //inherit the same page as the first child to be reparented
11361                     rEleNew.m_nFirstPageObject	= m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
11362                     rEleNew.m_nObject           = createObject();//assign a PDF object number
11363                     //add the object to the kid list of the parent
11364                     aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) );
11365                     aNewChildren.push_back( nNewId );
11366 
11367                     std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
11368                     std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
11369                     advance( aChildEndIt, ncMaxPDFArraySize );
11370                     advance( aKidEndIt, ncMaxPDFArraySize );
11371 
11372                     rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
11373                                             rEle.m_aKids,
11374                                             rEle.m_aKids.begin(),
11375                                             aKidEndIt );
11376                     rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
11377                                                 rEle.m_aChildren,
11378                                                 rEle.m_aChildren.begin(),
11379                                                 aChildEndIt );
11380                     // set the kid's new parent
11381                     for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
11382                          it != rEleNew.m_aChildren.end(); ++it )
11383                     {
11384                         m_aStructure[ *it ].m_nParentElement = nNewId;
11385                     }
11386                 }
11387                 //finally add the new kids resulting from the container added
11388                 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
11389                 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
11390             }
11391         }
11392     }
11393 }
11394 //<--- i94258
11395 
11396 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
11397 {
11398     bool bSuccess = false;
11399 
11400     if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
11401     {
11402         // end eventual previous marked content sequence
11403         endStructureElementMCSeq();
11404 
11405         m_nCurrentStructElement = nEle;
11406         m_bEmitStructure = checkEmitStructure();
11407 #if OSL_DEBUG_LEVEL > 1
11408         OStringBuffer aLine( "setCurrentStructureElement " );
11409         aLine.append( m_nCurrentStructElement );
11410         aLine.append( ": " );
11411         aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11412         if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
11413         {
11414             aLine.append( " aliased as \"" );
11415             aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11416             aLine.append( '\"' );
11417         }
11418         if( ! m_bEmitStructure )
11419             aLine.append( " (inside NonStruct)" );
11420         emitComment( aLine.getStr() );
11421 #endif
11422         bSuccess = true;
11423     }
11424 
11425     return bSuccess;
11426 }
11427 
11428 sal_Int32 PDFWriterImpl::getCurrentStructureElement()
11429 {
11430     return m_nCurrentStructElement;
11431 }
11432 
11433 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
11434 {
11435     if( !m_aContext.Tagged )
11436         return false;
11437 
11438     bool bInsert = false;
11439     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11440     {
11441         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11442         switch( eAttr )
11443         {
11444             case PDFWriter::Placement:
11445                 if( eVal == PDFWriter::Block		||
11446                     eVal == PDFWriter::Inline		||
11447                     eVal == PDFWriter::Before		||
11448                     eVal == PDFWriter::Start		||
11449                     eVal == PDFWriter::End )
11450                     bInsert = true;
11451                 break;
11452             case PDFWriter::WritingMode:
11453                 if( eVal == PDFWriter::LrTb			||
11454                     eVal == PDFWriter::RlTb			||
11455                     eVal == PDFWriter::TbRl )
11456                 {
11457                     bInsert = true;
11458                 }
11459                 break;
11460             case PDFWriter::TextAlign:
11461                 if( eVal == PDFWriter::Start		||
11462                     eVal == PDFWriter::Center		||
11463                     eVal == PDFWriter::End			||
11464                     eVal == PDFWriter::Justify )
11465                 {
11466                     if( eType == PDFWriter::Paragraph	||
11467                         eType == PDFWriter::Heading		||
11468                         eType == PDFWriter::H1			||
11469                         eType == PDFWriter::H2			||
11470                         eType == PDFWriter::H3			||
11471                         eType == PDFWriter::H4			||
11472                         eType == PDFWriter::H5			||
11473                         eType == PDFWriter::H6			||
11474                         eType == PDFWriter::List		||
11475                         eType == PDFWriter::ListItem	||
11476                         eType == PDFWriter::LILabel		||
11477                         eType == PDFWriter::LIBody		||
11478                         eType == PDFWriter::Table		||
11479                         eType == PDFWriter::TableRow	||
11480                         eType == PDFWriter::TableHeader	||
11481                         eType == PDFWriter::TableData )
11482                     {
11483                         bInsert = true;
11484                     }
11485                 }
11486                 break;
11487             case PDFWriter::Width:
11488             case PDFWriter::Height:
11489                 if( eVal == PDFWriter::Auto )
11490                 {
11491                     if( eType == PDFWriter::Figure		||
11492                         eType == PDFWriter::Formula		||
11493                         eType == PDFWriter::Form		||
11494                         eType == PDFWriter::Table		||
11495                         eType == PDFWriter::TableHeader	||
11496                         eType == PDFWriter::TableData )
11497                     {
11498                         bInsert = true;
11499                     }
11500                 }
11501                 break;
11502             case PDFWriter::BlockAlign:
11503                 if( eVal == PDFWriter::Before		||
11504                     eVal == PDFWriter::Middle		||
11505                     eVal == PDFWriter::After		||
11506                     eVal == PDFWriter::Justify )
11507                 {
11508                     if( eType == PDFWriter::TableHeader	||
11509                         eType == PDFWriter::TableData )
11510                     {
11511                         bInsert = true;
11512                     }
11513                 }
11514                 break;
11515             case PDFWriter::InlineAlign:
11516                 if( eVal == PDFWriter::Start		||
11517                     eVal == PDFWriter::Center		||
11518                     eVal == PDFWriter::End )
11519                 {
11520                     if( eType == PDFWriter::TableHeader	||
11521                         eType == PDFWriter::TableData )
11522                     {
11523                         bInsert = true;
11524                     }
11525                 }
11526                 break;
11527             case PDFWriter::LineHeight:
11528                 if( eVal == PDFWriter::Normal		||
11529                     eVal == PDFWriter::Auto )
11530                 {
11531                     // only for ILSE and BLSE
11532                     if( eType == PDFWriter::Paragraph	||
11533                         eType == PDFWriter::Heading		||
11534                         eType == PDFWriter::H1			||
11535                         eType == PDFWriter::H2			||
11536                         eType == PDFWriter::H3			||
11537                         eType == PDFWriter::H4			||
11538                         eType == PDFWriter::H5			||
11539                         eType == PDFWriter::H6			||
11540                         eType == PDFWriter::List		||
11541                         eType == PDFWriter::ListItem	||
11542                         eType == PDFWriter::LILabel		||
11543                         eType == PDFWriter::LIBody		||
11544                         eType == PDFWriter::Table		||
11545                         eType == PDFWriter::TableRow	||
11546                         eType == PDFWriter::TableHeader	||
11547                         eType == PDFWriter::TableData	||
11548                         eType == PDFWriter::Span		||
11549                         eType == PDFWriter::Quote		||
11550                         eType == PDFWriter::Note		||
11551                         eType == PDFWriter::Reference	||
11552                         eType == PDFWriter::BibEntry	||
11553                         eType == PDFWriter::Code		||
11554                         eType == PDFWriter::Link )
11555                     {
11556                         bInsert = true;
11557                     }
11558                 }
11559                 break;
11560             case PDFWriter::TextDecorationType:
11561                 if( eVal == PDFWriter::NONE			||
11562                     eVal == PDFWriter::Underline	||
11563                     eVal == PDFWriter::Overline		||
11564                     eVal == PDFWriter::LineThrough )
11565                 {
11566                     // only for ILSE and BLSE
11567                     if( eType == PDFWriter::Paragraph	||
11568                         eType == PDFWriter::Heading		||
11569                         eType == PDFWriter::H1			||
11570                         eType == PDFWriter::H2			||
11571                         eType == PDFWriter::H3			||
11572                         eType == PDFWriter::H4			||
11573                         eType == PDFWriter::H5			||
11574                         eType == PDFWriter::H6			||
11575                         eType == PDFWriter::List		||
11576                         eType == PDFWriter::ListItem	||
11577                         eType == PDFWriter::LILabel		||
11578                         eType == PDFWriter::LIBody		||
11579                         eType == PDFWriter::Table		||
11580                         eType == PDFWriter::TableRow	||
11581                         eType == PDFWriter::TableHeader	||
11582                         eType == PDFWriter::TableData	||
11583                         eType == PDFWriter::Span		||
11584                         eType == PDFWriter::Quote		||
11585                         eType == PDFWriter::Note		||
11586                         eType == PDFWriter::Reference	||
11587                         eType == PDFWriter::BibEntry	||
11588                         eType == PDFWriter::Code		||
11589                         eType == PDFWriter::Link )
11590                     {
11591                         bInsert = true;
11592                     }
11593                 }
11594                 break;
11595             case PDFWriter::ListNumbering:
11596                 if( eVal == PDFWriter::NONE			||
11597                     eVal == PDFWriter::Disc			||
11598                     eVal == PDFWriter::Circle		||
11599                     eVal == PDFWriter::Square		||
11600                     eVal == PDFWriter::Decimal		||
11601                     eVal == PDFWriter::UpperRoman	||
11602                     eVal == PDFWriter::LowerRoman	||
11603                     eVal == PDFWriter::UpperAlpha	||
11604                     eVal == PDFWriter::LowerAlpha )
11605                 {
11606                     if( eType == PDFWriter::List )
11607                         bInsert = true;
11608                 }
11609                 break;
11610             default: break;
11611         }
11612     }
11613 
11614     if( bInsert )
11615         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
11616 #if OSL_DEBUG_LEVEL > 1
11617     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11618         fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
11619                  getAttributeTag( eAttr ),
11620                  getAttributeValueTag( eVal ),
11621                  getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11622                  m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
11623                  );
11624 #endif
11625 
11626     return bInsert;
11627 }
11628 
11629 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
11630 {
11631     if( ! m_aContext.Tagged )
11632         return false;
11633 
11634     bool bInsert = false;
11635     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11636     {
11637         if( eAttr == PDFWriter::Language )
11638         {
11639             m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue );
11640             return true;
11641         }
11642 
11643         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11644         switch( eAttr )
11645         {
11646             case PDFWriter::SpaceBefore:
11647             case PDFWriter::SpaceAfter:
11648             case PDFWriter::StartIndent:
11649             case PDFWriter::EndIndent:
11650                 // just for BLSE
11651                 if( eType == PDFWriter::Paragraph	||
11652                     eType == PDFWriter::Heading		||
11653                     eType == PDFWriter::H1			||
11654                     eType == PDFWriter::H2			||
11655                     eType == PDFWriter::H3			||
11656                     eType == PDFWriter::H4			||
11657                     eType == PDFWriter::H5			||
11658                     eType == PDFWriter::H6			||
11659                     eType == PDFWriter::List		||
11660                     eType == PDFWriter::ListItem	||
11661                     eType == PDFWriter::LILabel		||
11662                     eType == PDFWriter::LIBody		||
11663                     eType == PDFWriter::Table		||
11664                     eType == PDFWriter::TableRow	||
11665                     eType == PDFWriter::TableHeader	||
11666                     eType == PDFWriter::TableData )
11667                 {
11668                     bInsert = true;
11669                 }
11670                 break;
11671             case PDFWriter::TextIndent:
11672                 // paragraph like BLSE and additional elements
11673                 if( eType == PDFWriter::Paragraph	||
11674                     eType == PDFWriter::Heading		||
11675                     eType == PDFWriter::H1			||
11676                     eType == PDFWriter::H2			||
11677                     eType == PDFWriter::H3			||
11678                     eType == PDFWriter::H4			||
11679                     eType == PDFWriter::H5			||
11680                     eType == PDFWriter::H6			||
11681                     eType == PDFWriter::LILabel		||
11682                     eType == PDFWriter::LIBody		||
11683                     eType == PDFWriter::TableHeader	||
11684                     eType == PDFWriter::TableData )
11685                 {
11686                     bInsert = true;
11687                 }
11688                 break;
11689             case PDFWriter::Width:
11690             case PDFWriter::Height:
11691                 if( eType == PDFWriter::Figure		||
11692                     eType == PDFWriter::Formula		||
11693                     eType == PDFWriter::Form		||
11694                     eType == PDFWriter::Table		||
11695                     eType == PDFWriter::TableHeader	||
11696                     eType == PDFWriter::TableData )
11697                 {
11698                     bInsert = true;
11699                 }
11700                 break;
11701             case PDFWriter::LineHeight:
11702             case PDFWriter::BaselineShift:
11703                 // only for ILSE and BLSE
11704                 if( eType == PDFWriter::Paragraph	||
11705                     eType == PDFWriter::Heading		||
11706                     eType == PDFWriter::H1			||
11707                     eType == PDFWriter::H2			||
11708                     eType == PDFWriter::H3			||
11709                     eType == PDFWriter::H4			||
11710                     eType == PDFWriter::H5			||
11711                     eType == PDFWriter::H6			||
11712                     eType == PDFWriter::List		||
11713                     eType == PDFWriter::ListItem	||
11714                     eType == PDFWriter::LILabel		||
11715                     eType == PDFWriter::LIBody		||
11716                     eType == PDFWriter::Table		||
11717                     eType == PDFWriter::TableRow	||
11718                     eType == PDFWriter::TableHeader	||
11719                     eType == PDFWriter::TableData	||
11720                     eType == PDFWriter::Span		||
11721                     eType == PDFWriter::Quote		||
11722                     eType == PDFWriter::Note		||
11723                     eType == PDFWriter::Reference	||
11724                     eType == PDFWriter::BibEntry	||
11725                     eType == PDFWriter::Code		||
11726                     eType == PDFWriter::Link )
11727                 {
11728                         bInsert = true;
11729                 }
11730                 break;
11731             case PDFWriter::RowSpan:
11732             case PDFWriter::ColSpan:
11733                 // only for table cells
11734                 if( eType == PDFWriter::TableHeader	||
11735                     eType == PDFWriter::TableData )
11736                 {
11737                     bInsert = true;
11738                 }
11739                 break;
11740             case PDFWriter::LinkAnnotation:
11741                 if( eType == PDFWriter::Link )
11742                     bInsert = true;
11743                 break;
11744             default: break;
11745         }
11746     }
11747 
11748     if( bInsert )
11749         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
11750 #if OSL_DEBUG_LEVEL > 1
11751     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11752         fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
11753                  getAttributeTag( eAttr ),
11754                  (int)nValue,
11755                  getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11756                  m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() );
11757 #endif
11758 
11759     return bInsert;
11760 }
11761 
11762 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect )
11763 {
11764     sal_Int32 nPageNr = m_nCurrentPage;
11765     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
11766         return;
11767 
11768 
11769     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11770     {
11771         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11772         if( eType == PDFWriter::Figure		||
11773             eType == PDFWriter::Formula		||
11774             eType == PDFWriter::Form		||
11775             eType == PDFWriter::Table )
11776         {
11777             m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
11778             // convert to default user space now, since the mapmode may change
11779             m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
11780         }
11781     }
11782 }
11783 
11784 void PDFWriterImpl::setActualText( const String& rText )
11785 {
11786     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11787     {
11788         m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
11789     }
11790 }
11791 
11792 void PDFWriterImpl::setAlternateText( const String& rText )
11793 {
11794     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11795     {
11796         m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
11797     }
11798 }
11799 
11800 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
11801 {
11802     if( nPageNr < 0 )
11803         nPageNr = m_nCurrentPage;
11804 
11805     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11806         return;
11807 
11808     m_aPages[ nPageNr ].m_nDuration = nSeconds;
11809 }
11810 
11811 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
11812 {
11813     if( nPageNr < 0 )
11814         nPageNr = m_nCurrentPage;
11815 
11816     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11817         return;
11818 
11819     m_aPages[ nPageNr ].m_eTransition	= eType;
11820     m_aPages[ nPageNr ].m_nTransTime	= nMilliSec;
11821 }
11822 
11823 void PDFWriterImpl::ensureUniqueRadioOnValues()
11824 {
11825     // loop over radio groups
11826     for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
11827          group != m_aRadioGroupWidgets.end(); ++group )
11828     {
11829         PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
11830         // check whether all kids have a unique OnValue
11831         std::hash_map< OUString, sal_Int32, OUStringHash > aOnValues;
11832         int nChildren = rGroupWidget.m_aKidsIndex.size();
11833         bool bIsUnique = true;
11834         for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
11835         {
11836             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11837             const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
11838             #if OSL_DEBUG_LEVEL > 1
11839             fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() );
11840             #endif
11841             if( aOnValues.find( rVal ) == aOnValues.end() )
11842             {
11843                 aOnValues[ rVal ] = 1;
11844             }
11845             else
11846             {
11847                 bIsUnique = false;
11848             }
11849         }
11850         if( ! bIsUnique )
11851         {
11852             #if OSL_DEBUG_LEVEL > 1
11853             fprintf( stderr, "enforcing unique OnValues\n" );
11854             #endif
11855             // make unique by using ascending OnValues
11856             for( int nKid = 0; nKid < nChildren; nKid++ )
11857             {
11858                 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11859                 PDFWidget& rKid = m_aWidgets[nKidIndex];
11860                 rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) );
11861                 if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11862                     rKid.m_aValue = rKid.m_aOnValue;
11863             }
11864         }
11865         // finally move the "Yes" appearance to the OnValue appearance
11866         for( int nKid = 0; nKid < nChildren; nKid++ )
11867         {
11868             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11869             PDFWidget& rKid = m_aWidgets[nKidIndex];
11870             PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
11871             if( app_it != rKid.m_aAppearances.end() )
11872             {
11873                 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
11874                 if( stream_it != app_it->second.end() )
11875                 {
11876                     SvMemoryStream* pStream = stream_it->second;
11877                     app_it->second.erase( stream_it );
11878                     OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
11879                     appendName( rKid.m_aOnValue, aBuf );
11880                     (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11881                 }
11882                 #if OSL_DEBUG_LEVEL > 1
11883                 else
11884                     fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" );
11885                 #endif
11886             }
11887             // update selected radio button
11888             if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11889             {
11890                 rGroupWidget.m_aValue = rKid.m_aValue;
11891             }
11892         }
11893     }
11894 }
11895 
11896 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
11897 {
11898     sal_Int32 nRadioGroupWidget = -1;
11899 
11900     std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
11901 
11902     if( it == m_aRadioGroupWidgets.end() )
11903     {
11904         m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
11905             sal_Int32(m_aWidgets.size());
11906 
11907         // new group, insert the radiobutton
11908         m_aWidgets.push_back( PDFWidget() );
11909         m_aWidgets.back().m_nObject		= createObject();
11910         m_aWidgets.back().m_nPage		= m_nCurrentPage;
11911         m_aWidgets.back().m_eType		= PDFWriter::RadioButton;
11912         m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
11913         m_aWidgets.back().m_nFlags |= 0x0000C000;   // NoToggleToOff and Radio bits
11914 
11915         createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn );
11916     }
11917     else
11918         nRadioGroupWidget = it->second;
11919 
11920     return nRadioGroupWidget;
11921 }
11922 
11923 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
11924 {
11925     if( nPageNr < 0 )
11926         nPageNr = m_nCurrentPage;
11927 
11928     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11929         return -1;
11930 
11931     sal_Int32 nNewWidget = m_aWidgets.size();
11932     m_aWidgets.push_back( PDFWidget() );
11933 
11934     m_aWidgets.back().m_nObject			= createObject();
11935     m_aWidgets.back().m_aRect				= rControl.Location;
11936     m_aWidgets.back().m_nPage				= nPageNr;
11937     m_aWidgets.back().m_eType				= rControl.getType();
11938 
11939     sal_Int32 nRadioGroupWidget = -1;
11940     // for unknown reasons the radio buttons of a radio group must not have a
11941     // field name, else the buttons are in fact check boxes -
11942     // that is multiple buttons of the radio group can be selected
11943     if( rControl.getType() == PDFWriter::RadioButton )
11944         nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
11945     else
11946     {
11947         createWidgetFieldName( nNewWidget, rControl );
11948     }
11949 
11950     // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
11951     PDFWidget& rNewWidget			= m_aWidgets[nNewWidget];
11952     rNewWidget.m_aDescription		= rControl.Description;
11953     rNewWidget.m_aText				= rControl.Text;
11954     rNewWidget.m_nTextStyle			= rControl.TextStyle &
11955         (  TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP |
11956            TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM |
11957            TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK  );
11958     rNewWidget.m_nTabOrder          = rControl.TabOrder;
11959 
11960     // various properties are set via the flags (/Ff) property of the field dict
11961     if( rControl.ReadOnly )
11962         rNewWidget.m_nFlags |= 1;
11963     if( rControl.getType() == PDFWriter::PushButton )
11964     {
11965         const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
11966         if( rNewWidget.m_nTextStyle == 0 )
11967             rNewWidget.m_nTextStyle =
11968                 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER |
11969                 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11970 
11971         rNewWidget.m_nFlags |= 0x00010000;
11972         if( rBtn.URL.getLength() )
11973             rNewWidget.m_aListEntries.push_back( rBtn.URL );
11974         rNewWidget.m_bSubmit    = rBtn.Submit;
11975         rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
11976         rNewWidget.m_nDest      = rBtn.Dest;
11977         createDefaultPushButtonAppearance( rNewWidget, rBtn );
11978     }
11979     else if( rControl.getType() == PDFWriter::RadioButton )
11980     {
11981         const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
11982         if( rNewWidget.m_nTextStyle == 0 )
11983             rNewWidget.m_nTextStyle =
11984                 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11985         /*  PDF sees a RadioButton group as one radio button with
11986          *  children which are in turn check boxes
11987          *
11988          *  so we need to create a radio button on demand for a new group
11989          *  and insert a checkbox for each RadioButtonWidget as its child
11990          */
11991         rNewWidget.m_eType			= PDFWriter::CheckBox;
11992         rNewWidget.m_nRadioGroup	= rBtn.RadioGroup;
11993 
11994         DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" );
11995 
11996         PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
11997         rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
11998         rRadioButton.m_aKidsIndex.push_back( nNewWidget );
11999         rNewWidget.m_nParent = rRadioButton.m_nObject;
12000 
12001         rNewWidget.m_aValue     = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) );
12002         rNewWidget.m_aOnValue   = rBtn.OnValue;
12003         if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected )
12004         {
12005             rNewWidget.m_aValue		= rNewWidget.m_aOnValue;
12006             rRadioButton.m_aValue	= rNewWidget.m_aOnValue;
12007         }
12008         createDefaultRadioButtonAppearance( rNewWidget, rBtn );
12009 
12010         // union rect of radio group
12011         Rectangle aRect = rNewWidget.m_aRect;
12012         m_aPages[ nPageNr ].convertRect( aRect );
12013         rRadioButton.m_aRect.Union( aRect );
12014     }
12015     else if( rControl.getType() == PDFWriter::CheckBox )
12016     {
12017         const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
12018         if( rNewWidget.m_nTextStyle == 0 )
12019             rNewWidget.m_nTextStyle =
12020                 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12021 
12022         rNewWidget.m_aValue = OUString::createFromAscii( rBox.Checked ? "Yes" : "Off" );
12023         // create default appearance before m_aRect gets transformed
12024         createDefaultCheckBoxAppearance( rNewWidget, rBox );
12025     }
12026     else if( rControl.getType() == PDFWriter::ListBox )
12027     {
12028         if( rNewWidget.m_nTextStyle == 0 )
12029             rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
12030 
12031         const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
12032         rNewWidget.m_aListEntries	  = rLstBox.Entries;
12033         rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
12034         rNewWidget.m_aValue			  = rLstBox.Text;
12035         if( rLstBox.DropDown )
12036             rNewWidget.m_nFlags |= 0x00020000;
12037         if( rLstBox.Sort )
12038             rNewWidget.m_nFlags |= 0x00080000;
12039         if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 )
12040             rNewWidget.m_nFlags |= 0x00200000;
12041 
12042         createDefaultListBoxAppearance( rNewWidget, rLstBox );
12043     }
12044     else if( rControl.getType() == PDFWriter::ComboBox )
12045     {
12046         if( rNewWidget.m_nTextStyle == 0 )
12047             rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
12048 
12049         const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
12050         rNewWidget.m_aValue			= rBox.Text;
12051         rNewWidget.m_aListEntries	= rBox.Entries;
12052         rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
12053         if( rBox.Sort )
12054             rNewWidget.m_nFlags |= 0x00080000;
12055 
12056         PDFWriter::ListBoxWidget aLBox;
12057         aLBox.Name				= rBox.Name;
12058         aLBox.Description		= rBox.Description;
12059         aLBox.Text				= rBox.Text;
12060         aLBox.TextStyle			= rBox.TextStyle;
12061         aLBox.ReadOnly			= rBox.ReadOnly;
12062         aLBox.Border			= rBox.Border;
12063         aLBox.BorderColor		= rBox.BorderColor;
12064         aLBox.Background		= rBox.Background;
12065         aLBox.BackgroundColor	= rBox.BackgroundColor;
12066         aLBox.TextFont			= rBox.TextFont;
12067         aLBox.TextColor			= rBox.TextColor;
12068         aLBox.DropDown			= true;
12069         aLBox.Sort				= rBox.Sort;
12070         aLBox.MultiSelect		= false;
12071         aLBox.Entries			= rBox.Entries;
12072 
12073         createDefaultListBoxAppearance( rNewWidget, aLBox );
12074     }
12075     else if( rControl.getType() == PDFWriter::Edit )
12076     {
12077         if( rNewWidget.m_nTextStyle == 0 )
12078             rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
12079 
12080         const PDFWriter::EditWidget& rEdit = static_cast<const  PDFWriter::EditWidget&>(rControl);
12081         if( rEdit.MultiLine )
12082         {
12083             rNewWidget.m_nFlags |= 0x00001000;
12084             rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12085         }
12086         if( rEdit.Password )
12087             rNewWidget.m_nFlags |= 0x00002000;
12088         if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 )
12089             rNewWidget.m_nFlags |= 0x00100000;
12090         rNewWidget.m_nMaxLen = rEdit.MaxLen;
12091         rNewWidget.m_aValue = rEdit.Text;
12092 
12093         createDefaultEditAppearance( rNewWidget, rEdit );
12094     }
12095 
12096     // convert to default user space now, since the mapmode may change
12097     // note: create default appearances before m_aRect gets transformed
12098     m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
12099 
12100     // insert widget to page's annotation list
12101     m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
12102 
12103     // mark page as having widgets
12104     m_aPages[ nPageNr ].m_bHasWidgets = true;
12105 
12106     return nNewWidget;
12107 }
12108 
12109 void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl )
12110 {
12111     if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() )
12112         return;
12113 
12114     PDFWidget& rWidget = m_aWidgets[ nControl ];
12115     m_nCurrentControl = nControl;
12116 
12117     SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 );
12118     // back conversion of control rect to current MapMode; necessary because
12119     // MapMode between createControl and beginControlAppearance
12120     // could have changed; therefore the widget rectangle is
12121     // already converted
12122     Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ),
12123                      rWidget.m_aRect.GetSize() );
12124     aBack = lcl_convert( m_aMapMode,
12125                          m_aGraphicsStack.front().m_aMapMode,
12126                          getReferenceDevice(),
12127                          aBack );
12128     beginRedirect( pControlStream, aBack );
12129     writeBuffer( "/Tx BMC\n", 8 );
12130 }
12131 
12132 bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState )
12133 {
12134     bool bRet = false;
12135     if( ! m_aOutputStreams.empty() )
12136         writeBuffer( "\nEMC\n", 5 );
12137     SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect());
12138     if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() )
12139     {
12140         PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ];
12141         OString aState, aStyle;
12142         switch( rWidget.m_eType )
12143         {
12144             case PDFWriter::PushButton:
12145                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12146                 {
12147                     aState = (eState == PDFWriter::Up) ? "N" : "D";
12148                     aStyle = "Standard";
12149                 }
12150                 break;
12151             case PDFWriter::CheckBox:
12152                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12153                 {
12154                     aState = "N";
12155                     aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes";
12156                     /* cf PDFReference 3rd ed. V1.4 p539:
12157                        recommended name for on state is "Yes",
12158                        recommended name for off state is "Off"
12159                      */
12160                 }
12161                 break;
12162             case PDFWriter::RadioButton:
12163                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12164                 {
12165                     aState = "N";
12166                     if( eState == PDFWriter::Up )
12167                         aStyle = "Off";
12168                     else
12169                     {
12170                         OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 );
12171                         appendName( rWidget.m_aOnValue, aBuf );
12172                         aStyle = aBuf.makeStringAndClear();
12173                     }
12174                 }
12175                 break;
12176             case PDFWriter::Edit:
12177                 aState = "N";
12178                 aStyle = "Standard";
12179                 break;
12180             case PDFWriter::ListBox:
12181             case PDFWriter::ComboBox:
12182             case PDFWriter::Hierarchy:
12183                 break;
12184         }
12185         if( aState.getLength() && aStyle.getLength() )
12186         {
12187             // delete eventual existing stream
12188             PDFAppearanceStreams::iterator it =
12189                 rWidget.m_aAppearances[ aState ].find( aStyle );
12190             if( it != rWidget.m_aAppearances[ aState ].end() )
12191                 delete it->second;
12192             rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance;
12193             bRet = true;
12194         }
12195     }
12196 
12197     if( ! bRet )
12198         delete pAppearance;
12199 
12200     m_nCurrentControl = -1;
12201 
12202     return bRet;
12203 }
12204 
12205 void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress )
12206 {
12207     if( pStream )
12208     {
12209         m_aAdditionalStreams.push_back( PDFAddStream() );
12210         PDFAddStream& rStream = m_aAdditionalStreams.back();
12211         rStream.m_aMimeType = rMimeType.Len()
12212                               ? OUString( rMimeType )
12213                               : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) );
12214         rStream.m_pStream = pStream;
12215         rStream.m_bCompress = bCompress;
12216     }
12217 }
12218 
12219 
12220 
12221