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