xref: /aoo42x/main/vcl/source/gdi/pdfwriter_impl.cxx (revision 5aaf853b)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #define _USE_MATH_DEFINES
28 #include <math.h>
29 #include <algorithm>
30 
31 #include <tools/urlobj.hxx>
32 
33 #include <pdfwriter_impl.hxx>
34 
35 #include <basegfx/polygon/b2dpolygon.hxx>
36 #include <basegfx/polygon/b2dpolypolygon.hxx>
37 #include <basegfx/polygon/b2dpolygontools.hxx>
38 #include <basegfx/polygon/b2dpolypolygontools.hxx>
39 #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
40 #include <basegfx/matrix/b2dhommatrix.hxx>
41 
42 #include <osl/thread.h>
43 #include <osl/file.h>
44 
45 #include <rtl/crc.h>
46 #include <rtl/digest.h>
47 #include <rtl/ustrbuf.hxx>
48 
49 #include <tools/debug.hxx>
50 #include <tools/zcodec.hxx>
51 #include <tools/stream.hxx>
52 
53 #include <i18npool/mslangid.hxx>
54 
55 #include <vcl/virdev.hxx>
56 #include <vcl/bmpacc.hxx>
57 #include <vcl/bitmapex.hxx>
58 #include <vcl/image.hxx>
59 #include <vcl/metric.hxx>
60 #include <vcl/svapp.hxx>
61 #include <vcl/lineinfo.hxx>
62 #include "vcl/cvtgrf.hxx"
63 #include "vcl/strhelper.hxx"
64 
65 #include <fontsubset.hxx>
66 #include <outdev.h>
67 #include <sallayout.hxx>
68 #include <textlayout.hxx>
69 #include <salgdi.hxx>
70 
71 #include <icc/sRGB-IEC61966-2.1.hxx>
72 
73 #include <comphelper/processfactory.hxx>
74 
75 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
76 #include <com/sun/star/util/URL.hpp>
77 
78 #include "cppuhelper/implbase1.hxx"
79 
80 using namespace vcl;
81 using namespace rtl;
82 
83 #if (OSL_DEBUG_LEVEL < 2)
84 #define COMPRESS_PAGES
85 #else
86 #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams
87 #endif
88 
89 #ifdef DO_TEST_PDF
90 class PDFTestOutputStream : public PDFOutputStream
91 {
92     public:
93     virtual ~PDFTestOutputStream();
94     virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream );
95 };
96 
97 PDFTestOutputStream::~PDFTestOutputStream()
98 {
99 }
100 
101 void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream )
102 {
103     OString aStr( "lalala\ntest\ntest\ntest" );
104     com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() );
105     rtl_copyMemory( aData.getArray(), aStr.getStr(), aStr.getLength() );
106     xStream->writeBytes( aData );
107 }
108 
109 // this test code cannot be used to test PDF/A-1 because it forces
110 // control item (widgets) to bypass the structure controlling
111 // the embedding of such elements in actual run
112 void doTestCode()
113 {
114     static const char* pHome = getenv( "HOME"  );
115     rtl::OUString aTestFile( RTL_CONSTASCII_USTRINGPARAM( "file://" ) );
116     aTestFile += rtl::OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 );
117     aTestFile += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/pdf_export_test.pdf" ) );
118 
119     PDFWriter::PDFWriterContext aContext;
120     aContext.URL			= aTestFile;
121     aContext.Version		= PDFWriter::PDF_1_4;
122     aContext.Tagged			= true;
123     aContext.InitialPage    = 2;
124     aContext.DocumentInfo.Title = OUString( RTL_CONSTASCII_USTRINGPARAM( "PDF export test document" ) );
125     aContext.DocumentInfo.Producer = OUString( RTL_CONSTASCII_USTRINGPARAM( "VCL" ) );
126 
127     PDFWriter aWriter( aContext );
128     aWriter.NewPage( 595, 842 );
129     aWriter.BeginStructureElement( PDFWriter::Document );
130     // set duration of 3 sec for first page
131     aWriter.SetAutoAdvanceTime( 3 );
132     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
133 
134     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
135     aWriter.SetLineColor( Color( COL_LIGHTGREEN ) );
136     aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
137 
138     aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
139     aWriter.SetTextColor( Color( COL_BLACK ) );
140     aWriter.SetLineColor( Color( COL_BLACK ) );
141     aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
142 
143     Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
144     aWriter.DrawRect( aRect );
145     aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 1" ) ) );
146     sal_Int32 nFirstLink = aWriter.CreateLink( aRect );
147     PDFNote aNote;
148     aNote.Title = String( RTL_CONSTASCII_USTRINGPARAM( "A small test note" ) );
149     aNote.Contents = String( RTL_CONSTASCII_USTRINGPARAM( "There is no business like show business like no business i know. Everything about it is appealing." ) );
150     aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote );
151 
152     Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
153     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
154     aWriter.DrawRect( aTargetRect );
155     aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest second link" ) ) );
156     sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect );
157 
158     aWriter.BeginStructureElement( PDFWriter::Section );
159     aWriter.BeginStructureElement( PDFWriter::Heading );
160     aWriter.DrawText( Point(4500, 9000), String( RTL_CONSTASCII_USTRINGPARAM( "A small structure test" ) ) );
161     aWriter.EndStructureElement();
162     aWriter.BeginStructureElement( PDFWriter::Paragraph );
163     aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
164     aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline );
165     aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
166                       String( RTL_CONSTASCII_USTRINGPARAM( "It was the best of PDF, it was the worst of PDF ... or so. This is a pretty nonsensical text to denote a paragraph. I suggest you stop reading it. Because if you read on you might get bored. So continue on your on risk. Hey, you're still here ? Why do you continue to read this as it is of no use at all ? OK, it's your time, but still... . Woah, i even get bored writing this, so let's end this here and now." ) ),
167                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
168                       );
169     aWriter.SetActualText( String( RTL_CONSTASCII_USTRINGPARAM( "It was the best of PDF, it was the worst of PDF ... or so. This is a pretty nonsensical text to denote a paragraph. I suggest you stop reading it. Because if you read on you might get bored. So continue on your on risk. Hey, you're still here ? Why do you continue to read this as it is of no use at all ? OK, it's your time, but still... . Woah, i even get bored writing this, so let's end this here and now." ) ) );
170     aWriter.SetAlternateText( String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." ) ) );
171     aWriter.EndStructureElement();
172     sal_Int32 nLongPara = aWriter.BeginStructureElement( PDFWriter::Paragraph );
173     aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
174     aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
175                       String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph is nothing special either but ends on the next page structurewise" ) ),
176                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
177                       );
178 
179     aWriter.NewPage( 595, 842 );
180     // test AddStream interface
181     aWriter.AddStream( String( RTL_CONSTASCII_USTRINGPARAM( "text/plain" ) ), new PDFTestOutputStream(), true );
182     // set transitional mode
183     aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 );
184     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
185     aWriter.SetTextColor( Color( COL_BLACK ) );
186     aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
187     aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
188                       String( RTL_CONSTASCII_USTRINGPARAM( "Here's where all things come to an end ... well at least the paragaph from the last page." ) ),
189                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
190                       );
191     aWriter.EndStructureElement();
192 
193     aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
194     // disable structure
195     aWriter.BeginStructureElement( PDFWriter::NonStructElement );
196     aWriter.DrawRect( aRect );
197     aWriter.BeginStructureElement( PDFWriter::Paragraph );
198     aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 2" ) ) );
199     sal_Int32 nSecondLink = aWriter.CreateLink( aRect );
200 
201     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
202     aWriter.BeginStructureElement( PDFWriter::ListItem );
203     aWriter.DrawRect( aTargetRect );
204     aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest first link" ) ) );
205     sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect );
206     // enable structure
207     aWriter.EndStructureElement();
208     // add something to the long paragraph as an afterthought
209     sal_Int32 nSaveStruct = aWriter.GetCurrentStructureElement();
210     aWriter.SetCurrentStructureElement( nLongPara );
211     aWriter.DrawText( Rectangle( Point( 4500,4500 ),  Size( 12000, 1000 ) ),
212                       String( RTL_CONSTASCII_USTRINGPARAM( "Add something to the longish paragraph above." ) ),
213                       TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
214     aWriter.SetCurrentStructureElement( nSaveStruct );
215     aWriter.EndStructureElement();
216     aWriter.EndStructureElement();
217     aWriter.BeginStructureElement( PDFWriter::Figure );
218     aWriter.BeginStructureElement( PDFWriter::Caption );
219     aWriter.DrawText( Point( 4500, 9000 ), String( RTL_CONSTASCII_USTRINGPARAM( "Some drawing stuff inside the structure" ) ) );
220     aWriter.EndStructureElement();
221 
222     // test clipping
223     basegfx::B2DPolyPolygon aClip;
224     basegfx::B2DPolygon aClipPoly;
225     aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) );
226     aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) );
227     aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) );
228     aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) );
229     aClipPoly.setClosed( true );
230     //aClipPoly.flip();
231     aClip.append( aClipPoly );
232 
233     aWriter.Push( PUSH_CLIPREGION | PUSH_FILLCOLOR );
234     aWriter.SetClipRegion( aClip );
235     aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
236     aWriter.MoveClipRegion( 1000, 500 );
237     aWriter.SetFillColor( Color( COL_RED ) );
238     aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
239     aWriter.Pop();
240     // test transparency
241     // draw background
242     Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
243     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
244     aWriter.DrawRect( aTranspRect );
245     aWriter.BeginTransparencyGroup();
246 
247     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
248     aWriter.DrawEllipse( aTranspRect );
249     aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
250     aWriter.DrawText( aTranspRect,
251                       String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
252                       TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
253 
254     aWriter.EndTransparencyGroup( aTranspRect, 50 );
255 
256     // prepare an alpha mask
257     Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
258     BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess();
259     for( int nX = 0; nX < 256; nX++ )
260         for( int nY = 0; nY < 256; nY++ )
261             pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) );
262     aTransMask.ReleaseAccess( pAcc );
263     aTransMask.SetPrefMapMode( MAP_MM );
264     aTransMask.SetPrefSize( Size( 10, 10 ) );
265 
266     aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask );
267 
268     aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
269     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
270     aWriter.DrawRect( aTranspRect );
271     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
272     aWriter.DrawEllipse( aTranspRect );
273     aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
274     aWriter.DrawText( aTranspRect,
275                       String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
276                       TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
277     aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
278     aWriter.SetFillColor( Color( COL_LIGHTRED ) );
279     aWriter.DrawRect( aTranspRect );
280     aWriter.BeginTransparencyGroup();
281     aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
282     aWriter.DrawEllipse( aTranspRect );
283     aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
284     aWriter.DrawText( aTranspRect,
285                       String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
286                       TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
287     aWriter.EndTransparencyGroup( aTranspRect, aTransMask );
288 
289     Bitmap aImageBmp( Size( 256, 256 ), 24 );
290     pAcc = aImageBmp.AcquireWriteAccess();
291     pAcc->SetFillColor( Color( 0xff, 0, 0xff ) );
292     pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
293     aImageBmp.ReleaseAccess( pAcc );
294     BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) );
295     aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx );
296 
297 
298     aWriter.EndStructureElement();
299     aWriter.EndStructureElement();
300 
301     LineInfo aLI( LINE_DASH, 3 );
302     aLI.SetDashCount( 2 );
303     aLI.SetDashLen( 50 );
304     aLI.SetDotCount( 2 );
305     aLI.SetDotLen( 25 );
306     aLI.SetDistance( 15 );
307     Point aLIPoints[] = { Point( 4000, 10000 ),
308                           Point( 8000, 12000 ),
309                           Point( 3000, 19000 ) };
310     Polygon aLIPoly( 3, aLIPoints );
311     aWriter.SetLineColor( Color( COL_BLUE ) );
312     aWriter.SetFillColor();
313     aWriter.DrawPolyLine( aLIPoly, aLI );
314 
315     aLI.SetDashCount( 4 );
316     aLIPoly.Move( 1000, 1000 );
317     aWriter.DrawPolyLine( aLIPoly, aLI );
318 
319     aWriter.NewPage( 595, 842 );
320     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
321     Wallpaper aWall( aTransMask );
322     aWall.SetStyle( WALLPAPER_TILE );
323     aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall );
324 
325     aWriter.Push( PUSH_ALL );
326     aWriter.BeginPattern(Rectangle(Point(0,0),Size(2000,1000)));
327     aWriter.SetFillColor( Color( COL_RED ) );
328     aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
329     Point aFillPoints[] = { Point( 1000, 0 ),
330                             Point( 0, 1000 ),
331                             Point( 2000, 1000 ) };
332     aWriter.DrawPolygon( Polygon( 3, aFillPoints ) );
333     aWriter.DrawBitmap( Point( 200, 200 ), Size( 1600, 600 ), aTransMask );
334     aWriter.DrawText( Rectangle( Point( 200, 200 ), Size( 1600, 600 ) ), String( RTL_CONSTASCII_USTRINGPARAM( "Pattern" ) ) );
335     sal_Int32 nPattern = aWriter.EndPattern( SvtGraphicFill::Transform() );
336     aWriter.Pop();
337     Rectangle aPolyRect( Point( 3800, 11200 ), Size( 10200, 6300 ) );
338     aWriter.DrawPolyPolygon( PolyPolygon( Polygon( aPolyRect ) ), nPattern, true );
339     aWriter.SetFillColor();
340     aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
341     aWriter.DrawRect( aPolyRect );
342 
343     aWriter.NewPage( 595, 842 );
344     aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
345     aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
346     aWriter.SetTextColor( Color( COL_BLACK ) );
347     aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
348     aWriter.DrawRect( aRect );
349     aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "www.heise.de" ) ) );
350     sal_Int32 nURILink = aWriter.CreateLink( aRect );
351     aWriter.SetLinkURL( nURILink, OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) ) );
352 
353     aWriter.SetLinkDest( nFirstLink, nFirstDest );
354     aWriter.SetLinkDest( nSecondLink, nSecondDest );
355 
356     // include a button
357     PDFWriter::PushButtonWidget aBtn;
358     aBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testButton" ) );
359     aBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test button" ) );
360     aBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "hit me" ) );
361     aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
362     aBtn.Border = aBtn.Background = true;
363     aWriter.CreateControl( aBtn );
364 
365     // include a uri button
366     PDFWriter::PushButtonWidget aUriBtn;
367     aUriBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "wwwButton" ) );
368     aUriBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A URI button" ) );
369     aUriBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to www" ) );
370     aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
371     aUriBtn.Border = aUriBtn.Background = true;
372     aUriBtn.URL = OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) );
373     aWriter.CreateControl( aUriBtn );
374 
375     // include a dest button
376     PDFWriter::PushButtonWidget aDstBtn;
377     aDstBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "destButton" ) );
378     aDstBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A Dest button" ) );
379     aDstBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to paragraph" ) );
380     aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
381     aDstBtn.Border = aDstBtn.Background = true;
382     aDstBtn.Dest = nFirstDest;
383     aWriter.CreateControl( aDstBtn );
384 
385     PDFWriter::CheckBoxWidget aCBox;
386     aCBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox" ) );
387     aCBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test check box" ) );
388     aCBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me" ) );
389     aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
390     aCBox.Checked = true;
391     aCBox.Border = aCBox.Background = false;
392     aWriter.CreateControl( aCBox );
393 
394     PDFWriter::CheckBoxWidget aCBox2;
395     aCBox2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox2" ) );
396     aCBox2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "Another test check box" ) );
397     aCBox2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me right" ) );
398     aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
399     aCBox2.Checked = true;
400     aCBox2.Border = aCBox2.Background = false;
401     aCBox2.ButtonIsLeft = false;
402     aWriter.CreateControl( aCBox2 );
403 
404     PDFWriter::RadioButtonWidget aRB1;
405     aRB1.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_1" ) );
406     aRB1.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 1" ) );
407     aRB1.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Despair" ) );
408     aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
409     aRB1.Selected = true;
410     aRB1.RadioGroup = 1;
411     aRB1.Border = aRB1.Background = true;
412     aRB1.ButtonIsLeft = false;
413     aRB1.BorderColor = Color( COL_LIGHTGREEN );
414     aRB1.BackgroundColor = Color( COL_LIGHTBLUE );
415     aRB1.TextColor = Color( COL_LIGHTRED );
416     aRB1.TextFont = Font( String( RTL_CONSTASCII_USTRINGPARAM( "Courier" ) ), Size( 0, 800 ) );
417     aWriter.CreateControl( aRB1 );
418 
419     PDFWriter::RadioButtonWidget aRB2;
420     aRB2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb2_1" ) );
421     aRB2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 2 button 1" ) );
422     aRB2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Joy" ) );
423     aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
424     aRB2.Selected = true;
425     aRB2.RadioGroup = 2;
426     aWriter.CreateControl( aRB2 );
427 
428     PDFWriter::RadioButtonWidget aRB3;
429     aRB3.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_2" ) );
430     aRB3.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 2" ) );
431     aRB3.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Desperation" ) );
432     aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
433     aRB3.Selected = true;
434     aRB3.RadioGroup = 1;
435     aWriter.CreateControl( aRB3 );
436 
437     PDFWriter::EditWidget aEditBox;
438     aEditBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testEdit" ) );
439     aEditBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test edit field" ) );
440     aEditBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "A little test text" ) );
441     aEditBox.TextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
442     aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
443     aEditBox.MaxLen = 100;
444     aEditBox.Border = aEditBox.Background = true;
445     aEditBox.BorderColor = Color( COL_BLACK );
446     aWriter.CreateControl( aEditBox );
447 
448     // normal list box
449     PDFWriter::ListBoxWidget aLstBox;
450     aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testListBox" ) );
451     aLstBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) );
452     aLstBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "select me" ) );
453     aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
454     aLstBox.Sort = true;
455     aLstBox.MultiSelect = true;
456     aLstBox.Border = aLstBox.Background = true;
457     aLstBox.BorderColor = Color( COL_BLACK );
458     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) ) );
459     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Two" ) ) );
460     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Three" ) ) );
461     aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Four" ) ) );
462     aLstBox.SelectedEntries.push_back( 1 );
463     aLstBox.SelectedEntries.push_back( 2 );
464     aWriter.CreateControl( aLstBox );
465 
466     // dropdown list box
467     aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testDropDownListBox" ) );
468     aLstBox.DropDown = true;
469     aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
470     aWriter.CreateControl( aLstBox );
471 
472     // combo box
473     PDFWriter::ComboBoxWidget aComboBox;
474     aComboBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testComboBox" ) );
475     aComboBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "test a combobox" ) );
476     aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Larry" ) ) );
477     aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Curly" ) ) );
478     aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Moe" ) ) );
479     aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
480     aWriter.CreateControl( aComboBox );
481 
482     // test outlines
483     sal_Int32 nPage1OL = aWriter.CreateOutlineItem();
484     aWriter.SetOutlineItemText( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 1" ) ) );
485     aWriter.SetOutlineItemDest( nPage1OL, nSecondDest );
486     aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2" ) ), nSecondDest );
487     aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 revisited" ) ), nSecondDest );
488     aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 again" ) ), nSecondDest );
489     sal_Int32 nPage2OL = aWriter.CreateOutlineItem();
490     aWriter.SetOutlineItemText( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 2" ) ) );
491     aWriter.CreateOutlineItem( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 1" ) ), nFirstDest );
492 
493     aWriter.EndStructureElement(); // close document
494     aWriter.Emit();
495 }
496 #endif
497 
498 static const sal_Int32 nLog10Divisor = 1;
499 static const double fDivisor = 10.0;
500 
501 static inline double pixelToPoint( sal_Int32 px ) { return double(px)/fDivisor; }
502 static inline double pixelToPoint( double px ) { return px/fDivisor; }
503 static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); }
504 
505 const sal_uInt8 PDFWriterImpl::s_nPadString[32] =
506 {
507     0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
508     0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
509 };
510 
511 static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
512 {
513     static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
514                                            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
515     rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
516     rBuffer.append( pHexDigits[ nInt & 15 ] );
517 }
518 
519 static void appendName( const OUString& rStr, OStringBuffer& rBuffer )
520 {
521 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
522 // I guess than when reading the #xx sequence it will count for a single character.
523     OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) );
524     const sal_Char* pStr = aStr.getStr();
525     int nLen = aStr.getLength();
526     for( int i = 0; i < nLen; i++ )
527     {
528         /*  #i16920# PDF recommendation: output UTF8, any byte
529          *  outside the interval [33(=ASCII'!');126(=ASCII'~')]
530          *  should be escaped hexadecimal
531          *  for the sake of ghostscript which also reads PDF
532          *  but has a narrower acceptance rate we only pass
533          *  alphanumerics and '-' literally.
534          */
535         if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) ||
536             (pStr[i] >= 'a' && pStr[i] <= 'z' ) ||
537             (pStr[i] >= '0' && pStr[i] <= '9' ) ||
538             pStr[i] == '-' )
539         {
540             rBuffer.append( pStr[i] );
541         }
542         else
543         {
544             rBuffer.append( '#' );
545             appendHex( (sal_Int8)pStr[i], rBuffer );
546         }
547     }
548 }
549 
550 static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer )
551 {
552 //FIXME i59651 see above
553     while( pStr && *pStr )
554     {
555         if( (*pStr >= 'A' && *pStr <= 'Z' ) ||
556             (*pStr >= 'a' && *pStr <= 'z' ) ||
557             (*pStr >= '0' && *pStr <= '9' ) ||
558             *pStr == '-' )
559         {
560             rBuffer.append( *pStr );
561         }
562         else
563         {
564             rBuffer.append( '#' );
565             appendHex( (sal_Int8)*pStr, rBuffer );
566         }
567         pStr++;
568     }
569 }
570 
571 //used only to emit encoded passwords
572 static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer )
573 {
574 	while( nLength )
575 	{
576 		switch( *pStr )
577 		{
578 		case '\n' :
579 			rBuffer.append( "\\n" );
580 			break;
581 		case '\r' :
582 			rBuffer.append( "\\r" );
583 			break;
584 		case '\t' :
585 			rBuffer.append( "\\t" );
586 			break;
587 		case '\b' :
588 			rBuffer.append( "\\b" );
589 			break;
590 		case '\f' :
591 			rBuffer.append( "\\f" );
592 			break;
593 		case '(' :
594 		case ')' :
595 		case '\\' :
596 			rBuffer.append( "\\" );
597 			rBuffer.append( (sal_Char) *pStr );
598 			break;
599 		default:
600 			rBuffer.append( (sal_Char) *pStr );
601 			break;
602 		}
603 		pStr++;
604 		nLength--;
605 	}
606 }
607 
608 /**--->i56629
609  * Convert a string before using it.
610  *
611  * This string conversion function is needed because the destination name
612  * in a PDF file seen through an Internet browser should be
613  * specially crafted, in order to be used directly by the browser.
614  * In this way the fragment part of a hyperlink to a PDF file (e.g. something
615  * as 'test1/test2/a-file.pdf#thefragment) will be (hopefully) interpreted by the
616  * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
617  * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
618  * and go to named destination thefragment using default zoom'.
619  * The conversion is needed because in case of a fragment in the form: Slide%201
620  * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
621  * using this conversion, in both the generated named destinations, fragment and GoToR
622  * destination.
623  *
624  * The names for destinations are name objects and so they don't need to be encrypted
625  * even though they expose the content of PDF file (e.g. guessing the PDF content from the
626  * destination name).
627  *
628  * Fhurter limitation: it is advisable to use standard ASCII characters for
629  * OOo bookmarks.
630 */
631 static void appendDestinationName( const rtl::OUString& rString, OStringBuffer& rBuffer )
632 {
633     const sal_Unicode* pStr = rString.getStr();
634     sal_Int32 nLen = rString.getLength();
635     for( int i = 0; i < nLen; i++ )
636     {
637         sal_Unicode aChar = pStr[i];
638         if( (aChar >= '0' && aChar <= '9' ) ||
639             (aChar >= 'a' && aChar <= 'z' ) ||
640             (aChar >= 'A' && aChar <= 'Z' ) ||
641             aChar == '-' )
642         {
643             rBuffer.append((sal_Char)aChar);
644         }
645         else
646         {
647             sal_Int8 aValueHigh = sal_Int8(aChar >> 8);
648             if(aValueHigh > 0)
649                 appendHex( aValueHigh, rBuffer );
650             appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
651         }
652     }
653 }
654 //<--- i56629
655 
656 static void appendUnicodeTextString( const rtl::OUString& rString, OStringBuffer& rBuffer )
657 {
658 	rBuffer.append( "FEFF" );
659 	const sal_Unicode* pStr = rString.getStr();
660 	sal_Int32 nLen = rString.getLength();
661 	for( int i = 0; i < nLen; i++ )
662 	{
663 		sal_Unicode aChar = pStr[i];
664 		appendHex( (sal_Int8)(aChar >> 8), rBuffer );
665 		appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
666 	}
667 }
668 
669 void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl )
670 {
671     /* #i80258# previously we use appendName here
672        however we need a slightly different coding scheme than the normal
673        name encoding for field names
674     */
675     const OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ? i_rControl.Name : i_rControl.Text;
676     OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) );
677     const sal_Char* pStr = aStr.getStr();
678     int nLen = aStr.getLength();
679 
680     OStringBuffer aBuffer( rName.getLength()+64 );
681     for( int i = 0; i < nLen; i++ )
682     {
683         /*  #i16920# PDF recommendation: output UTF8, any byte
684          *  outside the interval [32(=ASCII' ');126(=ASCII'~')]
685          *  should be escaped hexadecimal
686          */
687         if( (pStr[i] >= 32 && pStr[i] <= 126 ) )
688             aBuffer.append( pStr[i] );
689         else
690         {
691             aBuffer.append( '#' );
692             appendHex( (sal_Int8)pStr[i], aBuffer );
693         }
694     }
695 
696     OString aFullName( aBuffer.makeStringAndClear() );
697 
698     /* #i82785# create hierarchical fields down to the for each dot in i_rName */
699     sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0;
700     OString aPartialName;
701     OString aDomain;
702     do
703     {
704         nLastTokenIndex = nTokenIndex;
705         aPartialName = aFullName.getToken( 0, '.', nTokenIndex );
706         if( nTokenIndex != -1 )
707         {
708             // find or create a hierarchical field
709             // first find the fully qualified name up to this field
710             aDomain = aFullName.copy( 0, nTokenIndex-1 );
711             std::hash_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
712             if( it == m_aFieldNameMap.end() )
713             {
714                  // create new hierarchy field
715                 sal_Int32 nNewWidget = m_aWidgets.size();
716                 m_aWidgets.push_back( PDFWidget() );
717                 m_aWidgets[nNewWidget].m_nObject = createObject();
718                 m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy;
719                 m_aWidgets[nNewWidget].m_aName = aPartialName;
720                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
721                 m_aFieldNameMap[aDomain] = nNewWidget;
722                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
723                 if( nLastTokenIndex > 0 )
724                 {
725                     // this field is not a root field and
726                     // needs to be inserted to its parent
727                     OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) );
728                     it = m_aFieldNameMap.find( aParentDomain );
729                     OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" );
730                     if( it != m_aFieldNameMap.end()  )
731                     {
732                         OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" );
733                         if( it->second < sal_Int32(m_aWidgets.size()) )
734                         {
735                             PDFWidget& rParentField( m_aWidgets[it->second] );
736                             rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject );
737                             rParentField.m_aKidsIndex.push_back( nNewWidget );
738                             m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject;
739                         }
740                     }
741                 }
742             }
743             else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy )
744             {
745                 // this is invalid, someone tries to have a terminal field as parent
746                 // example: a button with the name foo.bar exists and
747                 // another button is named foo.bar.no
748                 // workaround: put the second terminal field as much up in the hierarchy as
749                 // necessary to have a non-terminal field as parent (or none at all)
750                 // since it->second already is terminal, we just need to use its parent
751                 aDomain = OString();
752                 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
753                 if( nLastTokenIndex > 0 )
754                 {
755                     aDomain = aFullName.copy( 0, nLastTokenIndex-1 );
756                     OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() );
757                     aBuf.append( aDomain );
758                     aBuf.append( '.' );
759                     aBuf.append( aPartialName );
760                     aFullName = aBuf.makeStringAndClear();
761                 }
762                 else
763                     aFullName = aPartialName;
764                 break;
765             }
766         }
767     } while( nTokenIndex != -1 );
768 
769     // insert widget into its hierarchy field
770     if( aDomain.getLength() )
771     {
772         std::hash_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
773         if( it != m_aFieldNameMap.end() )
774         {
775             OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" );
776             if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) )
777             {
778                 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject;
779                 m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject);
780                 m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex );
781             }
782         }
783     }
784 
785     if( aPartialName.getLength() == 0 )
786     {
787         // how funny, an empty field name
788         if( i_rControl.getType() == PDFWriter::RadioButton )
789         {
790             aPartialName  = "RadioGroup";
791             aPartialName += OString::valueOf( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup );
792         }
793         else
794             aPartialName = OString( "Widget" );
795     }
796 
797     if( ! m_aContext.AllowDuplicateFieldNames )
798     {
799         std::hash_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aFullName );
800 
801         if( it != m_aFieldNameMap.end() ) // not unique
802         {
803             std::hash_map< OString, sal_Int32, OStringHash >::const_iterator check_it;
804             OString aTry;
805             sal_Int32 nTry = 2;
806             do
807             {
808                 OStringBuffer aUnique( aFullName.getLength() + 16 );
809                 aUnique.append( aFullName );
810                 aUnique.append( '_' );
811                 aUnique.append( nTry++ );
812                 aTry = aUnique.makeStringAndClear();
813                 check_it = m_aFieldNameMap.find( aTry );
814             } while( check_it != m_aFieldNameMap.end() );
815             aFullName = aTry;
816             m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
817             aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
818         }
819         else
820             m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
821     }
822 
823     // finally
824     m_aWidgets[i_nWidgetIndex].m_aName = aPartialName;
825 }
826 
827 static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor )
828 {
829     if( nValue < 0 )
830     {
831         rBuffer.append( '-' );
832         nValue = -nValue;
833     }
834     sal_Int32 nFactor = 1, nDiv = nPrecision;
835     while( nDiv-- )
836         nFactor *= 10;
837 
838     sal_Int32 nInt		= nValue / nFactor;
839     rBuffer.append( nInt );
840     if( nFactor > 1 )
841     {
842         sal_Int32 nDecimal	= nValue % nFactor;
843         if( nDecimal )
844         {
845             rBuffer.append( '.' );
846             // omit trailing zeros
847             while( (nDecimal % 10) == 0 )
848                 nDecimal /= 10;
849             rBuffer.append( nDecimal );
850         }
851     }
852 }
853 
854 
855 // appends a double. PDF does not accept exponential format, only fixed point
856 static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 )
857 {
858     bool bNeg = false;
859     if( fValue < 0.0 )
860     {
861         bNeg = true;
862         fValue=-fValue;
863     }
864 
865     sal_Int64 nInt = (sal_Int64)fValue;
866     fValue -= (double)nInt;
867     // optimizing hardware may lead to a value of 1.0 after the subtraction
868     if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision )
869     {
870         nInt++;
871         fValue = 0.0;
872     }
873     sal_Int64 nFrac = 0;
874     if( fValue )
875     {
876         fValue *= pow( 10.0, (double)nPrecision );
877         nFrac = (sal_Int64)fValue;
878     }
879     if( bNeg && ( nInt || nFrac ) )
880         rBuffer.append( '-' );
881     rBuffer.append( nInt );
882     if( nFrac )
883     {
884 		int i;
885         rBuffer.append( '.' );
886 		sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5);
887 		for ( i = 0; ( i < nPrecision ) && nFrac; i++ )
888 		{
889 			sal_Int64 nNumb = nFrac / nBound;
890 			nFrac -= nNumb * nBound;
891 			rBuffer.append( nNumb );
892 			nBound /= 10;
893 		}
894     }
895 }
896 
897 
898 static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey = false )
899 {
900 
901     if( rColor != Color( COL_TRANSPARENT ) )
902     {
903         if( bConvertToGrey )
904         {
905             sal_uInt8 cByte = rColor.GetLuminance();
906             appendDouble( (double)cByte / 255.0, rBuffer );
907         }
908         else
909         {
910             appendDouble( (double)rColor.GetRed() / 255.0, rBuffer );
911             rBuffer.append( ' ' );
912             appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer );
913             rBuffer.append( ' ' );
914             appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer );
915         }
916     }
917 }
918 
919 void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
920 {
921     if( rColor != Color( COL_TRANSPARENT ) )
922     {
923         bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
924         appendColor( rColor, rBuffer, bGrey );
925         rBuffer.append( bGrey ? " G" : " RG" );
926     }
927 }
928 
929 void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
930 {
931     if( rColor != Color( COL_TRANSPARENT ) )
932     {
933         bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
934         appendColor( rColor, rBuffer, bGrey );
935         rBuffer.append( bGrey ? " g" : " rg" );
936     }
937 }
938 
939 // matrix helper class
940 // TODO: use basegfx matrix class instead or derive from it
941 namespace vcl // TODO: use anonymous namespace to keep this class local
942 {
943 /*	for sparse matrices of the form (2D linear transformations)
944  *  f[0] f[1] 0
945  *  f[2] f[3] 0
946  *  f[4] f[5] 1
947  */
948 class Matrix3
949 {
950     double f[6];
951 
952     void set( double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; }
953 public:
954     Matrix3();
955     ~Matrix3() {}
956 
957     void skew( double alpha, double beta );
958     void scale( double sx, double sy );
959     void rotate( double angle );
960     void translate( double tx, double ty );
961     bool invert();
962 
963     void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL );
964 
965     Point transform( const Point& rPoint ) const;
966 };
967 }
968 
969 Matrix3::Matrix3()
970 {
971     // initialize to unity
972     f[0] = 1.0;
973     f[1] = 0.0;
974     f[2] = 0.0;
975     f[3] = 1.0;
976     f[4] = 0.0;
977     f[5] = 0.0;
978 }
979 
980 Point Matrix3::transform( const Point& rOrig ) const
981 {
982     double x = (double)rOrig.X(), y = (double)rOrig.Y();
983     return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) );
984 }
985 
986 void Matrix3::skew( double alpha, double beta )
987 {
988     double fn[6];
989     double tb = tan( beta );
990     fn[0] = f[0] + f[2]*tb;
991     fn[1] = f[1];
992     fn[2] = f[2] + f[3]*tb;
993     fn[3] = f[3];
994     fn[4] = f[4] + f[5]*tb;
995     fn[5] = f[5];
996     if( alpha != 0.0 )
997     {
998         double ta = tan( alpha );
999         fn[1] += f[0]*ta;
1000         fn[3] += f[2]*ta;
1001         fn[5] += f[4]*ta;
1002     }
1003     set( fn );
1004 }
1005 
1006 void Matrix3::scale( double sx, double sy )
1007 {
1008     double fn[6];
1009     fn[0] = sx*f[0];
1010     fn[1] = sy*f[1];
1011     fn[2] = sx*f[2];
1012     fn[3] = sy*f[3];
1013     fn[4] = sx*f[4];
1014     fn[5] = sy*f[5];
1015     set( fn );
1016 }
1017 
1018 void Matrix3::rotate( double angle )
1019 {
1020     double fn[6];
1021     double fSin = sin(angle);
1022     double fCos = cos(angle);
1023     fn[0] = f[0]*fCos - f[1]*fSin;
1024     fn[1] = f[0]*fSin + f[1]*fCos;
1025     fn[2] = f[2]*fCos - f[3]*fSin;
1026     fn[3] = f[2]*fSin + f[3]*fCos;
1027     fn[4] = f[4]*fCos - f[5]*fSin;
1028     fn[5] = f[4]*fSin + f[5]*fCos;
1029     set( fn );
1030 }
1031 
1032 void Matrix3::translate( double tx, double ty )
1033 {
1034     f[4] += tx;
1035     f[5] += ty;
1036 }
1037 
1038 bool Matrix3::invert()
1039 {
1040 	// short circuit trivial cases
1041 	if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 )
1042 	{
1043 		f[4] = -f[4];
1044 		f[5] = -f[5];
1045 		return true;
1046 	}
1047 
1048 	// check determinant
1049 	const double fDet = f[0]*f[3]-f[1]*f[2];
1050 	if( fDet == 0.0 )
1051 		return false;
1052 
1053 	// invert the matrix
1054 	double fn[6];
1055 	fn[0] = +f[3] / fDet;
1056 	fn[1] = -f[1] / fDet;
1057 	fn[2] = -f[2] / fDet;
1058 	fn[3] = +f[0] / fDet;
1059 
1060 	// apply inversion to translation
1061 	fn[4] = -(f[4]*fn[0] + f[5]*fn[2]);
1062 	fn[5] = -(f[4]*fn[1] + f[5]*fn[3]);
1063 
1064 	set( fn );
1065 	return true;
1066 }
1067 
1068 void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack )
1069 {
1070     appendDouble( f[0], rBuffer );
1071     rBuffer.append( ' ' );
1072     appendDouble( f[1], rBuffer );
1073     rBuffer.append( ' ' );
1074     appendDouble( f[2], rBuffer );
1075     rBuffer.append( ' ' );
1076     appendDouble( f[3], rBuffer );
1077     rBuffer.append( ' ' );
1078     rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack );
1079 }
1080 
1081 static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList )
1082 {
1083     if( rList.empty() )
1084         return;
1085     rBuf.append( '/' );
1086     rBuf.append( pPrefix );
1087     rBuf.append( "<<" );
1088     int ni = 0;
1089     for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it )
1090     {
1091         if( it->first.getLength() && it->second > 0 )
1092         {
1093             rBuf.append( '/' );
1094             rBuf.append( it->first );
1095             rBuf.append( ' ' );
1096             rBuf.append( it->second );
1097             rBuf.append( " 0 R" );
1098             if( ((++ni) & 7) == 0 )
1099                 rBuf.append( '\n' );
1100         }
1101     }
1102     rBuf.append( ">>\n" );
1103 }
1104 
1105 void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject )
1106 {
1107     rBuf.append( "<</Font " );
1108     rBuf.append( nFontDictObject );
1109     rBuf.append( " 0 R\n" );
1110     appendResourceMap( rBuf, "XObject", m_aXObjects );
1111     appendResourceMap( rBuf, "ExtGState", m_aExtGStates );
1112     appendResourceMap( rBuf, "Shading", m_aShadings );
1113     appendResourceMap( rBuf, "Pattern", m_aPatterns );
1114     rBuf.append( "/ProcSet[/PDF/Text" );
1115     if( !m_aXObjects.empty() )
1116         rBuf.append( "/ImageC/ImageI/ImageB" );
1117     rBuf.append( "]\n>>\n" );
1118 };
1119 
1120 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
1121         :
1122         m_pWriter( pWriter ),
1123         m_nPageWidth( nPageWidth ),
1124         m_nPageHeight( nPageHeight ),
1125         m_eOrientation( eOrientation ),
1126         m_nPageObject( 0 ),  // invalid object number
1127         m_nPageIndex( -1 ), // invalid index
1128         m_nStreamLengthObject( 0 ),
1129         m_nBeginStreamPos( 0 ),
1130         m_eTransition( PDFWriter::Regular ),
1131         m_nTransTime( 0 ),
1132         m_nDuration( 0 ),
1133         m_bHasWidgets( false )
1134 {
1135     // object ref must be only ever updated in emit()
1136     m_nPageObject = m_pWriter->createObject();
1137 }
1138 
1139 PDFWriterImpl::PDFPage::~PDFPage()
1140 {
1141 }
1142 
1143 void PDFWriterImpl::PDFPage::beginStream()
1144 {
1145 #if OSL_DEBUG_LEVEL > 1
1146     {
1147         OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" );
1148          m_pWriter->emitComment( aLine.getStr() );
1149     }
1150 #endif
1151     m_aStreamObjects.push_back(m_pWriter->createObject());
1152     if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) )
1153         return;
1154 
1155     m_nStreamLengthObject = m_pWriter->createObject();
1156     // write content stream header
1157     OStringBuffer aLine;
1158     aLine.append( m_aStreamObjects.back() );
1159     aLine.append( " 0 obj\n<</Length " );
1160     aLine.append( m_nStreamLengthObject );
1161     aLine.append( " 0 R" );
1162 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1163     aLine.append( "/Filter/FlateDecode" );
1164 #endif
1165     aLine.append( ">>\nstream\n" );
1166     if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) )
1167         return;
1168     if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &m_nBeginStreamPos ) )
1169     {
1170         osl_closeFile( m_pWriter->m_aFile );
1171         m_pWriter->m_bOpen = false;
1172     }
1173 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1174     m_pWriter->beginCompression();
1175 #endif
1176     m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() );
1177 }
1178 
1179 void PDFWriterImpl::PDFPage::endStream()
1180 {
1181 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1182     m_pWriter->endCompression();
1183 #endif
1184     sal_uInt64 nEndStreamPos;
1185     if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &nEndStreamPos ) )
1186     {
1187         osl_closeFile( m_pWriter->m_aFile );
1188         m_pWriter->m_bOpen = false;
1189         return;
1190     }
1191     m_pWriter->disableStreamEncryption();
1192     if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1193         return;
1194     // emit stream length object
1195     if( ! m_pWriter->updateObject( m_nStreamLengthObject ) )
1196         return;
1197     OStringBuffer aLine;
1198     aLine.append( m_nStreamLengthObject );
1199     aLine.append( " 0 obj\n" );
1200     aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) );
1201     aLine.append( "\nendobj\n\n" );
1202     m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1203 }
1204 
1205 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject )
1206 {
1207     // emit page object
1208     if( ! m_pWriter->updateObject( m_nPageObject ) )
1209         return false;
1210     OStringBuffer aLine;
1211 
1212     aLine.append( m_nPageObject );
1213     aLine.append( " 0 obj\n"
1214                   "<</Type/Page/Parent " );
1215     aLine.append( nParentObject );
1216     aLine.append( " 0 R" );
1217     aLine.append( "/Resources " );
1218     aLine.append( m_pWriter->getResourceDictObj() );
1219     aLine.append( " 0 R" );
1220     if( m_nPageWidth && m_nPageHeight )
1221     {
1222         aLine.append( "/MediaBox[0 0 " );
1223         aLine.append( m_nPageWidth );
1224         aLine.append( ' ' );
1225         aLine.append( m_nPageHeight );
1226         aLine.append( "]" );
1227     }
1228     switch( m_eOrientation )
1229     {
1230         case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
1231         case PDFWriter::Seascape:  aLine.append( "/Rotate -90\n" );break;
1232         case PDFWriter::Portrait:  aLine.append( "/Rotate 0\n" );break;
1233 
1234         case PDFWriter::Inherit:
1235         default:
1236             break;
1237     }
1238     int nAnnots = m_aAnnotations.size();
1239     if( nAnnots > 0 )
1240     {
1241         aLine.append( "/Annots[\n" );
1242         for( int i = 0; i < nAnnots; i++ )
1243         {
1244             aLine.append( m_aAnnotations[i] );
1245             aLine.append( " 0 R" );
1246             aLine.append( ((i+1)%15) ? " " : "\n" );
1247         }
1248         aLine.append( "]\n" );
1249     }
1250     #if 0
1251     // FIXME: implement tab order as Structure Tree
1252     if( m_bHasWidgets && m_pWriter->getVersion() >= PDFWriter::PDF_1_5 )
1253         aLine.append( "   /Tabs /S\n" );
1254     #endif
1255     if( m_aMCIDParents.size() > 0 )
1256     {
1257         OStringBuffer aStructParents( 1024 );
1258         aStructParents.append( "[ " );
1259         int nParents = m_aMCIDParents.size();
1260         for( int i = 0; i < nParents; i++ )
1261         {
1262             aStructParents.append( m_aMCIDParents[i] );
1263             aStructParents.append( " 0 R" );
1264             aStructParents.append( ((i%10) == 9) ? "\n" : " " );
1265         }
1266         aStructParents.append( "]" );
1267         m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() );
1268 
1269         aLine.append( "/StructParents " );
1270         aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) );
1271         aLine.append( "\n" );
1272     }
1273     if( m_nDuration > 0 )
1274     {
1275         aLine.append( "/Dur " );
1276         aLine.append( (sal_Int32)m_nDuration );
1277         aLine.append( "\n" );
1278     }
1279     if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 )
1280     {
1281         // transition duration
1282         aLine.append( "/Trans<</D " );
1283         appendDouble( (double)m_nTransTime/1000.0, aLine, 3 );
1284         aLine.append( "\n" );
1285         const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL;
1286         switch( m_eTransition )
1287         {
1288             case PDFWriter::SplitHorizontalInward:
1289                 pStyle = "Split"; pDm = "H"; pM = "I"; break;
1290             case PDFWriter::SplitHorizontalOutward:
1291                 pStyle = "Split"; pDm = "H"; pM = "O"; break;
1292             case PDFWriter::SplitVerticalInward:
1293                 pStyle = "Split"; pDm = "V"; pM = "I"; break;
1294             case PDFWriter::SplitVerticalOutward:
1295                 pStyle = "Split"; pDm = "V"; pM = "O"; break;
1296             case PDFWriter::BlindsHorizontal:
1297                 pStyle = "Blinds"; pDm = "H"; break;
1298             case PDFWriter::BlindsVertical:
1299                 pStyle = "Blinds"; pDm = "V"; break;
1300             case PDFWriter::BoxInward:
1301                 pStyle = "Box"; pM = "I"; break;
1302             case PDFWriter::BoxOutward:
1303                 pStyle = "Box"; pM = "O"; break;
1304             case PDFWriter::WipeLeftToRight:
1305                 pStyle = "Wipe"; pDi = "0"; break;
1306             case PDFWriter::WipeBottomToTop:
1307                 pStyle = "Wipe"; pDi = "90"; break;
1308             case PDFWriter::WipeRightToLeft:
1309                 pStyle = "Wipe"; pDi = "180"; break;
1310             case PDFWriter::WipeTopToBottom:
1311                 pStyle = "Wipe"; pDi = "270"; break;
1312             case PDFWriter::Dissolve:
1313                 pStyle = "Dissolve"; break;
1314             case PDFWriter::GlitterLeftToRight:
1315                 pStyle = "Glitter"; pDi = "0"; break;
1316             case PDFWriter::GlitterTopToBottom:
1317                 pStyle = "Glitter"; pDi = "270"; break;
1318             case PDFWriter::GlitterTopLeftToBottomRight:
1319                 pStyle = "Glitter"; pDi = "315"; break;
1320             case PDFWriter::Regular:
1321                 break;
1322         }
1323         // transition style
1324         if( pStyle )
1325         {
1326             aLine.append( "/S/" );
1327             aLine.append( pStyle );
1328             aLine.append( "\n" );
1329         }
1330         if( pDm )
1331         {
1332             aLine.append( "/Dm/" );
1333             aLine.append( pDm );
1334             aLine.append( "\n" );
1335         }
1336         if( pM )
1337         {
1338             aLine.append( "/M/" );
1339             aLine.append( pM );
1340             aLine.append( "\n" );
1341         }
1342         if( pDi  )
1343         {
1344             aLine.append( "/Di " );
1345             aLine.append( pDi );
1346             aLine.append( "\n" );
1347         }
1348         aLine.append( ">>\n" );
1349     }
1350     if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 )
1351     {
1352         aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1353     }
1354     aLine.append( "/Contents" );
1355     unsigned int nStreamObjects = m_aStreamObjects.size();
1356     if( nStreamObjects > 1 )
1357         aLine.append( '[' );
1358     for( unsigned int i = 0; i < m_aStreamObjects.size(); i++ )
1359     {
1360         aLine.append( ' ' );
1361         aLine.append( m_aStreamObjects[i] );
1362         aLine.append( " 0 R" );
1363     }
1364     if( nStreamObjects > 1 )
1365         aLine.append( ']' );
1366     aLine.append( ">>\nendobj\n\n" );
1367     return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1368 }
1369 
1370 namespace vcl
1371 {
1372 template < class GEOMETRY >
1373 GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
1374 {
1375     GEOMETRY aPoint;
1376     if ( MAP_PIXEL == _rSource.GetMapUnit() )
1377     {
1378         aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
1379     }
1380     else
1381     {
1382         aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
1383     }
1384     return aPoint;
1385 }
1386 }
1387 
1388 void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const
1389 {
1390     if( pOutPoint )
1391     {
1392         Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1393                                    m_pWriter->m_aMapMode,
1394                                    m_pWriter->getReferenceDevice(),
1395                                    rPoint ) );
1396         *pOutPoint = aPoint;
1397     }
1398 
1399     Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1400                                m_pWriter->m_aMapMode,
1401                                m_pWriter->getReferenceDevice(),
1402                                rPoint ) );
1403 
1404     sal_Int32 nValue	= aPoint.X();
1405     if( bNeg )
1406         nValue = -nValue;
1407 
1408     appendFixedInt( nValue, rBuffer );
1409 
1410     rBuffer.append( ' ' );
1411 
1412     nValue		= pointToPixel(getHeight()) - aPoint.Y();
1413     if( bNeg )
1414         nValue = -nValue;
1415 
1416     appendFixedInt( nValue, rBuffer );
1417 }
1418 
1419 void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const
1420 {
1421     double fValue	= pixelToPoint(rPoint.getX());
1422 
1423     appendDouble( fValue, rBuffer, nLog10Divisor );
1424 
1425     rBuffer.append( ' ' );
1426 
1427     fValue		= double(getHeight()) - pixelToPoint(rPoint.getY());
1428 
1429     appendDouble( fValue, rBuffer, nLog10Divisor );
1430 }
1431 
1432 void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const
1433 {
1434     appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer );
1435     rBuffer.append( ' ' );
1436     appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false );
1437     rBuffer.append( ' ' );
1438     appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true );
1439     rBuffer.append( " re" );
1440 }
1441 
1442 void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const
1443 {
1444     Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1445                              m_pWriter->m_aMapMode,
1446                              m_pWriter->getReferenceDevice(),
1447                              rRect.BottomLeft() + Point( 0, 1 )
1448                              );
1449     Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1450                               m_pWriter->m_aMapMode,
1451                               m_pWriter->getReferenceDevice(),
1452                               rRect.GetSize() );
1453     rRect.Left()	= aLL.X();
1454     rRect.Right()	= aLL.X() + aSize.Width();
1455     rRect.Top()		= pointToPixel(getHeight()) - aLL.Y();
1456     rRect.Bottom()	= rRect.Top() + aSize.Height();
1457 }
1458 
1459 void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1460 {
1461     sal_uInt16 nPoints = rPoly.GetSize();
1462     /*
1463      *  #108582# applications do weird things
1464      */
1465     sal_uInt32 nBufLen = rBuffer.getLength();
1466     if( nPoints > 0 )
1467     {
1468         const sal_uInt8* pFlagArray = rPoly.GetConstFlagAry();
1469         appendPoint( rPoly[0], rBuffer );
1470         rBuffer.append( " m\n" );
1471         for( sal_uInt16 i = 1; i < nPoints; i++ )
1472         {
1473             if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 )
1474             {
1475                 // bezier
1476                 DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" );
1477                 appendPoint( rPoly[i], rBuffer );
1478                 rBuffer.append( " " );
1479                 appendPoint( rPoly[i+1], rBuffer );
1480                 rBuffer.append( " " );
1481                 appendPoint( rPoly[i+2], rBuffer );
1482                 rBuffer.append( " c" );
1483                 i += 2; // add additionally consumed points
1484             }
1485             else
1486             {
1487                 // line
1488                 appendPoint( rPoly[i], rBuffer );
1489                 rBuffer.append( " l" );
1490             }
1491             if( (rBuffer.getLength() - nBufLen) > 65 )
1492             {
1493                 rBuffer.append( "\n" );
1494                 nBufLen = rBuffer.getLength();
1495             }
1496             else
1497                 rBuffer.append( " " );
1498         }
1499         if( bClose )
1500             rBuffer.append( "h\n" );
1501     }
1502 }
1503 
1504 void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1505 {
1506     basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1507                                             m_pWriter->m_aMapMode,
1508                                             m_pWriter->getReferenceDevice(),
1509                                             rPoly ) );
1510 
1511     if( basegfx::tools::isRectangle( aPoly ) )
1512     {
1513         basegfx::B2DRange aRange( aPoly.getB2DRange() );
1514         basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() );
1515         appendPixelPoint( aBL, rBuffer );
1516         rBuffer.append( ' ' );
1517         appendMappedLength( aRange.getWidth(), rBuffer, false, NULL, nLog10Divisor );
1518         rBuffer.append( ' ' );
1519         appendMappedLength( aRange.getHeight(), rBuffer, true, NULL, nLog10Divisor );
1520         rBuffer.append( " re\n" );
1521         return;
1522     }
1523     sal_uInt32 nPoints = aPoly.count();
1524     if( nPoints > 0 )
1525     {
1526         sal_uInt32 nBufLen = rBuffer.getLength();
1527         basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) );
1528         appendPixelPoint( aLastPoint, rBuffer );
1529         rBuffer.append( " m\n" );
1530         for( sal_uInt32 i = 1; i <= nPoints; i++ )
1531         {
1532             if( i != nPoints || aPoly.isClosed() )
1533             {
1534                 sal_uInt32 nCurPoint  = i % nPoints;
1535                 sal_uInt32 nLastPoint = i-1;
1536                 basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) );
1537                 if( aPoly.isNextControlPointUsed( nLastPoint ) &&
1538                     aPoly.isPrevControlPointUsed( nCurPoint ) )
1539                 {
1540                     appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1541                     rBuffer.append( ' ' );
1542                     appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1543                     rBuffer.append( ' ' );
1544                     appendPixelPoint( aPoint, rBuffer );
1545                     rBuffer.append( " c" );
1546                 }
1547                 else if( aPoly.isNextControlPointUsed( nLastPoint ) )
1548                 {
1549                     appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1550                     rBuffer.append( ' ' );
1551                     appendPixelPoint( aPoint, rBuffer );
1552                     rBuffer.append( " y" );
1553                 }
1554                 else if( aPoly.isPrevControlPointUsed( nCurPoint ) )
1555                 {
1556                     appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1557                     rBuffer.append( ' ' );
1558                     appendPixelPoint( aPoint, rBuffer );
1559                     rBuffer.append( " v" );
1560                 }
1561                 else
1562                 {
1563                     appendPixelPoint( aPoint, rBuffer );
1564                     rBuffer.append( " l" );
1565                 }
1566                 if( (rBuffer.getLength() - nBufLen) > 65 )
1567                 {
1568                     rBuffer.append( "\n" );
1569                     nBufLen = rBuffer.getLength();
1570                 }
1571                 else
1572                     rBuffer.append( " " );
1573             }
1574         }
1575         if( bClose )
1576             rBuffer.append( "h\n" );
1577     }
1578 }
1579 
1580 void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1581 {
1582     sal_uInt16 nPolygons = rPolyPoly.Count();
1583     for( sal_uInt16 n = 0; n < nPolygons; n++ )
1584         appendPolygon( rPolyPoly[n], rBuffer, bClose );
1585 }
1586 
1587 void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1588 {
1589     sal_uInt32 nPolygons = rPolyPoly.count();
1590     for( sal_uInt32 n = 0; n < nPolygons; n++ )
1591         appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer, bClose );
1592 }
1593 
1594 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1595 {
1596     sal_Int32 nValue = nLength;
1597     if ( nLength < 0 )
1598     {
1599         rBuffer.append( '-' );
1600         nValue = -nLength;
1601     }
1602     Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1603                              m_pWriter->m_aMapMode,
1604                              m_pWriter->getReferenceDevice(),
1605                              Size( nValue, nValue ) ) );
1606     nValue = bVertical ? aSize.Height() : aSize.Width();
1607     if( pOutLength )
1608         *pOutLength = ((nLength < 0 ) ? -nValue : nValue);
1609 
1610     appendFixedInt( nValue, rBuffer, 1 );
1611 }
1612 
1613 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength, sal_Int32 nPrecision ) const
1614 {
1615     Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1616                              m_pWriter->m_aMapMode,
1617                              m_pWriter->getReferenceDevice(),
1618                              Size( 1000, 1000 )	) );
1619     if( pOutLength )
1620         *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0);
1621     fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0);
1622     appendDouble( fLength, rBuffer, nPrecision );
1623 }
1624 
1625 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const
1626 {
1627     if(LINE_DASH == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen())
1628     {
1629         // dashed and non-degraded case, check for implementation limits of dash array
1630         // in PDF reader apps (e.g. acroread)
1631         if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10)
1632         {
1633             return false;
1634         }
1635     }
1636 
1637     if(basegfx::B2DLINEJOIN_NONE != rInfo.GetLineJoin())
1638     {
1639         // LineJoin used, ExtLineInfo required
1640         return false;
1641     }
1642 
1643     if(com::sun::star::drawing::LineCap_BUTT != rInfo.GetLineCap())
1644     {
1645         // LineCap used, ExtLineInfo required
1646         return false;
1647     }
1648 
1649     if( rInfo.GetStyle() == LINE_DASH )
1650     {
1651         rBuffer.append( "[ " );
1652         if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case
1653         {
1654             appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1655             rBuffer.append( ' ' );
1656             appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1657             rBuffer.append( ' ' );
1658         }
1659         else
1660         {
1661             for( int n = 0; n < rInfo.GetDashCount(); n++ )
1662             {
1663                 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1664                 rBuffer.append( ' ' );
1665                 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1666                 rBuffer.append( ' ' );
1667             }
1668             for( int m = 0; m < rInfo.GetDotCount(); m++ )
1669             {
1670                 appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer );
1671                 rBuffer.append( ' ' );
1672                 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1673                 rBuffer.append( ' ' );
1674             }
1675         }
1676         rBuffer.append( "] 0 d\n" );
1677     }
1678 
1679     if( rInfo.GetWidth() > 1 )
1680     {
1681         appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer );
1682         rBuffer.append( " w\n" );
1683     }
1684     else if( rInfo.GetWidth() == 0 )
1685     {
1686         // "pixel" line
1687         appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->ImplGetDPIX()), rBuffer );
1688         rBuffer.append( " w\n" );
1689     }
1690 
1691     return true;
1692 }
1693 
1694 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const
1695 {
1696     if( nWidth <= 0 )
1697         return;
1698     if( nDelta < 1 )
1699         nDelta = 1;
1700 
1701     rBuffer.append( "0 " );
1702     appendMappedLength( nY, rBuffer, true );
1703     rBuffer.append( " m\n" );
1704     for( sal_Int32 n = 0; n < nWidth; )
1705     {
1706         n += nDelta;
1707         appendMappedLength( n, rBuffer, false );
1708         rBuffer.append( ' ' );
1709         appendMappedLength( nDelta+nY, rBuffer, true );
1710         rBuffer.append( ' ' );
1711         n += nDelta;
1712         appendMappedLength( n, rBuffer, false );
1713         rBuffer.append( ' ' );
1714         appendMappedLength( nY, rBuffer, true );
1715         rBuffer.append( " v " );
1716         if( n < nWidth )
1717         {
1718             n += nDelta;
1719             appendMappedLength( n, rBuffer, false );
1720             rBuffer.append( ' ' );
1721             appendMappedLength( nY-nDelta, rBuffer, true );
1722             rBuffer.append( ' ' );
1723             n += nDelta;
1724             appendMappedLength( n, rBuffer, false );
1725             rBuffer.append( ' ' );
1726             appendMappedLength( nY, rBuffer, true );
1727             rBuffer.append( " v\n" );
1728         }
1729     }
1730     rBuffer.append( "S\n" );
1731 }
1732 
1733 /*
1734  *  class PDFWriterImpl
1735  */
1736 
1737  PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext,
1738                                const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc,
1739                                PDFWriter& i_rOuterFace)
1740         :
1741         m_pReferenceDevice( NULL ),
1742         m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ),
1743         m_nCurrentStructElement( 0 ),
1744         m_bEmitStructure( true ),
1745         m_bNewMCID( false ),
1746         m_nCurrentControl( -1 ),
1747         m_bEmbedStandardFonts( false ),
1748         m_nNextFID( 1 ),
1749         m_nInheritedPageWidth( 595 ),  // default A4
1750         m_nInheritedPageHeight( 842 ), // default A4
1751         m_eInheritedOrientation( PDFWriter::Portrait ),
1752         m_nCurrentPage( -1 ),
1753         m_nResourceDict( -1 ),
1754         m_nFontDictObject( -1 ),
1755         m_pCodec( NULL ),
1756         m_aDocDigest( rtl_digest_createMD5() ),
1757 		m_aCipher( (rtlCipher)NULL ),
1758 		m_aDigest( NULL ),
1759 		m_bEncryptThisStream( false ),
1760 		m_pEncryptionBuffer( NULL ),
1761 		m_nEncryptionBufferSize( 0 ),
1762         m_bIsPDF_A1( false ),
1763         m_rOuterFace( i_rOuterFace )
1764 {
1765 #ifdef DO_TEST_PDF
1766     static bool bOnce = true;
1767     if( bOnce )
1768     {
1769         bOnce = false;
1770         doTestCode();
1771     }
1772 #endif
1773     m_aContext = rContext;
1774     m_aStructure.push_back( PDFStructureElement() );
1775     m_aStructure[0].m_nOwnElement		= 0;
1776     m_aStructure[0].m_nParentElement	= 0;
1777 
1778     Font aFont;
1779     aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
1780     aFont.SetSize( Size( 0, 12 ) );
1781 
1782     GraphicsState aState;
1783     aState.m_aMapMode		= m_aMapMode;
1784     aState.m_aFont			= aFont;
1785     m_aGraphicsStack.push_front( aState );
1786 
1787     oslFileError  aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
1788     if( aError != osl_File_E_None )
1789     {
1790         if( aError == osl_File_E_EXIST )
1791         {
1792             aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write );
1793             if( aError == osl_File_E_None )
1794                 aError = osl_setFileSize( m_aFile, 0 );
1795         }
1796     }
1797     if( aError != osl_File_E_None )
1798         return;
1799 
1800     m_bOpen = true;
1801 
1802     // setup DocInfo
1803     setupDocInfo();
1804 
1805     /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1806 	m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1807 	m_aDigest = rtl_digest_createMD5();
1808 
1809 	/* the size of the Codec default maximum */
1810 	checkEncryptionBufferSize( 0x4000 );
1811 
1812 	if( xEnc.is() )
1813 	    prepareEncryption( xEnc );
1814 
1815 	if( m_aContext.Encryption.Encrypt() )
1816 	{
1817 	    // sanity check
1818 	    if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE ||
1819 	        m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE ||
1820 	        m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH
1821 	       )
1822 	    {
1823 	        // the field lengths are invalid ? This was not setup by initEncryption.
1824 	        // do not encrypt after all
1825 	        m_aContext.Encryption.OValue.clear();
1826 	        m_aContext.Encryption.UValue.clear();
1827 	        OSL_ENSURE( 0, "encryption data failed sanity check, encryption disabled" );
1828 	    }
1829 	    else // setup key lengths
1830 	        m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength );
1831 	}
1832 
1833     // write header
1834     OStringBuffer aBuffer( 20 );
1835     aBuffer.append( "%PDF-" );
1836     switch( m_aContext.Version )
1837     {
1838         case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break;
1839         case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break;
1840         case PDFWriter::PDF_A_1:
1841         default:
1842         case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break;
1843         case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break;
1844     }
1845     // append something binary as comment (suggested in PDF Reference)
1846     aBuffer.append( "\n%äüöß\n" );
1847     if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) )
1848     {
1849         osl_closeFile( m_aFile );
1850         m_bOpen = false;
1851         return;
1852     }
1853 
1854     // insert outline root
1855     m_aOutline.push_back( PDFOutlineEntry() );
1856 
1857     m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1);
1858     if( m_bIsPDF_A1 )
1859         m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour
1860 
1861     m_bEmbedStandardFonts = m_aContext.EmbedStandardFonts;
1862 }
1863 
1864 PDFWriterImpl::~PDFWriterImpl()
1865 {
1866     if( m_aDocDigest )
1867         rtl_digest_destroyMD5( m_aDocDigest );
1868     delete static_cast<VirtualDevice*>(m_pReferenceDevice);
1869 
1870 	if( m_aCipher )
1871 		rtl_cipher_destroyARCFOUR( m_aCipher );
1872 	if( m_aDigest )
1873 		rtl_digest_destroyMD5( m_aDigest );
1874 
1875     rtl_freeMemory( m_pEncryptionBuffer );
1876 }
1877 
1878 void PDFWriterImpl::setupDocInfo()
1879 {
1880     std::vector< sal_uInt8 > aId;
1881     computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString );
1882     if( m_aContext.Encryption.DocumentIdentifier.empty() )
1883         m_aContext.Encryption.DocumentIdentifier = aId;
1884 }
1885 
1886 void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier,
1887                                                const vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
1888                                                rtl::OString& o_rCString1,
1889                                                rtl::OString& o_rCString2
1890                                                )
1891 {
1892     o_rIdentifier.clear();
1893 
1894     //build the document id
1895 	rtl::OString aInfoValuesOut;
1896 	OStringBuffer aID( 1024 );
1897 	if( i_rDocInfo.Title.Len() )
1898 		appendUnicodeTextString( i_rDocInfo.Title, aID );
1899 	if( i_rDocInfo.Author.Len() )
1900 		appendUnicodeTextString( i_rDocInfo.Author, aID );
1901 	if( i_rDocInfo.Subject.Len() )
1902 		appendUnicodeTextString( i_rDocInfo.Subject, aID );
1903 	if( i_rDocInfo.Keywords.Len() )
1904 		appendUnicodeTextString( i_rDocInfo.Keywords, aID );
1905 	if( i_rDocInfo.Creator.Len() )
1906 		appendUnicodeTextString( i_rDocInfo.Creator, aID );
1907 	if( i_rDocInfo.Producer.Len() )
1908 		appendUnicodeTextString( i_rDocInfo.Producer, aID );
1909 
1910 	TimeValue aTVal, aGMT;
1911 	oslDateTime aDT;
1912 	osl_getSystemTime( &aGMT );
1913 	osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
1914 	osl_getDateTimeFromTimeValue( &aTVal, &aDT );
1915 	rtl::OStringBuffer aCreationDateString(64), aCreationMetaDateString(64);
1916 	aCreationDateString.append( "D:" );
1917 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1918 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1919 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1920 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1921 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1922 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1923 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1924 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1925 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1926 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1927 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1928 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1929 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1930 	aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1931 
1932 	//--> i59651, we fill the Metadata date string as well, if PDF/A is requested
1933     // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1934     // local time zone offset UTC only, whereas Acrobat 8 seems
1935     // to use the localtime notation only
1936     // according to a raccomandation in XMP Specification (Jan 2004, page 75)
1937     // the Acrobat way seems the right approach
1938     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1939     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1940     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1941     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1942     aCreationMetaDateString.append( "-" );
1943     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1944     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1945     aCreationMetaDateString.append( "-" );
1946     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1947     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1948     aCreationMetaDateString.append( "T" );
1949     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1950     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1951     aCreationMetaDateString.append( ":" );
1952     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1953     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1954     aCreationMetaDateString.append( ":" );
1955     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1956     aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1957 
1958 	sal_uInt32 nDelta = 0;
1959 	if( aGMT.Seconds > aTVal.Seconds )
1960 	{
1961 		aCreationDateString.append( "-" );
1962 		nDelta = aGMT.Seconds-aTVal.Seconds;
1963 		aCreationMetaDateString.append( "-" );
1964 	}
1965 	else if( aGMT.Seconds < aTVal.Seconds )
1966 	{
1967 		aCreationDateString.append( "+" );
1968 		nDelta = aTVal.Seconds-aGMT.Seconds;
1969 		aCreationMetaDateString.append( "+" );
1970 	}
1971 	else
1972     {
1973 		aCreationDateString.append( "Z" );
1974 		aCreationMetaDateString.append( "Z" );
1975 
1976     }
1977 	if( nDelta )
1978 	{
1979 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1980 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1981 		aCreationDateString.append( "'" );
1982 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1983 		aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1984 
1985 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1986 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1987 		aCreationMetaDateString.append( ":" );
1988 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1989 		aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1990 	}
1991 	aCreationDateString.append( "'" );
1992 	aID.append( aCreationDateString.getStr(), aCreationDateString.getLength() );
1993 
1994 	aInfoValuesOut = aID.makeStringAndClear();
1995 	o_rCString1 = aCreationDateString.makeStringAndClear();
1996 	o_rCString2 = aCreationMetaDateString.makeStringAndClear();
1997 
1998 	rtlDigest aDigest = rtl_digest_createMD5();
1999 	OSL_ENSURE( aDigest != NULL, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" );
2000 	if( aDigest )
2001 	{
2002 		rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) );
2003 		if( nError == rtl_Digest_E_None )
2004 			nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() );
2005 		if( nError == rtl_Digest_E_None )
2006 		{
2007 		    o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 );
2008 		    //the binary form of the doc id is needed for encryption stuff
2009 			rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 );
2010 		}
2011 	}
2012 }
2013 
2014 /* i12626 methods */
2015 /*
2016 check if the Unicode string must be encrypted or not, perform the requested task,
2017 append the string as unicode hex, encrypted if needed
2018  */
2019 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2020 {
2021 	rOutBuffer.append( "<" );
2022 	if( m_aContext.Encryption.Encrypt() )
2023 	{
2024 		const sal_Unicode* pStr = rInString.getStr();
2025 		sal_Int32 nLen = rInString.getLength();
2026 //prepare a unicode string, encrypt it
2027 		if( checkEncryptionBufferSize( nLen*2 ) )
2028 		{
2029 			enableStringEncryption( nInObjectNumber );
2030 			register sal_uInt8 *pCopy = m_pEncryptionBuffer;
2031 			sal_Int32 nChars = 2;
2032 			*pCopy++ = 0xFE;
2033 			*pCopy++ = 0xFF;
2034 // we need to prepare a byte stream from the unicode string buffer
2035 			for( register int i = 0; i < nLen; i++ )
2036 			{
2037 				register sal_Unicode aUnChar = pStr[i];
2038 				*pCopy++ = (sal_uInt8)( aUnChar >> 8 );
2039 				*pCopy++ = (sal_uInt8)( aUnChar & 255 );
2040 				nChars += 2;
2041 			}
2042 //encrypt in place
2043 			rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars );
2044 //now append, hexadecimal (appendHex), the encrypted result
2045 			for(register int i = 0; i < nChars; i++)
2046 				appendHex( m_pEncryptionBuffer[i], rOutBuffer );
2047 		}
2048 	}
2049 	else
2050 		appendUnicodeTextString( rInString, rOutBuffer );
2051 	rOutBuffer.append( ">" );
2052 }
2053 
2054 inline void PDFWriterImpl::appendLiteralStringEncrypt( rtl::OStringBuffer& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
2055 {
2056 	rOutBuffer.append( "(" );
2057 	sal_Int32 nChars = rInString.getLength();
2058 //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
2059 	if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) )
2060 	{
2061 //encrypt the string in a buffer, then append it
2062 		enableStringEncryption( nInObjectNumber );
2063 		rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars );
2064 		appendLiteralString( (const sal_Char*)m_pEncryptionBuffer, nChars, rOutBuffer );
2065 	}
2066 	else
2067         appendLiteralString( rInString.getStr(), nChars , rOutBuffer );
2068 	rOutBuffer.append( ")" );
2069 }
2070 
2071 inline void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
2072 {
2073 	rtl::OStringBuffer aBufferString( rInString );
2074 	appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2075 }
2076 
2077 void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc )
2078 {
2079 	rtl::OString aBufferString( rtl::OUStringToOString( rInString, nEnc ) );
2080 	sal_Int32 nLen = aBufferString.getLength();
2081 	rtl::OStringBuffer aBuf( nLen );
2082 	const sal_Char* pT = aBufferString.getStr();
2083 
2084 	for( sal_Int32 i = 0; i < nLen; i++, pT++ )
2085 	{
2086 	    if( (*pT & 0x80) == 0 )
2087 	        aBuf.append( *pT );
2088 	    else
2089 	    {
2090 	        aBuf.append( '<' );
2091 	        appendHex( *pT, aBuf );
2092 	        aBuf.append( '>' );
2093 	    }
2094 	}
2095 	aBufferString = aBuf.makeStringAndClear();
2096 	appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2097 }
2098 
2099 /* end i12626 methods */
2100 
2101 void PDFWriterImpl::emitComment( const char* pComment )
2102 {
2103     OStringBuffer aLine( 64 );
2104     aLine.append( "% " );
2105     aLine.append( (const sal_Char*)pComment );
2106     aLine.append( "\n" );
2107     writeBuffer( aLine.getStr(), aLine.getLength() );
2108 }
2109 
2110 bool PDFWriterImpl::compressStream( SvMemoryStream* pStream )
2111 {
2112 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2113     pStream->Seek( STREAM_SEEK_TO_END );
2114     sal_uLong nEndPos = pStream->Tell();
2115     pStream->Seek( STREAM_SEEK_TO_BEGIN );
2116     ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
2117     SvMemoryStream aStream;
2118     pCodec->BeginCompression();
2119     pCodec->Write( aStream, (const sal_uInt8*)pStream->GetData(), nEndPos );
2120     pCodec->EndCompression();
2121     delete pCodec;
2122     nEndPos = aStream.Tell();
2123     pStream->Seek( STREAM_SEEK_TO_BEGIN );
2124     aStream.Seek( STREAM_SEEK_TO_BEGIN );
2125     pStream->SetStreamSize( nEndPos );
2126     pStream->Write( aStream.GetData(), nEndPos );
2127     return true;
2128 #else
2129     (void)pStream;
2130     return false;
2131 #endif
2132 }
2133 
2134 void PDFWriterImpl::beginCompression()
2135 {
2136 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2137     m_pCodec = new ZCodec( 0x4000, 0x4000 );
2138     m_pMemStream = new SvMemoryStream();
2139     m_pCodec->BeginCompression();
2140 #endif
2141 }
2142 
2143 void PDFWriterImpl::endCompression()
2144 {
2145 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2146     if( m_pCodec )
2147     {
2148         m_pCodec->EndCompression();
2149         delete m_pCodec;
2150         m_pCodec = NULL;
2151         sal_uInt64 nLen = m_pMemStream->Tell();
2152         m_pMemStream->Seek( 0 );
2153         writeBuffer( m_pMemStream->GetData(), nLen );
2154         delete m_pMemStream;
2155         m_pMemStream = NULL;
2156     }
2157 #endif
2158 }
2159 
2160 bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes )
2161 {
2162     if( ! m_bOpen ) // we are already down the drain
2163         return false;
2164 
2165     if( ! nBytes ) // huh ?
2166         return true;
2167 
2168     if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2169     {
2170         m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END );
2171         m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) );
2172         return true;
2173     }
2174 
2175     sal_uInt64 nWritten;
2176     if( m_pCodec )
2177     {
2178         m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes );
2179         nWritten = nBytes;
2180     }
2181     else
2182     {
2183 		sal_Bool  buffOK = sal_True;
2184 		if( m_bEncryptThisStream )
2185 		{
2186 /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
2187 			if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) != sal_False )
2188 				rtl_cipher_encodeARCFOUR( m_aCipher,
2189                                           (sal_uInt8*)pBuffer, static_cast<sal_Size>(nBytes),
2190                                           m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) );
2191 		}
2192 
2193         const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer  : pBuffer;
2194         if( m_aDocDigest )
2195             rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) );
2196 
2197         if( osl_writeFile( m_aFile,
2198                            pWriteBuffer,
2199                            nBytes, &nWritten ) != osl_File_E_None )
2200             nWritten = 0;
2201 
2202         if( nWritten != nBytes )
2203         {
2204             osl_closeFile( m_aFile );
2205             m_bOpen = false;
2206         }
2207     }
2208 
2209     return nWritten == nBytes;
2210 }
2211 
2212 OutputDevice* PDFWriterImpl::getReferenceDevice()
2213 {
2214     if( ! m_pReferenceDevice )
2215     {
2216         VirtualDevice*  pVDev = new VirtualDevice( 0 );
2217 
2218         m_pReferenceDevice = pVDev;
2219 
2220         if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 )
2221             pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 );
2222         else
2223             pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy );
2224 
2225         pVDev->SetOutputSizePixel( Size( 640, 480 ) );
2226         pVDev->SetMapMode( MAP_MM );
2227 
2228         m_pReferenceDevice->mpPDFWriter = this;
2229         m_pReferenceDevice->ImplUpdateFontData( sal_True );
2230     }
2231     return m_pReferenceDevice;
2232 }
2233 
2234 class ImplPdfBuiltinFontData : public ImplFontData
2235 {
2236 private:
2237     const PDFWriterImpl::BuiltinFont& mrBuiltin;
2238 
2239 public:
2240     enum {PDF_FONT_MAGIC = 0xBDFF0A1C };
2241                                         ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& );
2242     const PDFWriterImpl::BuiltinFont*   GetBuiltinFont() const  { return &mrBuiltin; }
2243 
2244     virtual ImplFontData*               Clone() const { return new ImplPdfBuiltinFontData(*this); }
2245     virtual ImplFontEntry*              CreateFontInstance( ImplFontSelectData& ) const;
2246     virtual sal_IntPtr                  GetFontId() const { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); }
2247 };
2248 
2249 inline const ImplPdfBuiltinFontData* GetPdfFontData( const ImplFontData* pFontData )
2250 {
2251     const ImplPdfBuiltinFontData* pFD = NULL;
2252     if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) )
2253         pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData );
2254     return pFD;
2255 }
2256 
2257 static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin )
2258 {
2259     ImplDevFontAttributes aDFA;
2260     aDFA.maName         = String::CreateFromAscii( rBuiltin.m_pName );
2261     aDFA.maStyleName    = String::CreateFromAscii( rBuiltin.m_pStyleName );
2262     aDFA.meFamily       = rBuiltin.m_eFamily;
2263     aDFA.mbSymbolFlag   = (rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 );
2264     aDFA.mePitch        = rBuiltin.m_ePitch;
2265     aDFA.meWeight       = rBuiltin.m_eWeight;
2266     aDFA.meItalic       = rBuiltin.m_eItalic;
2267     aDFA.meWidthType    = rBuiltin.m_eWidthType;
2268 
2269     aDFA.mbOrientation  = true;
2270     aDFA.mbDevice       = true;
2271     aDFA.mnQuality      = 50000;
2272     aDFA.mbSubsettable  = false;
2273     aDFA.mbEmbeddable   = false;
2274     return aDFA;
2275 }
2276 
2277 ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin )
2278 :   ImplFontData( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ),
2279     mrBuiltin( rBuiltin )
2280 {}
2281 
2282 ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
2283 {
2284     ImplFontEntry* pEntry = new ImplFontEntry( rFSD );
2285     return pEntry;
2286 }
2287 
2288 ImplDevFontList* PDFWriterImpl::filterDevFontList( ImplDevFontList* pFontList )
2289 {
2290     DBG_ASSERT( m_aSubsets.size() == 0, "Fonts changing during PDF generation, document will be invalid" );
2291     ImplDevFontList* pFiltered = pFontList->Clone( true, true );
2292 
2293     // append the PDF builtin fonts
2294     if( !m_bIsPDF_A1 && !m_bEmbedStandardFonts)
2295         for( unsigned int i = 0; i < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); i++ )
2296         {
2297             ImplFontData* pNewData = new ImplPdfBuiltinFontData( m_aBuiltinFonts[i] );
2298             pFiltered->Add( pNewData );
2299         }
2300     return pFiltered;
2301 }
2302 
2303 bool PDFWriterImpl::isBuiltinFont( const ImplFontData* pFont ) const
2304 {
2305     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
2306     return (pFD != NULL);
2307 }
2308 
2309 void PDFWriterImpl::getFontMetric( ImplFontSelectData* pSelect, ImplFontMetricData* pMetric ) const
2310 {
2311     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2312     if( !pFD )
2313         return;
2314     const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2315 
2316     pMetric->mnOrientation	= sal::static_int_cast<short>(pSelect->mnOrientation);
2317     pMetric->meFamily		= pBuiltinFont->m_eFamily;
2318     pMetric->mePitch		= pBuiltinFont->m_ePitch;
2319     pMetric->meWeight		= pBuiltinFont->m_eWeight;
2320     pMetric->meItalic		= pBuiltinFont->m_eItalic;
2321     pMetric->mbSymbolFlag	= pFD->IsSymbolFont();
2322     pMetric->mnWidth		= pSelect->mnHeight;
2323     pMetric->mnAscent		= ( pSelect->mnHeight * +pBuiltinFont->m_nAscent + 500 ) / 1000;
2324     pMetric->mnDescent		= ( pSelect->mnHeight * -pBuiltinFont->m_nDescent + 500 ) / 1000;
2325     pMetric->mnIntLeading	= 0;
2326     pMetric->mnExtLeading	= 0;
2327     pMetric->mnSlant		= 0;
2328     pMetric->mbScalableFont	= true;
2329     pMetric->mbDevice		= true;
2330 }
2331 
2332 // -----------------------------------------------------------------------
2333 
2334 namespace vcl {
2335 
2336 class PDFSalLayout : public GenericSalLayout
2337 {
2338     PDFWriterImpl&  mrPDFWriterImpl;
2339     const PDFWriterImpl::BuiltinFont& mrBuiltinFont;
2340     bool            mbIsSymbolFont;
2341     long            mnPixelPerEM;
2342     String          maOrigText;
2343 
2344 public:
2345                     PDFSalLayout( PDFWriterImpl&,
2346                                   const PDFWriterImpl::BuiltinFont&,
2347                                   long nPixelPerEM, int nOrientation );
2348 
2349     void            SetText( const String& rText )  { maOrigText = rText; }
2350     virtual bool    LayoutText( ImplLayoutArgs& );
2351     virtual void    InitFont() const;
2352     virtual void    DrawText( SalGraphics& ) const;
2353 };
2354 
2355 }
2356 
2357 // -----------------------------------------------------------------------
2358 
2359 PDFSalLayout::PDFSalLayout( PDFWriterImpl& rPDFWriterImpl,
2360     const PDFWriterImpl::BuiltinFont& rBuiltinFont,
2361     long nPixelPerEM, int nOrientation )
2362 :   mrPDFWriterImpl( rPDFWriterImpl ),
2363     mrBuiltinFont( rBuiltinFont ),
2364     mnPixelPerEM( nPixelPerEM )
2365 {
2366     mbIsSymbolFont = (rBuiltinFont.m_eCharSet != RTL_TEXTENCODING_MS_1252);
2367     SetOrientation( nOrientation );
2368 }
2369 
2370 // -----------------------------------------------------------------------
2371 
2372 bool PDFSalLayout::LayoutText( ImplLayoutArgs& rArgs )
2373 {
2374     const String aText( rArgs.mpStr+rArgs.mnMinCharPos, sal::static_int_cast<xub_StrLen>(rArgs.mnEndCharPos-rArgs.mnMinCharPos) );
2375     SetText( aText );
2376     SetUnitsPerPixel( 1000 );
2377 
2378     rtl_UnicodeToTextConverter aConv = rtl_createTextToUnicodeConverter( mrBuiltinFont.m_eCharSet );
2379 
2380     Point aNewPos( 0, 0 );
2381     bool bRightToLeft;
2382     for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); )
2383     {
2384         // TODO: handle unicode surrogates
2385         // on the other hand the PDF builtin fonts don't support them anyway
2386         sal_Unicode cChar = rArgs.mpStr[ nCharPos ];
2387         if( bRightToLeft )
2388             cChar = static_cast<sal_Unicode>(GetMirroredChar( cChar ));
2389 
2390         if( 1 ) // TODO: shortcut for ASCII?
2391         {
2392             sal_Char aBuf[4];
2393             sal_uInt32 nInfo;
2394             sal_Size nSrcCvtChars;
2395 
2396             sal_Size nConv = rtl_convertUnicodeToText( aConv,
2397                                                        NULL,
2398                                                        &cChar, 1,
2399                                                        aBuf, sizeof(aBuf)/sizeof(*aBuf),
2400                                                        RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR,
2401                                                        &nInfo, &nSrcCvtChars );
2402             // check whether conversion was possible
2403             // else fallback font is needed as the standard fonts
2404             // are handled via WinAnsi encoding
2405             if( nConv > 0 )
2406                 cChar = ((sal_Unicode)aBuf[0]) & 0x00ff;
2407         }
2408         if( cChar & 0xff00 )
2409         {
2410             cChar = 0;   // NotDef glyph
2411             rArgs.NeedFallback( nCharPos, bRightToLeft );
2412         }
2413 
2414         long nGlyphWidth = (long)mrBuiltinFont.m_aWidths[cChar] * mnPixelPerEM;
2415         long nGlyphFlags = 0; // builtin fonts don't have diacritic glyphs
2416         if( bRightToLeft )
2417             nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
2418         // TODO: get kerning from builtin fonts
2419         GlyphItem aGI( nCharPos, cChar, aNewPos, nGlyphFlags, nGlyphWidth );
2420         AppendGlyph( aGI );
2421 
2422         aNewPos.X() += nGlyphWidth;
2423     }
2424 
2425     rtl_destroyUnicodeToTextConverter( aConv );
2426 
2427     return true;
2428 }
2429 
2430 // -----------------------------------------------------------------------
2431 
2432 void PDFSalLayout::InitFont() const
2433 {
2434     // TODO: recreate font with all its attributes
2435 }
2436 
2437 // -----------------------------------------------------------------------
2438 
2439 void PDFSalLayout::DrawText( SalGraphics& ) const
2440 {
2441     mrPDFWriterImpl.drawLayout( *const_cast<PDFSalLayout*>(this), maOrigText, true );
2442 }
2443 
2444 // -----------------------------------------------------------------------
2445 
2446 SalLayout* PDFWriterImpl::GetTextLayout( ImplLayoutArgs& rArgs, ImplFontSelectData* pSelect )
2447 {
2448     DBG_ASSERT( (pSelect->mpFontData != NULL),
2449         "PDFWriterImpl::GetTextLayout mpFontData is NULL" );
2450 
2451     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2452     if( !pFD )
2453         return NULL;
2454     const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2455 
2456     long nPixelPerEM = pSelect->mnWidth ? pSelect->mnWidth : pSelect->mnHeight;
2457     int nOrientation = pSelect->mnOrientation;
2458     PDFSalLayout* pLayout = new PDFSalLayout( *this, *pBuiltinFont, nPixelPerEM, nOrientation );
2459     pLayout->SetText( rArgs.mpStr );
2460     return pLayout;
2461 }
2462 
2463 sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
2464 {
2465     endPage();
2466     m_nCurrentPage = m_aPages.size();
2467     m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) );
2468     m_aPages.back().m_nPageIndex = m_nCurrentPage;
2469     m_aPages.back().beginStream();
2470 
2471     // setup global graphics state
2472     // linewidth is "1 pixel" by default
2473     OStringBuffer aBuf( 16 );
2474     appendDouble( 72.0/double(getReferenceDevice()->ImplGetDPIX()), aBuf );
2475     aBuf.append( " w\n" );
2476     writeBuffer( aBuf.getStr(), aBuf.getLength() );
2477 
2478     return m_nCurrentPage;
2479 }
2480 
2481 void PDFWriterImpl::endPage()
2482 {
2483     if( m_aPages.begin() != m_aPages.end() )
2484     {
2485         // close eventual MC sequence
2486         endStructureElementMCSeq();
2487 
2488         // sanity check
2489         if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2490         {
2491             DBG_ERROR( "redirection across pages !!!" );
2492             m_aOutputStreams.clear(); // leak !
2493             m_aMapMode.SetOrigin( Point() );
2494         }
2495 
2496         m_aGraphicsStack.clear();
2497         m_aGraphicsStack.push_back( GraphicsState() );
2498 
2499         // this should pop the PDF graphics stack if necessary
2500         updateGraphicsState();
2501 
2502         m_aPages.back().endStream();
2503 
2504         // reset the default font
2505         Font aFont;
2506         aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
2507         aFont.SetSize( Size( 0, 12 ) );
2508 
2509         m_aCurrentPDFState = m_aGraphicsStack.front();
2510         m_aGraphicsStack.front().m_aFont =  aFont;
2511 
2512         for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin();
2513              it != m_aBitmaps.end(); ++it )
2514         {
2515             if( ! it->m_aBitmap.IsEmpty() )
2516             {
2517                 writeBitmapObject( *it );
2518                 it->m_aBitmap = BitmapEx();
2519             }
2520         }
2521         for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg )
2522         {
2523             if( jpeg->m_pStream )
2524             {
2525                 writeJPG( *jpeg );
2526                 delete jpeg->m_pStream;
2527                 jpeg->m_pStream = NULL;
2528                 jpeg->m_aMask = Bitmap();
2529             }
2530         }
2531         for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin();
2532              t != m_aTransparentObjects.end(); ++t )
2533         {
2534             if( t->m_pContentStream )
2535             {
2536                 writeTransparentObject( *t );
2537                 delete t->m_pContentStream;
2538                 t->m_pContentStream = NULL;
2539             }
2540         }
2541     }
2542 }
2543 
2544 sal_Int32 PDFWriterImpl::createObject()
2545 {
2546     m_aObjects.push_back( ~0U );
2547     return m_aObjects.size();
2548 }
2549 
2550 bool PDFWriterImpl::updateObject( sal_Int32 n )
2551 {
2552     if( ! m_bOpen )
2553         return false;
2554 
2555     sal_uInt64 nOffset = ~0U;
2556     oslFileError aError = osl_getFilePos( m_aFile, &nOffset );
2557     DBG_ASSERT( aError == osl_File_E_None, "could not register object" );
2558     if( aError != osl_File_E_None )
2559     {
2560         osl_closeFile( m_aFile );
2561         m_bOpen = false;
2562     }
2563     m_aObjects[ n-1 ] = nOffset;
2564     return aError == osl_File_E_None;
2565 }
2566 
2567 #define CHECK_RETURN( x ) if( !(x) ) return 0
2568 
2569 sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject )
2570 {
2571     if( nObject > 0 )
2572     {
2573         OStringBuffer aLine( 1024 );
2574 
2575         aLine.append( nObject );
2576         aLine.append( " 0 obj\n"
2577                       "<</Nums[\n" );
2578         sal_Int32 nTreeItems = m_aStructParentTree.size();
2579         for( sal_Int32 n = 0; n < nTreeItems; n++ )
2580         {
2581             aLine.append( n );
2582             aLine.append( ' ' );
2583             aLine.append( m_aStructParentTree[n] );
2584             aLine.append( "\n" );
2585         }
2586         aLine.append( "]>>\nendobj\n\n" );
2587         CHECK_RETURN( updateObject( nObject ) );
2588         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2589     }
2590     return nObject;
2591 }
2592 
2593 const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr )
2594 {
2595     static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings;
2596     // fill maps once
2597     if( aAttributeStrings.empty() )
2598     {
2599         aAttributeStrings[ PDFWriter::Placement ]			= "Placement";
2600         aAttributeStrings[ PDFWriter::WritingMode ]			= "WritingMode";
2601         aAttributeStrings[ PDFWriter::SpaceBefore ]			= "SpaceBefore";
2602         aAttributeStrings[ PDFWriter::SpaceAfter ]			= "SpaceAfter";
2603         aAttributeStrings[ PDFWriter::StartIndent ]			= "StartIndent";
2604         aAttributeStrings[ PDFWriter::EndIndent ]			= "EndIndent";
2605         aAttributeStrings[ PDFWriter::TextIndent ]			= "TextIndent";
2606         aAttributeStrings[ PDFWriter::TextAlign ]			= "TextAlign";
2607         aAttributeStrings[ PDFWriter::Width ]				= "Width";
2608         aAttributeStrings[ PDFWriter::Height ]				= "Height";
2609         aAttributeStrings[ PDFWriter::BlockAlign ]			= "BlockAlign";
2610         aAttributeStrings[ PDFWriter::InlineAlign ]			= "InlineAlign";
2611         aAttributeStrings[ PDFWriter::LineHeight ]			= "LineHeight";
2612         aAttributeStrings[ PDFWriter::BaselineShift ]		= "BaselineShift";
2613         aAttributeStrings[ PDFWriter::TextDecorationType ]	= "TextDecorationType";
2614         aAttributeStrings[ PDFWriter::ListNumbering ]		= "ListNumbering";
2615         aAttributeStrings[ PDFWriter::RowSpan ]				= "RowSpan";
2616         aAttributeStrings[ PDFWriter::ColSpan ]				= "ColSpan";
2617         aAttributeStrings[ PDFWriter::LinkAnnotation ]      = "LinkAnnotation";
2618     }
2619 
2620     std::map< PDFWriter::StructAttribute, const char* >::const_iterator it =
2621         aAttributeStrings.find( eAttr );
2622 
2623 #if OSL_DEBUG_LEVEL > 1
2624     if( it == aAttributeStrings.end() )
2625         fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr );
2626 #endif
2627 
2628     return it != aAttributeStrings.end() ? it->second : "";
2629 }
2630 
2631 const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal )
2632 {
2633     static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings;
2634 
2635     if( aValueStrings.empty() )
2636     {
2637         aValueStrings[ PDFWriter::NONE ]					= "None";
2638         aValueStrings[ PDFWriter::Block ]					= "Block";
2639         aValueStrings[ PDFWriter::Inline ]					= "Inline";
2640         aValueStrings[ PDFWriter::Before ]					= "Before";
2641         aValueStrings[ PDFWriter::After ]					= "After";
2642         aValueStrings[ PDFWriter::Start ]					= "Start";
2643         aValueStrings[ PDFWriter::End ]						= "End";
2644         aValueStrings[ PDFWriter::LrTb ]					= "LrTb";
2645         aValueStrings[ PDFWriter::RlTb ]					= "RlTb";
2646         aValueStrings[ PDFWriter::TbRl ]					= "TbRl";
2647         aValueStrings[ PDFWriter::Center ]					= "Center";
2648         aValueStrings[ PDFWriter::Justify ]					= "Justify";
2649         aValueStrings[ PDFWriter::Auto ]					= "Auto";
2650         aValueStrings[ PDFWriter::Middle ]					= "Middle";
2651         aValueStrings[ PDFWriter::Normal ]					= "Normal";
2652         aValueStrings[ PDFWriter::Underline ]				= "Underline";
2653 		aValueStrings[ PDFWriter::Overline ]				= "Overline";
2654         aValueStrings[ PDFWriter::LineThrough ]				= "LineThrough";
2655         aValueStrings[ PDFWriter::Disc ]					= "Disc";
2656         aValueStrings[ PDFWriter::Circle ]					= "Circle";
2657         aValueStrings[ PDFWriter::Square ]					= "Square";
2658         aValueStrings[ PDFWriter::Decimal ]					= "Decimal";
2659         aValueStrings[ PDFWriter::UpperRoman ]				= "UpperRoman";
2660         aValueStrings[ PDFWriter::LowerRoman ]				= "LowerRoman";
2661         aValueStrings[ PDFWriter::UpperAlpha ]				= "UpperAlpha";
2662         aValueStrings[ PDFWriter::LowerAlpha ]				= "LowerAlpha";
2663     }
2664 
2665     std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it =
2666         aValueStrings.find( eVal );
2667 
2668 #if OSL_DEBUG_LEVEL > 1
2669     if( it == aValueStrings.end() )
2670         fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal );
2671 #endif
2672 
2673     return it != aValueStrings.end() ? it->second : "";
2674 }
2675 
2676 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt )
2677 {
2678     o_rLine.append( "/" );
2679     o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) );
2680 
2681     if( i_rVal.eValue != PDFWriter::Invalid )
2682     {
2683         o_rLine.append( "/" );
2684         o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) );
2685     }
2686     else
2687     {
2688         // numerical value
2689         o_rLine.append( " " );
2690         if( i_bIsFixedInt )
2691             appendFixedInt( i_rVal.nValue, o_rLine );
2692         else
2693             o_rLine.append( i_rVal.nValue );
2694     }
2695     o_rLine.append( "\n" );
2696 }
2697 
2698 OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
2699 {
2700     // create layout, list and table attribute sets
2701     OStringBuffer aLayout(256), aList(64), aTable(64);
2702     for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin();
2703          it != i_rEle.m_aAttributes.end(); ++it )
2704     {
2705         if( it->first == PDFWriter::ListNumbering )
2706             appendStructureAttributeLine( it->first, it->second, aList, true );
2707         else if( it->first == PDFWriter::RowSpan ||
2708                  it->first == PDFWriter::ColSpan )
2709             appendStructureAttributeLine( it->first, it->second, aTable, false );
2710         else if( it->first == PDFWriter::LinkAnnotation )
2711         {
2712             sal_Int32 nLink = it->second.nValue;
2713             std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
2714                 m_aLinkPropertyMap.find( nLink );
2715             if( link_it != m_aLinkPropertyMap.end() )
2716                 nLink = link_it->second;
2717             if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() )
2718             {
2719                 // update struct parent of link
2720                 OStringBuffer aStructParentEntry( 32 );
2721                 aStructParentEntry.append( i_rEle.m_nObject );
2722                 aStructParentEntry.append( " 0 R" );
2723                 m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() );
2724                 m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1;
2725 
2726                 sal_Int32 nRefObject = createObject();
2727                 OStringBuffer aRef( 256 );
2728                 aRef.append( nRefObject );
2729                 aRef.append( " 0 obj\n"
2730                              "<</Type/OBJR/Obj " );
2731                 aRef.append( m_aLinks[ nLink ].m_nObject );
2732                 aRef.append( " 0 R>>\n"
2733                              "endobj\n\n"
2734                              );
2735                 updateObject( nRefObject );
2736                 writeBuffer( aRef.getStr(), aRef.getLength() );
2737 
2738                 i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) );
2739             }
2740             else
2741             {
2742                 DBG_ERROR( "unresolved link id for Link structure" );
2743 #if OSL_DEBUG_LEVEL > 1
2744                 fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink );
2745                 {
2746                     OStringBuffer aLine( "unresolved link id " );
2747                     aLine.append( nLink );
2748                     aLine.append( " for Link structure" );
2749                     emitComment( aLine.getStr() );
2750                 }
2751 #endif
2752             }
2753         }
2754         else
2755             appendStructureAttributeLine( it->first, it->second, aLayout, true );
2756     }
2757     if( ! i_rEle.m_aBBox.IsEmpty() )
2758     {
2759         aLayout.append( "/BBox[" );
2760         appendFixedInt( i_rEle.m_aBBox.Left(), aLayout );
2761         aLayout.append( " " );
2762         appendFixedInt( i_rEle.m_aBBox.Top(), aLayout );
2763         aLayout.append( " " );
2764         appendFixedInt( i_rEle.m_aBBox.Right(), aLayout );
2765         aLayout.append( " " );
2766         appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout );
2767         aLayout.append( "]\n" );
2768     }
2769 
2770     std::vector< sal_Int32 > aAttribObjects;
2771     if( aLayout.getLength() )
2772     {
2773         aAttribObjects.push_back( createObject() );
2774         updateObject( aAttribObjects.back() );
2775         OStringBuffer aObj( 64 );
2776         aObj.append( aAttribObjects.back() );
2777         aObj.append( " 0 obj\n"
2778                      "<</O/Layout\n" );
2779         aLayout.append( ">>\nendobj\n\n" );
2780         writeBuffer( aObj.getStr(), aObj.getLength() );
2781         writeBuffer( aLayout.getStr(), aLayout.getLength() );
2782     }
2783     if( aList.getLength() )
2784     {
2785         aAttribObjects.push_back( createObject() );
2786         updateObject( aAttribObjects.back() );
2787         OStringBuffer aObj( 64 );
2788         aObj.append( aAttribObjects.back() );
2789         aObj.append( " 0 obj\n"
2790                      "<</O/List\n" );
2791         aList.append( ">>\nendobj\n\n" );
2792         writeBuffer( aObj.getStr(), aObj.getLength() );
2793         writeBuffer( aList.getStr(), aList.getLength() );
2794     }
2795     if( aTable.getLength() )
2796     {
2797         aAttribObjects.push_back( createObject() );
2798         updateObject( aAttribObjects.back() );
2799         OStringBuffer aObj( 64 );
2800         aObj.append( aAttribObjects.back() );
2801         aObj.append( " 0 obj\n"
2802                      "<</O/Table\n" );
2803         aTable.append( ">>\nendobj\n\n" );
2804         writeBuffer( aObj.getStr(), aObj.getLength() );
2805         writeBuffer( aTable.getStr(), aTable.getLength() );
2806     }
2807 
2808     OStringBuffer aRet( 64 );
2809     if( aAttribObjects.size() > 1 )
2810         aRet.append( " [" );
2811     for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin();
2812          at_it != aAttribObjects.end(); ++at_it )
2813     {
2814         aRet.append( " " );
2815         aRet.append( *at_it );
2816         aRet.append( " 0 R" );
2817     }
2818     if( aAttribObjects.size() > 1 )
2819         aRet.append( " ]" );
2820     return aRet.makeStringAndClear();
2821 }
2822 
2823 sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
2824 {
2825     if(
2826        // do not emit NonStruct and its children
2827        rEle.m_eType == PDFWriter::NonStructElement &&
2828        rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root
2829        )
2830         return 0;
2831 
2832     for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
2833     {
2834         if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
2835         {
2836             PDFStructureElement& rChild = m_aStructure[ *it ];
2837             if( rChild.m_eType != PDFWriter::NonStructElement )
2838             {
2839                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
2840                     emitStructure( rChild );
2841                 else
2842                 {
2843                     DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure element" );
2844 #if OSL_DEBUG_LEVEL > 1
2845                     fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
2846 #endif
2847                 }
2848             }
2849         }
2850         else
2851         {
2852             DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
2853 #if OSL_DEBUG_LEVEL > 1
2854             fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
2855 #endif
2856         }
2857     }
2858 
2859     OStringBuffer aLine( 512 );
2860     aLine.append( rEle.m_nObject );
2861     aLine.append( " 0 obj\n"
2862                   "<</Type" );
2863     sal_Int32 nParentTree = -1;
2864     if( rEle.m_nOwnElement == rEle.m_nParentElement )
2865     {
2866         nParentTree = createObject();
2867         CHECK_RETURN( nParentTree );
2868         aLine.append( "/StructTreeRoot\n" );
2869         aLine.append( "/ParentTree " );
2870         aLine.append( nParentTree );
2871         aLine.append( " 0 R\n" );
2872         if( ! m_aRoleMap.empty() )
2873         {
2874             aLine.append( "/RoleMap<<" );
2875             for( std::hash_map<OString,OString,OStringHash>::const_iterator
2876                  it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it )
2877             {
2878                 aLine.append( '/' );
2879                 aLine.append(it->first);
2880                 aLine.append( '/' );
2881                 aLine.append( it->second );
2882                 aLine.append( '\n' );
2883             }
2884             aLine.append( ">>\n" );
2885         }
2886     }
2887     else
2888     {
2889         aLine.append( "/StructElem\n"
2890                       "/S/" );
2891         if( rEle.m_aAlias.getLength() > 0 )
2892             aLine.append( rEle.m_aAlias );
2893         else
2894             aLine.append( getStructureTag( rEle.m_eType ) );
2895         aLine.append( "\n"
2896                       "/P " );
2897         aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject );
2898         aLine.append( " 0 R\n"
2899                       "/Pg " );
2900         aLine.append( rEle.m_nFirstPageObject );
2901         aLine.append( " 0 R\n" );
2902         if( rEle.m_aActualText.getLength() )
2903         {
2904             aLine.append( "/ActualText" );
2905             appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine );
2906             aLine.append( "\n" );
2907         }
2908         if( rEle.m_aAltText.getLength() )
2909         {
2910             aLine.append( "/Alt" );
2911             appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine );
2912             aLine.append( "\n" );
2913         }
2914     }
2915     if( ! rEle.m_aBBox.IsEmpty() || rEle.m_aAttributes.size() )
2916     {
2917         OString aAttribs =  emitStructureAttributes( rEle );
2918         if( aAttribs.getLength() )
2919         {
2920             aLine.append( "/A" );
2921             aLine.append( aAttribs );
2922             aLine.append( "\n" );
2923         }
2924     }
2925     if( rEle.m_aLocale.Language.getLength() > 0 )
2926     {
2927         OUStringBuffer aLocBuf( 16 );
2928         aLocBuf.append( rEle.m_aLocale.Language.toAsciiLowerCase() );
2929         if( rEle.m_aLocale.Country.getLength() > 0 )
2930         {
2931             aLocBuf.append( sal_Unicode('-') );
2932             aLocBuf.append( rEle.m_aLocale.Country );
2933         }
2934         aLine.append( "/Lang" );
2935         appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine );
2936         aLine.append( "\n" );
2937     }
2938     if( ! rEle.m_aKids.empty() )
2939     {
2940         unsigned int i = 0;
2941         aLine.append( "/K[" );
2942         for( std::list< PDFStructureElementKid >::const_iterator it =
2943                  rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ )
2944         {
2945             if( it->nMCID == -1 )
2946             {
2947                 aLine.append( it->nObject );
2948                 aLine.append( " 0 R" );
2949                 aLine.append( ( (i & 15) == 15 ) ? "\n" : " " );
2950             }
2951             else
2952             {
2953                 if( it->nObject == rEle.m_nFirstPageObject )
2954                 {
2955                     aLine.append( it->nMCID );
2956                     aLine.append( " " );
2957                 }
2958                 else
2959                 {
2960                     aLine.append( "<</Type/MCR/Pg " );
2961                     aLine.append( it->nObject );
2962                     aLine.append( " 0 R /MCID " );
2963                     aLine.append( it->nMCID );
2964                     aLine.append( ">>\n" );
2965                 }
2966             }
2967         }
2968         aLine.append( "]\n" );
2969     }
2970     aLine.append( ">>\nendobj\n\n" );
2971 
2972     CHECK_RETURN( updateObject( rEle.m_nObject ) );
2973     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2974 
2975     CHECK_RETURN( emitStructParentTree( nParentTree ) );
2976 
2977     return rEle.m_nObject;
2978 }
2979 
2980 bool PDFWriterImpl::emitGradients()
2981 {
2982     for( std::list<GradientEmit>::iterator it = m_aGradients.begin();
2983          it != m_aGradients.end(); ++it )
2984     {
2985         CHECK_RETURN( writeGradientFunction( *it ) );
2986     }
2987     return true;
2988 }
2989 
2990 bool PDFWriterImpl::emitTilings()
2991 {
2992     OStringBuffer aTilingObj( 1024 );
2993 
2994     for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it )
2995     {
2996         DBG_ASSERT( it->m_pTilingStream, "tiling without stream" );
2997         if( ! it->m_pTilingStream )
2998             continue;
2999 
3000         aTilingObj.setLength( 0 );
3001 
3002         #if OSL_DEBUG_LEVEL > 1
3003         emitComment( "PDFWriterImpl::emitTilings" );
3004         #endif
3005 
3006         sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left();
3007         sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top();
3008         sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth();
3009         sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight();
3010         if( it->m_aCellSize.Width() == 0 )
3011             it->m_aCellSize.Width() = nW;
3012         if( it->m_aCellSize.Height() == 0 )
3013             it->m_aCellSize.Height() = nH;
3014 
3015         bool bDeflate = compressStream( it->m_pTilingStream );
3016         it->m_pTilingStream->Seek( STREAM_SEEK_TO_END );
3017         sal_Size nTilingStreamSize = it->m_pTilingStream->Tell();
3018         it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN );
3019 
3020         // write pattern object
3021         aTilingObj.append( it->m_nObject );
3022         aTilingObj.append( " 0 obj\n" );
3023         aTilingObj.append( "<</Type/Pattern/PatternType 1\n"
3024                            "/PaintType 1\n"
3025                            "/TilingType 2\n"
3026                            "/BBox[" );
3027         appendFixedInt( nX, aTilingObj );
3028         aTilingObj.append( ' ' );
3029         appendFixedInt( nY, aTilingObj );
3030         aTilingObj.append( ' ' );
3031         appendFixedInt( nX+nW, aTilingObj );
3032         aTilingObj.append( ' ' );
3033         appendFixedInt( nY+nH, aTilingObj );
3034         aTilingObj.append( "]\n"
3035                            "/XStep " );
3036         appendFixedInt( it->m_aCellSize.Width(), aTilingObj );
3037         aTilingObj.append( "\n"
3038                            "/YStep " );
3039         appendFixedInt( it->m_aCellSize.Height(), aTilingObj );
3040         aTilingObj.append( "\n" );
3041         if( it->m_aTransform.matrix[0] != 1.0 ||
3042             it->m_aTransform.matrix[1] != 0.0 ||
3043             it->m_aTransform.matrix[3] != 0.0 ||
3044             it->m_aTransform.matrix[4] != 1.0 ||
3045             it->m_aTransform.matrix[2] != 0.0 ||
3046             it->m_aTransform.matrix[5] != 0.0 )
3047         {
3048             aTilingObj.append( "/Matrix [" );
3049             // TODO: scaling, mirroring on y, etc
3050             appendDouble( it->m_aTransform.matrix[0], aTilingObj );
3051             aTilingObj.append( ' ' );
3052             appendDouble( it->m_aTransform.matrix[1], aTilingObj );
3053             aTilingObj.append( ' ' );
3054             appendDouble( it->m_aTransform.matrix[3], aTilingObj );
3055             aTilingObj.append( ' ' );
3056             appendDouble( it->m_aTransform.matrix[4], aTilingObj );
3057             aTilingObj.append( ' ' );
3058             appendDouble( it->m_aTransform.matrix[2], aTilingObj );
3059             aTilingObj.append( ' ' );
3060             appendDouble( it->m_aTransform.matrix[5], aTilingObj );
3061             aTilingObj.append( "]\n" );
3062         }
3063         aTilingObj.append( "/Resources" );
3064         it->m_aResources.append( aTilingObj, getFontDictObject() );
3065         if( bDeflate )
3066             aTilingObj.append( "/Filter/FlateDecode" );
3067         aTilingObj.append( "/Length " );
3068         aTilingObj.append( (sal_Int32)nTilingStreamSize );
3069         aTilingObj.append( ">>\nstream\n" );
3070         CHECK_RETURN( updateObject( it->m_nObject ) );
3071         CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
3072         checkAndEnableStreamEncryption( it->m_nObject );
3073         nTilingStreamSize = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize );
3074         delete it->m_pTilingStream;
3075         it->m_pTilingStream = NULL;
3076         if( nTilingStreamSize == 0 )
3077             return false;
3078         disableStreamEncryption();
3079         aTilingObj.setLength( 0 );
3080         aTilingObj.append( "\nendstream\nendobj\n\n" );
3081         CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
3082     }
3083     return true;
3084 }
3085 
3086 sal_Int32 PDFWriterImpl::emitBuiltinFont( const ImplFontData* pFont, sal_Int32 nFontObject )
3087 {
3088     const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
3089     if( !pFD )
3090         return 0;
3091     const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
3092 
3093     OStringBuffer aLine( 1024 );
3094 
3095     if( nFontObject <= 0 )
3096         nFontObject = createObject();
3097     CHECK_RETURN( updateObject( nFontObject ) );
3098     aLine.append( nFontObject );
3099     aLine.append( " 0 obj\n"
3100                   "<</Type/Font/Subtype/Type1/BaseFont/" );
3101     appendName( pBuiltinFont->m_pPSName, aLine );
3102     aLine.append( "\n" );
3103     if( pBuiltinFont->m_eCharSet == RTL_TEXTENCODING_MS_1252 )
3104          aLine.append( "/Encoding/WinAnsiEncoding\n" );
3105     aLine.append( ">>\nendobj\n\n" );
3106     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3107     return nFontObject;
3108 }
3109 
3110 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const ImplFontData* pFont, EmbedFont& rEmbed )
3111 {
3112     std::map< sal_Int32, sal_Int32 > aRet;
3113     if( isBuiltinFont( pFont ) )
3114     {
3115         aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont );
3116         return aRet;
3117     }
3118 
3119     sal_Int32 nFontObject = 0;
3120     sal_Int32 nFontDescriptor = 0;
3121     rtl::OString aSubType( "/Type1" );
3122     FontSubsetInfo aInfo;
3123     // fill in dummy values
3124     aInfo.m_nAscent = 1000;
3125 	aInfo.m_nDescent = 200;
3126 	aInfo.m_nCapHeight = 1000;
3127 	aInfo.m_aFontBBox = Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) );
3128     aInfo.m_aPSName = pFont->maName;
3129     sal_Int32 pWidths[256];
3130     rtl_zeroMemory( pWidths, sizeof(pWidths) );
3131     if( pFont->IsEmbeddable() )
3132     {
3133         const unsigned char* pFontData = NULL;
3134         long nFontLen = 0;
3135         sal_Ucs nEncodedCodes[256];
3136         sal_Int32 pEncWidths[256];
3137         if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pEncWidths, aInfo, &nFontLen )) != NULL )
3138         {
3139             m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3140             for( int i = 0; i < 256; i++ )
3141             {
3142                 if( nEncodedCodes[i] >= 32 && nEncodedCodes[i] < 256 )
3143                 {
3144                     pWidths[i] = pEncWidths[ i ];
3145                 }
3146             }
3147         }
3148     }
3149     else if( pFont->mbSubsettable )
3150     {
3151         aSubType = rtl::OString( "/TrueType" );
3152         Int32Vector aGlyphWidths;
3153         Ucs2UIntMap aUnicodeMap;
3154         m_pReferenceDevice->mpGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap );
3155 
3156         OUString aTmpName;
3157         osl_createTempFile( NULL, NULL, &aTmpName.pData );
3158         sal_Int32 pGlyphIDs[ 256 ];
3159         sal_uInt8 pEncoding[ 256 ];
3160         sal_Ucs   pUnicodes[ 256 ];
3161         sal_Int32 pDuWidths[ 256 ];
3162 
3163         memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) );
3164         memset( pEncoding, 0, sizeof( pEncoding ) );
3165         memset( pUnicodes, 0, sizeof( pUnicodes ) );
3166         memset( pDuWidths, 0, sizeof( pDuWidths ) );
3167 
3168         for( sal_Ucs c = 32; c < 256; c++ )
3169         {
3170             pUnicodes[c] = c;
3171             pEncoding[c] = c;
3172             pGlyphIDs[c] = 0;
3173             if( aUnicodeMap.find( c ) != aUnicodeMap.end() )
3174                 pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ];
3175         }
3176 
3177         m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, pFont, pGlyphIDs, pEncoding, pDuWidths, 256, aInfo );
3178         osl_removeFile( aTmpName.pData );
3179     }
3180     else
3181     {
3182         DBG_ERROR( "system font neither embeddable nor subsettable" );
3183     }
3184 
3185     // write font descriptor
3186     nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 );
3187     if( nFontDescriptor )
3188     {
3189         // write font object
3190         sal_Int32 nObject = createObject();
3191         if( updateObject( nObject ) )
3192         {
3193             OStringBuffer aLine( 1024 );
3194             aLine.append( nObject );
3195             aLine.append( " 0 obj\n"
3196                           "<</Type/Font/Subtype" );
3197             aLine.append( aSubType );
3198             aLine.append( "/BaseFont/" );
3199             appendName( aInfo.m_aPSName, aLine );
3200             aLine.append( "\n" );
3201             if( !pFont->mbSymbolFlag )
3202                 aLine.append( "/Encoding/WinAnsiEncoding\n" );
3203             aLine.append( "/FirstChar 32 /LastChar 255\n"
3204                           "/Widths[" );
3205             for( int i = 32; i < 256; i++ )
3206             {
3207                 aLine.append( pWidths[i] );
3208                 aLine.append( ((i&15) == 15) ? "\n" : " " );
3209             }
3210             aLine.append( "]\n"
3211                           "/FontDescriptor " );
3212             aLine.append( nFontDescriptor );
3213             aLine.append( " 0 R>>\n"
3214                           "endobj\n\n" );
3215             writeBuffer( aLine.getStr(), aLine.getLength() );
3216 
3217             nFontObject = nObject;
3218             aRet[ rEmbed.m_nNormalFontID ] = nObject;
3219         }
3220     }
3221 
3222     return aRet;
3223 }
3224 
3225 typedef int ThreeInts[3];
3226 static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen,
3227 	ThreeInts& rSegmentLengths )
3228 {
3229 	if( !pFontBytes || (nByteLen < 0) )
3230 		return false;
3231 	const unsigned char* pPtr = pFontBytes;
3232 	const unsigned char* pEnd = pFontBytes + nByteLen;
3233 
3234 	for( int i = 0; i < 3; ++i) {
3235 		// read segment1 header
3236 		if( pPtr+6 >= pEnd )
3237 			return false;
3238 		if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) )
3239 			return false;
3240 		const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2];
3241 		if( nLen <= 0)
3242 			return false;
3243 		rSegmentLengths[i] = nLen;
3244 		pPtr += nLen + 6;
3245 	}
3246 
3247 	// read segment-end header
3248 	if( pPtr+2 >= pEnd )
3249 		return false;
3250 	if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) )
3251 		return false;
3252 
3253 	return true;
3254 }
3255 
3256 struct FontException : public std::exception
3257 {
3258 };
3259 
3260 // TODO: always subset instead of embedding the full font => this method becomes obsolete then
3261 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFontData* pFont, EmbedFont& rEmbed )
3262 {
3263     std::map< sal_Int32, sal_Int32 > aRet;
3264     if( isBuiltinFont( pFont ) )
3265     {
3266         aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont );
3267         return aRet;
3268     }
3269 
3270     sal_Int32 nFontObject = 0;
3271     sal_Int32 nStreamObject = 0;
3272     sal_Int32 nFontDescriptor = 0;
3273 
3274     // prepare font encoding
3275     const Ucs2SIntMap* pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pFont, NULL );
3276     sal_Int32 nToUnicodeStream = 0;
3277     sal_uInt8 nEncoding[256];
3278     sal_Ucs nEncodedCodes[256];
3279     std::vector<sal_Ucs> aUnicodes;
3280     aUnicodes.reserve( 256 );
3281     sal_Int32 pUnicodesPerGlyph[256];
3282     sal_Int32 pEncToUnicodeIndex[256];
3283     if( pEncoding )
3284     {
3285         rtl_zeroMemory( nEncoding, sizeof(nEncoding) );
3286         rtl_zeroMemory( nEncodedCodes, sizeof(nEncodedCodes) );
3287         rtl_zeroMemory( pUnicodesPerGlyph, sizeof(pUnicodesPerGlyph) );
3288         rtl_zeroMemory( pEncToUnicodeIndex, sizeof(pEncToUnicodeIndex) );
3289         for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it )
3290         {
3291             if( it->second != -1 )
3292             {
3293                 sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff);
3294                 nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode );
3295                 nEncodedCodes[ nCode ] = it->first;
3296                 pEncToUnicodeIndex[ nCode ] = static_cast<sal_Int32>(aUnicodes.size());
3297                 aUnicodes.push_back( it->first );
3298                 pUnicodesPerGlyph[ nCode ] = 1;
3299             }
3300         }
3301     }
3302 
3303     FontSubsetInfo aInfo;
3304     sal_Int32 pWidths[256];
3305     const unsigned char* pFontData = NULL;
3306     long nFontLen = 0;
3307     sal_Int32 nLength1, nLength2;
3308     try
3309     {
3310         if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL )
3311         {
3312             if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 )
3313                 throw FontException();
3314             // see whether it is pfb or pfa; if it is a pfb, fill ranges
3315             // of 6 bytes that are not part of the font program
3316             std::list< int > aSections;
3317             std::list< int >::const_iterator it;
3318             int nIndex = 0;
3319             while( pFontData[nIndex] == 0x80 && nIndex < nFontLen-1 )
3320             {
3321                 aSections.push_back( nIndex );
3322                 if( pFontData[nIndex+1] == 0x03 )
3323                     break;
3324                 sal_Int32 nBytes =
3325                 ((sal_Int32)pFontData[nIndex+2])			|
3326                 ((sal_Int32)pFontData[nIndex+3]) << 8		|
3327                 ((sal_Int32)pFontData[nIndex+4]) << 16		|
3328                 ((sal_Int32)pFontData[nIndex+5]) << 24;
3329                 nIndex += nBytes+6;
3330             }
3331 
3332             // search for eexec
3333             // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below
3334             nIndex = 0;
3335             int nEndAsciiIndex;
3336             int nBeginBinaryIndex;
3337             int nEndBinaryIndex;
3338             do
3339             {
3340                 while( nIndex < nFontLen-4 &&
3341                     ( pFontData[nIndex] != 'e'	||
3342                         pFontData[nIndex+1] != 'e' ||
3343                         pFontData[nIndex+2] != 'x' ||
3344                         pFontData[nIndex+3] != 'e' ||
3345                         pFontData[nIndex+4] != 'c'
3346                         )
3347                     )
3348                 nIndex++;
3349                 // check whether we are in a excluded section
3350                 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3351                     ;
3352             } while( it != aSections.end() && nIndex < nFontLen-4 );
3353             // this should end the ascii part
3354             if( nIndex > nFontLen-5 )
3355                 throw FontException();
3356 
3357             nEndAsciiIndex = nIndex+4;
3358             // now count backwards until we can account for 512 '0'
3359             // which is the endmarker of the (hopefully) binary data
3360             // do not count the pfb header sections
3361             int nFound = 0;
3362             nIndex =  nFontLen-1;
3363             while( nIndex > 0 && nFound < 512 )
3364             {
3365                 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3366                     ;
3367                 if( it == aSections.end() )
3368                 {
3369                     // inside the 512 '0' block there may only be whitespace
3370                     // according to T1 spec; probably it would be to simple
3371                     // if all fonts complied
3372                     if( pFontData[nIndex] == '0' )
3373                         nFound++;
3374                         else if( nFound > 0					&&
3375                             pFontData[nIndex] != '\r'		&&
3376                         pFontData[nIndex] != '\t'		&&
3377                         pFontData[nIndex] != '\n'		&&
3378                         pFontData[nIndex] != ' ' )
3379                         break;
3380                 }
3381                 nIndex--;
3382             }
3383 
3384             if( nIndex < 1 || nIndex <= nEndAsciiIndex )
3385                 throw FontException();
3386 
3387             // nLength3 is the rest of the file - excluding any section headers
3388             // nIndex now points to the first of the 512 '0' characters marking the
3389             // fixed content portion
3390             sal_Int32 nLength3 = nFontLen - nIndex;
3391             for( it = aSections.begin(); it != aSections.end(); ++it )
3392             {
3393                 // special case: nIndex inside a section marker
3394                 if( nIndex >= (*it) && (*it)+6 > nIndex )
3395                     nLength3 -= (*it)+6 - nIndex;
3396                 else if( *it >= nIndex  )
3397                 {
3398                     if( *it < nFontLen - 6 )
3399                         nLength3 -= 6;
3400                     else // the last section 0x8003 is only 2 bytes after all
3401                         nLength3 -= (nFontLen - *it);
3402                 }
3403             }
3404 
3405             // there may be whitespace to ignore before the 512 '0'
3406             while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' )
3407             {
3408                 nIndex--;
3409                 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3410                     ;
3411                 if( it != aSections.end() )
3412                 {
3413                     nIndex = (*it)-1;
3414                     break; // this is surely a binary boundary, in ascii case it wouldn't matter
3415                 }
3416             }
3417             nEndBinaryIndex = nIndex;
3418 
3419             // search for beginning of binary section
3420             nBeginBinaryIndex = nEndAsciiIndex;
3421             do
3422             {
3423                 nBeginBinaryIndex++;
3424                 for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it )
3425                     ;
3426                     } while( nBeginBinaryIndex < nEndBinaryIndex &&
3427                         ( pFontData[nBeginBinaryIndex] == '\r'	||
3428                             pFontData[nBeginBinaryIndex] == '\n'	||
3429                             it != aSections.end() ) );
3430 
3431                     // it seems to be vital to copy the exact whitespace between binary data
3432                     // and eexec, else a invalid font results. so make nEndAsciiIndex
3433                     // always immediate in front of nBeginBinaryIndex
3434                     nEndAsciiIndex = nBeginBinaryIndex-1;
3435                     for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it )
3436                         ;
3437                     if( it != aSections.end() )
3438                         nEndAsciiIndex = (*it)-1;
3439 
3440                     nLength1 = nEndAsciiIndex+1; // including the last character
3441                     for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it )
3442                         nLength1 -= 6; // decrease by pfb section size
3443 
3444                     // if the first four bytes are all ascii hex characters, then binary data
3445                     // has to be converted to real binary data
3446                     for( nIndex = 0; nIndex < 4 &&
3447                         ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) ||
3448                             ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) ||
3449                             ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' )
3450                             ); ++nIndex )
3451                     ;
3452                     bool bConvertHexData = true;
3453                     if( nIndex < 4 )
3454                     {
3455                         bConvertHexData = false;
3456                         nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte
3457                         for( it = aSections.begin(); it != aSections.end(); ++it )
3458                             if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex )
3459                             nLength2 -= 6;
3460                     }
3461                     else
3462                     {
3463                         // count the hex ascii characters to get nLength2
3464                         nLength2 = 0;
3465                         int nNextSectionIndex = 0;
3466                         for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3467                             ;
3468                         if( it != aSections.end() )
3469                             nNextSectionIndex = *it;
3470                         for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3471                         {
3472                             if( nIndex == nNextSectionIndex )
3473                             {
3474                                 nIndex += 6;
3475                                 ++it;
3476                                 nNextSectionIndex = (it == aSections.end() ? 0 : *it );
3477                             }
3478                             if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) ||
3479                                 ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) ||
3480                             ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) )
3481                             nLength2++;
3482                         }
3483                         DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" );
3484                         nLength2 /= 2;
3485                     }
3486 
3487                     // now we can actually write the font stream !
3488                     #if OSL_DEBUG_LEVEL > 1
3489                     emitComment( " PDFWriterImpl::emitEmbeddedFont" );
3490                     #endif
3491                     OStringBuffer aLine( 512 );
3492                     nStreamObject = createObject();
3493                     if( !updateObject(nStreamObject))
3494                         throw FontException();
3495                     sal_Int32 nStreamLengthObject = createObject();
3496                     aLine.append( nStreamObject );
3497                     aLine.append( " 0 obj\n"
3498                         "<</Length " );
3499                     aLine.append( nStreamLengthObject );
3500                     aLine.append( " 0 R"
3501                         #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3502                         "/Filter/FlateDecode"
3503                         #endif
3504                         "/Length1 " );
3505                     aLine.append( nLength1 );
3506                     aLine.append( " /Length2 " );
3507                     aLine.append( nLength2 );
3508                     aLine.append( " /Length3 ");
3509                     aLine.append( nLength3 );
3510                     aLine.append( ">>\n"
3511                         "stream\n" );
3512                     if( !writeBuffer( aLine.getStr(), aLine.getLength() ) )
3513                         throw FontException();
3514 
3515                     sal_uInt64 nBeginStreamPos = 0;
3516                     osl_getFilePos( m_aFile, &nBeginStreamPos );
3517 
3518                     beginCompression();
3519                     checkAndEnableStreamEncryption( nStreamObject );
3520 
3521                     // write ascii section
3522                     if( aSections.begin() == aSections.end() )
3523                     {
3524                         if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) )
3525                             throw FontException();
3526                     }
3527                     else
3528                     {
3529                         // first section always starts at 0
3530                         it = aSections.begin();
3531                         nIndex = (*it)+6;
3532                         ++it;
3533                         while( *it < nEndAsciiIndex )
3534                         {
3535                             if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) )
3536                                 throw FontException();
3537                             nIndex = (*it)+6;
3538                             ++it;
3539                         }
3540                         // write partial last section
3541                         if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) )
3542                             throw FontException();
3543                     }
3544 
3545                     // write binary section
3546                     if( ! bConvertHexData )
3547                     {
3548                         if( aSections.begin() == aSections.end() )
3549                         {
3550                             if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) )
3551                                 throw FontException();
3552                         }
3553                         else
3554                         {
3555                             for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it )
3556                                 ;
3557                             // write first partial section
3558                             if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) )
3559                                 throw FontException();
3560                             // write following sections
3561                             while( it != aSections.end() )
3562                             {
3563                                 nIndex = (*it)+6;
3564                                 ++it;
3565                                 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3566                                 {
3567                                     sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3568                                     if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3569                                         throw FontException();
3570                                 }
3571                             }
3572                         }
3573                     }
3574                     else
3575                     {
3576                         boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] );
3577                         rtl_zeroMemory( pWriteBuffer.get(), nLength2 );
3578                         int nWriteIndex = 0;
3579 
3580                         int nNextSectionIndex = 0;
3581                         for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3582                             ;
3583                         if( it != aSections.end() )
3584                             nNextSectionIndex = *it;
3585                         for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3586                         {
3587                             if( nIndex == nNextSectionIndex )
3588                             {
3589                                 nIndex += 6;
3590                                 ++it;
3591                                 nNextSectionIndex = (it == aSections.end() ? nFontLen : *it );
3592                             }
3593                             unsigned char cNibble = 0x80;
3594                             if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' )
3595                                 cNibble = pFontData[nIndex] - '0';
3596                             else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' )
3597                                 cNibble = pFontData[nIndex] - 'a' + 10;
3598                             else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' )
3599                                 cNibble = pFontData[nIndex] - 'A' + 10;
3600                             if( cNibble != 0x80 )
3601                             {
3602                                 if( !(nWriteIndex & 1 ) )
3603                                     cNibble <<= 4;
3604                                 pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble;
3605                                 nWriteIndex++;
3606                             }
3607                         }
3608                         if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) )
3609                             throw FontException();
3610                         if( aSections.empty() )
3611                         {
3612                             if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) )
3613                                 throw FontException();
3614                         }
3615                         else
3616                         {
3617                             // write rest of this section
3618                             if( nIndex < nNextSectionIndex )
3619                             {
3620                                 if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) )
3621                                     throw FontException();
3622                             }
3623                             // write following sections
3624                             while( it != aSections.end() )
3625                             {
3626                                 nIndex = (*it)+6;
3627                                 ++it;
3628                                 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3629                                 {
3630                                     sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3631                                     if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3632                                         throw FontException();
3633                                 }
3634                             }
3635                         }
3636                     }
3637                     endCompression();
3638                     disableStreamEncryption();
3639 
3640 
3641                     sal_uInt64 nEndStreamPos = 0;
3642                     osl_getFilePos( m_aFile, &nEndStreamPos );
3643 
3644                     // and finally close the stream
3645                     aLine.setLength( 0 );
3646                     aLine.append( "\nendstream\nendobj\n\n" );
3647                     if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3648                         throw FontException();
3649 
3650                     // write stream length object
3651                     aLine.setLength( 0 );
3652                     if( ! updateObject( nStreamLengthObject ) )
3653                         throw FontException();
3654                     aLine.append( nStreamLengthObject );
3655                     aLine.append( " 0 obj\n" );
3656                     aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) );
3657                     aLine.append( "\nendobj\n\n" );
3658                     if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3659                         throw FontException();
3660         }
3661         else
3662         {
3663             rtl::OStringBuffer aErrorComment( 256 );
3664             aErrorComment.append( "GetEmbedFontData failed for font \"" );
3665             aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3666             aErrorComment.append( '\"' );
3667             if( pFont->GetSlant() == ITALIC_NORMAL )
3668                 aErrorComment.append( " italic" );
3669             else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3670                 aErrorComment.append( " oblique" );
3671             aErrorComment.append( " weight=" );
3672             aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3673             emitComment( aErrorComment.getStr() );
3674         }
3675 
3676         if( nStreamObject )
3677             // write font descriptor
3678         nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject );
3679 
3680         if( nFontDescriptor )
3681         {
3682             if( pEncoding )
3683                 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, sizeof(nEncoding)/sizeof(nEncoding[0]) );
3684 
3685             // write font object
3686             sal_Int32 nObject = createObject();
3687             if( ! updateObject( nObject ) )
3688                 throw FontException();
3689 
3690             OStringBuffer aLine( 1024 );
3691             aLine.append( nObject );
3692             aLine.append( " 0 obj\n"
3693                 "<</Type/Font/Subtype/Type1/BaseFont/" );
3694             appendName( aInfo.m_aPSName, aLine );
3695             aLine.append( "\n" );
3696             if( !pFont->mbSymbolFlag &&  pEncoding == 0 )
3697                 aLine.append( "/Encoding/WinAnsiEncoding\n" );
3698             if( nToUnicodeStream )
3699             {
3700                 aLine.append( "/ToUnicode " );
3701                 aLine.append( nToUnicodeStream );
3702                 aLine.append( " 0 R\n" );
3703             }
3704             aLine.append( "/FirstChar 0 /LastChar 255\n"
3705                 "/Widths[" );
3706             for( int i = 0; i < 256; i++ )
3707             {
3708                 aLine.append( pWidths[i] );
3709                 aLine.append( ((i&15) == 15) ? "\n" : " " );
3710             }
3711             aLine.append( "]\n"
3712                 "/FontDescriptor " );
3713             aLine.append( nFontDescriptor );
3714             aLine.append( " 0 R>>\n"
3715                 "endobj\n\n" );
3716             if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3717                 throw FontException();
3718 
3719             nFontObject = nObject;
3720 
3721             aRet[ rEmbed.m_nNormalFontID ] = nObject;
3722 
3723             // write additional encodings
3724             for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it )
3725             {
3726                 sal_Int32 aEncWidths[ 256 ];
3727                 // emit encoding dict
3728                 sal_Int32 nEncObject = createObject();
3729                 if( ! updateObject( nEncObject ) )
3730                     throw FontException();
3731 
3732                 OutputDevice* pRef = getReferenceDevice();
3733                 pRef->Push( PUSH_FONT | PUSH_MAPMODE );
3734                 pRef->SetMapMode( MapMode( MAP_PIXEL ) );
3735                 Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) );
3736                 aFont.SetWeight( pFont->GetWeight() );
3737                 aFont.SetItalic( pFont->GetSlant() );
3738                 aFont.SetPitch( pFont->GetPitch() );
3739                 pRef->SetFont( aFont );
3740                 pRef->ImplNewFont();
3741 
3742                 aLine.setLength( 0 );
3743                 aLine.append( nEncObject );
3744                 aLine.append( " 0 obj\n"
3745                     "<</Type/Encoding/Differences[ 0\n" );
3746                 int nEncoded = 0;
3747                 aUnicodes.clear();
3748                 for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it )
3749                 {
3750                     String aStr( str_it->m_aUnicode );
3751                     aEncWidths[nEncoded] = pRef->GetTextWidth( aStr );
3752                     nEncodedCodes[nEncoded] = str_it->m_aUnicode;
3753                     nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded);
3754                     pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size());
3755                     aUnicodes.push_back( nEncodedCodes[nEncoded] );
3756                     pUnicodesPerGlyph[nEncoded] = 1;
3757 
3758                     aLine.append( " /" );
3759                     aLine.append( str_it->m_aName );
3760                     if( !((++nEncoded) & 15) )
3761                         aLine.append( "\n" );
3762                 }
3763                 aLine.append( "]>>\n"
3764                     "endobj\n\n" );
3765 
3766                 pRef->Pop();
3767 
3768                 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3769                     throw FontException();
3770 
3771                 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded );
3772 
3773                 nObject = createObject();
3774                 if( ! updateObject( nObject ) )
3775                     throw FontException();
3776 
3777                 aLine.setLength( 0 );
3778                 aLine.append( nObject );
3779                 aLine.append( " 0 obj\n"
3780                     "<</Type/Font/Subtype/Type1/BaseFont/" );
3781                 appendName( aInfo.m_aPSName, aLine );
3782                 aLine.append( "\n" );
3783                 aLine.append( "/Encoding " );
3784                 aLine.append( nEncObject );
3785                 aLine.append( " 0 R\n" );
3786                 if( nToUnicodeStream )
3787                 {
3788                     aLine.append( "/ToUnicode " );
3789                     aLine.append( nToUnicodeStream );
3790                     aLine.append( " 0 R\n" );
3791                 }
3792                 aLine.append( "/FirstChar 0\n"
3793                     "/LastChar " );
3794                 aLine.append( (sal_Int32)(nEncoded-1) );
3795                 aLine.append( "\n"
3796                     "/Widths[" );
3797                 for( int i = 0; i < nEncoded; i++ )
3798                 {
3799                     aLine.append( aEncWidths[i] );
3800                     aLine.append( ((i&15) == 15) ? "\n" : " " );
3801                 }
3802                 aLine.append( " ]\n"
3803                     "/FontDescriptor " );
3804                 aLine.append( nFontDescriptor );
3805                 aLine.append( " 0 R>>\n"
3806                     "endobj\n\n" );
3807                 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3808                     throw FontException();
3809 
3810                 aRet[ enc_it->m_nFontID ] = nObject;
3811             }
3812         }
3813     }
3814     catch( FontException& )
3815     {
3816         // these do nothing in case there was no compression or encryption ongoing
3817         endCompression();
3818         disableStreamEncryption();
3819     }
3820 
3821     if( pFontData )
3822         m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3823 
3824     return aRet;
3825 }
3826 
3827 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
3828 {
3829     if( nSubsetID )
3830     {
3831         for( int i = 0; i < 6; i++ )
3832         {
3833             int nOffset = (nSubsetID % 26);
3834             nSubsetID /= 26;
3835             rBuffer.append( (sal_Char)('A'+nOffset) );
3836         }
3837         rBuffer.append( '+' );
3838     }
3839     appendName( rPSName, rBuffer );
3840 }
3841 
3842 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding,
3843                                               sal_Ucs* pUnicodes,
3844                                               sal_Int32* pUnicodesPerGlyph,
3845                                               sal_Int32* pEncToUnicodeIndex,
3846                                               int nGlyphs )
3847 {
3848     int nMapped = 0, n = 0;
3849     for( n = 0; n < nGlyphs; n++ )
3850         if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3851             nMapped++;
3852 
3853     if( nMapped == 0 )
3854         return 0;
3855 
3856     sal_Int32 nStream = createObject();
3857     CHECK_RETURN( updateObject( nStream ) );
3858 
3859     OStringBuffer aContents( 1024 );
3860     aContents.append(
3861                      "/CIDInit/ProcSet findresource begin\n"
3862                      "12 dict begin\n"
3863                      "begincmap\n"
3864                      "/CIDSystemInfo<<\n"
3865                      "/Registry (Adobe)\n"
3866                      "/Ordering (UCS)\n"
3867                      "/Supplement 0\n"
3868                      ">> def\n"
3869                      "/CMapName/Adobe-Identity-UCS def\n"
3870                      "/CMapType 2 def\n"
3871                      "1 begincodespacerange\n"
3872                      "<00> <FF>\n"
3873                      "endcodespacerange\n"
3874                      );
3875     int nCount = 0;
3876     for( n = 0; n < nGlyphs; n++ )
3877     {
3878         if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3879         {
3880             if( (nCount % 100) == 0 )
3881             {
3882                 if( nCount )
3883                     aContents.append( "endbfchar\n" );
3884                 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) );
3885                 aContents.append( " beginbfchar\n" );
3886             }
3887             aContents.append( '<' );
3888             appendHex( (sal_Int8)pEncoding[n], aContents );
3889             aContents.append( "> <" );
3890             // TODO: handle unicodes>U+FFFF
3891             sal_Int32 nIndex = pEncToUnicodeIndex[n];
3892 	        for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ )
3893 	        {
3894                 appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents );
3895                 appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents );
3896             }
3897             aContents.append( ">\n" );
3898             nCount++;
3899         }
3900     }
3901     aContents.append( "endbfchar\n"
3902                       "endcmap\n"
3903                       "CMapName currentdict /CMap defineresource pop\n"
3904                       "end\n"
3905                       "end\n" );
3906 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3907     ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
3908     SvMemoryStream aStream;
3909     pCodec->BeginCompression();
3910     pCodec->Write( aStream, (const sal_uInt8*)aContents.getStr(), aContents.getLength() );
3911     pCodec->EndCompression();
3912     delete pCodec;
3913 #endif
3914 
3915     #if OSL_DEBUG_LEVEL > 1
3916     emitComment( "PDFWriterImpl::createToUnicodeCMap" );
3917     #endif
3918     OStringBuffer aLine( 40 );
3919 
3920     aLine.append( nStream );
3921     aLine.append( " 0 obj\n<</Length " );
3922 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3923     sal_Int32 nLen = (sal_Int32)aStream.Tell();
3924     aStream.Seek( 0 );
3925     aLine.append( nLen );
3926     aLine.append( "/Filter/FlateDecode" );
3927 #else
3928     aLine.append( aContents.getLength() );
3929 #endif
3930     aLine.append( ">>\nstream\n" );
3931     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3932     checkAndEnableStreamEncryption( nStream );
3933 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3934     CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
3935 #else
3936     CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
3937 #endif
3938     disableStreamEncryption();
3939     aLine.setLength( 0 );
3940     aLine.append( "\nendstream\n"
3941                   "endobj\n\n" );
3942     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3943     return nStream;
3944 }
3945 
3946 sal_Int32 PDFWriterImpl::emitFontDescriptor( const ImplFontData* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
3947 {
3948     OStringBuffer aLine( 1024 );
3949     // get font flags, see PDF reference 1.4 p. 358
3950     // possibly characters outside Adobe standard encoding
3951     // so set Symbolic flag
3952     sal_Int32 nFontFlags = (1<<2);
3953     if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE )
3954         nFontFlags |= (1 << 6);
3955     if( pFont->GetPitch() == PITCH_FIXED )
3956         nFontFlags |= 1;
3957     if( pFont->GetFamilyType() == FAMILY_SCRIPT )
3958         nFontFlags |= (1 << 3);
3959     else if( pFont->GetFamilyType() == FAMILY_ROMAN )
3960         nFontFlags |= (1 << 1);
3961 
3962     sal_Int32 nFontDescriptor = createObject();
3963     CHECK_RETURN( updateObject( nFontDescriptor ) );
3964     aLine.setLength( 0 );
3965     aLine.append( nFontDescriptor );
3966     aLine.append( " 0 obj\n"
3967                   "<</Type/FontDescriptor/FontName/" );
3968     appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
3969     aLine.append( "\n"
3970                   "/Flags " );
3971     aLine.append( nFontFlags );
3972     aLine.append( "\n"
3973                   "/FontBBox[" );
3974     // note: Top and Bottom are reversed in VCL and PDF rectangles
3975     aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() );
3976     aLine.append( ' ' );
3977     aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() );
3978     aLine.append( ' ' );
3979     aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() );
3980     aLine.append( ' ' );
3981     aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) );
3982     aLine.append( "]/ItalicAngle " );
3983     if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL )
3984         aLine.append( "-30" );
3985     else
3986         aLine.append( "0" );
3987     aLine.append( "\n"
3988                   "/Ascent " );
3989     aLine.append( (sal_Int32)rInfo.m_nAscent );
3990     aLine.append( "\n"
3991                   "/Descent " );
3992     aLine.append( (sal_Int32)-rInfo.m_nDescent );
3993     aLine.append( "\n"
3994                   "/CapHeight " );
3995     aLine.append( (sal_Int32)rInfo.m_nCapHeight );
3996     // According to PDF reference 1.4 StemV is required
3997     // seems a tad strange to me, but well ...
3998     aLine.append( "\n"
3999                   "/StemV 80\n" );
4000     if( nFontStream )
4001     {
4002         aLine.append( "/FontFile" );
4003         switch( rInfo.m_nFontType )
4004         {
4005             case FontSubsetInfo::SFNT_TTF:
4006                 aLine.append( '2' );
4007                 break;
4008             case FontSubsetInfo::TYPE1_PFA:
4009             case FontSubsetInfo::TYPE1_PFB:
4010             case FontSubsetInfo::ANY_TYPE1:
4011                 break;
4012             default:
4013                 DBG_ERROR( "unknown fonttype in PDF font descriptor" );
4014                 return 0;
4015         }
4016         aLine.append( ' ' );
4017         aLine.append( nFontStream );
4018         aLine.append( " 0 R\n" );
4019     }
4020     aLine.append( ">>\n"
4021                   "endobj\n\n" );
4022     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4023 
4024     return nFontDescriptor;
4025 }
4026 
4027 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const
4028 {
4029     for( std::map< sal_Int32, sal_Int32 >::const_iterator it =
4030          m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it )
4031     {
4032         rDict.append( m_aBuiltinFonts[it->first].getNameObject() );
4033         rDict.append( ' ' );
4034         rDict.append( it->second );
4035         rDict.append( " 0 R" );
4036     }
4037 }
4038 
4039 bool PDFWriterImpl::emitFonts()
4040 {
4041     if( ! m_pReferenceDevice->ImplGetGraphics() )
4042         return false;
4043 
4044     OStringBuffer aLine( 1024 );
4045 
4046     std::map< sal_Int32, sal_Int32 > aFontIDToObject;
4047 
4048     OUString aTmpName;
4049     osl_createTempFile( NULL, NULL, &aTmpName.pData );
4050     for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it )
4051     {
4052         for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit )
4053         {
4054             sal_Int32 pGlyphIDs[ 256 ];
4055             sal_Int32 pWidths[ 256 ];
4056             sal_uInt8 pEncoding[ 256 ];
4057             sal_Int32 pEncToUnicodeIndex[ 256 ];
4058             sal_Int32 pUnicodesPerGlyph[ 256 ];
4059             std::vector<sal_Ucs> aUnicodes;
4060             aUnicodes.reserve( 256 );
4061             int nGlyphs = 1;
4062             // fill arrays and prepare encoding index map
4063             sal_Int32 nToUnicodeStream = 0;
4064 
4065             rtl_zeroMemory( pGlyphIDs, sizeof( pGlyphIDs ) );
4066             rtl_zeroMemory( pEncoding, sizeof( pEncoding ) );
4067             rtl_zeroMemory( pUnicodesPerGlyph, sizeof( pUnicodesPerGlyph ) );
4068             rtl_zeroMemory( pEncToUnicodeIndex, sizeof( pEncToUnicodeIndex ) );
4069             for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit )
4070             {
4071                 sal_uInt8 nEnc = fit->second.getGlyphId();
4072 
4073                 DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" );
4074                 DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" );
4075 
4076                 pGlyphIDs[ nEnc ] = fit->first;
4077                 pEncoding[ nEnc ] = nEnc;
4078                 pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size());
4079                 pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes();
4080                 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ )
4081                     aUnicodes.push_back( fit->second.getCode( n ) );
4082                 if( fit->second.getCode(0) )
4083                     nToUnicodeStream = 1;
4084                 if( nGlyphs < 256 )
4085                     nGlyphs++;
4086                 else
4087                 {
4088                     DBG_ERROR( "too many glyphs for subset" );
4089                 }
4090             }
4091             FontSubsetInfo aSubsetInfo;
4092             if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
4093             {
4094                 // create font stream
4095                 oslFileHandle aFontFile;
4096                 CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) );
4097                 // get file size
4098                 sal_uInt64 nLength1;
4099                 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) );
4100                 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength1 ) ) );
4101                 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
4102 
4103                 #if OSL_DEBUG_LEVEL > 1
4104                 emitComment( "PDFWriterImpl::emitFonts" );
4105                 #endif
4106                 sal_Int32 nFontStream = createObject();
4107                 sal_Int32 nStreamLengthObject = createObject();
4108                 CHECK_RETURN( updateObject( nFontStream ) );
4109                 aLine.setLength( 0 );
4110                 aLine.append( nFontStream );
4111                 aLine.append( " 0 obj\n"
4112                              "<</Length " );
4113                 aLine.append( (sal_Int32)nStreamLengthObject );
4114                 aLine.append( " 0 R"
4115                              #ifndef DEBUG_DISABLE_PDFCOMPRESSION
4116                              "/Filter/FlateDecode"
4117                              #endif
4118                              "/Length1 " );
4119 
4120                 sal_uInt64 nStartPos = 0;
4121                 if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF )
4122                 {
4123                     aLine.append( (sal_Int32)nLength1 );
4124 
4125                     aLine.append( ">>\n"
4126                                  "stream\n" );
4127                     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4128                     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
4129 
4130                     // copy font file
4131                     beginCompression();
4132                     checkAndEnableStreamEncryption( nFontStream );
4133                     sal_Bool bEOF = sal_False;
4134                     do
4135                     {
4136                         char buf[8192];
4137                         sal_uInt64 nRead;
4138                         CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) );
4139                         CHECK_RETURN( writeBuffer( buf, nRead ) );
4140                         CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) );
4141                     } while( ! bEOF );
4142                 }
4143                 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 )
4144                 {
4145                     // TODO: implement
4146                     DBG_ERROR( "PDFWriterImpl does not support CFF-font subsets yet!" );
4147                 }
4148                 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA?
4149                 {
4150                     boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] );
4151 
4152                     sal_uInt64 nBytesRead = 0;
4153                     CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer.get(), nLength1, &nBytesRead ) ) );
4154                     DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" );
4155                     CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
4156                     // get the PFB-segment lengths
4157                     ThreeInts aSegmentLengths = {0,0,0};
4158                     getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths );
4159                     // the lengths below are mandatory for PDF-exported Type1 fonts
4160                     // because the PFB segment headers get stripped! WhyOhWhy.
4161                     aLine.append( (sal_Int32)aSegmentLengths[0] );
4162                     aLine.append( "/Length2 " );
4163                     aLine.append( (sal_Int32)aSegmentLengths[1] );
4164                     aLine.append( "/Length3 " );
4165                     aLine.append( (sal_Int32)aSegmentLengths[2] );
4166 
4167                     aLine.append( ">>\n"
4168                                  "stream\n" );
4169                     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4170                     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
4171 
4172                     // emit PFB-sections without section headers
4173                     beginCompression();
4174                     checkAndEnableStreamEncryption( nFontStream );
4175                     CHECK_RETURN( writeBuffer( &pBuffer[6], aSegmentLengths[0] ) );
4176                     CHECK_RETURN( writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) );
4177                     CHECK_RETURN( writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) );
4178                 }
4179                 else
4180                 {
4181                     fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType);
4182                     aLine.append( "0 >>\nstream\n" );
4183                 }
4184 
4185                 endCompression();
4186                 disableStreamEncryption();
4187                 // close the file
4188                 osl_closeFile( aFontFile );
4189 
4190                 sal_uInt64 nEndPos = 0;
4191                 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) );
4192                 // end the stream
4193                 aLine.setLength( 0 );
4194                 aLine.append( "\nendstream\nendobj\n\n" );
4195                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4196 
4197                 // emit stream length object
4198                 CHECK_RETURN( updateObject( nStreamLengthObject ) );
4199                 aLine.setLength( 0 );
4200                 aLine.append( nStreamLengthObject );
4201                 aLine.append( " 0 obj\n" );
4202                 aLine.append( (sal_Int64)(nEndPos-nStartPos) );
4203                 aLine.append( "\nendobj\n\n" );
4204                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4205 
4206                 // write font descriptor
4207                 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream );
4208 
4209                 if( nToUnicodeStream )
4210                     nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs );
4211 
4212                 sal_Int32 nFontObject = createObject();
4213                 CHECK_RETURN( updateObject( nFontObject ) );
4214                 aLine.setLength( 0 );
4215                 aLine.append( nFontObject );
4216 
4217                 aLine.append( " 0 obj\n" );
4218                 aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ?
4219                              "<</Type/Font/Subtype/Type1/BaseFont/" :
4220                              "<</Type/Font/Subtype/TrueType/BaseFont/" );
4221                 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine );
4222                 aLine.append( "\n"
4223                              "/FirstChar 0\n"
4224                              "/LastChar " );
4225                 aLine.append( (sal_Int32)(nGlyphs-1) );
4226                 aLine.append( "\n"
4227                              "/Widths[" );
4228                 for( int i = 0; i < nGlyphs; i++ )
4229                 {
4230                     aLine.append( pWidths[ i ] );
4231                     aLine.append( ((i & 15) == 15) ? "\n" : " " );
4232                 }
4233                 aLine.append( "]\n"
4234                              "/FontDescriptor " );
4235                 aLine.append( nFontDescriptor );
4236                 aLine.append( " 0 R\n" );
4237                 if( nToUnicodeStream )
4238                 {
4239                     aLine.append( "/ToUnicode " );
4240                     aLine.append( nToUnicodeStream );
4241                     aLine.append( " 0 R\n" );
4242                 }
4243                 aLine.append( ">>\n"
4244                              "endobj\n\n" );
4245                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4246 
4247                 aFontIDToObject[ lit->m_nFontID ] = nFontObject;
4248             }
4249             else
4250             {
4251                 const ImplFontData* pFont = it->first;
4252                 rtl::OStringBuffer aErrorComment( 256 );
4253                 aErrorComment.append( "CreateFontSubset failed for font \"" );
4254                 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
4255                 aErrorComment.append( '\"' );
4256                 if( pFont->GetSlant() == ITALIC_NORMAL )
4257                     aErrorComment.append( " italic" );
4258                 else if( pFont->GetSlant() == ITALIC_OBLIQUE )
4259                     aErrorComment.append( " oblique" );
4260                 aErrorComment.append( " weight=" );
4261                 aErrorComment.append( sal_Int32(pFont->GetWeight()) );
4262                 emitComment( aErrorComment.getStr() );
4263             }
4264         }
4265     }
4266     osl_removeFile( aTmpName.pData );
4267 
4268     // emit embedded fonts
4269     for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit )
4270     {
4271         std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second );
4272         for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4273         {
4274             CHECK_RETURN( fit->second );
4275             aFontIDToObject[ fit->first ] = fit->second;
4276         }
4277     }
4278 
4279     // emit system fonts
4280     for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit )
4281     {
4282         std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second );
4283         for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4284         {
4285             CHECK_RETURN( fit->second );
4286             aFontIDToObject[ fit->first ] = fit->second;
4287         }
4288     }
4289 
4290     OStringBuffer aFontDict( 1024 );
4291     aFontDict.append( getFontDictObject() );
4292     aFontDict.append( " 0 obj\n"
4293                      "<<" );
4294     int ni = 0;
4295     for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit )
4296     {
4297         aFontDict.append( "/F" );
4298         aFontDict.append( mit->first );
4299         aFontDict.append( ' ' );
4300         aFontDict.append( mit->second );
4301         aFontDict.append( " 0 R" );
4302         if( ((++ni) & 7) == 0 )
4303             aFontDict.append( '\n' );
4304     }
4305     // emit builtin font for widget apperances / variable text
4306     for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin();
4307         it != m_aBuiltinFontToObjectMap.end(); ++it )
4308     {
4309         ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]);
4310         it->second = emitBuiltinFont( &aData, it->second );
4311     }
4312     appendBuiltinFontsToDict( aFontDict );
4313     aFontDict.append( "\n>>\nendobj\n\n" );
4314 
4315     CHECK_RETURN( updateObject( getFontDictObject() ) );
4316     CHECK_RETURN( writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) );
4317     return true;
4318 }
4319 
4320 sal_Int32 PDFWriterImpl::emitResources()
4321 {
4322     // emit shadings
4323     if( ! m_aGradients.empty() )
4324         CHECK_RETURN( emitGradients() );
4325     // emit tilings
4326     if( ! m_aTilings.empty() )
4327         CHECK_RETURN( emitTilings() );
4328 
4329     // emit font dict
4330     CHECK_RETURN( emitFonts() );
4331 
4332     // emit Resource dict
4333     OStringBuffer aLine( 512 );
4334     sal_Int32 nResourceDict = getResourceDictObj();
4335     CHECK_RETURN( updateObject( nResourceDict ) );
4336     aLine.setLength( 0 );
4337     aLine.append( nResourceDict );
4338     aLine.append( " 0 obj\n" );
4339     m_aGlobalResourceDict.append( aLine, getFontDictObject() );
4340     aLine.append( "endobj\n\n" );
4341     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4342     return nResourceDict;
4343 }
4344 
4345 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
4346                                                  sal_Int32 nItemLevel,
4347                                                  sal_Int32 nCurrentItemId )
4348 {
4349     /* The /Count number of an item is
4350        positive: the number of visible subitems
4351        negative: the negative number of subitems that will become visible if
4352                  the item gets opened
4353        see PDF ref 1.4 p 478
4354     */
4355 
4356     sal_Int32 nCount = 0;
4357 
4358     if( m_aContext.OpenBookmarkLevels < 0           || // all levels arevisible
4359         m_aContext.OpenBookmarkLevels >= nItemLevel    // this level is visible
4360       )
4361     {
4362         PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4363         sal_Int32 nChildren = rItem.m_aChildren.size();
4364         for( sal_Int32 i = 0; i < nChildren; i++ )
4365             nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4366         rCounts[nCurrentItemId] = nCount;
4367         // return 1 (this item) + visible sub items
4368         if( nCount < 0 )
4369             nCount = 0;
4370         nCount++;
4371     }
4372     else
4373     {
4374         // this bookmark level is invisible
4375         PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4376         sal_Int32 nChildren = rItem.m_aChildren.size();
4377         rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
4378         for( sal_Int32 i = 0; i < nChildren; i++ )
4379             updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4380         nCount = -1;
4381     }
4382 
4383     return nCount;
4384 }
4385 
4386 sal_Int32 PDFWriterImpl::emitOutline()
4387 {
4388     int i, nItems = m_aOutline.size();
4389 
4390     // do we have an outline at all ?
4391     if( nItems < 2 )
4392         return 0;
4393 
4394     // reserve object numbers for all outline items
4395     for( i = 0; i < nItems; ++i )
4396         m_aOutline[i].m_nObject = createObject();
4397 
4398     // update all parent, next and prev object ids
4399     for( i = 0; i < nItems; ++i )
4400     {
4401         PDFOutlineEntry& rItem = m_aOutline[i];
4402         int nChildren = rItem.m_aChildren.size();
4403 
4404         if( nChildren )
4405         {
4406             for( int n = 0; n < nChildren; ++n )
4407             {
4408                 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
4409 
4410                 rChild.m_nParentObject = rItem.m_nObject;
4411                 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
4412                 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
4413             }
4414 
4415         }
4416     }
4417 
4418     // calculate Count entries for all items
4419     std::vector< sal_Int32 > aCounts( nItems );
4420     updateOutlineItemCount( aCounts, 0, 0 );
4421 
4422     // emit hierarchy
4423     for( i = 0; i < nItems; ++i )
4424     {
4425         PDFOutlineEntry& rItem = m_aOutline[i];
4426         OStringBuffer aLine( 1024 );
4427 
4428         CHECK_RETURN( updateObject( rItem.m_nObject ) );
4429         aLine.append( rItem.m_nObject );
4430         aLine.append( " 0 obj\n" );
4431         aLine.append( "<<" );
4432         // number of visible children (all levels)
4433         if( i > 0 || aCounts[0] > 0 )
4434         {
4435             aLine.append( "/Count " );
4436             aLine.append( aCounts[i] );
4437         }
4438         if( ! rItem.m_aChildren.empty() )
4439         {
4440             // children list: First, Last
4441             aLine.append( "/First " );
4442             aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
4443             aLine.append( " 0 R/Last " );
4444             aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
4445             aLine.append( " 0 R\n" );
4446         }
4447         if( i > 0 )
4448         {
4449             // Title, Dest, Parent, Prev, Next
4450             aLine.append( "/Title" );
4451             appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
4452             aLine.append( "\n" );
4453             // Dest is not required
4454             if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() )
4455             {
4456                 aLine.append( "/Dest" );
4457                 appendDest( rItem.m_nDestID, aLine );
4458             }
4459             aLine.append( "/Parent " );
4460             aLine.append( rItem.m_nParentObject );
4461             aLine.append( " 0 R" );
4462             if( rItem.m_nPrevObject )
4463             {
4464                 aLine.append( "/Prev " );
4465                 aLine.append( rItem.m_nPrevObject );
4466                 aLine.append( " 0 R" );
4467             }
4468             if( rItem.m_nNextObject )
4469             {
4470                 aLine.append( "/Next " );
4471                 aLine.append( rItem.m_nNextObject );
4472                 aLine.append( " 0 R" );
4473             }
4474         }
4475         aLine.append( ">>\nendobj\n\n" );
4476         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4477     }
4478 
4479     return m_aOutline[0].m_nObject;
4480 }
4481 
4482 #undef CHECK_RETURN
4483 #define CHECK_RETURN( x ) if( !x ) return false
4484 
4485 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
4486 {
4487     if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() )
4488     {
4489 #if OSL_DEBUG_LEVEL > 1
4490         fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID );
4491 #endif
4492         return false;
4493     }
4494 
4495 
4496     const PDFDest& rDest		= m_aDests[ nDestID ];
4497     const PDFPage& rDestPage	= m_aPages[ rDest.m_nPage ];
4498 
4499     rBuffer.append( '[' );
4500     rBuffer.append( rDestPage.m_nPageObject );
4501     rBuffer.append( " 0 R" );
4502 
4503     switch( rDest.m_eType )
4504     {
4505         case PDFWriter::XYZ:
4506         default:
4507             rBuffer.append( "/XYZ " );
4508             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4509             rBuffer.append( ' ' );
4510             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4511             rBuffer.append( " 0" );
4512             break;
4513         case PDFWriter::Fit:
4514             rBuffer.append( "/Fit" );
4515             break;
4516         case PDFWriter::FitRectangle:
4517             rBuffer.append( "/FitR " );
4518             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4519             rBuffer.append( ' ' );
4520             appendFixedInt( rDest.m_aRect.Top(), rBuffer );
4521             rBuffer.append( ' ' );
4522             appendFixedInt( rDest.m_aRect.Right(), rBuffer );
4523             rBuffer.append( ' ' );
4524             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4525             break;
4526         case PDFWriter::FitHorizontal:
4527             rBuffer.append( "/FitH " );
4528             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4529             break;
4530         case PDFWriter::FitVertical:
4531             rBuffer.append( "/FitV " );
4532             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4533             break;
4534         case PDFWriter::FitPageBoundingBox:
4535             rBuffer.append( "/FitB" );
4536             break;
4537         case PDFWriter::FitPageBoundingBoxHorizontal:
4538             rBuffer.append( "/FitBH " );
4539             appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4540             break;
4541         case PDFWriter::FitPageBoundingBoxVertical:
4542             rBuffer.append( "/FitBV " );
4543             appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4544             break;
4545     }
4546     rBuffer.append( ']' );
4547 
4548     return true;
4549 }
4550 
4551 bool PDFWriterImpl::emitLinkAnnotations()
4552 {
4553     int nAnnots = m_aLinks.size();
4554     for( int i = 0; i < nAnnots; i++ )
4555     {
4556         const PDFLink& rLink			= m_aLinks[i];
4557         if( ! updateObject( rLink.m_nObject ) )
4558             continue;
4559 
4560         OStringBuffer aLine( 1024 );
4561         aLine.append( rLink.m_nObject );
4562         aLine.append( " 0 obj\n" );
4563 //i59651  key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4564 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4565         aLine.append( "<</Type/Annot" );
4566         if( m_bIsPDF_A1 )
4567             aLine.append( "/F 4" );
4568         aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
4569 
4570         appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
4571         aLine.append( ' ' );
4572         appendFixedInt( rLink.m_aRect.Top(), aLine );
4573         aLine.append( ' ' );
4574         appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
4575         aLine.append( ' ' );
4576         appendFixedInt( rLink.m_aRect.Bottom(), aLine );
4577         aLine.append( "]" );
4578         if( rLink.m_nDest >= 0 )
4579         {
4580             aLine.append( "/Dest" );
4581             appendDest( rLink.m_nDest, aLine );
4582         }
4583         else
4584         {
4585 /*--->i56629
4586 destination is external to the document, so
4587 we check in the following sequence:
4588 
4589  if target type is neither .pdf, nor .od[tpgs], then
4590           check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
4591                              end processing
4592  else if target is .od[tpgs]: then
4593       if conversion of type from od[tpgs]  to pdf is requested, convert it and this becomes the new target file
4594       processing continue
4595 
4596  if (new)target is .pdf : then
4597      if GotToR is requested, then
4598            convert the target in GoToR where the fragment of the URI is
4599            considered the named destination in the target file, set relative or absolute as requested
4600      else strip the fragment from URL and then set URI or 'launch application' as requested
4601 */
4602 //
4603 // FIXME: check if the decode mechanisms for URL processing throughout this implementation
4604 // are the correct one!!
4605 //
4606 // extract target file type
4607             INetURLObject aDocumentURL( m_aContext.BaseURL );
4608             INetURLObject aTargetURL( rLink.m_aURL );
4609             sal_Int32   nChangeFileExtensionToPDF = 0;
4610             sal_Int32   nSetGoToRMode = 0;
4611             sal_Bool    bTargetHasPDFExtension = sal_False;
4612             INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
4613 			sal_Bool    bIsUNCPath = sal_False;
4614 // check if the protocol is a known one, or if there is no protocol at all (on target only)
4615 // if there is no protocol, make the target relative to the current document directory
4616 // getting the needed URL information from the current document path
4617             if( eTargetProtocol == INET_PROT_NOT_VALID )
4618             {
4619 				if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.compareToAscii( "\\\\\\\\", 4 ) == 0)
4620 				{
4621 					bIsUNCPath = sal_True;
4622 				}
4623 				else
4624 				{
4625 					INetURLObject aNewBase( aDocumentURL );//duplicate document URL
4626 					aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
4627 											  //target document
4628 					aNewBase.insertName( rLink.m_aURL );
4629 					aTargetURL = aNewBase;//reassign the new target URL
4630 //recompute the target protocol, with the new URL
4631 //normal URL processing resumes
4632 					eTargetProtocol = aTargetURL.GetProtocol();
4633 				}
4634             }
4635 
4636             rtl::OUString aFileExtension = aTargetURL.GetFileExtension();
4637 
4638 // Check if the URL ends in '/': if yes it's a directory,
4639 // it will be forced to a URI link.
4640 // possibly a malformed URI, leave it as it is, force as URI
4641             if( aTargetURL.hasFinalSlash() )
4642                 m_aContext.DefaultLinkAction = PDFWriter::URIAction;
4643 
4644             if( aFileExtension.getLength() > 0 )
4645             {
4646                 if( m_aContext.ConvertOOoTargetToPDFTarget )
4647                 {
4648 //examine the file type (.odm .odt. .odp, odg, ods)
4649                     if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odm" ) ) ) )
4650                         nChangeFileExtensionToPDF++;
4651                     if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odt" ) ) ) )
4652                         nChangeFileExtensionToPDF++;
4653                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odp" ) ) ) )
4654                         nChangeFileExtensionToPDF++;
4655                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odg" ) ) ) )
4656                         nChangeFileExtensionToPDF++;
4657                     else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ods" ) ) ) )
4658                         nChangeFileExtensionToPDF++;
4659                     if( nChangeFileExtensionToPDF )
4660                         aTargetURL.setExtension(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4661                 }
4662 //check if extension is pdf, see if GoToR should be forced
4663                 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) );
4664                 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
4665                     nSetGoToRMode++;
4666             }
4667 //prepare the URL, if relative or not
4668             INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
4669 //queue the string common to all types of actions
4670             aLine.append( "/A<</Type/Action/S");
4671 			if( bIsUNCPath ) // handle Win UNC paths
4672 			{
4673 				aLine.append( "/Launch/Win<</F" );
4674 				// INetURLObject is not good with UNC paths, use original path
4675 				appendLiteralStringEncrypt(  rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4676 				aLine.append( ">>" );
4677 			}
4678 			else
4679 			{
4680 			    bool bSetRelative = false;
4681 			    bool bFileSpec = false;
4682 //check if relative file link is requested and if the protocol is 'file://'
4683 				if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE )
4684 					bSetRelative = true;
4685 
4686 				rtl::OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is,
4687 				if( nSetGoToRMode == 0 )
4688 				{
4689 					switch( m_aContext.DefaultLinkAction )
4690 					{
4691 					default:
4692 					case PDFWriter::URIAction :
4693 					case PDFWriter::URIActionDestination :
4694 						aLine.append( "/URI/URI" );
4695 						break;
4696 					case PDFWriter::LaunchAction:
4697 // now:
4698 // if a launch action is requested and the hyperlink target has a fragment
4699 // and the target file does not have a pdf extension, or it's not a 'file:://' protocol
4700 // then force the uri action on it
4701 // This code will permit the correct opening of application on web pages, the one that
4702 // normally have fragments (but I may be wrong...)
4703 // and will force the use of URI when the protocol is not file://
4704 						if( (aFragment.getLength() > 0 && !bTargetHasPDFExtension) ||
4705 										eTargetProtocol != INET_PROT_FILE )
4706 							aLine.append( "/URI/URI" );
4707 						else
4708 						{
4709 							aLine.append( "/Launch/F" );
4710 							bFileSpec = true;
4711 						}
4712 						break;
4713 					}
4714 				}
4715 //fragment are encoded in the same way as in the named destination processing
4716 				if( nSetGoToRMode )
4717 				{//add the fragment
4718 				    rtl::OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET );
4719 					aLine.append("/GoToR");
4720 					aLine.append("/F");
4721 					bFileSpec = true;
4722 					appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark,
4723 																						 INetURLObject::WAS_ENCODED,
4724 																						 INetURLObject::DECODE_WITH_CHARSET ) :
4725 																   aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4726 					if( aFragment.getLength() > 0 )
4727 					{
4728 						aLine.append("/D/");
4729 						appendDestinationName( aFragment , aLine );
4730 					}
4731 				}
4732 				else
4733 				{
4734 // change the fragment to accomodate the bookmark (only if the file extension is PDF and
4735 // the requested action is of the correct type)
4736 					if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination &&
4737 							   bTargetHasPDFExtension && aFragment.getLength() > 0 )
4738 					{
4739 						OStringBuffer aLineLoc( 1024 );
4740 						appendDestinationName( aFragment , aLineLoc );
4741 //substitute the fragment
4742 						aTargetURL.SetMark( aLineLoc.getStr() );
4743 					}
4744 					rtl::OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE );
4745 // check if we have a URL available, if the string is empty, set it as the original one
4746 //                 if( aURL.getLength() == 0 )
4747 //                     appendLiteralStringEncrypt( rLink.m_aURL , rLink.m_nObject, aLine );
4748 //                 else
4749 						appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL,
4750 						                                                                    INetURLObject::WAS_ENCODED,
4751 						                                                                    bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE
4752 						                                                                    ) :
4753 																   aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4754 				}
4755 //<--- i56629
4756 			}
4757             aLine.append( ">>\n" );
4758         }
4759         if( rLink.m_nStructParent > 0 )
4760         {
4761             aLine.append( "/StructParent " );
4762             aLine.append( rLink.m_nStructParent );
4763         }
4764         aLine.append( ">>\nendobj\n\n" );
4765         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4766     }
4767 
4768     return true;
4769 }
4770 
4771 bool PDFWriterImpl::emitNoteAnnotations()
4772 {
4773     // emit note annotations
4774     int nAnnots = m_aNotes.size();
4775     for( int i = 0; i < nAnnots; i++ )
4776     {
4777         const PDFNoteEntry& rNote		= m_aNotes[i];
4778         if( ! updateObject( rNote.m_nObject ) )
4779             return false;
4780 
4781         OStringBuffer aLine( 1024 );
4782         aLine.append( rNote.m_nObject );
4783         aLine.append( " 0 obj\n" );
4784 //i59651  key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4785 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4786         aLine.append( "<</Type/Annot" );
4787         if( m_bIsPDF_A1 )
4788             aLine.append( "/F 4" );
4789         aLine.append( "/Subtype/Text/Rect[" );
4790 
4791         appendFixedInt( rNote.m_aRect.Left(), aLine );
4792         aLine.append( ' ' );
4793         appendFixedInt( rNote.m_aRect.Top(), aLine );
4794         aLine.append( ' ' );
4795         appendFixedInt( rNote.m_aRect.Right(), aLine );
4796         aLine.append( ' ' );
4797         appendFixedInt( rNote.m_aRect.Bottom(), aLine );
4798         aLine.append( "]" );
4799 
4800         // contents of the note (type text string)
4801         aLine.append( "/Contents\n" );
4802         appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
4803         aLine.append( "\n" );
4804 
4805         // optional title
4806         if( rNote.m_aContents.Title.Len() )
4807         {
4808             aLine.append( "/T" );
4809             appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
4810             aLine.append( "\n" );
4811         }
4812 
4813         aLine.append( ">>\nendobj\n\n" );
4814         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4815     }
4816     return true;
4817 }
4818 
4819 Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font&  rAppSetFont )
4820 {
4821     bool bAdjustSize = false;
4822 
4823     Font aFont( rControlFont );
4824     if( ! aFont.GetName().Len() )
4825     {
4826         aFont = rAppSetFont;
4827         if( rControlFont.GetHeight() )
4828             aFont.SetSize( Size( 0, rControlFont.GetHeight() ) );
4829         else
4830             bAdjustSize = true;
4831         if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
4832             aFont.SetItalic( rControlFont.GetItalic() );
4833         if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
4834             aFont.SetWeight( rControlFont.GetWeight() );
4835     }
4836     else if( ! aFont.GetHeight() )
4837     {
4838         aFont.SetSize( rAppSetFont.GetSize() );
4839         bAdjustSize = true;
4840     }
4841     if( bAdjustSize )
4842     {
4843         Size aFontSize = aFont.GetSize();
4844         OutputDevice* pDefDev = Application::GetDefaultDevice();
4845         aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
4846         aFont.SetSize( aFontSize );
4847     }
4848     return aFont;
4849 }
4850 
4851 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont )
4852 {
4853     sal_Int32 nBest = 4; // default to Helvetica
4854     OUString aFontName( rFont.GetName() );
4855     aFontName = aFontName.toAsciiLowerCase();
4856 
4857     if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "times" ) ) ) != -1 )
4858         nBest = 8;
4859     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "courier" ) ) ) != -1 )
4860         nBest = 0;
4861     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "dingbats" ) ) ) != -1 )
4862         nBest = 13;
4863     else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "symbol" ) ) ) != -1 )
4864         nBest = 12;
4865     if( nBest < 12 )
4866     {
4867         if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
4868             nBest += 1;
4869         if( rFont.GetWeight() > WEIGHT_MEDIUM )
4870             nBest += 2;
4871     }
4872 
4873     if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() )
4874         m_aBuiltinFontToObjectMap[ nBest ] = createObject();
4875 
4876     return nBest;
4877 }
4878 
4879 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
4880 {
4881     return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1;
4882 }
4883 
4884 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget )
4885 {
4886     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4887 
4888     // save graphics state
4889     push( sal::static_int_cast<sal_uInt16>(~0U) );
4890 
4891     // transform relative to control's coordinates since an
4892     // appearance stream is a form XObject
4893     // this relies on the m_aRect member of rButton NOT already being transformed
4894     // to default user space
4895     if( rWidget.Background || rWidget.Border )
4896     {
4897         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) );
4898         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) );
4899         drawRectangle( rWidget.Location );
4900     }
4901     // prepare font to use
4902     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
4903     setFont( aFont );
4904     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
4905 
4906     drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
4907 
4908     // create DA string while local mapmode is still in place
4909     // (that is before endRedirect())
4910     OStringBuffer aDA( 256 );
4911     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
4912     Font aDummyFont( String( RTL_CONSTASCII_USTRINGPARAM( "Helvetica" ) ), aFont.GetSize() );
4913     sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont );
4914     aDA.append( ' ' );
4915     aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() );
4916     aDA.append( ' ' );
4917     m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4918     aDA.append( " Tf" );
4919     rButton.m_aDAString = aDA.makeStringAndClear();
4920 
4921     pop();
4922 
4923     rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
4924 
4925     /* seems like a bad hack but at least works in both AR5 and 6:
4926        we draw the button ourselves and tell AR
4927        the button would be totally transparent with no text
4928 
4929        One would expect that simply setting a normal appearance
4930        should suffice, but no, as soon as the user actually presses
4931        the button and an action is tied to it (gasp! a button that
4932        does something) the appearance gets replaced by some crap that AR
4933        creates on the fly even if no DA or MK is given. On AR6 at least
4934        the DA and MK work as expected, but on AR5 this creates a region
4935        filled with the background color but nor text. Urgh.
4936     */
4937     rButton.m_aMKDict = "/BC [] /BG [] /CA";
4938     rButton.m_aMKDictCAString = "";
4939 }
4940 
4941 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern,
4942                                      const PDFWriter::AnyWidget& rWidget,
4943                                      const StyleSettings& rSettings )
4944 {
4945     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
4946 
4947     if( rWidget.Background || rWidget.Border )
4948     {
4949         if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) )
4950         {
4951             sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500;
4952             if( nDelta < 1 )
4953                 nDelta = 1;
4954             setLineColor( Color( COL_TRANSPARENT ) );
4955             Rectangle aRect = rIntern.m_aRect;
4956             setFillColor( rSettings.GetLightBorderColor() );
4957             drawRectangle( aRect );
4958             aRect.Left()  += nDelta; aRect.Top()     += nDelta;
4959             aRect.Right() -= nDelta; aRect.Bottom()  -= nDelta;
4960             setFillColor( rSettings.GetFieldColor() );
4961             drawRectangle( aRect );
4962             setFillColor( rSettings.GetLightColor() );
4963             drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
4964             drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
4965             setFillColor( rSettings.GetDarkShadowColor() );
4966             drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
4967             drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
4968         }
4969         else
4970         {
4971             setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) );
4972             setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4973             drawRectangle( rIntern.m_aRect );
4974         }
4975 
4976         if( rWidget.Border )
4977         {
4978             // adjust edit area accounting for border
4979             sal_Int32 nDelta = aFont.GetHeight()/4;
4980             if( nDelta < 1 )
4981                 nDelta = 1;
4982             rIntern.m_aRect.Left()	+= nDelta;
4983             rIntern.m_aRect.Top()	+= nDelta;
4984             rIntern.m_aRect.Right()	-= nDelta;
4985             rIntern.m_aRect.Bottom()-= nDelta;
4986         }
4987     }
4988     return aFont;
4989 }
4990 
4991 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget )
4992 {
4993     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4994     SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
4995 
4996     push( sal::static_int_cast<sal_uInt16>(~0U) );
4997 
4998     // prepare font to use, draw field border
4999     Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
5000     sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
5001 
5002     // prepare DA string
5003     OStringBuffer aDA( 32 );
5004     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
5005     aDA.append( ' ' );
5006     if( m_aContext.FieldsUseSystemFonts )
5007     {
5008         aDA.append( "/F" );
5009         aDA.append( nBest );
5010 
5011         OStringBuffer aDR( 32 );
5012         aDR.append( "/Font " );
5013         aDR.append( getFontDictObject() );
5014         aDR.append( " 0 R" );
5015         rEdit.m_aDRDict = aDR.makeStringAndClear();
5016     }
5017     else
5018         aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5019     aDA.append( ' ' );
5020     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
5021     aDA.append( " Tf" );
5022 
5023     /*  create an empty appearance stream, let the viewer create
5024         the appearance at runtime. This is because AR5 seems to
5025         paint the widget appearance always, and a dynamically created
5026         appearance on top of it. AR6 is well behaved in that regard, so
5027         that behaviour seems to be a bug. Anyway this empty appearance
5028         relies on /NeedAppearances in the AcroForm dictionary set to "true"
5029      */
5030     beginRedirect( pEditStream, rEdit.m_aRect );
5031     OStringBuffer aAppearance( 32 );
5032     aAppearance.append( "/Tx BMC\nEMC\n" );
5033     writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5034 
5035     endRedirect();
5036     pop();
5037 
5038     rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
5039 
5040     rEdit.m_aDAString = aDA.makeStringAndClear();
5041 }
5042 
5043 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget )
5044 {
5045     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5046     SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
5047 
5048     push( sal::static_int_cast<sal_uInt16>(~0U) );
5049 
5050     // prepare font to use, draw field border
5051     Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
5052     sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
5053 
5054     beginRedirect( pListBoxStream, rBox.m_aRect );
5055     OStringBuffer aAppearance( 64 );
5056 
5057 #if 0
5058     if( ! rWidget.DropDown )
5059     {
5060         // prepare linewidth for DA string hack, see below
5061         Size aFontSize = lcl_convert( m_aGraphicsStack.front().m_aMapMode,
5062                                       m_aMapMode,
5063                                       getReferenceDevice(),
5064                                       Size( 0, aFont.GetHeight() ) );
5065         sal_Int32 nLW = aFontSize.Height() / 40;
5066         appendFixedInt( nLW > 0 ? nLW : 1, aAppearance );
5067         aAppearance.append( " w\n" );
5068         writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5069         aAppearance.setLength( 0 );
5070     }
5071 #endif
5072 
5073     setLineColor( Color( COL_TRANSPARENT ) );
5074     setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
5075     drawRectangle( rBox.m_aRect );
5076 
5077     // empty appearance, see createDefaultEditAppearance for reference
5078     aAppearance.append( "/Tx BMC\nEMC\n" );
5079     writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5080 
5081     endRedirect();
5082     pop();
5083 
5084     rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
5085 
5086     // prepare DA string
5087     OStringBuffer aDA( 256 );
5088 #if 0
5089     if( !rWidget.DropDown )
5090     {
5091         /* another of AR5's peculiarities: the selected item of a choice
5092            field is highlighted using the non stroking color - same as the
5093            text color. so workaround that by using text rendering mode 2
5094            (fill, then stroke) and set the stroking color
5095          */
5096         appendStrokingColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ), aDA );
5097         aDA.append( " 2 Tr " );
5098     }
5099 #endif
5100     // prepare DA string
5101     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
5102     aDA.append( ' ' );
5103     if( m_aContext.FieldsUseSystemFonts )
5104     {
5105         aDA.append( "/F" );
5106         aDA.append( nBest );
5107 
5108         OStringBuffer aDR( 32 );
5109         aDR.append( "/Font " );
5110         aDR.append( getFontDictObject() );
5111         aDR.append( " 0 R" );
5112         rBox.m_aDRDict = aDR.makeStringAndClear();
5113     }
5114     else
5115         aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5116     aDA.append( ' ' );
5117     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
5118     aDA.append( " Tf" );
5119     rBox.m_aDAString = aDA.makeStringAndClear();
5120 }
5121 
5122 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget )
5123 {
5124     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5125 
5126     // save graphics state
5127     push( sal::static_int_cast<sal_uInt16>(~0U) );
5128 
5129     if( rWidget.Background || rWidget.Border )
5130     {
5131         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5132         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5133         drawRectangle( rBox.m_aRect );
5134     }
5135 
5136     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5137     setFont( aFont );
5138     Size aFontSize = aFont.GetSize();
5139     if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5140         aFontSize.Height() = rBox.m_aRect.GetHeight();
5141     sal_Int32 nDelta = aFontSize.Height()/10;
5142     if( nDelta < 1 )
5143         nDelta = 1;
5144 
5145     Rectangle aCheckRect, aTextRect;
5146     if( rWidget.ButtonIsLeft )
5147     {
5148         aCheckRect.Left()	= rBox.m_aRect.Left() + nDelta;
5149         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5150         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5151         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5152 
5153         // #i74206# handle small controls without text area
5154         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5155         {
5156             aCheckRect.Right()  -= nDelta;
5157             aCheckRect.Top()    += nDelta/2;
5158             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5159         }
5160 
5161         aTextRect.Left()	= rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5162         aTextRect.Top()		= rBox.m_aRect.Top();
5163         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5164         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5165     }
5166     else
5167     {
5168         aCheckRect.Left()	= rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5169         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5170         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5171         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5172 
5173         // #i74206# handle small controls without text area
5174         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5175         {
5176             aCheckRect.Left()   += nDelta;
5177             aCheckRect.Top()    += nDelta/2;
5178             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5179         }
5180 
5181         aTextRect.Left()	= rBox.m_aRect.Left();
5182         aTextRect.Top()		= rBox.m_aRect.Top();
5183         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5184         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5185     }
5186     setLineColor( Color( COL_BLACK ) );
5187     setFillColor( Color( COL_TRANSPARENT ) );
5188     OStringBuffer aLW( 32 );
5189     aLW.append( "q " );
5190     m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
5191     aLW.append( " w " );
5192     writeBuffer( aLW.getStr(), aLW.getLength() );
5193     drawRectangle( aCheckRect );
5194     writeBuffer( " Q\n", 3 );
5195     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5196     drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5197 
5198     pop();
5199 
5200     OStringBuffer aDA( 256 );
5201     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5202     sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
5203     aDA.append( ' ' );
5204     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5205     aDA.append( " 0 Tf" );
5206     rBox.m_aDAString = aDA.makeStringAndClear();
5207     rBox.m_aMKDict = "/CA";
5208     rBox.m_aMKDictCAString = "8";
5209     rBox.m_aRect = aCheckRect;
5210 
5211     // create appearance streams
5212     sal_Char cMark = '8';
5213     sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)];
5214     nCharXOffset *= aCheckRect.GetHeight();
5215     nCharXOffset /= 2000;
5216     sal_Int32 nCharYOffset = 1000-
5217         (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative
5218     nCharYOffset *= aCheckRect.GetHeight();
5219     nCharYOffset /= 2000;
5220 
5221     SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5222     beginRedirect( pCheckStream, aCheckRect );
5223     aDA.append( "/Tx BMC\nq BT\n" );
5224     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5225     aDA.append( ' ' );
5226     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5227     aDA.append( ' ' );
5228     m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5229     aDA.append( " Tf\n" );
5230     m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
5231 	aDA.append( " " );
5232 	m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
5233 	aDA.append( " Td (" );
5234 	aDA.append( cMark );
5235 	aDA.append( ") Tj\nET\nQ\nEMC\n" );
5236     writeBuffer( aDA.getStr(), aDA.getLength() );
5237     endRedirect();
5238     rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5239 
5240     SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5241     beginRedirect( pUncheckStream, aCheckRect );
5242     writeBuffer( "/Tx BMC\nEMC\n", 12 );
5243     endRedirect();
5244     rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5245 }
5246 
5247 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget )
5248 {
5249     const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5250 
5251     // save graphics state
5252     push( sal::static_int_cast<sal_uInt16>(~0U) );
5253 
5254     if( rWidget.Background || rWidget.Border )
5255     {
5256         setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5257         setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5258         drawRectangle( rBox.m_aRect );
5259     }
5260 
5261     Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5262     setFont( aFont );
5263     Size aFontSize = aFont.GetSize();
5264     if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5265         aFontSize.Height() = rBox.m_aRect.GetHeight();
5266     sal_Int32 nDelta = aFontSize.Height()/10;
5267     if( nDelta < 1 )
5268         nDelta = 1;
5269 
5270     Rectangle aCheckRect, aTextRect;
5271     if( rWidget.ButtonIsLeft )
5272     {
5273         aCheckRect.Left()	= rBox.m_aRect.Left() + nDelta;
5274         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5275         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5276         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5277 
5278         // #i74206# handle small controls without text area
5279         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5280         {
5281             aCheckRect.Right()  -= nDelta;
5282             aCheckRect.Top()    += nDelta/2;
5283             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5284         }
5285 
5286         aTextRect.Left()	= rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5287         aTextRect.Top()		= rBox.m_aRect.Top();
5288         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5289         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5290     }
5291     else
5292     {
5293         aCheckRect.Left()	= rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5294         aCheckRect.Top()	= rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5295         aCheckRect.Right()	= aCheckRect.Left() + aFontSize.Height();
5296         aCheckRect.Bottom()	= aCheckRect.Top() + aFontSize.Height();
5297 
5298         // #i74206# handle small controls without text area
5299         while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5300         {
5301             aCheckRect.Left()   += nDelta;
5302             aCheckRect.Top()    += nDelta/2;
5303             aCheckRect.Bottom() -= nDelta - (nDelta/2);
5304         }
5305 
5306         aTextRect.Left()	= rBox.m_aRect.Left();
5307         aTextRect.Top()		= rBox.m_aRect.Top();
5308         aTextRect.Right()	= aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5309         aTextRect.Bottom()	= rBox.m_aRect.Bottom();
5310     }
5311     setLineColor( Color( COL_BLACK ) );
5312     setFillColor( Color( COL_TRANSPARENT ) );
5313     OStringBuffer aLW( 32 );
5314     aLW.append( "q " );
5315     m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
5316     aLW.append( " w " );
5317     writeBuffer( aLW.getStr(), aLW.getLength() );
5318     drawEllipse( aCheckRect );
5319     writeBuffer( " Q\n", 3 );
5320     setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5321     drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5322 
5323     pop();
5324 
5325     OStringBuffer aDA( 256 );
5326     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5327     sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
5328     aDA.append( ' ' );
5329     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5330     aDA.append( " 0 Tf" );
5331     rBox.m_aDAString = aDA.makeStringAndClear();
5332 //to encrypt this (el)
5333     rBox.m_aMKDict = "/CA";
5334 //after this assignement, to m_aMKDic cannot be added anything
5335     rBox.m_aMKDictCAString = "l";
5336 
5337     rBox.m_aRect = aCheckRect;
5338 
5339     // create appearance streams
5340     push( sal::static_int_cast<sal_uInt16>(~0U) );
5341     SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5342 
5343     beginRedirect( pCheckStream, aCheckRect );
5344     aDA.append( "/Tx BMC\nq BT\n" );
5345     appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5346     aDA.append( ' ' );
5347     aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5348     aDA.append( ' ' );
5349     m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5350     aDA.append( " Tf\n0 0 Td\nET\nQ\n" );
5351     writeBuffer( aDA.getStr(), aDA.getLength() );
5352     setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5353     setLineColor( Color( COL_TRANSPARENT ) );
5354     aCheckRect.Left()	+= 3*nDelta;
5355     aCheckRect.Top()	+= 3*nDelta;
5356     aCheckRect.Bottom()	-= 3*nDelta;
5357     aCheckRect.Right()	-= 3*nDelta;
5358     drawEllipse( aCheckRect );
5359     writeBuffer( "\nEMC\n", 5 );
5360     endRedirect();
5361 
5362     pop();
5363     rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5364 
5365     SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5366     beginRedirect( pUncheckStream, aCheckRect );
5367     writeBuffer( "/Tx BMC\nEMC\n", 12 );
5368     endRedirect();
5369     rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5370 }
5371 
5372 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
5373 {
5374 
5375     // TODO: check and insert default streams
5376     rtl::OString aStandardAppearance;
5377     switch( rWidget.m_eType )
5378     {
5379         case PDFWriter::CheckBox:
5380             aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
5381             break;
5382         default:
5383             break;
5384     }
5385 
5386     if( rWidget.m_aAppearances.size() )
5387     {
5388         rAnnotDict.append( "/AP<<\n" );
5389         for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it )
5390         {
5391             rAnnotDict.append( "/" );
5392             rAnnotDict.append( dict_it->first );
5393             bool bUseSubDict = (dict_it->second.size() > 1);
5394             rAnnotDict.append( bUseSubDict ? "<<" : " " );
5395 
5396             for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin();
5397                  stream_it != dict_it->second.end(); ++stream_it )
5398             {
5399                 SvMemoryStream* pApppearanceStream = stream_it->second;
5400                 dict_it->second[ stream_it->first ] = NULL;
5401 
5402                 bool bDeflate = compressStream( pApppearanceStream );
5403 
5404                 pApppearanceStream->Seek( STREAM_SEEK_TO_END );
5405                 sal_Int64 nStreamLen = pApppearanceStream->Tell();
5406                 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
5407                 sal_Int32 nObject = createObject();
5408                 CHECK_RETURN( updateObject( nObject ) );
5409                 #if OSL_DEBUG_LEVEL > 1
5410                 emitComment( "PDFWriterImpl::emitAppearances" );
5411                 #endif
5412                 OStringBuffer aLine;
5413                 aLine.append( nObject );
5414 
5415                 aLine.append( " 0 obj\n"
5416                               "<</Type/XObject\n"
5417                               "/Subtype/Form\n"
5418                               "/BBox[0 0 " );
5419                 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
5420                 aLine.append( " " );
5421                 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
5422                 aLine.append( "]\n"
5423                               "/Resources " );
5424                 aLine.append( getResourceDictObj() );
5425                 aLine.append( " 0 R\n"
5426                               "/Length " );
5427                 aLine.append( nStreamLen );
5428                 aLine.append( "\n" );
5429                 if( bDeflate )
5430                     aLine.append( "/Filter/FlateDecode\n" );
5431                 aLine.append( ">>\nstream\n" );
5432                 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5433                 checkAndEnableStreamEncryption( nObject );
5434                 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
5435                 disableStreamEncryption();
5436                 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
5437 
5438                 if( bUseSubDict )
5439                 {
5440                     rAnnotDict.append( " /" );
5441                     rAnnotDict.append( stream_it->first );
5442                     rAnnotDict.append( " " );
5443                 }
5444                 rAnnotDict.append( nObject );
5445                 rAnnotDict.append( " 0 R" );
5446 
5447                 delete pApppearanceStream;
5448             }
5449 
5450             rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
5451         }
5452         rAnnotDict.append( ">>\n" );
5453         if( aStandardAppearance.getLength() )
5454         {
5455             rAnnotDict.append( "/AS /" );
5456             rAnnotDict.append( aStandardAppearance );
5457             rAnnotDict.append( "\n" );
5458         }
5459     }
5460 
5461     return true;
5462 }
5463 
5464 bool PDFWriterImpl::emitWidgetAnnotations()
5465 {
5466     ensureUniqueRadioOnValues();
5467 
5468     int nAnnots = m_aWidgets.size();
5469     for( int a = 0; a < nAnnots; a++ )
5470     {
5471         PDFWidget& rWidget = m_aWidgets[a];
5472 
5473         OStringBuffer aLine( 1024 );
5474         OStringBuffer aValue( 256 );
5475         aLine.append( rWidget.m_nObject );
5476         aLine.append( " 0 obj\n"
5477                       "<<" );
5478         if( rWidget.m_eType != PDFWriter::Hierarchy )
5479         {
5480             // emit widget annotation only for terminal fields
5481             if( rWidget.m_aKids.empty() )
5482             {
5483                 aLine.append( "/Type/Annot/Subtype/Widget/F 4\n"
5484                               "/Rect[" );
5485                 appendFixedInt( rWidget.m_aRect.Left()-1, aLine );
5486                 aLine.append( ' ' );
5487                 appendFixedInt( rWidget.m_aRect.Top()+1, aLine );
5488                 aLine.append( ' ' );
5489                 appendFixedInt( rWidget.m_aRect.Right()+1, aLine );
5490                 aLine.append( ' ' );
5491                 appendFixedInt( rWidget.m_aRect.Bottom()-1, aLine );
5492                 aLine.append( "]\n" );
5493             }
5494             aLine.append( "/FT/" );
5495             switch( rWidget.m_eType )
5496             {
5497                 case PDFWriter::RadioButton:
5498                 case PDFWriter::CheckBox:
5499                     // for radio buttons only the RadioButton field, not the
5500                     // CheckBox children should have a value, else acrobat reader
5501                     // does not always check the right button
5502                     // of course real check boxes (not belonging to a readio group)
5503                     // need their values, too
5504                     if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
5505                     {
5506                         aValue.append( "/" );
5507                         // check for radio group with all buttons unpressed
5508                         if( rWidget.m_aValue.getLength() == 0 )
5509                             aValue.append( "Off" );
5510                         else
5511                             appendName( rWidget.m_aValue, aValue );
5512                     }
5513                 case PDFWriter::PushButton:
5514                     aLine.append( "Btn" );
5515                     break;
5516                 case PDFWriter::ListBox:
5517                     if( rWidget.m_nFlags & 0x200000 ) // multiselect
5518                     {
5519                         aValue.append( "[" );
5520                         for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
5521                         {
5522                             sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
5523                             if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
5524                                 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
5525                         }
5526                         aValue.append( "]" );
5527                     }
5528                     else if( rWidget.m_aSelectedEntries.size() > 0 &&
5529                              rWidget.m_aSelectedEntries[0] >= 0 &&
5530                              rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
5531                     {
5532                         appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
5533                     }
5534                     else
5535                         appendUnicodeTextStringEncrypt( rtl::OUString(), rWidget.m_nObject, aValue );
5536                     aLine.append( "Ch" );
5537                     break;
5538                 case PDFWriter::ComboBox:
5539                     appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5540                     aLine.append( "Ch" );
5541                     break;
5542                 case PDFWriter::Edit:
5543                     aLine.append( "Tx" );
5544                     appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5545                     break;
5546                 case PDFWriter::Hierarchy: // make the compiler happy
5547                     break;
5548             }
5549             aLine.append( "\n" );
5550             aLine.append( "/P " );
5551             aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
5552             aLine.append( " 0 R\n" );
5553         }
5554         if( rWidget.m_nParent )
5555         {
5556             aLine.append( "/Parent " );
5557             aLine.append( rWidget.m_nParent );
5558             aLine.append( " 0 R\n" );
5559         }
5560         if( rWidget.m_aKids.size() )
5561         {
5562             aLine.append( "/Kids[" );
5563             for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ )
5564             {
5565                 aLine.append( rWidget.m_aKids[i] );
5566                 aLine.append( " 0 R" );
5567                 aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5568             }
5569             aLine.append( "]\n" );
5570         }
5571         if( rWidget.m_aName.getLength() )
5572         {
5573             aLine.append( "/T" );
5574             appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
5575             aLine.append( "\n" );
5576         }
5577         if( m_aContext.Version > PDFWriter::PDF_1_2 && rWidget.m_aDescription.getLength() )
5578         {
5579             // the alternate field name should be unicode able since it is
5580             // supposed to be used in UI
5581             aLine.append( "/TU" );
5582             appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
5583             aLine.append( "\n" );
5584         }
5585 
5586         if( rWidget.m_nFlags )
5587         {
5588             aLine.append( "/Ff " );
5589             aLine.append( rWidget.m_nFlags );
5590             aLine.append( "\n" );
5591         }
5592         if( aValue.getLength() )
5593         {
5594             OString aVal = aValue.makeStringAndClear();
5595             aLine.append( "/V " );
5596             aLine.append( aVal );
5597             aLine.append( "\n"
5598                           "/DV " );
5599             aLine.append( aVal );
5600             aLine.append( "\n" );
5601         }
5602         if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
5603         {
5604             sal_Int32 nTI = -1;
5605             aLine.append( "/Opt[\n" );
5606             sal_Int32 i = 0;
5607             for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i )
5608             {
5609                 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine );
5610                 aLine.append( "\n" );
5611                 if( *it == rWidget.m_aValue )
5612                     nTI = i;
5613             }
5614             aLine.append( "]\n" );
5615             if( nTI > 0 )
5616             {
5617                 aLine.append( "/TI " );
5618                 aLine.append( nTI );
5619                 aLine.append( "\n" );
5620                 if( rWidget.m_nFlags & 0x200000 ) // Multiselect
5621                 {
5622                     aLine.append( "/I [" );
5623                     aLine.append( nTI );
5624                     aLine.append( "]\n" );
5625                 }
5626             }
5627         }
5628         if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
5629         {
5630             aLine.append( "/MaxLen " );
5631             aLine.append( rWidget.m_nMaxLen );
5632             aLine.append( "\n" );
5633         }
5634         if( rWidget.m_eType == PDFWriter::PushButton )
5635         {
5636             if(!m_bIsPDF_A1)
5637             {
5638                 OStringBuffer aDest;
5639                 if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) )
5640                 {
5641                     aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
5642                     aLine.append( aDest.makeStringAndClear() );
5643                     aLine.append( ">>>>\n" );
5644                 }
5645                 else if( rWidget.m_aListEntries.empty() )
5646                 {
5647                     // create a reset form action
5648                     aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
5649                 }
5650                 else if( rWidget.m_bSubmit )
5651                 {
5652                     // create a submit form action
5653                     aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
5654                     appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() );
5655                     aLine.append( "/Flags " );
5656 
5657                     sal_Int32 nFlags = 0;
5658                     switch( m_aContext.SubmitFormat )
5659                     {
5660                     case PDFWriter::HTML:
5661                         nFlags |= 4;
5662                         break;
5663                     case PDFWriter::XML:
5664                         if( m_aContext.Version > PDFWriter::PDF_1_3 )
5665                             nFlags |= 32;
5666                         break;
5667                     case PDFWriter::PDF:
5668                         if( m_aContext.Version > PDFWriter::PDF_1_3 )
5669                             nFlags |= 256;
5670                         break;
5671                     case PDFWriter::FDF:
5672                     default:
5673                         break;
5674                     }
5675                     if( rWidget.m_bSubmitGet )
5676                         nFlags |= 8;
5677                     aLine.append( nFlags );
5678                     aLine.append( ">>>>\n" );
5679                 }
5680                 else
5681                 {
5682                     // create a URI action
5683                     aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
5684                     aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
5685                     aLine.append( ")>>>>\n" );
5686                 }
5687             }
5688             else
5689                 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA );
5690         }
5691         if( rWidget.m_aDAString.getLength() )
5692         {
5693             if( rWidget.m_aDRDict.getLength() )
5694             {
5695                 aLine.append( "/DR<<" );
5696                 aLine.append( rWidget.m_aDRDict );
5697                 aLine.append( ">>\n" );
5698             }
5699             else
5700             {
5701                 aLine.append( "/DR<</Font<<" );
5702                 appendBuiltinFontsToDict( aLine );
5703                 aLine.append( ">>>>\n" );
5704             }
5705             aLine.append( "/DA" );
5706             appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
5707             aLine.append( "\n" );
5708             if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER )
5709                 aLine.append( "/Q 1\n" );
5710             else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT )
5711                 aLine.append( "/Q 2\n" );
5712         }
5713         // appearance charactristics for terminal fields
5714         // which are supposed to have an appearance constructed
5715         // by the viewer application
5716         if( rWidget.m_aMKDict.getLength() )
5717         {
5718             aLine.append( "/MK<<" );
5719             aLine.append( rWidget.m_aMKDict );
5720 //add the CA string, encrypting it
5721             appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
5722             aLine.append( ">>\n" );
5723         }
5724 
5725         CHECK_RETURN( emitAppearances( rWidget, aLine ) );
5726 
5727         aLine.append( ">>\n"
5728                       "endobj\n\n" );
5729         CHECK_RETURN( updateObject( rWidget.m_nObject ) );
5730         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5731     }
5732     return true;
5733 }
5734 
5735 bool PDFWriterImpl::emitAnnotations()
5736 {
5737     if( m_aPages.size() < 1 )
5738         return false;
5739 
5740     CHECK_RETURN( emitLinkAnnotations() );
5741 
5742     CHECK_RETURN( emitNoteAnnotations() );
5743 
5744     CHECK_RETURN( emitWidgetAnnotations() );
5745 
5746     return true;
5747 }
5748 
5749 #undef CHECK_RETURN
5750 #define CHECK_RETURN( x ) if( !x ) return false
5751 
5752 bool PDFWriterImpl::emitCatalog()
5753 {
5754     // build page tree
5755     // currently there is only one node that contains all leaves
5756 
5757     // first create a page tree node id
5758     sal_Int32 nTreeNode = createObject();
5759 
5760     // emit global resource dictionary (page emit needs it)
5761     CHECK_RETURN( emitResources() );
5762 
5763     // emit all pages
5764     for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it )
5765         if( ! it->emit( nTreeNode ) )
5766             return false;
5767 
5768     sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
5769 
5770     sal_Int32 nOutlineDict = emitOutline();
5771 
5772     //emit Output intent i59651
5773     sal_Int32 nOutputIntentObject = emitOutputIntent();
5774 
5775     //emit metadata
5776     sal_Int32 nMetadataObject = emitDocumentMetadata();
5777 
5778     sal_Int32 nStructureDict = 0;
5779     if(m_aStructure.size() > 1)
5780     {
5781 ///check if dummy structure containers are needed
5782         addInternalStructureContainer(m_aStructure[0]);
5783         nStructureDict = m_aStructure[0].m_nObject = createObject();
5784         emitStructure( m_aStructure[ 0 ] );
5785     }
5786 
5787     // adjust tree node file offset
5788     if( ! updateObject( nTreeNode ) )
5789         return false;
5790 
5791     // emit tree node
5792     OStringBuffer aLine( 2048 );
5793     aLine.append( nTreeNode );
5794     aLine.append( " 0 obj\n" );
5795     aLine.append( "<</Type/Pages\n" );
5796     aLine.append( "/Resources " );
5797     aLine.append( getResourceDictObj() );
5798     aLine.append( " 0 R\n" );
5799 
5800     switch( m_eInheritedOrientation )
5801     {
5802         case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
5803         case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
5804 
5805         case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant
5806         case PDFWriter::Portrait:
5807         default:
5808             break;
5809     }
5810     sal_Int32 nMediaBoxWidth = 0;
5811     sal_Int32 nMediaBoxHeight = 0;
5812     if( m_aPages.empty() ) // sanity check, this should not happen
5813     {
5814         nMediaBoxWidth = m_nInheritedPageWidth;
5815         nMediaBoxHeight = m_nInheritedPageHeight;
5816     }
5817     else
5818     {
5819         for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter )
5820         {
5821             if( iter->m_nPageWidth > nMediaBoxWidth )
5822                 nMediaBoxWidth = iter->m_nPageWidth;
5823             if( iter->m_nPageHeight > nMediaBoxHeight )
5824                 nMediaBoxHeight = iter->m_nPageHeight;
5825         }
5826     }
5827     aLine.append( "/MediaBox[ 0 0 " );
5828     aLine.append( nMediaBoxWidth );
5829     aLine.append( ' ' );
5830     aLine.append( nMediaBoxHeight );
5831     aLine.append( " ]\n"
5832                   "/Kids[ " );
5833     unsigned int i = 0;
5834     for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ )
5835     {
5836         aLine.append( iter->m_nPageObject );
5837         aLine.append( " 0 R" );
5838         aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5839     }
5840     aLine.append( "]\n"
5841                   "/Count " );
5842     aLine.append( (sal_Int32)m_aPages.size() );
5843     aLine.append( ">>\n"
5844                   "endobj\n\n" );
5845     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5846 
5847     // emit annotation objects
5848     CHECK_RETURN( emitAnnotations() );
5849 
5850     // emit Catalog
5851     m_nCatalogObject = createObject();
5852     if( ! updateObject( m_nCatalogObject ) )
5853         return false;
5854     aLine.setLength( 0 );
5855     aLine.append( m_nCatalogObject );
5856     aLine.append( " 0 obj\n"
5857                   "<</Type/Catalog/Pages " );
5858     aLine.append( nTreeNode );
5859     aLine.append( " 0 R\n" );
5860 //--->i56629
5861 //check if there are named destinations to emit (root must be inside the catalog)
5862     if( nNamedDestinationsDictionary )
5863     {
5864         aLine.append("/Dests ");
5865         aLine.append( nNamedDestinationsDictionary );
5866         aLine.append( " 0 R\n" );
5867     }
5868 //<----
5869     if( m_aContext.PageLayout != PDFWriter::DefaultLayout )
5870         switch(  m_aContext.PageLayout )
5871         {
5872         default :
5873         case  PDFWriter::SinglePage :
5874             aLine.append( "/PageLayout/SinglePage\n" );
5875             break;
5876         case  PDFWriter::Continuous :
5877             aLine.append( "/PageLayout/OneColumn\n" );
5878             break;
5879         case  PDFWriter::ContinuousFacing :
5880 //the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5881             aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5882             break;
5883         }
5884     if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode )
5885         switch(  m_aContext.PDFDocumentMode )
5886         {
5887         default :
5888             aLine.append( "/PageMode/UseNone\n" );
5889             break;
5890         case PDFWriter::UseOutlines :
5891             aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5892             break;
5893         case PDFWriter::UseThumbs :
5894             aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5895             break;
5896         }
5897     else if( m_aContext.OpenInFullScreenMode )
5898         aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5899 
5900     OStringBuffer aInitPageRef;
5901     if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() )
5902     {
5903         aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5904         aInitPageRef.append( " 0 R" );
5905     }
5906     else
5907         aInitPageRef.append( "0" );
5908     switch( m_aContext.PDFDocumentAction )
5909     {
5910     case PDFWriter::ActionDefault :     //do nothing, this is the Acrobat default
5911     default:
5912         if( aInitPageRef.getLength() > 1 )
5913         {
5914             aLine.append( "/OpenAction[" );
5915             aLine.append( aInitPageRef );
5916             aLine.append( " /XYZ null null 0]\n" );
5917         }
5918         break;
5919     case PDFWriter::FitInWindow :
5920         aLine.append( "/OpenAction[" );
5921         aLine.append( aInitPageRef );
5922         aLine.append( " /Fit]\n" ); //Open fit page
5923         break;
5924     case PDFWriter::FitWidth :
5925         aLine.append( "/OpenAction[" );
5926         aLine.append( aInitPageRef );
5927         aLine.append( " /FitH " );
5928         aLine.append( m_nInheritedPageHeight );//Open fit width
5929         aLine.append( "]\n" );
5930         break;
5931     case PDFWriter::FitVisible :
5932         aLine.append( "/OpenAction[" );
5933         aLine.append( aInitPageRef );
5934         aLine.append( " /FitBH " );
5935         aLine.append( m_nInheritedPageHeight );//Open fit visible
5936         aLine.append( "]\n" );
5937         break;
5938     case PDFWriter::ActionZoom :
5939         aLine.append( "/OpenAction[" );
5940         aLine.append( aInitPageRef );
5941         aLine.append( " /XYZ null null " );
5942         if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5943             aLine.append( (double)m_aContext.Zoom/100.0 );
5944         else
5945             aLine.append( "0" );
5946         aLine.append( "]\n" );
5947         break;
5948     }
5949 // viewer preferences, if we had some, then emit
5950     if( m_aContext.HideViewerToolbar ||
5951         ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) ||
5952         m_aContext.HideViewerMenubar ||
5953         m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
5954         m_aContext.CenterWindow || (m_aContext.FirstPageLeft  &&  m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
5955         m_aContext.OpenInFullScreenMode )
5956     {
5957         aLine.append( "/ViewerPreferences<<" );
5958         if( m_aContext.HideViewerToolbar )
5959             aLine.append( "/HideToolbar true\n" );
5960         if( m_aContext.HideViewerMenubar )
5961             aLine.append( "/HideMenubar true\n" );
5962         if( m_aContext.HideViewerWindowControls )
5963             aLine.append( "/HideWindowUI true\n" );
5964         if( m_aContext.FitWindow )
5965             aLine.append( "/FitWindow true\n" );
5966         if( m_aContext.CenterWindow )
5967             aLine.append( "/CenterWindow true\n" );
5968         if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle )
5969             aLine.append( "/DisplayDocTitle true\n" );
5970         if( m_aContext.FirstPageLeft &&  m_aContext.PageLayout == PDFWriter::ContinuousFacing )
5971             aLine.append( "/Direction/R2L\n" );
5972         if( m_aContext.OpenInFullScreenMode )
5973             switch( m_aContext.PDFDocumentMode )
5974             {
5975             default :
5976             case PDFWriter::ModeDefault :
5977                 aLine.append( "/NonFullScreenPageMode/UseNone\n" );
5978                 break;
5979             case PDFWriter::UseOutlines :
5980                 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
5981                 break;
5982             case PDFWriter::UseThumbs :
5983                 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
5984                 break;
5985             }
5986         aLine.append( ">>\n" );
5987     }
5988 
5989     if( nOutlineDict )
5990     {
5991         aLine.append( "/Outlines " );
5992         aLine.append( nOutlineDict );
5993         aLine.append( " 0 R\n" );
5994     }
5995     if( nStructureDict )
5996     {
5997         aLine.append( "/StructTreeRoot " );
5998         aLine.append( nStructureDict );
5999         aLine.append( " 0 R\n" );
6000     }
6001     if( m_aContext.DocumentLocale.Language.getLength() > 0 )
6002     {
6003         OUStringBuffer aLocBuf( 16 );
6004         aLocBuf.append( m_aContext.DocumentLocale.Language.toAsciiLowerCase() );
6005         if( m_aContext.DocumentLocale.Country.getLength() > 0 )
6006         {
6007             aLocBuf.append( sal_Unicode('-') );
6008             aLocBuf.append( m_aContext.DocumentLocale.Country );
6009         }
6010         aLine.append( "/Lang" );
6011         appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
6012         aLine.append( "\n" );
6013     }
6014     if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 )
6015     {
6016         aLine.append( "/MarkInfo<</Marked true>>\n" );
6017     }
6018     if( m_aWidgets.size() > 0 )
6019     {
6020         aLine.append( "/AcroForm<</Fields[\n" );
6021         int nWidgets = m_aWidgets.size();
6022         int nOut = 0;
6023         for( int j = 0; j < nWidgets; j++ )
6024         {
6025             // output only root fields
6026             if( m_aWidgets[j].m_nParent < 1 )
6027             {
6028                 aLine.append( m_aWidgets[j].m_nObject );
6029                 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
6030             }
6031         }
6032         aLine.append( "\n]/DR " );
6033         aLine.append( getResourceDictObj() );
6034         aLine.append( " 0 R" );
6035         if( m_bIsPDF_A1 )
6036             aLine.append( ">>\n" );
6037         else
6038             aLine.append( "/NeedAppearances true>>\n" );
6039     }
6040 //--->i59651
6041 //check if there is a Metadata object
6042     if( nOutputIntentObject )
6043     {
6044         aLine.append("/OutputIntents[");
6045         aLine.append( nOutputIntentObject );
6046         aLine.append( " 0 R]" );
6047     }
6048     if( nMetadataObject )
6049     {
6050         aLine.append("/Metadata ");
6051         aLine.append( nMetadataObject );
6052         aLine.append( " 0 R" );
6053     }
6054 //<----
6055     aLine.append( ">>\n"
6056                   "endobj\n\n" );
6057     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6058 
6059     return true;
6060 }
6061 
6062 sal_Int32 PDFWriterImpl::emitInfoDict( )
6063 {
6064     sal_Int32 nObject = createObject();
6065 
6066     if( updateObject( nObject ) )
6067     {
6068         OStringBuffer aLine( 1024 );
6069         aLine.append( nObject );
6070         aLine.append( " 0 obj\n"
6071                       "<<" );
6072         if( m_aContext.DocumentInfo.Title.Len() )
6073         {
6074             aLine.append( "/Title" );
6075             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine );
6076             aLine.append( "\n" );
6077         }
6078         if( m_aContext.DocumentInfo.Author.Len() )
6079         {
6080             aLine.append( "/Author" );
6081             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine );
6082             aLine.append( "\n" );
6083         }
6084         if( m_aContext.DocumentInfo.Subject.Len() )
6085         {
6086             aLine.append( "/Subject" );
6087             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine );
6088             aLine.append( "\n" );
6089         }
6090         if( m_aContext.DocumentInfo.Keywords.Len() )
6091         {
6092             aLine.append( "/Keywords" );
6093             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine );
6094             aLine.append( "\n" );
6095         }
6096         if( m_aContext.DocumentInfo.Creator.Len() )
6097         {
6098             aLine.append( "/Creator" );
6099             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine );
6100             aLine.append( "\n" );
6101         }
6102         if( m_aContext.DocumentInfo.Producer.Len() )
6103         {
6104             aLine.append( "/Producer" );
6105             appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine );
6106             aLine.append( "\n" );
6107         }
6108 
6109          aLine.append( "/CreationDate" );
6110          appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine );
6111         aLine.append( ">>\nendobj\n\n" );
6112         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6113             nObject = 0;
6114     }
6115     else
6116         nObject = 0;
6117 
6118     return nObject;
6119 }
6120 
6121 //--->i56629
6122 // Part of this function may be shared with method appendDest.
6123 //
6124 sal_Int32 PDFWriterImpl::emitNamedDestinations()
6125 {
6126     sal_Int32  nCount = m_aNamedDests.size();
6127     if( nCount <= 0 )
6128         return 0;//define internal error
6129 
6130 //get the object number for all the destinations
6131     sal_Int32 nObject = createObject();
6132 
6133     if( updateObject( nObject ) )
6134     {
6135 //emit the dictionary
6136         OStringBuffer aLine( 1024 );
6137         aLine.append( nObject );
6138         aLine.append( " 0 obj\n"
6139                       "<<" );
6140 
6141         sal_Int32  nDestID;
6142         for( nDestID = 0; nDestID < nCount; nDestID++ )
6143         {
6144             const PDFNamedDest& rDest   = m_aNamedDests[ nDestID ];
6145 // In order to correctly function both under an Internet browser and
6146 // directly with a reader (provided the reader has the feature) we
6147 // need to set the name of the destination the same way it will be encoded
6148 // in an Internet link
6149             INetURLObject aLocalURL(
6150                 OUString( RTL_CONSTASCII_USTRINGPARAM( "http://ahost.ax" ) ) ); //dummy location, won't be used
6151             aLocalURL.SetMark( rDest.m_aDestName );
6152 
6153             const rtl::OUString aName   = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as
6154             // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
6155             const PDFPage& rDestPage    = m_aPages[ rDest.m_nPage ];
6156 
6157             aLine.append( '/' );
6158             appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
6159             aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
6160                                  //maps the preceeding character properly
6161             aLine.append( rDestPage.m_nPageObject );
6162             aLine.append( " 0 R" );
6163 
6164             switch( rDest.m_eType )
6165             {
6166             case PDFWriter::XYZ:
6167             default:
6168                 aLine.append( "/XYZ " );
6169                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6170                 aLine.append( ' ' );
6171                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6172                 aLine.append( " 0" );
6173                 break;
6174             case PDFWriter::Fit:
6175                 aLine.append( "/Fit" );
6176                 break;
6177             case PDFWriter::FitRectangle:
6178                 aLine.append( "/FitR " );
6179                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6180                 aLine.append( ' ' );
6181                 appendFixedInt( rDest.m_aRect.Top(), aLine );
6182                 aLine.append( ' ' );
6183                 appendFixedInt( rDest.m_aRect.Right(), aLine );
6184                 aLine.append( ' ' );
6185                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6186                 break;
6187             case PDFWriter::FitHorizontal:
6188                 aLine.append( "/FitH " );
6189                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6190                 break;
6191             case PDFWriter::FitVertical:
6192                 aLine.append( "/FitV " );
6193                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6194                 break;
6195             case PDFWriter::FitPageBoundingBox:
6196                 aLine.append( "/FitB" );
6197                 break;
6198             case PDFWriter::FitPageBoundingBoxHorizontal:
6199                 aLine.append( "/FitBH " );
6200                 appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6201                 break;
6202             case PDFWriter::FitPageBoundingBoxVertical:
6203                 aLine.append( "/FitBV " );
6204                 appendFixedInt( rDest.m_aRect.Left(), aLine );
6205                 break;
6206             }
6207             aLine.append( "]\n" );
6208         }
6209 //close
6210 
6211         aLine.append( ">>\nendobj\n\n" );
6212         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6213             nObject = 0;
6214     }
6215     else
6216         nObject = 0;
6217 
6218     return nObject;
6219 }
6220 //<--- i56629
6221 
6222 //--->i59651
6223 // emits the output intent dictionary
6224 
6225 sal_Int32 PDFWriterImpl::emitOutputIntent()
6226 {
6227     if( !m_bIsPDF_A1 )
6228         return 0;
6229 
6230 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
6231 
6232     OStringBuffer aLine( 1024 );
6233     sal_Int32 nICCObject = createObject();
6234     sal_Int32 nStreamLengthObject = createObject();
6235 
6236     aLine.append( nICCObject );
6237 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
6238     aLine.append( " 0 obj\n<</N 3/Length " );
6239     aLine.append( nStreamLengthObject );
6240     aLine.append( " 0 R" );
6241 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
6242     aLine.append( "/Filter/FlateDecode" );
6243 #endif
6244     aLine.append( ">>\nstream\n" );
6245     CHECK_RETURN( updateObject( nICCObject ) );
6246     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6247 //get file position
6248     sal_uInt64 nBeginStreamPos = 0;
6249     osl_getFilePos( m_aFile, &nBeginStreamPos );
6250     beginCompression();
6251     checkAndEnableStreamEncryption( nICCObject );
6252     sal_Int32 nStreamSize = writeBuffer( nsRGB_ICC_profile, (sal_Int32) sizeof( nsRGB_ICC_profile ) );
6253     disableStreamEncryption();
6254     endCompression();
6255     sal_uInt64 nEndStreamPos = 0;
6256     osl_getFilePos( m_aFile, &nEndStreamPos );
6257 
6258     if( nStreamSize == 0 )
6259         return 0;
6260     if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6261         return 0 ;
6262     aLine.setLength( 0 );
6263 
6264 //emit the stream length   object
6265     CHECK_RETURN( updateObject( nStreamLengthObject ) );
6266     aLine.setLength( 0 );
6267     aLine.append( nStreamLengthObject );
6268     aLine.append( " 0 obj\n" );
6269     aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6270     aLine.append( "\nendobj\n\n" );
6271     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6272     aLine.setLength( 0 );
6273 
6274 //emit the OutputIntent dictionary
6275     sal_Int32 nOIObject = createObject();
6276     CHECK_RETURN( updateObject( nOIObject ) );
6277     aLine.append( nOIObject );
6278     aLine.append( " 0 obj\n"
6279                   "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
6280 
6281     rtl::OUString aComment( RTL_CONSTASCII_USTRINGPARAM( "sRGB IEC61966-2.1" ) );
6282     appendLiteralStringEncrypt( aComment ,nOIObject, aLine );
6283     aLine.append("/DestOutputProfile ");
6284     aLine.append( nICCObject );
6285     aLine.append( " 0 R>>\nendobj\n\n" );;
6286     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6287 
6288     return nOIObject;
6289 }
6290 
6291 // formats the string for the XML stream
6292 static void escapeStringXML( const rtl::OUString& rStr, rtl::OUString &rValue)
6293 {
6294     const sal_Unicode* pUni = rStr.getStr();
6295     int nLen = rStr.getLength();
6296     for( ; nLen; nLen--, pUni++ )
6297     {
6298         switch( *pUni )
6299         {
6300         case sal_Unicode('&'):
6301             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&amp;" ) );
6302         break;
6303         case sal_Unicode('<'):
6304             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&lt;" ) );
6305         break;
6306         case sal_Unicode('>'):
6307             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&gt;" ) );
6308         break;
6309         case sal_Unicode('\''):
6310             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&apos;" ) );
6311         break;
6312         case sal_Unicode('"'):
6313             rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&quot;" ) );
6314         break;
6315         default:
6316             rValue += rtl::OUString( *pUni );
6317             break;
6318         }
6319     }
6320 }
6321 
6322 // emits the document metadata
6323 //
6324 sal_Int32 PDFWriterImpl::emitDocumentMetadata()
6325 {
6326     if( !m_bIsPDF_A1 )
6327         return 0;
6328 
6329     //get the object number for all the destinations
6330     sal_Int32 nObject = createObject();
6331 
6332     if( updateObject( nObject ) )
6333     {
6334 // the following string are written in UTF-8 unicode
6335         OStringBuffer aMetadataStream( 8192 );
6336 
6337         aMetadataStream.append( "<?xpacket begin=\"" );
6338 // this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used
6339 // as a byte-order marker.
6340         aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) );
6341         aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
6342         aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
6343         aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
6344 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
6345         aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6346         aMetadataStream.append( "      xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
6347         aMetadataStream.append( "   <pdfaid:part>1</pdfaid:part>\n" );
6348         aMetadataStream.append( "   <pdfaid:conformance>A</pdfaid:conformance>\n" );
6349         aMetadataStream.append( "  </rdf:Description>\n" );
6350 //... Dublin Core properties go here
6351         if( m_aContext.DocumentInfo.Title.Len() ||
6352             m_aContext.DocumentInfo.Author.Len() ||
6353             m_aContext.DocumentInfo.Subject.Len() )
6354         {
6355             aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6356             aMetadataStream.append( "      xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
6357             if( m_aContext.DocumentInfo.Title.Len() )
6358             {
6359 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6360                 aMetadataStream.append( "   <dc:title>\n" );
6361                 aMetadataStream.append( "    <rdf:Alt>\n" );
6362                 aMetadataStream.append( "     <rdf:li xml:lang=\"x-default\">" );
6363                 rtl::OUString aTitle;
6364                 escapeStringXML( m_aContext.DocumentInfo.Title, aTitle );
6365                 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 )  );
6366                 aMetadataStream.append( "</rdf:li>\n" );
6367                 aMetadataStream.append( "    </rdf:Alt>\n" );
6368                 aMetadataStream.append( "   </dc:title>\n" );
6369             }
6370             if( m_aContext.DocumentInfo.Author.Len() )
6371             {
6372                 aMetadataStream.append( "   <dc:creator>\n" );
6373                 aMetadataStream.append( "    <rdf:Seq>\n" );
6374                 aMetadataStream.append( "     <rdf:li>" );
6375                 rtl::OUString aAuthor;
6376                 escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor );
6377                 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 )  );
6378                 aMetadataStream.append( "</rdf:li>\n" );
6379                 aMetadataStream.append( "    </rdf:Seq>\n" );
6380                 aMetadataStream.append( "   </dc:creator>\n" );
6381             }
6382             if( m_aContext.DocumentInfo.Subject.Len() )
6383             {
6384 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6385                 aMetadataStream.append( "   <dc:description>\n" );
6386                 aMetadataStream.append( "    <rdf:Alt>\n" );
6387                 aMetadataStream.append( "     <rdf:li xml:lang=\"x-default\">" );
6388                 rtl::OUString aSubject;
6389                 escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject );
6390                 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 )  );
6391                 aMetadataStream.append( "</rdf:li>\n" );
6392                 aMetadataStream.append( "    </rdf:Alt>\n" );
6393                 aMetadataStream.append( "   </dc:description>\n" );
6394             }
6395             aMetadataStream.append( "  </rdf:Description>\n" );
6396         }
6397 
6398 //... PDF properties go here
6399         if( m_aContext.DocumentInfo.Producer.Len() ||
6400             m_aContext.DocumentInfo.Keywords.Len() )
6401         {
6402             aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6403             aMetadataStream.append( "     xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
6404             if( m_aContext.DocumentInfo.Producer.Len() )
6405             {
6406                 aMetadataStream.append( "   <pdf:Producer>" );
6407                 rtl::OUString aProducer;
6408                 escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer );
6409                 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 )  );
6410                 aMetadataStream.append( "</pdf:Producer>\n" );
6411             }
6412             if( m_aContext.DocumentInfo.Keywords.Len() )
6413             {
6414                 aMetadataStream.append( "   <pdf:Keywords>" );
6415                 rtl::OUString aKeywords;
6416                 escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords );
6417                 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 )  );
6418                 aMetadataStream.append( "</pdf:Keywords>\n" );
6419             }
6420             aMetadataStream.append( "  </rdf:Description>\n" );
6421         }
6422 
6423         aMetadataStream.append( "  <rdf:Description rdf:about=\"\"\n" );
6424         aMetadataStream.append( "    xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
6425         if( m_aContext.DocumentInfo.Creator.Len() )
6426         {
6427             aMetadataStream.append( "   <xmp:CreatorTool>" );
6428             rtl::OUString aCreator;
6429             escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator );
6430             aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 )  );
6431             aMetadataStream.append( "</xmp:CreatorTool>\n" );
6432         }
6433 //creation date
6434         aMetadataStream.append( "   <xmp:CreateDate>" );
6435         aMetadataStream.append( m_aCreationMetaDateString );
6436         aMetadataStream.append( "</xmp:CreateDate>\n" );
6437 
6438         aMetadataStream.append( "  </rdf:Description>\n" );
6439         aMetadataStream.append( " </rdf:RDF>\n" );
6440         aMetadataStream.append( "</x:xmpmeta>\n" );
6441 
6442 //add the padding
6443         for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ )
6444         {
6445             aMetadataStream.append( " " );
6446             if( nSpaces % 100 == 0 )
6447                 aMetadataStream.append( "\n" );
6448         }
6449 
6450         aMetadataStream.append( "<?xpacket end=\"w\"?>\n" );
6451 
6452         OStringBuffer aMetadataObj( 1024 );
6453 
6454         aMetadataObj.append( nObject );
6455         aMetadataObj.append( " 0 obj\n" );
6456 
6457         aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
6458 
6459         aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() );
6460         aMetadataObj.append( ">>\nstream\n" );
6461         CHECK_RETURN( writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) );
6462 //emit the stream
6463         CHECK_RETURN( writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) );
6464 
6465         aMetadataObj.setLength( 0 );
6466         aMetadataObj.append( "\nendstream\nendobj\n\n" );
6467         if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
6468             nObject = 0;
6469     }
6470     else
6471         nObject = 0;
6472 
6473     return nObject;
6474 }
6475 //<---i59651
6476 
6477 bool PDFWriterImpl::emitTrailer()
6478 {
6479     // emit doc info
6480     OString aInfoValuesOut;
6481     sal_Int32 nDocInfoObject = emitInfoDict( );
6482 
6483     sal_Int32 nSecObject = 0;
6484 
6485 	if( m_aContext.Encryption.Encrypt() )
6486 	{
6487 //emit the security information
6488 //must be emitted as indirect dictionary object, since
6489 //Acrobat Reader 5 works only with this kind of implementation
6490 		nSecObject = createObject();
6491 
6492 		if( updateObject( nSecObject ) )
6493 		{
6494 			OStringBuffer aLineS( 1024 );
6495 			aLineS.append( nSecObject );
6496 			aLineS.append( " 0 obj\n"
6497 						   "<</Filter/Standard/V " );
6498 			// check the version
6499 			if( m_aContext.Encryption.Security128bit )
6500 				aLineS.append( "2/Length 128/R 3" );
6501 			else
6502 				aLineS.append( "1/R 2" );
6503 
6504 			// emit the owner password, must not be encrypted
6505 			aLineS.append( "/O(" );
6506 			appendLiteralString( (const sal_Char*)&m_aContext.Encryption.OValue[0], sal_Int32(m_aContext.Encryption.OValue.size()), aLineS );
6507 			aLineS.append( ")/U(" );
6508 			appendLiteralString( (const sal_Char*)&m_aContext.Encryption.UValue[0], sal_Int32(m_aContext.Encryption.UValue.size()), aLineS );
6509 			aLineS.append( ")/P " );// the permission set
6510 			aLineS.append( m_nAccessPermissions );
6511 			aLineS.append( ">>\nendobj\n\n" );
6512 			if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) )
6513 				nSecObject = 0;
6514 		}
6515 		else
6516 			nSecObject = 0;
6517 	}
6518     // emit xref table
6519     // remember start
6520     sal_uInt64 nXRefOffset = 0;
6521     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) );
6522     CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
6523 
6524     sal_Int32 nObjects = m_aObjects.size();
6525     OStringBuffer aLine;
6526     aLine.append( "0 " );
6527     aLine.append( (sal_Int32)(nObjects+1) );
6528     aLine.append( "\n" );
6529     aLine.append( "0000000000 65535 f \n" );
6530     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6531 
6532     for( sal_Int32 i = 0; i < nObjects; i++ )
6533     {
6534         aLine.setLength( 0 );
6535         OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] );
6536         for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
6537             aLine.append( '0' );
6538         aLine.append( aOffset );
6539         aLine.append( " 00000 n \n" );
6540         DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" );
6541         CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6542     }
6543 
6544     // prepare document checksum
6545     OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
6546     if( m_aDocDigest )
6547     {
6548         sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
6549         rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) );
6550         for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ )
6551             appendHex( nMD5Sum[i], aDocChecksum );
6552     }
6553     // document id set in setDocInfo method
6554     // emit trailer
6555     aLine.setLength( 0 );
6556     aLine.append( "trailer\n"
6557                   "<</Size " );
6558     aLine.append( (sal_Int32)(nObjects+1) );
6559     aLine.append( "/Root " );
6560     aLine.append( m_nCatalogObject );
6561     aLine.append( " 0 R\n" );
6562     if( nSecObject |= 0 )
6563     {
6564         aLine.append( "/Encrypt ");
6565         aLine.append( nSecObject );
6566         aLine.append( " 0 R\n" );
6567     }
6568     if( nDocInfoObject )
6569     {
6570         aLine.append( "/Info " );
6571         aLine.append( nDocInfoObject );
6572         aLine.append( " 0 R\n" );
6573     }
6574     if( ! m_aContext.Encryption.DocumentIdentifier.empty() )
6575     {
6576         aLine.append( "/ID [ <" );
6577         for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6578              it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6579         {
6580             appendHex( sal_Int8(*it), aLine );
6581         }
6582         aLine.append( ">\n"
6583                       "<" );
6584         for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6585              it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6586         {
6587             appendHex( sal_Int8(*it), aLine );
6588         }
6589         aLine.append( "> ]\n" );
6590     }
6591     if( aDocChecksum.getLength() )
6592     {
6593         aLine.append( "/DocChecksum /" );
6594         aLine.append( aDocChecksum );
6595         aLine.append( "\n" );
6596     }
6597     if( m_aAdditionalStreams.size() > 0 )
6598     {
6599         aLine.append( "/AdditionalStreams [" );
6600         for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ )
6601         {
6602             aLine.append( "/" );
6603             appendName( m_aAdditionalStreams[i].m_aMimeType, aLine );
6604             aLine.append( " " );
6605             aLine.append( m_aAdditionalStreams[i].m_nStreamObject );
6606             aLine.append( " 0 R\n" );
6607         }
6608         aLine.append( "]\n" );
6609     }
6610     aLine.append( ">>\n"
6611                   "startxref\n" );
6612     aLine.append( (sal_Int64)nXRefOffset );
6613     aLine.append( "\n"
6614                   "%%EOF\n" );
6615     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6616 
6617     return true;
6618 }
6619 
6620 struct AnnotationSortEntry
6621 {
6622     sal_Int32 nTabOrder;
6623     sal_Int32 nObject;
6624     sal_Int32 nWidgetIndex;
6625 
6626     AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
6627         nTabOrder( nTab ),
6628         nObject( nObj ),
6629         nWidgetIndex( nI )
6630     {}
6631 };
6632 
6633 struct AnnotSortContainer
6634 {
6635     std::set< sal_Int32 >               aObjects;
6636     std::vector< AnnotationSortEntry >    aSortedAnnots;
6637 };
6638 
6639 struct AnnotSorterLess
6640 {
6641     std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets;
6642 
6643     AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {}
6644 
6645     bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
6646     {
6647         if( rLeft.nTabOrder < rRight.nTabOrder )
6648             return true;
6649         if( rRight.nTabOrder < rLeft.nTabOrder )
6650             return false;
6651         if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
6652             return false;
6653         if( rRight.nWidgetIndex < 0 )
6654             return true;
6655         if( rLeft.nWidgetIndex < 0 )
6656             return false;
6657         // remember: widget rects are in PDF coordinates, so they are ordered down up
6658         if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
6659             m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
6660             return true;
6661         if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
6662             m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
6663             return false;
6664         if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
6665             m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
6666             return true;
6667         return false;
6668     }
6669 };
6670 
6671 void PDFWriterImpl::sortWidgets()
6672 {
6673     // sort widget annotations on each page as per their
6674     // TabOrder attribute
6675     std::hash_map< sal_Int32, AnnotSortContainer > sorted;
6676     int nWidgets = m_aWidgets.size();
6677     for( int nW = 0; nW < nWidgets; nW++ )
6678     {
6679         const PDFWidget& rWidget = m_aWidgets[nW];
6680         if( rWidget.m_nPage >= 0 )
6681         {
6682             AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
6683             // optimize vector allocation
6684             if( rCont.aSortedAnnots.empty() )
6685                 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
6686             // insert widget to tab sorter
6687             // RadioButtons are not page annotations, only their individual check boxes are
6688             if( rWidget.m_eType != PDFWriter::RadioButton )
6689             {
6690                 rCont.aObjects.insert( rWidget.m_nObject );
6691                 rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) );
6692             }
6693         }
6694     }
6695     for( std::hash_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it )
6696     {
6697         // append entries for non widget annotations
6698         PDFPage& rPage = m_aPages[ it->first ];
6699         unsigned int nAnnots = rPage.m_aAnnotations.size();
6700         for( unsigned int nA = 0; nA < nAnnots; nA++ )
6701             if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end())
6702                 it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) );
6703 
6704         AnnotSorterLess aLess( m_aWidgets );
6705         std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess );
6706         // sanity check
6707         if( it->second.aSortedAnnots.size() == nAnnots)
6708         {
6709             for( unsigned int nA = 0; nA < nAnnots; nA++ )
6710                 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject;
6711         }
6712         else
6713         {
6714             DBG_ASSERT( 0, "wrong number of sorted annotations" );
6715             #if OSL_DEBUG_LEVEL > 0
6716             fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n"
6717 					 "    %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots );
6718             #endif
6719         }
6720     }
6721 
6722     // FIXME: implement tab order in structure tree for PDF 1.5
6723 }
6724 
6725 namespace vcl {
6726 class PDFStreamIf :
6727 		public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream	>
6728 {
6729     PDFWriterImpl*  m_pWriter;
6730     bool            m_bWrite;
6731     public:
6732     PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
6733     virtual ~PDFStreamIf();
6734 
6735     virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw();
6736     virtual void SAL_CALL flush() throw();
6737     virtual void SAL_CALL closeOutput() throw();
6738 };
6739 }
6740 
6741 PDFStreamIf::~PDFStreamIf()
6742 {
6743 }
6744 
6745 void SAL_CALL  PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw()
6746 {
6747     if( m_bWrite )
6748     {
6749         sal_Int32 nBytes = aData.getLength();
6750         if( nBytes > 0 )
6751             m_pWriter->writeBuffer( aData.getConstArray(), nBytes );
6752     }
6753 }
6754 
6755 void SAL_CALL PDFStreamIf::flush() throw()
6756 {
6757 }
6758 
6759 void SAL_CALL PDFStreamIf::closeOutput() throw()
6760 {
6761     m_bWrite = false;
6762 }
6763 
6764 bool PDFWriterImpl::emitAdditionalStreams()
6765 {
6766     unsigned int nStreams = m_aAdditionalStreams.size();
6767     for( unsigned int i = 0; i < nStreams; i++ )
6768     {
6769         PDFAddStream& rStream = m_aAdditionalStreams[i];
6770         rStream.m_nStreamObject = createObject();
6771         sal_Int32 nSizeObject = createObject();
6772 
6773         if( ! updateObject( rStream.m_nStreamObject ) )
6774             return false;
6775 
6776         OStringBuffer aLine;
6777         aLine.append( rStream.m_nStreamObject );
6778         aLine.append( " 0 obj\n<</Length " );
6779         aLine.append( nSizeObject );
6780         aLine.append( " 0 R" );
6781         if( rStream.m_bCompress )
6782             aLine.append( "/Filter/FlateDecode" );
6783         aLine.append( ">>\nstream\n" );
6784         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6785             return false;
6786         sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0;
6787         if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) )
6788         {
6789             osl_closeFile( m_aFile );
6790             m_bOpen = false;
6791         }
6792         if( rStream.m_bCompress )
6793             beginCompression();
6794 
6795         checkAndEnableStreamEncryption( rStream.m_nStreamObject );
6796         com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) );
6797         rStream.m_pStream->write( xStream );
6798         xStream.clear();
6799         delete rStream.m_pStream;
6800         rStream.m_pStream = NULL;
6801         disableStreamEncryption();
6802 
6803         if( rStream.m_bCompress )
6804             endCompression();
6805 
6806         if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) )
6807         {
6808             osl_closeFile( m_aFile );
6809             m_bOpen = false;
6810             return false;
6811         }
6812         if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6813             return false ;
6814         // emit stream length object
6815         if( ! updateObject( nSizeObject ) )
6816             return false;
6817         aLine.setLength( 0 );
6818         aLine.append( nSizeObject );
6819         aLine.append( " 0 obj\n" );
6820         aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6821         aLine.append( "\nendobj\n\n" );
6822         if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6823             return false;
6824     }
6825     return true;
6826 }
6827 
6828 bool PDFWriterImpl::emit()
6829 {
6830     endPage();
6831 
6832     // resort structure tree and annotations if necessary
6833     // needed for widget tab order
6834     sortWidgets();
6835 
6836     // emit additional streams
6837     CHECK_RETURN( emitAdditionalStreams() );
6838 
6839     // emit catalog
6840     CHECK_RETURN( emitCatalog() );
6841 
6842     // emit trailer
6843     CHECK_RETURN( emitTrailer() );
6844 
6845     osl_closeFile( m_aFile );
6846     m_bOpen = false;
6847 
6848     return true;
6849 }
6850 
6851 std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors()
6852 {
6853     return m_aErrors;
6854 }
6855 
6856 sal_Int32 PDFWriterImpl::getSystemFont( const Font& i_rFont )
6857 {
6858     getReferenceDevice()->Push();
6859     getReferenceDevice()->SetFont( i_rFont );
6860     getReferenceDevice()->ImplNewFont();
6861 
6862     const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6863     sal_Int32 nFontID = 0;
6864     FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont );
6865     if( it != m_aSystemFonts.end() )
6866         nFontID = it->second.m_nNormalFontID;
6867     else
6868     {
6869         nFontID = m_nNextFID++;
6870         m_aSystemFonts[ pDevFont ] = EmbedFont();
6871         m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID;
6872     }
6873 
6874     getReferenceDevice()->Pop();
6875     getReferenceDevice()->ImplNewFont();
6876 
6877     return nFontID;
6878 }
6879 
6880 void PDFWriterImpl::registerGlyphs( int nGlyphs,
6881                                     sal_GlyphId* pGlyphs,
6882                                     sal_Int32* pGlyphWidths,
6883                                     sal_Ucs* pUnicodes,
6884                                     sal_Int32* pUnicodesPerGlyph,
6885                                     sal_uInt8* pMappedGlyphs,
6886                                     sal_Int32* pMappedFontObjects,
6887                                     const ImplFontData* pFallbackFonts[] )
6888 {
6889     const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6890     sal_Ucs* pCurUnicode = pUnicodes;
6891     for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ )
6892     {
6893         const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB);
6894         const ImplFontData* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont;
6895 
6896         if( isBuiltinFont( pCurrentFont ) )
6897         {
6898             sal_Int32 nFontID = 0;
6899             FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6900             if( it != m_aEmbeddedFonts.end() )
6901                 nFontID = it->second.m_nNormalFontID;
6902             else
6903             {
6904                 nFontID = m_nNextFID++;
6905                 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6906                 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6907             }
6908 
6909             pGlyphWidths[ i ] = 0;
6910             pMappedGlyphs[ i ] = sal::static_int_cast<sal_Int8>( nFontGlyphId );
6911             pMappedFontObjects[ i ] = nFontID;
6912             const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pCurrentFont );
6913             if( pFD )
6914             {
6915                 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
6916                 pGlyphWidths[i] = pBuiltinFont->m_aWidths[ nFontGlyphId & 0x00ff ];
6917             }
6918         }
6919         else if( pCurrentFont->mbSubsettable )
6920         {
6921             FontSubset& rSubset = m_aSubsets[ pCurrentFont ];
6922             // search for font specific glyphID
6923             FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId );
6924             if( it != rSubset.m_aMapping.end() )
6925             {
6926                 pMappedFontObjects[i] = it->second.m_nFontID;
6927                 pMappedGlyphs[i] = it->second.m_nSubsetGlyphID;
6928             }
6929             else
6930             {
6931                 // create new subset if necessary
6932                 if( rSubset.m_aSubsets.empty()
6933                 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
6934                 {
6935                     rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) );
6936                 }
6937 
6938                 // copy font id
6939                 pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID;
6940                 // create new glyph in subset
6941                 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
6942                 pMappedGlyphs[i] = nNewId;
6943 
6944                 // add new glyph to emitted font subset
6945                 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
6946                 rNewGlyphEmit.setGlyphId( nNewId );
6947                 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ )
6948                     rNewGlyphEmit.addCode( pCurUnicode[n] );
6949 
6950                 // add new glyph to font mapping
6951                 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
6952                 rNewGlyph.m_nFontID = pMappedFontObjects[i];
6953                 rNewGlyph.m_nSubsetGlyphID = nNewId;
6954             }
6955             getReferenceDevice()->ImplGetGraphics();
6956             const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0);
6957             pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont,
6958                                                           nFontGlyphId,
6959                                                           bVertical,
6960                                                           m_pReferenceDevice->mpGraphics );
6961         }
6962         else if( pCurrentFont->IsEmbeddable() )
6963         {
6964             sal_Int32 nFontID = 0;
6965             FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
6966             if( it != m_aEmbeddedFonts.end() )
6967                 nFontID = it->second.m_nNormalFontID;
6968             else
6969             {
6970                 nFontID = m_nNextFID++;
6971                 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
6972                 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
6973             }
6974             EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont];
6975 
6976             const Ucs2SIntMap* pEncoding = NULL;
6977             const Ucs2OStrMap* pNonEncoded = NULL;
6978             getReferenceDevice()->ImplGetGraphics();
6979             pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded );
6980 
6981             Ucs2SIntMap::const_iterator enc_it;
6982             Ucs2OStrMap::const_iterator nonenc_it;
6983 
6984             sal_Int32 nCurFontID = nFontID;
6985             sal_Ucs cChar = *pCurUnicode;
6986             if( pEncoding )
6987             {
6988                 enc_it = pEncoding->find( cChar );
6989                 if( enc_it != pEncoding->end() && enc_it->second > 0 )
6990                 {
6991                     DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" );
6992                     cChar = (sal_Ucs)enc_it->second;
6993                 }
6994                 else if( (enc_it == pEncoding->end() || enc_it->second == -1) &&
6995                          pNonEncoded &&
6996                          (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() )
6997                 {
6998                     nCurFontID = 0;
6999                     // find non encoded glyph
7000                     for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it )
7001                     {
7002                         if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() )
7003                         {
7004                             nCurFontID = nec_it->m_nFontID;
7005                             cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ];
7006                             break;
7007                         }
7008                     }
7009                     if( nCurFontID == 0 ) // new nonencoded glyph
7010                     {
7011                         if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 )
7012                         {
7013                             rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() );
7014                             rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++;
7015                         }
7016                         EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back();
7017                         rEncoding.m_aEncVector.push_back( EmbedCode() );
7018                         rEncoding.m_aEncVector.back().m_aUnicode = cChar;
7019                         rEncoding.m_aEncVector.back().m_aName = nonenc_it->second;
7020                         rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1);
7021                         nCurFontID = rEncoding.m_nFontID;
7022                         cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ];
7023                     }
7024                 }
7025                 else
7026                     pEncoding = NULL;
7027             }
7028             if( ! pEncoding )
7029             {
7030                 if( cChar & 0xff00 )
7031                 {
7032                     // some characters can be used by conversion
7033                     if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area
7034                         cChar -= 0xf000;
7035                     else
7036                     {
7037                         String aString(cChar);
7038                         ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 );
7039                         cChar = ((sal_Ucs)aChar.GetChar( 0 )) & 0x00ff;
7040                     }
7041                 }
7042             }
7043 
7044             pMappedGlyphs[ i ] = (sal_Int8)cChar;
7045             pMappedFontObjects[ i ] = nCurFontID;
7046             pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont,
7047                                                             (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR,
7048                                                             false,
7049                                                             m_pReferenceDevice->mpGraphics );
7050         }
7051     }
7052 }
7053 
7054 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines )
7055 {
7056     push( PUSH_ALL );
7057 
7058     FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief();
7059 
7060     Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor();
7061     Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7062     Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7063     Color aReliefColor( COL_LIGHTGRAY );
7064     if( aTextColor == COL_BLACK )
7065         aTextColor = Color( COL_WHITE );
7066     if( aTextLineColor == COL_BLACK )
7067         aTextLineColor = Color( COL_WHITE );
7068     if( aOverlineColor == COL_BLACK )
7069         aOverlineColor = Color( COL_WHITE );
7070     if( aTextColor == COL_WHITE )
7071         aReliefColor = Color( COL_BLACK );
7072 
7073     Font aSetFont = m_aCurrentPDFState.m_aFont;
7074     aSetFont.SetRelief( RELIEF_NONE );
7075     aSetFont.SetShadow( sal_False );
7076 
7077     aSetFont.SetColor( aReliefColor );
7078     setTextLineColor( aReliefColor );
7079     setOverlineColor( aReliefColor );
7080     setFont( aSetFont );
7081     long nOff = 1 + getReferenceDevice()->mnDPIX/300;
7082     if( eRelief == RELIEF_ENGRAVED )
7083         nOff = -nOff;
7084 
7085     rLayout.DrawOffset() += Point( nOff, nOff );
7086     updateGraphicsState();
7087     drawLayout( rLayout, rText, bTextLines );
7088 
7089     rLayout.DrawOffset() -= Point( nOff, nOff );
7090     setTextLineColor( aTextLineColor );
7091     setOverlineColor( aOverlineColor );
7092     aSetFont.SetColor( aTextColor );
7093     setFont( aSetFont );
7094     updateGraphicsState();
7095     drawLayout( rLayout, rText, bTextLines );
7096 
7097     // clean up the mess
7098     pop();
7099 }
7100 
7101 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines )
7102 {
7103     Font aSaveFont = m_aCurrentPDFState.m_aFont;
7104     Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7105     Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7106 
7107     Font& rFont = m_aCurrentPDFState.m_aFont;
7108     if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 )
7109         rFont.SetColor( Color( COL_LIGHTGRAY ) );
7110     else
7111         rFont.SetColor( Color( COL_BLACK ) );
7112     rFont.SetShadow( sal_False );
7113     rFont.SetOutline( sal_False );
7114     setFont( rFont );
7115     setTextLineColor( rFont.GetColor() );
7116     setOverlineColor( rFont.GetColor() );
7117     updateGraphicsState();
7118 
7119     long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24);
7120     if( rFont.IsOutline() )
7121         nOff++;
7122     rLayout.DrawBase() += Point( nOff, nOff );
7123     drawLayout( rLayout, rText, bTextLines );
7124     rLayout.DrawBase() -= Point( nOff, nOff );
7125 
7126     setFont( aSaveFont );
7127     setTextLineColor( aSaveTextLineColor );
7128     setOverlineColor( aSaveOverlineColor );
7129     updateGraphicsState();
7130 }
7131 
7132 void PDFWriterImpl::drawVerticalGlyphs(
7133         const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7134         OStringBuffer& rLine,
7135         const Point& rAlignOffset,
7136         const Matrix3& rRotScale,
7137         double fAngle,
7138         double fXScale,
7139         double fSkew,
7140         sal_Int32 nFontHeight )
7141 {
7142     long nXOffset = 0;
7143     Point aCurPos( rGlyphs[0].m_aPos );
7144     aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7145     aCurPos += rAlignOffset;
7146     for( size_t i = 0; i < rGlyphs.size(); i++ )
7147     {
7148         // have to emit each glyph on its own
7149         double fDeltaAngle = 0.0;
7150         double fYScale = 1.0;
7151         double fTempXScale = fXScale;
7152         double fSkewB = fSkew;
7153         double fSkewA = 0.0;
7154 
7155         Point aDeltaPos;
7156         if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL )
7157         {
7158             fDeltaAngle = M_PI/2.0;
7159             aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent();
7160             aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale);
7161             fYScale = fXScale;
7162             fTempXScale = 1.0;
7163             fSkewA = -fSkewB;
7164             fSkewB = 0.0;
7165         }
7166         else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR )
7167         {
7168             fDeltaAngle = -M_PI/2.0;
7169             aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale);
7170             aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent();
7171             fYScale = fXScale;
7172             fTempXScale = 1.0;
7173             fSkewA = fSkewB;
7174             fSkewB = 0.0;
7175         }
7176         aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) );
7177         if( i < rGlyphs.size()-1 )
7178             nXOffset += rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
7179         if( ! rGlyphs[i].m_nGlyphId )
7180             continue;
7181 
7182         aDeltaPos = rRotScale.transform( aDeltaPos );
7183 
7184         Matrix3 aMat;
7185         if( fSkewB != 0.0 || fSkewA != 0.0 )
7186             aMat.skew( fSkewA, fSkewB );
7187         aMat.scale( fTempXScale, fYScale );
7188         aMat.rotate( fAngle+fDeltaAngle );
7189         aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
7190         aMat.append( m_aPages.back(), rLine );
7191         rLine.append( " Tm" );
7192         if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
7193         {
7194             rLine.append( " /F" );
7195             rLine.append( rGlyphs[i].m_nMappedFontId );
7196             rLine.append( ' ' );
7197             m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7198             rLine.append( " Tf" );
7199         }
7200         rLine.append( "<" );
7201         appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
7202         rLine.append( ">Tj\n" );
7203     }
7204 }
7205 
7206 void PDFWriterImpl::drawHorizontalGlyphs(
7207         const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7208         OStringBuffer& rLine,
7209         const Point& rAlignOffset,
7210         double fAngle,
7211         double fXScale,
7212         double fSkew,
7213         sal_Int32 nFontHeight,
7214         sal_Int32 nPixelFontHeight
7215         )
7216 {
7217     // horizontal (= normal) case
7218 
7219     // fill in  run end indices
7220     // end is marked by index of the first glyph of the next run
7221     // a run is marked by same mapped font id and same Y position
7222     std::vector< sal_uInt32 > aRunEnds;
7223     aRunEnds.reserve( rGlyphs.size() );
7224     for( size_t i = 1; i < rGlyphs.size(); i++ )
7225     {
7226         if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
7227             rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
7228         {
7229             aRunEnds.push_back(i);
7230         }
7231     }
7232     // last run ends at last glyph
7233     aRunEnds.push_back( rGlyphs.size() );
7234 
7235     // loop over runs of the same font
7236     sal_uInt32 nBeginRun = 0;
7237     for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
7238     {
7239         // setup text matrix
7240         Point aCurPos = rGlyphs[nBeginRun].m_aPos;
7241         // back transformation to current coordinate system
7242         aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7243         aCurPos += rAlignOffset;
7244         // the first run can be set with "Td" operator
7245         // subsequent use of that operator would move
7246         // the texline matrix relative to what was set before
7247         // making use of that would drive us into rounding issues
7248         Matrix3 aMat;
7249         if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
7250         {
7251             m_aPages.back().appendPoint( aCurPos, rLine, false );
7252             rLine.append( " Td " );
7253         }
7254         else
7255         {
7256             if( fSkew != 0.0 )
7257                 aMat.skew( 0.0, fSkew );
7258             aMat.scale( fXScale, 1.0 );
7259             aMat.rotate( fAngle );
7260             aMat.translate( aCurPos.X(), aCurPos.Y() );
7261             aMat.append( m_aPages.back(), rLine );
7262             rLine.append( " Tm\n" );
7263         }
7264         // set up correct font
7265         rLine.append( "/F" );
7266         rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
7267         rLine.append( ' ' );
7268         m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7269         rLine.append( " Tf" );
7270 
7271         // output glyphs using Tj or TJ
7272         OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
7273         aKernedLine.append( "[<" );
7274         aUnkernedLine.append( '<' );
7275         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
7276         appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
7277 
7278         aMat.invert();
7279         bool bNeedKern = false;
7280         for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
7281         {
7282             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
7283             // check if default glyph positioning is sufficient
7284             const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
7285             const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
7286             double fAdvance = aThisPos.X() - aPrevPos.X();
7287             fAdvance *= 1000.0 / nPixelFontHeight;
7288             const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5);
7289             if( nAdjustment != 0 )
7290             {
7291                 // apply individual glyph positioning
7292                 bNeedKern = true;
7293                 aKernedLine.append( ">" );
7294                 aKernedLine.append( nAdjustment );
7295                 aKernedLine.append( "<" );
7296             }
7297             appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
7298         }
7299         aKernedLine.append( ">]TJ\n" );
7300         aUnkernedLine.append( ">Tj\n" );
7301         rLine.append( bNeedKern ? aKernedLine : aUnkernedLine );
7302 
7303         // set beginning of next run
7304         nBeginRun = aRunEnds[nRun];
7305     }
7306 }
7307 
7308 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines )
7309 {
7310     // relief takes precedence over shadow (see outdev3.cxx)
7311     if(  m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE )
7312     {
7313         drawRelief( rLayout, rText, bTextLines );
7314         return;
7315     }
7316     else if( m_aCurrentPDFState.m_aFont.IsShadow() )
7317         drawShadow( rLayout, rText, bTextLines );
7318 
7319     OStringBuffer aLine( 512 );
7320 
7321     const int nMaxGlyphs = 256;
7322 
7323     sal_GlyphId pGlyphs[nMaxGlyphs];
7324     sal_Int32 pGlyphWidths[nMaxGlyphs];
7325     sal_uInt8 pMappedGlyphs[nMaxGlyphs];
7326     sal_Int32 pMappedFontObjects[nMaxGlyphs];
7327     std::vector<sal_Ucs> aUnicodes;
7328     aUnicodes.reserve( nMaxGlyphs );
7329     sal_Int32 pUnicodesPerGlyph[nMaxGlyphs];
7330     int pCharPosAry[nMaxGlyphs];
7331     sal_Int32 nAdvanceWidths[nMaxGlyphs];
7332     const ImplFontData* pFallbackFonts[nMaxGlyphs];
7333     bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
7334     int nGlyphs;
7335     int nIndex = 0;
7336     int nMinCharPos = 0, nMaxCharPos = rText.Len()-1;
7337     double fXScale = 1.0;
7338     double fSkew = 0.0;
7339     sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight;
7340     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
7341 
7342     // transform font height back to current units
7343     // note: the layout calculates in outdevs device pixel !!
7344     sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
7345     if( m_aCurrentPDFState.m_aFont.GetWidth() )
7346     {
7347         Font aFont( m_aCurrentPDFState.m_aFont );
7348         aFont.SetWidth( 0 );
7349         FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
7350         if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() )
7351         {
7352             fXScale =
7353                 (double)m_aCurrentPDFState.m_aFont.GetWidth() /
7354                 (double)aMetric.GetWidth();
7355         }
7356         // force state before GetFontMetric
7357         m_pReferenceDevice->ImplNewFont();
7358     }
7359 
7360     // perform artificial italics if necessary
7361     if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
7362           m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
7363         !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL ||
7364            m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE )
7365         )
7366     {
7367         fSkew = M_PI/12.0;
7368     }
7369 
7370     // if the mapmode is distorted we need to adjust for that also
7371     if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
7372     {
7373         fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
7374     }
7375 
7376     int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
7377     // normalize angles
7378     while( nAngle < 0 )
7379         nAngle += 3600;
7380     nAngle = nAngle % 3600;
7381     double fAngle = (double)nAngle * M_PI / 1800.0;
7382 
7383     Matrix3 aRotScale;
7384     aRotScale.scale( fXScale, 1.0 );
7385     if( fAngle != 0.0 )
7386         aRotScale.rotate( -fAngle );
7387 
7388     bool bPop = false;
7389     bool bABold = false;
7390     // artificial bold necessary ?
7391     if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
7392         m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
7393     {
7394         if( ! bPop )
7395             aLine.append( "q " );
7396         bPop = true;
7397         bABold = true;
7398     }
7399     // setup text colors (if necessary)
7400     Color aStrokeColor( COL_TRANSPARENT );
7401     Color aNonStrokeColor( COL_TRANSPARENT );
7402 
7403     if( m_aCurrentPDFState.m_aFont.IsOutline() )
7404     {
7405         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7406         aNonStrokeColor = Color( COL_WHITE );
7407     }
7408     else
7409         aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7410     if( bABold )
7411         aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7412 
7413     if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
7414     {
7415         if( ! bPop )
7416             aLine.append( "q " );
7417         bPop = true;
7418         appendStrokingColor( aStrokeColor, aLine );
7419         aLine.append( "\n" );
7420     }
7421     if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
7422     {
7423         if( ! bPop )
7424             aLine.append( "q " );
7425         bPop = true;
7426         appendNonStrokingColor( aNonStrokeColor, aLine );
7427         aLine.append( "\n" );
7428     }
7429 
7430     // begin text object
7431     aLine.append( "BT\n" );
7432     // outline attribute ?
7433     if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
7434     {
7435         // set correct text mode, set stroke width
7436         aLine.append( "2 Tr " ); // fill, then stroke
7437 
7438         if( m_aCurrentPDFState.m_aFont.IsOutline() )
7439         {
7440             // unclear what to do in case of outline and artificial bold
7441             // for the time being outline wins
7442             aLine.append( "0.25 w \n" );
7443         }
7444         else
7445         {
7446             double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0;
7447             m_aPages.back().appendMappedLength( fW, aLine );
7448             aLine.append ( " w\n" );
7449         }
7450     }
7451 
7452     FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
7453 
7454     // collect the glyphs into a single array
7455     const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
7456     std::vector< PDFGlyph > aGlyphs;
7457     aGlyphs.reserve( nTmpMaxGlyphs );
7458     // first get all the glyphs and register them; coordinates still in Pixel
7459     Point aGNGlyphPos;
7460     while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 )
7461     {
7462         aUnicodes.clear();
7463         for( int i = 0; i < nGlyphs; i++ )
7464         {
7465             pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] );
7466 
7467             // default case: 1 glyph is one unicode
7468             pUnicodesPerGlyph[i] = 1;
7469             if( (pGlyphs[i] & GF_ISCHAR) )
7470             {
7471                 aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) );
7472             }
7473             else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
7474             {
7475                 int nChars = 1;
7476                 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) );
7477                 pUnicodesPerGlyph[i] = 1;
7478                 // try to handle ligatures and such
7479                 if( i < nGlyphs-1 )
7480                 {
7481                     nChars = pCharPosAry[i+1] - pCharPosAry[i];
7482                     // #i115618# fix for simple RTL+CTL cases
7483                     // TODO: sanitize for RTL ligatures, more complex CTL, etc.
7484                     if( nChars < 0 )
7485                         nChars = -nChars;
7486 		    else if( nChars == 0 )
7487                         nChars = 1;
7488                     pUnicodesPerGlyph[i] = nChars;
7489                     for( int n = 1; n < nChars; n++ )
7490                         aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) );
7491                 }
7492                 // #i36691# hack that is needed because currently the pGlyphs[]
7493                 // argument is ignored for embeddable fonts and so the layout
7494                 // engine's glyph work is ignored (i.e. char mirroring)
7495                 // TODO: a real solution would be to map the layout engine's
7496                 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
7497                 // back to unicode and then to embeddable font's encoding
7498                 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL )
7499                 {
7500                     size_t nI = aUnicodes.size()-1;
7501                     for( int n = 0; n < nChars; n++, nI-- )
7502                         aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI]));
7503                 }
7504             }
7505             else
7506                 aUnicodes.push_back( 0 );
7507             // note: in case of ctl one character may result
7508             // in multiple glyphs. The current SalLayout
7509             // implementations set -1 then to indicate that no direct
7510             // mapping is possible
7511         }
7512 
7513         registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
7514 
7515         for( int i = 0; i < nGlyphs; i++ )
7516         {
7517             aGlyphs.push_back( PDFGlyph( aGNGlyphPos,
7518                                          pGlyphWidths[i],
7519                                          pGlyphs[i],
7520                                          pMappedFontObjects[i],
7521                                          pMappedGlyphs[i] ) );
7522             if( bVertical )
7523                 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7524             else
7525                 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7526         }
7527     }
7528 
7529     Point aAlignOffset;
7530     if ( eAlign == ALIGN_BOTTOM )
7531         aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
7532     else if ( eAlign == ALIGN_TOP )
7533         aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
7534     if( aAlignOffset.X() || aAlignOffset.Y() )
7535         aAlignOffset = aRotScale.transform( aAlignOffset );
7536 
7537     /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
7538        string contained only on of the UTF16 BOMs
7539     */
7540     if( ! aGlyphs.empty() )
7541     {
7542         if( bVertical )
7543             drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
7544         else
7545             drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
7546     }
7547 
7548     // end textobject
7549     aLine.append( "ET\n" );
7550     if( bPop )
7551         aLine.append( "Q\n" );
7552 
7553     writeBuffer( aLine.getStr(), aLine.getLength() );
7554 
7555     // draw eventual textlines
7556     FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
7557     FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
7558     FontUnderline eOverline  = m_aCurrentPDFState.m_aFont.GetOverline();
7559     if( bTextLines &&
7560         (
7561          ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) ||
7562          ( eOverline  != UNDERLINE_NONE && eOverline  != UNDERLINE_DONTKNOW ) ||
7563          ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
7564          )
7565         )
7566     {
7567         sal_Bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
7568         if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
7569         {
7570             Point aPos, aStartPt;
7571             sal_Int32 nWidth = 0, nAdvance=0;
7572             for( int nStart = 0;;)
7573             {
7574                 sal_GlyphId nGlyphIndex;
7575                 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7576                     break;
7577 
7578                 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7579                 {
7580                     if( !nWidth )
7581                         aStartPt = aPos;
7582 
7583                     nWidth += nAdvance;
7584                 }
7585                 else if( nWidth > 0 )
7586                 {
7587                     drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7588                                   m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7589                                   eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7590                     nWidth = 0;
7591                 }
7592             }
7593 
7594             if( nWidth > 0 )
7595             {
7596                 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7597                               m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7598                               eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7599             }
7600         }
7601         else
7602         {
7603             Point aStartPt = rLayout.GetDrawPosition();
7604             int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
7605             drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7606                           m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7607                           eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7608         }
7609     }
7610 
7611     // write eventual emphasis marks
7612     if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
7613     {
7614         PolyPolygon 			aEmphPoly;
7615         Rectangle				aEmphRect1;
7616         Rectangle				aEmphRect2;
7617         long					nEmphYOff;
7618         long					nEmphWidth;
7619         long					nEmphHeight;
7620         sal_Bool					bEmphPolyLine;
7621         FontEmphasisMark		nEmphMark;
7622 
7623         push( PUSH_ALL );
7624 
7625         aLine.setLength( 0 );
7626         aLine.append( "q\n" );
7627 
7628         nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
7629         if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7630             nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
7631         else
7632             nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
7633         m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
7634                                                  bEmphPolyLine,
7635                                                  aEmphRect1,
7636                                                  aEmphRect2,
7637                                                  nEmphYOff,
7638                                                  nEmphWidth,
7639                                                  nEmphMark,
7640                                                  m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight),
7641                                                  m_pReferenceDevice->mpFontEntry->mnOrientation );
7642         if ( bEmphPolyLine )
7643         {
7644             setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
7645             setFillColor( Color( COL_TRANSPARENT ) );
7646         }
7647         else
7648         {
7649             setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
7650             setLineColor( Color( COL_TRANSPARENT ) );
7651         }
7652         writeBuffer( aLine.getStr(), aLine.getLength() );
7653 
7654         Point aOffset = Point(0,0);
7655 
7656         if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7657             aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff;
7658         else
7659             aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff;
7660 
7661         long nEmphWidth2     = nEmphWidth / 2;
7662         long nEmphHeight2    = nEmphHeight / 2;
7663         aOffset += Point( nEmphWidth2, nEmphHeight2 );
7664 
7665         if ( eAlign == ALIGN_BOTTOM )
7666             aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent;
7667         else if ( eAlign == ALIGN_TOP )
7668             aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent;
7669 
7670         for( int nStart = 0;;)
7671         {
7672             Point aPos;
7673             sal_GlyphId nGlyphIndex;
7674             sal_Int32 nAdvance;
7675             if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7676                 break;
7677 
7678             if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7679             {
7680                 Point aAdjOffset = aOffset;
7681                 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2;
7682                 aAdjOffset = aRotScale.transform( aAdjOffset );
7683 
7684                 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
7685 
7686                 aPos += aAdjOffset;
7687                 aPos = m_pReferenceDevice->PixelToLogic( aPos );
7688                 drawEmphasisMark( aPos.X(), aPos.Y(),
7689                                   aEmphPoly, bEmphPolyLine,
7690                                   aEmphRect1, aEmphRect2 );
7691             }
7692         }
7693 
7694         writeBuffer( "Q\n", 2 );
7695         pop();
7696     }
7697 
7698 }
7699 
7700 void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
7701                                       const PolyPolygon& rPolyPoly, sal_Bool bPolyLine,
7702                                       const Rectangle& rRect1, const Rectangle& rRect2 )
7703 {
7704     // TODO: pass nWidth as width of this mark
7705     // long nWidth = 0;
7706 
7707     if ( rPolyPoly.Count() )
7708     {
7709         if ( bPolyLine )
7710         {
7711             Polygon aPoly = rPolyPoly.GetObject( 0 );
7712             aPoly.Move( nX, nY );
7713             drawPolyLine( aPoly );
7714         }
7715         else
7716         {
7717             PolyPolygon aPolyPoly = rPolyPoly;
7718             aPolyPoly.Move( nX, nY );
7719             drawPolyPolygon( aPolyPoly );
7720         }
7721     }
7722 
7723     if ( !rRect1.IsEmpty() )
7724     {
7725         Rectangle aRect( Point( nX+rRect1.Left(),
7726                                 nY+rRect1.Top() ), rRect1.GetSize() );
7727         drawRectangle( aRect );
7728     }
7729 
7730     if ( !rRect2.IsEmpty() )
7731     {
7732         Rectangle aRect( Point( nX+rRect2.Left(),
7733                                 nY+rRect2.Top() ), rRect2.GetSize() );
7734 
7735         drawRectangle( aRect );
7736     }
7737 }
7738 
7739 void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7740 {
7741     MARK( "drawText" );
7742 
7743     updateGraphicsState();
7744 
7745     // get a layout from the OuputDevice's SalGraphics
7746     // this also enforces font substitution and sets the font on SalGraphics
7747     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
7748     if( pLayout )
7749     {
7750         drawLayout( *pLayout, rText, bTextLines );
7751         pLayout->Release();
7752     }
7753 }
7754 
7755 void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7756 {
7757     MARK( "drawText with array" );
7758 
7759     updateGraphicsState();
7760 
7761     // get a layout from the OuputDevice's SalGraphics
7762     // this also enforces font substitution and sets the font on SalGraphics
7763     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
7764     if( pLayout )
7765     {
7766         drawLayout( *pLayout, rText, bTextLines );
7767         pLayout->Release();
7768     }
7769 }
7770 
7771 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
7772 {
7773     MARK( "drawStretchText" );
7774 
7775     updateGraphicsState();
7776 
7777     // get a layout from the OuputDevice's SalGraphics
7778     // this also enforces font substitution and sets the font on SalGraphics
7779     SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
7780     if( pLayout )
7781     {
7782         drawLayout( *pLayout, rText, bTextLines );
7783         pLayout->Release();
7784     }
7785 }
7786 
7787 void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines )
7788 {
7789     long        nWidth          = rRect.GetWidth();
7790     long        nHeight         = rRect.GetHeight();
7791 
7792     if ( nWidth <= 0 || nHeight <= 0 )
7793         return;
7794 
7795     MARK( "drawText with rectangle" );
7796 
7797     updateGraphicsState();
7798 
7799     // clip with rectangle
7800     OStringBuffer aLine;
7801     aLine.append( "q " );
7802     m_aPages.back().appendRect( rRect, aLine );
7803     aLine.append( " W* n\n" );
7804     writeBuffer( aLine.getStr(), aLine.getLength() );
7805 
7806     // if disabled text is needed, put in here
7807 
7808     Point       aPos            = rRect.TopLeft();
7809 
7810     long		nTextHeight		= m_pReferenceDevice->GetTextHeight();
7811     xub_StrLen  nMnemonicPos    = STRING_NOTFOUND;
7812 
7813     String aStr = rOrigStr;
7814     if ( nStyle & TEXT_DRAW_MNEMONIC )
7815         aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos );
7816 
7817     // multiline text
7818     if ( nStyle & TEXT_DRAW_MULTILINE )
7819     {
7820         XubString               aLastLine;
7821         ImplMultiTextLineInfo   aMultiLineInfo;
7822         ImplTextLineInfo*       pLineInfo;
7823         long                    nMaxTextWidth;
7824         xub_StrLen              i;
7825         xub_StrLen              nLines;
7826         xub_StrLen              nFormatLines;
7827 
7828         if ( nTextHeight )
7829         {
7830             ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice );
7831             nMaxTextWidth = OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout );
7832             nLines = (xub_StrLen)(nHeight/nTextHeight);
7833             nFormatLines = aMultiLineInfo.Count();
7834             if ( !nLines )
7835                 nLines = 1;
7836             if ( nFormatLines > nLines )
7837             {
7838                 if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
7839                 {
7840                     // handle last line
7841                     nFormatLines = nLines-1;
7842 
7843                     pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
7844                     aLastLine = aStr.Copy( pLineInfo->GetIndex() );
7845                     aLastLine.ConvertLineEnd( LINEEND_LF );
7846                     // replace line feed by space
7847                     xub_StrLen nLastLineLen = aLastLine.Len();
7848                     for ( i = 0; i < nLastLineLen; i++ )
7849                     {
7850                         if ( aLastLine.GetChar( i ) == _LF )
7851                             aLastLine.SetChar( i, ' ' );
7852                     }
7853                     aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
7854                     nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
7855                     nStyle |= TEXT_DRAW_TOP;
7856                 }
7857             }
7858 
7859             // vertical alignment
7860             if ( nStyle & TEXT_DRAW_BOTTOM )
7861                 aPos.Y() += nHeight-(nFormatLines*nTextHeight);
7862             else if ( nStyle & TEXT_DRAW_VCENTER )
7863                 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
7864 
7865             // draw all lines excluding the last
7866             for ( i = 0; i < nFormatLines; i++ )
7867             {
7868                 pLineInfo = aMultiLineInfo.GetLine( i );
7869                 if ( nStyle & TEXT_DRAW_RIGHT )
7870                     aPos.X() += nWidth-pLineInfo->GetWidth();
7871                 else if ( nStyle & TEXT_DRAW_CENTER )
7872                     aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
7873                 xub_StrLen nIndex   = pLineInfo->GetIndex();
7874                 xub_StrLen nLineLen = pLineInfo->GetLen();
7875                 drawText( aPos, aStr, nIndex, nLineLen, bTextLines );
7876                 // mnemonics should not appear in documents,
7877                 // if the need arises, put them in here
7878                 aPos.Y() += nTextHeight;
7879                 aPos.X() = rRect.Left();
7880             }
7881 
7882 
7883             // output last line left adjusted since it was shortened
7884             if ( aLastLine.Len() )
7885                 drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines );
7886         }
7887     }
7888     else
7889     {
7890         long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7891 
7892         // Evt. Text kuerzen
7893         if ( nTextWidth > nWidth )
7894         {
7895             if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) )
7896             {
7897                 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
7898                 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
7899                 nStyle |= TEXT_DRAW_LEFT;
7900                 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7901             }
7902         }
7903 
7904         // vertical alignment
7905         if ( nStyle & TEXT_DRAW_RIGHT )
7906             aPos.X() += nWidth-nTextWidth;
7907         else if ( nStyle & TEXT_DRAW_CENTER )
7908             aPos.X() += (nWidth-nTextWidth)/2;
7909 
7910         if ( nStyle & TEXT_DRAW_BOTTOM )
7911             aPos.Y() += nHeight-nTextHeight;
7912         else if ( nStyle & TEXT_DRAW_VCENTER )
7913             aPos.Y() += (nHeight-nTextHeight)/2;
7914 
7915         // mnemonics should be inserted here if the need arises
7916 
7917         // draw the actual text
7918         drawText( aPos, aStr, 0, STRING_LEN, bTextLines );
7919     }
7920 
7921     // reset clip region to original value
7922     aLine.setLength( 0 );
7923     aLine.append( "Q\n" );
7924     writeBuffer( aLine.getStr(), aLine.getLength() );
7925 }
7926 
7927 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
7928 {
7929     MARK( "drawLine" );
7930 
7931     updateGraphicsState();
7932 
7933     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7934         return;
7935 
7936     OStringBuffer aLine;
7937     m_aPages.back().appendPoint( rStart, aLine );
7938     aLine.append( " m " );
7939     m_aPages.back().appendPoint( rStop, aLine );
7940     aLine.append( " l S\n" );
7941 
7942     writeBuffer( aLine.getStr(), aLine.getLength() );
7943 }
7944 
7945 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
7946 {
7947     MARK( "drawLine with LineInfo" );
7948     updateGraphicsState();
7949 
7950     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7951         return;
7952 
7953     if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 )
7954     {
7955         drawLine( rStart, rStop );
7956         return;
7957     }
7958 
7959     OStringBuffer aLine;
7960 
7961     aLine.append( "q " );
7962     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
7963     {
7964         m_aPages.back().appendPoint( rStart, aLine );
7965         aLine.append( " m " );
7966         m_aPages.back().appendPoint( rStop, aLine );
7967         aLine.append( " l S Q\n" );
7968 
7969         writeBuffer( aLine.getStr(), aLine.getLength() );
7970     }
7971     else
7972     {
7973         PDFWriter::ExtLineInfo aInfo;
7974         convertLineInfoToExtLineInfo( rInfo, aInfo );
7975         Point aPolyPoints[2] = { rStart, rStop };
7976         Polygon aPoly( 2, aPolyPoints );
7977         drawPolyLine( aPoly, aInfo );
7978     }
7979 }
7980 
7981 void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth )
7982 {
7983     Point aDiff( rStop-rStart );
7984     double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) );
7985     if( fLen < 1.0 )
7986         return;
7987 
7988     MARK( "drawWaveLine" );
7989     updateGraphicsState();
7990 
7991     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
7992         return;
7993 
7994     OStringBuffer aLine( 512 );
7995     aLine.append( "q " );
7996     m_aPages.back().appendMappedLength( nLineWidth, aLine, true );
7997     aLine.append( " w " );
7998 
7999     appendDouble( (double)aDiff.X()/fLen, aLine );
8000     aLine.append( ' ' );
8001     appendDouble( -(double)aDiff.Y()/fLen, aLine );
8002     aLine.append( ' ' );
8003     appendDouble( (double)aDiff.Y()/fLen, aLine );
8004     aLine.append( ' ' );
8005     appendDouble( (double)aDiff.X()/fLen, aLine );
8006     aLine.append( ' ' );
8007     m_aPages.back().appendPoint( rStart, aLine );
8008     aLine.append( " cm " );
8009     m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine );
8010     aLine.append( "Q\n" );
8011     writeBuffer( aLine.getStr(), aLine.getLength() );
8012 }
8013 
8014 #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x )
8015 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
8016 
8017 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8018 {
8019     // note: units in pFontEntry are ref device pixel
8020     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8021     long			nLineHeight = 0;
8022     long			nLinePos = 0;
8023 
8024     appendStrokingColor( aColor, aLine );
8025     aLine.append( "\n" );
8026 
8027     if ( bIsAbove )
8028     {
8029         if ( !pFontEntry->maMetric.mnAboveWUnderlineSize )
8030             m_pReferenceDevice->ImplInitAboveTextLineSize();
8031         nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize );
8032         nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset );
8033     }
8034     else
8035     {
8036         if ( !pFontEntry->maMetric.mnWUnderlineSize )
8037             m_pReferenceDevice->ImplInitTextLineSize();
8038         nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize );
8039         nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset );
8040     }
8041     if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
8042         nLineHeight = 3;
8043 
8044     long nLineWidth = getReferenceDevice()->mnDPIX/450;
8045     if ( ! nLineWidth )
8046         nLineWidth = 1;
8047 
8048     if ( eTextLine == UNDERLINE_BOLDWAVE )
8049         nLineWidth = 3*nLineWidth;
8050 
8051     m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
8052     aLine.append( " w " );
8053 
8054     if ( eTextLine == UNDERLINE_DOUBLEWAVE )
8055     {
8056         long nOrgLineHeight = nLineHeight;
8057         nLineHeight /= 3;
8058         if ( nLineHeight < 2 )
8059         {
8060             if ( nOrgLineHeight > 1 )
8061                 nLineHeight = 2;
8062             else
8063                 nLineHeight = 1;
8064         }
8065         long nLineDY = nOrgLineHeight-(nLineHeight*2);
8066         if ( nLineDY < nLineWidth )
8067             nLineDY = nLineWidth;
8068         long nLineDY2 = nLineDY/2;
8069         if ( !nLineDY2 )
8070             nLineDY2 = 1;
8071 
8072         nLinePos -= nLineWidth-nLineDY2;
8073 
8074         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8075 
8076         nLinePos += nLineWidth+nLineDY;
8077         m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8078     }
8079     else
8080     {
8081         if ( eTextLine != UNDERLINE_BOLDWAVE )
8082             nLinePos -= nLineWidth/2;
8083         m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
8084     }
8085 }
8086 
8087 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8088 {
8089     // note: units in pFontEntry are ref device pixel
8090     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8091     long			nLineHeight = 0;
8092     long			nLinePos  = 0;
8093     long			nLinePos2 = 0;
8094 
8095     if ( eTextLine > UNDERLINE_BOLDWAVE )
8096         eTextLine = UNDERLINE_SINGLE;
8097 
8098     switch ( eTextLine )
8099     {
8100         case UNDERLINE_SINGLE:
8101         case UNDERLINE_DOTTED:
8102         case UNDERLINE_DASH:
8103         case UNDERLINE_LONGDASH:
8104         case UNDERLINE_DASHDOT:
8105         case UNDERLINE_DASHDOTDOT:
8106             if ( bIsAbove )
8107             {
8108                 if ( !pFontEntry->maMetric.mnAboveUnderlineSize )
8109                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8110                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize );
8111                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset );
8112             }
8113             else
8114             {
8115                 if ( !pFontEntry->maMetric.mnUnderlineSize )
8116                     m_pReferenceDevice->ImplInitTextLineSize();
8117                 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize );
8118                 nLinePos    = HCONV( pFontEntry->maMetric.mnUnderlineOffset );
8119             }
8120             break;
8121         case UNDERLINE_BOLD:
8122         case UNDERLINE_BOLDDOTTED:
8123         case UNDERLINE_BOLDDASH:
8124         case UNDERLINE_BOLDLONGDASH:
8125         case UNDERLINE_BOLDDASHDOT:
8126         case UNDERLINE_BOLDDASHDOTDOT:
8127             if ( bIsAbove )
8128             {
8129                 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize )
8130                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8131                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize );
8132                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset );
8133             }
8134             else
8135             {
8136                 if ( !pFontEntry->maMetric.mnBUnderlineSize )
8137                     m_pReferenceDevice->ImplInitTextLineSize();
8138                 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize );
8139                 nLinePos    = HCONV( pFontEntry->maMetric.mnBUnderlineOffset );
8140                 nLinePos += nLineHeight/2;
8141             }
8142             break;
8143         case UNDERLINE_DOUBLE:
8144             if ( bIsAbove )
8145             {
8146                 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize )
8147                     m_pReferenceDevice->ImplInitAboveTextLineSize();
8148                 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize );
8149                 nLinePos    = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 );
8150                 nLinePos2   = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 );
8151             }
8152             else
8153             {
8154                 if ( !pFontEntry->maMetric.mnDUnderlineSize )
8155                     m_pReferenceDevice->ImplInitTextLineSize();
8156                 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize );
8157                 nLinePos    = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 );
8158                 nLinePos2   = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 );
8159             }
8160         default:
8161             break;
8162     }
8163 
8164     if ( nLineHeight )
8165     {
8166         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8167         aLine.append( " w " );
8168         appendStrokingColor( aColor, aLine );
8169         aLine.append( "\n" );
8170 
8171         switch ( eTextLine )
8172         {
8173             case UNDERLINE_DOTTED:
8174             case UNDERLINE_BOLDDOTTED:
8175                 aLine.append( "[ " );
8176                 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8177                 aLine.append( " ] 0 d\n" );
8178                 break;
8179             case UNDERLINE_DASH:
8180             case UNDERLINE_LONGDASH:
8181             case UNDERLINE_BOLDDASH:
8182             case UNDERLINE_BOLDLONGDASH:
8183                 {
8184                     sal_Int32 nDashLength = 4*nLineHeight;
8185                     sal_Int32 nVoidLength = 2*nLineHeight;
8186                     if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) )
8187                         nDashLength = 8*nLineHeight;
8188 
8189                     aLine.append( "[ " );
8190                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8191                     aLine.append( ' ' );
8192                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8193                     aLine.append( " ] 0 d\n" );
8194                 }
8195                 break;
8196             case UNDERLINE_DASHDOT:
8197             case UNDERLINE_BOLDDASHDOT:
8198                 {
8199                     sal_Int32 nDashLength = 4*nLineHeight;
8200                     sal_Int32 nVoidLength = 2*nLineHeight;
8201                     aLine.append( "[ " );
8202                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8203                     aLine.append( ' ' );
8204                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8205                     aLine.append( ' ' );
8206                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8207                     aLine.append( ' ' );
8208                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8209                     aLine.append( " ] 0 d\n" );
8210                 }
8211                 break;
8212             case UNDERLINE_DASHDOTDOT:
8213             case UNDERLINE_BOLDDASHDOTDOT:
8214                 {
8215                     sal_Int32 nDashLength = 4*nLineHeight;
8216                     sal_Int32 nVoidLength = 2*nLineHeight;
8217                     aLine.append( "[ " );
8218                     m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8219                     aLine.append( ' ' );
8220                     m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8221                     aLine.append( ' ' );
8222                     m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, 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( " ] 0 d\n" );
8230                 }
8231                 break;
8232             default:
8233                 break;
8234         }
8235 
8236         aLine.append( "0 " );
8237         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8238         aLine.append( " m " );
8239         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8240         aLine.append( ' ' );
8241         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8242         aLine.append( " l S\n" );
8243         if ( eTextLine == UNDERLINE_DOUBLE )
8244         {
8245             aLine.append( "0 " );
8246             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8247             aLine.append( " m " );
8248             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8249             aLine.append( ' ' );
8250             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8251             aLine.append( " l S\n" );
8252         }
8253     }
8254 }
8255 
8256 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
8257 {
8258     // note: units in pFontEntry are ref device pixel
8259     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8260     long			nLineHeight = 0;
8261     long			nLinePos  = 0;
8262     long			nLinePos2 = 0;
8263 
8264     if ( eStrikeout > STRIKEOUT_X )
8265         eStrikeout = STRIKEOUT_SINGLE;
8266 
8267     switch ( eStrikeout )
8268     {
8269         case STRIKEOUT_SINGLE:
8270             if ( !pFontEntry->maMetric.mnStrikeoutSize )
8271                 m_pReferenceDevice->ImplInitTextLineSize();
8272             nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize );
8273             nLinePos    = HCONV( pFontEntry->maMetric.mnStrikeoutOffset );
8274             break;
8275         case STRIKEOUT_BOLD:
8276             if ( !pFontEntry->maMetric.mnBStrikeoutSize )
8277                 m_pReferenceDevice->ImplInitTextLineSize();
8278             nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize );
8279             nLinePos    = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset );
8280             break;
8281         case STRIKEOUT_DOUBLE:
8282             if ( !pFontEntry->maMetric.mnDStrikeoutSize )
8283                 m_pReferenceDevice->ImplInitTextLineSize();
8284             nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize );
8285             nLinePos    = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 );
8286             nLinePos2   = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 );
8287             break;
8288         default:
8289             break;
8290     }
8291 
8292     if ( nLineHeight )
8293     {
8294         m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8295         aLine.append( " w " );
8296         appendStrokingColor( aColor, aLine );
8297         aLine.append( "\n" );
8298 
8299         aLine.append( "0 " );
8300         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8301         aLine.append( " m " );
8302         m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8303         aLine.append( ' ' );
8304         m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8305         aLine.append( " l S\n" );
8306 
8307         if ( eStrikeout == STRIKEOUT_DOUBLE )
8308         {
8309             aLine.append( "0 " );
8310             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8311             aLine.append( " m " );
8312             m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8313             aLine.append( ' ' );
8314             m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8315             aLine.append( " l S\n" );
8316         }
8317     }
8318 }
8319 
8320 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
8321 {
8322     String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" );
8323     String aStrikeout = aStrikeoutChar;
8324     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
8325         aStrikeout.Append( aStrikeout );
8326 
8327     // do not get broader than nWidth modulo 1 character
8328     while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
8329         aStrikeout.Erase( 0, 1 );
8330     aStrikeout.Append( aStrikeoutChar );
8331     sal_Bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
8332     if ( bShadow )
8333     {
8334         Font aFont = m_aCurrentPDFState.m_aFont;
8335         aFont.SetShadow( sal_False );
8336         setFont( aFont );
8337         updateGraphicsState();
8338     }
8339 
8340     // strikeout string is left aligned non-CTL text
8341     sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode();
8342     m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED );
8343     drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false );
8344     m_pReferenceDevice->SetLayoutMode( nOrigTLM );
8345 
8346     if ( bShadow )
8347     {
8348         Font aFont = m_aCurrentPDFState.m_aFont;
8349         aFont.SetShadow( sal_True );
8350         setFont( aFont );
8351         updateGraphicsState();
8352     }
8353 }
8354 
8355 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove )
8356 {
8357     if ( !nWidth ||
8358          ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
8359            ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) &&
8360            ((eOverline  == UNDERLINE_NONE)||(eOverline  == UNDERLINE_DONTKNOW)) ) )
8361         return;
8362 
8363     MARK( "drawTextLine" );
8364     updateGraphicsState();
8365 
8366     // note: units in pFontEntry are ref device pixel
8367     ImplFontEntry*	pFontEntry = m_pReferenceDevice->mpFontEntry;
8368     Color			aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
8369     Color			aOverlineColor  = m_aCurrentPDFState.m_aOverlineColor;
8370     Color			aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
8371     bool			bStrikeoutDone = false;
8372     bool			bUnderlineDone = false;
8373     bool			bOverlineDone  = false;
8374 
8375     if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
8376     {
8377         drawStrikeoutChar( rPos, nWidth, eStrikeout );
8378         bStrikeoutDone = true;
8379     }
8380 
8381     Point aPos( rPos );
8382     TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
8383     if( eAlign == ALIGN_TOP )
8384         aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent );
8385     else if( eAlign == ALIGN_BOTTOM )
8386         aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent );
8387 
8388     OStringBuffer aLine( 512 );
8389     // save GS
8390     aLine.append( "q " );
8391 
8392     // rotate and translate matrix
8393     double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
8394     Matrix3 aMat;
8395     aMat.rotate( fAngle );
8396     aMat.translate( aPos.X(), aPos.Y() );
8397     aMat.append( m_aPages.back(), aLine );
8398     aLine.append( " cm\n" );
8399 
8400     if ( aUnderlineColor.GetTransparency() != 0 )
8401         aUnderlineColor = aStrikeoutColor;
8402 
8403     if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
8404          (eUnderline == UNDERLINE_WAVE) ||
8405          (eUnderline == UNDERLINE_DOUBLEWAVE) ||
8406          (eUnderline == UNDERLINE_BOLDWAVE) )
8407     {
8408         drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8409         bUnderlineDone = true;
8410     }
8411 
8412     if ( (eOverline == UNDERLINE_SMALLWAVE) ||
8413          (eOverline == UNDERLINE_WAVE) ||
8414          (eOverline == UNDERLINE_DOUBLEWAVE) ||
8415          (eOverline == UNDERLINE_BOLDWAVE) )
8416     {
8417         drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8418         bOverlineDone = true;
8419     }
8420 
8421     if ( !bUnderlineDone )
8422     {
8423         drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8424     }
8425 
8426     if ( !bOverlineDone )
8427     {
8428         drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8429     }
8430 
8431     if ( !bStrikeoutDone )
8432     {
8433         drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
8434     }
8435 
8436     aLine.append( "Q\n" );
8437     writeBuffer( aLine.getStr(), aLine.getLength() );
8438 }
8439 
8440 void PDFWriterImpl::drawPolygon( const Polygon& rPoly )
8441 {
8442     MARK( "drawPolygon" );
8443 
8444     updateGraphicsState();
8445 
8446     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8447         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8448         return;
8449 
8450     int nPoints = rPoly.GetSize();
8451     OStringBuffer aLine( 20 * nPoints );
8452     m_aPages.back().appendPolygon( rPoly, aLine );
8453     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8454         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8455         aLine.append( "B*\n" );
8456     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8457         aLine.append( "S\n" );
8458     else
8459         aLine.append( "f*\n" );
8460 
8461     writeBuffer( aLine.getStr(), aLine.getLength() );
8462 }
8463 
8464 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly )
8465 {
8466     MARK( "drawPolyPolygon" );
8467 
8468     updateGraphicsState();
8469 
8470     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8471         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8472         return;
8473 
8474     int nPolygons = rPolyPoly.Count();
8475 
8476     OStringBuffer aLine( 40 * nPolygons );
8477     m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
8478     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8479         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8480         aLine.append( "B*\n" );
8481     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8482         aLine.append( "S\n" );
8483     else
8484         aLine.append( "f*\n" );
8485 
8486     writeBuffer( aLine.getStr(), aLine.getLength() );
8487 }
8488 
8489 void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
8490 {
8491     DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8492     nTransparentPercent = nTransparentPercent % 100;
8493 
8494     MARK( "drawTransparent" );
8495 
8496     updateGraphicsState();
8497 
8498     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8499         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8500         return;
8501 
8502     if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 )
8503     {
8504         m_aErrors.insert( m_bIsPDF_A1 ?
8505                           PDFWriter::Warning_Transparency_Omitted_PDFA :
8506                           PDFWriter::Warning_Transparency_Omitted_PDF13 );
8507 
8508         drawPolyPolygon( rPolyPoly );
8509         return;
8510     }
8511 
8512     // create XObject
8513     m_aTransparentObjects.push_back( TransparencyEmit() );
8514     // FIXME: polygons with beziers may yield incorrect bound rect
8515     m_aTransparentObjects.back().m_aBoundRect	  = rPolyPoly.GetBoundRect();
8516     // convert rectangle to default user space
8517     m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8518     m_aTransparentObjects.back().m_nObject		    = createObject();
8519     m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8520     m_aTransparentObjects.back().m_fAlpha		    = (double)(100-nTransparentPercent) / 100.0;
8521     m_aTransparentObjects.back().m_pContentStream   = new SvMemoryStream( 256, 256 );
8522     // create XObject's content stream
8523     OStringBuffer aContent( 256 );
8524     m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
8525     if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
8526         m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
8527         aContent.append( " B*\n" );
8528     else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
8529         aContent.append( " S\n" );
8530     else
8531         aContent.append( " f*\n" );
8532     m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() );
8533 
8534     OStringBuffer aObjName( 16 );
8535     aObjName.append( "Tr" );
8536     aObjName.append( m_aTransparentObjects.back().m_nObject );
8537     OString aTrName( aObjName.makeStringAndClear() );
8538     aObjName.append( "EGS" );
8539     aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8540     OString aExtName( aObjName.makeStringAndClear() );
8541 
8542     OStringBuffer aLine( 80 );
8543     // insert XObject
8544     aLine.append( "q /" );
8545     aLine.append( aExtName );
8546     aLine.append( " gs /" );
8547     aLine.append( aTrName );
8548     aLine.append( " Do Q\n" );
8549     writeBuffer( aLine.getStr(), aLine.getLength() );
8550 
8551     pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8552     pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8553 }
8554 
8555 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
8556 {
8557     if( nObject >= 0 )
8558     {
8559         switch( eKind )
8560         {
8561             case ResXObject:
8562                 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
8563                 if( ! m_aOutputStreams.empty() )
8564                     m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
8565                 break;
8566             case ResExtGState:
8567                 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
8568                 if( ! m_aOutputStreams.empty() )
8569                     m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
8570                 break;
8571             case ResShading:
8572                 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
8573                 if( ! m_aOutputStreams.empty() )
8574                     m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
8575                 break;
8576             case ResPattern:
8577                 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
8578                 if( ! m_aOutputStreams.empty() )
8579                     m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
8580                 break;
8581         }
8582     }
8583 }
8584 
8585 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect )
8586 {
8587     push( PUSH_ALL );
8588 
8589     // force reemitting clip region
8590     clearClipRegion();
8591     updateGraphicsState();
8592 
8593     m_aOutputStreams.push_front( StreamRedirect() );
8594     m_aOutputStreams.front().m_pStream = pStream;
8595     m_aOutputStreams.front().m_aMapMode = m_aMapMode;
8596 
8597     if( !rTargetRect.IsEmpty() )
8598     {
8599         m_aOutputStreams.front().m_aTargetRect =
8600             lcl_convert( m_aGraphicsStack.front().m_aMapMode,
8601                          m_aMapMode,
8602                          getReferenceDevice(),
8603                          rTargetRect );
8604         Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
8605         long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
8606         aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
8607         m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
8608     }
8609 
8610     // setup graphics state for independent object stream
8611 
8612     // force reemitting colors
8613     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8614     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8615 }
8616 
8617 Rectangle PDFWriterImpl::getRedirectTargetRect() const
8618 {
8619     return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect;
8620 }
8621 
8622 SvStream* PDFWriterImpl::endRedirect()
8623 {
8624     SvStream* pStream = NULL;
8625     if( ! m_aOutputStreams.empty() )
8626     {
8627         pStream		= m_aOutputStreams.front().m_pStream;
8628         m_aMapMode	= m_aOutputStreams.front().m_aMapMode;
8629         m_aOutputStreams.pop_front();
8630     }
8631 
8632     pop();
8633     // force reemitting colors and clip region
8634     clearClipRegion();
8635     m_aCurrentPDFState.m_bClipRegion = m_aGraphicsStack.front().m_bClipRegion;
8636     m_aCurrentPDFState.m_aClipRegion = m_aGraphicsStack.front().m_aClipRegion;
8637     m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8638     m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8639 
8640     updateGraphicsState();
8641 
8642     return pStream;
8643 }
8644 
8645 void PDFWriterImpl::beginTransparencyGroup()
8646 {
8647     updateGraphicsState();
8648     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8649         beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8650 }
8651 
8652 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
8653 {
8654     DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8655     nTransparentPercent = nTransparentPercent % 100;
8656 
8657     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8658     {
8659         // create XObject
8660         m_aTransparentObjects.push_back( TransparencyEmit() );
8661         m_aTransparentObjects.back().m_aBoundRect	= rBoundingBox;
8662         // convert rectangle to default user space
8663         m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8664         m_aTransparentObjects.back().m_nObject		= createObject();
8665         m_aTransparentObjects.back().m_fAlpha		= (double)(100-nTransparentPercent) / 100.0;
8666         // get XObject's content stream
8667         m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8668         m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8669 
8670         OStringBuffer aObjName( 16 );
8671         aObjName.append( "Tr" );
8672         aObjName.append( m_aTransparentObjects.back().m_nObject );
8673         OString aTrName( aObjName.makeStringAndClear() );
8674         aObjName.append( "EGS" );
8675         aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8676         OString aExtName( aObjName.makeStringAndClear() );
8677 
8678         OStringBuffer aLine( 80 );
8679         // insert XObject
8680         aLine.append( "q /" );
8681         aLine.append( aExtName );
8682         aLine.append( " gs /" );
8683         aLine.append( aTrName );
8684         aLine.append( " Do Q\n" );
8685         writeBuffer( aLine.getStr(), aLine.getLength() );
8686 
8687         pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8688         pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8689     }
8690 }
8691 
8692 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask )
8693 {
8694     if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8695     {
8696         // create XObject
8697         m_aTransparentObjects.push_back( TransparencyEmit() );
8698         m_aTransparentObjects.back().m_aBoundRect	= rBoundingBox;
8699         // convert rectangle to default user space
8700         m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8701         m_aTransparentObjects.back().m_nObject		= createObject();
8702         m_aTransparentObjects.back().m_fAlpha		= 0.0;
8703         // get XObject's content stream
8704         m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8705         m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8706 
8707         // draw soft mask
8708         beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8709         drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask );
8710         m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect());
8711 
8712         OStringBuffer aObjName( 16 );
8713         aObjName.append( "Tr" );
8714         aObjName.append( m_aTransparentObjects.back().m_nObject );
8715         OString aTrName( aObjName.makeStringAndClear() );
8716         aObjName.append( "EGS" );
8717         aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8718         OString aExtName( aObjName.makeStringAndClear() );
8719 
8720         OStringBuffer aLine( 80 );
8721         // insert XObject
8722         aLine.append( "q /" );
8723         aLine.append( aExtName );
8724         aLine.append( " gs /" );
8725         aLine.append( aTrName );
8726         aLine.append( " Do Q\n" );
8727         writeBuffer( aLine.getStr(), aLine.getLength() );
8728 
8729         pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8730         pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8731     }
8732 }
8733 
8734 void PDFWriterImpl::drawRectangle( const Rectangle& rRect )
8735 {
8736     MARK( "drawRectangle" );
8737 
8738     updateGraphicsState();
8739 
8740     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8741         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8742         return;
8743 
8744     OStringBuffer aLine( 40 );
8745     m_aPages.back().appendRect( rRect, aLine );
8746 
8747     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8748         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8749         aLine.append( " B*\n" );
8750     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8751         aLine.append( " S\n" );
8752     else
8753         aLine.append( " f*\n" );
8754 
8755     writeBuffer( aLine.getStr(), aLine.getLength() );
8756 }
8757 
8758 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
8759 {
8760     MARK( "drawRectangle with rounded edges" );
8761 
8762     if( !nHorzRound && !nVertRound )
8763         drawRectangle( rRect );
8764 
8765     updateGraphicsState();
8766 
8767     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8768         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8769         return;
8770 
8771     if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
8772         nHorzRound = rRect.GetWidth()/2;
8773     if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
8774         nVertRound = rRect.GetHeight()/2;
8775 
8776     Point aPoints[16];
8777     const double kappa = 0.5522847498;
8778     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
8779     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
8780 
8781     aPoints[1]  = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
8782     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8783     aPoints[2]  = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
8784     aPoints[3]  = Point( aPoints[2].X()+kx, aPoints[2].Y() );
8785 
8786     aPoints[5]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
8787     aPoints[4]  = Point( aPoints[5].X(), aPoints[5].Y()-ky );
8788     aPoints[6]  = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
8789     aPoints[7]  = Point( aPoints[6].X(), aPoints[6].Y()+ky );
8790 
8791     aPoints[9]  = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
8792     aPoints[8]  = Point( aPoints[9].X()+kx, aPoints[9].Y() );
8793     aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
8794     aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
8795 
8796     aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
8797     aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
8798     aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
8799     aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
8800 
8801 
8802     OStringBuffer aLine( 80 );
8803     m_aPages.back().appendPoint( aPoints[1], aLine );
8804     aLine.append( " m " );
8805     m_aPages.back().appendPoint( aPoints[2], aLine );
8806     aLine.append( " l " );
8807     m_aPages.back().appendPoint( aPoints[3], aLine );
8808     aLine.append( ' ' );
8809     m_aPages.back().appendPoint( aPoints[4], aLine );
8810     aLine.append( ' ' );
8811     m_aPages.back().appendPoint( aPoints[5], aLine );
8812     aLine.append( " c\n" );
8813     m_aPages.back().appendPoint( aPoints[6], aLine );
8814     aLine.append( " l " );
8815     m_aPages.back().appendPoint( aPoints[7], aLine );
8816     aLine.append( ' ' );
8817     m_aPages.back().appendPoint( aPoints[8], aLine );
8818     aLine.append( ' ' );
8819     m_aPages.back().appendPoint( aPoints[9], aLine );
8820     aLine.append( " c\n" );
8821     m_aPages.back().appendPoint( aPoints[10], aLine );
8822     aLine.append( " l " );
8823     m_aPages.back().appendPoint( aPoints[11], aLine );
8824     aLine.append( ' ' );
8825     m_aPages.back().appendPoint( aPoints[12], aLine );
8826     aLine.append( ' ' );
8827     m_aPages.back().appendPoint( aPoints[13], aLine );
8828     aLine.append( " c\n" );
8829     m_aPages.back().appendPoint( aPoints[14], aLine );
8830     aLine.append( " l " );
8831     m_aPages.back().appendPoint( aPoints[15], aLine );
8832     aLine.append( ' ' );
8833     m_aPages.back().appendPoint( aPoints[0], aLine );
8834     aLine.append( ' ' );
8835     m_aPages.back().appendPoint( aPoints[1], aLine );
8836     aLine.append( " c " );
8837 
8838     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8839         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8840         aLine.append( "b*\n" );
8841     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8842         aLine.append( "s\n" );
8843     else
8844         aLine.append( "f*\n" );
8845 
8846     writeBuffer( aLine.getStr(), aLine.getLength() );
8847 }
8848 
8849 void PDFWriterImpl::drawEllipse( const Rectangle& rRect )
8850 {
8851     MARK( "drawEllipse" );
8852 
8853     updateGraphicsState();
8854 
8855     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8856         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8857         return;
8858 
8859     Point aPoints[12];
8860     const double kappa = 0.5522847498;
8861     const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
8862     const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
8863 
8864     aPoints[1]  = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
8865     aPoints[0]  = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8866     aPoints[2]  = Point( aPoints[1].X() + kx, aPoints[1].Y() );
8867 
8868     aPoints[4]  = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
8869     aPoints[3]  = Point( aPoints[4].X(), aPoints[4].Y() - ky );
8870     aPoints[5]  = Point( aPoints[4].X(), aPoints[4].Y() + ky );
8871 
8872     aPoints[7]  = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
8873     aPoints[6]  = Point( aPoints[7].X() + kx, aPoints[7].Y() );
8874     aPoints[8]  = Point( aPoints[7].X() - kx, aPoints[7].Y() );
8875 
8876     aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
8877     aPoints[9]  = Point( aPoints[10].X(), aPoints[10].Y() + ky );
8878     aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
8879 
8880     OStringBuffer aLine( 80 );
8881     m_aPages.back().appendPoint( aPoints[1], aLine );
8882     aLine.append( " m " );
8883     m_aPages.back().appendPoint( aPoints[2], aLine );
8884     aLine.append( ' ' );
8885     m_aPages.back().appendPoint( aPoints[3], aLine );
8886     aLine.append( ' ' );
8887     m_aPages.back().appendPoint( aPoints[4], aLine );
8888     aLine.append( " c\n" );
8889     m_aPages.back().appendPoint( aPoints[5], aLine );
8890     aLine.append( ' ' );
8891     m_aPages.back().appendPoint( aPoints[6], aLine );
8892     aLine.append( ' ' );
8893     m_aPages.back().appendPoint( aPoints[7], aLine );
8894     aLine.append( " c\n" );
8895     m_aPages.back().appendPoint( aPoints[8], aLine );
8896     aLine.append( ' ' );
8897     m_aPages.back().appendPoint( aPoints[9], aLine );
8898     aLine.append( ' ' );
8899     m_aPages.back().appendPoint( aPoints[10], aLine );
8900     aLine.append( " c\n" );
8901     m_aPages.back().appendPoint( aPoints[11], aLine );
8902     aLine.append( ' ' );
8903     m_aPages.back().appendPoint( aPoints[0], aLine );
8904     aLine.append( ' ' );
8905     m_aPages.back().appendPoint( aPoints[1], aLine );
8906     aLine.append( " c " );
8907 
8908     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8909         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8910         aLine.append( "b*\n" );
8911     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8912         aLine.append( "s\n" );
8913     else
8914         aLine.append( "f*\n" );
8915 
8916     writeBuffer( aLine.getStr(), aLine.getLength() );
8917 }
8918 
8919 static double calcAngle( const Rectangle& rRect, const Point& rPoint )
8920 {
8921     Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
8922                   (rRect.Top()+rRect.Bottom()+1)/2);
8923     Point aPoint = rPoint - aOrigin;
8924 
8925     double fX = (double)aPoint.X();
8926     double fY = (double)-aPoint.Y();
8927 
8928     if( rRect.GetWidth() > rRect.GetHeight() )
8929         fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
8930     else if( rRect.GetHeight() > rRect.GetWidth() )
8931         fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
8932     return atan2( fY, fX );
8933 }
8934 
8935 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
8936 {
8937     MARK( "drawArc" );
8938 
8939     updateGraphicsState();
8940 
8941     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8942         m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8943         return;
8944 
8945     // calculate start and stop angles
8946     const double fStartAngle = calcAngle( rRect, rStart );
8947     double fStopAngle  = calcAngle( rRect, rStop );
8948     while( fStopAngle < fStartAngle )
8949         fStopAngle += 2.0*M_PI;
8950     const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
8951     const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
8952     const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
8953     const double halfWidth = (double)rRect.GetWidth()/2.0;
8954     const double halfHeight = (double)rRect.GetHeight()/2.0;
8955 
8956     const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
8957                          (rRect.Top()+rRect.Bottom()+1)/2 );
8958 
8959     OStringBuffer aLine( 30*nFragments );
8960     Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
8961                   -(int)(halfHeight * sin(fStartAngle) ) );
8962     aPoint += aCenter;
8963     m_aPages.back().appendPoint( aPoint, aLine );
8964     aLine.append( " m " );
8965     if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
8966     {
8967         for( int i = 0; i < nFragments; i++ )
8968         {
8969             const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
8970             const double fStopFragment = fStartFragment + fFragmentDelta;
8971             aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
8972                             -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
8973             aPoint += aCenter;
8974             m_aPages.back().appendPoint( aPoint, aLine );
8975             aLine.append( ' ' );
8976 
8977             aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
8978                             -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
8979             aPoint += aCenter;
8980             m_aPages.back().appendPoint( aPoint, aLine );
8981             aLine.append( ' ' );
8982 
8983             aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
8984                             -(int)(halfHeight * sin(fStopFragment) ) );
8985             aPoint += aCenter;
8986             m_aPages.back().appendPoint( aPoint, aLine );
8987             aLine.append( " c\n" );
8988         }
8989     }
8990     if( bWithChord || bWithPie )
8991     {
8992         if( bWithPie )
8993         {
8994             m_aPages.back().appendPoint( aCenter, aLine );
8995             aLine.append( " l " );
8996         }
8997         aLine.append( "h " );
8998     }
8999     if( ! bWithChord && ! bWithPie )
9000         aLine.append( "S\n" );
9001     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
9002         m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
9003         aLine.append( "B*\n" );
9004     else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9005         aLine.append( "S\n" );
9006     else
9007         aLine.append( "f*\n" );
9008 
9009     writeBuffer( aLine.getStr(), aLine.getLength() );
9010 }
9011 
9012 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly )
9013 {
9014     MARK( "drawPolyLine" );
9015 
9016     sal_uInt16 nPoints = rPoly.GetSize();
9017     if( nPoints < 2 )
9018         return;
9019 
9020     updateGraphicsState();
9021 
9022     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9023         return;
9024 
9025     OStringBuffer aLine( 20 * nPoints );
9026     m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
9027     aLine.append( "S\n" );
9028 
9029     writeBuffer( aLine.getStr(), aLine.getLength() );
9030 }
9031 
9032 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo )
9033 {
9034     MARK( "drawPolyLine with LineInfo" );
9035 
9036     updateGraphicsState();
9037 
9038     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9039         return;
9040 
9041     OStringBuffer aLine;
9042     aLine.append( "q " );
9043     if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
9044     {
9045         writeBuffer( aLine.getStr(), aLine.getLength() );
9046         drawPolyLine( rPoly );
9047         writeBuffer( "Q\n", 2 );
9048     }
9049     else
9050     {
9051         PDFWriter::ExtLineInfo aInfo;
9052         convertLineInfoToExtLineInfo( rInfo, aInfo );
9053         drawPolyLine( rPoly, aInfo );
9054     }
9055 }
9056 
9057 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
9058 {
9059     DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" );
9060     rOut.m_fLineWidth           = rIn.GetWidth();
9061     rOut.m_fTransparency        = 0.0;
9062     rOut.m_eCap                 = PDFWriter::capButt;
9063     rOut.m_eJoin                = PDFWriter::joinMiter;
9064     rOut.m_fMiterLimit          = 10;
9065     rOut.m_aDashArray.clear();
9066 
9067     // add DashDot to DashArray
9068     const int nDashes     = rIn.GetDashCount();
9069     const int nDashLen    = rIn.GetDashLen();
9070     const int nDistance   = rIn.GetDistance();
9071 
9072     for( int n  = 0; n < nDashes; n++ )
9073     {
9074         rOut.m_aDashArray.push_back( nDashLen );
9075         rOut.m_aDashArray.push_back( nDistance );
9076     }
9077 
9078     const int nDots       = rIn.GetDotCount();
9079     const int nDotLen     = rIn.GetDotLen();
9080 
9081     for( int n  = 0; n < nDots; n++ )
9082     {
9083         rOut.m_aDashArray.push_back( nDotLen );
9084         rOut.m_aDashArray.push_back( nDistance );
9085     }
9086 
9087     // add LineJoin
9088     switch(rIn.GetLineJoin())
9089     {
9090         case basegfx::B2DLINEJOIN_BEVEL :
9091         {
9092             rOut.m_eJoin = PDFWriter::joinBevel;
9093             break;
9094         }
9095         default : // basegfx::B2DLINEJOIN_NONE :
9096         // Pdf has no 'none' lineJoin, default is miter
9097         case basegfx::B2DLINEJOIN_MIDDLE :
9098         case basegfx::B2DLINEJOIN_MITER :
9099         {
9100             rOut.m_eJoin = PDFWriter::joinMiter;
9101             break;
9102         }
9103         case basegfx::B2DLINEJOIN_ROUND :
9104         {
9105             rOut.m_eJoin = PDFWriter::joinRound;
9106             break;
9107         }
9108     }
9109 
9110     // add LineCap
9111     switch(rIn.GetLineCap())
9112     {
9113         default: /* com::sun::star::drawing::LineCap_BUTT */
9114         {
9115             rOut.m_eCap = PDFWriter::capButt;
9116             break;
9117         }
9118         case com::sun::star::drawing::LineCap_ROUND:
9119         {
9120             rOut.m_eCap = PDFWriter::capRound;
9121             break;
9122         }
9123         case com::sun::star::drawing::LineCap_SQUARE:
9124         {
9125             rOut.m_eCap = PDFWriter::capSquare;
9126             break;
9127         }
9128     }
9129 }
9130 
9131 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
9132 {
9133     MARK( "drawPolyLine with ExtLineInfo" );
9134 
9135     updateGraphicsState();
9136 
9137     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9138         return;
9139 
9140     if( rInfo.m_fTransparency >= 1.0 )
9141         return;
9142 
9143     if( rInfo.m_fTransparency != 0.0 )
9144         beginTransparencyGroup();
9145 
9146     OStringBuffer aLine;
9147     aLine.append( "q " );
9148     m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
9149     aLine.append( " w" );
9150     if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader
9151     {
9152         switch( rInfo.m_eCap )
9153         {
9154             default:
9155             case PDFWriter::capButt:   aLine.append( " 0 J" );break;
9156             case PDFWriter::capRound:  aLine.append( " 1 J" );break;
9157             case PDFWriter::capSquare: aLine.append( " 2 J" );break;
9158         }
9159         switch( rInfo.m_eJoin )
9160         {
9161             default:
9162             case PDFWriter::joinMiter:
9163             {
9164                 double fLimit = rInfo.m_fMiterLimit;
9165                 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
9166                     fLimit = fLimit / rInfo.m_fLineWidth;
9167                 if( fLimit < 1.0 )
9168                     fLimit = 1.0;
9169                 aLine.append( " 0 j " );
9170                 appendDouble( fLimit, aLine );
9171                 aLine.append( " M" );
9172             }
9173             break;
9174             case PDFWriter::joinRound:  aLine.append( " 1 j" );break;
9175             case PDFWriter::joinBevel:  aLine.append( " 2 j" );break;
9176         }
9177         if( rInfo.m_aDashArray.size() > 0 )
9178         {
9179             aLine.append( " [ " );
9180             for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
9181                  it != rInfo.m_aDashArray.end(); ++it )
9182             {
9183                 m_aPages.back().appendMappedLength( *it, aLine );
9184                 aLine.append( ' ' );
9185             }
9186             aLine.append( "] 0 d" );
9187         }
9188         aLine.append( "\n" );
9189         writeBuffer( aLine.getStr(), aLine.getLength() );
9190         drawPolyLine( rPoly );
9191     }
9192     else
9193     {
9194         basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
9195         basegfx::B2DPolyPolygon aPolyPoly;
9196 
9197 		basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
9198 
9199 		// Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
9200 		// To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
9201 		// this line needs to be removed and the loop below adapted accordingly
9202 		aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
9203 
9204 		const sal_uInt32 nPolygonCount(aPolyPoly.count());
9205 
9206 		for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
9207         {
9208             aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
9209             aPoly = aPolyPoly.getB2DPolygon( nPoly );
9210 			const sal_uInt32 nPointCount(aPoly.count());
9211 
9212 			if(nPointCount)
9213 			{
9214 				const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
9215 				basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
9216 
9217 				for(sal_uInt32 a(0); a < nEdgeCount; a++)
9218 				{
9219                     if( a > 0 )
9220                         aLine.append( " " );
9221 					const sal_uInt32 nNextIndex((a + 1) % nPointCount);
9222 					const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
9223 
9224 					m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
9225 														FRound(aCurrent.getY()) ),
9226 												 aLine );
9227 					aLine.append( " m " );
9228 					m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
9229 														FRound(aNext.getY()) ),
9230 												 aLine );
9231 					aLine.append( " l" );
9232 
9233 					// prepare next edge
9234 					aCurrent = aNext;
9235 				}
9236 			}
9237         }
9238         aLine.append( " S " );
9239         writeBuffer( aLine.getStr(), aLine.getLength() );
9240     }
9241     writeBuffer( "Q\n", 2 );
9242 
9243     if( rInfo.m_fTransparency != 0.0 )
9244     {
9245         // FIXME: actually this may be incorrect with bezier polygons
9246         Rectangle aBoundRect( rPoly.GetBoundRect() );
9247         // avoid clipping with thick lines
9248         if( rInfo.m_fLineWidth > 0.0 )
9249         {
9250             sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
9251             aBoundRect.Top()    -= nLW;
9252             aBoundRect.Left()   -= nLW;
9253             aBoundRect.Right()  += nLW;
9254             aBoundRect.Bottom() += nLW;
9255         }
9256         endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) );
9257     }
9258 }
9259 
9260 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
9261 {
9262     MARK( "drawPixel" );
9263 
9264     Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
9265 
9266     if( aColor == Color( COL_TRANSPARENT ) )
9267         return;
9268 
9269     // pixels are drawn in line color, so have to set
9270     // the nonstroking color to line color
9271     Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
9272     setFillColor( aColor );
9273 
9274     updateGraphicsState();
9275 
9276     OStringBuffer aLine( 20 );
9277     m_aPages.back().appendPoint( rPoint, aLine );
9278     aLine.append( ' ' );
9279     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine );
9280     aLine.append( ' ' );
9281     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine );
9282     aLine.append( " re f\n" );
9283     writeBuffer( aLine.getStr(), aLine.getLength() );
9284 
9285     setFillColor( aOldFillColor );
9286 }
9287 
9288 void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors )
9289 {
9290     MARK( "drawPixel with Polygon" );
9291 
9292     updateGraphicsState();
9293 
9294     if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors )
9295         return;
9296 
9297     sal_uInt16 nPoints = rPoints.GetSize();
9298     OStringBuffer aLine( nPoints*40 );
9299     aLine.append( "q " );
9300     if( ! pColors )
9301     {
9302         appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine );
9303         aLine.append( ' ' );
9304     }
9305 
9306     OStringBuffer aPixel(32);
9307     aPixel.append( ' ' );
9308     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel );
9309     aPixel.append( ' ' );
9310     appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel );
9311     OString aPixelStr = aPixel.makeStringAndClear();
9312     for( sal_uInt16 i = 0; i < nPoints; i++ )
9313     {
9314         if( pColors )
9315         {
9316             if( pColors[i] == Color( COL_TRANSPARENT ) )
9317                 continue;
9318 
9319             appendNonStrokingColor( pColors[i], aLine );
9320             aLine.append( ' ' );
9321         }
9322         m_aPages.back().appendPoint( rPoints[i], aLine );
9323         aLine.append( aPixelStr );
9324         aLine.append( " re f\n" );
9325     }
9326     aLine.append( "Q\n" );
9327     writeBuffer( aLine.getStr(), aLine.getLength() );
9328 }
9329 
9330 class AccessReleaser
9331 {
9332     BitmapReadAccess* m_pAccess;
9333 public:
9334     AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){}
9335     ~AccessReleaser() { delete m_pAccess; }
9336 };
9337 
9338 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
9339 {
9340     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9341 
9342     bool bFlateFilter = compressStream( rObject.m_pContentStream );
9343     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
9344     sal_uLong nSize = rObject.m_pContentStream->Tell();
9345     rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
9346     #if OSL_DEBUG_LEVEL > 1
9347     emitComment( "PDFWriterImpl::writeTransparentObject" );
9348     #endif
9349     OStringBuffer aLine( 512 );
9350     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9351     aLine.append( rObject.m_nObject );
9352     aLine.append( " 0 obj\n"
9353                   "<</Type/XObject\n"
9354                   "/Subtype/Form\n"
9355                   "/BBox[ " );
9356     appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
9357     aLine.append( ' ' );
9358     appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
9359     aLine.append( ' ' );
9360     appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
9361     aLine.append( ' ' );
9362     appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
9363     aLine.append( " ]\n" );
9364     if( ! rObject.m_pSoftMaskStream )
9365     {
9366         if( ! m_bIsPDF_A1 )
9367         {
9368             aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
9369         }
9370     }
9371     /* #i42884# the PDF reference recommends that each Form XObject
9372     *  should have a resource dict; alas if that is the same object
9373     *  as the one of the page it triggers an endless recursion in
9374     *  acroread 5 (6 and up have that fixed). Since we have only one
9375     *  resource dict anyway, let's use the one from the page by NOT
9376     *  emitting a Resources entry.
9377     */
9378     #if 0
9379     aLine.append( "   /Resources " );
9380     aLine.append( getResourceDictObj() );
9381     aLine.append( " 0 R\n" );
9382     #endif
9383 
9384     aLine.append( "/Length " );
9385     aLine.append( (sal_Int32)(nSize) );
9386     aLine.append( "\n" );
9387     if( bFlateFilter )
9388         aLine.append( "/Filter/FlateDecode\n" );
9389     aLine.append( ">>\n"
9390                   "stream\n" );
9391     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9392     checkAndEnableStreamEncryption( rObject.m_nObject );
9393     CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
9394     disableStreamEncryption();
9395     aLine.setLength( 0 );
9396     aLine.append( "\n"
9397                   "endstream\n"
9398                   "endobj\n\n" );
9399     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9400 
9401     // write ExtGState dict for this XObject
9402     aLine.setLength( 0 );
9403     aLine.append( rObject.m_nExtGStateObject );
9404     aLine.append( " 0 obj\n"
9405                   "<<" );
9406     if( ! rObject.m_pSoftMaskStream )
9407     {
9408 //i59651
9409         if( m_bIsPDF_A1 )
9410         {
9411             aLine.append( "/CA 1.0/ca 1.0" );
9412             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9413         }
9414         else
9415         {
9416             aLine.append(  "/CA " );
9417             appendDouble( rObject.m_fAlpha, aLine );
9418             aLine.append( "\n"
9419                           "   /ca " );
9420             appendDouble( rObject.m_fAlpha, aLine );
9421         }
9422         aLine.append( "\n" );
9423     }
9424     else
9425     {
9426         if( m_bIsPDF_A1 )
9427         {
9428             aLine.append( "/SMask/None" );
9429             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9430         }
9431         else
9432         {
9433             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
9434             sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
9435             rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
9436             sal_Int32 nMaskObject = createObject();
9437             aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
9438             aLine.append( nMaskObject );
9439             aLine.append( " 0 R>>\n" );
9440 
9441             OStringBuffer aMask;
9442             aMask.append( nMaskObject );
9443             aMask.append( " 0 obj\n"
9444                           "<</Type/XObject\n"
9445                           "/Subtype/Form\n"
9446                           "/BBox[" );
9447             appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
9448             aMask.append( ' ' );
9449             appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
9450             aMask.append( ' ' );
9451             appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
9452             aMask.append( ' ' );
9453             appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
9454             aMask.append( "]\n" );
9455 
9456             /* #i42884# see above */
9457 #if 0
9458             aLine.append( "/Resources " );
9459             aMask.append( getResourceDictObj() );
9460             aMask.append( " 0 R\n" );
9461 #endif
9462 
9463             aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
9464             aMask.append( "/Length " );
9465             aMask.append( nMaskSize );
9466             aMask.append( ">>\n"
9467                           "stream\n" );
9468             CHECK_RETURN( updateObject( nMaskObject ) );
9469             checkAndEnableStreamEncryption(  nMaskObject );
9470             CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9471             CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
9472             disableStreamEncryption();
9473             aMask.setLength( 0 );
9474             aMask.append( "\nendstream\n"
9475                           "endobj\n\n" );
9476             CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9477         }
9478     }
9479     aLine.append( ">>\n"
9480                   "endobj\n\n" );
9481     CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) );
9482     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9483 
9484     return true;
9485 }
9486 
9487 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
9488 {
9489     sal_Int32 nFunctionObject = createObject();
9490     CHECK_RETURN( updateObject( nFunctionObject ) );
9491 
9492     VirtualDevice aDev;
9493     aDev.SetOutputSizePixel( rObject.m_aSize );
9494     aDev.SetMapMode( MapMode( MAP_PIXEL ) );
9495     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9496         aDev.SetDrawMode( aDev.GetDrawMode() |
9497                           ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT |
9498                             DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) );
9499     aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
9500 
9501     Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize );
9502     BitmapReadAccess* pAccess = aSample.AcquireReadAccess();
9503     AccessReleaser aReleaser( pAccess );
9504 
9505     Size aSize = aSample.GetSizePixel();
9506 
9507     sal_Int32 nStreamLengthObject = createObject();
9508     #if OSL_DEBUG_LEVEL > 1
9509     emitComment( "PDFWriterImpl::writeGradientFunction" );
9510     #endif
9511     OStringBuffer aLine( 120 );
9512     aLine.append( nFunctionObject );
9513     aLine.append( " 0 obj\n"
9514                   "<</FunctionType 0\n"
9515                   "/Domain[ 0 1 0 1 ]\n"
9516                   "/Size[ " );
9517     aLine.append( (sal_Int32)aSize.Width() );
9518     aLine.append( ' ' );
9519     aLine.append( (sal_Int32)aSize.Height() );
9520     aLine.append( " ]\n"
9521                   "/BitsPerSample 8\n"
9522                   "/Range[ 0 1 0 1 0 1 ]\n"
9523                   "/Order 3\n"
9524                   "/Length " );
9525     aLine.append( nStreamLengthObject );
9526     aLine.append( " 0 R\n"
9527 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9528                   "/Filter/FlateDecode"
9529 #endif
9530                   ">>\n"
9531                   "stream\n" );
9532     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9533 
9534     sal_uInt64 nStartStreamPos = 0;
9535     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) );
9536 
9537     checkAndEnableStreamEncryption( nFunctionObject );
9538     beginCompression();
9539     for( int y = aSize.Height()-1; y >= 0; y-- )
9540     {
9541         for( int x = 0; x < aSize.Width(); x++ )
9542         {
9543             sal_uInt8 aCol[3];
9544             BitmapColor aColor = pAccess->GetColor( y, x );
9545             aCol[0] = aColor.GetRed();
9546             aCol[1] = aColor.GetGreen();
9547             aCol[2] = aColor.GetBlue();
9548             CHECK_RETURN( writeBuffer( aCol, 3 ) );
9549         }
9550     }
9551     endCompression();
9552     disableStreamEncryption();
9553 
9554     sal_uInt64 nEndStreamPos = 0;
9555     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) );
9556 
9557     aLine.setLength( 0 );
9558     aLine.append( "\nendstream\nendobj\n\n" );
9559     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9560 
9561     // write stream length
9562     CHECK_RETURN( updateObject( nStreamLengthObject ) );
9563     aLine.setLength( 0 );
9564     aLine.append( nStreamLengthObject );
9565     aLine.append( " 0 obj\n" );
9566     aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
9567     aLine.append( "\nendobj\n\n" );
9568     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9569 
9570     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9571     aLine.setLength( 0 );
9572     aLine.append( rObject.m_nObject );
9573     aLine.append( " 0 obj\n"
9574                   "<</ShadingType 1\n"
9575                   "/ColorSpace/DeviceRGB\n"
9576                   "/AntiAlias true\n"
9577                   "/Domain[ 0 1 0 1 ]\n"
9578                   "/Matrix[ " );
9579     aLine.append( (sal_Int32)aSize.Width() );
9580     aLine.append( " 0 0 " );
9581     aLine.append( (sal_Int32)aSize.Height() );
9582     aLine.append( " 0 0 ]\n"
9583                   "/Function " );
9584     aLine.append( nFunctionObject );
9585     aLine.append( " 0 R\n"
9586                   ">>\n"
9587                   "endobj\n\n" );
9588     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9589 
9590     return true;
9591 }
9592 
9593 bool PDFWriterImpl::writeJPG( JPGEmit& rObject )
9594 {
9595     CHECK_RETURN( rObject.m_pStream );
9596     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9597 
9598     sal_Int32 nLength = 0;
9599     rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
9600     nLength = rObject.m_pStream->Tell();
9601     rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
9602 
9603     sal_Int32 nMaskObject = 0;
9604     if( !!rObject.m_aMask )
9605     {
9606         if( rObject.m_aMask.GetBitCount() == 1 ||
9607             ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651
9608             )
9609         {
9610             nMaskObject = createObject();
9611         }
9612         else if( m_bIsPDF_A1 )
9613             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9614         else if( m_aContext.Version < PDFWriter::PDF_1_4 )
9615             m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
9616 
9617     }
9618     #if OSL_DEBUG_LEVEL > 1
9619     emitComment( "PDFWriterImpl::writeJPG" );
9620     #endif
9621 
9622     OStringBuffer aLine(200);
9623     aLine.append( rObject.m_nObject );
9624     aLine.append( " 0 obj\n"
9625                   "<</Type/XObject/Subtype/Image/Width " );
9626     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
9627     aLine.append( " /Height " );
9628     aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
9629     aLine.append( " /BitsPerComponent 8 " );
9630     if( rObject.m_bTrueColor )
9631         aLine.append( "/ColorSpace/DeviceRGB" );
9632     else
9633         aLine.append( "/ColorSpace/DeviceGray" );
9634     aLine.append( "/Filter/DCTDecode/Length " );
9635     aLine.append( nLength );
9636     if( nMaskObject )
9637     {
9638         aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
9639         aLine.append( nMaskObject );
9640         aLine.append( " 0 R " );
9641     }
9642     aLine.append( ">>\nstream\n" );
9643     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9644 
9645     checkAndEnableStreamEncryption( rObject.m_nObject );
9646     CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
9647     disableStreamEncryption();
9648 
9649     aLine.setLength( 0 );
9650     aLine.append( "\nendstream\nendobj\n\n" );
9651     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9652 
9653     if( nMaskObject )
9654     {
9655         BitmapEmit aEmit;
9656         aEmit.m_nObject = nMaskObject;
9657         if( rObject.m_aMask.GetBitCount() == 1 )
9658             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
9659         else if( rObject.m_aMask.GetBitCount() == 8 )
9660             aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
9661         writeBitmapObject( aEmit, true );
9662     }
9663 
9664     return true;
9665 }
9666 
9667 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
9668 {
9669     CHECK_RETURN( updateObject( rObject.m_nObject ) );
9670 
9671     Bitmap	aBitmap;
9672     Color	aTransparentColor( COL_TRANSPARENT );
9673     bool	bWriteMask = false;
9674     if( ! bMask )
9675     {
9676         aBitmap = rObject.m_aBitmap.GetBitmap();
9677         if( rObject.m_aBitmap.IsAlpha() )
9678         {
9679             if( m_aContext.Version >= PDFWriter::PDF_1_4 )
9680                 bWriteMask = true;
9681             // else draw without alpha channel
9682         }
9683         else
9684         {
9685             switch( rObject.m_aBitmap.GetTransparentType() )
9686             {
9687                 case TRANSPARENT_NONE:
9688                     // comes from drawMask function
9689                     if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask )
9690                         bMask = true;
9691                     break;
9692                 case TRANSPARENT_COLOR:
9693                     aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
9694                     break;
9695                 case TRANSPARENT_BITMAP:
9696                     bWriteMask = true;
9697                     break;
9698             }
9699         }
9700     }
9701     else
9702     {
9703         if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
9704         {
9705             aBitmap = rObject.m_aBitmap.GetMask();
9706             aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
9707             DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
9708         }
9709         else if( aBitmap.GetBitCount() != 8 )
9710         {
9711             aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap();
9712             aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
9713             DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" );
9714         }
9715     }
9716 
9717     BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();
9718     AccessReleaser aReleaser( pAccess );
9719 
9720     bool bTrueColor;
9721     sal_Int32 nBitsPerComponent;
9722     switch( aBitmap.GetBitCount() )
9723     {
9724         case 1:
9725         case 2:
9726         case 4:
9727         case 8:
9728             bTrueColor = false;
9729             nBitsPerComponent = aBitmap.GetBitCount();
9730             break;
9731         default:
9732             bTrueColor = true;
9733             nBitsPerComponent = 8;
9734             break;
9735     }
9736 
9737     sal_Int32 nStreamLengthObject	= createObject();
9738     sal_Int32 nMaskObject			= 0;
9739 
9740     #if OSL_DEBUG_LEVEL > 1
9741     emitComment( "PDFWriterImpl::writeBitmapObject" );
9742     #endif
9743     OStringBuffer aLine(1024);
9744     aLine.append( rObject.m_nObject );
9745     aLine.append( " 0 obj\n"
9746                   "<</Type/XObject/Subtype/Image/Width " );
9747     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9748     aLine.append( "/Height " );
9749     aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
9750     aLine.append( "/BitsPerComponent " );
9751     aLine.append( nBitsPerComponent );
9752     aLine.append( "/Length " );
9753     aLine.append( nStreamLengthObject );
9754     aLine.append( " 0 R\n" );
9755 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9756     if( nBitsPerComponent != 1 )
9757     {
9758         aLine.append( "/Filter/FlateDecode" );
9759     }
9760     else
9761     {
9762         aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
9763         aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9764         aLine.append( ">>\n" );
9765     }
9766 #endif
9767     if( ! bMask )
9768     {
9769         aLine.append( "/ColorSpace" );
9770         if( bTrueColor )
9771             aLine.append( "/DeviceRGB\n" );
9772         else if( aBitmap.HasGreyPalette() )
9773         {
9774             aLine.append( "/DeviceGray\n" );
9775             if( aBitmap.GetBitCount() == 1 )
9776             {
9777                 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
9778                 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9779                 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9780                 if( nBlackIndex == 1 )
9781                     aLine.append( "/Decode[1 0]\n" );
9782             }
9783         }
9784         else
9785         {
9786             aLine.append( "[ /Indexed/DeviceRGB " );
9787             aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
9788             aLine.append( "\n<" );
9789 			if( m_aContext.Encryption.Encrypt() )
9790 			{
9791 				enableStringEncryption( rObject.m_nObject );
9792 				//check encryption buffer size
9793 				if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
9794 				{
9795 					int	nChar = 0;
9796 					//fill the encryption buffer
9797 					for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9798 					{
9799 						const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9800 						m_pEncryptionBuffer[nChar++] = rColor.GetRed();
9801 						m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
9802 						m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
9803 					}
9804 					//encrypt the colorspace lookup table
9805 					rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
9806 					//now queue the data for output
9807                     nChar = 0;
9808 					for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9809 					{
9810 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9811 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9812 						appendHex(m_pEncryptionBuffer[nChar++], aLine );
9813 					}
9814 				}
9815 			}
9816 			else //no encryption requested (PDF/A-1a program flow drops here)
9817 			{
9818 				for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9819 				{
9820 					const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9821 					appendHex( rColor.GetRed(), aLine );
9822 					appendHex( rColor.GetGreen(), aLine );
9823 					appendHex( rColor.GetBlue(), aLine );
9824 				}
9825 			}
9826             aLine.append( ">\n]\n" );
9827         }
9828     }
9829     else
9830     {
9831         if( aBitmap.GetBitCount() == 1 )
9832         {
9833             aLine.append( "/ImageMask true\n" );
9834             sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9835             DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9836             if( nBlackIndex )
9837                 aLine.append( "/Decode[ 1 0 ]\n" );
9838             else
9839                 aLine.append( "/Decode[ 0 1 ]\n" );
9840         }
9841         else if( aBitmap.GetBitCount() == 8 )
9842         {
9843             aLine.append( "/ColorSpace/DeviceGray\n"
9844                           "/Decode [ 1 0 ]\n" );
9845         }
9846     }
9847 
9848     if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651
9849     {
9850         if( bWriteMask )
9851         {
9852             nMaskObject = createObject();
9853             if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 )
9854                 aLine.append( "/SMask " );
9855             else
9856                 aLine.append( "/Mask " );
9857             aLine.append( nMaskObject );
9858             aLine.append( " 0 R\n" );
9859         }
9860         else if( aTransparentColor != Color( COL_TRANSPARENT ) )
9861         {
9862             aLine.append( "/Mask[ " );
9863             if( bTrueColor )
9864             {
9865                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9866                 aLine.append( ' ' );
9867                 aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9868                 aLine.append( ' ' );
9869                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9870                 aLine.append( ' ' );
9871                 aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9872                 aLine.append( ' ' );
9873                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9874                 aLine.append( ' ' );
9875                 aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9876             }
9877             else
9878             {
9879                 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
9880                 aLine.append( nIndex );
9881             }
9882             aLine.append( " ]\n" );
9883         }
9884     }
9885     else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) )
9886         m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9887 
9888     aLine.append( ">>\n"
9889                   "stream\n" );
9890     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9891     sal_uInt64 nStartPos = 0;
9892     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) );
9893 
9894     checkAndEnableStreamEncryption( rObject.m_nObject );
9895 #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9896     if( nBitsPerComponent == 1 )
9897     {
9898         writeG4Stream( pAccess );
9899     }
9900     else
9901 #endif
9902     {
9903         beginCompression();
9904         if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
9905         {
9906             const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U );
9907 
9908             for( int i = 0; i < pAccess->Height(); i++ )
9909             {
9910                 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
9911             }
9912         }
9913         else
9914         {
9915             const int nScanLineBytes = pAccess->Width()*3;
9916             boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] );
9917             for( int y = 0; y < pAccess->Height(); y++ )
9918             {
9919                 for( int x = 0; x < pAccess->Width(); x++ )
9920                 {
9921                     BitmapColor aColor = pAccess->GetColor( y, x );
9922                     pCol[3*x+0] = aColor.GetRed();
9923                     pCol[3*x+1] = aColor.GetGreen();
9924                     pCol[3*x+2] = aColor.GetBlue();
9925                 }
9926                 CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) );
9927             }
9928         }
9929         endCompression();
9930     }
9931     disableStreamEncryption();
9932 
9933     sal_uInt64 nEndPos = 0;
9934     CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) );
9935     aLine.setLength( 0 );
9936     aLine.append( "\nendstream\nendobj\n\n" );
9937     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9938     CHECK_RETURN( updateObject( nStreamLengthObject ) );
9939     aLine.setLength( 0 );
9940     aLine.append( nStreamLengthObject );
9941     aLine.append( " 0 obj\n" );
9942     aLine.append( (sal_Int64)(nEndPos-nStartPos) );
9943     aLine.append( "\nendobj\n\n" );
9944     CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9945 
9946     if( nMaskObject )
9947     {
9948         BitmapEmit aEmit;
9949         aEmit.m_nObject				= nMaskObject;
9950         aEmit.m_aBitmap				= rObject.m_aBitmap;
9951         return writeBitmapObject( aEmit, true );
9952     }
9953 
9954     return true;
9955 }
9956 
9957 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask )
9958 {
9959     MARK( "drawJPGBitmap" );
9960 
9961     OStringBuffer aLine( 80 );
9962     updateGraphicsState();
9963 
9964     // #i40055# sanity check
9965     if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
9966         return;
9967     if( ! (rSizePixel.Width() && rSizePixel.Height()) )
9968         return;
9969 
9970     rDCTData.Seek( 0 );
9971     if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9972     {
9973         // need to convert to grayscale;
9974         // load stream to bitmap and draw the bitmap instead
9975         Graphic aGraphic;
9976         GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG );
9977         Bitmap aBmp( aGraphic.GetBitmap() );
9978         if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() )
9979         {
9980             BitmapEx aBmpEx( aBmp, rMask );
9981             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx );
9982         }
9983         else
9984             drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp );
9985         return;
9986     }
9987 
9988     SvMemoryStream* pStream = new SvMemoryStream;
9989     *pStream << rDCTData;
9990     pStream->Seek( STREAM_SEEK_TO_END );
9991 
9992     BitmapID aID;
9993     aID.m_aPixelSize	= rSizePixel;
9994     aID.m_nSize			= pStream->Tell();
9995     pStream->Seek( STREAM_SEEK_TO_BEGIN );
9996     aID.m_nChecksum		= rtl_crc32( 0, pStream->GetData(), aID.m_nSize );
9997     if( ! rMask.IsEmpty() )
9998         aID.m_nMaskChecksum	= rMask.GetChecksum();
9999 
10000     std::list< JPGEmit >::const_iterator it;
10001     for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
10002         ;
10003     if( it == m_aJPGs.end() )
10004     {
10005         m_aJPGs.push_front( JPGEmit() );
10006         JPGEmit& rEmit = m_aJPGs.front();
10007         rEmit.m_nObject		= createObject();
10008         rEmit.m_aID			= aID;
10009         rEmit.m_pStream		= pStream;
10010         rEmit.m_bTrueColor  = bIsTrueColor;
10011         if( !! rMask && rMask.GetSizePixel() == rSizePixel )
10012             rEmit.m_aMask	= rMask;
10013 
10014         it = m_aJPGs.begin();
10015     }
10016     else
10017         delete pStream;
10018 
10019     aLine.append( "q " );
10020     sal_Int32 nCheckWidth = 0;
10021     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth );
10022     aLine.append( " 0 0 " );
10023     sal_Int32 nCheckHeight = 0;
10024     m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight );
10025     aLine.append( ' ' );
10026     m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
10027     aLine.append( " cm\n/Im" );
10028     aLine.append( it->m_nObject );
10029     aLine.append( " Do Q\n" );
10030     if( nCheckWidth == 0 || nCheckHeight == 0 )
10031     {
10032         // #i97512# avoid invalid current matrix
10033         aLine.setLength( 0 );
10034         aLine.append( "\n%jpeg image /Im" );
10035         aLine.append( it->m_nObject );
10036         aLine.append( " scaled to zero size, omitted\n" );
10037     }
10038     writeBuffer( aLine.getStr(), aLine.getLength() );
10039 
10040     OStringBuffer aObjName( 16 );
10041     aObjName.append( "Im" );
10042     aObjName.append( it->m_nObject );
10043     pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10044 
10045 }
10046 
10047 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
10048 {
10049     OStringBuffer aLine( 80 );
10050     updateGraphicsState();
10051 
10052     aLine.append( "q " );
10053     if( rFillColor != Color( COL_TRANSPARENT ) )
10054     {
10055         appendNonStrokingColor( rFillColor, aLine );
10056         aLine.append( ' ' );
10057     }
10058     sal_Int32 nCheckWidth = 0;
10059     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth );
10060     aLine.append( " 0 0 " );
10061     sal_Int32 nCheckHeight = 0;
10062     m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight );
10063     aLine.append( ' ' );
10064     m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
10065     aLine.append( " cm\n/Im" );
10066     aLine.append( rBitmap.m_nObject );
10067     aLine.append( " Do Q\n" );
10068     if( nCheckWidth == 0 || nCheckHeight == 0 )
10069     {
10070         // #i97512# avoid invalid current matrix
10071         aLine.setLength( 0 );
10072         aLine.append( "\n%bitmap image /Im" );
10073         aLine.append( rBitmap.m_nObject );
10074         aLine.append( " scaled to zero size, omitted\n" );
10075     }
10076     writeBuffer( aLine.getStr(), aLine.getLength() );
10077 }
10078 
10079 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask )
10080 {
10081     BitmapEx aBitmap( i_rBitmap );
10082     if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
10083     {
10084         BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS;
10085         int nDepth = aBitmap.GetBitmap().GetBitCount();
10086         if( nDepth <= 4 )
10087             eConv = BMP_CONVERSION_4BIT_GREYS;
10088         if( nDepth > 1 )
10089             aBitmap.Convert( eConv );
10090     }
10091     BitmapID aID;
10092     aID.m_aPixelSize		= aBitmap.GetSizePixel();
10093     aID.m_nSize				= aBitmap.GetBitCount();
10094     aID.m_nChecksum			= aBitmap.GetBitmap().GetChecksum();
10095     aID.m_nMaskChecksum		= 0;
10096     if( aBitmap.IsAlpha() )
10097         aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum();
10098     else
10099     {
10100         Bitmap aMask = aBitmap.GetMask();
10101         if( ! aMask.IsEmpty() )
10102             aID.m_nMaskChecksum = aMask.GetChecksum();
10103     }
10104     std::list< BitmapEmit >::const_iterator it;
10105     for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
10106     {
10107         if( aID == it->m_aID )
10108             break;
10109     }
10110     if( it == m_aBitmaps.end() )
10111     {
10112         m_aBitmaps.push_front( BitmapEmit() );
10113         m_aBitmaps.front().m_aID		= aID;
10114         m_aBitmaps.front().m_aBitmap	= aBitmap;
10115         m_aBitmaps.front().m_nObject	= createObject();
10116         m_aBitmaps.front().m_bDrawMask	= bDrawMask;
10117         it = m_aBitmaps.begin();
10118     }
10119 
10120     OStringBuffer aObjName( 16 );
10121     aObjName.append( "Im" );
10122     aObjName.append( it->m_nObject );
10123     pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10124 
10125     return *it;
10126 }
10127 
10128 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap )
10129 {
10130     MARK( "drawBitmap (Bitmap)" );
10131 
10132     // #i40055# sanity check
10133     if( ! (rDestSize.Width() && rDestSize.Height()) )
10134         return;
10135 
10136     const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) );
10137     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10138 }
10139 
10140 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
10141 {
10142     MARK( "drawBitmap (BitmapEx)" );
10143 
10144     // #i40055# sanity check
10145     if( ! (rDestSize.Width() && rDestSize.Height()) )
10146         return;
10147 
10148     const BitmapEmit& rEmit = createBitmapEmit( rBitmap );
10149     drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10150 }
10151 
10152 void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor )
10153 {
10154     MARK( "drawMask" );
10155 
10156     // #i40055# sanity check
10157     if( ! (rDestSize.Width() && rDestSize.Height()) )
10158         return;
10159 
10160     Bitmap aBitmap( rBitmap );
10161     if( aBitmap.GetBitCount() > 1 )
10162         aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
10163     DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
10164 
10165     const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true );
10166     drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor );
10167 }
10168 
10169 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
10170 {
10171     Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10172                                MapMode( MAP_POINT ),
10173                                getReferenceDevice(),
10174                                rSize ) );
10175     // check if we already have this gradient
10176     std::list<GradientEmit>::iterator it;
10177     // rounding to point will generally lose some pixels
10178     // round up to point boundary
10179     aPtSize.Width()++;
10180     aPtSize.Height()++;
10181     for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
10182     {
10183         if( it->m_aGradient == rGradient )
10184         {
10185             if( it->m_aSize == aPtSize )
10186                 break;
10187         }
10188     }
10189     if( it == m_aGradients.end() )
10190     {
10191         m_aGradients.push_front( GradientEmit() );
10192         m_aGradients.front().m_aGradient	= rGradient;
10193         m_aGradients.front().m_nObject	    = createObject();
10194         m_aGradients.front().m_aSize		= aPtSize;
10195         it = m_aGradients.begin();
10196     }
10197 
10198     OStringBuffer aObjName( 16 );
10199     aObjName.append( 'P' );
10200     aObjName.append( it->m_nObject );
10201     pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
10202 
10203     return it->m_nObject;
10204 }
10205 
10206 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient )
10207 {
10208     MARK( "drawGradient (Rectangle)" );
10209 
10210     if( m_aContext.Version == PDFWriter::PDF_1_2 )
10211     {
10212         drawRectangle( rRect );
10213         return;
10214     }
10215 
10216     sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
10217 
10218     Point aTranslate( rRect.BottomLeft() );
10219     aTranslate += Point( 0, 1 );
10220 
10221     updateGraphicsState();
10222 
10223     OStringBuffer aLine( 80 );
10224     aLine.append( "q 1 0 0 1 " );
10225     m_aPages.back().appendPoint( aTranslate, aLine );
10226     aLine.append( " cm " );
10227     // if a stroke is appended reset the clip region before stroke
10228     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10229         aLine.append( "q " );
10230     aLine.append( "0 0 " );
10231     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10232     aLine.append( ' ' );
10233     m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10234     aLine.append( " re W n\n" );
10235 
10236     aLine.append( "/P" );
10237     aLine.append( nGradient );
10238     aLine.append( " sh " );
10239     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10240     {
10241         aLine.append( "Q 0 0 " );
10242         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10243         aLine.append( ' ' );
10244         m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10245         aLine.append( " re S " );
10246     }
10247     aLine.append( "Q\n" );
10248     writeBuffer( aLine.getStr(), aLine.getLength() );
10249 }
10250 
10251 void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
10252 {
10253     MARK( "drawGradient (PolyPolygon)" );
10254 
10255     if( m_aContext.Version == PDFWriter::PDF_1_2 )
10256     {
10257         drawPolyPolygon( rPolyPoly );
10258         return;
10259     }
10260 
10261     Rectangle aBoundRect = rPolyPoly.GetBoundRect();
10262     sal_Int32 nGradient = createGradient( rGradient, aBoundRect.GetSize() );
10263 
10264     updateGraphicsState();
10265 
10266     Point aTranslate = aBoundRect.BottomLeft();
10267     int nPolygons = rPolyPoly.Count();
10268 
10269     OStringBuffer aLine( 80*nPolygons );
10270     aLine.append( "q " );
10271     // set PolyPolygon as clip path
10272     m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
10273     aLine.append( "W* n\n" );
10274     aLine.append( "1 0 0 1 " );
10275     m_aPages.back().appendPoint( aTranslate, aLine );
10276     aLine.append( " cm\n" );
10277     aLine.append( "/P" );
10278     aLine.append( nGradient );
10279     aLine.append( " sh Q\n" );
10280     if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10281     {
10282         // and draw the surrounding path
10283         m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
10284         aLine.append( "S\n" );
10285     }
10286     writeBuffer( aLine.getStr(), aLine.getLength() );
10287 }
10288 
10289 void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
10290 {
10291     MARK( "drawHatch" );
10292 
10293     updateGraphicsState();
10294 
10295 	if( rPolyPoly.Count() )
10296 	{
10297 		PolyPolygon		aPolyPoly( rPolyPoly );
10298 
10299 		aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
10300 		push( PUSH_LINECOLOR );
10301 		setLineColor( rHatch.GetColor() );
10302 		getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, sal_False );
10303 		pop();
10304 	}
10305 }
10306 
10307 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall )
10308 {
10309     MARK( "drawWallpaper" );
10310 
10311     bool bDrawColor			= false;
10312     bool bDrawGradient		= false;
10313     bool bDrawBitmap		= false;
10314 
10315     BitmapEx aBitmap;
10316     Point aBmpPos = rRect.TopLeft();
10317     Size aBmpSize;
10318     if( rWall.IsBitmap() )
10319     {
10320         aBitmap = rWall.GetBitmap();
10321 		aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
10322                                 getMapMode(),
10323                                 getReferenceDevice(),
10324                                 aBitmap.GetPrefSize() );
10325         Rectangle aRect( rRect );
10326         if( rWall.IsRect() )
10327         {
10328             aRect = rWall.GetRect();
10329             aBmpPos = aRect.TopLeft();
10330             aBmpSize = aRect.GetSize();
10331         }
10332         if( rWall.GetStyle() != WALLPAPER_SCALE )
10333         {
10334             if( rWall.GetStyle() != WALLPAPER_TILE )
10335             {
10336                 bDrawBitmap		= true;
10337                 if( rWall.IsGradient() )
10338                     bDrawGradient = true;
10339                 else
10340                     bDrawColor = true;
10341                 switch( rWall.GetStyle() )
10342                 {
10343                     case WALLPAPER_TOPLEFT:
10344                         break;
10345                     case WALLPAPER_TOP:
10346                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10347                         break;
10348                     case WALLPAPER_LEFT:
10349                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10350                         break;
10351                     case WALLPAPER_TOPRIGHT:
10352                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10353                         break;
10354                     case WALLPAPER_CENTER:
10355                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10356                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10357                         break;
10358                     case WALLPAPER_RIGHT:
10359                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10360                         aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10361                         break;
10362                     case WALLPAPER_BOTTOMLEFT:
10363                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10364                         break;
10365                     case WALLPAPER_BOTTOM:
10366                         aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10367                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10368                         break;
10369                     case WALLPAPER_BOTTOMRIGHT:
10370                         aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10371                         aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10372                         break;
10373                     default: ;
10374                 }
10375             }
10376             else
10377             {
10378                 // push the bitmap
10379                 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) );
10380 
10381                 // convert to page coordinates; this needs to be done here
10382                 // since the emit does not know the page anymore
10383                 Rectangle aConvertRect( aBmpPos, aBmpSize );
10384                 m_aPages.back().convertRect( aConvertRect );
10385 
10386                 OStringBuffer aNameBuf(16);
10387                 aNameBuf.append( "Im" );
10388                 aNameBuf.append( rEmit.m_nObject );
10389                 OString aImageName( aNameBuf.makeStringAndClear() );
10390 
10391                 // push the pattern
10392                 OStringBuffer aTilingStream( 32 );
10393                 appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
10394                 aTilingStream.append( " 0 0 " );
10395                 appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
10396                 aTilingStream.append( " 0 0 cm\n/" );
10397                 aTilingStream.append( aImageName );
10398                 aTilingStream.append( " Do\n" );
10399 
10400                 m_aTilings.push_back( TilingEmit() );
10401                 m_aTilings.back().m_nObject			= createObject();
10402                 m_aTilings.back().m_aRectangle		= Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
10403                 m_aTilings.back().m_pTilingStream   = new SvMemoryStream();
10404                 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() );
10405                 // phase the tiling so wallpaper begins on upper left
10406                 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
10407                 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
10408                 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
10409 
10410                 updateGraphicsState();
10411 
10412                 OStringBuffer aObjName( 16 );
10413                 aObjName.append( 'P' );
10414                 aObjName.append( m_aTilings.back().m_nObject );
10415                 OString aPatternName( aObjName.makeStringAndClear() );
10416                 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
10417 
10418                 // fill a rRect with the pattern
10419                 OStringBuffer aLine( 100 );
10420                 aLine.append( "q /Pattern cs /" );
10421                 aLine.append( aPatternName );
10422                 aLine.append( " scn " );
10423                 m_aPages.back().appendRect( rRect, aLine );
10424                 aLine.append( " f Q\n" );
10425                 writeBuffer( aLine.getStr(), aLine.getLength() );
10426             }
10427         }
10428         else
10429         {
10430             aBmpPos		= aRect.TopLeft();
10431             aBmpSize	= aRect.GetSize();
10432             bDrawBitmap	= true;
10433         }
10434 
10435         if( aBitmap.IsTransparent() )
10436         {
10437             if( rWall.IsGradient() )
10438                 bDrawGradient = true;
10439             else
10440                 bDrawColor = true;
10441         }
10442     }
10443     else if( rWall.IsGradient() )
10444         bDrawGradient = true;
10445     else
10446         bDrawColor = true;
10447 
10448     if( bDrawGradient )
10449     {
10450         drawGradient( rRect, rWall.GetGradient() );
10451     }
10452     if( bDrawColor )
10453     {
10454         Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
10455         Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
10456         setLineColor( Color( COL_TRANSPARENT ) );
10457         setFillColor( rWall.GetColor() );
10458         drawRectangle( rRect );
10459         setLineColor( aOldLineColor );
10460         setFillColor( aOldFillColor );
10461     }
10462     if( bDrawBitmap )
10463     {
10464         // set temporary clip region since aBmpPos and aBmpSize
10465         // may be outside rRect
10466         OStringBuffer aLine( 20 );
10467         aLine.append( "q " );
10468         m_aPages.back().appendRect( rRect, aLine );
10469         aLine.append( " W n\n" );
10470         writeBuffer( aLine.getStr(), aLine.getLength() );
10471         drawBitmap( aBmpPos, aBmpSize, aBitmap );
10472         writeBuffer( "Q\n", 2 );
10473     }
10474 }
10475 
10476 void PDFWriterImpl::beginPattern( const Rectangle& rCellRect )
10477 {
10478     beginRedirect( new SvMemoryStream(), rCellRect );
10479 }
10480 
10481 sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform )
10482 {
10483     Rectangle aConvertRect( getRedirectTargetRect() );
10484     DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" );
10485 
10486     // get scaling between current mapmode and PDF output
10487     Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) );
10488     double fSX = (double(aScaling.Width()) / 10000.0);
10489     double fSY = (double(aScaling.Height()) / 10000.0);
10490 
10491     // transform translation part of matrix
10492     Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] );
10493     aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation );
10494 
10495     sal_Int32 nTilingId = m_aTilings.size();
10496     m_aTilings.push_back( TilingEmit() );
10497     TilingEmit& rTile = m_aTilings.back();
10498     rTile.m_nObject         = createObject();
10499     rTile.m_aResources      = m_aOutputStreams.front().m_aResourceDict;
10500     rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX;
10501     rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY;
10502     rTile.m_aTransform.matrix[2] = aTranslation.Width();
10503     rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX;
10504     rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY;
10505     rTile.m_aTransform.matrix[5] = -aTranslation.Height();
10506     // caution: endRedirect pops the stream, so do this last
10507     rTile.m_pTilingStream   = dynamic_cast<SvMemoryStream*>(endRedirect());
10508     // FIXME: bound rect will not work with rotated matrix
10509     rTile.m_aRectangle      = Rectangle( Point(0,0), aConvertRect.GetSize() );
10510     rTile.m_aCellSize       = aConvertRect.GetSize();
10511 
10512     OStringBuffer aObjName( 16 );
10513     aObjName.append( 'P' );
10514     aObjName.append( rTile.m_nObject );
10515     pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject );
10516     return nTilingId;
10517 }
10518 
10519 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill )
10520 {
10521     if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() )
10522         return;
10523 
10524     m_aPages.back().endStream();
10525     sal_Int32 nXObject = createObject();
10526     OStringBuffer aNameBuf( 16 );
10527     aNameBuf.append( "Pol" );
10528     aNameBuf.append( nXObject );
10529     OString aObjName( aNameBuf.makeStringAndClear() );
10530     Rectangle aObjRect;
10531     if( updateObject( nXObject ) )
10532     {
10533         // get bounding rect of object
10534         PolyPolygon aSubDiv;
10535         rPolyPoly.AdaptiveSubdivide( aSubDiv );
10536         aObjRect = aSubDiv.GetBoundRect();
10537         Rectangle aConvObjRect( aObjRect );
10538         m_aPages.back().convertRect( aConvObjRect );
10539 
10540         // move polypolygon to bottom left of page
10541         PolyPolygon aLocalPath( rPolyPoly );
10542         sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72;
10543         sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72;
10544         Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode );
10545         sal_Int32 nXOff = aObjRect.Left();
10546         sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom();
10547         aLocalPath.Move( -nXOff, nYOff );
10548 
10549         // prepare XObject's content stream
10550         OStringBuffer aStream( 512 );
10551         aStream.append( "/Pattern cs /P" );
10552         aStream.append( m_aTilings[ nPattern ].m_nObject );
10553         aStream.append( " scn\n" );
10554         m_aPages.back().appendPolyPolygon( aLocalPath, aStream );
10555         aStream.append( bEOFill ? "f*" : "f" );
10556         SvMemoryStream aMemStream( aStream.getLength() );
10557         aMemStream.Write( aStream.getStr(), aStream.getLength() );
10558         bool bDeflate = compressStream( &aMemStream );
10559         aMemStream.Seek( STREAM_SEEK_TO_END );
10560         sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell();
10561         aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
10562 
10563         // add new XObject to global resource dict
10564         m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject;
10565 
10566         // write XObject
10567         OStringBuffer aLine( 512 );
10568         aLine.append( nXObject );
10569         aLine.append( " 0 obj\n"
10570                       "<</Type/XObject/Subtype/Form/BBox[0 0 " );
10571         appendFixedInt( aConvObjRect.GetWidth(), aLine );
10572         aLine.append( ' ' );
10573         appendFixedInt( aConvObjRect.GetHeight(), aLine );
10574         aLine.append( "]/Length " );
10575         aLine.append( nStreamLen );
10576         if( bDeflate )
10577             aLine.append( "/Filter/FlateDecode" );
10578         aLine.append( ">>\n"
10579                       "stream\n" );
10580         writeBuffer( aLine.getStr(), aLine.getLength() );
10581         checkAndEnableStreamEncryption( nXObject );
10582         writeBuffer( aMemStream.GetData(), nStreamLen );
10583         disableStreamEncryption();
10584         writeBuffer( "\nendstream\nendobj\n\n", 19 );
10585     }
10586     m_aPages.back().beginStream();
10587     OStringBuffer aLine( 80 );
10588     aLine.append( "q 1 0 0 1 " );
10589     m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine );
10590     aLine.append( " cm/" );
10591     aLine.append( aObjName );
10592     aLine.append( " Do Q\n" );
10593     writeBuffer( aLine.getStr(), aLine.getLength() );
10594 }
10595 
10596 void PDFWriterImpl::updateGraphicsState()
10597 {
10598     OStringBuffer aLine( 256 );
10599     GraphicsState& rNewState = m_aGraphicsStack.front();
10600     // first set clip region since it might invalidate everything else
10601 
10602     if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) )
10603     {
10604         rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion;
10605 
10606         if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion ||
10607             ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) )
10608         {
10609             if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() )
10610             {
10611                 aLine.append( "Q " );
10612                 // invalidate everything but the clip region
10613                 m_aCurrentPDFState = GraphicsState();
10614                 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion);
10615             }
10616             if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() )
10617             {
10618                 // clip region is always stored in private PDF mapmode
10619                 MapMode aNewMapMode = rNewState.m_aMapMode;
10620                 rNewState.m_aMapMode = m_aMapMode;
10621                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10622                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10623 
10624                 aLine.append( "q " );
10625                 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine );
10626                 aLine.append( "W* n\n" );
10627                 rNewState.m_aMapMode = aNewMapMode;
10628                 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10629                 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10630             }
10631         }
10632     }
10633 
10634     if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) )
10635     {
10636         rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode;
10637         getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10638     }
10639 
10640     if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) )
10641     {
10642         rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont;
10643         getReferenceDevice()->SetFont( rNewState.m_aFont );
10644         getReferenceDevice()->ImplNewFont();
10645     }
10646 
10647     if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) )
10648     {
10649         rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode;
10650         getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
10651     }
10652 
10653     if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) )
10654     {
10655         rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage;
10656         getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
10657     }
10658 
10659     if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) )
10660     {
10661         rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor;
10662         if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
10663             rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
10664         {
10665             appendStrokingColor( rNewState.m_aLineColor, aLine );
10666             aLine.append( "\n" );
10667         }
10668     }
10669 
10670     if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) )
10671     {
10672         rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor;
10673         if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
10674             rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
10675         {
10676             appendNonStrokingColor( rNewState.m_aFillColor, aLine );
10677             aLine.append( "\n" );
10678         }
10679     }
10680 
10681     if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) )
10682     {
10683         rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent;
10684         if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent )
10685         {
10686             // TODO: switch extended graphicsstate
10687         }
10688     }
10689 
10690     // everything is up to date now
10691     m_aCurrentPDFState = m_aGraphicsStack.front();
10692     if( aLine.getLength() )
10693         writeBuffer( aLine.getStr(), aLine.getLength() );
10694 }
10695 
10696 /* #i47544# imitate OutputDevice behaviour:
10697 *  if a font with a nontransparent color is set, it overwrites the current
10698 *  text color. OTOH setting the text color will overwrite the color of the font.
10699 */
10700 void PDFWriterImpl::setFont( const Font& rFont )
10701 {
10702     Color aColor = rFont.GetColor();
10703     if( aColor == Color( COL_TRANSPARENT ) )
10704         aColor = m_aGraphicsStack.front().m_aFont.GetColor();
10705     m_aGraphicsStack.front().m_aFont = rFont;
10706     m_aGraphicsStack.front().m_aFont.SetColor( aColor );
10707     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont;
10708 }
10709 
10710 void PDFWriterImpl::push( sal_uInt16 nFlags )
10711 {
10712     OSL_ENSURE( m_aGraphicsStack.size() > 0, "invalid graphics stack" );
10713     m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
10714     m_aGraphicsStack.front().m_nFlags = nFlags;
10715 }
10716 
10717 void PDFWriterImpl::pop()
10718 {
10719     OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" );
10720     if( m_aGraphicsStack.size() < 2 )
10721         return;
10722 
10723     GraphicsState aState = m_aGraphicsStack.front();
10724     m_aGraphicsStack.pop_front();
10725     GraphicsState& rOld = m_aGraphicsStack.front();
10726 
10727     // move those parameters back that were not pushed
10728     // in the first place
10729     if( ! (aState.m_nFlags & PUSH_LINECOLOR) )
10730         setLineColor( aState.m_aLineColor );
10731     if( ! (aState.m_nFlags & PUSH_FILLCOLOR) )
10732         setFillColor( aState.m_aFillColor );
10733     if( ! (aState.m_nFlags & PUSH_FONT) )
10734         setFont( aState.m_aFont );
10735     if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) )
10736         setTextColor( aState.m_aFont.GetColor() );
10737     if( ! (aState.m_nFlags & PUSH_MAPMODE) )
10738         setMapMode( aState.m_aMapMode );
10739     if( ! (aState.m_nFlags & PUSH_CLIPREGION) )
10740     {
10741         // do not use setClipRegion here
10742         // it would convert again assuming the current mapmode
10743         rOld.m_aClipRegion = aState.m_aClipRegion;
10744         rOld.m_bClipRegion = aState.m_bClipRegion;
10745     }
10746     if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) )
10747         setTextLineColor( aState.m_aTextLineColor );
10748     if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) )
10749         setOverlineColor( aState.m_aOverlineColor );
10750     if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) )
10751         setTextAlign( aState.m_aFont.GetAlign() );
10752     if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) )
10753         setTextFillColor( aState.m_aFont.GetFillColor() );
10754     if( ! (aState.m_nFlags & PUSH_REFPOINT) )
10755     {
10756         // what ?
10757     }
10758     // invalidate graphics state
10759     m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U);
10760 }
10761 
10762 void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
10763 {
10764     m_aGraphicsStack.front().m_aMapMode = rMapMode;
10765     getReferenceDevice()->SetMapMode( rMapMode );
10766     m_aCurrentPDFState.m_aMapMode = rMapMode;
10767 }
10768 
10769 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10770 {
10771     basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10772     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10773     m_aGraphicsStack.front().m_aClipRegion = aRegion;
10774     m_aGraphicsStack.front().m_bClipRegion = true;
10775     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10776 }
10777 
10778 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
10779 {
10780     if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() )
10781     {
10782         Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10783                                    m_aMapMode,
10784                                    getReferenceDevice(),
10785                                    Point( nX, nY ) ) );
10786         aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10787                                m_aMapMode,
10788                                getReferenceDevice(),
10789                                Point() );
10790         basegfx::B2DHomMatrix aMat;
10791         aMat.translate( aPoint.X(), aPoint.Y() );
10792         m_aGraphicsStack.front().m_aClipRegion.transform( aMat );
10793         m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10794     }
10795 }
10796 
10797 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect )
10798 {
10799     basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect(
10800         basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) );
10801     return intersectClipRegion( aRect );
10802 }
10803 
10804 
10805 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10806 {
10807     basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) );
10808     aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10809     m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10810     if( m_aGraphicsStack.front().m_bClipRegion )
10811     {
10812         basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) );
10813         aRegion = basegfx::tools::prepareForPolygonOperation( aRegion );
10814         m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion );
10815     }
10816     else
10817     {
10818         m_aGraphicsStack.front().m_aClipRegion = aRegion;
10819         m_aGraphicsStack.front().m_bClipRegion = true;
10820     }
10821     return true;
10822 }
10823 
10824 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
10825 {
10826     if( nPageNr < 0 )
10827         nPageNr = m_nCurrentPage;
10828 
10829     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10830         return;
10831 
10832     m_aNotes.push_back( PDFNoteEntry() );
10833     m_aNotes.back().m_nObject		= createObject();
10834     m_aNotes.back().m_aContents		= rNote;
10835     m_aNotes.back().m_aRect			= rRect;
10836     // convert to default user space now, since the mapmode may change
10837     m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
10838 
10839     // insert note to page's annotation list
10840     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
10841 }
10842 
10843 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr )
10844 {
10845     if( nPageNr < 0 )
10846         nPageNr = m_nCurrentPage;
10847 
10848     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10849         return -1;
10850 
10851     sal_Int32 nRet = m_aLinks.size();
10852 
10853     m_aLinks.push_back( PDFLink() );
10854     m_aLinks.back().m_nObject	= createObject();
10855     m_aLinks.back().m_nPage		= nPageNr;
10856     m_aLinks.back().m_aRect		= rRect;
10857     // convert to default user space now, since the mapmode may change
10858     m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
10859 
10860     // insert link to page's annotation list
10861     m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
10862 
10863     return nRet;
10864 }
10865 
10866 //--->i56629
10867 sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10868 {
10869     if( nPageNr < 0 )
10870         nPageNr = m_nCurrentPage;
10871 
10872     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10873         return -1;
10874 
10875     sal_Int32 nRet = m_aNamedDests.size();
10876 
10877     m_aNamedDests.push_back( PDFNamedDest() );
10878     m_aNamedDests.back().m_aDestName = sDestName;
10879     m_aNamedDests.back().m_nPage = nPageNr;
10880     m_aNamedDests.back().m_eType = eType;
10881     m_aNamedDests.back().m_aRect = rRect;
10882     // convert to default user space now, since the mapmode may change
10883     m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
10884 
10885     return nRet;
10886 }
10887 //<---i56629
10888 
10889 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10890 {
10891     if( nPageNr < 0 )
10892         nPageNr = m_nCurrentPage;
10893 
10894     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10895         return -1;
10896 
10897     sal_Int32 nRet = m_aDests.size();
10898 
10899     m_aDests.push_back( PDFDest() );
10900     m_aDests.back().m_nPage = nPageNr;
10901     m_aDests.back().m_eType	= eType;
10902     m_aDests.back().m_aRect = rRect;
10903     // convert to default user space now, since the mapmode may change
10904     m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
10905 
10906     return nRet;
10907 }
10908 
10909 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10910 {
10911     return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType );
10912 }
10913 
10914 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
10915 {
10916     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10917         return -1;
10918     if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
10919         return -2;
10920 
10921     m_aLinks[ nLinkId ].m_nDest = nDestId;
10922 
10923     return 0;
10924 }
10925 
10926 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
10927 {
10928     if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10929         return -1;
10930 
10931     m_aLinks[ nLinkId ].m_nDest	= -1;
10932 
10933     using namespace ::com::sun::star;
10934 
10935     if (!m_xTrans.is())
10936     {
10937         uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() );
10938         if( xFact.is() )
10939         {
10940             m_xTrans = uno::Reference < util::XURLTransformer >(
10941                 xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY );
10942         }
10943     }
10944 
10945     util::URL aURL;
10946     aURL.Complete = rURL;
10947 
10948     if (m_xTrans.is())
10949         m_xTrans->parseStrict( aURL );
10950 
10951     m_aLinks[ nLinkId ].m_aURL	= aURL.Complete;
10952 
10953     return 0;
10954 }
10955 
10956 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
10957 {
10958     m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
10959 }
10960 
10961 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
10962 {
10963     // create new item
10964     sal_Int32 nNewItem = m_aOutline.size();
10965     m_aOutline.push_back( PDFOutlineEntry() );
10966 
10967     // set item attributes
10968     setOutlineItemParent( nNewItem, nParent );
10969     setOutlineItemText( nNewItem, rText );
10970     setOutlineItemDest( nNewItem, nDestID );
10971 
10972     return nNewItem;
10973 }
10974 
10975 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
10976 {
10977     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10978         return -1;
10979 
10980     int nRet = 0;
10981 
10982     if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
10983     {
10984         nNewParent = 0;
10985         nRet = -2;
10986     }
10987     // remove item from previous parent
10988     sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID;
10989     if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() )
10990     {
10991         PDFOutlineEntry& rParent = m_aOutline[ nParentID ];
10992 
10993         for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin();
10994              it != rParent.m_aChildren.end(); ++it )
10995         {
10996             if( *it == nItem )
10997             {
10998                 rParent.m_aChildren.erase( it );
10999                 break;
11000             }
11001         }
11002     }
11003 
11004     // insert item to new parent's list of children
11005     m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
11006 
11007     return nRet;
11008 }
11009 
11010 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
11011 {
11012     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
11013         return -1;
11014 
11015     m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText );
11016     return 0;
11017 }
11018 
11019 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
11020 {
11021     if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
11022         return -1;
11023     if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
11024         return -2;
11025     m_aOutline[nItem].m_nDestID = nDestID;
11026     return 0;
11027 }
11028 
11029 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
11030 {
11031     static std::map< PDFWriter::StructElement, const char* > aTagStrings;
11032     if( aTagStrings.empty() )
11033     {
11034         aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
11035         aTagStrings[ PDFWriter::Document ]		= "Document";
11036         aTagStrings[ PDFWriter::Part ]			= "Part";
11037         aTagStrings[ PDFWriter::Article ]		= "Art";
11038         aTagStrings[ PDFWriter::Section ]		= "Sect";
11039         aTagStrings[ PDFWriter::Division ]		= "Div";
11040         aTagStrings[ PDFWriter::BlockQuote ]	= "BlockQuote";
11041         aTagStrings[ PDFWriter::Caption ]		= "Caption";
11042         aTagStrings[ PDFWriter::TOC ]			= "TOC";
11043         aTagStrings[ PDFWriter::TOCI ]			= "TOCI";
11044         aTagStrings[ PDFWriter::Index ]			= "Index";
11045         aTagStrings[ PDFWriter::Paragraph ]		= "P";
11046         aTagStrings[ PDFWriter::Heading ]		= "H";
11047         aTagStrings[ PDFWriter::H1 ]			= "H1";
11048         aTagStrings[ PDFWriter::H2 ]			= "H2";
11049         aTagStrings[ PDFWriter::H3 ]			= "H3";
11050         aTagStrings[ PDFWriter::H4 ]			= "H4";
11051         aTagStrings[ PDFWriter::H5 ]			= "H5";
11052         aTagStrings[ PDFWriter::H6 ]			= "H6";
11053         aTagStrings[ PDFWriter::List ]			= "L";
11054         aTagStrings[ PDFWriter::ListItem ]		= "LI";
11055         aTagStrings[ PDFWriter::LILabel ]		= "Lbl";
11056         aTagStrings[ PDFWriter::LIBody ]		= "LBody";
11057         aTagStrings[ PDFWriter::Table ]			= "Table";
11058         aTagStrings[ PDFWriter::TableRow ]		= "TR";
11059         aTagStrings[ PDFWriter::TableHeader ]	= "TH";
11060         aTagStrings[ PDFWriter::TableData ]		= "TD";
11061         aTagStrings[ PDFWriter::Span ]			= "Span";
11062         aTagStrings[ PDFWriter::Quote ]			= "Quote";
11063         aTagStrings[ PDFWriter::Note ]			= "Note";
11064         aTagStrings[ PDFWriter::Reference ]		= "Reference";
11065         aTagStrings[ PDFWriter::BibEntry ]		= "BibEntry";
11066         aTagStrings[ PDFWriter::Code ]			= "Code";
11067         aTagStrings[ PDFWriter::Link ]			= "Link";
11068         aTagStrings[ PDFWriter::Figure ]		= "Figure";
11069         aTagStrings[ PDFWriter::Formula ]		= "Formula";
11070         aTagStrings[ PDFWriter::Form ]			= "Form";
11071     }
11072 
11073     std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
11074 
11075     return it != aTagStrings.end() ? it->second : "Div";
11076 }
11077 
11078 void PDFWriterImpl::beginStructureElementMCSeq()
11079 {
11080     if(	m_bEmitStructure &&
11081         m_nCurrentStructElement > 0 && // StructTreeRoot
11082         ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11083         )
11084     {
11085         PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
11086         OStringBuffer aLine( 128 );
11087         sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
11088         aLine.append( "/" );
11089         if( rEle.m_aAlias.getLength() > 0 )
11090             aLine.append( rEle.m_aAlias );
11091         else
11092             aLine.append( getStructureTag( rEle.m_eType ) );
11093         aLine.append( "<</MCID " );
11094         aLine.append( nMCID );
11095         aLine.append( ">>BDC\n" );
11096         writeBuffer( aLine.getStr(), aLine.getLength() );
11097 
11098         // update the element's content list
11099 #if OSL_DEBUG_LEVEL > 1
11100         fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n",
11101                  nMCID,
11102                  m_aPages[ m_nCurrentPage ].m_nPageObject,
11103                  rEle.m_nFirstPageObject );
11104 #endif
11105         rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) );
11106         // update the page's mcid parent list
11107         m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
11108         // mark element MC sequence as open
11109         rEle.m_bOpenMCSeq = true;
11110     }
11111     // handle artifacts
11112     else if( ! m_bEmitStructure && m_aContext.Tagged &&
11113                m_nCurrentStructElement > 0 &&
11114                m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
11115              ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11116              )
11117     {
11118         OStringBuffer aLine( 128 );
11119         aLine.append( "/Artifact BMC\n" );
11120         writeBuffer( aLine.getStr(), aLine.getLength() );
11121         // mark element MC sequence as open
11122         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
11123     }
11124 }
11125 
11126 void PDFWriterImpl::endStructureElementMCSeq()
11127 {
11128     if( m_nCurrentStructElement > 0 && // StructTreeRoot
11129         ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
11130         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
11131         )
11132     {
11133         writeBuffer( "EMC\n", 4 );
11134         m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
11135     }
11136 }
11137 
11138 bool PDFWriterImpl::checkEmitStructure()
11139 {
11140     bool bEmit = false;
11141     if( m_aContext.Tagged )
11142     {
11143         bEmit = true;
11144         sal_Int32 nEle = m_nCurrentStructElement;
11145         while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
11146         {
11147             if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
11148             {
11149                 bEmit = false;
11150                 break;
11151             }
11152             nEle = m_aStructure[ nEle ].m_nParentElement;
11153         }
11154     }
11155     return bEmit;
11156 }
11157 
11158 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias )
11159 {
11160     if( m_nCurrentPage < 0 )
11161         return -1;
11162 
11163     if( ! m_aContext.Tagged )
11164         return -1;
11165 
11166     // close eventual current MC sequence
11167     endStructureElementMCSeq();
11168 
11169     if( m_nCurrentStructElement == 0 &&
11170         eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
11171     {
11172         // struct tree root hit, but not beginning document
11173         // this might happen with setCurrentStructureElement
11174         // silently insert structure into document again if one properly exists
11175         if( ! m_aStructure[ 0 ].m_aChildren.empty() )
11176         {
11177             PDFWriter::StructElement childType = PDFWriter::NonStructElement;
11178             sal_Int32 nNewCurElement = 0;
11179             const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
11180             for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
11181                  childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
11182             {
11183                 nNewCurElement = *it;
11184                 childType = m_aStructure[ nNewCurElement ].m_eType;
11185             }
11186             if( childType == PDFWriter::Document )
11187             {
11188                 m_nCurrentStructElement = nNewCurElement;
11189                 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" );
11190             }
11191             else {
11192                 DBG_ERROR( "document structure in disorder !" );
11193             }
11194         }
11195         else {
11196             DBG_ERROR( "PDF document structure MUST be contained in a Document element" );
11197         }
11198     }
11199 
11200     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11201     m_aStructure.push_back( PDFStructureElement() );
11202     PDFStructureElement& rEle = m_aStructure.back();
11203     rEle.m_eType			= eType;
11204     rEle.m_nOwnElement		= nNewId;
11205     rEle.m_nParentElement	= m_nCurrentStructElement;
11206     rEle.m_nFirstPageObject	= m_aPages[ m_nCurrentPage ].m_nPageObject;
11207     m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
11208     m_nCurrentStructElement = nNewId;
11209 
11210     // handle alias names
11211     if( rAlias.getLength() && eType != PDFWriter::NonStructElement )
11212     {
11213         OStringBuffer aNameBuf( rAlias.getLength() );
11214         appendName( rAlias, aNameBuf );
11215         OString aAliasName( aNameBuf.makeStringAndClear() );
11216         rEle.m_aAlias = aAliasName;
11217         m_aRoleMap[ aAliasName ] = getStructureTag( eType );
11218     }
11219 
11220 #if OSL_DEBUG_LEVEL > 1
11221     OStringBuffer aLine( "beginStructureElement " );
11222     aLine.append( m_nCurrentStructElement );
11223     aLine.append( ": " );
11224     aLine.append( getStructureTag( eType ) );
11225     if( rEle.m_aAlias.getLength() )
11226     {
11227         aLine.append( " aliased as \"" );
11228         aLine.append( rEle.m_aAlias );
11229         aLine.append( '\"' );
11230     }
11231     emitComment( aLine.getStr() );
11232 #endif
11233 
11234     // check whether to emit structure henceforth
11235     m_bEmitStructure = checkEmitStructure();
11236 
11237     if( m_bEmitStructure ) // don't create nonexistant objects
11238     {
11239         rEle.m_nObject		= createObject();
11240         // update parent's kids list
11241         m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject );
11242     }
11243     return nNewId;
11244 }
11245 
11246 void PDFWriterImpl::endStructureElement()
11247 {
11248     if( m_nCurrentPage < 0 )
11249         return;
11250 
11251     if( ! m_aContext.Tagged )
11252         return;
11253 
11254     if( m_nCurrentStructElement == 0 )
11255     {
11256         // hit the struct tree root, that means there is an endStructureElement
11257         // without corresponding beginStructureElement
11258         return;
11259     }
11260 
11261     // end the marked content sequence
11262     endStructureElementMCSeq();
11263 
11264 #if OSL_DEBUG_LEVEL > 1
11265     OStringBuffer aLine( "endStructureElement " );
11266     aLine.append( m_nCurrentStructElement );
11267     aLine.append( ": " );
11268     aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11269     if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
11270     {
11271         aLine.append( " aliased as \"" );
11272         aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11273         aLine.append( '\"' );
11274     }
11275 #endif
11276 
11277     // "end" the structure element, the parent becomes current element
11278     m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
11279 
11280     // check whether to emit structure henceforth
11281     m_bEmitStructure = checkEmitStructure();
11282 
11283 #if OSL_DEBUG_LEVEL > 1
11284     if( m_bEmitStructure )
11285         emitComment( aLine.getStr() );
11286 #endif
11287 }
11288 
11289 //---> i94258
11290 /*
11291  * This function adds an internal structure list container to overcome the 8191 elements array limitation
11292  * in kids element emission.
11293  * Recursive function
11294  *
11295  */
11296 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
11297 {
11298     if( rEle.m_eType == PDFWriter::NonStructElement &&
11299         rEle.m_nOwnElement != rEle.m_nParentElement )
11300         return;
11301 
11302     for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
11303     {
11304         if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
11305         {
11306             PDFStructureElement& rChild = m_aStructure[ *it ];
11307             if( rChild.m_eType != PDFWriter::NonStructElement )
11308             {
11309                 //triggered when a child of the rEle element is found
11310                 if( rChild.m_nParentElement == rEle.m_nOwnElement )
11311                     addInternalStructureContainer( rChild );//examine the child
11312                 else
11313                 {
11314                     DBG_ERROR( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
11315 #if OSL_DEBUG_LEVEL > 1
11316                     fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
11317 #endif
11318                 }
11319             }
11320         }
11321         else
11322         {
11323             DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" );
11324 #if OSL_DEBUG_LEVEL > 1
11325             fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
11326 #endif
11327         }
11328     }
11329 
11330     if( rEle.m_nOwnElement != rEle.m_nParentElement )
11331     {
11332         if( !rEle.m_aKids.empty() )
11333         {
11334             if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
11335                 //then we need to add the containers for the kids elements
11336                 // a list to be used for the new kid element
11337                 std::list< PDFStructureElementKid > aNewKids;
11338                 std::list< sal_Int32 > aNewChildren;
11339 
11340                 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
11341                 OStringBuffer aNameBuf( "Div" );
11342                 OString aAliasName( aNameBuf.makeStringAndClear() );
11343                 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
11344 
11345                 while( rEle.m_aKids.size() > ncMaxPDFArraySize )
11346                 {
11347                     sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
11348                     sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11349                     m_aStructure.push_back( PDFStructureElement() );
11350                     PDFStructureElement& rEleNew = m_aStructure.back();
11351                     rEleNew.m_aAlias            = aAliasName;
11352                     rEleNew.m_eType			    = PDFWriter::Division; // a new Div type container
11353                     rEleNew.m_nOwnElement		= nNewId;
11354                     rEleNew.m_nParentElement	= nCurrentStructElement;
11355                     //inherit the same page as the first child to be reparented
11356                     rEleNew.m_nFirstPageObject	= m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
11357                     rEleNew.m_nObject           = createObject();//assign a PDF object number
11358                     //add the object to the kid list of the parent
11359                     aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) );
11360                     aNewChildren.push_back( nNewId );
11361 
11362                     std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
11363                     std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
11364                     advance( aChildEndIt, ncMaxPDFArraySize );
11365                     advance( aKidEndIt, ncMaxPDFArraySize );
11366 
11367                     rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
11368                                             rEle.m_aKids,
11369                                             rEle.m_aKids.begin(),
11370                                             aKidEndIt );
11371                     rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
11372                                                 rEle.m_aChildren,
11373                                                 rEle.m_aChildren.begin(),
11374                                                 aChildEndIt );
11375                     // set the kid's new parent
11376                     for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
11377                          it != rEleNew.m_aChildren.end(); ++it )
11378                     {
11379                         m_aStructure[ *it ].m_nParentElement = nNewId;
11380                     }
11381                 }
11382                 //finally add the new kids resulting from the container added
11383                 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
11384                 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
11385             }
11386         }
11387     }
11388 }
11389 //<--- i94258
11390 
11391 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
11392 {
11393     bool bSuccess = false;
11394 
11395     if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
11396     {
11397         // end eventual previous marked content sequence
11398         endStructureElementMCSeq();
11399 
11400         m_nCurrentStructElement = nEle;
11401         m_bEmitStructure = checkEmitStructure();
11402 #if OSL_DEBUG_LEVEL > 1
11403         OStringBuffer aLine( "setCurrentStructureElement " );
11404         aLine.append( m_nCurrentStructElement );
11405         aLine.append( ": " );
11406         aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11407         if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() )
11408         {
11409             aLine.append( " aliased as \"" );
11410             aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11411             aLine.append( '\"' );
11412         }
11413         if( ! m_bEmitStructure )
11414             aLine.append( " (inside NonStruct)" );
11415         emitComment( aLine.getStr() );
11416 #endif
11417         bSuccess = true;
11418     }
11419 
11420     return bSuccess;
11421 }
11422 
11423 sal_Int32 PDFWriterImpl::getCurrentStructureElement()
11424 {
11425     return m_nCurrentStructElement;
11426 }
11427 
11428 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
11429 {
11430     if( !m_aContext.Tagged )
11431         return false;
11432 
11433     bool bInsert = false;
11434     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11435     {
11436         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11437         switch( eAttr )
11438         {
11439             case PDFWriter::Placement:
11440                 if( eVal == PDFWriter::Block		||
11441                     eVal == PDFWriter::Inline		||
11442                     eVal == PDFWriter::Before		||
11443                     eVal == PDFWriter::Start		||
11444                     eVal == PDFWriter::End )
11445                     bInsert = true;
11446                 break;
11447             case PDFWriter::WritingMode:
11448                 if( eVal == PDFWriter::LrTb			||
11449                     eVal == PDFWriter::RlTb			||
11450                     eVal == PDFWriter::TbRl )
11451                 {
11452                     bInsert = true;
11453                 }
11454                 break;
11455             case PDFWriter::TextAlign:
11456                 if( eVal == PDFWriter::Start		||
11457                     eVal == PDFWriter::Center		||
11458                     eVal == PDFWriter::End			||
11459                     eVal == PDFWriter::Justify )
11460                 {
11461                     if( eType == PDFWriter::Paragraph	||
11462                         eType == PDFWriter::Heading		||
11463                         eType == PDFWriter::H1			||
11464                         eType == PDFWriter::H2			||
11465                         eType == PDFWriter::H3			||
11466                         eType == PDFWriter::H4			||
11467                         eType == PDFWriter::H5			||
11468                         eType == PDFWriter::H6			||
11469                         eType == PDFWriter::List		||
11470                         eType == PDFWriter::ListItem	||
11471                         eType == PDFWriter::LILabel		||
11472                         eType == PDFWriter::LIBody		||
11473                         eType == PDFWriter::Table		||
11474                         eType == PDFWriter::TableRow	||
11475                         eType == PDFWriter::TableHeader	||
11476                         eType == PDFWriter::TableData )
11477                     {
11478                         bInsert = true;
11479                     }
11480                 }
11481                 break;
11482             case PDFWriter::Width:
11483             case PDFWriter::Height:
11484                 if( eVal == PDFWriter::Auto )
11485                 {
11486                     if( eType == PDFWriter::Figure		||
11487                         eType == PDFWriter::Formula		||
11488                         eType == PDFWriter::Form		||
11489                         eType == PDFWriter::Table		||
11490                         eType == PDFWriter::TableHeader	||
11491                         eType == PDFWriter::TableData )
11492                     {
11493                         bInsert = true;
11494                     }
11495                 }
11496                 break;
11497             case PDFWriter::BlockAlign:
11498                 if( eVal == PDFWriter::Before		||
11499                     eVal == PDFWriter::Middle		||
11500                     eVal == PDFWriter::After		||
11501                     eVal == PDFWriter::Justify )
11502                 {
11503                     if( eType == PDFWriter::TableHeader	||
11504                         eType == PDFWriter::TableData )
11505                     {
11506                         bInsert = true;
11507                     }
11508                 }
11509                 break;
11510             case PDFWriter::InlineAlign:
11511                 if( eVal == PDFWriter::Start		||
11512                     eVal == PDFWriter::Center		||
11513                     eVal == PDFWriter::End )
11514                 {
11515                     if( eType == PDFWriter::TableHeader	||
11516                         eType == PDFWriter::TableData )
11517                     {
11518                         bInsert = true;
11519                     }
11520                 }
11521                 break;
11522             case PDFWriter::LineHeight:
11523                 if( eVal == PDFWriter::Normal		||
11524                     eVal == PDFWriter::Auto )
11525                 {
11526                     // only for ILSE and BLSE
11527                     if( eType == PDFWriter::Paragraph	||
11528                         eType == PDFWriter::Heading		||
11529                         eType == PDFWriter::H1			||
11530                         eType == PDFWriter::H2			||
11531                         eType == PDFWriter::H3			||
11532                         eType == PDFWriter::H4			||
11533                         eType == PDFWriter::H5			||
11534                         eType == PDFWriter::H6			||
11535                         eType == PDFWriter::List		||
11536                         eType == PDFWriter::ListItem	||
11537                         eType == PDFWriter::LILabel		||
11538                         eType == PDFWriter::LIBody		||
11539                         eType == PDFWriter::Table		||
11540                         eType == PDFWriter::TableRow	||
11541                         eType == PDFWriter::TableHeader	||
11542                         eType == PDFWriter::TableData	||
11543                         eType == PDFWriter::Span		||
11544                         eType == PDFWriter::Quote		||
11545                         eType == PDFWriter::Note		||
11546                         eType == PDFWriter::Reference	||
11547                         eType == PDFWriter::BibEntry	||
11548                         eType == PDFWriter::Code		||
11549                         eType == PDFWriter::Link )
11550                     {
11551                         bInsert = true;
11552                     }
11553                 }
11554                 break;
11555             case PDFWriter::TextDecorationType:
11556                 if( eVal == PDFWriter::NONE			||
11557                     eVal == PDFWriter::Underline	||
11558                     eVal == PDFWriter::Overline		||
11559                     eVal == PDFWriter::LineThrough )
11560                 {
11561                     // only for ILSE and BLSE
11562                     if( eType == PDFWriter::Paragraph	||
11563                         eType == PDFWriter::Heading		||
11564                         eType == PDFWriter::H1			||
11565                         eType == PDFWriter::H2			||
11566                         eType == PDFWriter::H3			||
11567                         eType == PDFWriter::H4			||
11568                         eType == PDFWriter::H5			||
11569                         eType == PDFWriter::H6			||
11570                         eType == PDFWriter::List		||
11571                         eType == PDFWriter::ListItem	||
11572                         eType == PDFWriter::LILabel		||
11573                         eType == PDFWriter::LIBody		||
11574                         eType == PDFWriter::Table		||
11575                         eType == PDFWriter::TableRow	||
11576                         eType == PDFWriter::TableHeader	||
11577                         eType == PDFWriter::TableData	||
11578                         eType == PDFWriter::Span		||
11579                         eType == PDFWriter::Quote		||
11580                         eType == PDFWriter::Note		||
11581                         eType == PDFWriter::Reference	||
11582                         eType == PDFWriter::BibEntry	||
11583                         eType == PDFWriter::Code		||
11584                         eType == PDFWriter::Link )
11585                     {
11586                         bInsert = true;
11587                     }
11588                 }
11589                 break;
11590             case PDFWriter::ListNumbering:
11591                 if( eVal == PDFWriter::NONE			||
11592                     eVal == PDFWriter::Disc			||
11593                     eVal == PDFWriter::Circle		||
11594                     eVal == PDFWriter::Square		||
11595                     eVal == PDFWriter::Decimal		||
11596                     eVal == PDFWriter::UpperRoman	||
11597                     eVal == PDFWriter::LowerRoman	||
11598                     eVal == PDFWriter::UpperAlpha	||
11599                     eVal == PDFWriter::LowerAlpha )
11600                 {
11601                     if( eType == PDFWriter::List )
11602                         bInsert = true;
11603                 }
11604                 break;
11605             default: break;
11606         }
11607     }
11608 
11609     if( bInsert )
11610         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
11611 #if OSL_DEBUG_LEVEL > 1
11612     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11613         fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
11614                  getAttributeTag( eAttr ),
11615                  getAttributeValueTag( eVal ),
11616                  getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11617                  m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
11618                  );
11619 #endif
11620 
11621     return bInsert;
11622 }
11623 
11624 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
11625 {
11626     if( ! m_aContext.Tagged )
11627         return false;
11628 
11629     bool bInsert = false;
11630     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11631     {
11632         if( eAttr == PDFWriter::Language )
11633         {
11634             m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue );
11635             return true;
11636         }
11637 
11638         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11639         switch( eAttr )
11640         {
11641             case PDFWriter::SpaceBefore:
11642             case PDFWriter::SpaceAfter:
11643             case PDFWriter::StartIndent:
11644             case PDFWriter::EndIndent:
11645                 // just for BLSE
11646                 if( eType == PDFWriter::Paragraph	||
11647                     eType == PDFWriter::Heading		||
11648                     eType == PDFWriter::H1			||
11649                     eType == PDFWriter::H2			||
11650                     eType == PDFWriter::H3			||
11651                     eType == PDFWriter::H4			||
11652                     eType == PDFWriter::H5			||
11653                     eType == PDFWriter::H6			||
11654                     eType == PDFWriter::List		||
11655                     eType == PDFWriter::ListItem	||
11656                     eType == PDFWriter::LILabel		||
11657                     eType == PDFWriter::LIBody		||
11658                     eType == PDFWriter::Table		||
11659                     eType == PDFWriter::TableRow	||
11660                     eType == PDFWriter::TableHeader	||
11661                     eType == PDFWriter::TableData )
11662                 {
11663                     bInsert = true;
11664                 }
11665                 break;
11666             case PDFWriter::TextIndent:
11667                 // paragraph like BLSE and additional elements
11668                 if( eType == PDFWriter::Paragraph	||
11669                     eType == PDFWriter::Heading		||
11670                     eType == PDFWriter::H1			||
11671                     eType == PDFWriter::H2			||
11672                     eType == PDFWriter::H3			||
11673                     eType == PDFWriter::H4			||
11674                     eType == PDFWriter::H5			||
11675                     eType == PDFWriter::H6			||
11676                     eType == PDFWriter::LILabel		||
11677                     eType == PDFWriter::LIBody		||
11678                     eType == PDFWriter::TableHeader	||
11679                     eType == PDFWriter::TableData )
11680                 {
11681                     bInsert = true;
11682                 }
11683                 break;
11684             case PDFWriter::Width:
11685             case PDFWriter::Height:
11686                 if( eType == PDFWriter::Figure		||
11687                     eType == PDFWriter::Formula		||
11688                     eType == PDFWriter::Form		||
11689                     eType == PDFWriter::Table		||
11690                     eType == PDFWriter::TableHeader	||
11691                     eType == PDFWriter::TableData )
11692                 {
11693                     bInsert = true;
11694                 }
11695                 break;
11696             case PDFWriter::LineHeight:
11697             case PDFWriter::BaselineShift:
11698                 // only for ILSE and BLSE
11699                 if( eType == PDFWriter::Paragraph	||
11700                     eType == PDFWriter::Heading		||
11701                     eType == PDFWriter::H1			||
11702                     eType == PDFWriter::H2			||
11703                     eType == PDFWriter::H3			||
11704                     eType == PDFWriter::H4			||
11705                     eType == PDFWriter::H5			||
11706                     eType == PDFWriter::H6			||
11707                     eType == PDFWriter::List		||
11708                     eType == PDFWriter::ListItem	||
11709                     eType == PDFWriter::LILabel		||
11710                     eType == PDFWriter::LIBody		||
11711                     eType == PDFWriter::Table		||
11712                     eType == PDFWriter::TableRow	||
11713                     eType == PDFWriter::TableHeader	||
11714                     eType == PDFWriter::TableData	||
11715                     eType == PDFWriter::Span		||
11716                     eType == PDFWriter::Quote		||
11717                     eType == PDFWriter::Note		||
11718                     eType == PDFWriter::Reference	||
11719                     eType == PDFWriter::BibEntry	||
11720                     eType == PDFWriter::Code		||
11721                     eType == PDFWriter::Link )
11722                 {
11723                         bInsert = true;
11724                 }
11725                 break;
11726             case PDFWriter::RowSpan:
11727             case PDFWriter::ColSpan:
11728                 // only for table cells
11729                 if( eType == PDFWriter::TableHeader	||
11730                     eType == PDFWriter::TableData )
11731                 {
11732                     bInsert = true;
11733                 }
11734                 break;
11735             case PDFWriter::LinkAnnotation:
11736                 if( eType == PDFWriter::Link )
11737                     bInsert = true;
11738                 break;
11739             default: break;
11740         }
11741     }
11742 
11743     if( bInsert )
11744         m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
11745 #if OSL_DEBUG_LEVEL > 1
11746     else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11747         fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
11748                  getAttributeTag( eAttr ),
11749                  (int)nValue,
11750                  getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11751                  m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() );
11752 #endif
11753 
11754     return bInsert;
11755 }
11756 
11757 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect )
11758 {
11759     sal_Int32 nPageNr = m_nCurrentPage;
11760     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
11761         return;
11762 
11763 
11764     if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11765     {
11766         PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11767         if( eType == PDFWriter::Figure		||
11768             eType == PDFWriter::Formula		||
11769             eType == PDFWriter::Form		||
11770             eType == PDFWriter::Table )
11771         {
11772             m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
11773             // convert to default user space now, since the mapmode may change
11774             m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
11775         }
11776     }
11777 }
11778 
11779 void PDFWriterImpl::setActualText( const String& rText )
11780 {
11781     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11782     {
11783         m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
11784     }
11785 }
11786 
11787 void PDFWriterImpl::setAlternateText( const String& rText )
11788 {
11789     if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11790     {
11791         m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
11792     }
11793 }
11794 
11795 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
11796 {
11797     if( nPageNr < 0 )
11798         nPageNr = m_nCurrentPage;
11799 
11800     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11801         return;
11802 
11803     m_aPages[ nPageNr ].m_nDuration = nSeconds;
11804 }
11805 
11806 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
11807 {
11808     if( nPageNr < 0 )
11809         nPageNr = m_nCurrentPage;
11810 
11811     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11812         return;
11813 
11814     m_aPages[ nPageNr ].m_eTransition	= eType;
11815     m_aPages[ nPageNr ].m_nTransTime	= nMilliSec;
11816 }
11817 
11818 void PDFWriterImpl::ensureUniqueRadioOnValues()
11819 {
11820     // loop over radio groups
11821     for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
11822          group != m_aRadioGroupWidgets.end(); ++group )
11823     {
11824         PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
11825         // check whether all kids have a unique OnValue
11826         std::hash_map< OUString, sal_Int32, OUStringHash > aOnValues;
11827         int nChildren = rGroupWidget.m_aKidsIndex.size();
11828         bool bIsUnique = true;
11829         for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
11830         {
11831             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11832             const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
11833             #if OSL_DEBUG_LEVEL > 1
11834             fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() );
11835             #endif
11836             if( aOnValues.find( rVal ) == aOnValues.end() )
11837             {
11838                 aOnValues[ rVal ] = 1;
11839             }
11840             else
11841             {
11842                 bIsUnique = false;
11843             }
11844         }
11845         if( ! bIsUnique )
11846         {
11847             #if OSL_DEBUG_LEVEL > 1
11848             fprintf( stderr, "enforcing unique OnValues\n" );
11849             #endif
11850             // make unique by using ascending OnValues
11851             for( int nKid = 0; nKid < nChildren; nKid++ )
11852             {
11853                 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11854                 PDFWidget& rKid = m_aWidgets[nKidIndex];
11855                 rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) );
11856                 if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11857                     rKid.m_aValue = rKid.m_aOnValue;
11858             }
11859         }
11860         // finally move the "Yes" appearance to the OnValue appearance
11861         for( int nKid = 0; nKid < nChildren; nKid++ )
11862         {
11863             int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11864             PDFWidget& rKid = m_aWidgets[nKidIndex];
11865             PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
11866             if( app_it != rKid.m_aAppearances.end() )
11867             {
11868                 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
11869                 if( stream_it != app_it->second.end() )
11870                 {
11871                     SvMemoryStream* pStream = stream_it->second;
11872                     app_it->second.erase( stream_it );
11873                     OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
11874                     appendName( rKid.m_aOnValue, aBuf );
11875                     (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11876                 }
11877                 #if OSL_DEBUG_LEVEL > 1
11878                 else
11879                     fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" );
11880                 #endif
11881             }
11882             // update selected radio button
11883             if( ! rKid.m_aValue.equalsAscii( "Off" ) )
11884             {
11885                 rGroupWidget.m_aValue = rKid.m_aValue;
11886             }
11887         }
11888     }
11889 }
11890 
11891 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
11892 {
11893     sal_Int32 nRadioGroupWidget = -1;
11894 
11895     std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
11896 
11897     if( it == m_aRadioGroupWidgets.end() )
11898     {
11899         m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
11900             sal_Int32(m_aWidgets.size());
11901 
11902         // new group, insert the radiobutton
11903         m_aWidgets.push_back( PDFWidget() );
11904         m_aWidgets.back().m_nObject		= createObject();
11905         m_aWidgets.back().m_nPage		= m_nCurrentPage;
11906         m_aWidgets.back().m_eType		= PDFWriter::RadioButton;
11907         m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
11908         m_aWidgets.back().m_nFlags |= 0x0000C000;   // NoToggleToOff and Radio bits
11909 
11910         createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn );
11911     }
11912     else
11913         nRadioGroupWidget = it->second;
11914 
11915     return nRadioGroupWidget;
11916 }
11917 
11918 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
11919 {
11920     if( nPageNr < 0 )
11921         nPageNr = m_nCurrentPage;
11922 
11923     if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11924         return -1;
11925 
11926     sal_Int32 nNewWidget = m_aWidgets.size();
11927     m_aWidgets.push_back( PDFWidget() );
11928 
11929     m_aWidgets.back().m_nObject			= createObject();
11930     m_aWidgets.back().m_aRect				= rControl.Location;
11931     m_aWidgets.back().m_nPage				= nPageNr;
11932     m_aWidgets.back().m_eType				= rControl.getType();
11933 
11934     sal_Int32 nRadioGroupWidget = -1;
11935     // for unknown reasons the radio buttons of a radio group must not have a
11936     // field name, else the buttons are in fact check boxes -
11937     // that is multiple buttons of the radio group can be selected
11938     if( rControl.getType() == PDFWriter::RadioButton )
11939         nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
11940     else
11941     {
11942         createWidgetFieldName( nNewWidget, rControl );
11943     }
11944 
11945     // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
11946     PDFWidget& rNewWidget			= m_aWidgets[nNewWidget];
11947     rNewWidget.m_aDescription		= rControl.Description;
11948     rNewWidget.m_aText				= rControl.Text;
11949     rNewWidget.m_nTextStyle			= rControl.TextStyle &
11950         (  TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP |
11951            TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM |
11952            TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK  );
11953     rNewWidget.m_nTabOrder          = rControl.TabOrder;
11954 
11955     // various properties are set via the flags (/Ff) property of the field dict
11956     if( rControl.ReadOnly )
11957         rNewWidget.m_nFlags |= 1;
11958     if( rControl.getType() == PDFWriter::PushButton )
11959     {
11960         const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
11961         if( rNewWidget.m_nTextStyle == 0 )
11962             rNewWidget.m_nTextStyle =
11963                 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER |
11964                 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11965 
11966         rNewWidget.m_nFlags |= 0x00010000;
11967         if( rBtn.URL.getLength() )
11968             rNewWidget.m_aListEntries.push_back( rBtn.URL );
11969         rNewWidget.m_bSubmit    = rBtn.Submit;
11970         rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
11971         rNewWidget.m_nDest      = rBtn.Dest;
11972         createDefaultPushButtonAppearance( rNewWidget, rBtn );
11973     }
11974     else if( rControl.getType() == PDFWriter::RadioButton )
11975     {
11976         const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
11977         if( rNewWidget.m_nTextStyle == 0 )
11978             rNewWidget.m_nTextStyle =
11979                 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11980         /*  PDF sees a RadioButton group as one radio button with
11981          *  children which are in turn check boxes
11982          *
11983          *  so we need to create a radio button on demand for a new group
11984          *  and insert a checkbox for each RadioButtonWidget as its child
11985          */
11986         rNewWidget.m_eType			= PDFWriter::CheckBox;
11987         rNewWidget.m_nRadioGroup	= rBtn.RadioGroup;
11988 
11989         DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" );
11990 
11991         PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
11992         rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
11993         rRadioButton.m_aKidsIndex.push_back( nNewWidget );
11994         rNewWidget.m_nParent = rRadioButton.m_nObject;
11995 
11996         rNewWidget.m_aValue     = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) );
11997         rNewWidget.m_aOnValue   = rBtn.OnValue;
11998         if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected )
11999         {
12000             rNewWidget.m_aValue		= rNewWidget.m_aOnValue;
12001             rRadioButton.m_aValue	= rNewWidget.m_aOnValue;
12002         }
12003         createDefaultRadioButtonAppearance( rNewWidget, rBtn );
12004 
12005         // union rect of radio group
12006         Rectangle aRect = rNewWidget.m_aRect;
12007         m_aPages[ nPageNr ].convertRect( aRect );
12008         rRadioButton.m_aRect.Union( aRect );
12009     }
12010     else if( rControl.getType() == PDFWriter::CheckBox )
12011     {
12012         const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
12013         if( rNewWidget.m_nTextStyle == 0 )
12014             rNewWidget.m_nTextStyle =
12015                 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12016 
12017         rNewWidget.m_aValue = OUString::createFromAscii( rBox.Checked ? "Yes" : "Off" );
12018         // create default appearance before m_aRect gets transformed
12019         createDefaultCheckBoxAppearance( rNewWidget, rBox );
12020     }
12021     else if( rControl.getType() == PDFWriter::ListBox )
12022     {
12023         if( rNewWidget.m_nTextStyle == 0 )
12024             rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
12025 
12026         const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
12027         rNewWidget.m_aListEntries	  = rLstBox.Entries;
12028         rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
12029         rNewWidget.m_aValue			  = rLstBox.Text;
12030         if( rLstBox.DropDown )
12031             rNewWidget.m_nFlags |= 0x00020000;
12032         if( rLstBox.Sort )
12033             rNewWidget.m_nFlags |= 0x00080000;
12034         if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 )
12035             rNewWidget.m_nFlags |= 0x00200000;
12036 
12037         createDefaultListBoxAppearance( rNewWidget, rLstBox );
12038     }
12039     else if( rControl.getType() == PDFWriter::ComboBox )
12040     {
12041         if( rNewWidget.m_nTextStyle == 0 )
12042             rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
12043 
12044         const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
12045         rNewWidget.m_aValue			= rBox.Text;
12046         rNewWidget.m_aListEntries	= rBox.Entries;
12047         rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
12048         if( rBox.Sort )
12049             rNewWidget.m_nFlags |= 0x00080000;
12050 
12051         PDFWriter::ListBoxWidget aLBox;
12052         aLBox.Name				= rBox.Name;
12053         aLBox.Description		= rBox.Description;
12054         aLBox.Text				= rBox.Text;
12055         aLBox.TextStyle			= rBox.TextStyle;
12056         aLBox.ReadOnly			= rBox.ReadOnly;
12057         aLBox.Border			= rBox.Border;
12058         aLBox.BorderColor		= rBox.BorderColor;
12059         aLBox.Background		= rBox.Background;
12060         aLBox.BackgroundColor	= rBox.BackgroundColor;
12061         aLBox.TextFont			= rBox.TextFont;
12062         aLBox.TextColor			= rBox.TextColor;
12063         aLBox.DropDown			= true;
12064         aLBox.Sort				= rBox.Sort;
12065         aLBox.MultiSelect		= false;
12066         aLBox.Entries			= rBox.Entries;
12067 
12068         createDefaultListBoxAppearance( rNewWidget, aLBox );
12069     }
12070     else if( rControl.getType() == PDFWriter::Edit )
12071     {
12072         if( rNewWidget.m_nTextStyle == 0 )
12073             rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
12074 
12075         const PDFWriter::EditWidget& rEdit = static_cast<const  PDFWriter::EditWidget&>(rControl);
12076         if( rEdit.MultiLine )
12077         {
12078             rNewWidget.m_nFlags |= 0x00001000;
12079             rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12080         }
12081         if( rEdit.Password )
12082             rNewWidget.m_nFlags |= 0x00002000;
12083         if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 )
12084             rNewWidget.m_nFlags |= 0x00100000;
12085         rNewWidget.m_nMaxLen = rEdit.MaxLen;
12086         rNewWidget.m_aValue = rEdit.Text;
12087 
12088         createDefaultEditAppearance( rNewWidget, rEdit );
12089     }
12090 
12091     // convert to default user space now, since the mapmode may change
12092     // note: create default appearances before m_aRect gets transformed
12093     m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
12094 
12095     // insert widget to page's annotation list
12096     m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
12097 
12098     // mark page as having widgets
12099     m_aPages[ nPageNr ].m_bHasWidgets = true;
12100 
12101     return nNewWidget;
12102 }
12103 
12104 void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl )
12105 {
12106     if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() )
12107         return;
12108 
12109     PDFWidget& rWidget = m_aWidgets[ nControl ];
12110     m_nCurrentControl = nControl;
12111 
12112     SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 );
12113     // back conversion of control rect to current MapMode; necessary because
12114     // MapMode between createControl and beginControlAppearance
12115     // could have changed; therefore the widget rectangle is
12116     // already converted
12117     Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ),
12118                      rWidget.m_aRect.GetSize() );
12119     aBack = lcl_convert( m_aMapMode,
12120                          m_aGraphicsStack.front().m_aMapMode,
12121                          getReferenceDevice(),
12122                          aBack );
12123     beginRedirect( pControlStream, aBack );
12124     writeBuffer( "/Tx BMC\n", 8 );
12125 }
12126 
12127 bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState )
12128 {
12129     bool bRet = false;
12130     if( ! m_aOutputStreams.empty() )
12131         writeBuffer( "\nEMC\n", 5 );
12132     SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect());
12133     if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() )
12134     {
12135         PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ];
12136         OString aState, aStyle;
12137         switch( rWidget.m_eType )
12138         {
12139             case PDFWriter::PushButton:
12140                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12141                 {
12142                     aState = (eState == PDFWriter::Up) ? "N" : "D";
12143                     aStyle = "Standard";
12144                 }
12145                 break;
12146             case PDFWriter::CheckBox:
12147                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12148                 {
12149                     aState = "N";
12150                     aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes";
12151                     /* cf PDFReference 3rd ed. V1.4 p539:
12152                        recommended name for on state is "Yes",
12153                        recommended name for off state is "Off"
12154                      */
12155                 }
12156                 break;
12157             case PDFWriter::RadioButton:
12158                 if( eState == PDFWriter::Up || eState == PDFWriter::Down )
12159                 {
12160                     aState = "N";
12161                     if( eState == PDFWriter::Up )
12162                         aStyle = "Off";
12163                     else
12164                     {
12165                         OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 );
12166                         appendName( rWidget.m_aOnValue, aBuf );
12167                         aStyle = aBuf.makeStringAndClear();
12168                     }
12169                 }
12170                 break;
12171             case PDFWriter::Edit:
12172                 aState = "N";
12173                 aStyle = "Standard";
12174                 break;
12175             case PDFWriter::ListBox:
12176             case PDFWriter::ComboBox:
12177             case PDFWriter::Hierarchy:
12178                 break;
12179         }
12180         if( aState.getLength() && aStyle.getLength() )
12181         {
12182             // delete eventual existing stream
12183             PDFAppearanceStreams::iterator it =
12184                 rWidget.m_aAppearances[ aState ].find( aStyle );
12185             if( it != rWidget.m_aAppearances[ aState ].end() )
12186                 delete it->second;
12187             rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance;
12188             bRet = true;
12189         }
12190     }
12191 
12192     if( ! bRet )
12193         delete pAppearance;
12194 
12195     m_nCurrentControl = -1;
12196 
12197     return bRet;
12198 }
12199 
12200 void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress )
12201 {
12202     if( pStream )
12203     {
12204         m_aAdditionalStreams.push_back( PDFAddStream() );
12205         PDFAddStream& rStream = m_aAdditionalStreams.back();
12206         rStream.m_aMimeType = rMimeType.Len()
12207                               ? OUString( rMimeType )
12208                               : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) );
12209         rStream.m_pStream = pStream;
12210         rStream.m_bCompress = bCompress;
12211     }
12212 }
12213 
12214 
12215 
12216