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