xref: /trunk/main/vcl/source/gdi/pdfwriter_impl.cxx (revision 248a599f)
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_GlyphId aGlyphIds[ 256 ];
3159         sal_uInt8 pEncoding[ 256 ];
3160         sal_Ucs   pUnicodes[ 256 ];
3161         sal_Int32 pDuWidths[ 256 ];
3162 
3163         memset( aGlyphIds, 0, sizeof( aGlyphIds ) );
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             aGlyphIds[c] = 0;
3173             if( aUnicodeMap.find( c ) != aUnicodeMap.end() )
3174                 pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ];
3175         }
3176 
3177         m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, pFont, aGlyphIds, 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_GlyphId aGlyphIds[ 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( aGlyphIds, sizeof( aGlyphIds ) );
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( aGlyphIds[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" );
4074                 DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" );
4075 
4076                 aGlyphIds[ 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, aGlyphIds, 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 )
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         {
7179             long nOffsetX = rGlyphs[i+1].m_aPos.X() - rGlyphs[i].m_aPos.X();
7180             long nOffsetY = rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
7181             nXOffset += (int)sqrt(double(nOffsetX*nOffsetX + nOffsetY*nOffsetY));
7182         }
7183         if( ! rGlyphs[i].m_nGlyphId )
7184             continue;
7185 
7186         aDeltaPos = rRotScale.transform( aDeltaPos );
7187 
7188         Matrix3 aMat;
7189         if( fSkewB != 0.0 || fSkewA != 0.0 )
7190             aMat.skew( fSkewA, fSkewB );
7191         aMat.scale( fTempXScale, fYScale );
7192         aMat.rotate( fAngle+fDeltaAngle );
7193         aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
7194         aMat.append( m_aPages.back(), rLine );
7195         rLine.append( " Tm" );
7196         if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
7197         {
7198             rLine.append( " /F" );
7199             rLine.append( rGlyphs[i].m_nMappedFontId );
7200             rLine.append( ' ' );
7201             m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7202             rLine.append( " Tf" );
7203         }
7204         rLine.append( "<" );
7205         appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
7206         rLine.append( ">Tj\n" );
7207     }
7208 }
7209 
7210 void PDFWriterImpl::drawHorizontalGlyphs(
7211         const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7212         OStringBuffer& rLine,
7213         const Point& rAlignOffset,
7214         double fAngle,
7215         double fXScale,
7216         double fSkew,
7217         sal_Int32 nFontHeight,
7218         sal_Int32 nPixelFontHeight
7219         )
7220 {
7221     // horizontal (= normal) case
7222 
7223     // fill in  run end indices
7224     // end is marked by index of the first glyph of the next run
7225     // a run is marked by same mapped font id and same Y position
7226     std::vector< sal_uInt32 > aRunEnds;
7227     aRunEnds.reserve( rGlyphs.size() );
7228     for( size_t i = 1; i < rGlyphs.size(); i++ )
7229     {
7230         if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
7231             rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
7232         {
7233             aRunEnds.push_back(i);
7234         }
7235     }
7236     // last run ends at last glyph
7237     aRunEnds.push_back( rGlyphs.size() );
7238 
7239     // loop over runs of the same font
7240     sal_uInt32 nBeginRun = 0;
7241     for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
7242     {
7243         // setup text matrix
7244         Point aCurPos = rGlyphs[nBeginRun].m_aPos;
7245         // back transformation to current coordinate system
7246         aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7247         aCurPos += rAlignOffset;
7248         // the first run can be set with "Td" operator
7249         // subsequent use of that operator would move
7250         // the texline matrix relative to what was set before
7251         // making use of that would drive us into rounding issues
7252         Matrix3 aMat;
7253         if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
7254         {
7255             m_aPages.back().appendPoint( aCurPos, rLine, false );
7256             rLine.append( " Td " );
7257         }
7258         else
7259         {
7260             if( fSkew != 0.0 )
7261                 aMat.skew( 0.0, fSkew );
7262             aMat.scale( fXScale, 1.0 );
7263             aMat.rotate( fAngle );
7264             aMat.translate( aCurPos.X(), aCurPos.Y() );
7265             aMat.append( m_aPages.back(), rLine );
7266             rLine.append( " Tm\n" );
7267         }
7268         // set up correct font
7269         rLine.append( "/F" );
7270         rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
7271         rLine.append( ' ' );
7272         m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7273         rLine.append( " Tf" );
7274 
7275         // output glyphs using Tj or TJ
7276         OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
7277         aKernedLine.append( "[<" );
7278         aUnkernedLine.append( '<' );
7279         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
7280         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
7281 
7282         aMat.invert();
7283         bool bNeedKern = false;
7284         for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
7285         {
7286             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
7287             // check if default glyph positioning is sufficient
7288             const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
7289             const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
7290             double fAdvance = aThisPos.X() - aPrevPos.X();
7291             fAdvance *= 1000.0 / nPixelFontHeight;
7292             const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5);
7293             if( nAdjustment != 0 )
7294             {
7295                 // apply individual glyph positioning
7296                 bNeedKern = true;
7297                 aKernedLine.append( ">" );
7298                 aKernedLine.append( nAdjustment );
7299                 aKernedLine.append( "<" );
7300             }
7301             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
7302         }
7303         aKernedLine.append( ">]TJ\n" );
7304         aUnkernedLine.append( ">Tj\n" );
7305         rLine.append( bNeedKern ? aKernedLine : aUnkernedLine );
7306 
7307         // set beginning of next run
7308         nBeginRun = aRunEnds[nRun];
7309     }
7310 }
7311 
7312 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines )
7313 {
7314     // relief takes precedence over shadow (see outdev3.cxx)
7315     if(  m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE )
7316     {
7317         drawRelief( rLayout, rText, bTextLines );
7318         return;
7319     }
7320     else if( m_aCurrentPDFState.m_aFont.IsShadow() )
7321         drawShadow( rLayout, rText, bTextLines );
7322 
7323     OStringBuffer aLine( 512 );
7324 
7325     const int nMaxGlyphs = 256;
7326 
7327     sal_GlyphId pGlyphs[nMaxGlyphs];
7328     sal_Int32 pGlyphWidths[nMaxGlyphs];
7329     sal_uInt8 pMappedGlyphs[nMaxGlyphs];
7330     sal_Int32 pMappedFontObjects[nMaxGlyphs];
7331     std::vector<sal_Ucs> aUnicodes;
7332     aUnicodes.reserve( nMaxGlyphs );
7333     sal_Int32 pUnicodesPerGlyph[nMaxGlyphs];
7334     int pCharPosAry[nMaxGlyphs];
7335     sal_Int32 nAdvanceWidths[nMaxGlyphs];
7336     const ImplFontData* pFallbackFonts[nMaxGlyphs];
7337     bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
7338     int nGlyphs;
7339     int nIndex = 0;
7340     int nMinCharPos = 0, nMaxCharPos = rText.Len()-1;
7341     double fXScale = 1.0;
7342     double fSkew = 0.0;
7343     sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight;
7344     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
7345 
7346     // transform font height back to current units
7347     // note: the layout calculates in outdevs device pixel !!
7348     sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
7349     if( m_aCurrentPDFState.m_aFont.GetWidth() )
7350     {
7351         Font aFont( m_aCurrentPDFState.m_aFont );
7352         aFont.SetWidth( 0 );
7353         FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
7354         if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() )
7355         {
7356             fXScale =
7357                 (double)m_aCurrentPDFState.m_aFont.GetWidth() /
7358                 (double)aMetric.GetWidth();
7359         }
7360         // force state before GetFontMetric
7361         m_pReferenceDevice->ImplNewFont();
7362     }
7363 
7364     // perform artificial italics if necessary
7365     if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
7366           m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
7367         !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL ||
7368            m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE )
7369         )
7370     {
7371         fSkew = M_PI/12.0;
7372     }
7373 
7374     // if the mapmode is distorted we need to adjust for that also
7375     if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
7376     {
7377         fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
7378     }
7379 
7380     int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
7381     // normalize angles
7382     while( nAngle < 0 )
7383         nAngle += 3600;
7384     nAngle = nAngle % 3600;
7385     double fAngle = (double)nAngle * M_PI / 1800.0;
7386 
7387     Matrix3 aRotScale;
7388     aRotScale.scale( fXScale, 1.0 );
7389     if( fAngle != 0.0 )
7390         aRotScale.rotate( -fAngle );
7391 
7392     bool bPop = false;
7393     bool bABold = false;
7394     // artificial bold necessary ?
7395     if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
7396         m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
7397     {
7398         if( ! bPop )
7399             aLine.append( "q " );
7400         bPop = true;
7401         bABold = true;
7402     }
7403     // setup text colors (if necessary)
7404     Color aStrokeColor( COL_TRANSPARENT );
7405     Color aNonStrokeColor( COL_TRANSPARENT );
7406 
7407     if( m_aCurrentPDFState.m_aFont.IsOutline() )
7408     {
7409         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7410         aNonStrokeColor = Color( COL_WHITE );
7411     }
7412     else
7413         aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7414     if( bABold )
7415         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7416 
7417     if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
7418     {
7419         if( ! bPop )
7420             aLine.append( "q " );
7421         bPop = true;
7422         appendStrokingColor( aStrokeColor, aLine );
7423         aLine.append( "\n" );
7424     }
7425     if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
7426     {
7427         if( ! bPop )
7428             aLine.append( "q " );
7429         bPop = true;
7430         appendNonStrokingColor( aNonStrokeColor, aLine );
7431         aLine.append( "\n" );
7432     }
7433 
7434     // begin text object
7435     aLine.append( "BT\n" );
7436     // outline attribute ?
7437     if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
7438     {
7439         // set correct text mode, set stroke width
7440         aLine.append( "2 Tr " ); // fill, then stroke
7441 
7442         if( m_aCurrentPDFState.m_aFont.IsOutline() )
7443         {
7444             // unclear what to do in case of outline and artificial bold
7445             // for the time being outline wins
7446             aLine.append( "0.25 w \n" );
7447         }
7448         else
7449         {
7450             double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0;
7451             m_aPages.back().appendMappedLength( fW, aLine );
7452             aLine.append ( " w\n" );
7453         }
7454     }
7455 
7456     FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
7457 
7458     // collect the glyphs into a single array
7459     const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
7460     std::vector< PDFGlyph > aGlyphs;
7461     aGlyphs.reserve( nTmpMaxGlyphs );
7462     // first get all the glyphs and register them; coordinates still in Pixel
7463     Point aGNGlyphPos;
7464     while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 )
7465     {
7466         aUnicodes.clear();
7467         for( int i = 0; i < nGlyphs; i++ )
7468         {
7469             pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] );
7470 
7471             // default case: 1 glyph is one unicode
7472             pUnicodesPerGlyph[i] = 1;
7473             if( (pGlyphs[i] & GF_ISCHAR) )
7474             {
7475                 aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) );
7476             }
7477             else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
7478             {
7479                 int nChars = 1;
7480                 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) );
7481                 pUnicodesPerGlyph[i] = 1;
7482                 // try to handle ligatures and such
7483                 if( i < nGlyphs-1 )
7484                 {
7485                     nChars = pCharPosAry[i+1] - pCharPosAry[i];
7486                     // #i115618# fix for simple RTL+CTL cases
7487                     // TODO: sanitize for RTL ligatures, more complex CTL, etc.
7488                     if( nChars < 0 )
7489                         nChars = -nChars;
7490 		    else if( nChars == 0 )
7491                         nChars = 1;
7492                     pUnicodesPerGlyph[i] = nChars;
7493                     for( int n = 1; n < nChars; n++ )
7494                         aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) );
7495                 }
7496                 // #i36691# hack that is needed because currently the pGlyphs[]
7497                 // argument is ignored for embeddable fonts and so the layout
7498                 // engine's glyph work is ignored (i.e. char mirroring)
7499                 // TODO: a real solution would be to map the layout engine's
7500                 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
7501                 // back to unicode and then to embeddable font's encoding
7502                 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL )
7503                 {
7504                     size_t nI = aUnicodes.size()-1;
7505                     for( int n = 0; n < nChars; n++, nI-- )
7506                         aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI]));
7507                 }
7508             }
7509             else
7510                 aUnicodes.push_back( 0 );
7511             // note: in case of ctl one character may result
7512             // in multiple glyphs. The current SalLayout
7513             // implementations set -1 then to indicate that no direct
7514             // mapping is possible
7515         }
7516 
7517         registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
7518 
7519         for( int i = 0; i < nGlyphs; i++ )
7520         {
7521             aGlyphs.push_back( PDFGlyph( aGNGlyphPos,
7522                                          pGlyphWidths[i],
7523                                          pGlyphs[i],
7524                                          pMappedFontObjects[i],
7525                                          pMappedGlyphs[i] ) );
7526             if( bVertical )
7527                 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7528             else
7529                 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7530         }
7531     }
7532 
7533     Point aAlignOffset;
7534     if ( eAlign == ALIGN_BOTTOM )
7535         aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
7536     else if ( eAlign == ALIGN_TOP )
7537         aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
7538     if( aAlignOffset.X() || aAlignOffset.Y() )
7539         aAlignOffset = aRotScale.transform( aAlignOffset );
7540 
7541     /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
7542        string contained only on of the UTF16 BOMs
7543     */
7544     if( ! aGlyphs.empty() )
7545     {
7546         if( bVertical )
7547             drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
7548         else
7549             drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
7550     }
7551 
7552     // end textobject
7553     aLine.append( "ET\n" );
7554     if( bPop )
7555         aLine.append( "Q\n" );
7556 
7557     writeBuffer( aLine.getStr(), aLine.getLength() );
7558 
7559     // draw eventual textlines
7560     FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
7561     FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
7562     FontUnderline eOverline  = m_aCurrentPDFState.m_aFont.GetOverline();
7563     if( bTextLines &&
7564         (
7565          ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) ||
7566          ( eOverline  != UNDERLINE_NONE && eOverline  != UNDERLINE_DONTKNOW ) ||
7567          ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
7568          )
7569         )
7570     {
7571         sal_Bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
7572         if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
7573         {
7574             Point aPos, aStartPt;
7575             sal_Int32 nWidth = 0, nAdvance=0;
7576             for( int nStart = 0;;)
7577             {
7578                 sal_GlyphId aGlyphId;
7579                 if( !rLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) )
7580                     break;
7581 
7582                 if( !rLayout.IsSpacingGlyph( aGlyphId ) )
7583                 {
7584                     if( !nWidth )
7585                         aStartPt = aPos;
7586 
7587                     nWidth += nAdvance;
7588                 }
7589                 else if( nWidth > 0 )
7590                 {
7591                     drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7592                                   m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7593                                   eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7594                     nWidth = 0;
7595                 }
7596             }
7597 
7598             if( nWidth > 0 )
7599             {
7600                 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7601                               m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7602                               eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7603             }
7604         }
7605         else
7606         {
7607             Point aStartPt = rLayout.GetDrawPosition();
7608             int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
7609             drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7610                           m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7611                           eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7612         }
7613     }
7614 
7615     // write eventual emphasis marks
7616     if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
7617     {
7618         PolyPolygon 			aEmphPoly;
7619         Rectangle				aEmphRect1;
7620         Rectangle				aEmphRect2;
7621         long					nEmphYOff;
7622         long					nEmphWidth;
7623         long					nEmphHeight;
7624         sal_Bool					bEmphPolyLine;
7625         FontEmphasisMark		nEmphMark;
7626 
7627         push( PUSH_ALL );
7628 
7629         aLine.setLength( 0 );
7630         aLine.append( "q\n" );
7631 
7632         nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
7633         if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7634             nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
7635         else
7636             nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
7637         m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
7638                                                  bEmphPolyLine,
7639                                                  aEmphRect1,
7640                                                  aEmphRect2,
7641                                                  nEmphYOff,
7642                                                  nEmphWidth,
7643                                                  nEmphMark,
7644                                                  m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight),
7645                                                  m_pReferenceDevice->mpFontEntry->mnOrientation );
7646         if ( bEmphPolyLine )
7647         {
7648             setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
7649             setFillColor( Color( COL_TRANSPARENT ) );
7650         }
7651         else
7652         {
7653             setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
7654             setLineColor( Color( COL_TRANSPARENT ) );
7655         }
7656         writeBuffer( aLine.getStr(), aLine.getLength() );
7657 
7658         Point aOffset = Point(0,0);
7659 
7660         if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7661             aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff;
7662         else
7663             aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff;
7664 
7665         long nEmphWidth2     = nEmphWidth / 2;
7666         long nEmphHeight2    = nEmphHeight / 2;
7667         aOffset += Point( nEmphWidth2, nEmphHeight2 );
7668 
7669         if ( eAlign == ALIGN_BOTTOM )
7670             aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent;
7671         else if ( eAlign == ALIGN_TOP )
7672             aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent;
7673 
7674         for( int nStart = 0;;)
7675         {
7676             Point aPos;
7677             sal_GlyphId aGlyphId;
7678             sal_Int32 nAdvance;
7679             if( !rLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) )
7680                 break;
7681 
7682             if( !rLayout.IsSpacingGlyph( aGlyphId ) )
7683             {
7684                 Point aAdjOffset = aOffset;
7685                 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2;
7686                 aAdjOffset = aRotScale.transform( aAdjOffset );
7687 
7688                 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
7689 
7690                 aPos += aAdjOffset;
7691                 aPos = m_pReferenceDevice->PixelToLogic( aPos );
7692                 drawEmphasisMark( aPos.X(), aPos.Y(),
7693                                   aEmphPoly, bEmphPolyLine,
7694                                   aEmphRect1, aEmphRect2 );
7695             }
7696         }
7697 
7698         writeBuffer( "Q\n", 2 );
7699         pop();
7700     }
7701 
7702 }
7703 
7704 void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
7705                                       const PolyPolygon& rPolyPoly, sal_Bool bPolyLine,
7706                                       const Rectangle& rRect1, const Rectangle& rRect2 )
7707 {
7708     // TODO: pass nWidth as width of this mark
7709     // long nWidth = 0;
7710 
7711     if ( rPolyPoly.Count() )
7712     {
7713         if ( bPolyLine )
7714         {
7715             Polygon aPoly = rPolyPoly.GetObject( 0 );
7716             aPoly.Move( nX, nY );
7717             drawPolyLine( aPoly );
7718         }
7719         else
7720         {
7721             PolyPolygon aPolyPoly = rPolyPoly;
7722             aPolyPoly.Move( nX, nY );
7723             drawPolyPolygon( aPolyPoly );
7724         }
7725     }
7726 
7727     if ( !rRect1.IsEmpty() )
7728     {
7729         Rectangle aRect( Point( nX+rRect1.Left(),
7730                                 nY+rRect1.Top() ), rRect1.GetSize() );
7731         drawRectangle( aRect );
7732     }
7733 
7734     if ( !rRect2.IsEmpty() )
7735     {
7736         Rectangle aRect( Point( nX+rRect2.Left(),
7737                                 nY+rRect2.Top() ), rRect2.GetSize() );
7738 
7739         drawRectangle( aRect );
7740     }
7741 }
7742 
7743 void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7744 {
7745     MARK( "drawText" );
7746 
7747     updateGraphicsState();
7748 
7749     // get a layout from the OuputDevice's SalGraphics
7750     // this also enforces font substitution and sets the font on SalGraphics
7751     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
7752     if( pLayout )
7753     {
7754         drawLayout( *pLayout, rText, bTextLines );
7755         pLayout->Release();
7756     }
7757 }
7758 
7759 void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7760 {
7761     MARK( "drawText with array" );
7762 
7763     updateGraphicsState();
7764 
7765     // get a layout from the OuputDevice's SalGraphics
7766     // this also enforces font substitution and sets the font on SalGraphics
7767     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
7768     if( pLayout )
7769     {
7770         drawLayout( *pLayout, rText, bTextLines );
7771         pLayout->Release();
7772     }
7773 }
7774 
7775 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7776 {
7777     MARK( "drawStretchText" );
7778 
7779     updateGraphicsState();
7780 
7781     // get a layout from the OuputDevice's SalGraphics
7782     // this also enforces font substitution and sets the font on SalGraphics
7783     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
7784     if( pLayout )
7785     {
7786         drawLayout( *pLayout, rText, bTextLines );
7787         pLayout->Release();
7788     }
7789 }
7790 
7791 void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines )
7792 {
7793     long        nWidth          = rRect.GetWidth();
7794     long        nHeight         = rRect.GetHeight();
7795 
7796     if ( nWidth <= 0 || nHeight <= 0 )
7797         return;
7798 
7799     MARK( "drawText with rectangle" );
7800 
7801     updateGraphicsState();
7802 
7803     // clip with rectangle
7804     OStringBuffer aLine;
7805     aLine.append( "q " );
7806     m_aPages.back().appendRect( rRect, aLine );
7807     aLine.append( " W* n\n" );
7808     writeBuffer( aLine.getStr(), aLine.getLength() );
7809 
7810     // if disabled text is needed, put in here
7811 
7812     Point       aPos            = rRect.TopLeft();
7813 
7814     long		nTextHeight		= m_pReferenceDevice->GetTextHeight();
7815     xub_StrLen  nMnemonicPos    = STRING_NOTFOUND;
7816 
7817     String aStr = rOrigStr;
7818     if ( nStyle & TEXT_DRAW_MNEMONIC )
7819         aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos );
7820 
7821     // multiline text
7822     if ( nStyle & TEXT_DRAW_MULTILINE )
7823     {
7824         XubString               aLastLine;
7825         ImplMultiTextLineInfo   aMultiLineInfo;
7826         ImplTextLineInfo*       pLineInfo;
7827         long                    nMaxTextWidth;
7828         xub_StrLen              i;
7829         xub_StrLen              nLines;
7830         xub_StrLen              nFormatLines;
7831 
7832         if ( nTextHeight )
7833         {
7834             ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice );
7835             nMaxTextWidth = OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout );
7836             nLines = (xub_StrLen)(nHeight/nTextHeight);
7837             nFormatLines = aMultiLineInfo.Count();
7838             if ( !nLines )
7839                 nLines = 1;
7840             if ( nFormatLines > nLines )
7841             {
7842                 if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
7843                 {
7844                     // handle last line
7845                     nFormatLines = nLines-1;
7846 
7847                     pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
7848                     aLastLine = aStr.Copy( pLineInfo->GetIndex() );
7849                     aLastLine.ConvertLineEnd( LINEEND_LF );
7850                     // replace line feed by space
7851                     xub_StrLen nLastLineLen = aLastLine.Len();
7852                     for ( i = 0; i < nLastLineLen; i++ )
7853                     {
7854                         if ( aLastLine.GetChar( i ) == _LF )
7855                             aLastLine.SetChar( i, ' ' );
7856                     }
7857                     aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
7858                     nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
7859                     nStyle |= TEXT_DRAW_TOP;
7860                 }
7861             }
7862 
7863             // vertical alignment
7864             if ( nStyle & TEXT_DRAW_BOTTOM )
7865                 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
7866             else if ( nStyle & TEXT_DRAW_VCENTER )
7867                 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
7868 
7869             // draw all lines excluding the last
7870             for ( i = 0; i < nFormatLines; i++ )
7871             {
7872                 pLineInfo = aMultiLineInfo.GetLine( i );
7873                 if ( nStyle & TEXT_DRAW_RIGHT )
7874                     aPos.X() += nWidth-pLineInfo->GetWidth();
7875                 else if ( nStyle & TEXT_DRAW_CENTER )
7876                     aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
7877                 xub_StrLen nIndex   = pLineInfo->GetIndex();
7878                 xub_StrLen nLineLen = pLineInfo->GetLen();
7879                 drawText( aPos, aStr, nIndex, nLineLen, bTextLines );
7880                 // mnemonics should not appear in documents,
7881                 // if the need arises, put them in here
7882                 aPos.Y() += nTextHeight;
7883                 aPos.X() = rRect.Left();
7884             }
7885 
7886 
7887             // output last line left adjusted since it was shortened
7888             if ( aLastLine.Len() )
7889                 drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines );
7890         }
7891     }
7892     else
7893     {
7894         long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7895 
7896         // Evt. Text kuerzen
7897         if ( nTextWidth > nWidth )
7898         {
7899             if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) )
7900             {
7901                 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
7902                 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
7903                 nStyle |= TEXT_DRAW_LEFT;
7904                 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7905             }
7906         }
7907 
7908         // vertical alignment
7909         if ( nStyle & TEXT_DRAW_RIGHT )
7910             aPos.X() += nWidth-nTextWidth;
7911         else if ( nStyle & TEXT_DRAW_CENTER )
7912             aPos.X() += (nWidth-nTextWidth)/2;
7913 
7914         if ( nStyle & TEXT_DRAW_BOTTOM )
7915             aPos.Y() += nHeight-nTextHeight;
7916         else if ( nStyle & TEXT_DRAW_VCENTER )
7917             aPos.Y() += (nHeight-nTextHeight)/2;
7918 
7919         // mnemonics should be inserted here if the need arises
7920 
7921         // draw the actual text
7922         drawText( aPos, aStr, 0, STRING_LEN, bTextLines );
7923     }
7924 
7925     // reset clip region to original value
7926     aLine.setLength( 0 );
7927     aLine.append( "Q\n" );
7928     writeBuffer( aLine.getStr(), aLine.getLength() );
7929 }
7930 
7931 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
7932 {
7933     MARK( "drawLine" );
7934 
7935     updateGraphicsState();
7936 
7937     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7938         return;
7939 
7940     OStringBuffer aLine;
7941     m_aPages.back().appendPoint( rStart, aLine );
7942     aLine.append( " m " );
7943     m_aPages.back().appendPoint( rStop, aLine );
7944     aLine.append( " l S\n" );
7945 
7946     writeBuffer( aLine.getStr(), aLine.getLength() );
7947 }
7948 
7949 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
7950 {
7951     MARK( "drawLine with LineInfo" );
7952     updateGraphicsState();
7953 
7954     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7955         return;
7956 
7957     if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 )
7958     {
7959         drawLine( rStart, rStop );
7960         return;
7961     }
7962 
7963     OStringBuffer aLine;
7964 
7965     aLine.append( "q " );
7966     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
7967     {
7968         m_aPages.back().appendPoint( rStart, aLine );
7969         aLine.append( " m " );
7970         m_aPages.back().appendPoint( rStop, aLine );
7971         aLine.append( " l S Q\n" );
7972 
7973         writeBuffer( aLine.getStr(), aLine.getLength() );
7974     }
7975     else
7976     {
7977         PDFWriter::ExtLineInfo aInfo;
7978         convertLineInfoToExtLineInfo( rInfo, aInfo );
7979         Point aPolyPoints[2] = { rStart, rStop };
7980         Polygon aPoly( 2, aPolyPoints );
7981         drawPolyLine( aPoly, aInfo );
7982     }
7983 }
7984 
7985 void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth )
7986 {
7987     Point aDiff( rStop-rStart );
7988     double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) );
7989     if( fLen < 1.0 )
7990         return;
7991 
7992     MARK( "drawWaveLine" );
7993     updateGraphicsState();
7994 
7995     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7996         return;
7997 
7998     OStringBuffer aLine( 512 );
7999     aLine.append( "q " );
8000     m_aPages.back().appendMappedLength( nLineWidth, aLine, true );
8001     aLine.append( " w " );
8002 
8003     appendDouble( (double)aDiff.X()/fLen, aLine );
8004     aLine.append( ' ' );
8005     appendDouble( -(double)aDiff.Y()/fLen, aLine );
8006     aLine.append( ' ' );
8007     appendDouble( (double)aDiff.Y()/fLen, aLine );
8008     aLine.append( ' ' );
8009     appendDouble( (double)aDiff.X()/fLen, aLine );
8010     aLine.append( ' ' );
8011     m_aPages.back().appendPoint( rStart, aLine );
8012     aLine.append( " cm " );
8013     m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine );
8014     aLine.append( "Q\n" );
8015     writeBuffer( aLine.getStr(), aLine.getLength() );
8016 }
8017 
8018 #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x )
8019 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
8020 
8021 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8022 {
8023     // note: units in pFontEntry are ref device pixel
8024     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8025     long			nLineHeight = 0;
8026     long			nLinePos = 0;
8027 
8028     appendStrokingColor( aColor, aLine );
8029     aLine.append( "\n" );
8030 
8031     if ( bIsAbove )
8032     {
8033         if ( !pFontEntry->maMetric.mnAboveWUnderlineSize )
8034             m_pReferenceDevice->ImplInitAboveTextLineSize();
8035         nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize );
8036         nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset );
8037     }
8038     else
8039     {
8040         if ( !pFontEntry->maMetric.mnWUnderlineSize )
8041             m_pReferenceDevice->ImplInitTextLineSize();
8042         nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize );
8043         nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset );
8044     }
8045     if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
8046         nLineHeight = 3;
8047 
8048     long nLineWidth = getReferenceDevice()->mnDPIX/450;
8049     if ( ! nLineWidth )
8050         nLineWidth = 1;
8051 
8052     if ( eTextLine == UNDERLINE_BOLDWAVE )
8053         nLineWidth = 3*nLineWidth;
8054 
8055     m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
8056     aLine.append( " w " );
8057 
8058     if ( eTextLine == UNDERLINE_DOUBLEWAVE )
8059     {
8060         long nOrgLineHeight = nLineHeight;
8061         nLineHeight /= 3;
8062         if ( nLineHeight < 2 )
8063         {
8064             if ( nOrgLineHeight > 1 )
8065                 nLineHeight = 2;
8066             else
8067                 nLineHeight = 1;
8068         }
8069         long nLineDY = nOrgLineHeight-(nLineHeight*2);
8070         if ( nLineDY < nLineWidth )
8071             nLineDY = nLineWidth;
8072         long nLineDY2 = nLineDY/2;
8073         if ( !nLineDY2 )
8074             nLineDY2 = 1;
8075 
8076         nLinePos -= nLineWidth-nLineDY2;
8077 
8078         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8079 
8080         nLinePos += nLineWidth+nLineDY;
8081         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8082     }
8083     else
8084     {
8085         if ( eTextLine != UNDERLINE_BOLDWAVE )
8086             nLinePos -= nLineWidth/2;
8087         m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
8088     }
8089 }
8090 
8091 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8092 {
8093     // note: units in pFontEntry are ref device pixel
8094     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8095     long			nLineHeight = 0;
8096     long			nLinePos  = 0;
8097     long			nLinePos2 = 0;
8098 
8099     if ( eTextLine > UNDERLINE_BOLDWAVE )
8100         eTextLine = UNDERLINE_SINGLE;
8101 
8102     switch ( eTextLine )
8103     {
8104         case UNDERLINE_SINGLE:
8105         case UNDERLINE_DOTTED:
8106         case UNDERLINE_DASH:
8107         case UNDERLINE_LONGDASH:
8108         case UNDERLINE_DASHDOT:
8109         case UNDERLINE_DASHDOTDOT:
8110             if ( bIsAbove )
8111             {
8112                 if ( !pFontEntry->maMetric.mnAboveUnderlineSize )
8113                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8114                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize );
8115                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset );
8116             }
8117             else
8118             {
8119                 if ( !pFontEntry->maMetric.mnUnderlineSize )
8120                     m_pReferenceDevice->ImplInitTextLineSize();
8121                 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize );
8122                 nLinePos    = HCONV( pFontEntry->maMetric.mnUnderlineOffset );
8123             }
8124             break;
8125         case UNDERLINE_BOLD:
8126         case UNDERLINE_BOLDDOTTED:
8127         case UNDERLINE_BOLDDASH:
8128         case UNDERLINE_BOLDLONGDASH:
8129         case UNDERLINE_BOLDDASHDOT:
8130         case UNDERLINE_BOLDDASHDOTDOT:
8131             if ( bIsAbove )
8132             {
8133                 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize )
8134                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8135                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize );
8136                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset );
8137             }
8138             else
8139             {
8140                 if ( !pFontEntry->maMetric.mnBUnderlineSize )
8141                     m_pReferenceDevice->ImplInitTextLineSize();
8142                 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize );
8143                 nLinePos    = HCONV( pFontEntry->maMetric.mnBUnderlineOffset );
8144                 nLinePos += nLineHeight/2;
8145             }
8146             break;
8147         case UNDERLINE_DOUBLE:
8148             if ( bIsAbove )
8149             {
8150                 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize )
8151                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8152                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize );
8153                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 );
8154                 nLinePos2   = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 );
8155             }
8156             else
8157             {
8158                 if ( !pFontEntry->maMetric.mnDUnderlineSize )
8159                     m_pReferenceDevice->ImplInitTextLineSize();
8160                 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize );
8161                 nLinePos    = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 );
8162                 nLinePos2   = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 );
8163             }
8164         default:
8165             break;
8166     }
8167 
8168     if ( nLineHeight )
8169     {
8170         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8171         aLine.append( " w " );
8172         appendStrokingColor( aColor, aLine );
8173         aLine.append( "\n" );
8174 
8175         switch ( eTextLine )
8176         {
8177             case UNDERLINE_DOTTED:
8178             case UNDERLINE_BOLDDOTTED:
8179                 aLine.append( "[ " );
8180                 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8181                 aLine.append( " ] 0 d\n" );
8182                 break;
8183             case UNDERLINE_DASH:
8184             case UNDERLINE_LONGDASH:
8185             case UNDERLINE_BOLDDASH:
8186             case UNDERLINE_BOLDLONGDASH:
8187                 {
8188                     sal_Int32 nDashLength = 4*nLineHeight;
8189                     sal_Int32 nVoidLength = 2*nLineHeight;
8190                     if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) )
8191                         nDashLength = 8*nLineHeight;
8192 
8193                     aLine.append( "[ " );
8194                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8195                     aLine.append( ' ' );
8196                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8197                     aLine.append( " ] 0 d\n" );
8198                 }
8199                 break;
8200             case UNDERLINE_DASHDOT:
8201             case UNDERLINE_BOLDDASHDOT:
8202                 {
8203                     sal_Int32 nDashLength = 4*nLineHeight;
8204                     sal_Int32 nVoidLength = 2*nLineHeight;
8205                     aLine.append( "[ " );
8206                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8207                     aLine.append( ' ' );
8208                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8209                     aLine.append( ' ' );
8210                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8211                     aLine.append( ' ' );
8212                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8213                     aLine.append( " ] 0 d\n" );
8214                 }
8215                 break;
8216             case UNDERLINE_DASHDOTDOT:
8217             case UNDERLINE_BOLDDASHDOTDOT:
8218                 {
8219                     sal_Int32 nDashLength = 4*nLineHeight;
8220                     sal_Int32 nVoidLength = 2*nLineHeight;
8221                     aLine.append( "[ " );
8222                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8223                     aLine.append( ' ' );
8224                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8225                     aLine.append( ' ' );
8226                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8227                     aLine.append( ' ' );
8228                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8229                     aLine.append( ' ' );
8230                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8231                     aLine.append( ' ' );
8232                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8233                     aLine.append( " ] 0 d\n" );
8234                 }
8235                 break;
8236             default:
8237                 break;
8238         }
8239 
8240         aLine.append( "0 " );
8241         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8242         aLine.append( " m " );
8243         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8244         aLine.append( ' ' );
8245         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8246         aLine.append( " l S\n" );
8247         if ( eTextLine == UNDERLINE_DOUBLE )
8248         {
8249             aLine.append( "0 " );
8250             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8251             aLine.append( " m " );
8252             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8253             aLine.append( ' ' );
8254             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8255             aLine.append( " l S\n" );
8256         }
8257     }
8258 }
8259 
8260 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
8261 {
8262     // note: units in pFontEntry are ref device pixel
8263     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8264     long			nLineHeight = 0;
8265     long			nLinePos  = 0;
8266     long			nLinePos2 = 0;
8267 
8268     if ( eStrikeout > STRIKEOUT_X )
8269         eStrikeout = STRIKEOUT_SINGLE;
8270 
8271     switch ( eStrikeout )
8272     {
8273         case STRIKEOUT_SINGLE:
8274             if ( !pFontEntry->maMetric.mnStrikeoutSize )
8275                 m_pReferenceDevice->ImplInitTextLineSize();
8276             nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize );
8277             nLinePos    = HCONV( pFontEntry->maMetric.mnStrikeoutOffset );
8278             break;
8279         case STRIKEOUT_BOLD:
8280             if ( !pFontEntry->maMetric.mnBStrikeoutSize )
8281                 m_pReferenceDevice->ImplInitTextLineSize();
8282             nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize );
8283             nLinePos    = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset );
8284             break;
8285         case STRIKEOUT_DOUBLE:
8286             if ( !pFontEntry->maMetric.mnDStrikeoutSize )
8287                 m_pReferenceDevice->ImplInitTextLineSize();
8288             nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize );
8289             nLinePos    = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 );
8290             nLinePos2   = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 );
8291             break;
8292         default:
8293             break;
8294     }
8295 
8296     if ( nLineHeight )
8297     {
8298         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8299         aLine.append( " w " );
8300         appendStrokingColor( aColor, aLine );
8301         aLine.append( "\n" );
8302 
8303         aLine.append( "0 " );
8304         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8305         aLine.append( " m " );
8306         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8307         aLine.append( ' ' );
8308         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8309         aLine.append( " l S\n" );
8310 
8311         if ( eStrikeout == STRIKEOUT_DOUBLE )
8312         {
8313             aLine.append( "0 " );
8314             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8315             aLine.append( " m " );
8316             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8317             aLine.append( ' ' );
8318             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8319             aLine.append( " l S\n" );
8320         }
8321     }
8322 }
8323 
8324 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
8325 {
8326     String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" );
8327     String aStrikeout = aStrikeoutChar;
8328     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
8329         aStrikeout.Append( aStrikeout );
8330 
8331     // do not get broader than nWidth modulo 1 character
8332     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
8333         aStrikeout.Erase( 0, 1 );
8334     aStrikeout.Append( aStrikeoutChar );
8335     sal_Bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
8336     if ( bShadow )
8337     {
8338         Font aFont = m_aCurrentPDFState.m_aFont;
8339         aFont.SetShadow( sal_False );
8340         setFont( aFont );
8341         updateGraphicsState();
8342     }
8343 
8344     // strikeout string is left aligned non-CTL text
8345     sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode();
8346     m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED );
8347     drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false );
8348     m_pReferenceDevice->SetLayoutMode( nOrigTLM );
8349 
8350     if ( bShadow )
8351     {
8352         Font aFont = m_aCurrentPDFState.m_aFont;
8353         aFont.SetShadow( sal_True );
8354         setFont( aFont );
8355         updateGraphicsState();
8356     }
8357 }
8358 
8359 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove )
8360 {
8361     if ( !nWidth ||
8362          ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
8363            ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) &&
8364            ((eOverline  == UNDERLINE_NONE)||(eOverline  == UNDERLINE_DONTKNOW)) ) )
8365         return;
8366 
8367     MARK( "drawTextLine" );
8368     updateGraphicsState();
8369 
8370     // note: units in pFontEntry are ref device pixel
8371     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8372     Color			aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
8373     Color			aOverlineColor  = m_aCurrentPDFState.m_aOverlineColor;
8374     Color			aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
8375     bool			bStrikeoutDone = false;
8376     bool			bUnderlineDone = false;
8377     bool			bOverlineDone  = false;
8378 
8379     if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
8380     {
8381         drawStrikeoutChar( rPos, nWidth, eStrikeout );
8382         bStrikeoutDone = true;
8383     }
8384 
8385     Point aPos( rPos );
8386     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
8387     if( eAlign == ALIGN_TOP )
8388         aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent );
8389     else if( eAlign == ALIGN_BOTTOM )
8390         aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent );
8391 
8392     OStringBuffer aLine( 512 );
8393     // save GS
8394     aLine.append( "q " );
8395 
8396     // rotate and translate matrix
8397     double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
8398     Matrix3 aMat;
8399     aMat.rotate( fAngle );
8400     aMat.translate( aPos.X(), aPos.Y() );
8401     aMat.append( m_aPages.back(), aLine );
8402     aLine.append( " cm\n" );
8403 
8404     if ( aUnderlineColor.GetTransparency() != 0 )
8405         aUnderlineColor = aStrikeoutColor;
8406 
8407     if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
8408          (eUnderline == UNDERLINE_WAVE) ||
8409          (eUnderline == UNDERLINE_DOUBLEWAVE) ||
8410          (eUnderline == UNDERLINE_BOLDWAVE) )
8411     {
8412         drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8413         bUnderlineDone = true;
8414     }
8415 
8416     if ( (eOverline == UNDERLINE_SMALLWAVE) ||
8417          (eOverline == UNDERLINE_WAVE) ||
8418          (eOverline == UNDERLINE_DOUBLEWAVE) ||
8419          (eOverline == UNDERLINE_BOLDWAVE) )
8420     {
8421         drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8422         bOverlineDone = true;
8423     }
8424 
8425     if ( !bUnderlineDone )
8426     {
8427         drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8428     }
8429 
8430     if ( !bOverlineDone )
8431     {
8432         drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8433     }
8434 
8435     if ( !bStrikeoutDone )
8436     {
8437         drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
8438     }
8439 
8440     aLine.append( "Q\n" );
8441     writeBuffer( aLine.getStr(), aLine.getLength() );
8442 }
8443 
8444 void PDFWriterImpl::drawPolygon( const Polygon& rPoly )
8445 {
8446     MARK( "drawPolygon" );
8447 
8448     updateGraphicsState();
8449 
8450     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8451         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8452         return;
8453 
8454     int nPoints = rPoly.GetSize();
8455     OStringBuffer aLine( 20 * nPoints );
8456     m_aPages.back().appendPolygon( rPoly, aLine );
8457     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8458         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8459         aLine.append( "B*\n" );
8460     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8461         aLine.append( "S\n" );
8462     else
8463         aLine.append( "f*\n" );
8464 
8465     writeBuffer( aLine.getStr(), aLine.getLength() );
8466 }
8467 
8468 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly )
8469 {
8470     MARK( "drawPolyPolygon" );
8471 
8472     updateGraphicsState();
8473 
8474     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8475         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8476         return;
8477 
8478     int nPolygons = rPolyPoly.Count();
8479 
8480     OStringBuffer aLine( 40 * nPolygons );
8481     m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
8482     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8483         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8484         aLine.append( "B*\n" );
8485     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8486         aLine.append( "S\n" );
8487     else
8488         aLine.append( "f*\n" );
8489 
8490     writeBuffer( aLine.getStr(), aLine.getLength() );
8491 }
8492 
8493 void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
8494 {
8495     DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8496     nTransparentPercent = nTransparentPercent % 100;
8497 
8498     MARK( "drawTransparent" );
8499 
8500     updateGraphicsState();
8501 
8502     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8503         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8504         return;
8505 
8506     if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 )
8507     {
8508         m_aErrors.insert( m_bIsPDF_A1 ?
8509                           PDFWriter::Warning_Transparency_Omitted_PDFA :
8510                           PDFWriter::Warning_Transparency_Omitted_PDF13 );
8511 
8512         drawPolyPolygon( rPolyPoly );
8513         return;
8514     }
8515 
8516     // create XObject
8517     m_aTransparentObjects.push_back( TransparencyEmit() );
8518     // FIXME: polygons with beziers may yield incorrect bound rect
8519     m_aTransparentObjects.back().m_aBoundRect	  = rPolyPoly.GetBoundRect();
8520     // convert rectangle to default user space
8521     m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8522     m_aTransparentObjects.back().m_nObject		    = createObject();
8523     m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8524     m_aTransparentObjects.back().m_fAlpha		    = (double)(100-nTransparentPercent) / 100.0;
8525     m_aTransparentObjects.back().m_pContentStream   = new SvMemoryStream( 256, 256 );
8526     // create XObject's content stream
8527     OStringBuffer aContent( 256 );
8528     m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
8529     if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
8530         m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
8531         aContent.append( " B*\n" );
8532     else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
8533         aContent.append( " S\n" );
8534     else
8535         aContent.append( " f*\n" );
8536     m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() );
8537 
8538     OStringBuffer aObjName( 16 );
8539     aObjName.append( "Tr" );
8540     aObjName.append( m_aTransparentObjects.back().m_nObject );
8541     OString aTrName( aObjName.makeStringAndClear() );
8542     aObjName.append( "EGS" );
8543     aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8544     OString aExtName( aObjName.makeStringAndClear() );
8545 
8546     OStringBuffer aLine( 80 );
8547     // insert XObject
8548     aLine.append( "q /" );
8549     aLine.append( aExtName );
8550     aLine.append( " gs /" );
8551     aLine.append( aTrName );
8552     aLine.append( " Do Q\n" );
8553     writeBuffer( aLine.getStr(), aLine.getLength() );
8554 
8555     pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8556     pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8557 }
8558 
8559 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
8560 {
8561     if( nObject >= 0 )
8562     {
8563         switch( eKind )
8564         {
8565             case ResXObject:
8566                 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
8567                 if( ! m_aOutputStreams.empty() )
8568                     m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
8569                 break;
8570             case ResExtGState:
8571                 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
8572                 if( ! m_aOutputStreams.empty() )
8573                     m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
8574                 break;
8575             case ResShading:
8576                 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
8577                 if( ! m_aOutputStreams.empty() )
8578                     m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
8579                 break;
8580             case ResPattern:
8581                 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
8582                 if( ! m_aOutputStreams.empty() )
8583                     m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
8584                 break;
8585         }
8586     }
8587 }
8588 
8589 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect )
8590 {
8591     push( PUSH_ALL );
8592 
8593     // force reemitting clip region
8594     clearClipRegion();
8595     updateGraphicsState();
8596 
8597     m_aOutputStreams.push_front( StreamRedirect() );
8598     m_aOutputStreams.front().m_pStream = pStream;
8599     m_aOutputStreams.front().m_aMapMode = m_aMapMode;
8600 
8601     if( !rTargetRect.IsEmpty() )
8602     {
8603         m_aOutputStreams.front().m_aTargetRect =
8604             lcl_convert( m_aGraphicsStack.front().m_aMapMode,
8605                          m_aMapMode,
8606                          getReferenceDevice(),
8607                          rTargetRect );
8608         Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
8609         long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
8610         aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
8611         m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
8612     }
8613 
8614     // setup graphics state for independent object stream
8615 
8616     // force reemitting colors
8617     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8618     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8619 }
8620 
8621 Rectangle PDFWriterImpl::getRedirectTargetRect() const
8622 {
8623     return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect;
8624 }
8625 
8626 SvStream* PDFWriterImpl::endRedirect()
8627 {
8628     SvStream* pStream = NULL;
8629     if( ! m_aOutputStreams.empty() )
8630     {
8631         pStream		= m_aOutputStreams.front().m_pStream;
8632         m_aMapMode	= m_aOutputStreams.front().m_aMapMode;
8633         m_aOutputStreams.pop_front();
8634     }
8635 
8636     pop();
8637     // force reemitting colors and clip region
8638     clearClipRegion();
8639     m_aCurrentPDFState.m_bClipRegion = m_aGraphicsStack.front().m_bClipRegion;
8640     m_aCurrentPDFState.m_aClipRegion = m_aGraphicsStack.front().m_aClipRegion;
8641     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8642     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8643 
8644     updateGraphicsState();
8645 
8646     return pStream;
8647 }
8648 
8649 void PDFWriterImpl::beginTransparencyGroup()
8650 {
8651     updateGraphicsState();
8652     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8653         beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8654 }
8655 
8656 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
8657 {
8658     DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8659     nTransparentPercent = nTransparentPercent % 100;
8660 
8661     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8662     {
8663         // create XObject
8664         m_aTransparentObjects.push_back( TransparencyEmit() );
8665         m_aTransparentObjects.back().m_aBoundRect	= rBoundingBox;
8666         // convert rectangle to default user space
8667         m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8668         m_aTransparentObjects.back().m_nObject		= createObject();
8669         m_aTransparentObjects.back().m_fAlpha		= (double)(100-nTransparentPercent) / 100.0;
8670         // get XObject's content stream
8671         m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8672         m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8673 
8674         OStringBuffer aObjName( 16 );
8675         aObjName.append( "Tr" );
8676         aObjName.append( m_aTransparentObjects.back().m_nObject );
8677         OString aTrName( aObjName.makeStringAndClear() );
8678         aObjName.append( "EGS" );
8679         aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8680         OString aExtName( aObjName.makeStringAndClear() );
8681 
8682         OStringBuffer aLine( 80 );
8683         // insert XObject
8684         aLine.append( "q /" );
8685         aLine.append( aExtName );
8686         aLine.append( " gs /" );
8687         aLine.append( aTrName );
8688         aLine.append( " Do Q\n" );
8689         writeBuffer( aLine.getStr(), aLine.getLength() );
8690 
8691         pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8692         pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8693     }
8694 }
8695 
8696 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask )
8697 {
8698     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8699     {
8700         // create XObject
8701         m_aTransparentObjects.push_back( TransparencyEmit() );
8702         m_aTransparentObjects.back().m_aBoundRect	= rBoundingBox;
8703         // convert rectangle to default user space
8704         m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8705         m_aTransparentObjects.back().m_nObject		= createObject();
8706         m_aTransparentObjects.back().m_fAlpha		= 0.0;
8707         // get XObject's content stream
8708         m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8709         m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8710 
8711         // draw soft mask
8712         beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8713         drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask );
8714         m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect());
8715 
8716         OStringBuffer aObjName( 16 );
8717         aObjName.append( "Tr" );
8718         aObjName.append( m_aTransparentObjects.back().m_nObject );
8719         OString aTrName( aObjName.makeStringAndClear() );
8720         aObjName.append( "EGS" );
8721         aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8722         OString aExtName( aObjName.makeStringAndClear() );
8723 
8724         OStringBuffer aLine( 80 );
8725         // insert XObject
8726         aLine.append( "q /" );
8727         aLine.append( aExtName );
8728         aLine.append( " gs /" );
8729         aLine.append( aTrName );
8730         aLine.append( " Do Q\n" );
8731         writeBuffer( aLine.getStr(), aLine.getLength() );
8732 
8733         pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8734         pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8735     }
8736 }
8737 
8738 void PDFWriterImpl::drawRectangle( const Rectangle& rRect )
8739 {
8740     MARK( "drawRectangle" );
8741 
8742     updateGraphicsState();
8743 
8744     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8745         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8746         return;
8747 
8748     OStringBuffer aLine( 40 );
8749     m_aPages.back().appendRect( rRect, aLine );
8750 
8751     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8752         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8753         aLine.append( " B*\n" );
8754     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8755         aLine.append( " S\n" );
8756     else
8757         aLine.append( " f*\n" );
8758 
8759     writeBuffer( aLine.getStr(), aLine.getLength() );
8760 }
8761 
8762 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
8763 {
8764     MARK( "drawRectangle with rounded edges" );
8765 
8766     if( !nHorzRound && !nVertRound )
8767         drawRectangle( rRect );
8768 
8769     updateGraphicsState();
8770 
8771     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8772         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8773         return;
8774 
8775     if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
8776         nHorzRound = rRect.GetWidth()/2;
8777     if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
8778         nVertRound = rRect.GetHeight()/2;
8779 
8780     Point aPoints[16];
8781     const double kappa = 0.5522847498;
8782     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
8783     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
8784 
8785     aPoints[1]  = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
8786     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8787     aPoints[2]  = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
8788     aPoints[3]  = Point( aPoints[2].X()+kx, aPoints[2].Y() );
8789 
8790     aPoints[5]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
8791     aPoints[4]  = Point( aPoints[5].X(), aPoints[5].Y()-ky );
8792     aPoints[6]  = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
8793     aPoints[7]  = Point( aPoints[6].X(), aPoints[6].Y()+ky );
8794 
8795     aPoints[9]  = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
8796     aPoints[8]  = Point( aPoints[9].X()+kx, aPoints[9].Y() );
8797     aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
8798     aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
8799 
8800     aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
8801     aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
8802     aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
8803     aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
8804 
8805 
8806     OStringBuffer aLine( 80 );
8807     m_aPages.back().appendPoint( aPoints[1], aLine );
8808     aLine.append( " m " );
8809     m_aPages.back().appendPoint( aPoints[2], aLine );
8810     aLine.append( " l " );
8811     m_aPages.back().appendPoint( aPoints[3], aLine );
8812     aLine.append( ' ' );
8813     m_aPages.back().appendPoint( aPoints[4], aLine );
8814     aLine.append( ' ' );
8815     m_aPages.back().appendPoint( aPoints[5], aLine );
8816     aLine.append( " c\n" );
8817     m_aPages.back().appendPoint( aPoints[6], aLine );
8818     aLine.append( " l " );
8819     m_aPages.back().appendPoint( aPoints[7], aLine );
8820     aLine.append( ' ' );
8821     m_aPages.back().appendPoint( aPoints[8], aLine );
8822     aLine.append( ' ' );
8823     m_aPages.back().appendPoint( aPoints[9], aLine );
8824     aLine.append( " c\n" );
8825     m_aPages.back().appendPoint( aPoints[10], aLine );
8826     aLine.append( " l " );
8827     m_aPages.back().appendPoint( aPoints[11], aLine );
8828     aLine.append( ' ' );
8829     m_aPages.back().appendPoint( aPoints[12], aLine );
8830     aLine.append( ' ' );
8831     m_aPages.back().appendPoint( aPoints[13], aLine );
8832     aLine.append( " c\n" );
8833     m_aPages.back().appendPoint( aPoints[14], aLine );
8834     aLine.append( " l " );
8835     m_aPages.back().appendPoint( aPoints[15], aLine );
8836     aLine.append( ' ' );
8837     m_aPages.back().appendPoint( aPoints[0], aLine );
8838     aLine.append( ' ' );
8839     m_aPages.back().appendPoint( aPoints[1], aLine );
8840     aLine.append( " c " );
8841 
8842     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8843         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8844         aLine.append( "b*\n" );
8845     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8846         aLine.append( "s\n" );
8847     else
8848         aLine.append( "f*\n" );
8849 
8850     writeBuffer( aLine.getStr(), aLine.getLength() );
8851 }
8852 
8853 void PDFWriterImpl::drawEllipse( const Rectangle& rRect )
8854 {
8855     MARK( "drawEllipse" );
8856 
8857     updateGraphicsState();
8858 
8859     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8860         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8861         return;
8862 
8863     Point aPoints[12];
8864     const double kappa = 0.5522847498;
8865     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
8866     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
8867 
8868     aPoints[1]  = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
8869     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8870     aPoints[2]  = Point( aPoints[1].X() + kx, aPoints[1].Y() );
8871 
8872     aPoints[4]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
8873     aPoints[3]  = Point( aPoints[4].X(), aPoints[4].Y() - ky );
8874     aPoints[5]  = Point( aPoints[4].X(), aPoints[4].Y() + ky );
8875 
8876     aPoints[7]  = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
8877     aPoints[6]  = Point( aPoints[7].X() + kx, aPoints[7].Y() );
8878     aPoints[8]  = Point( aPoints[7].X() - kx, aPoints[7].Y() );
8879 
8880     aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
8881     aPoints[9]  = Point( aPoints[10].X(), aPoints[10].Y() + ky );
8882     aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
8883 
8884     OStringBuffer aLine( 80 );
8885     m_aPages.back().appendPoint( aPoints[1], aLine );
8886     aLine.append( " m " );
8887     m_aPages.back().appendPoint( aPoints[2], aLine );
8888     aLine.append( ' ' );
8889     m_aPages.back().appendPoint( aPoints[3], aLine );
8890     aLine.append( ' ' );
8891     m_aPages.back().appendPoint( aPoints[4], aLine );
8892     aLine.append( " c\n" );
8893     m_aPages.back().appendPoint( aPoints[5], aLine );
8894     aLine.append( ' ' );
8895     m_aPages.back().appendPoint( aPoints[6], aLine );
8896     aLine.append( ' ' );
8897     m_aPages.back().appendPoint( aPoints[7], aLine );
8898     aLine.append( " c\n" );
8899     m_aPages.back().appendPoint( aPoints[8], aLine );
8900     aLine.append( ' ' );
8901     m_aPages.back().appendPoint( aPoints[9], aLine );
8902     aLine.append( ' ' );
8903     m_aPages.back().appendPoint( aPoints[10], aLine );
8904     aLine.append( " c\n" );
8905     m_aPages.back().appendPoint( aPoints[11], aLine );
8906     aLine.append( ' ' );
8907     m_aPages.back().appendPoint( aPoints[0], aLine );
8908     aLine.append( ' ' );
8909     m_aPages.back().appendPoint( aPoints[1], aLine );
8910     aLine.append( " c " );
8911 
8912     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8913         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8914         aLine.append( "b*\n" );
8915     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8916         aLine.append( "s\n" );
8917     else
8918         aLine.append( "f*\n" );
8919 
8920     writeBuffer( aLine.getStr(), aLine.getLength() );
8921 }
8922 
8923 static double calcAngle( const Rectangle& rRect, const Point& rPoint )
8924 {
8925     Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
8926                   (rRect.Top()+rRect.Bottom()+1)/2);
8927     Point aPoint = rPoint - aOrigin;
8928 
8929     double fX = (double)aPoint.X();
8930     double fY = (double)-aPoint.Y();
8931 
8932     if( rRect.GetWidth() > rRect.GetHeight() )
8933         fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
8934     else if( rRect.GetHeight() > rRect.GetWidth() )
8935         fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
8936     return atan2( fY, fX );
8937 }
8938 
8939 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
8940 {
8941     MARK( "drawArc" );
8942 
8943     updateGraphicsState();
8944 
8945     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8946         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8947         return;
8948 
8949     // calculate start and stop angles
8950     const double fStartAngle = calcAngle( rRect, rStart );
8951     double fStopAngle  = calcAngle( rRect, rStop );
8952     while( fStopAngle < fStartAngle )
8953         fStopAngle += 2.0*M_PI;
8954     const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
8955     const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
8956     const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
8957     const double halfWidth = (double)rRect.GetWidth()/2.0;
8958     const double halfHeight = (double)rRect.GetHeight()/2.0;
8959 
8960     const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
8961                          (rRect.Top()+rRect.Bottom()+1)/2 );
8962 
8963     OStringBuffer aLine( 30*nFragments );
8964     Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
8965                   -(int)(halfHeight * sin(fStartAngle) ) );
8966     aPoint += aCenter;
8967     m_aPages.back().appendPoint( aPoint, aLine );
8968     aLine.append( " m " );
8969     if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
8970     {
8971         for( int i = 0; i < nFragments; i++ )
8972         {
8973             const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
8974             const double fStopFragment = fStartFragment + fFragmentDelta;
8975             aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
8976                             -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
8977             aPoint += aCenter;
8978             m_aPages.back().appendPoint( aPoint, aLine );
8979             aLine.append( ' ' );
8980 
8981             aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
8982                             -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
8983             aPoint += aCenter;
8984             m_aPages.back().appendPoint( aPoint, aLine );
8985             aLine.append( ' ' );
8986 
8987             aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
8988                             -(int)(halfHeight * sin(fStopFragment) ) );
8989             aPoint += aCenter;
8990             m_aPages.back().appendPoint( aPoint, aLine );
8991             aLine.append( " c\n" );
8992         }
8993     }
8994     if( bWithChord || bWithPie )
8995     {
8996         if( bWithPie )
8997         {
8998             m_aPages.back().appendPoint( aCenter, aLine );
8999             aLine.append( " l " );
9000         }
9001         aLine.append( "h " );
9002     }
9003     if( ! bWithChord && ! bWithPie )
9004         aLine.append( "S\n" );
9005     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
9006         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
9007         aLine.append( "B*\n" );
9008     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9009         aLine.append( "S\n" );
9010     else
9011         aLine.append( "f*\n" );
9012 
9013     writeBuffer( aLine.getStr(), aLine.getLength() );
9014 }
9015 
9016 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly )
9017 {
9018     MARK( "drawPolyLine" );
9019 
9020     sal_uInt16 nPoints = rPoly.GetSize();
9021     if( nPoints < 2 )
9022         return;
9023 
9024     updateGraphicsState();
9025 
9026     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9027         return;
9028 
9029     OStringBuffer aLine( 20 * nPoints );
9030     m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
9031     aLine.append( "S\n" );
9032 
9033     writeBuffer( aLine.getStr(), aLine.getLength() );
9034 }
9035 
9036 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo )
9037 {
9038     MARK( "drawPolyLine with LineInfo" );
9039 
9040     updateGraphicsState();
9041 
9042     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9043         return;
9044 
9045     OStringBuffer aLine;
9046     aLine.append( "q " );
9047     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
9048     {
9049         writeBuffer( aLine.getStr(), aLine.getLength() );
9050         drawPolyLine( rPoly );
9051         writeBuffer( "Q\n", 2 );
9052     }
9053     else
9054     {
9055         PDFWriter::ExtLineInfo aInfo;
9056         convertLineInfoToExtLineInfo( rInfo, aInfo );
9057         drawPolyLine( rPoly, aInfo );
9058     }
9059 }
9060 
9061 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
9062 {
9063     DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" );
9064     rOut.m_fLineWidth           = rIn.GetWidth();
9065     rOut.m_fTransparency        = 0.0;
9066     rOut.m_eCap                 = PDFWriter::capButt;
9067     rOut.m_eJoin                = PDFWriter::joinMiter;
9068     rOut.m_fMiterLimit          = 10;
9069     rOut.m_aDashArray.clear();
9070 
9071     // add DashDot to DashArray
9072     const int nDashes     = rIn.GetDashCount();
9073     const int nDashLen    = rIn.GetDashLen();
9074     const int nDistance   = rIn.GetDistance();
9075 
9076     for( int n  = 0; n < nDashes; n++ )
9077     {
9078         rOut.m_aDashArray.push_back( nDashLen );
9079         rOut.m_aDashArray.push_back( nDistance );
9080     }
9081 
9082     const int nDots       = rIn.GetDotCount();
9083     const int nDotLen     = rIn.GetDotLen();
9084 
9085     for( int n  = 0; n < nDots; n++ )
9086     {
9087         rOut.m_aDashArray.push_back( nDotLen );
9088         rOut.m_aDashArray.push_back( nDistance );
9089     }
9090 
9091     // add LineJoin
9092     switch(rIn.GetLineJoin())
9093     {
9094         case basegfx::B2DLINEJOIN_BEVEL :
9095         {
9096             rOut.m_eJoin = PDFWriter::joinBevel;
9097             break;
9098         }
9099         default : // basegfx::B2DLINEJOIN_NONE :
9100         // Pdf has no 'none' lineJoin, default is miter
9101         case basegfx::B2DLINEJOIN_MIDDLE :
9102         case basegfx::B2DLINEJOIN_MITER :
9103         {
9104             rOut.m_eJoin = PDFWriter::joinMiter;
9105             break;
9106         }
9107         case basegfx::B2DLINEJOIN_ROUND :
9108         {
9109             rOut.m_eJoin = PDFWriter::joinRound;
9110             break;
9111         }
9112     }
9113 
9114     // add LineCap
9115     switch(rIn.GetLineCap())
9116     {
9117         default: /* com::sun::star::drawing::LineCap_BUTT */
9118         {
9119             rOut.m_eCap = PDFWriter::capButt;
9120             break;
9121         }
9122         case com::sun::star::drawing::LineCap_ROUND:
9123         {
9124             rOut.m_eCap = PDFWriter::capRound;
9125             break;
9126         }
9127         case com::sun::star::drawing::LineCap_SQUARE:
9128         {
9129             rOut.m_eCap = PDFWriter::capSquare;
9130             break;
9131         }
9132     }
9133 }
9134 
9135 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
9136 {
9137     MARK( "drawPolyLine with ExtLineInfo" );
9138 
9139     updateGraphicsState();
9140 
9141     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9142         return;
9143 
9144     if( rInfo.m_fTransparency >= 1.0 )
9145         return;
9146 
9147     if( rInfo.m_fTransparency != 0.0 )
9148         beginTransparencyGroup();
9149 
9150     OStringBuffer aLine;
9151     aLine.append( "q " );
9152     m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
9153     aLine.append( " w" );
9154     if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader
9155     {
9156         switch( rInfo.m_eCap )
9157         {
9158             default:
9159             case PDFWriter::capButt:   aLine.append( " 0 J" );break;
9160             case PDFWriter::capRound:  aLine.append( " 1 J" );break;
9161             case PDFWriter::capSquare: aLine.append( " 2 J" );break;
9162         }
9163         switch( rInfo.m_eJoin )
9164         {
9165             default:
9166             case PDFWriter::joinMiter:
9167             {
9168                 double fLimit = rInfo.m_fMiterLimit;
9169                 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
9170                     fLimit = fLimit / rInfo.m_fLineWidth;
9171                 if( fLimit < 1.0 )
9172                     fLimit = 1.0;
9173                 aLine.append( " 0 j " );
9174                 appendDouble( fLimit, aLine );
9175                 aLine.append( " M" );
9176             }
9177             break;
9178             case PDFWriter::joinRound:  aLine.append( " 1 j" );break;
9179             case PDFWriter::joinBevel:  aLine.append( " 2 j" );break;
9180         }
9181         if( rInfo.m_aDashArray.size() > 0 )
9182         {
9183             aLine.append( " [ " );
9184             for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
9185                  it != rInfo.m_aDashArray.end(); ++it )
9186             {
9187                 m_aPages.back().appendMappedLength( *it, aLine );
9188                 aLine.append( ' ' );
9189             }
9190             aLine.append( "] 0 d" );
9191         }
9192         aLine.append( "\n" );
9193         writeBuffer( aLine.getStr(), aLine.getLength() );
9194         drawPolyLine( rPoly );
9195     }
9196     else
9197     {
9198         basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
9199         basegfx::B2DPolyPolygon aPolyPoly;
9200 
9201 		basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
9202 
9203 		// Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
9204 		// To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
9205 		// this line needs to be removed and the loop below adapted accordingly
9206 		aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
9207 
9208 		const sal_uInt32 nPolygonCount(aPolyPoly.count());
9209 
9210 		for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
9211         {
9212             aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
9213             aPoly = aPolyPoly.getB2DPolygon( nPoly );
9214 			const sal_uInt32 nPointCount(aPoly.count());
9215 
9216 			if(nPointCount)
9217 			{
9218 				const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
9219 				basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
9220 
9221 				for(sal_uInt32 a(0); a < nEdgeCount; a++)
9222 				{
9223                     if( a > 0 )
9224                         aLine.append( " " );
9225 					const sal_uInt32 nNextIndex((a + 1) % nPointCount);
9226 					const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
9227 
9228 					m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
9229 														FRound(aCurrent.getY()) ),
9230 												 aLine );
9231 					aLine.append( " m " );
9232 					m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
9233 														FRound(aNext.getY()) ),
9234 												 aLine );
9235 					aLine.append( " l" );
9236 
9237 					// prepare next edge
9238 					aCurrent = aNext;
9239 				}
9240 			}
9241         }
9242         aLine.append( " S " );
9243         writeBuffer( aLine.getStr(), aLine.getLength() );
9244     }
9245     writeBuffer( "Q\n", 2 );
9246 
9247     if( rInfo.m_fTransparency != 0.0 )
9248     {
9249         // FIXME: actually this may be incorrect with bezier polygons
9250         Rectangle aBoundRect( rPoly.GetBoundRect() );
9251         // avoid clipping with thick lines
9252         if( rInfo.m_fLineWidth > 0.0 )
9253         {
9254             sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
9255             aBoundRect.Top()    -= nLW;
9256             aBoundRect.Left()   -= nLW;
9257             aBoundRect.Right()  += nLW;
9258             aBoundRect.Bottom() += nLW;
9259         }
9260         endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) );
9261     }
9262 }
9263 
9264 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
9265 {
9266     MARK( "drawPixel" );
9267 
9268     Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
9269 
9270     if( aColor == Color( COL_TRANSPARENT ) )
9271         return;
9272 
9273     // pixels are drawn in line color, so have to set
9274     // the nonstroking color to line color
9275     Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
9276     setFillColor( aColor );
9277 
9278     updateGraphicsState();
9279 
9280     OStringBuffer aLine( 20 );
9281     m_aPages.back().appendPoint( rPoint, aLine );
9282     aLine.append( ' ' );
9283     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine );
9284     aLine.append( ' ' );
9285     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine );
9286     aLine.append( " re f\n" );
9287     writeBuffer( aLine.getStr(), aLine.getLength() );
9288 
9289     setFillColor( aOldFillColor );
9290 }
9291 
9292 void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors )
9293 {
9294     MARK( "drawPixel with Polygon" );
9295 
9296     updateGraphicsState();
9297 
9298     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors )
9299         return;
9300 
9301     sal_uInt16 nPoints = rPoints.GetSize();
9302     OStringBuffer aLine( nPoints*40 );
9303     aLine.append( "q " );
9304     if( ! pColors )
9305     {
9306         appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine );
9307         aLine.append( ' ' );
9308     }
9309 
9310     OStringBuffer aPixel(32);
9311     aPixel.append( ' ' );
9312     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel );
9313     aPixel.append( ' ' );
9314     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel );
9315     OString aPixelStr = aPixel.makeStringAndClear();
9316     for( sal_uInt16 i = 0; i < nPoints; i++ )
9317     {
9318         if( pColors )
9319         {
9320             if( pColors[i] == Color( COL_TRANSPARENT ) )
9321                 continue;
9322 
9323             appendNonStrokingColor( pColors[i], aLine );
9324             aLine.append( ' ' );
9325         }
9326         m_aPages.back().appendPoint( rPoints[i], aLine );
9327         aLine.append( aPixelStr );
9328         aLine.append( " re f\n" );
9329     }
9330     aLine.append( "Q\n" );
9331     writeBuffer( aLine.getStr(), aLine.getLength() );
9332 }
9333 
9334 class AccessReleaser
9335 {
9336     BitmapReadAccess* m_pAccess;
9337 public:
9338     AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){}
9339     ~AccessReleaser() { delete m_pAccess; }
9340 };
9341 
9342 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
9343 {
9344     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9345 
9346     bool bFlateFilter = compressStream( rObject.m_pContentStream );
9347     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
9348     sal_uLong nSize = rObject.m_pContentStream->Tell();
9349     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
9350     #if OSL_DEBUG_LEVEL > 1
9351     emitComment( "PDFWriterImpl::writeTransparentObject" );
9352     #endif
9353     OStringBuffer aLine( 512 );
9354     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9355     aLine.append( rObject.m_nObject );
9356     aLine.append( " 0 obj\n"
9357                   "<</Type/XObject\n"
9358                   "/Subtype/Form\n"
9359                   "/BBox[ " );
9360     appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
9361     aLine.append( ' ' );
9362     appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
9363     aLine.append( ' ' );
9364     appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
9365     aLine.append( ' ' );
9366     appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
9367     aLine.append( " ]\n" );
9368     if( ! rObject.m_pSoftMaskStream )
9369     {
9370         if( ! m_bIsPDF_A1 )
9371         {
9372             aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
9373         }
9374     }
9375     /* #i42884# the PDF reference recommends that each Form XObject
9376     *  should have a resource dict; alas if that is the same object
9377     *  as the one of the page it triggers an endless recursion in
9378     *  acroread 5 (6 and up have that fixed). Since we have only one
9379     *  resource dict anyway, let's use the one from the page by NOT
9380     *  emitting a Resources entry.
9381     */
9382     #if 0
9383     aLine.append( "   /Resources " );
9384     aLine.append( getResourceDictObj() );
9385     aLine.append( " 0 R\n" );
9386     #endif
9387 
9388     aLine.append( "/Length " );
9389     aLine.append( (sal_Int32)(nSize) );
9390     aLine.append( "\n" );
9391     if( bFlateFilter )
9392         aLine.append( "/Filter/FlateDecode\n" );
9393     aLine.append( ">>\n"
9394                   "stream\n" );
9395     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9396     checkAndEnableStreamEncryption( rObject.m_nObject );
9397     CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
9398     disableStreamEncryption();
9399     aLine.setLength( 0 );
9400     aLine.append( "\n"
9401                   "endstream\n"
9402                   "endobj\n\n" );
9403     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9404 
9405     // write ExtGState dict for this XObject
9406     aLine.setLength( 0 );
9407     aLine.append( rObject.m_nExtGStateObject );
9408     aLine.append( " 0 obj\n"
9409                   "<<" );
9410     if( ! rObject.m_pSoftMaskStream )
9411     {
9412 //i59651
9413         if( m_bIsPDF_A1 )
9414         {
9415             aLine.append( "/CA 1.0/ca 1.0" );
9416             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9417         }
9418         else
9419         {
9420             aLine.append(  "/CA " );
9421             appendDouble( rObject.m_fAlpha, aLine );
9422             aLine.append( "\n"
9423                           "   /ca " );
9424             appendDouble( rObject.m_fAlpha, aLine );
9425         }
9426         aLine.append( "\n" );
9427     }
9428     else
9429     {
9430         if( m_bIsPDF_A1 )
9431         {
9432             aLine.append( "/SMask/None" );
9433             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9434         }
9435         else
9436         {
9437             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
9438             sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
9439             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
9440             sal_Int32 nMaskObject = createObject();
9441             aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
9442             aLine.append( nMaskObject );
9443             aLine.append( " 0 R>>\n" );
9444 
9445             OStringBuffer aMask;
9446             aMask.append( nMaskObject );
9447             aMask.append( " 0 obj\n"
9448                           "<</Type/XObject\n"
9449                           "/Subtype/Form\n"
9450                           "/BBox[" );
9451             appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
9452             aMask.append( ' ' );
9453             appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
9454             aMask.append( ' ' );
9455             appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
9456             aMask.append( ' ' );
9457             appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
9458             aMask.append( "]\n" );
9459 
9460             /* #i42884# see above */
9461 #if 0
9462             aLine.append( "/Resources " );
9463             aMask.append( getResourceDictObj() );
9464             aMask.append( " 0 R\n" );
9465 #endif
9466 
9467             aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
9468             aMask.append( "/Length " );
9469             aMask.append( nMaskSize );
9470             aMask.append( ">>\n"
9471                           "stream\n" );
9472             CHECK_RETURN( updateObject( nMaskObject ) );
9473             checkAndEnableStreamEncryption(  nMaskObject );
9474             CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9475             CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
9476             disableStreamEncryption();
9477             aMask.setLength( 0 );
9478             aMask.append( "\nendstream\n"
9479                           "endobj\n\n" );
9480             CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9481         }
9482     }
9483     aLine.append( ">>\n"
9484                   "endobj\n\n" );
9485     CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) );
9486     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9487 
9488     return true;
9489 }
9490 
9491 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
9492 {
9493     sal_Int32 nFunctionObject = createObject();
9494     CHECK_RETURN( updateObject( nFunctionObject ) );
9495 
9496     VirtualDevice aDev;
9497     aDev.SetOutputSizePixel( rObject.m_aSize );
9498     aDev.SetMapMode( MapMode( MAP_PIXEL ) );
9499     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9500         aDev.SetDrawMode( aDev.GetDrawMode() |
9501                           ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT |
9502                             DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) );
9503     aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
9504 
9505     Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize );
9506     BitmapReadAccess* pAccess = aSample.AcquireReadAccess();
9507     AccessReleaser aReleaser( pAccess );
9508 
9509     Size aSize = aSample.GetSizePixel();
9510 
9511     sal_Int32 nStreamLengthObject = createObject();
9512     #if OSL_DEBUG_LEVEL > 1
9513     emitComment( "PDFWriterImpl::writeGradientFunction" );
9514     #endif
9515     OStringBuffer aLine( 120 );
9516     aLine.append( nFunctionObject );
9517     aLine.append( " 0 obj\n"
9518                   "<</FunctionType 0\n"
9519                   "/Domain[ 0 1 0 1 ]\n"
9520                   "/Size[ " );
9521     aLine.append( (sal_Int32)aSize.Width() );
9522     aLine.append( ' ' );
9523     aLine.append( (sal_Int32)aSize.Height() );
9524     aLine.append( " ]\n"
9525                   "/BitsPerSample 8\n"
9526                   "/Range[ 0 1 0 1 0 1 ]\n"
9527                   "/Order 3\n"
9528                   "/Length " );
9529     aLine.append( nStreamLengthObject );
9530     aLine.append( " 0 R\n"
9531 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9532                   "/Filter/FlateDecode"
9533 #endif
9534                   ">>\n"
9535                   "stream\n" );
9536     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9537 
9538     sal_uInt64 nStartStreamPos = 0;
9539     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) );
9540 
9541     checkAndEnableStreamEncryption( nFunctionObject );
9542     beginCompression();
9543     for( int y = aSize.Height()-1; y >= 0; y-- )
9544     {
9545         for( int x = 0; x < aSize.Width(); x++ )
9546         {
9547             sal_uInt8 aCol[3];
9548             BitmapColor aColor = pAccess->GetColor( y, x );
9549             aCol[0] = aColor.GetRed();
9550             aCol[1] = aColor.GetGreen();
9551             aCol[2] = aColor.GetBlue();
9552             CHECK_RETURN( writeBuffer( aCol, 3 ) );
9553         }
9554     }
9555     endCompression();
9556     disableStreamEncryption();
9557 
9558     sal_uInt64 nEndStreamPos = 0;
9559     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) );
9560 
9561     aLine.setLength( 0 );
9562     aLine.append( "\nendstream\nendobj\n\n" );
9563     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9564 
9565     // write stream length
9566     CHECK_RETURN( updateObject( nStreamLengthObject ) );
9567     aLine.setLength( 0 );
9568     aLine.append( nStreamLengthObject );
9569     aLine.append( " 0 obj\n" );
9570     aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
9571     aLine.append( "\nendobj\n\n" );
9572     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9573 
9574     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9575     aLine.setLength( 0 );
9576     aLine.append( rObject.m_nObject );
9577     aLine.append( " 0 obj\n"
9578                   "<</ShadingType 1\n"
9579                   "/ColorSpace/DeviceRGB\n"
9580                   "/AntiAlias true\n"
9581                   "/Domain[ 0 1 0 1 ]\n"
9582                   "/Matrix[ " );
9583     aLine.append( (sal_Int32)aSize.Width() );
9584     aLine.append( " 0 0 " );
9585     aLine.append( (sal_Int32)aSize.Height() );
9586     aLine.append( " 0 0 ]\n"
9587                   "/Function " );
9588     aLine.append( nFunctionObject );
9589     aLine.append( " 0 R\n"
9590                   ">>\n"
9591                   "endobj\n\n" );
9592     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9593 
9594     return true;
9595 }
9596 
9597 bool PDFWriterImpl::writeJPG( JPGEmit& rObject )
9598 {
9599     CHECK_RETURN( rObject.m_pStream );
9600     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9601 
9602     sal_Int32 nLength = 0;
9603     rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
9604     nLength = rObject.m_pStream->Tell();
9605     rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
9606 
9607     sal_Int32 nMaskObject = 0;
9608     if( !!rObject.m_aMask )
9609     {
9610         if( rObject.m_aMask.GetBitCount() == 1 ||
9611             ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651
9612             )
9613         {
9614             nMaskObject = createObject();
9615         }
9616         else if( m_bIsPDF_A1 )
9617             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9618         else if( m_aContext.Version < PDFWriter::PDF_1_4 )
9619             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
9620 
9621     }
9622     #if OSL_DEBUG_LEVEL > 1
9623     emitComment( "PDFWriterImpl::writeJPG" );
9624     #endif
9625 
9626     OStringBuffer aLine(200);
9627     aLine.append( rObject.m_nObject );
9628     aLine.append( " 0 obj\n"
9629                   "<</Type/XObject/Subtype/Image/Width " );
9630     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
9631     aLine.append( " /Height " );
9632     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
9633     aLine.append( " /BitsPerComponent 8 " );
9634     if( rObject.m_bTrueColor )
9635         aLine.append( "/ColorSpace/DeviceRGB" );
9636     else
9637         aLine.append( "/ColorSpace/DeviceGray" );
9638     aLine.append( "/Filter/DCTDecode/Length " );
9639     aLine.append( nLength );
9640     if( nMaskObject )
9641     {
9642         aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
9643         aLine.append( nMaskObject );
9644         aLine.append( " 0 R " );
9645     }
9646     aLine.append( ">>\nstream\n" );
9647     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9648 
9649     checkAndEnableStreamEncryption( rObject.m_nObject );
9650     CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
9651     disableStreamEncryption();
9652 
9653     aLine.setLength( 0 );
9654     aLine.append( "\nendstream\nendobj\n\n" );
9655     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9656 
9657     if( nMaskObject )
9658     {
9659         BitmapEmit aEmit;
9660         aEmit.m_nObject = nMaskObject;
9661         if( rObject.m_aMask.GetBitCount() == 1 )
9662             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
9663         else if( rObject.m_aMask.GetBitCount() == 8 )
9664             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
9665         writeBitmapObject( aEmit, true );
9666     }
9667 
9668     return true;
9669 }
9670 
9671 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
9672 {
9673     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9674 
9675     Bitmap	aBitmap;
9676     Color	aTransparentColor( COL_TRANSPARENT );
9677     bool	bWriteMask = false;
9678     if( ! bMask )
9679     {
9680         aBitmap = rObject.m_aBitmap.GetBitmap();
9681         if( rObject.m_aBitmap.IsAlpha() )
9682         {
9683             if( m_aContext.Version >= PDFWriter::PDF_1_4 )
9684                 bWriteMask = true;
9685             // else draw without alpha channel
9686         }
9687         else
9688         {
9689             switch( rObject.m_aBitmap.GetTransparentType() )
9690             {
9691                 case TRANSPARENT_NONE:
9692                     // comes from drawMask function
9693                     if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask )
9694                         bMask = true;
9695                     break;
9696                 case TRANSPARENT_COLOR:
9697                     aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
9698                     break;
9699                 case TRANSPARENT_BITMAP:
9700                     bWriteMask = true;
9701                     break;
9702             }
9703         }
9704     }
9705     else
9706     {
9707         if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
9708         {
9709             aBitmap = rObject.m_aBitmap.GetMask();
9710             aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
9711             DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
9712         }
9713         else if( aBitmap.GetBitCount() != 8 )
9714         {
9715             aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap();
9716             aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
9717             DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" );
9718         }
9719     }
9720 
9721     BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();
9722     AccessReleaser aReleaser( pAccess );
9723 
9724     bool bTrueColor;
9725     sal_Int32 nBitsPerComponent;
9726     switch( aBitmap.GetBitCount() )
9727     {
9728         case 1:
9729         case 2:
9730         case 4:
9731         case 8:
9732             bTrueColor = false;
9733             nBitsPerComponent = aBitmap.GetBitCount();
9734             break;
9735         default:
9736             bTrueColor = true;
9737             nBitsPerComponent = 8;
9738             break;
9739     }
9740 
9741     sal_Int32 nStreamLengthObject	= createObject();
9742     sal_Int32 nMaskObject			= 0;
9743 
9744     #if OSL_DEBUG_LEVEL > 1
9745     emitComment( "PDFWriterImpl::writeBitmapObject" );
9746     #endif
9747     OStringBuffer aLine(1024);
9748     aLine.append( rObject.m_nObject );
9749     aLine.append( " 0 obj\n"
9750                   "<</Type/XObject/Subtype/Image/Width " );
9751     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9752     aLine.append( "/Height " );
9753     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
9754     aLine.append( "/BitsPerComponent " );
9755     aLine.append( nBitsPerComponent );
9756     aLine.append( "/Length " );
9757     aLine.append( nStreamLengthObject );
9758     aLine.append( " 0 R\n" );
9759 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9760     if( nBitsPerComponent != 1 )
9761     {
9762         aLine.append( "/Filter/FlateDecode" );
9763     }
9764     else
9765     {
9766         aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
9767         aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9768         aLine.append( ">>\n" );
9769     }
9770 #endif
9771     if( ! bMask )
9772     {
9773         aLine.append( "/ColorSpace" );
9774         if( bTrueColor )
9775             aLine.append( "/DeviceRGB\n" );
9776         else if( aBitmap.HasGreyPalette() )
9777         {
9778             aLine.append( "/DeviceGray\n" );
9779             if( aBitmap.GetBitCount() == 1 )
9780             {
9781                 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
9782                 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9783                 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9784                 if( nBlackIndex == 1 )
9785                     aLine.append( "/Decode[1 0]\n" );
9786             }
9787         }
9788         else
9789         {
9790             aLine.append( "[ /Indexed/DeviceRGB " );
9791             aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
9792             aLine.append( "\n<" );
9793 			if( m_aContext.Encryption.Encrypt() )
9794 			{
9795 				enableStringEncryption( rObject.m_nObject );
9796 				//check encryption buffer size
9797 				if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
9798 				{
9799 					int	nChar = 0;
9800 					//fill the encryption buffer
9801 					for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9802 					{
9803 						const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9804 						m_pEncryptionBuffer[nChar++] = rColor.GetRed();
9805 						m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
9806 						m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
9807 					}
9808 					//encrypt the colorspace lookup table
9809 					rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
9810 					//now queue the data for output
9811                     nChar = 0;
9812 					for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9813 					{
9814 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9815 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9816 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9817 					}
9818 				}
9819 			}
9820 			else //no encryption requested (PDF/A-1a program flow drops here)
9821 			{
9822 				for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9823 				{
9824 					const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9825 					appendHex( rColor.GetRed(), aLine );
9826 					appendHex( rColor.GetGreen(), aLine );
9827 					appendHex( rColor.GetBlue(), aLine );
9828 				}
9829 			}
9830             aLine.append( ">\n]\n" );
9831         }
9832     }
9833     else
9834     {
9835         if( aBitmap.GetBitCount() == 1 )
9836         {
9837             aLine.append( "/ImageMask true\n" );
9838             sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9839             DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9840             if( nBlackIndex )
9841                 aLine.append( "/Decode[ 1 0 ]\n" );
9842             else
9843                 aLine.append( "/Decode[ 0 1 ]\n" );
9844         }
9845         else if( aBitmap.GetBitCount() == 8 )
9846         {
9847             aLine.append( "/ColorSpace/DeviceGray\n"
9848                           "/Decode [ 1 0 ]\n" );
9849         }
9850     }
9851 
9852     if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651
9853     {
9854         if( bWriteMask )
9855         {
9856             nMaskObject = createObject();
9857             if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 )
9858                 aLine.append( "/SMask " );
9859             else
9860                 aLine.append( "/Mask " );
9861             aLine.append( nMaskObject );
9862             aLine.append( " 0 R\n" );
9863         }
9864         else if( aTransparentColor != Color( COL_TRANSPARENT ) )
9865         {
9866             aLine.append( "/Mask[ " );
9867             if( bTrueColor )
9868             {
9869                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9870                 aLine.append( ' ' );
9871                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9872                 aLine.append( ' ' );
9873                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9874                 aLine.append( ' ' );
9875                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9876                 aLine.append( ' ' );
9877                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9878                 aLine.append( ' ' );
9879                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9880             }
9881             else
9882             {
9883                 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
9884                 aLine.append( nIndex );
9885             }
9886             aLine.append( " ]\n" );
9887         }
9888     }
9889     else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) )
9890         m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9891 
9892     aLine.append( ">>\n"
9893                   "stream\n" );
9894     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9895     sal_uInt64 nStartPos = 0;
9896     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) );
9897 
9898     checkAndEnableStreamEncryption( rObject.m_nObject );
9899 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9900     if( nBitsPerComponent == 1 )
9901     {
9902         writeG4Stream( pAccess );
9903     }
9904     else
9905 #endif
9906     {
9907         beginCompression();
9908         if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
9909         {
9910             const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U );
9911 
9912             for( int i = 0; i < pAccess->Height(); i++ )
9913             {
9914                 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
9915             }
9916         }
9917         else
9918         {
9919             const int nScanLineBytes = pAccess->Width()*3;
9920             boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] );
9921             for( int y = 0; y < pAccess->Height(); y++ )
9922             {
9923                 for( int x = 0; x < pAccess->Width(); x++ )
9924                 {
9925                     BitmapColor aColor = pAccess->GetColor( y, x );
9926                     pCol[3*x+0] = aColor.GetRed();
9927                     pCol[3*x+1] = aColor.GetGreen();
9928                     pCol[3*x+2] = aColor.GetBlue();
9929                 }
9930                 CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) );
9931             }
9932         }
9933         endCompression();
9934     }
9935     disableStreamEncryption();
9936 
9937     sal_uInt64 nEndPos = 0;
9938     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) );
9939     aLine.setLength( 0 );
9940     aLine.append( "\nendstream\nendobj\n\n" );
9941     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9942     CHECK_RETURN( updateObject( nStreamLengthObject ) );
9943     aLine.setLength( 0 );
9944     aLine.append( nStreamLengthObject );
9945     aLine.append( " 0 obj\n" );
9946     aLine.append( (sal_Int64)(nEndPos-nStartPos) );
9947     aLine.append( "\nendobj\n\n" );
9948     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9949 
9950     if( nMaskObject )
9951     {
9952         BitmapEmit aEmit;
9953         aEmit.m_nObject				= nMaskObject;
9954         aEmit.m_aBitmap				= rObject.m_aBitmap;
9955         return writeBitmapObject( aEmit, true );
9956     }
9957 
9958     return true;
9959 }
9960 
9961 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask )
9962 {
9963     MARK( "drawJPGBitmap" );
9964 
9965     OStringBuffer aLine( 80 );
9966     updateGraphicsState();
9967 
9968     // #i40055# sanity check
9969     if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
9970         return;
9971     if( ! (rSizePixel.Width() && rSizePixel.Height()) )
9972         return;
9973 
9974     rDCTData.Seek( 0 );
9975     if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9976     {
9977         // need to convert to grayscale;
9978         // load stream to bitmap and draw the bitmap instead
9979         Graphic aGraphic;
9980         GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG );
9981         Bitmap aBmp( aGraphic.GetBitmap() );
9982         if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() )
9983         {
9984             BitmapEx aBmpEx( aBmp, rMask );
9985             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx );
9986         }
9987         else
9988             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp );
9989         return;
9990     }
9991 
9992     SvMemoryStream* pStream = new SvMemoryStream;
9993     *pStream << rDCTData;
9994     pStream->Seek( STREAM_SEEK_TO_END );
9995 
9996     BitmapID aID;
9997     aID.m_aPixelSize	= rSizePixel;
9998     aID.m_nSize			= pStream->Tell();
9999     pStream->Seek( STREAM_SEEK_TO_BEGIN );
10000     aID.m_nChecksum		= rtl_crc32( 0, pStream->GetData(), aID.m_nSize );
10001     if( ! rMask.IsEmpty() )
10002         aID.m_nMaskChecksum	= rMask.GetChecksum();
10003 
10004     std::list< JPGEmit >::const_iterator it;
10005     for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
10006         ;
10007     if( it == m_aJPGs.end() )
10008     {
10009         m_aJPGs.push_front( JPGEmit() );
10010         JPGEmit& rEmit = m_aJPGs.front();
10011         rEmit.m_nObject		= createObject();
10012         rEmit.m_aID			= aID;
10013         rEmit.m_pStream		= pStream;
10014         rEmit.m_bTrueColor  = bIsTrueColor;
10015         if( !! rMask && rMask.GetSizePixel() == rSizePixel )
10016             rEmit.m_aMask	= rMask;
10017 
10018         it = m_aJPGs.begin();
10019     }
10020     else
10021         delete pStream;
10022 
10023     aLine.append( "q " );
10024     sal_Int32 nCheckWidth = 0;
10025     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth );
10026     aLine.append( " 0 0 " );
10027     sal_Int32 nCheckHeight = 0;
10028     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight );
10029     aLine.append( ' ' );
10030     m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
10031     aLine.append( " cm\n/Im" );
10032     aLine.append( it->m_nObject );
10033     aLine.append( " Do Q\n" );
10034     if( nCheckWidth == 0 || nCheckHeight == 0 )
10035     {
10036         // #i97512# avoid invalid current matrix
10037         aLine.setLength( 0 );
10038         aLine.append( "\n%jpeg image /Im" );
10039         aLine.append( it->m_nObject );
10040         aLine.append( " scaled to zero size, omitted\n" );
10041     }
10042     writeBuffer( aLine.getStr(), aLine.getLength() );
10043 
10044     OStringBuffer aObjName( 16 );
10045     aObjName.append( "Im" );
10046     aObjName.append( it->m_nObject );
10047     pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10048 
10049 }
10050 
10051 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
10052 {
10053     OStringBuffer aLine( 80 );
10054     updateGraphicsState();
10055 
10056     aLine.append( "q " );
10057     if( rFillColor != Color( COL_TRANSPARENT ) )
10058     {
10059         appendNonStrokingColor( rFillColor, aLine );
10060         aLine.append( ' ' );
10061     }
10062     sal_Int32 nCheckWidth = 0;
10063     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth );
10064     aLine.append( " 0 0 " );
10065     sal_Int32 nCheckHeight = 0;
10066     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight );
10067     aLine.append( ' ' );
10068     m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
10069     aLine.append( " cm\n/Im" );
10070     aLine.append( rBitmap.m_nObject );
10071     aLine.append( " Do Q\n" );
10072     if( nCheckWidth == 0 || nCheckHeight == 0 )
10073     {
10074         // #i97512# avoid invalid current matrix
10075         aLine.setLength( 0 );
10076         aLine.append( "\n%bitmap image /Im" );
10077         aLine.append( rBitmap.m_nObject );
10078         aLine.append( " scaled to zero size, omitted\n" );
10079     }
10080     writeBuffer( aLine.getStr(), aLine.getLength() );
10081 }
10082 
10083 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask )
10084 {
10085     BitmapEx aBitmap( i_rBitmap );
10086     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
10087     {
10088         BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS;
10089         int nDepth = aBitmap.GetBitmap().GetBitCount();
10090         if( nDepth <= 4 )
10091             eConv = BMP_CONVERSION_4BIT_GREYS;
10092         if( nDepth > 1 )
10093             aBitmap.Convert( eConv );
10094     }
10095     BitmapID aID;
10096     aID.m_aPixelSize		= aBitmap.GetSizePixel();
10097     aID.m_nSize				= aBitmap.GetBitCount();
10098     aID.m_nChecksum			= aBitmap.GetBitmap().GetChecksum();
10099     aID.m_nMaskChecksum		= 0;
10100     if( aBitmap.IsAlpha() )
10101         aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum();
10102     else
10103     {
10104         Bitmap aMask = aBitmap.GetMask();
10105         if( ! aMask.IsEmpty() )
10106             aID.m_nMaskChecksum = aMask.GetChecksum();
10107     }
10108     std::list< BitmapEmit >::const_iterator it;
10109     for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
10110     {
10111         if( aID == it->m_aID )
10112             break;
10113     }
10114     if( it == m_aBitmaps.end() )
10115     {
10116         m_aBitmaps.push_front( BitmapEmit() );
10117         m_aBitmaps.front().m_aID		= aID;
10118         m_aBitmaps.front().m_aBitmap	= aBitmap;
10119         m_aBitmaps.front().m_nObject	= createObject();
10120         m_aBitmaps.front().m_bDrawMask	= bDrawMask;
10121         it = m_aBitmaps.begin();
10122     }
10123 
10124     OStringBuffer aObjName( 16 );
10125     aObjName.append( "Im" );
10126     aObjName.append( it->m_nObject );
10127     pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10128 
10129     return *it;
10130 }
10131 
10132 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap )
10133 {
10134     MARK( "drawBitmap (Bitmap)" );
10135 
10136     // #i40055# sanity check
10137     if( ! (rDestSize.Width() && rDestSize.Height()) )
10138         return;
10139 
10140     const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) );
10141     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10142 }
10143 
10144 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
10145 {
10146     MARK( "drawBitmap (BitmapEx)" );
10147 
10148     // #i40055# sanity check
10149     if( ! (rDestSize.Width() && rDestSize.Height()) )
10150         return;
10151 
10152     const BitmapEmit& rEmit = createBitmapEmit( rBitmap );
10153     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10154 }
10155 
10156 void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor )
10157 {
10158     MARK( "drawMask" );
10159 
10160     // #i40055# sanity check
10161     if( ! (rDestSize.Width() && rDestSize.Height()) )
10162         return;
10163 
10164     Bitmap aBitmap( rBitmap );
10165     if( aBitmap.GetBitCount() > 1 )
10166         aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
10167     DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
10168 
10169     const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true );
10170     drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor );
10171 }
10172 
10173 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
10174 {
10175     Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10176                                MapMode( MAP_POINT ),
10177                                getReferenceDevice(),
10178                                rSize ) );
10179     // check if we already have this gradient
10180     std::list<GradientEmit>::iterator it;
10181     // rounding to point will generally lose some pixels
10182     // round up to point boundary
10183     aPtSize.Width()++;
10184     aPtSize.Height()++;
10185     for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
10186     {
10187         if( it->m_aGradient == rGradient )
10188         {
10189             if( it->m_aSize == aPtSize )
10190                 break;
10191         }
10192     }
10193     if( it == m_aGradients.end() )
10194     {
10195         m_aGradients.push_front( GradientEmit() );
10196         m_aGradients.front().m_aGradient	= rGradient;
10197         m_aGradients.front().m_nObject	    = createObject();
10198         m_aGradients.front().m_aSize		= aPtSize;
10199         it = m_aGradients.begin();
10200     }
10201 
10202     OStringBuffer aObjName( 16 );
10203     aObjName.append( 'P' );
10204     aObjName.append( it->m_nObject );
10205     pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
10206 
10207     return it->m_nObject;
10208 }
10209 
10210 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient )
10211 {
10212     MARK( "drawGradient (Rectangle)" );
10213 
10214     if( m_aContext.Version == PDFWriter::PDF_1_2 )
10215     {
10216         drawRectangle( rRect );
10217         return;
10218     }
10219 
10220     sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
10221 
10222     Point aTranslate( rRect.BottomLeft() );
10223     aTranslate += Point( 0, 1 );
10224 
10225     updateGraphicsState();
10226 
10227     OStringBuffer aLine( 80 );
10228     aLine.append( "q 1 0 0 1 " );
10229     m_aPages.back().appendPoint( aTranslate, aLine );
10230     aLine.append( " cm " );
10231     // if a stroke is appended reset the clip region before stroke
10232     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10233         aLine.append( "q " );
10234     aLine.append( "0 0 " );
10235     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10236     aLine.append( ' ' );
10237     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10238     aLine.append( " re W n\n" );
10239 
10240     aLine.append( "/P" );
10241     aLine.append( nGradient );
10242     aLine.append( " sh " );
10243     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10244     {
10245         aLine.append( "Q 0 0 " );
10246         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10247         aLine.append( ' ' );
10248         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10249         aLine.append( " re S " );
10250     }
10251     aLine.append( "Q\n" );
10252     writeBuffer( aLine.getStr(), aLine.getLength() );
10253 }
10254 
10255 void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
10256 {
10257     MARK( "drawGradient (PolyPolygon)" );
10258 
10259     if( m_aContext.Version == PDFWriter::PDF_1_2 )
10260     {
10261         drawPolyPolygon( rPolyPoly );
10262         return;
10263     }
10264 
10265     Rectangle aBoundRect = rPolyPoly.GetBoundRect();
10266     sal_Int32 nGradient = createGradient( rGradient, aBoundRect.GetSize() );
10267 
10268     updateGraphicsState();
10269 
10270     Point aTranslate = aBoundRect.BottomLeft();
10271     int nPolygons = rPolyPoly.Count();
10272 
10273     OStringBuffer aLine( 80*nPolygons );
10274     aLine.append( "q " );
10275     // set PolyPolygon as clip path
10276     m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
10277     aLine.append( "W* n\n" );
10278     aLine.append( "1 0 0 1 " );
10279     m_aPages.back().appendPoint( aTranslate, aLine );
10280     aLine.append( " cm\n" );
10281     aLine.append( "/P" );
10282     aLine.append( nGradient );
10283     aLine.append( " sh Q\n" );
10284     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10285     {
10286         // and draw the surrounding path
10287         m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
10288         aLine.append( "S\n" );
10289     }
10290     writeBuffer( aLine.getStr(), aLine.getLength() );
10291 }
10292 
10293 void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
10294 {
10295     MARK( "drawHatch" );
10296 
10297     updateGraphicsState();
10298 
10299 	if( rPolyPoly.Count() )
10300 	{
10301 		PolyPolygon		aPolyPoly( rPolyPoly );
10302 
10303 		aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
10304 		push( PUSH_LINECOLOR );
10305 		setLineColor( rHatch.GetColor() );
10306 		getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, sal_False );
10307 		pop();
10308 	}
10309 }
10310 
10311 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall )
10312 {
10313     MARK( "drawWallpaper" );
10314 
10315     bool bDrawColor			= false;
10316     bool bDrawGradient		= false;
10317     bool bDrawBitmap		= false;
10318 
10319     BitmapEx aBitmap;
10320     Point aBmpPos = rRect.TopLeft();
10321     Size aBmpSize;
10322     if( rWall.IsBitmap() )
10323     {
10324         aBitmap = rWall.GetBitmap();
10325 		aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
10326                                 getMapMode(),
10327                                 getReferenceDevice(),
10328                                 aBitmap.GetPrefSize() );
10329         Rectangle aRect( rRect );
10330         if( rWall.IsRect() )
10331         {
10332             aRect = rWall.GetRect();
10333             aBmpPos = aRect.TopLeft();
10334             aBmpSize = aRect.GetSize();
10335         }
10336         if( rWall.GetStyle() != WALLPAPER_SCALE )
10337         {
10338             if( rWall.GetStyle() != WALLPAPER_TILE )
10339             {
10340                 bDrawBitmap		= true;
10341                 if( rWall.IsGradient() )
10342                     bDrawGradient = true;
10343                 else
10344                     bDrawColor = true;
10345                 switch( rWall.GetStyle() )
10346                 {
10347                     case WALLPAPER_TOPLEFT:
10348                         break;
10349                     case WALLPAPER_TOP:
10350                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10351                         break;
10352                     case WALLPAPER_LEFT:
10353                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10354                         break;
10355                     case WALLPAPER_TOPRIGHT:
10356                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10357                         break;
10358                     case WALLPAPER_CENTER:
10359                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10360                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10361                         break;
10362                     case WALLPAPER_RIGHT:
10363                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10364                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10365                         break;
10366                     case WALLPAPER_BOTTOMLEFT:
10367                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10368                         break;
10369                     case WALLPAPER_BOTTOM:
10370                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10371                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10372                         break;
10373                     case WALLPAPER_BOTTOMRIGHT:
10374                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10375                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10376                         break;
10377                     default: ;
10378                 }
10379             }
10380             else
10381             {
10382                 // push the bitmap
10383                 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) );
10384 
10385                 // convert to page coordinates; this needs to be done here
10386                 // since the emit does not know the page anymore
10387                 Rectangle aConvertRect( aBmpPos, aBmpSize );
10388                 m_aPages.back().convertRect( aConvertRect );
10389 
10390                 OStringBuffer aNameBuf(16);
10391                 aNameBuf.append( "Im" );
10392                 aNameBuf.append( rEmit.m_nObject );
10393                 OString aImageName( aNameBuf.makeStringAndClear() );
10394 
10395                 // push the pattern
10396                 OStringBuffer aTilingStream( 32 );
10397                 appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
10398                 aTilingStream.append( " 0 0 " );
10399                 appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
10400                 aTilingStream.append( " 0 0 cm\n/" );
10401                 aTilingStream.append( aImageName );
10402                 aTilingStream.append( " Do\n" );
10403 
10404                 m_aTilings.push_back( TilingEmit() );
10405                 m_aTilings.back().m_nObject			= createObject();
10406                 m_aTilings.back().m_aRectangle		= Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
10407                 m_aTilings.back().m_pTilingStream   = new SvMemoryStream();
10408                 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() );
10409                 // phase the tiling so wallpaper begins on upper left
10410                 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
10411                 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
10412                 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
10413 
10414                 updateGraphicsState();
10415 
10416                 OStringBuffer aObjName( 16 );
10417                 aObjName.append( 'P' );
10418                 aObjName.append( m_aTilings.back().m_nObject );
10419                 OString aPatternName( aObjName.makeStringAndClear() );
10420                 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
10421 
10422                 // fill a rRect with the pattern
10423                 OStringBuffer aLine( 100 );
10424                 aLine.append( "q /Pattern cs /" );
10425                 aLine.append( aPatternName );
10426                 aLine.append( " scn " );
10427                 m_aPages.back().appendRect( rRect, aLine );
10428                 aLine.append( " f Q\n" );
10429                 writeBuffer( aLine.getStr(), aLine.getLength() );
10430             }
10431         }
10432         else
10433         {
10434             aBmpPos		= aRect.TopLeft();
10435             aBmpSize	= aRect.GetSize();
10436             bDrawBitmap	= true;
10437         }
10438 
10439         if( aBitmap.IsTransparent() )
10440         {
10441             if( rWall.IsGradient() )
10442                 bDrawGradient = true;
10443             else
10444                 bDrawColor = true;
10445         }
10446     }
10447     else if( rWall.IsGradient() )
10448         bDrawGradient = true;
10449     else
10450         bDrawColor = true;
10451 
10452     if( bDrawGradient )
10453     {
10454         drawGradient( rRect, rWall.GetGradient() );
10455     }
10456     if( bDrawColor )
10457     {
10458         Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
10459         Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
10460         setLineColor( Color( COL_TRANSPARENT ) );
10461         setFillColor( rWall.GetColor() );
10462         drawRectangle( rRect );
10463         setLineColor( aOldLineColor );
10464         setFillColor( aOldFillColor );
10465     }
10466     if( bDrawBitmap )
10467     {
10468         // set temporary clip region since aBmpPos and aBmpSize
10469         // may be outside rRect
10470         OStringBuffer aLine( 20 );
10471         aLine.append( "q " );
10472         m_aPages.back().appendRect( rRect, aLine );
10473         aLine.append( " W n\n" );
10474         writeBuffer( aLine.getStr(), aLine.getLength() );
10475         drawBitmap( aBmpPos, aBmpSize, aBitmap );
10476         writeBuffer( "Q\n", 2 );
10477     }
10478 }
10479 
10480 void PDFWriterImpl::beginPattern( const Rectangle& rCellRect )
10481 {
10482     beginRedirect( new SvMemoryStream(), rCellRect );
10483 }
10484 
10485 sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform )
10486 {
10487     Rectangle aConvertRect( getRedirectTargetRect() );
10488     DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" );
10489 
10490     // get scaling between current mapmode and PDF output
10491     Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) );
10492     double fSX = (double(aScaling.Width()) / 10000.0);
10493     double fSY = (double(aScaling.Height()) / 10000.0);
10494 
10495     // transform translation part of matrix
10496     Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] );
10497     aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation );
10498 
10499     sal_Int32 nTilingId = m_aTilings.size();
10500     m_aTilings.push_back( TilingEmit() );
10501     TilingEmit& rTile = m_aTilings.back();
10502     rTile.m_nObject         = createObject();
10503     rTile.m_aResources      = m_aOutputStreams.front().m_aResourceDict;
10504     rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX;
10505     rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY;
10506     rTile.m_aTransform.matrix[2] = aTranslation.Width();
10507     rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX;
10508     rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY;
10509     rTile.m_aTransform.matrix[5] = -aTranslation.Height();
10510     // caution: endRedirect pops the stream, so do this last
10511     rTile.m_pTilingStream   = dynamic_cast<SvMemoryStream*>(endRedirect());
10512     // FIXME: bound rect will not work with rotated matrix
10513     rTile.m_aRectangle      = Rectangle( Point(0,0), aConvertRect.GetSize() );
10514     rTile.m_aCellSize       = aConvertRect.GetSize();
10515 
10516     OStringBuffer aObjName( 16 );
10517     aObjName.append( 'P' );
10518     aObjName.append( rTile.m_nObject );
10519     pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject );
10520     return nTilingId;
10521 }
10522 
10523 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill )
10524 {
10525     if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() )
10526         return;
10527 
10528     m_aPages.back().endStream();
10529     sal_Int32 nXObject = createObject();
10530     OStringBuffer aNameBuf( 16 );
10531     aNameBuf.append( "Pol" );
10532     aNameBuf.append( nXObject );
10533     OString aObjName( aNameBuf.makeStringAndClear() );
10534     Rectangle aObjRect;
10535     if( updateObject( nXObject ) )
10536     {
10537         // get bounding rect of object
10538         PolyPolygon aSubDiv;
10539         rPolyPoly.AdaptiveSubdivide( aSubDiv );
10540         aObjRect = aSubDiv.GetBoundRect();
10541         Rectangle aConvObjRect( aObjRect );
10542         m_aPages.back().convertRect( aConvObjRect );
10543 
10544         // move polypolygon to bottom left of page
10545         PolyPolygon aLocalPath( rPolyPoly );
10546         sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72;
10547         sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72;
10548         Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode );
10549         sal_Int32 nXOff = aObjRect.Left();
10550         sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom();
10551         aLocalPath.Move( -nXOff, nYOff );
10552 
10553         // prepare XObject's content stream
10554         OStringBuffer aStream( 512 );
10555         aStream.append( "/Pattern cs /P" );
10556         aStream.append( m_aTilings[ nPattern ].m_nObject );
10557         aStream.append( " scn\n" );
10558         m_aPages.back().appendPolyPolygon( aLocalPath, aStream );
10559         aStream.append( bEOFill ? "f*" : "f" );
10560         SvMemoryStream aMemStream( aStream.getLength() );
10561         aMemStream.Write( aStream.getStr(), aStream.getLength() );
10562         bool bDeflate = compressStream( &aMemStream );
10563         aMemStream.Seek( STREAM_SEEK_TO_END );
10564         sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell();
10565         aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
10566 
10567         // add new XObject to global resource dict
10568         m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject;
10569 
10570         // write XObject
10571         OStringBuffer aLine( 512 );
10572         aLine.append( nXObject );
10573         aLine.append( " 0 obj\n"
10574                       "<</Type/XObject/Subtype/Form/BBox[0 0 " );
10575         appendFixedInt( aConvObjRect.GetWidth(), aLine );
10576         aLine.append( ' ' );
10577         appendFixedInt( aConvObjRect.GetHeight(), aLine );
10578         aLine.append( "]/Length " );
10579         aLine.append( nStreamLen );
10580         if( bDeflate )
10581             aLine.append( "/Filter/FlateDecode" );
10582         aLine.append( ">>\n"
10583                       "stream\n" );
10584         writeBuffer( aLine.getStr(), aLine.getLength() );
10585         checkAndEnableStreamEncryption( nXObject );
10586         writeBuffer( aMemStream.GetData(), nStreamLen );
10587         disableStreamEncryption();
10588         writeBuffer( "\nendstream\nendobj\n\n", 19 );
10589     }
10590     m_aPages.back().beginStream();
10591     OStringBuffer aLine( 80 );
10592     aLine.append( "q 1 0 0 1 " );
10593     m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine );
10594     aLine.append( " cm/" );
10595     aLine.append( aObjName );
10596     aLine.append( " Do Q\n" );
10597     writeBuffer( aLine.getStr(), aLine.getLength() );
10598 }
10599 
10600 void PDFWriterImpl::updateGraphicsState()
10601 {
10602     OStringBuffer aLine( 256 );
10603     GraphicsState& rNewState = m_aGraphicsStack.front();
10604     // first set clip region since it might invalidate everything else
10605 
10606     if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) )
10607     {
10608         rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion;
10609 
10610         if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion ||
10611             ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) )
10612         {
10613             if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() )
10614             {
10615                 aLine.append( "Q " );
10616                 // invalidate everything but the clip region
10617                 m_aCurrentPDFState = GraphicsState();
10618                 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion);
10619             }
10620             if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() )
10621             {
10622                 // clip region is always stored in private PDF mapmode
10623                 MapMode aNewMapMode = rNewState.m_aMapMode;
10624                 rNewState.m_aMapMode = m_aMapMode;
10625                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10626                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10627 
10628                 aLine.append( "q " );
10629                 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine );
10630                 aLine.append( "W* n\n" );
10631                 rNewState.m_aMapMode = aNewMapMode;
10632                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10633                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10634             }
10635         }
10636     }
10637 
10638     if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) )
10639     {
10640         rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode;
10641         getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10642     }
10643 
10644     if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) )
10645     {
10646         rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont;
10647         getReferenceDevice()->SetFont( rNewState.m_aFont );
10648         getReferenceDevice()->ImplNewFont();
10649     }
10650 
10651     if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) )
10652     {
10653         rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode;
10654         getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
10655     }
10656 
10657     if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) )
10658     {
10659         rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage;
10660         getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
10661     }
10662 
10663     if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) )
10664     {
10665         rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor;
10666         if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
10667             rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
10668         {
10669             appendStrokingColor( rNewState.m_aLineColor, aLine );
10670             aLine.append( "\n" );
10671         }
10672     }
10673 
10674     if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) )
10675     {
10676         rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor;
10677         if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
10678             rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
10679         {
10680             appendNonStrokingColor( rNewState.m_aFillColor, aLine );
10681             aLine.append( "\n" );
10682         }
10683     }
10684 
10685     if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) )
10686     {
10687         rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent;
10688         if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent )
10689         {
10690             // TODO: switch extended graphicsstate
10691         }
10692     }
10693 
10694     // everything is up to date now
10695     m_aCurrentPDFState = m_aGraphicsStack.front();
10696     if( aLine.getLength() )
10697         writeBuffer( aLine.getStr(), aLine.getLength() );
10698 }
10699 
10700 /* #i47544# imitate OutputDevice behaviour:
10701 *  if a font with a nontransparent color is set, it overwrites the current
10702 *  text color. OTOH setting the text color will overwrite the color of the font.
10703 */
10704 void PDFWriterImpl::setFont( const Font& rFont )
10705 {
10706     Color aColor = rFont.GetColor();
10707     if( aColor == Color( COL_TRANSPARENT ) )
10708         aColor = m_aGraphicsStack.front().m_aFont.GetColor();
10709     m_aGraphicsStack.front().m_aFont = rFont;
10710     m_aGraphicsStack.front().m_aFont.SetColor( aColor );
10711     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont;
10712 }
10713 
10714 void PDFWriterImpl::push( sal_uInt16 nFlags )
10715 {
10716     OSL_ENSURE( m_aGraphicsStack.size() > 0, "invalid graphics stack" );
10717     m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
10718     m_aGraphicsStack.front().m_nFlags = nFlags;
10719 }
10720 
10721 void PDFWriterImpl::pop()
10722 {
10723     OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" );
10724     if( m_aGraphicsStack.size() < 2 )
10725         return;
10726 
10727     GraphicsState aState = m_aGraphicsStack.front();
10728     m_aGraphicsStack.pop_front();
10729     GraphicsState& rOld = m_aGraphicsStack.front();
10730 
10731     // move those parameters back that were not pushed
10732     // in the first place
10733     if( ! (aState.m_nFlags & PUSH_LINECOLOR) )
10734         setLineColor( aState.m_aLineColor );
10735     if( ! (aState.m_nFlags & PUSH_FILLCOLOR) )
10736         setFillColor( aState.m_aFillColor );
10737     if( ! (aState.m_nFlags & PUSH_FONT) )
10738         setFont( aState.m_aFont );
10739     if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) )
10740         setTextColor( aState.m_aFont.GetColor() );
10741     if( ! (aState.m_nFlags & PUSH_MAPMODE) )
10742         setMapMode( aState.m_aMapMode );
10743     if( ! (aState.m_nFlags & PUSH_CLIPREGION) )
10744     {
10745         // do not use setClipRegion here
10746         // it would convert again assuming the current mapmode
10747         rOld.m_aClipRegion = aState.m_aClipRegion;
10748         rOld.m_bClipRegion = aState.m_bClipRegion;
10749     }
10750     if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) )
10751         setTextLineColor( aState.m_aTextLineColor );
10752     if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) )
10753         setOverlineColor( aState.m_aOverlineColor );
10754     if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) )
10755         setTextAlign( aState.m_aFont.GetAlign() );
10756     if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) )
10757         setTextFillColor( aState.m_aFont.GetFillColor() );
10758     if( ! (aState.m_nFlags & PUSH_REFPOINT) )
10759     {
10760         // what ?
10761     }
10762     // invalidate graphics state
10763     m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U);
10764 }
10765 
10766 void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
10767 {
10768     m_aGraphicsStack.front().m_aMapMode = rMapMode;
10769     getReferenceDevice()->SetMapMode( rMapMode );
10770     m_aCurrentPDFState.m_aMapMode = rMapMode;
10771 }
10772 
10773 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10774 {
10775     basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10776     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10777     m_aGraphicsStack.front().m_aClipRegion = aRegion;
10778     m_aGraphicsStack.front().m_bClipRegion = true;
10779     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10780 }
10781 
10782 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
10783 {
10784     if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() )
10785     {
10786         Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10787                                    m_aMapMode,
10788                                    getReferenceDevice(),
10789                                    Point( nX, nY ) ) );
10790         aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10791                                m_aMapMode,
10792                                getReferenceDevice(),
10793                                Point() );
10794         basegfx::B2DHomMatrix aMat;
10795         aMat.translate( aPoint.X(), aPoint.Y() );
10796         m_aGraphicsStack.front().m_aClipRegion.transform( aMat );
10797         m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10798     }
10799 }
10800 
10801 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect )
10802 {
10803     basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect(
10804         basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) );
10805     return intersectClipRegion( aRect );
10806 }
10807 
10808 
10809 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10810 {
10811     basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) );
10812     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10813     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10814     if( m_aGraphicsStack.front().m_bClipRegion )
10815     {
10816         basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) );
10817         aRegion = basegfx::tools::prepareForPolygonOperation( aRegion );
10818         m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion );
10819     }
10820     else
10821     {
10822         m_aGraphicsStack.front().m_aClipRegion = aRegion;
10823         m_aGraphicsStack.front().m_bClipRegion = true;
10824     }
10825     return true;
10826 }
10827 
10828 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
10829 {
10830     if( nPageNr < 0 )
10831         nPageNr = m_nCurrentPage;
10832 
10833     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10834         return;
10835 
10836     m_aNotes.push_back( PDFNoteEntry() );
10837     m_aNotes.back().m_nObject		= createObject();
10838     m_aNotes.back().m_aContents		= rNote;
10839     m_aNotes.back().m_aRect			= rRect;
10840     // convert to default user space now, since the mapmode may change
10841     m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
10842 
10843     // insert note to page's annotation list
10844     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
10845 }
10846 
10847 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr )
10848 {
10849     if( nPageNr < 0 )
10850         nPageNr = m_nCurrentPage;
10851 
10852     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10853         return -1;
10854 
10855     sal_Int32 nRet = m_aLinks.size();
10856 
10857     m_aLinks.push_back( PDFLink() );
10858     m_aLinks.back().m_nObject	= createObject();
10859     m_aLinks.back().m_nPage		= nPageNr;
10860     m_aLinks.back().m_aRect		= rRect;
10861     // convert to default user space now, since the mapmode may change
10862     m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
10863 
10864     // insert link to page's annotation list
10865     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
10866 
10867     return nRet;
10868 }
10869 
10870 //--->i56629
10871 sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10872 {
10873     if( nPageNr < 0 )
10874         nPageNr = m_nCurrentPage;
10875 
10876     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10877         return -1;
10878 
10879     sal_Int32 nRet = m_aNamedDests.size();
10880 
10881     m_aNamedDests.push_back( PDFNamedDest() );
10882     m_aNamedDests.back().m_aDestName = sDestName;
10883     m_aNamedDests.back().m_nPage = nPageNr;
10884     m_aNamedDests.back().m_eType = eType;
10885     m_aNamedDests.back().m_aRect = rRect;
10886     // convert to default user space now, since the mapmode may change
10887     m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
10888 
10889     return nRet;
10890 }
10891 //<---i56629
10892 
10893 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10894 {
10895     if( nPageNr < 0 )
10896         nPageNr = m_nCurrentPage;
10897 
10898     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10899         return -1;
10900 
10901     sal_Int32 nRet = m_aDests.size();
10902 
10903     m_aDests.push_back( PDFDest() );
10904     m_aDests.back().m_nPage = nPageNr;
10905     m_aDests.back().m_eType	= eType;
10906     m_aDests.back().m_aRect = rRect;
10907     // convert to default user space now, since the mapmode may change
10908     m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
10909 
10910     return nRet;
10911 }
10912 
10913 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10914 {
10915     return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType );
10916 }
10917 
10918 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
10919 {
10920     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10921         return -1;
10922     if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
10923         return -2;
10924 
10925     m_aLinks[ nLinkId ].m_nDest = nDestId;
10926 
10927     return 0;
10928 }
10929 
10930 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
10931 {
10932     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10933         return -1;
10934 
10935     m_aLinks[ nLinkId ].m_nDest	= -1;
10936 
10937     using namespace ::com::sun::star;
10938 
10939     if (!m_xTrans.is())
10940     {
10941         uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() );
10942         if( xFact.is() )
10943         {
10944             m_xTrans = uno::Reference < util::XURLTransformer >(
10945                 xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY );
10946         }
10947     }
10948 
10949     util::URL aURL;
10950     aURL.Complete = rURL;
10951 
10952     if (m_xTrans.is())
10953         m_xTrans->parseStrict( aURL );
10954 
10955     m_aLinks[ nLinkId ].m_aURL	= aURL.Complete;
10956 
10957     return 0;
10958 }
10959 
10960 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
10961 {
10962     m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
10963 }
10964 
10965 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
10966 {
10967     // create new item
10968     sal_Int32 nNewItem = m_aOutline.size();
10969     m_aOutline.push_back( PDFOutlineEntry() );
10970 
10971     // set item attributes
10972     setOutlineItemParent( nNewItem, nParent );
10973     setOutlineItemText( nNewItem, rText );
10974     setOutlineItemDest( nNewItem, nDestID );
10975 
10976     return nNewItem;
10977 }
10978 
10979 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
10980 {
10981     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10982         return -1;
10983 
10984     int nRet = 0;
10985 
10986     if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
10987     {
10988         nNewParent = 0;
10989         nRet = -2;
10990     }
10991     // remove item from previous parent
10992     sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID;
10993     if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() )
10994     {
10995         PDFOutlineEntry& rParent = m_aOutline[ nParentID ];
10996 
10997         for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin();
10998              it != rParent.m_aChildren.end(); ++it )
10999         {
11000             if( *it == nItem )
11001             {
11002                 rParent.m_aChildren.erase( it );
11003                 break;
11004             }
11005         }
11006     }
11007 
11008     // insert item to new parent's list of children
11009     m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
11010 
11011     return nRet;
11012 }
11013 
11014 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
11015 {
11016     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
11017         return -1;
11018 
11019     m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText );
11020     return 0;
11021 }
11022 
11023 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
11024 {
11025     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
11026         return -1;
11027     if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
11028         return -2;
11029     m_aOutline[nItem].m_nDestID = nDestID;
11030     return 0;
11031 }
11032 
11033 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
11034 {
11035     static std::map< PDFWriter::StructElement, const char* > aTagStrings;
11036     if( aTagStrings.empty() )
11037     {
11038         aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
11039         aTagStrings[ PDFWriter::Document ]		= "Document";
11040         aTagStrings[ PDFWriter::Part ]			= "Part";
11041         aTagStrings[ PDFWriter::Article ]		= "Art";
11042         aTagStrings[ PDFWriter::Section ]		= "Sect";
11043         aTagStrings[ PDFWriter::Division ]		= "Div";
11044         aTagStrings[ PDFWriter::BlockQuote ]	= "BlockQuote";
11045         aTagStrings[ PDFWriter::Caption ]		= "Caption";
11046         aTagStrings[ PDFWriter::TOC ]			= "TOC";
11047         aTagStrings[ PDFWriter::TOCI ]			= "TOCI";
11048         aTagStrings[ PDFWriter::Index ]			= "Index";
11049         aTagStrings[ PDFWriter::Paragraph ]		= "P";
11050         aTagStrings[ PDFWriter::Heading ]		= "H";
11051         aTagStrings[ PDFWriter::H1 ]			= "H1";
11052         aTagStrings[ PDFWriter::H2 ]			= "H2";
11053         aTagStrings[ PDFWriter::H3 ]			= "H3";
11054         aTagStrings[ PDFWriter::H4 ]			= "H4";
11055         aTagStrings[ PDFWriter::H5 ]			= "H5";
11056         aTagStrings[ PDFWriter::H6 ]			= "H6";
11057         aTagStrings[ PDFWriter::List ]			= "L";
11058         aTagStrings[ PDFWriter::ListItem ]		= "LI";
11059         aTagStrings[ PDFWriter::LILabel ]		= "Lbl";
11060         aTagStrings[ PDFWriter::LIBody ]		= "LBody";
11061         aTagStrings[ PDFWriter::Table ]			= "Table";
11062         aTagStrings[ PDFWriter::TableRow ]		= "TR";
11063         aTagStrings[ PDFWriter::TableHeader ]	= "TH";
11064         aTagStrings[ PDFWriter::TableData ]		= "TD";
11065         aTagStrings[ PDFWriter::Span ]			= "Span";
11066         aTagStrings[ PDFWriter::Quote ]			= "Quote";
11067         aTagStrings[ PDFWriter::Note ]			= "Note";
11068         aTagStrings[ PDFWriter::Reference ]		= "Reference";
11069         aTagStrings[ PDFWriter::BibEntry ]		= "BibEntry";
11070         aTagStrings[ PDFWriter::Code ]			= "Code";
11071         aTagStrings[ PDFWriter::Link ]			= "Link";
11072         aTagStrings[ PDFWriter::Figure ]		= "Figure";
11073         aTagStrings[ PDFWriter::Formula ]		= "Formula";
11074         aTagStrings[ PDFWriter::Form ]			= "Form";
11075     }
11076 
11077     std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
11078 
11079     return it != aTagStrings.end() ? it->second : "Div";
11080 }
11081 
11082 void PDFWriterImpl::beginStructureElementMCSeq()
11083 {
11084     if(	m_bEmitStructure &&
11085         m_nCurrentStructElement > 0 && // StructTreeRoot
11086         ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11087         )
11088     {
11089         PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
11090         OStringBuffer aLine( 128 );
11091         sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
11092         aLine.append( "/" );
11093         if( rEle.m_aAlias.getLength() > 0 )
11094             aLine.append( rEle.m_aAlias );
11095         else
11096             aLine.append( getStructureTag( rEle.m_eType ) );
11097         aLine.append( "<</MCID " );
11098         aLine.append( nMCID );
11099         aLine.append( ">>BDC\n" );
11100         writeBuffer( aLine.getStr(), aLine.getLength() );
11101 
11102         // update the element's content list
11103 #if OSL_DEBUG_LEVEL > 1
11104         fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n",
11105                  nMCID,
11106                  m_aPages[ m_nCurrentPage ].m_nPageObject,
11107                  rEle.m_nFirstPageObject );
11108 #endif
11109         rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) );
11110         // update the page's mcid parent list
11111         m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
11112         // mark element MC sequence as open
11113         rEle.m_bOpenMCSeq = true;
11114     }
11115     // handle artifacts
11116     else if( ! m_bEmitStructure && m_aContext.Tagged &&
11117                m_nCurrentStructElement > 0 &&
11118                m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
11119              ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11120              )
11121     {
11122         OStringBuffer aLine( 128 );
11123         aLine.append( "/Artifact BMC\n" );
11124         writeBuffer( aLine.getStr(), aLine.getLength() );
11125         // mark element MC sequence as open
11126         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
11127     }
11128 }
11129 
11130 void PDFWriterImpl::endStructureElementMCSeq()
11131 {
11132     if( m_nCurrentStructElement > 0 && // StructTreeRoot
11133         ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
11134         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
11135         )
11136     {
11137         writeBuffer( "EMC\n", 4 );
11138         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
11139     }
11140 }
11141 
11142 bool PDFWriterImpl::checkEmitStructure()
11143 {
11144     bool bEmit = false;
11145     if( m_aContext.Tagged )
11146     {
11147         bEmit = true;
11148         sal_Int32 nEle = m_nCurrentStructElement;
11149         while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
11150         {
11151             if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
11152             {
11153                 bEmit = false;
11154                 break;
11155             }
11156             nEle = m_aStructure[ nEle ].m_nParentElement;
11157         }
11158     }
11159     return bEmit;
11160 }
11161 
11162 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias )
11163 {
11164     if( m_nCurrentPage < 0 )
11165         return -1;
11166 
11167     if( ! m_aContext.Tagged )
11168         return -1;
11169 
11170     // close eventual current MC sequence
11171     endStructureElementMCSeq();
11172 
11173     if( m_nCurrentStructElement == 0 &&
11174         eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
11175     {
11176         // struct tree root hit, but not beginning document
11177         // this might happen with setCurrentStructureElement
11178         // silently insert structure into document again if one properly exists
11179         if( ! m_aStructure[ 0 ].m_aChildren.empty() )
11180         {
11181             PDFWriter::StructElement childType = PDFWriter::NonStructElement;
11182             sal_Int32 nNewCurElement = 0;
11183             const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
11184             for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
11185                  childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
11186             {
11187                 nNewCurElement = *it;
11188                 childType = m_aStructure[ nNewCurElement ].m_eType;
11189             }
11190             if( childType == PDFWriter::Document )
11191             {
11192                 m_nCurrentStructElement = nNewCurElement;
11193                 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" );
11194             }
11195             else {
11196                 DBG_ERROR( "document structure in disorder !" );
11197             }
11198         }
11199         else {
11200             DBG_ERROR( "PDF document structure MUST be contained in a Document element" );
11201         }
11202     }
11203 
11204     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11205     m_aStructure.push_back( PDFStructureElement() );
11206     PDFStructureElement& rEle = m_aStructure.back();
11207     rEle.m_eType			= eType;
11208     rEle.m_nOwnElement		= nNewId;
11209     rEle.m_nParentElement	= m_nCurrentStructElement;
11210     rEle.m_nFirstPageObject	= m_aPages[ m_nCurrentPage ].m_nPageObject;
11211     m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
11212     m_nCurrentStructElement = nNewId;
11213 
11214     // handle alias names
11215     if( rAlias.getLength() && eType != PDFWriter::NonStructElement )
11216     {
11217         OStringBuffer aNameBuf( rAlias.getLength() );
11218         appendName( rAlias, aNameBuf );
11219         OString aAliasName( aNameBuf.makeStringAndClear() );
11220         rEle.m_aAlias = aAliasName;
11221         m_aRoleMap[ aAliasName ] = getStructureTag( eType );
11222     }
11223 
11224 #if OSL_DEBUG_LEVEL > 1
11225     OStringBuffer aLine( "beginStructureElement " );
11226     aLine.append( m_nCurrentStructElement );
11227     aLine.append( ": " );
11228     aLine.append( getStructureTag( eType ) );
11229     if( rEle.m_aAlias.getLength() )
11230     {
11231         aLine.append( " aliased as \"" );
11232         aLine.append( rEle.m_aAlias );
11233         aLine.append( '\"' );
11234     }
11235     emitComment( aLine.getStr() );
11236 #endif
11237 
11238     // check whether to emit structure henceforth
11239     m_bEmitStructure = checkEmitStructure();
11240 
11241     if( m_bEmitStructure ) // don't create nonexistant objects
11242     {
11243         rEle.m_nObject		= createObject();
11244         // update parent's kids list
11245         m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject );
11246     }
11247     return nNewId;
11248 }
11249 
11250 void PDFWriterImpl::endStructureElement()
11251 {
11252     if( m_nCurrentPage < 0 )
11253         return;
11254 
11255     if( ! m_aContext.Tagged )
11256         return;
11257 
11258     if( m_nCurrentStructElement == 0 )
11259     {
11260         // hit the struct tree root, that means there is an endStructureElement
11261         // without corresponding beginStructureElement
11262         return;
11263     }
11264 
11265     // end the marked content sequence
11266     endStructureElementMCSeq();
11267 
11268 #if OSL_DEBUG_LEVEL > 1
11269     OStringBuffer aLine( "endStructureElement " );
11270     aLine.append( m_nCurrentStructElement );
11271     aLine.append( ": " );
11272     aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11273     if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
11274     {
11275         aLine.append( " aliased as \"" );
11276         aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11277         aLine.append( '\"' );
11278     }
11279 #endif
11280 
11281     // "end" the structure element, the parent becomes current element
11282     m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
11283 
11284     // check whether to emit structure henceforth
11285     m_bEmitStructure = checkEmitStructure();
11286 
11287 #if OSL_DEBUG_LEVEL > 1
11288     if( m_bEmitStructure )
11289         emitComment( aLine.getStr() );
11290 #endif
11291 }
11292 
11293 //---> i94258
11294 /*
11295  * This function adds an internal structure list container to overcome the 8191 elements array limitation
11296  * in kids element emission.
11297  * Recursive function
11298  *
11299  */
11300 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
11301 {
11302     if( rEle.m_eType == PDFWriter::NonStructElement &&
11303         rEle.m_nOwnElement != rEle.m_nParentElement )
11304         return;
11305 
11306     for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
11307     {
11308         if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
11309         {
11310             PDFStructureElement& rChild = m_aStructure[ *it ];
11311             if( rChild.m_eType != PDFWriter::NonStructElement )
11312             {
11313                 //triggered when a child of the rEle element is found
11314                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
11315                     addInternalStructureContainer( rChild );//examine the child
11316                 else
11317                 {
11318                     DBG_ERROR( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
11319 #if OSL_DEBUG_LEVEL > 1
11320                     fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
11321 #endif
11322                 }
11323             }
11324         }
11325         else
11326         {
11327             DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
11328 #if OSL_DEBUG_LEVEL > 1
11329             fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
11330 #endif
11331         }
11332     }
11333 
11334     if( rEle.m_nOwnElement != rEle.m_nParentElement )
11335     {
11336         if( !rEle.m_aKids.empty() )
11337         {
11338             if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
11339                 //then we need to add the containers for the kids elements
11340                 // a list to be used for the new kid element
11341                 std::list< PDFStructureElementKid > aNewKids;
11342                 std::list< sal_Int32 > aNewChildren;
11343 
11344                 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
11345                 OStringBuffer aNameBuf( "Div" );
11346                 OString aAliasName( aNameBuf.makeStringAndClear() );
11347                 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
11348 
11349                 while( rEle.m_aKids.size() > ncMaxPDFArraySize )
11350                 {
11351                     sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
11352                     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11353                     m_aStructure.push_back( PDFStructureElement() );
11354                     PDFStructureElement& rEleNew = m_aStructure.back();
11355                     rEleNew.m_aAlias            = aAliasName;
11356                     rEleNew.m_eType			    = PDFWriter::Division; // a new Div type container
11357                     rEleNew.m_nOwnElement		= nNewId;
11358                     rEleNew.m_nParentElement	= nCurrentStructElement;
11359                     //inherit the same page as the first child to be reparented
11360                     rEleNew.m_nFirstPageObject	= m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
11361                     rEleNew.m_nObject           = createObject();//assign a PDF object number
11362                     //add the object to the kid list of the parent
11363                     aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) );
11364                     aNewChildren.push_back( nNewId );
11365 
11366                     std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
11367                     std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
11368                     advance( aChildEndIt, ncMaxPDFArraySize );
11369                     advance( aKidEndIt, ncMaxPDFArraySize );
11370 
11371                     rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
11372                                             rEle.m_aKids,
11373                                             rEle.m_aKids.begin(),
11374                                             aKidEndIt );
11375                     rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
11376                                                 rEle.m_aChildren,
11377                                                 rEle.m_aChildren.begin(),
11378                                                 aChildEndIt );
11379                     // set the kid's new parent
11380                     for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
11381                          it != rEleNew.m_aChildren.end(); ++it )
11382                     {
11383                         m_aStructure[ *it ].m_nParentElement = nNewId;
11384                     }
11385                 }
11386                 //finally add the new kids resulting from the container added
11387                 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
11388                 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
11389             }
11390         }
11391     }
11392 }
11393 //<--- i94258
11394 
11395 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
11396 {
11397     bool bSuccess = false;
11398 
11399     if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
11400     {
11401         // end eventual previous marked content sequence
11402         endStructureElementMCSeq();
11403 
11404         m_nCurrentStructElement = nEle;
11405         m_bEmitStructure = checkEmitStructure();
11406 #if OSL_DEBUG_LEVEL > 1
11407         OStringBuffer aLine( "setCurrentStructureElement " );
11408         aLine.append( m_nCurrentStructElement );
11409         aLine.append( ": " );
11410         aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11411         if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
11412         {
11413             aLine.append( " aliased as \"" );
11414             aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11415             aLine.append( '\"' );
11416         }
11417         if( ! m_bEmitStructure )
11418             aLine.append( " (inside NonStruct)" );
11419         emitComment( aLine.getStr() );
11420 #endif
11421         bSuccess = true;
11422     }
11423 
11424     return bSuccess;
11425 }
11426 
11427 sal_Int32 PDFWriterImpl::getCurrentStructureElement()
11428 {
11429     return m_nCurrentStructElement;
11430 }
11431 
11432 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
11433 {
11434     if( !m_aContext.Tagged )
11435         return false;
11436 
11437     bool bInsert = false;
11438     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11439     {
11440         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11441         switch( eAttr )
11442         {
11443             case PDFWriter::Placement:
11444                 if( eVal == PDFWriter::Block		||
11445                     eVal == PDFWriter::Inline		||
11446                     eVal == PDFWriter::Before		||
11447                     eVal == PDFWriter::Start		||
11448                     eVal == PDFWriter::End )
11449                     bInsert = true;
11450                 break;
11451             case PDFWriter::WritingMode:
11452                 if( eVal == PDFWriter::LrTb			||
11453                     eVal == PDFWriter::RlTb			||
11454                     eVal == PDFWriter::TbRl )
11455                 {
11456                     bInsert = true;
11457                 }
11458                 break;
11459             case PDFWriter::TextAlign:
11460                 if( eVal == PDFWriter::Start		||
11461                     eVal == PDFWriter::Center		||
11462                     eVal == PDFWriter::End			||
11463                     eVal == PDFWriter::Justify )
11464                 {
11465                     if( eType == PDFWriter::Paragraph	||
11466                         eType == PDFWriter::Heading		||
11467                         eType == PDFWriter::H1			||
11468                         eType == PDFWriter::H2			||
11469                         eType == PDFWriter::H3			||
11470                         eType == PDFWriter::H4			||
11471                         eType == PDFWriter::H5			||
11472                         eType == PDFWriter::H6			||
11473                         eType == PDFWriter::List		||
11474                         eType == PDFWriter::ListItem	||
11475                         eType == PDFWriter::LILabel		||
11476                         eType == PDFWriter::LIBody		||
11477                         eType == PDFWriter::Table		||
11478                         eType == PDFWriter::TableRow	||
11479                         eType == PDFWriter::TableHeader	||
11480                         eType == PDFWriter::TableData )
11481                     {
11482                         bInsert = true;
11483                     }
11484                 }
11485                 break;
11486             case PDFWriter::Width:
11487             case PDFWriter::Height:
11488                 if( eVal == PDFWriter::Auto )
11489                 {
11490                     if( eType == PDFWriter::Figure		||
11491                         eType == PDFWriter::Formula		||
11492                         eType == PDFWriter::Form		||
11493                         eType == PDFWriter::Table		||
11494                         eType == PDFWriter::TableHeader	||
11495                         eType == PDFWriter::TableData )
11496                     {
11497                         bInsert = true;
11498                     }
11499                 }
11500                 break;
11501             case PDFWriter::BlockAlign:
11502                 if( eVal == PDFWriter::Before		||
11503                     eVal == PDFWriter::Middle		||
11504                     eVal == PDFWriter::After		||
11505                     eVal == PDFWriter::Justify )
11506                 {
11507                     if( eType == PDFWriter::TableHeader	||
11508                         eType == PDFWriter::TableData )
11509                     {
11510                         bInsert = true;
11511                     }
11512                 }
11513                 break;
11514             case PDFWriter::InlineAlign:
11515                 if( eVal == PDFWriter::Start		||
11516                     eVal == PDFWriter::Center		||
11517                     eVal == PDFWriter::End )
11518                 {
11519                     if( eType == PDFWriter::TableHeader	||
11520                         eType == PDFWriter::TableData )
11521                     {
11522                         bInsert = true;
11523                     }
11524                 }
11525                 break;
11526             case PDFWriter::LineHeight:
11527                 if( eVal == PDFWriter::Normal		||
11528                     eVal == PDFWriter::Auto )
11529                 {
11530                     // only for ILSE and BLSE
11531                     if( eType == PDFWriter::Paragraph	||
11532                         eType == PDFWriter::Heading		||
11533                         eType == PDFWriter::H1			||
11534                         eType == PDFWriter::H2			||
11535                         eType == PDFWriter::H3			||
11536                         eType == PDFWriter::H4			||
11537                         eType == PDFWriter::H5			||
11538                         eType == PDFWriter::H6			||
11539                         eType == PDFWriter::List		||
11540                         eType == PDFWriter::ListItem	||
11541                         eType == PDFWriter::LILabel		||
11542                         eType == PDFWriter::LIBody		||
11543                         eType == PDFWriter::Table		||
11544                         eType == PDFWriter::TableRow	||
11545                         eType == PDFWriter::TableHeader	||
11546                         eType == PDFWriter::TableData	||
11547                         eType == PDFWriter::Span		||
11548                         eType == PDFWriter::Quote		||
11549                         eType == PDFWriter::Note		||
11550                         eType == PDFWriter::Reference	||
11551                         eType == PDFWriter::BibEntry	||
11552                         eType == PDFWriter::Code		||
11553                         eType == PDFWriter::Link )
11554                     {
11555                         bInsert = true;
11556                     }
11557                 }
11558                 break;
11559             case PDFWriter::TextDecorationType:
11560                 if( eVal == PDFWriter::NONE			||
11561                     eVal == PDFWriter::Underline	||
11562                     eVal == PDFWriter::Overline		||
11563                     eVal == PDFWriter::LineThrough )
11564                 {
11565                     // only for ILSE and BLSE
11566                     if( eType == PDFWriter::Paragraph	||
11567                         eType == PDFWriter::Heading		||
11568                         eType == PDFWriter::H1			||
11569                         eType == PDFWriter::H2			||
11570                         eType == PDFWriter::H3			||
11571                         eType == PDFWriter::H4			||
11572                         eType == PDFWriter::H5			||
11573                         eType == PDFWriter::H6			||
11574                         eType == PDFWriter::List		||
11575                         eType == PDFWriter::ListItem	||
11576                         eType == PDFWriter::LILabel		||
11577                         eType == PDFWriter::LIBody		||
11578                         eType == PDFWriter::Table		||
11579                         eType == PDFWriter::TableRow	||
11580                         eType == PDFWriter::TableHeader	||
11581                         eType == PDFWriter::TableData	||
11582                         eType == PDFWriter::Span		||
11583                         eType == PDFWriter::Quote		||
11584                         eType == PDFWriter::Note		||
11585                         eType == PDFWriter::Reference	||
11586                         eType == PDFWriter::BibEntry	||
11587                         eType == PDFWriter::Code		||
11588                         eType == PDFWriter::Link )
11589                     {
11590                         bInsert = true;
11591                     }
11592                 }
11593                 break;
11594             case PDFWriter::ListNumbering:
11595                 if( eVal == PDFWriter::NONE			||
11596                     eVal == PDFWriter::Disc			||
11597                     eVal == PDFWriter::Circle		||
11598                     eVal == PDFWriter::Square		||
11599                     eVal == PDFWriter::Decimal		||
11600                     eVal == PDFWriter::UpperRoman	||
11601                     eVal == PDFWriter::LowerRoman	||
11602                     eVal == PDFWriter::UpperAlpha	||
11603                     eVal == PDFWriter::LowerAlpha )
11604                 {
11605                     if( eType == PDFWriter::List )
11606                         bInsert = true;
11607                 }
11608                 break;
11609             default: break;
11610         }
11611     }
11612 
11613     if( bInsert )
11614         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
11615 #if OSL_DEBUG_LEVEL > 1
11616     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11617         fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
11618                  getAttributeTag( eAttr ),
11619                  getAttributeValueTag( eVal ),
11620                  getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11621                  m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
11622                  );
11623 #endif
11624 
11625     return bInsert;
11626 }
11627 
11628 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
11629 {
11630     if( ! m_aContext.Tagged )
11631         return false;
11632 
11633     bool bInsert = false;
11634     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11635     {
11636         if( eAttr == PDFWriter::Language )
11637         {
11638             m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue );
11639             return true;
11640         }
11641 
11642         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11643         switch( eAttr )
11644         {
11645             case PDFWriter::SpaceBefore:
11646             case PDFWriter::SpaceAfter:
11647             case PDFWriter::StartIndent:
11648             case PDFWriter::EndIndent:
11649                 // just for BLSE
11650                 if( eType == PDFWriter::Paragraph	||
11651                     eType == PDFWriter::Heading		||
11652                     eType == PDFWriter::H1			||
11653                     eType == PDFWriter::H2			||
11654                     eType == PDFWriter::H3			||
11655                     eType == PDFWriter::H4			||
11656                     eType == PDFWriter::H5			||
11657                     eType == PDFWriter::H6			||
11658                     eType == PDFWriter::List		||
11659                     eType == PDFWriter::ListItem	||
11660                     eType == PDFWriter::LILabel		||
11661                     eType == PDFWriter::LIBody		||
11662                     eType == PDFWriter::Table		||
11663                     eType == PDFWriter::TableRow	||
11664                     eType == PDFWriter::TableHeader	||
11665                     eType == PDFWriter::TableData )
11666                 {
11667                     bInsert = true;
11668                 }
11669                 break;
11670             case PDFWriter::TextIndent:
11671                 // paragraph like BLSE and additional elements
11672                 if( eType == PDFWriter::Paragraph	||
11673                     eType == PDFWriter::Heading		||
11674                     eType == PDFWriter::H1			||
11675                     eType == PDFWriter::H2			||
11676                     eType == PDFWriter::H3			||
11677                     eType == PDFWriter::H4			||
11678                     eType == PDFWriter::H5			||
11679                     eType == PDFWriter::H6			||
11680                     eType == PDFWriter::LILabel		||
11681                     eType == PDFWriter::LIBody		||
11682                     eType == PDFWriter::TableHeader	||
11683                     eType == PDFWriter::TableData )
11684                 {
11685                     bInsert = true;
11686                 }
11687                 break;
11688             case PDFWriter::Width:
11689             case PDFWriter::Height:
11690                 if( eType == PDFWriter::Figure		||
11691                     eType == PDFWriter::Formula		||
11692                     eType == PDFWriter::Form		||
11693                     eType == PDFWriter::Table		||
11694                     eType == PDFWriter::TableHeader	||
11695                     eType == PDFWriter::TableData )
11696                 {
11697                     bInsert = true;
11698                 }
11699                 break;
11700             case PDFWriter::LineHeight:
11701             case PDFWriter::BaselineShift:
11702                 // only for ILSE and BLSE
11703                 if( eType == PDFWriter::Paragraph	||
11704                     eType == PDFWriter::Heading		||
11705                     eType == PDFWriter::H1			||
11706                     eType == PDFWriter::H2			||
11707                     eType == PDFWriter::H3			||
11708                     eType == PDFWriter::H4			||
11709                     eType == PDFWriter::H5			||
11710                     eType == PDFWriter::H6			||
11711                     eType == PDFWriter::List		||
11712                     eType == PDFWriter::ListItem	||
11713                     eType == PDFWriter::LILabel		||
11714                     eType == PDFWriter::LIBody		||
11715                     eType == PDFWriter::Table		||
11716                     eType == PDFWriter::TableRow	||
11717                     eType == PDFWriter::TableHeader	||
11718                     eType == PDFWriter::TableData	||
11719                     eType == PDFWriter::Span		||
11720                     eType == PDFWriter::Quote		||
11721                     eType == PDFWriter::Note		||
11722                     eType == PDFWriter::Reference	||
11723                     eType == PDFWriter::BibEntry	||
11724                     eType == PDFWriter::Code		||
11725                     eType == PDFWriter::Link )
11726                 {
11727                         bInsert = true;
11728                 }
11729                 break;
11730             case PDFWriter::RowSpan:
11731             case PDFWriter::ColSpan:
11732                 // only for table cells
11733                 if( eType == PDFWriter::TableHeader	||
11734                     eType == PDFWriter::TableData )
11735                 {
11736                     bInsert = true;
11737                 }
11738                 break;
11739             case PDFWriter::LinkAnnotation:
11740                 if( eType == PDFWriter::Link )
11741                     bInsert = true;
11742                 break;
11743             default: break;
11744         }
11745     }
11746 
11747     if( bInsert )
11748         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
11749 #if OSL_DEBUG_LEVEL > 1
11750     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11751         fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
11752                  getAttributeTag( eAttr ),
11753                  (int)nValue,
11754                  getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11755                  m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() );
11756 #endif
11757 
11758     return bInsert;
11759 }
11760 
11761 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect )
11762 {
11763     sal_Int32 nPageNr = m_nCurrentPage;
11764     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
11765         return;
11766 
11767 
11768     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11769     {
11770         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11771         if( eType == PDFWriter::Figure		||
11772             eType == PDFWriter::Formula		||
11773             eType == PDFWriter::Form		||
11774             eType == PDFWriter::Table )
11775         {
11776             m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
11777             // convert to default user space now, since the mapmode may change
11778             m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
11779         }
11780     }
11781 }
11782 
11783 void PDFWriterImpl::setActualText( const String& rText )
11784 {
11785     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11786     {
11787         m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
11788     }
11789 }
11790 
11791 void PDFWriterImpl::setAlternateText( const String& rText )
11792 {
11793     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11794     {
11795         m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
11796     }
11797 }
11798 
11799 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
11800 {
11801     if( nPageNr < 0 )
11802         nPageNr = m_nCurrentPage;
11803 
11804     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11805         return;
11806 
11807     m_aPages[ nPageNr ].m_nDuration = nSeconds;
11808 }
11809 
11810 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
11811 {
11812     if( nPageNr < 0 )
11813         nPageNr = m_nCurrentPage;
11814 
11815     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11816         return;
11817 
11818     m_aPages[ nPageNr ].m_eTransition	= eType;
11819     m_aPages[ nPageNr ].m_nTransTime	= nMilliSec;
11820 }
11821 
11822 void PDFWriterImpl::ensureUniqueRadioOnValues()
11823 {
11824     // loop over radio groups
11825     for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
11826          group != m_aRadioGroupWidgets.end(); ++group )
11827     {
11828         PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
11829         // check whether all kids have a unique OnValue
11830         std::hash_map< OUString, sal_Int32, OUStringHash > aOnValues;
11831         int nChildren = rGroupWidget.m_aKidsIndex.size();
11832         bool bIsUnique = true;
11833         for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
11834         {
11835             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11836             const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
11837             #if OSL_DEBUG_LEVEL > 1
11838             fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() );
11839             #endif
11840             if( aOnValues.find( rVal ) == aOnValues.end() )
11841             {
11842                 aOnValues[ rVal ] = 1;
11843             }
11844             else
11845             {
11846                 bIsUnique = false;
11847             }
11848         }
11849         if( ! bIsUnique )
11850         {
11851             #if OSL_DEBUG_LEVEL > 1
11852             fprintf( stderr, "enforcing unique OnValues\n" );
11853             #endif
11854             // make unique by using ascending OnValues
11855             for( int nKid = 0; nKid < nChildren; nKid++ )
11856             {
11857                 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11858                 PDFWidget& rKid = m_aWidgets[nKidIndex];
11859                 rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) );
11860                 if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11861                     rKid.m_aValue = rKid.m_aOnValue;
11862             }
11863         }
11864         // finally move the "Yes" appearance to the OnValue appearance
11865         for( int nKid = 0; nKid < nChildren; nKid++ )
11866         {
11867             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11868             PDFWidget& rKid = m_aWidgets[nKidIndex];
11869             PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
11870             if( app_it != rKid.m_aAppearances.end() )
11871             {
11872                 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
11873                 if( stream_it != app_it->second.end() )
11874                 {
11875                     SvMemoryStream* pStream = stream_it->second;
11876                     app_it->second.erase( stream_it );
11877                     OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
11878                     appendName( rKid.m_aOnValue, aBuf );
11879                     (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11880                 }
11881                 #if OSL_DEBUG_LEVEL > 1
11882                 else
11883                     fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" );
11884                 #endif
11885             }
11886             // update selected radio button
11887             if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11888             {
11889                 rGroupWidget.m_aValue = rKid.m_aValue;
11890             }
11891         }
11892     }
11893 }
11894 
11895 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
11896 {
11897     sal_Int32 nRadioGroupWidget = -1;
11898 
11899     std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
11900 
11901     if( it == m_aRadioGroupWidgets.end() )
11902     {
11903         m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
11904             sal_Int32(m_aWidgets.size());
11905 
11906         // new group, insert the radiobutton
11907         m_aWidgets.push_back( PDFWidget() );
11908         m_aWidgets.back().m_nObject		= createObject();
11909         m_aWidgets.back().m_nPage		= m_nCurrentPage;
11910         m_aWidgets.back().m_eType		= PDFWriter::RadioButton;
11911         m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
11912         m_aWidgets.back().m_nFlags |= 0x0000C000;   // NoToggleToOff and Radio bits
11913 
11914         createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn );
11915     }
11916     else
11917         nRadioGroupWidget = it->second;
11918 
11919     return nRadioGroupWidget;
11920 }
11921 
11922 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
11923 {
11924     if( nPageNr < 0 )
11925         nPageNr = m_nCurrentPage;
11926 
11927     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11928         return -1;
11929 
11930     sal_Int32 nNewWidget = m_aWidgets.size();
11931     m_aWidgets.push_back( PDFWidget() );
11932 
11933     m_aWidgets.back().m_nObject			= createObject();
11934     m_aWidgets.back().m_aRect				= rControl.Location;
11935     m_aWidgets.back().m_nPage				= nPageNr;
11936     m_aWidgets.back().m_eType				= rControl.getType();
11937 
11938     sal_Int32 nRadioGroupWidget = -1;
11939     // for unknown reasons the radio buttons of a radio group must not have a
11940     // field name, else the buttons are in fact check boxes -
11941     // that is multiple buttons of the radio group can be selected
11942     if( rControl.getType() == PDFWriter::RadioButton )
11943         nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
11944     else
11945     {
11946         createWidgetFieldName( nNewWidget, rControl );
11947     }
11948 
11949     // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
11950     PDFWidget& rNewWidget			= m_aWidgets[nNewWidget];
11951     rNewWidget.m_aDescription		= rControl.Description;
11952     rNewWidget.m_aText				= rControl.Text;
11953     rNewWidget.m_nTextStyle			= rControl.TextStyle &
11954         (  TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP |
11955            TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM |
11956            TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK  );
11957     rNewWidget.m_nTabOrder          = rControl.TabOrder;
11958 
11959     // various properties are set via the flags (/Ff) property of the field dict
11960     if( rControl.ReadOnly )
11961         rNewWidget.m_nFlags |= 1;
11962     if( rControl.getType() == PDFWriter::PushButton )
11963     {
11964         const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
11965         if( rNewWidget.m_nTextStyle == 0 )
11966             rNewWidget.m_nTextStyle =
11967                 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER |
11968                 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11969 
11970         rNewWidget.m_nFlags |= 0x00010000;
11971         if( rBtn.URL.getLength() )
11972             rNewWidget.m_aListEntries.push_back( rBtn.URL );
11973         rNewWidget.m_bSubmit    = rBtn.Submit;
11974         rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
11975         rNewWidget.m_nDest      = rBtn.Dest;
11976         createDefaultPushButtonAppearance( rNewWidget, rBtn );
11977     }
11978     else if( rControl.getType() == PDFWriter::RadioButton )
11979     {
11980         const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
11981         if( rNewWidget.m_nTextStyle == 0 )
11982             rNewWidget.m_nTextStyle =
11983                 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11984         /*  PDF sees a RadioButton group as one radio button with
11985          *  children which are in turn check boxes
11986          *
11987          *  so we need to create a radio button on demand for a new group
11988          *  and insert a checkbox for each RadioButtonWidget as its child
11989          */
11990         rNewWidget.m_eType			= PDFWriter::CheckBox;
11991         rNewWidget.m_nRadioGroup	= rBtn.RadioGroup;
11992 
11993         DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" );
11994 
11995         PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
11996         rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
11997         rRadioButton.m_aKidsIndex.push_back( nNewWidget );
11998         rNewWidget.m_nParent = rRadioButton.m_nObject;
11999 
12000         rNewWidget.m_aValue     = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) );
12001         rNewWidget.m_aOnValue   = rBtn.OnValue;
12002         if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected )
12003         {
12004             rNewWidget.m_aValue		= rNewWidget.m_aOnValue;
12005             rRadioButton.m_aValue	= rNewWidget.m_aOnValue;
12006         }
12007         createDefaultRadioButtonAppearance( rNewWidget, rBtn );
12008 
12009         // union rect of radio group
12010         Rectangle aRect = rNewWidget.m_aRect;
12011         m_aPages[ nPageNr ].convertRect( aRect );
12012         rRadioButton.m_aRect.Union( aRect );
12013     }
12014     else if( rControl.getType() == PDFWriter::CheckBox )
12015     {
12016         const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
12017         if( rNewWidget.m_nTextStyle == 0 )
12018             rNewWidget.m_nTextStyle =
12019                 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12020 
12021         rNewWidget.m_aValue = OUString::createFromAscii( rBox.Checked ? "Yes" : "Off" );
12022         // create default appearance before m_aRect gets transformed
12023         createDefaultCheckBoxAppearance( rNewWidget, rBox );
12024     }
12025     else if( rControl.getType() == PDFWriter::ListBox )
12026     {
12027         if( rNewWidget.m_nTextStyle == 0 )
12028             rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
12029 
12030         const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
12031         rNewWidget.m_aListEntries	  = rLstBox.Entries;
12032         rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
12033         rNewWidget.m_aValue			  = rLstBox.Text;
12034         if( rLstBox.DropDown )
12035             rNewWidget.m_nFlags |= 0x00020000;
12036         if( rLstBox.Sort )
12037             rNewWidget.m_nFlags |= 0x00080000;
12038         if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 )
12039             rNewWidget.m_nFlags |= 0x00200000;
12040 
12041         createDefaultListBoxAppearance( rNewWidget, rLstBox );
12042     }
12043     else if( rControl.getType() == PDFWriter::ComboBox )
12044     {
12045         if( rNewWidget.m_nTextStyle == 0 )
12046             rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
12047 
12048         const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
12049         rNewWidget.m_aValue			= rBox.Text;
12050         rNewWidget.m_aListEntries	= rBox.Entries;
12051         rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
12052         if( rBox.Sort )
12053             rNewWidget.m_nFlags |= 0x00080000;
12054 
12055         PDFWriter::ListBoxWidget aLBox;
12056         aLBox.Name				= rBox.Name;
12057         aLBox.Description		= rBox.Description;
12058         aLBox.Text				= rBox.Text;
12059         aLBox.TextStyle			= rBox.TextStyle;
12060         aLBox.ReadOnly			= rBox.ReadOnly;
12061         aLBox.Border			= rBox.Border;
12062         aLBox.BorderColor		= rBox.BorderColor;
12063         aLBox.Background		= rBox.Background;
12064         aLBox.BackgroundColor	= rBox.BackgroundColor;
12065         aLBox.TextFont			= rBox.TextFont;
12066         aLBox.TextColor			= rBox.TextColor;
12067         aLBox.DropDown			= true;
12068         aLBox.Sort				= rBox.Sort;
12069         aLBox.MultiSelect		= false;
12070         aLBox.Entries			= rBox.Entries;
12071 
12072         createDefaultListBoxAppearance( rNewWidget, aLBox );
12073     }
12074     else if( rControl.getType() == PDFWriter::Edit )
12075     {
12076         if( rNewWidget.m_nTextStyle == 0 )
12077             rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
12078 
12079         const PDFWriter::EditWidget& rEdit = static_cast<const  PDFWriter::EditWidget&>(rControl);
12080         if( rEdit.MultiLine )
12081         {
12082             rNewWidget.m_nFlags |= 0x00001000;
12083             rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12084         }
12085         if( rEdit.Password )
12086             rNewWidget.m_nFlags |= 0x00002000;
12087         if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 )
12088             rNewWidget.m_nFlags |= 0x00100000;
12089         rNewWidget.m_nMaxLen = rEdit.MaxLen;
12090         rNewWidget.m_aValue = rEdit.Text;
12091 
12092         createDefaultEditAppearance( rNewWidget, rEdit );
12093     }
12094 
12095     // convert to default user space now, since the mapmode may change
12096     // note: create default appearances before m_aRect gets transformed
12097     m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
12098 
12099     // insert widget to page's annotation list
12100     m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
12101 
12102     // mark page as having widgets
12103     m_aPages[ nPageNr ].m_bHasWidgets = true;
12104 
12105     return nNewWidget;
12106 }
12107 
12108 void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl )
12109 {
12110     if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() )
12111         return;
12112 
12113     PDFWidget& rWidget = m_aWidgets[ nControl ];
12114     m_nCurrentControl = nControl;
12115 
12116     SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 );
12117     // back conversion of control rect to current MapMode; necessary because
12118     // MapMode between createControl and beginControlAppearance
12119     // could have changed; therefore the widget rectangle is
12120     // already converted
12121     Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ),
12122                      rWidget.m_aRect.GetSize() );
12123     aBack = lcl_convert( m_aMapMode,
12124                          m_aGraphicsStack.front().m_aMapMode,
12125                          getReferenceDevice(),
12126                          aBack );
12127     beginRedirect( pControlStream, aBack );
12128     writeBuffer( "/Tx BMC\n", 8 );
12129 }
12130 
12131 bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState )
12132 {
12133     bool bRet = false;
12134     if( ! m_aOutputStreams.empty() )
12135         writeBuffer( "\nEMC\n", 5 );
12136     SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect());
12137     if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() )
12138     {
12139         PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ];
12140         OString aState, aStyle;
12141         switch( rWidget.m_eType )
12142         {
12143             case PDFWriter::PushButton:
12144                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12145                 {
12146                     aState = (eState == PDFWriter::Up) ? "N" : "D";
12147                     aStyle = "Standard";
12148                 }
12149                 break;
12150             case PDFWriter::CheckBox:
12151                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12152                 {
12153                     aState = "N";
12154                     aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes";
12155                     /* cf PDFReference 3rd ed. V1.4 p539:
12156                        recommended name for on state is "Yes",
12157                        recommended name for off state is "Off"
12158                      */
12159                 }
12160                 break;
12161             case PDFWriter::RadioButton:
12162                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12163                 {
12164                     aState = "N";
12165                     if( eState == PDFWriter::Up )
12166                         aStyle = "Off";
12167                     else
12168                     {
12169                         OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 );
12170                         appendName( rWidget.m_aOnValue, aBuf );
12171                         aStyle = aBuf.makeStringAndClear();
12172                     }
12173                 }
12174                 break;
12175             case PDFWriter::Edit:
12176                 aState = "N";
12177                 aStyle = "Standard";
12178                 break;
12179             case PDFWriter::ListBox:
12180             case PDFWriter::ComboBox:
12181             case PDFWriter::Hierarchy:
12182                 break;
12183         }
12184         if( aState.getLength() && aStyle.getLength() )
12185         {
12186             // delete eventual existing stream
12187             PDFAppearanceStreams::iterator it =
12188                 rWidget.m_aAppearances[ aState ].find( aStyle );
12189             if( it != rWidget.m_aAppearances[ aState ].end() )
12190                 delete it->second;
12191             rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance;
12192             bRet = true;
12193         }
12194     }
12195 
12196     if( ! bRet )
12197         delete pAppearance;
12198 
12199     m_nCurrentControl = -1;
12200 
12201     return bRet;
12202 }
12203 
12204 void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress )
12205 {
12206     if( pStream )
12207     {
12208         m_aAdditionalStreams.push_back( PDFAddStream() );
12209         PDFAddStream& rStream = m_aAdditionalStreams.back();
12210         rStream.m_aMimeType = rMimeType.Len()
12211                               ? OUString( rMimeType )
12212                               : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) );
12213         rStream.m_pStream = pStream;
12214         rStream.m_bCompress = bCompress;
12215     }
12216 }
12217 
12218 
12219 
12220