Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #define _USE_MATH_DEFINES
21 : #include <math.h>
22 : #include <algorithm>
23 :
24 : #include <tools/urlobj.hxx>
25 :
26 : #include <pdfwriter_impl.hxx>
27 :
28 : #include <basegfx/polygon/b2dpolygon.hxx>
29 : #include <basegfx/polygon/b2dpolypolygon.hxx>
30 : #include <basegfx/polygon/b2dpolygontools.hxx>
31 : #include <basegfx/polygon/b2dpolypolygontools.hxx>
32 : #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
33 : #include <basegfx/matrix/b2dhommatrix.hxx>
34 :
35 : #include <osl/thread.h>
36 : #include <osl/file.h>
37 :
38 : #include <rtl/crc.h>
39 : #include <rtl/digest.h>
40 : #include <rtl/ustrbuf.hxx>
41 :
42 : #include <tools/debug.hxx>
43 : #include <tools/zcodec.hxx>
44 : #include <tools/stream.hxx>
45 :
46 : #include <i18npool/languagetag.hxx>
47 :
48 : #include <vcl/virdev.hxx>
49 : #include <vcl/bmpacc.hxx>
50 : #include <vcl/bitmapex.hxx>
51 : #include <vcl/image.hxx>
52 : #include <vcl/metric.hxx>
53 : #include <vcl/svapp.hxx>
54 : #include <vcl/lineinfo.hxx>
55 : #include "vcl/cvtgrf.hxx"
56 : #include "vcl/strhelper.hxx"
57 :
58 : #include <fontsubset.hxx>
59 : #include <outdev.h>
60 : #include <sallayout.hxx>
61 : #include <textlayout.hxx>
62 : #include <salgdi.hxx>
63 :
64 : #include <lcms2.h>
65 :
66 : #include <comphelper/processfactory.hxx>
67 : #include <comphelper/string.hxx>
68 :
69 : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
70 : #include <com/sun/star/util/URLTransformer.hpp>
71 : #include <com/sun/star/util/URL.hpp>
72 :
73 : #include "cppuhelper/implbase1.hxx"
74 :
75 : #if !defined(ANDROID) && !defined(IOS)
76 : // NSS header files for PDF signing support
77 : #include "nss.h"
78 : #include "cert.h"
79 : #include "hasht.h"
80 : #include "sechash.h"
81 : #include "cms.h"
82 : #include "cmst.h"
83 : #endif
84 :
85 : using namespace vcl;
86 :
87 : using ::rtl::OUString;
88 : using ::rtl::OUStringToOString;
89 : using ::rtl::OString;
90 : using ::rtl::OStringHash;
91 : using ::rtl::OUStringHash;
92 : using ::rtl::OStringBuffer;
93 : using ::rtl::OUStringBuffer;
94 :
95 : #if (OSL_DEBUG_LEVEL < 3)
96 : #define COMPRESS_PAGES
97 : #else
98 : #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams
99 : #endif
100 :
101 : #define MAX_SIGNATURE_CONTENT_LENGTH 0x4000
102 :
103 : #ifdef DO_TEST_PDF
104 : class PDFTestOutputStream : public PDFOutputStream
105 : {
106 : public:
107 : virtual ~PDFTestOutputStream();
108 : virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream );
109 : };
110 :
111 : PDFTestOutputStream::~PDFTestOutputStream()
112 : {
113 : }
114 :
115 : void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream )
116 : {
117 : OString aStr( "lalala\ntest\ntest\ntest" );
118 : com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() );
119 : memcpy( aData.getArray(), aStr.getStr(), aStr.getLength() );
120 : xStream->writeBytes( aData );
121 : }
122 :
123 : // this test code cannot be used to test PDF/A-1 because it forces
124 : // control item (widgets) to bypass the structure controlling
125 : // the embedding of such elements in actual run
126 : void doTestCode()
127 : {
128 : static const char* pHome = getenv( "HOME" );
129 : rtl::OUString aTestFile( "file://" );
130 : aTestFile += rtl::OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 );
131 : aTestFile += rtl::OUString( "/pdf_export_test.pdf" );
132 :
133 : PDFWriter::PDFWriterContext aContext;
134 : aContext.URL = aTestFile;
135 : aContext.Version = PDFWriter::PDF_1_4;
136 : aContext.Tagged = true;
137 : aContext.InitialPage = 2;
138 : aContext.DocumentInfo.Title = OUString( "PDF export test document" );
139 : aContext.DocumentInfo.Producer = OUString( "VCL" );
140 :
141 : aContext.SignPDF = true;
142 : aContext.SignLocation = OUString( "Burdur" );
143 : aContext.SignReason = OUString( "Some valid reason to sign" );
144 : aContext.SignContact = OUString( "signer@example.com" );
145 :
146 : com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder > xEnc;
147 : PDFWriter aWriter( aContext, xEnc );
148 : aWriter.NewPage( 595, 842 );
149 : aWriter.BeginStructureElement( PDFWriter::Document );
150 : // set duration of 3 sec for first page
151 : aWriter.SetAutoAdvanceTime( 3 );
152 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
153 :
154 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
155 : aWriter.SetLineColor( Color( COL_LIGHTGREEN ) );
156 : aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
157 :
158 : aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
159 : aWriter.SetTextColor( Color( COL_BLACK ) );
160 : aWriter.SetLineColor( Color( COL_BLACK ) );
161 : aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
162 :
163 : Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
164 : aWriter.DrawRect( aRect );
165 : aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 1" ) ) );
166 : sal_Int32 nFirstLink = aWriter.CreateLink( aRect );
167 : PDFNote aNote;
168 : aNote.Title = String( RTL_CONSTASCII_USTRINGPARAM( "A small test note" ) );
169 : aNote.Contents = String( RTL_CONSTASCII_USTRINGPARAM( "There is no business like show business like no business i know. Everything about it is appealing." ) );
170 : aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote );
171 :
172 : Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
173 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
174 : aWriter.DrawRect( aTargetRect );
175 : aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest second link" ) ) );
176 : sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect );
177 :
178 : aWriter.BeginStructureElement( PDFWriter::Section );
179 : aWriter.BeginStructureElement( PDFWriter::Heading );
180 : aWriter.DrawText( Point(4500, 9000), String( RTL_CONSTASCII_USTRINGPARAM( "A small structure test" ) ) );
181 : aWriter.EndStructureElement();
182 : aWriter.BeginStructureElement( PDFWriter::Paragraph );
183 : aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
184 : aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline );
185 : aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
186 : 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." ) ),
187 : TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
188 : );
189 : 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." ) ) );
190 : aWriter.SetAlternateText( String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." ) ) );
191 : aWriter.EndStructureElement();
192 : aWriter.BeginStructureElement( PDFWriter::Paragraph );
193 : aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
194 : aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
195 : String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph is nothing special either but ends on the next page structurewise" ) ),
196 : TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
197 : );
198 :
199 : aWriter.NewPage( 595, 842 );
200 : // test AddStream interface
201 : aWriter.AddStream( String( RTL_CONSTASCII_USTRINGPARAM( "text/plain" ) ), new PDFTestOutputStream(), true );
202 : // set transitional mode
203 : aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 );
204 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
205 : aWriter.SetTextColor( Color( COL_BLACK ) );
206 : aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
207 : aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
208 : String( RTL_CONSTASCII_USTRINGPARAM( "Here's where all things come to an end ... well at least the paragaph from the last page." ) ),
209 : TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
210 : );
211 : aWriter.EndStructureElement();
212 :
213 : aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
214 : // disable structure
215 : aWriter.BeginStructureElement( PDFWriter::NonStructElement );
216 : aWriter.DrawRect( aRect );
217 : aWriter.BeginStructureElement( PDFWriter::Paragraph );
218 : aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 2" ) ) );
219 : sal_Int32 nSecondLink = aWriter.CreateLink( aRect );
220 :
221 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
222 : aWriter.BeginStructureElement( PDFWriter::ListItem );
223 : aWriter.DrawRect( aTargetRect );
224 : aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest first link" ) ) );
225 : sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect );
226 : // enable structure
227 : aWriter.EndStructureElement();
228 :
229 : // add something to the long paragraph as an afterthought
230 : /* PDFWriter::aWriter.GetCurrentStructureElement removed as an unusedcode.easy item:
231 : http://cgit.freedesktop.org/libreoffice/core/commit/?id=09279fe3dad24ab58121e4f0a9564d252b64d81a
232 :
233 : sal_Int32 nSaveStruct = aWriter.GetCurrentStructureElement();
234 : aWriter.SetCurrentStructureElement( nLongPara );
235 : aWriter.DrawText( Rectangle( Point( 4500,4500 ), Size( 12000, 1000 ) ),
236 : String( RTL_CONSTASCII_USTRINGPARAM( "Add something to the longish paragraph above." ) ),
237 : TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
238 : aWriter.SetCurrentStructureElement( nSaveStruct );
239 : */
240 :
241 : aWriter.EndStructureElement();
242 : aWriter.EndStructureElement();
243 : aWriter.BeginStructureElement( PDFWriter::Figure );
244 : aWriter.BeginStructureElement( PDFWriter::Caption );
245 : aWriter.DrawText( Point( 4500, 9000 ), String( RTL_CONSTASCII_USTRINGPARAM( "Some drawing stuff inside the structure" ) ) );
246 : aWriter.EndStructureElement();
247 :
248 : // test clipping
249 : basegfx::B2DPolyPolygon aClip;
250 : basegfx::B2DPolygon aClipPoly;
251 : aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) );
252 : aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) );
253 : aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) );
254 : aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) );
255 : aClipPoly.setClosed( true );
256 : //aClipPoly.flip();
257 : aClip.append( aClipPoly );
258 :
259 : aWriter.Push( PUSH_CLIPREGION | PUSH_FILLCOLOR );
260 : aWriter.SetClipRegion( aClip );
261 : aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
262 : aWriter.MoveClipRegion( 1000, 500 );
263 : aWriter.SetFillColor( Color( COL_RED ) );
264 : aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
265 : aWriter.Pop();
266 : // test transparency
267 : // draw background
268 : Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
269 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
270 : aWriter.DrawRect( aTranspRect );
271 : aWriter.BeginTransparencyGroup();
272 :
273 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
274 : aWriter.DrawEllipse( aTranspRect );
275 : aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
276 : aWriter.DrawText( aTranspRect,
277 : String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
278 : TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
279 :
280 : aWriter.EndTransparencyGroup( aTranspRect, 50 );
281 :
282 : // prepare an alpha mask
283 : Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
284 : BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess();
285 : for( int nX = 0; nX < 256; nX++ )
286 : for( int nY = 0; nY < 256; nY++ )
287 : pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) );
288 : aTransMask.ReleaseAccess( pAcc );
289 : aTransMask.SetPrefMapMode( MAP_MM );
290 : aTransMask.SetPrefSize( Size( 10, 10 ) );
291 :
292 : aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask );
293 :
294 : aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
295 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
296 : aWriter.DrawRect( aTranspRect );
297 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
298 : aWriter.DrawEllipse( aTranspRect );
299 : aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
300 : aWriter.DrawText( aTranspRect,
301 : String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
302 : TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
303 : aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
304 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
305 : aWriter.DrawRect( aTranspRect );
306 :
307 : /*
308 : EndTransparencyGroup( const Rectangle& rBoundRect, const Bitmap& rAlphaMask ) is removed as an unusedcode.easy item:
309 : http://cgit.freedesktop.org/libreoffice/core/commit/?id=581e7d7057afa87036d84e42c0e0a8a7368e20c7
310 :
311 : aWriter.BeginTransparencyGroup();
312 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
313 : aWriter.DrawEllipse( aTranspRect );
314 : aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
315 : aWriter.DrawText( aTranspRect,
316 : String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ),
317 : TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
318 : aWriter.EndTransparencyGroup( aTranspRect, aTransMask );
319 : */
320 :
321 : Bitmap aImageBmp( Size( 256, 256 ), 24 );
322 : pAcc = aImageBmp.AcquireWriteAccess();
323 : pAcc->SetFillColor( Color( 0xff, 0, 0xff ) );
324 : pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
325 : aImageBmp.ReleaseAccess( pAcc );
326 : BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) );
327 : aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx );
328 :
329 :
330 : aWriter.EndStructureElement();
331 : aWriter.EndStructureElement();
332 :
333 : LineInfo aLI( LINE_DASH, 3 );
334 : aLI.SetDashCount( 2 );
335 : aLI.SetDashLen( 50 );
336 : aLI.SetDotCount( 2 );
337 : aLI.SetDotLen( 25 );
338 : aLI.SetDistance( 15 );
339 : Point aLIPoints[] = { Point( 4000, 10000 ),
340 : Point( 8000, 12000 ),
341 : Point( 3000, 19000 ) };
342 : Polygon aLIPoly( 3, aLIPoints );
343 : aWriter.SetLineColor( Color( COL_BLUE ) );
344 : aWriter.SetFillColor();
345 : aWriter.DrawPolyLine( aLIPoly, aLI );
346 :
347 : aLI.SetDashCount( 4 );
348 : aLIPoly.Move( 1000, 1000 );
349 : aWriter.DrawPolyLine( aLIPoly, aLI );
350 :
351 : aWriter.NewPage( 595, 842 );
352 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
353 : Wallpaper aWall( aTransMask );
354 : aWall.SetStyle( WALLPAPER_TILE );
355 : aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall );
356 :
357 : /*
358 : BeginPattern/EndPattern is removed as an unusedcode.easy item:
359 : http://cgit.freedesktop.org/libreoffice/core/commit/?id=581e7d7057afa87036d84e42c0e0a8a7368e20c7
360 :
361 : aWriter.Push( PUSH_ALL );
362 : aWriter.BeginPattern(Rectangle(Point(0,0),Size(2000,1000)));
363 : aWriter.SetFillColor( Color( COL_RED ) );
364 : aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
365 : Point aFillPoints[] = { Point( 1000, 0 ),
366 : Point( 0, 1000 ),
367 : Point( 2000, 1000 ) };
368 : aWriter.DrawPolygon( Polygon( 3, aFillPoints ) );
369 : aWriter.DrawBitmap( Point( 200, 200 ), Size( 1600, 600 ), aTransMask );
370 : aWriter.DrawText( Rectangle( Point( 200, 200 ), Size( 1600, 600 ) ), String( RTL_CONSTASCII_USTRINGPARAM( "Pattern" ) ) );
371 : sal_Int32 nPattern = aWriter.EndPattern( SvtGraphicFill::Transform() );
372 : aWriter.Pop();
373 : Rectangle aPolyRect( Point( 3800, 11200 ), Size( 10200, 6300 ) );
374 : aWriter.DrawPolyPolygon( PolyPolygon( Polygon( aPolyRect ) ), nPattern, true );
375 : aWriter.SetFillColor();
376 : aWriter.SetLineColor( Color( COL_LIGHTBLUE ) );
377 : aWriter.DrawRect( aPolyRect );
378 : */
379 :
380 : aWriter.NewPage( 595, 842 );
381 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
382 : aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) );
383 : aWriter.SetTextColor( Color( COL_BLACK ) );
384 : aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
385 : aWriter.DrawRect( aRect );
386 : aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "www.heise.de" ) ) );
387 : sal_Int32 nURILink = aWriter.CreateLink( aRect );
388 : aWriter.SetLinkURL( nURILink, OUString( "http://www.heise.de" ) );
389 :
390 : aWriter.SetLinkDest( nFirstLink, nFirstDest );
391 : aWriter.SetLinkDest( nSecondLink, nSecondDest );
392 :
393 : // include a button
394 : PDFWriter::PushButtonWidget aBtn;
395 : aBtn.Name = OUString( "testButton" );
396 : aBtn.Description = OUString( "A test button" );
397 : aBtn.Text = OUString( "hit me" );
398 : aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
399 : aBtn.Border = aBtn.Background = true;
400 : aWriter.CreateControl( aBtn );
401 :
402 : // include a uri button
403 : PDFWriter::PushButtonWidget aUriBtn;
404 : aUriBtn.Name = OUString( "wwwButton" );
405 : aUriBtn.Description = OUString( "A URI button" );
406 : aUriBtn.Text = OUString( "to www" );
407 : aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
408 : aUriBtn.Border = aUriBtn.Background = true;
409 : aUriBtn.URL = OUString( "http://www.heise.de" );
410 : aWriter.CreateControl( aUriBtn );
411 :
412 : // include a dest button
413 : PDFWriter::PushButtonWidget aDstBtn;
414 : aDstBtn.Name = OUString( "destButton" );
415 : aDstBtn.Description = OUString( "A Dest button" );
416 : aDstBtn.Text = OUString( "to paragraph" );
417 : aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
418 : aDstBtn.Border = aDstBtn.Background = true;
419 : aDstBtn.Dest = nFirstDest;
420 : aWriter.CreateControl( aDstBtn );
421 :
422 : PDFWriter::CheckBoxWidget aCBox;
423 : aCBox.Name = OUString( "textCheckBox" );
424 : aCBox.Description = OUString( "A test check box" );
425 : aCBox.Text = OUString( "check me" );
426 : aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
427 : aCBox.Checked = true;
428 : aCBox.Border = aCBox.Background = false;
429 : aWriter.CreateControl( aCBox );
430 :
431 : PDFWriter::CheckBoxWidget aCBox2;
432 : aCBox2.Name = OUString( "textCheckBox2" );
433 : aCBox2.Description = OUString( "Another test check box" );
434 : aCBox2.Text = OUString( "check me right" );
435 : aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
436 : aCBox2.Checked = true;
437 : aCBox2.Border = aCBox2.Background = false;
438 : aCBox2.ButtonIsLeft = false;
439 : aWriter.CreateControl( aCBox2 );
440 :
441 : PDFWriter::RadioButtonWidget aRB1;
442 : aRB1.Name = OUString( "rb1_1" );
443 : aRB1.Description = OUString( "radio 1 button 1" );
444 : aRB1.Text = OUString( "Despair" );
445 : aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
446 : aRB1.Selected = true;
447 : aRB1.RadioGroup = 1;
448 : aRB1.Border = aRB1.Background = true;
449 : aRB1.ButtonIsLeft = false;
450 : aRB1.BorderColor = Color( COL_LIGHTGREEN );
451 : aRB1.BackgroundColor = Color( COL_LIGHTBLUE );
452 : aRB1.TextColor = Color( COL_LIGHTRED );
453 : aRB1.TextFont = Font( String( RTL_CONSTASCII_USTRINGPARAM( "Courier" ) ), Size( 0, 800 ) );
454 : aWriter.CreateControl( aRB1 );
455 :
456 : PDFWriter::RadioButtonWidget aRB2;
457 : aRB2.Name = OUString( "rb2_1" );
458 : aRB2.Description = OUString( "radio 2 button 1" );
459 : aRB2.Text = OUString( "Joy" );
460 : aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
461 : aRB2.Selected = true;
462 : aRB2.RadioGroup = 2;
463 : aWriter.CreateControl( aRB2 );
464 :
465 : PDFWriter::RadioButtonWidget aRB3;
466 : aRB3.Name = OUString( "rb1_2" );
467 : aRB3.Description = OUString( "radio 1 button 2" );
468 : aRB3.Text = OUString( "Desperation" );
469 : aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
470 : aRB3.Selected = true;
471 : aRB3.RadioGroup = 1;
472 : aWriter.CreateControl( aRB3 );
473 :
474 : PDFWriter::EditWidget aEditBox;
475 : aEditBox.Name = OUString( "testEdit" );
476 : aEditBox.Description = OUString( "A test edit field" );
477 : aEditBox.Text = OUString( "A little test text" );
478 : aEditBox.TextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
479 : aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
480 : aEditBox.MaxLen = 100;
481 : aEditBox.Border = aEditBox.Background = true;
482 : aEditBox.BorderColor = Color( COL_BLACK );
483 : aWriter.CreateControl( aEditBox );
484 :
485 : // normal list box
486 : PDFWriter::ListBoxWidget aLstBox;
487 : aLstBox.Name = OUString( "testListBox" );
488 : aLstBox.Text = OUString( "One" );
489 : aLstBox.Description = OUString( "select me" );
490 : aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
491 : aLstBox.Sort = true;
492 : aLstBox.MultiSelect = true;
493 : aLstBox.Border = aLstBox.Background = true;
494 : aLstBox.BorderColor = Color( COL_BLACK );
495 : aLstBox.Entries.push_back( OUString( "One" ) );
496 : aLstBox.Entries.push_back( OUString( "Two" ) );
497 : aLstBox.Entries.push_back( OUString( "Three" ) );
498 : aLstBox.Entries.push_back( OUString( "Four" ) );
499 : aLstBox.SelectedEntries.push_back( 1 );
500 : aLstBox.SelectedEntries.push_back( 2 );
501 : aWriter.CreateControl( aLstBox );
502 :
503 : // dropdown list box
504 : aLstBox.Name = OUString( "testDropDownListBox" );
505 : aLstBox.DropDown = true;
506 : aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
507 : aWriter.CreateControl( aLstBox );
508 :
509 : // combo box
510 : PDFWriter::ComboBoxWidget aComboBox;
511 : aComboBox.Name = OUString( "testComboBox" );
512 : aComboBox.Text = OUString( "test a combobox" );
513 : aComboBox.Entries.push_back( OUString( "Larry" ) );
514 : aComboBox.Entries.push_back( OUString( "Curly" ) );
515 : aComboBox.Entries.push_back( OUString( "Moe" ) );
516 : aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
517 : aWriter.CreateControl( aComboBox );
518 :
519 : // test outlines
520 : sal_Int32 nPage1OL = aWriter.CreateOutlineItem();
521 : aWriter.SetOutlineItemText( nPage1OL, OUString( "Page 1" ) );
522 : aWriter.SetOutlineItemDest( nPage1OL, nSecondDest );
523 : aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2" ), nSecondDest );
524 : aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 revisited" ), nSecondDest );
525 : aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 again" ), nSecondDest );
526 : sal_Int32 nPage2OL = aWriter.CreateOutlineItem();
527 : aWriter.SetOutlineItemText( nPage2OL, OUString( "Page 2" ) );
528 : aWriter.CreateOutlineItem( nPage2OL, OUString( "Dest 1" ), nFirstDest );
529 :
530 : aWriter.EndStructureElement(); // close document
531 :
532 : aWriter.Emit();
533 : }
534 : #endif
535 :
536 : static const sal_Int32 nLog10Divisor = 1;
537 : static const double fDivisor = 10.0;
538 :
539 : static inline double pixelToPoint( sal_Int32 px ) { return double(px)/fDivisor; }
540 0 : static inline double pixelToPoint( double px ) { return px/fDivisor; }
541 0 : static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); }
542 :
543 : const sal_uInt8 PDFWriterImpl::s_nPadString[32] =
544 : {
545 : 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
546 : 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
547 : };
548 :
549 0 : static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
550 : {
551 : static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
552 : '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
553 0 : rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
554 0 : rBuffer.append( pHexDigits[ nInt & 15 ] );
555 0 : }
556 :
557 0 : static void appendName( const OUString& rStr, OStringBuffer& rBuffer )
558 : {
559 : // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
560 : // I guess than when reading the #xx sequence it will count for a single character.
561 0 : OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) );
562 0 : const sal_Char* pStr = aStr.getStr();
563 0 : int nLen = aStr.getLength();
564 0 : for( int i = 0; i < nLen; i++ )
565 : {
566 : /* #i16920# PDF recommendation: output UTF8, any byte
567 : * outside the interval [33(=ASCII'!');126(=ASCII'~')]
568 : * should be escaped hexadecimal
569 : * for the sake of ghostscript which also reads PDF
570 : * but has a narrower acceptance rate we only pass
571 : * alphanumerics and '-' literally.
572 : */
573 0 : if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) ||
574 0 : (pStr[i] >= 'a' && pStr[i] <= 'z' ) ||
575 0 : (pStr[i] >= '0' && pStr[i] <= '9' ) ||
576 0 : pStr[i] == '-' )
577 : {
578 0 : rBuffer.append( pStr[i] );
579 : }
580 : else
581 : {
582 0 : rBuffer.append( '#' );
583 0 : appendHex( (sal_Int8)pStr[i], rBuffer );
584 : }
585 0 : }
586 0 : }
587 :
588 0 : static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer )
589 : {
590 : //FIXME i59651 see above
591 0 : while( pStr && *pStr )
592 : {
593 0 : if( (*pStr >= 'A' && *pStr <= 'Z' ) ||
594 : (*pStr >= 'a' && *pStr <= 'z' ) ||
595 : (*pStr >= '0' && *pStr <= '9' ) ||
596 : *pStr == '-' )
597 : {
598 0 : rBuffer.append( *pStr );
599 : }
600 : else
601 : {
602 0 : rBuffer.append( '#' );
603 0 : appendHex( (sal_Int8)*pStr, rBuffer );
604 : }
605 0 : pStr++;
606 : }
607 0 : }
608 :
609 : //used only to emit encoded passwords
610 0 : static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer )
611 : {
612 0 : while( nLength )
613 : {
614 0 : switch( *pStr )
615 : {
616 : case '\n' :
617 0 : rBuffer.append( "\\n" );
618 0 : break;
619 : case '\r' :
620 0 : rBuffer.append( "\\r" );
621 0 : break;
622 : case '\t' :
623 0 : rBuffer.append( "\\t" );
624 0 : break;
625 : case '\b' :
626 0 : rBuffer.append( "\\b" );
627 0 : break;
628 : case '\f' :
629 0 : rBuffer.append( "\\f" );
630 0 : break;
631 : case '(' :
632 : case ')' :
633 : case '\\' :
634 0 : rBuffer.append( "\\" );
635 0 : rBuffer.append( (sal_Char) *pStr );
636 0 : break;
637 : default:
638 0 : rBuffer.append( (sal_Char) *pStr );
639 0 : break;
640 : }
641 0 : pStr++;
642 0 : nLength--;
643 : }
644 0 : }
645 :
646 : /**--->i56629
647 : * Convert a string before using it.
648 : *
649 : * This string conversion function is needed because the destination name
650 : * in a PDF file seen through an Internet browser should be
651 : * specially crafted, in order to be used directly by the browser.
652 : * In this way the fragment part of a hyperlink to a PDF file (e.g. something
653 : * as 'test1/test2/a-file.pdf#thefragment) will be (hopefully) interpreted by the
654 : * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
655 : * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
656 : * and go to named destination thefragment using default zoom'.
657 : * The conversion is needed because in case of a fragment in the form: Slide%201
658 : * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
659 : * using this conversion, in both the generated named destinations, fragment and GoToR
660 : * destination.
661 : *
662 : * The names for destinations are name objects and so they don't need to be encrypted
663 : * even though they expose the content of PDF file (e.g. guessing the PDF content from the
664 : * destination name).
665 : *
666 : * Fhurter limitation: it is advisable to use standard ASCII characters for
667 : * OOo bookmarks.
668 : */
669 0 : static void appendDestinationName( const rtl::OUString& rString, OStringBuffer& rBuffer )
670 : {
671 0 : const sal_Unicode* pStr = rString.getStr();
672 0 : sal_Int32 nLen = rString.getLength();
673 0 : for( int i = 0; i < nLen; i++ )
674 : {
675 0 : sal_Unicode aChar = pStr[i];
676 0 : if( (aChar >= '0' && aChar <= '9' ) ||
677 : (aChar >= 'a' && aChar <= 'z' ) ||
678 : (aChar >= 'A' && aChar <= 'Z' ) ||
679 : aChar == '-' )
680 : {
681 0 : rBuffer.append((sal_Char)aChar);
682 : }
683 : else
684 : {
685 0 : sal_Int8 aValueHigh = sal_Int8(aChar >> 8);
686 0 : if(aValueHigh > 0)
687 0 : appendHex( aValueHigh, rBuffer );
688 0 : appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
689 : }
690 : }
691 0 : }
692 : //<--- i56629
693 :
694 0 : static void appendUnicodeTextString( const rtl::OUString& rString, OStringBuffer& rBuffer )
695 : {
696 0 : rBuffer.append( "FEFF" );
697 0 : const sal_Unicode* pStr = rString.getStr();
698 0 : sal_Int32 nLen = rString.getLength();
699 0 : for( int i = 0; i < nLen; i++ )
700 : {
701 0 : sal_Unicode aChar = pStr[i];
702 0 : appendHex( (sal_Int8)(aChar >> 8), rBuffer );
703 0 : appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
704 : }
705 0 : }
706 :
707 0 : void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl )
708 : {
709 : /* #i80258# previously we use appendName here
710 : however we need a slightly different coding scheme than the normal
711 : name encoding for field names
712 : */
713 0 : const OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ? i_rControl.Name : i_rControl.Text;
714 0 : OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) );
715 0 : const sal_Char* pStr = aStr.getStr();
716 0 : int nLen = aStr.getLength();
717 :
718 0 : OStringBuffer aBuffer( rName.getLength()+64 );
719 0 : for( int i = 0; i < nLen; i++ )
720 : {
721 : /* #i16920# PDF recommendation: output UTF8, any byte
722 : * outside the interval [32(=ASCII' ');126(=ASCII'~')]
723 : * should be escaped hexadecimal
724 : */
725 0 : if( (pStr[i] >= 32 && pStr[i] <= 126 ) )
726 0 : aBuffer.append( pStr[i] );
727 : else
728 : {
729 0 : aBuffer.append( '#' );
730 0 : appendHex( (sal_Int8)pStr[i], aBuffer );
731 : }
732 : }
733 :
734 0 : OString aFullName( aBuffer.makeStringAndClear() );
735 :
736 : /* #i82785# create hierarchical fields down to the for each dot in i_rName */
737 0 : sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0;
738 0 : OString aPartialName;
739 0 : OString aDomain;
740 0 : do
741 : {
742 0 : nLastTokenIndex = nTokenIndex;
743 0 : aPartialName = aFullName.getToken( 0, '.', nTokenIndex );
744 0 : if( nTokenIndex != -1 )
745 : {
746 : // find or create a hierarchical field
747 : // first find the fully qualified name up to this field
748 0 : aDomain = aFullName.copy( 0, nTokenIndex-1 );
749 0 : boost::unordered_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
750 0 : if( it == m_aFieldNameMap.end() )
751 : {
752 : // create new hierarchy field
753 0 : sal_Int32 nNewWidget = m_aWidgets.size();
754 0 : m_aWidgets.push_back( PDFWidget() );
755 0 : m_aWidgets[nNewWidget].m_nObject = createObject();
756 0 : m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy;
757 0 : m_aWidgets[nNewWidget].m_aName = aPartialName;
758 0 : m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
759 0 : m_aFieldNameMap[aDomain] = nNewWidget;
760 0 : m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
761 0 : if( nLastTokenIndex > 0 )
762 : {
763 : // this field is not a root field and
764 : // needs to be inserted to its parent
765 0 : OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) );
766 0 : it = m_aFieldNameMap.find( aParentDomain );
767 : OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" );
768 0 : if( it != m_aFieldNameMap.end() )
769 : {
770 : OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" );
771 0 : if( it->second < sal_Int32(m_aWidgets.size()) )
772 : {
773 0 : PDFWidget& rParentField( m_aWidgets[it->second] );
774 0 : rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject );
775 0 : rParentField.m_aKidsIndex.push_back( nNewWidget );
776 0 : m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject;
777 : }
778 0 : }
779 : }
780 : }
781 0 : else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy )
782 : {
783 : // this is invalid, someone tries to have a terminal field as parent
784 : // example: a button with the name foo.bar exists and
785 : // another button is named foo.bar.no
786 : // workaround: put the second terminal field as much up in the hierarchy as
787 : // necessary to have a non-terminal field as parent (or none at all)
788 : // since it->second already is terminal, we just need to use its parent
789 0 : aDomain = OString();
790 0 : aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
791 0 : if( nLastTokenIndex > 0 )
792 : {
793 0 : aDomain = aFullName.copy( 0, nLastTokenIndex-1 );
794 0 : OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() );
795 0 : aBuf.append( aDomain );
796 0 : aBuf.append( '.' );
797 0 : aBuf.append( aPartialName );
798 0 : aFullName = aBuf.makeStringAndClear();
799 : }
800 : else
801 0 : aFullName = aPartialName;
802 : break;
803 : }
804 : }
805 : } while( nTokenIndex != -1 );
806 :
807 : // insert widget into its hierarchy field
808 0 : if( !aDomain.isEmpty() )
809 : {
810 0 : boost::unordered_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
811 0 : if( it != m_aFieldNameMap.end() )
812 : {
813 : OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" );
814 0 : if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) )
815 : {
816 0 : m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject;
817 0 : m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject);
818 0 : m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex );
819 : }
820 : }
821 : }
822 :
823 0 : if( aPartialName.isEmpty() )
824 : {
825 : // how funny, an empty field name
826 0 : if( i_rControl.getType() == PDFWriter::RadioButton )
827 : {
828 0 : aPartialName = "RadioGroup";
829 0 : aPartialName += OString::valueOf( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup );
830 : }
831 : else
832 0 : aPartialName = OString( "Widget" );
833 : }
834 :
835 0 : if( ! m_aContext.AllowDuplicateFieldNames )
836 : {
837 0 : boost::unordered_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aFullName );
838 :
839 0 : if( it != m_aFieldNameMap.end() ) // not unique
840 : {
841 0 : boost::unordered_map< OString, sal_Int32, OStringHash >::const_iterator check_it;
842 0 : OString aTry;
843 0 : sal_Int32 nTry = 2;
844 0 : do
845 : {
846 0 : OStringBuffer aUnique( aFullName.getLength() + 16 );
847 0 : aUnique.append( aFullName );
848 0 : aUnique.append( '_' );
849 0 : aUnique.append( nTry++ );
850 0 : aTry = aUnique.makeStringAndClear();
851 0 : check_it = m_aFieldNameMap.find( aTry );
852 0 : } while( check_it != m_aFieldNameMap.end() );
853 0 : aFullName = aTry;
854 0 : m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
855 0 : aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
856 : }
857 : else
858 0 : m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
859 : }
860 :
861 : // finally
862 0 : m_aWidgets[i_nWidgetIndex].m_aName = aPartialName;
863 0 : }
864 :
865 0 : static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor )
866 : {
867 0 : if( nValue < 0 )
868 : {
869 0 : rBuffer.append( '-' );
870 0 : nValue = -nValue;
871 : }
872 0 : sal_Int32 nFactor = 1, nDiv = nPrecision;
873 0 : while( nDiv-- )
874 0 : nFactor *= 10;
875 :
876 0 : sal_Int32 nInt = nValue / nFactor;
877 0 : rBuffer.append( nInt );
878 0 : if( nFactor > 1 )
879 : {
880 0 : sal_Int32 nDecimal = nValue % nFactor;
881 0 : if( nDecimal )
882 : {
883 0 : rBuffer.append( '.' );
884 : // omit trailing zeros
885 0 : while( (nDecimal % 10) == 0 )
886 0 : nDecimal /= 10;
887 0 : rBuffer.append( nDecimal );
888 : }
889 : }
890 0 : }
891 :
892 :
893 : // appends a double. PDF does not accept exponential format, only fixed point
894 0 : static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 )
895 : {
896 0 : bool bNeg = false;
897 0 : if( fValue < 0.0 )
898 : {
899 0 : bNeg = true;
900 0 : fValue=-fValue;
901 : }
902 :
903 0 : sal_Int64 nInt = (sal_Int64)fValue;
904 0 : fValue -= (double)nInt;
905 : // optimizing hardware may lead to a value of 1.0 after the subtraction
906 0 : if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision )
907 : {
908 0 : nInt++;
909 0 : fValue = 0.0;
910 : }
911 0 : sal_Int64 nFrac = 0;
912 0 : if( fValue )
913 : {
914 0 : fValue *= pow( 10.0, (double)nPrecision );
915 0 : nFrac = (sal_Int64)fValue;
916 : }
917 0 : if( bNeg && ( nInt || nFrac ) )
918 0 : rBuffer.append( '-' );
919 0 : rBuffer.append( nInt );
920 0 : if( nFrac )
921 : {
922 : int i;
923 0 : rBuffer.append( '.' );
924 0 : sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5);
925 0 : for ( i = 0; ( i < nPrecision ) && nFrac; i++ )
926 : {
927 0 : sal_Int64 nNumb = nFrac / nBound;
928 0 : nFrac -= nNumb * nBound;
929 0 : rBuffer.append( nNumb );
930 0 : nBound /= 10;
931 : }
932 : }
933 0 : }
934 :
935 :
936 0 : static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey = false )
937 : {
938 :
939 0 : if( rColor != Color( COL_TRANSPARENT ) )
940 : {
941 0 : if( bConvertToGrey )
942 : {
943 0 : sal_uInt8 cByte = rColor.GetLuminance();
944 0 : appendDouble( (double)cByte / 255.0, rBuffer );
945 : }
946 : else
947 : {
948 0 : appendDouble( (double)rColor.GetRed() / 255.0, rBuffer );
949 0 : rBuffer.append( ' ' );
950 0 : appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer );
951 0 : rBuffer.append( ' ' );
952 0 : appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer );
953 : }
954 : }
955 0 : }
956 :
957 0 : void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
958 : {
959 0 : if( rColor != Color( COL_TRANSPARENT ) )
960 : {
961 0 : bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
962 0 : appendColor( rColor, rBuffer, bGrey );
963 0 : rBuffer.append( bGrey ? " G" : " RG" );
964 : }
965 0 : }
966 :
967 0 : void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
968 : {
969 0 : if( rColor != Color( COL_TRANSPARENT ) )
970 : {
971 0 : bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
972 0 : appendColor( rColor, rBuffer, bGrey );
973 0 : rBuffer.append( bGrey ? " g" : " rg" );
974 : }
975 0 : }
976 :
977 : // matrix helper class
978 : // TODO: use basegfx matrix class instead or derive from it
979 : namespace vcl // TODO: use anonymous namespace to keep this class local
980 : {
981 : /* for sparse matrices of the form (2D linear transformations)
982 : * f[0] f[1] 0
983 : * f[2] f[3] 0
984 : * f[4] f[5] 1
985 : */
986 : class Matrix3
987 : {
988 : double f[6];
989 :
990 0 : void set( double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; }
991 : public:
992 : Matrix3();
993 0 : ~Matrix3() {}
994 :
995 : void skew( double alpha, double beta );
996 : void scale( double sx, double sy );
997 : void rotate( double angle );
998 : void translate( double tx, double ty );
999 : bool invert();
1000 :
1001 : void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL );
1002 :
1003 : Point transform( const Point& rPoint ) const;
1004 : };
1005 : }
1006 :
1007 0 : Matrix3::Matrix3()
1008 : {
1009 : // initialize to unity
1010 0 : f[0] = 1.0;
1011 0 : f[1] = 0.0;
1012 0 : f[2] = 0.0;
1013 0 : f[3] = 1.0;
1014 0 : f[4] = 0.0;
1015 0 : f[5] = 0.0;
1016 0 : }
1017 :
1018 0 : Point Matrix3::transform( const Point& rOrig ) const
1019 : {
1020 0 : double x = (double)rOrig.X(), y = (double)rOrig.Y();
1021 0 : return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) );
1022 : }
1023 :
1024 0 : void Matrix3::skew( double alpha, double beta )
1025 : {
1026 : double fn[6];
1027 0 : double tb = tan( beta );
1028 0 : fn[0] = f[0] + f[2]*tb;
1029 0 : fn[1] = f[1];
1030 0 : fn[2] = f[2] + f[3]*tb;
1031 0 : fn[3] = f[3];
1032 0 : fn[4] = f[4] + f[5]*tb;
1033 0 : fn[5] = f[5];
1034 0 : if( alpha != 0.0 )
1035 : {
1036 0 : double ta = tan( alpha );
1037 0 : fn[1] += f[0]*ta;
1038 0 : fn[3] += f[2]*ta;
1039 0 : fn[5] += f[4]*ta;
1040 : }
1041 0 : set( fn );
1042 0 : }
1043 :
1044 0 : void Matrix3::scale( double sx, double sy )
1045 : {
1046 : double fn[6];
1047 0 : fn[0] = sx*f[0];
1048 0 : fn[1] = sy*f[1];
1049 0 : fn[2] = sx*f[2];
1050 0 : fn[3] = sy*f[3];
1051 0 : fn[4] = sx*f[4];
1052 0 : fn[5] = sy*f[5];
1053 0 : set( fn );
1054 0 : }
1055 :
1056 0 : void Matrix3::rotate( double angle )
1057 : {
1058 : double fn[6];
1059 0 : double fSin = sin(angle);
1060 0 : double fCos = cos(angle);
1061 0 : fn[0] = f[0]*fCos - f[1]*fSin;
1062 0 : fn[1] = f[0]*fSin + f[1]*fCos;
1063 0 : fn[2] = f[2]*fCos - f[3]*fSin;
1064 0 : fn[3] = f[2]*fSin + f[3]*fCos;
1065 0 : fn[4] = f[4]*fCos - f[5]*fSin;
1066 0 : fn[5] = f[4]*fSin + f[5]*fCos;
1067 0 : set( fn );
1068 0 : }
1069 :
1070 0 : void Matrix3::translate( double tx, double ty )
1071 : {
1072 0 : f[4] += tx;
1073 0 : f[5] += ty;
1074 0 : }
1075 :
1076 0 : bool Matrix3::invert()
1077 : {
1078 : // short circuit trivial cases
1079 0 : if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 )
1080 : {
1081 0 : f[4] = -f[4];
1082 0 : f[5] = -f[5];
1083 0 : return true;
1084 : }
1085 :
1086 : // check determinant
1087 0 : const double fDet = f[0]*f[3]-f[1]*f[2];
1088 0 : if( fDet == 0.0 )
1089 0 : return false;
1090 :
1091 : // invert the matrix
1092 : double fn[6];
1093 0 : fn[0] = +f[3] / fDet;
1094 0 : fn[1] = -f[1] / fDet;
1095 0 : fn[2] = -f[2] / fDet;
1096 0 : fn[3] = +f[0] / fDet;
1097 :
1098 : // apply inversion to translation
1099 0 : fn[4] = -(f[4]*fn[0] + f[5]*fn[2]);
1100 0 : fn[5] = -(f[4]*fn[1] + f[5]*fn[3]);
1101 :
1102 0 : set( fn );
1103 0 : return true;
1104 : }
1105 :
1106 0 : void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack )
1107 : {
1108 0 : appendDouble( f[0], rBuffer );
1109 0 : rBuffer.append( ' ' );
1110 0 : appendDouble( f[1], rBuffer );
1111 0 : rBuffer.append( ' ' );
1112 0 : appendDouble( f[2], rBuffer );
1113 0 : rBuffer.append( ' ' );
1114 0 : appendDouble( f[3], rBuffer );
1115 0 : rBuffer.append( ' ' );
1116 0 : rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack );
1117 0 : }
1118 :
1119 0 : static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList )
1120 : {
1121 0 : if( rList.empty() )
1122 0 : return;
1123 0 : rBuf.append( '/' );
1124 0 : rBuf.append( pPrefix );
1125 0 : rBuf.append( "<<" );
1126 0 : int ni = 0;
1127 0 : for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it )
1128 : {
1129 0 : if( !it->first.isEmpty() && it->second > 0 )
1130 : {
1131 0 : rBuf.append( '/' );
1132 0 : rBuf.append( it->first );
1133 0 : rBuf.append( ' ' );
1134 0 : rBuf.append( it->second );
1135 0 : rBuf.append( " 0 R" );
1136 0 : if( ((++ni) & 7) == 0 )
1137 0 : rBuf.append( '\n' );
1138 : }
1139 : }
1140 0 : rBuf.append( ">>\n" );
1141 : }
1142 :
1143 0 : void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject )
1144 : {
1145 0 : rBuf.append( "<</Font " );
1146 0 : rBuf.append( nFontDictObject );
1147 0 : rBuf.append( " 0 R\n" );
1148 0 : appendResourceMap( rBuf, "XObject", m_aXObjects );
1149 0 : appendResourceMap( rBuf, "ExtGState", m_aExtGStates );
1150 0 : appendResourceMap( rBuf, "Shading", m_aShadings );
1151 0 : appendResourceMap( rBuf, "Pattern", m_aPatterns );
1152 0 : rBuf.append( "/ProcSet[/PDF/Text" );
1153 0 : if( !m_aXObjects.empty() )
1154 0 : rBuf.append( "/ImageC/ImageI/ImageB" );
1155 0 : rBuf.append( "]\n>>\n" );
1156 0 : };
1157 :
1158 0 : PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
1159 : :
1160 : m_pWriter( pWriter ),
1161 : m_nPageWidth( nPageWidth ),
1162 : m_nPageHeight( nPageHeight ),
1163 : m_eOrientation( eOrientation ),
1164 : m_nPageObject( 0 ), // invalid object number
1165 : m_nPageIndex( -1 ), // invalid index
1166 : m_nStreamLengthObject( 0 ),
1167 : m_nBeginStreamPos( 0 ),
1168 : m_eTransition( PDFWriter::Regular ),
1169 : m_nTransTime( 0 ),
1170 : m_nDuration( 0 ),
1171 0 : m_bHasWidgets( false )
1172 : {
1173 : // object ref must be only ever updated in emit()
1174 0 : m_nPageObject = m_pWriter->createObject();
1175 0 : }
1176 :
1177 0 : PDFWriterImpl::PDFPage::~PDFPage()
1178 : {
1179 0 : }
1180 :
1181 0 : void PDFWriterImpl::PDFPage::beginStream()
1182 : {
1183 : #if OSL_DEBUG_LEVEL > 1
1184 : {
1185 : OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" );
1186 : m_pWriter->emitComment( aLine.getStr() );
1187 : }
1188 : #endif
1189 0 : m_aStreamObjects.push_back(m_pWriter->createObject());
1190 0 : if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) )
1191 : return;
1192 :
1193 0 : m_nStreamLengthObject = m_pWriter->createObject();
1194 : // write content stream header
1195 0 : OStringBuffer aLine;
1196 0 : aLine.append( m_aStreamObjects.back() );
1197 0 : aLine.append( " 0 obj\n<</Length " );
1198 0 : aLine.append( m_nStreamLengthObject );
1199 0 : aLine.append( " 0 R" );
1200 : #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1201 0 : aLine.append( "/Filter/FlateDecode" );
1202 : #endif
1203 0 : aLine.append( ">>\nstream\n" );
1204 0 : if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) )
1205 : return;
1206 0 : if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &m_nBeginStreamPos ) )
1207 : {
1208 0 : osl_closeFile( m_pWriter->m_aFile );
1209 0 : m_pWriter->m_bOpen = false;
1210 : }
1211 : #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1212 0 : m_pWriter->beginCompression();
1213 : #endif
1214 0 : m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() );
1215 : }
1216 :
1217 0 : void PDFWriterImpl::PDFPage::endStream()
1218 : {
1219 : #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1220 0 : m_pWriter->endCompression();
1221 : #endif
1222 : sal_uInt64 nEndStreamPos;
1223 0 : if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &nEndStreamPos ) )
1224 : {
1225 0 : osl_closeFile( m_pWriter->m_aFile );
1226 0 : m_pWriter->m_bOpen = false;
1227 : return;
1228 : }
1229 0 : m_pWriter->disableStreamEncryption();
1230 0 : if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1231 : return;
1232 : // emit stream length object
1233 0 : if( ! m_pWriter->updateObject( m_nStreamLengthObject ) )
1234 : return;
1235 0 : OStringBuffer aLine;
1236 0 : aLine.append( m_nStreamLengthObject );
1237 0 : aLine.append( " 0 obj\n" );
1238 0 : aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) );
1239 0 : aLine.append( "\nendobj\n\n" );
1240 0 : m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1241 : }
1242 :
1243 0 : bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject )
1244 : {
1245 : // emit page object
1246 0 : if( ! m_pWriter->updateObject( m_nPageObject ) )
1247 0 : return false;
1248 0 : OStringBuffer aLine;
1249 :
1250 0 : aLine.append( m_nPageObject );
1251 : aLine.append( " 0 obj\n"
1252 0 : "<</Type/Page/Parent " );
1253 0 : aLine.append( nParentObject );
1254 0 : aLine.append( " 0 R" );
1255 0 : aLine.append( "/Resources " );
1256 0 : aLine.append( m_pWriter->getResourceDictObj() );
1257 0 : aLine.append( " 0 R" );
1258 0 : if( m_nPageWidth && m_nPageHeight )
1259 : {
1260 0 : aLine.append( "/MediaBox[0 0 " );
1261 0 : aLine.append( m_nPageWidth );
1262 0 : aLine.append( ' ' );
1263 0 : aLine.append( m_nPageHeight );
1264 0 : aLine.append( "]" );
1265 : }
1266 0 : switch( m_eOrientation )
1267 : {
1268 0 : case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
1269 0 : case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
1270 0 : case PDFWriter::Portrait: aLine.append( "/Rotate 0\n" );break;
1271 :
1272 : case PDFWriter::Inherit:
1273 : default:
1274 0 : break;
1275 : }
1276 0 : int nAnnots = m_aAnnotations.size();
1277 0 : if( nAnnots > 0 )
1278 : {
1279 0 : aLine.append( "/Annots[\n" );
1280 0 : for( int i = 0; i < nAnnots; i++ )
1281 : {
1282 0 : aLine.append( m_aAnnotations[i] );
1283 0 : aLine.append( " 0 R" );
1284 0 : aLine.append( ((i+1)%15) ? " " : "\n" );
1285 : }
1286 0 : aLine.append( "]\n" );
1287 : }
1288 0 : if( m_aMCIDParents.size() > 0 )
1289 : {
1290 0 : OStringBuffer aStructParents( 1024 );
1291 0 : aStructParents.append( "[ " );
1292 0 : int nParents = m_aMCIDParents.size();
1293 0 : for( int i = 0; i < nParents; i++ )
1294 : {
1295 0 : aStructParents.append( m_aMCIDParents[i] );
1296 0 : aStructParents.append( " 0 R" );
1297 0 : aStructParents.append( ((i%10) == 9) ? "\n" : " " );
1298 : }
1299 0 : aStructParents.append( "]" );
1300 0 : m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() );
1301 :
1302 0 : aLine.append( "/StructParents " );
1303 0 : aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) );
1304 0 : aLine.append( "\n" );
1305 : }
1306 0 : if( m_nDuration > 0 )
1307 : {
1308 0 : aLine.append( "/Dur " );
1309 0 : aLine.append( (sal_Int32)m_nDuration );
1310 0 : aLine.append( "\n" );
1311 : }
1312 0 : if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 )
1313 : {
1314 : // transition duration
1315 0 : aLine.append( "/Trans<</D " );
1316 0 : appendDouble( (double)m_nTransTime/1000.0, aLine, 3 );
1317 0 : aLine.append( "\n" );
1318 0 : const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL;
1319 0 : switch( m_eTransition )
1320 : {
1321 : case PDFWriter::SplitHorizontalInward:
1322 0 : pStyle = "Split"; pDm = "H"; pM = "I"; break;
1323 : case PDFWriter::SplitHorizontalOutward:
1324 0 : pStyle = "Split"; pDm = "H"; pM = "O"; break;
1325 : case PDFWriter::SplitVerticalInward:
1326 0 : pStyle = "Split"; pDm = "V"; pM = "I"; break;
1327 : case PDFWriter::SplitVerticalOutward:
1328 0 : pStyle = "Split"; pDm = "V"; pM = "O"; break;
1329 : case PDFWriter::BlindsHorizontal:
1330 0 : pStyle = "Blinds"; pDm = "H"; break;
1331 : case PDFWriter::BlindsVertical:
1332 0 : pStyle = "Blinds"; pDm = "V"; break;
1333 : case PDFWriter::BoxInward:
1334 0 : pStyle = "Box"; pM = "I"; break;
1335 : case PDFWriter::BoxOutward:
1336 0 : pStyle = "Box"; pM = "O"; break;
1337 : case PDFWriter::WipeLeftToRight:
1338 0 : pStyle = "Wipe"; pDi = "0"; break;
1339 : case PDFWriter::WipeBottomToTop:
1340 0 : pStyle = "Wipe"; pDi = "90"; break;
1341 : case PDFWriter::WipeRightToLeft:
1342 0 : pStyle = "Wipe"; pDi = "180"; break;
1343 : case PDFWriter::WipeTopToBottom:
1344 0 : pStyle = "Wipe"; pDi = "270"; break;
1345 : case PDFWriter::Dissolve:
1346 0 : pStyle = "Dissolve"; break;
1347 : case PDFWriter::GlitterLeftToRight:
1348 0 : pStyle = "Glitter"; pDi = "0"; break;
1349 : case PDFWriter::GlitterTopToBottom:
1350 0 : pStyle = "Glitter"; pDi = "270"; break;
1351 : case PDFWriter::GlitterTopLeftToBottomRight:
1352 0 : pStyle = "Glitter"; pDi = "315"; break;
1353 : case PDFWriter::Regular:
1354 0 : break;
1355 : }
1356 : // transition style
1357 0 : if( pStyle )
1358 : {
1359 0 : aLine.append( "/S/" );
1360 0 : aLine.append( pStyle );
1361 0 : aLine.append( "\n" );
1362 : }
1363 0 : if( pDm )
1364 : {
1365 0 : aLine.append( "/Dm/" );
1366 0 : aLine.append( pDm );
1367 0 : aLine.append( "\n" );
1368 : }
1369 0 : if( pM )
1370 : {
1371 0 : aLine.append( "/M/" );
1372 0 : aLine.append( pM );
1373 0 : aLine.append( "\n" );
1374 : }
1375 0 : if( pDi )
1376 : {
1377 0 : aLine.append( "/Di " );
1378 0 : aLine.append( pDi );
1379 0 : aLine.append( "\n" );
1380 : }
1381 0 : aLine.append( ">>\n" );
1382 : }
1383 0 : if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 )
1384 : {
1385 0 : aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1386 : }
1387 0 : aLine.append( "/Contents" );
1388 0 : unsigned int nStreamObjects = m_aStreamObjects.size();
1389 0 : if( nStreamObjects > 1 )
1390 0 : aLine.append( '[' );
1391 0 : for( unsigned int i = 0; i < m_aStreamObjects.size(); i++ )
1392 : {
1393 0 : aLine.append( ' ' );
1394 0 : aLine.append( m_aStreamObjects[i] );
1395 0 : aLine.append( " 0 R" );
1396 : }
1397 0 : if( nStreamObjects > 1 )
1398 0 : aLine.append( ']' );
1399 0 : aLine.append( ">>\nendobj\n\n" );
1400 0 : return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1401 : }
1402 :
1403 : namespace vcl
1404 : {
1405 : template < class GEOMETRY >
1406 0 : GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
1407 : {
1408 0 : GEOMETRY aPoint;
1409 0 : if ( MAP_PIXEL == _rSource.GetMapUnit() )
1410 : {
1411 0 : aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
1412 : }
1413 : else
1414 : {
1415 0 : aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
1416 : }
1417 0 : return aPoint;
1418 : }
1419 : }
1420 :
1421 0 : void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const
1422 : {
1423 0 : if( pOutPoint )
1424 : {
1425 0 : Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1426 : m_pWriter->m_aMapMode,
1427 : m_pWriter->getReferenceDevice(),
1428 0 : rPoint ) );
1429 0 : *pOutPoint = aPoint;
1430 : }
1431 :
1432 0 : Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1433 : m_pWriter->m_aMapMode,
1434 : m_pWriter->getReferenceDevice(),
1435 0 : rPoint ) );
1436 :
1437 0 : sal_Int32 nValue = aPoint.X();
1438 0 : if( bNeg )
1439 0 : nValue = -nValue;
1440 :
1441 0 : appendFixedInt( nValue, rBuffer );
1442 :
1443 0 : rBuffer.append( ' ' );
1444 :
1445 0 : nValue = pointToPixel(getHeight()) - aPoint.Y();
1446 0 : if( bNeg )
1447 0 : nValue = -nValue;
1448 :
1449 0 : appendFixedInt( nValue, rBuffer );
1450 0 : }
1451 :
1452 0 : void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const
1453 : {
1454 0 : double fValue = pixelToPoint(rPoint.getX());
1455 :
1456 0 : appendDouble( fValue, rBuffer, nLog10Divisor );
1457 :
1458 0 : rBuffer.append( ' ' );
1459 :
1460 0 : fValue = double(getHeight()) - pixelToPoint(rPoint.getY());
1461 :
1462 0 : appendDouble( fValue, rBuffer, nLog10Divisor );
1463 0 : }
1464 :
1465 0 : void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const
1466 : {
1467 0 : appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer );
1468 0 : rBuffer.append( ' ' );
1469 0 : appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false );
1470 0 : rBuffer.append( ' ' );
1471 0 : appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true );
1472 0 : rBuffer.append( " re" );
1473 0 : }
1474 :
1475 0 : void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const
1476 : {
1477 0 : Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1478 : m_pWriter->m_aMapMode,
1479 : m_pWriter->getReferenceDevice(),
1480 0 : rRect.BottomLeft() + Point( 0, 1 )
1481 0 : );
1482 0 : Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1483 : m_pWriter->m_aMapMode,
1484 : m_pWriter->getReferenceDevice(),
1485 0 : rRect.GetSize() );
1486 0 : rRect.Left() = aLL.X();
1487 0 : rRect.Right() = aLL.X() + aSize.Width();
1488 0 : rRect.Top() = pointToPixel(getHeight()) - aLL.Y();
1489 0 : rRect.Bottom() = rRect.Top() + aSize.Height();
1490 0 : }
1491 :
1492 0 : void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1493 : {
1494 0 : sal_uInt16 nPoints = rPoly.GetSize();
1495 : /*
1496 : * #108582# applications do weird things
1497 : */
1498 0 : sal_uInt32 nBufLen = rBuffer.getLength();
1499 0 : if( nPoints > 0 )
1500 : {
1501 0 : const sal_uInt8* pFlagArray = rPoly.GetConstFlagAry();
1502 0 : appendPoint( rPoly[0], rBuffer );
1503 0 : rBuffer.append( " m\n" );
1504 0 : for( sal_uInt16 i = 1; i < nPoints; i++ )
1505 : {
1506 0 : if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 )
1507 : {
1508 : // bezier
1509 : DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" );
1510 0 : appendPoint( rPoly[i], rBuffer );
1511 0 : rBuffer.append( " " );
1512 0 : appendPoint( rPoly[i+1], rBuffer );
1513 0 : rBuffer.append( " " );
1514 0 : appendPoint( rPoly[i+2], rBuffer );
1515 0 : rBuffer.append( " c" );
1516 0 : i += 2; // add additionally consumed points
1517 : }
1518 : else
1519 : {
1520 : // line
1521 0 : appendPoint( rPoly[i], rBuffer );
1522 0 : rBuffer.append( " l" );
1523 : }
1524 0 : if( (rBuffer.getLength() - nBufLen) > 65 )
1525 : {
1526 0 : rBuffer.append( "\n" );
1527 0 : nBufLen = rBuffer.getLength();
1528 : }
1529 : else
1530 0 : rBuffer.append( " " );
1531 : }
1532 0 : if( bClose )
1533 0 : rBuffer.append( "h\n" );
1534 : }
1535 0 : }
1536 :
1537 0 : void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1538 : {
1539 0 : basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1540 : m_pWriter->m_aMapMode,
1541 : m_pWriter->getReferenceDevice(),
1542 0 : rPoly ) );
1543 :
1544 0 : if( basegfx::tools::isRectangle( aPoly ) )
1545 : {
1546 0 : basegfx::B2DRange aRange( aPoly.getB2DRange() );
1547 0 : basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() );
1548 0 : appendPixelPoint( aBL, rBuffer );
1549 0 : rBuffer.append( ' ' );
1550 0 : appendMappedLength( aRange.getWidth(), rBuffer, false, NULL, nLog10Divisor );
1551 0 : rBuffer.append( ' ' );
1552 0 : appendMappedLength( aRange.getHeight(), rBuffer, true, NULL, nLog10Divisor );
1553 0 : rBuffer.append( " re\n" );
1554 0 : return;
1555 : }
1556 0 : sal_uInt32 nPoints = aPoly.count();
1557 0 : if( nPoints > 0 )
1558 : {
1559 0 : sal_uInt32 nBufLen = rBuffer.getLength();
1560 0 : basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) );
1561 0 : appendPixelPoint( aLastPoint, rBuffer );
1562 0 : rBuffer.append( " m\n" );
1563 0 : for( sal_uInt32 i = 1; i <= nPoints; i++ )
1564 : {
1565 0 : if( i != nPoints || aPoly.isClosed() )
1566 : {
1567 0 : sal_uInt32 nCurPoint = i % nPoints;
1568 0 : sal_uInt32 nLastPoint = i-1;
1569 0 : basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) );
1570 0 : if( aPoly.isNextControlPointUsed( nLastPoint ) &&
1571 0 : aPoly.isPrevControlPointUsed( nCurPoint ) )
1572 : {
1573 0 : appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1574 0 : rBuffer.append( ' ' );
1575 0 : appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1576 0 : rBuffer.append( ' ' );
1577 0 : appendPixelPoint( aPoint, rBuffer );
1578 0 : rBuffer.append( " c" );
1579 : }
1580 0 : else if( aPoly.isNextControlPointUsed( nLastPoint ) )
1581 : {
1582 0 : appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1583 0 : rBuffer.append( ' ' );
1584 0 : appendPixelPoint( aPoint, rBuffer );
1585 0 : rBuffer.append( " y" );
1586 : }
1587 0 : else if( aPoly.isPrevControlPointUsed( nCurPoint ) )
1588 : {
1589 0 : appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1590 0 : rBuffer.append( ' ' );
1591 0 : appendPixelPoint( aPoint, rBuffer );
1592 0 : rBuffer.append( " v" );
1593 : }
1594 : else
1595 : {
1596 0 : appendPixelPoint( aPoint, rBuffer );
1597 0 : rBuffer.append( " l" );
1598 : }
1599 0 : if( (rBuffer.getLength() - nBufLen) > 65 )
1600 : {
1601 0 : rBuffer.append( "\n" );
1602 0 : nBufLen = rBuffer.getLength();
1603 : }
1604 : else
1605 0 : rBuffer.append( " " );
1606 : }
1607 : }
1608 0 : if( bClose )
1609 0 : rBuffer.append( "h\n" );
1610 0 : }
1611 : }
1612 :
1613 0 : void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1614 : {
1615 0 : sal_uInt16 nPolygons = rPolyPoly.Count();
1616 0 : for( sal_uInt16 n = 0; n < nPolygons; n++ )
1617 0 : appendPolygon( rPolyPoly[n], rBuffer, bClose );
1618 0 : }
1619 :
1620 0 : void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1621 : {
1622 0 : sal_uInt32 nPolygons = rPolyPoly.count();
1623 0 : for( sal_uInt32 n = 0; n < nPolygons; n++ )
1624 0 : appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer, bClose );
1625 0 : }
1626 :
1627 0 : void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1628 : {
1629 0 : sal_Int32 nValue = nLength;
1630 0 : if ( nLength < 0 )
1631 : {
1632 0 : rBuffer.append( '-' );
1633 0 : nValue = -nLength;
1634 : }
1635 0 : Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1636 : m_pWriter->m_aMapMode,
1637 : m_pWriter->getReferenceDevice(),
1638 0 : Size( nValue, nValue ) ) );
1639 0 : nValue = bVertical ? aSize.Height() : aSize.Width();
1640 0 : if( pOutLength )
1641 0 : *pOutLength = ((nLength < 0 ) ? -nValue : nValue);
1642 :
1643 0 : appendFixedInt( nValue, rBuffer, 1 );
1644 0 : }
1645 :
1646 0 : void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength, sal_Int32 nPrecision ) const
1647 : {
1648 0 : Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1649 : m_pWriter->m_aMapMode,
1650 : m_pWriter->getReferenceDevice(),
1651 0 : Size( 1000, 1000 ) ) );
1652 0 : if( pOutLength )
1653 0 : *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0);
1654 0 : fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0);
1655 0 : appendDouble( fLength, rBuffer, nPrecision );
1656 0 : }
1657 :
1658 0 : bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const
1659 : {
1660 0 : if(LINE_DASH == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen())
1661 : {
1662 : // dashed and non-degraded case, check for implementation limits of dash array
1663 : // in PDF reader apps (e.g. acroread)
1664 0 : if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10)
1665 : {
1666 0 : return false;
1667 : }
1668 : }
1669 :
1670 0 : if(basegfx::B2DLINEJOIN_NONE != rInfo.GetLineJoin())
1671 : {
1672 : // LineJoin used, ExtLineInfo required
1673 0 : return false;
1674 : }
1675 :
1676 0 : if(com::sun::star::drawing::LineCap_BUTT != rInfo.GetLineCap())
1677 : {
1678 : // LineCap used, ExtLineInfo required
1679 0 : return false;
1680 : }
1681 :
1682 0 : if( rInfo.GetStyle() == LINE_DASH )
1683 : {
1684 0 : rBuffer.append( "[ " );
1685 0 : if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case
1686 : {
1687 0 : appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1688 0 : rBuffer.append( ' ' );
1689 0 : appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1690 0 : rBuffer.append( ' ' );
1691 : }
1692 : else
1693 : {
1694 0 : for( int n = 0; n < rInfo.GetDashCount(); n++ )
1695 : {
1696 0 : appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1697 0 : rBuffer.append( ' ' );
1698 0 : appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1699 0 : rBuffer.append( ' ' );
1700 : }
1701 0 : for( int m = 0; m < rInfo.GetDotCount(); m++ )
1702 : {
1703 0 : appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer );
1704 0 : rBuffer.append( ' ' );
1705 0 : appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1706 0 : rBuffer.append( ' ' );
1707 : }
1708 : }
1709 0 : rBuffer.append( "] 0 d\n" );
1710 : }
1711 :
1712 0 : if( rInfo.GetWidth() > 1 )
1713 : {
1714 0 : appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer );
1715 0 : rBuffer.append( " w\n" );
1716 : }
1717 0 : else if( rInfo.GetWidth() == 0 )
1718 : {
1719 : // "pixel" line
1720 0 : appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->ImplGetDPIX()), rBuffer );
1721 0 : rBuffer.append( " w\n" );
1722 : }
1723 :
1724 0 : return true;
1725 : }
1726 :
1727 0 : void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const
1728 : {
1729 0 : if( nWidth <= 0 )
1730 0 : return;
1731 0 : if( nDelta < 1 )
1732 0 : nDelta = 1;
1733 :
1734 0 : rBuffer.append( "0 " );
1735 0 : appendMappedLength( nY, rBuffer, true );
1736 0 : rBuffer.append( " m\n" );
1737 0 : for( sal_Int32 n = 0; n < nWidth; )
1738 : {
1739 0 : n += nDelta;
1740 0 : appendMappedLength( n, rBuffer, false );
1741 0 : rBuffer.append( ' ' );
1742 0 : appendMappedLength( nDelta+nY, rBuffer, true );
1743 0 : rBuffer.append( ' ' );
1744 0 : n += nDelta;
1745 0 : appendMappedLength( n, rBuffer, false );
1746 0 : rBuffer.append( ' ' );
1747 0 : appendMappedLength( nY, rBuffer, true );
1748 0 : rBuffer.append( " v " );
1749 0 : if( n < nWidth )
1750 : {
1751 0 : n += nDelta;
1752 0 : appendMappedLength( n, rBuffer, false );
1753 0 : rBuffer.append( ' ' );
1754 0 : appendMappedLength( nY-nDelta, rBuffer, true );
1755 0 : rBuffer.append( ' ' );
1756 0 : n += nDelta;
1757 0 : appendMappedLength( n, rBuffer, false );
1758 0 : rBuffer.append( ' ' );
1759 0 : appendMappedLength( nY, rBuffer, true );
1760 0 : rBuffer.append( " v\n" );
1761 : }
1762 : }
1763 0 : rBuffer.append( "S\n" );
1764 : }
1765 :
1766 : /*
1767 : * class PDFWriterImpl
1768 : */
1769 :
1770 0 : PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext,
1771 : const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc,
1772 : PDFWriter& i_rOuterFace)
1773 : :
1774 : m_pReferenceDevice( NULL ),
1775 : m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ),
1776 : m_nCurrentStructElement( 0 ),
1777 : m_bEmitStructure( true ),
1778 : m_bNewMCID( false ),
1779 : m_bEmbedStandardFonts( false ),
1780 : m_nNextFID( 1 ),
1781 : m_nInheritedPageWidth( 595 ), // default A4
1782 : m_nInheritedPageHeight( 842 ), // default A4
1783 : m_eInheritedOrientation( PDFWriter::Portrait ),
1784 : m_nCurrentPage( -1 ),
1785 : m_nSignatureObject( -1 ),
1786 : m_nSignatureContentOffset( 0 ),
1787 : m_nSignatureLastByteRangeNoOffset( 0 ),
1788 : m_nResourceDict( -1 ),
1789 : m_nFontDictObject( -1 ),
1790 : m_pCodec( NULL ),
1791 0 : m_aDocDigest( rtl_digest_createMD5() ),
1792 : m_aCipher( (rtlCipher)NULL ),
1793 : m_aDigest( NULL ),
1794 : m_bEncryptThisStream( false ),
1795 : m_pEncryptionBuffer( NULL ),
1796 : m_nEncryptionBufferSize( 0 ),
1797 : m_bIsPDF_A1( false ),
1798 0 : m_rOuterFace( i_rOuterFace )
1799 : {
1800 : #ifdef DO_TEST_PDF
1801 : static bool bOnce = true;
1802 : if( bOnce )
1803 : {
1804 : bOnce = false;
1805 : doTestCode();
1806 : }
1807 : #endif
1808 0 : m_aContext = rContext;
1809 0 : m_aStructure.push_back( PDFStructureElement() );
1810 0 : m_aStructure[0].m_nOwnElement = 0;
1811 0 : m_aStructure[0].m_nParentElement = 0;
1812 :
1813 0 : Font aFont;
1814 0 : aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
1815 0 : aFont.SetSize( Size( 0, 12 ) );
1816 :
1817 0 : GraphicsState aState;
1818 0 : aState.m_aMapMode = m_aMapMode;
1819 0 : aState.m_aFont = aFont;
1820 0 : m_aGraphicsStack.push_front( aState );
1821 :
1822 0 : oslFileError aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
1823 0 : if( aError != osl_File_E_None )
1824 : {
1825 0 : if( aError == osl_File_E_EXIST )
1826 : {
1827 0 : aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write );
1828 0 : if( aError == osl_File_E_None )
1829 0 : aError = osl_setFileSize( m_aFile, 0 );
1830 : }
1831 : }
1832 0 : if( aError != osl_File_E_None )
1833 : return;
1834 :
1835 0 : m_bOpen = true;
1836 :
1837 : // setup DocInfo
1838 0 : setupDocInfo();
1839 :
1840 : /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1841 0 : m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1842 0 : m_aDigest = rtl_digest_createMD5();
1843 :
1844 : /* the size of the Codec default maximum */
1845 0 : checkEncryptionBufferSize( 0x4000 );
1846 :
1847 0 : if( xEnc.is() )
1848 0 : prepareEncryption( xEnc );
1849 :
1850 0 : if( m_aContext.Encryption.Encrypt() )
1851 : {
1852 : // sanity check
1853 0 : if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE ||
1854 0 : m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE ||
1855 0 : m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH
1856 : )
1857 : {
1858 : // the field lengths are invalid ? This was not setup by initEncryption.
1859 : // do not encrypt after all
1860 0 : m_aContext.Encryption.OValue.clear();
1861 0 : m_aContext.Encryption.UValue.clear();
1862 : OSL_ENSURE( 0, "encryption data failed sanity check, encryption disabled" );
1863 : }
1864 : else // setup key lengths
1865 0 : m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength );
1866 : }
1867 :
1868 : // write header
1869 0 : OStringBuffer aBuffer( 20 );
1870 0 : aBuffer.append( "%PDF-" );
1871 0 : switch( m_aContext.Version )
1872 : {
1873 0 : case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break;
1874 0 : case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break;
1875 : case PDFWriter::PDF_A_1:
1876 : default:
1877 0 : case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break;
1878 0 : case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break;
1879 : }
1880 : // append something binary as comment (suggested in PDF Reference)
1881 0 : aBuffer.append( "\n%äüöß\n" );
1882 0 : if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) )
1883 : {
1884 0 : osl_closeFile( m_aFile );
1885 0 : m_bOpen = false;
1886 : return;
1887 : }
1888 :
1889 : // insert outline root
1890 0 : m_aOutline.push_back( PDFOutlineEntry() );
1891 :
1892 0 : m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1);
1893 0 : if( m_bIsPDF_A1 )
1894 0 : m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour
1895 :
1896 0 : m_bEmbedStandardFonts = m_aContext.EmbedStandardFonts;
1897 : }
1898 :
1899 0 : PDFWriterImpl::~PDFWriterImpl()
1900 : {
1901 0 : if( m_aDocDigest )
1902 0 : rtl_digest_destroyMD5( m_aDocDigest );
1903 0 : delete static_cast<VirtualDevice*>(m_pReferenceDevice);
1904 :
1905 0 : if( m_aCipher )
1906 0 : rtl_cipher_destroyARCFOUR( m_aCipher );
1907 0 : if( m_aDigest )
1908 0 : rtl_digest_destroyMD5( m_aDigest );
1909 :
1910 0 : rtl_freeMemory( m_pEncryptionBuffer );
1911 0 : }
1912 :
1913 0 : void PDFWriterImpl::setupDocInfo()
1914 : {
1915 0 : std::vector< sal_uInt8 > aId;
1916 0 : computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString );
1917 0 : if( m_aContext.Encryption.DocumentIdentifier.empty() )
1918 0 : m_aContext.Encryption.DocumentIdentifier = aId;
1919 0 : }
1920 :
1921 0 : void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier,
1922 : const vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
1923 : rtl::OString& o_rCString1,
1924 : rtl::OString& o_rCString2
1925 : )
1926 : {
1927 0 : o_rIdentifier.clear();
1928 :
1929 : //build the document id
1930 0 : rtl::OString aInfoValuesOut;
1931 0 : OStringBuffer aID( 1024 );
1932 0 : if( i_rDocInfo.Title.Len() )
1933 0 : appendUnicodeTextString( i_rDocInfo.Title, aID );
1934 0 : if( i_rDocInfo.Author.Len() )
1935 0 : appendUnicodeTextString( i_rDocInfo.Author, aID );
1936 0 : if( i_rDocInfo.Subject.Len() )
1937 0 : appendUnicodeTextString( i_rDocInfo.Subject, aID );
1938 0 : if( i_rDocInfo.Keywords.Len() )
1939 0 : appendUnicodeTextString( i_rDocInfo.Keywords, aID );
1940 0 : if( i_rDocInfo.Creator.Len() )
1941 0 : appendUnicodeTextString( i_rDocInfo.Creator, aID );
1942 0 : if( i_rDocInfo.Producer.Len() )
1943 0 : appendUnicodeTextString( i_rDocInfo.Producer, aID );
1944 :
1945 : TimeValue aTVal, aGMT;
1946 : oslDateTime aDT;
1947 0 : osl_getSystemTime( &aGMT );
1948 0 : osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
1949 0 : osl_getDateTimeFromTimeValue( &aTVal, &aDT );
1950 0 : rtl::OStringBuffer aCreationDateString(64), aCreationMetaDateString(64);
1951 0 : aCreationDateString.append( "D:" );
1952 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1953 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1954 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1955 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1956 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1957 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1958 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1959 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1960 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1961 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1962 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1963 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1964 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1965 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1966 :
1967 : //--> i59651, we fill the Metadata date string as well, if PDF/A is requested
1968 : // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1969 : // local time zone offset UTC only, whereas Acrobat 8 seems
1970 : // to use the localtime notation only
1971 : // according to a raccomandation in XMP Specification (Jan 2004, page 75)
1972 : // the Acrobat way seems the right approach
1973 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1974 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1975 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1976 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1977 0 : aCreationMetaDateString.append( "-" );
1978 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1979 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1980 0 : aCreationMetaDateString.append( "-" );
1981 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1982 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1983 0 : aCreationMetaDateString.append( "T" );
1984 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1985 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1986 0 : aCreationMetaDateString.append( ":" );
1987 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1988 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1989 0 : aCreationMetaDateString.append( ":" );
1990 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1991 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1992 :
1993 0 : sal_uInt32 nDelta = 0;
1994 0 : if( aGMT.Seconds > aTVal.Seconds )
1995 : {
1996 0 : aCreationDateString.append( "-" );
1997 0 : nDelta = aGMT.Seconds-aTVal.Seconds;
1998 0 : aCreationMetaDateString.append( "-" );
1999 : }
2000 0 : else if( aGMT.Seconds < aTVal.Seconds )
2001 : {
2002 0 : aCreationDateString.append( "+" );
2003 0 : nDelta = aTVal.Seconds-aGMT.Seconds;
2004 0 : aCreationMetaDateString.append( "+" );
2005 : }
2006 : else
2007 : {
2008 0 : aCreationDateString.append( "Z" );
2009 0 : aCreationMetaDateString.append( "Z" );
2010 :
2011 : }
2012 0 : if( nDelta )
2013 : {
2014 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
2015 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
2016 0 : aCreationDateString.append( "'" );
2017 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
2018 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
2019 :
2020 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
2021 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
2022 0 : aCreationMetaDateString.append( ":" );
2023 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
2024 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
2025 : }
2026 0 : aCreationDateString.append( "'" );
2027 0 : aID.append( aCreationDateString.getStr(), aCreationDateString.getLength() );
2028 :
2029 0 : aInfoValuesOut = aID.makeStringAndClear();
2030 0 : o_rCString1 = aCreationDateString.makeStringAndClear();
2031 0 : o_rCString2 = aCreationMetaDateString.makeStringAndClear();
2032 :
2033 0 : rtlDigest aDigest = rtl_digest_createMD5();
2034 : OSL_ENSURE( aDigest != NULL, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" );
2035 0 : if( aDigest )
2036 : {
2037 0 : rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) );
2038 0 : if( nError == rtl_Digest_E_None )
2039 0 : nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() );
2040 0 : if( nError == rtl_Digest_E_None )
2041 : {
2042 0 : o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 );
2043 : //the binary form of the doc id is needed for encryption stuff
2044 0 : rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 );
2045 : }
2046 0 : }
2047 0 : }
2048 :
2049 : /* i12626 methods */
2050 : /*
2051 : check if the Unicode string must be encrypted or not, perform the requested task,
2052 : append the string as unicode hex, encrypted if needed
2053 : */
2054 0 : inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2055 : {
2056 0 : rOutBuffer.append( "<" );
2057 0 : if( m_aContext.Encryption.Encrypt() )
2058 : {
2059 0 : const sal_Unicode* pStr = rInString.getStr();
2060 0 : sal_Int32 nLen = rInString.getLength();
2061 : //prepare a unicode string, encrypt it
2062 0 : if( checkEncryptionBufferSize( nLen*2 ) )
2063 : {
2064 0 : enableStringEncryption( nInObjectNumber );
2065 0 : register sal_uInt8 *pCopy = m_pEncryptionBuffer;
2066 0 : sal_Int32 nChars = 2;
2067 0 : *pCopy++ = 0xFE;
2068 0 : *pCopy++ = 0xFF;
2069 : // we need to prepare a byte stream from the unicode string buffer
2070 0 : for( register int i = 0; i < nLen; i++ )
2071 : {
2072 0 : register sal_Unicode aUnChar = pStr[i];
2073 0 : *pCopy++ = (sal_uInt8)( aUnChar >> 8 );
2074 0 : *pCopy++ = (sal_uInt8)( aUnChar & 255 );
2075 0 : nChars += 2;
2076 : }
2077 : //encrypt in place
2078 0 : rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars );
2079 : //now append, hexadecimal (appendHex), the encrypted result
2080 0 : for(register int i = 0; i < nChars; i++)
2081 0 : appendHex( m_pEncryptionBuffer[i], rOutBuffer );
2082 : }
2083 : }
2084 : else
2085 0 : appendUnicodeTextString( rInString, rOutBuffer );
2086 0 : rOutBuffer.append( ">" );
2087 0 : }
2088 :
2089 0 : inline void PDFWriterImpl::appendLiteralStringEncrypt( rtl::OStringBuffer& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
2090 : {
2091 0 : rOutBuffer.append( "(" );
2092 0 : sal_Int32 nChars = rInString.getLength();
2093 : //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
2094 0 : if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) )
2095 : {
2096 : //encrypt the string in a buffer, then append it
2097 0 : enableStringEncryption( nInObjectNumber );
2098 0 : rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars );
2099 0 : appendLiteralString( (const sal_Char*)m_pEncryptionBuffer, nChars, rOutBuffer );
2100 : }
2101 : else
2102 0 : appendLiteralString( rInString.getStr(), nChars , rOutBuffer );
2103 0 : rOutBuffer.append( ")" );
2104 0 : }
2105 :
2106 0 : inline void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer )
2107 : {
2108 0 : rtl::OStringBuffer aBufferString( rInString );
2109 0 : appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2110 0 : }
2111 :
2112 0 : void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc )
2113 : {
2114 0 : rtl::OString aBufferString( rtl::OUStringToOString( rInString, nEnc ) );
2115 0 : sal_Int32 nLen = aBufferString.getLength();
2116 0 : rtl::OStringBuffer aBuf( nLen );
2117 0 : const sal_Char* pT = aBufferString.getStr();
2118 :
2119 0 : for( sal_Int32 i = 0; i < nLen; i++, pT++ )
2120 : {
2121 0 : if( (*pT & 0x80) == 0 )
2122 0 : aBuf.append( *pT );
2123 : else
2124 : {
2125 0 : aBuf.append( '<' );
2126 0 : appendHex( *pT, aBuf );
2127 0 : aBuf.append( '>' );
2128 : }
2129 : }
2130 0 : aBufferString = aBuf.makeStringAndClear();
2131 0 : appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2132 0 : }
2133 :
2134 : /* end i12626 methods */
2135 :
2136 0 : void PDFWriterImpl::emitComment( const char* pComment )
2137 : {
2138 0 : OStringBuffer aLine( 64 );
2139 0 : aLine.append( "% " );
2140 0 : aLine.append( (const sal_Char*)pComment );
2141 0 : aLine.append( "\n" );
2142 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
2143 0 : }
2144 :
2145 0 : bool PDFWriterImpl::compressStream( SvMemoryStream* pStream )
2146 : {
2147 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2148 0 : pStream->Seek( STREAM_SEEK_TO_END );
2149 0 : sal_uLong nEndPos = pStream->Tell();
2150 0 : pStream->Seek( STREAM_SEEK_TO_BEGIN );
2151 0 : ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
2152 0 : SvMemoryStream aStream;
2153 0 : pCodec->BeginCompression();
2154 0 : pCodec->Write( aStream, (const sal_uInt8*)pStream->GetData(), nEndPos );
2155 0 : pCodec->EndCompression();
2156 0 : delete pCodec;
2157 0 : nEndPos = aStream.Tell();
2158 0 : pStream->Seek( STREAM_SEEK_TO_BEGIN );
2159 0 : aStream.Seek( STREAM_SEEK_TO_BEGIN );
2160 0 : pStream->SetStreamSize( nEndPos );
2161 0 : pStream->Write( aStream.GetData(), nEndPos );
2162 0 : return true;
2163 : #else
2164 : (void)pStream;
2165 : return false;
2166 : #endif
2167 : }
2168 :
2169 0 : void PDFWriterImpl::beginCompression()
2170 : {
2171 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2172 0 : m_pCodec = new ZCodec( 0x4000, 0x4000 );
2173 0 : m_pMemStream = new SvMemoryStream();
2174 0 : m_pCodec->BeginCompression();
2175 : #endif
2176 0 : }
2177 :
2178 0 : void PDFWriterImpl::endCompression()
2179 : {
2180 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2181 0 : if( m_pCodec )
2182 : {
2183 0 : m_pCodec->EndCompression();
2184 0 : delete m_pCodec;
2185 0 : m_pCodec = NULL;
2186 0 : sal_uInt64 nLen = m_pMemStream->Tell();
2187 0 : m_pMemStream->Seek( 0 );
2188 0 : writeBuffer( m_pMemStream->GetData(), nLen );
2189 0 : delete m_pMemStream;
2190 0 : m_pMemStream = NULL;
2191 : }
2192 : #endif
2193 0 : }
2194 :
2195 0 : bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes )
2196 : {
2197 0 : if( ! m_bOpen ) // we are already down the drain
2198 0 : return false;
2199 :
2200 0 : if( ! nBytes ) // huh ?
2201 0 : return true;
2202 :
2203 0 : if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2204 : {
2205 0 : m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END );
2206 0 : m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) );
2207 0 : return true;
2208 : }
2209 :
2210 : sal_uInt64 nWritten;
2211 0 : if( m_pCodec )
2212 : {
2213 0 : m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes );
2214 0 : nWritten = nBytes;
2215 : }
2216 : else
2217 : {
2218 0 : sal_Bool buffOK = sal_True;
2219 0 : if( m_bEncryptThisStream )
2220 : {
2221 : /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
2222 0 : if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) != sal_False )
2223 : rtl_cipher_encodeARCFOUR( m_aCipher,
2224 : (sal_uInt8*)pBuffer, static_cast<sal_Size>(nBytes),
2225 0 : m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) );
2226 : }
2227 :
2228 0 : const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer : pBuffer;
2229 0 : if( m_aDocDigest )
2230 0 : rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) );
2231 :
2232 0 : if( osl_writeFile( m_aFile,
2233 : pWriteBuffer,
2234 0 : nBytes, &nWritten ) != osl_File_E_None )
2235 0 : nWritten = 0;
2236 :
2237 0 : if( nWritten != nBytes )
2238 : {
2239 0 : osl_closeFile( m_aFile );
2240 0 : m_bOpen = false;
2241 : }
2242 : }
2243 :
2244 0 : return nWritten == nBytes;
2245 : }
2246 :
2247 0 : OutputDevice* PDFWriterImpl::getReferenceDevice()
2248 : {
2249 0 : if( ! m_pReferenceDevice )
2250 : {
2251 0 : VirtualDevice* pVDev = new VirtualDevice( 0 );
2252 :
2253 0 : m_pReferenceDevice = pVDev;
2254 :
2255 0 : if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 )
2256 0 : pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 );
2257 : else
2258 0 : pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy );
2259 :
2260 0 : pVDev->SetOutputSizePixel( Size( 640, 480 ) );
2261 0 : pVDev->SetMapMode( MAP_MM );
2262 :
2263 0 : m_pReferenceDevice->mpPDFWriter = this;
2264 0 : m_pReferenceDevice->ImplUpdateFontData( sal_True );
2265 : }
2266 0 : return m_pReferenceDevice;
2267 : }
2268 :
2269 0 : class ImplPdfBuiltinFontData : public PhysicalFontFace
2270 : {
2271 : private:
2272 : const PDFWriterImpl::BuiltinFont& mrBuiltin;
2273 :
2274 : public:
2275 : enum {PDF_FONT_MAGIC = 0xBDFF0A1C };
2276 : ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& );
2277 0 : const PDFWriterImpl::BuiltinFont* GetBuiltinFont() const { return &mrBuiltin; }
2278 :
2279 0 : virtual PhysicalFontFace* Clone() const { return new ImplPdfBuiltinFontData(*this); }
2280 : virtual ImplFontEntry* CreateFontInstance( FontSelectPattern& ) const;
2281 0 : virtual sal_IntPtr GetFontId() const { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); }
2282 : };
2283 :
2284 0 : inline const ImplPdfBuiltinFontData* GetPdfFontData( const PhysicalFontFace* pFontData )
2285 : {
2286 0 : const ImplPdfBuiltinFontData* pFD = NULL;
2287 0 : if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) )
2288 0 : pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData );
2289 0 : return pFD;
2290 : }
2291 :
2292 0 : static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin )
2293 : {
2294 0 : ImplDevFontAttributes aDFA;
2295 0 : aDFA.maName = rtl::OUString::createFromAscii( rBuiltin.m_pName );
2296 0 : aDFA.maStyleName = rtl::OUString::createFromAscii( rBuiltin.m_pStyleName );
2297 0 : aDFA.meFamily = rBuiltin.m_eFamily;
2298 0 : aDFA.mbSymbolFlag = (rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 );
2299 0 : aDFA.mePitch = rBuiltin.m_ePitch;
2300 0 : aDFA.meWeight = rBuiltin.m_eWeight;
2301 0 : aDFA.meItalic = rBuiltin.m_eItalic;
2302 0 : aDFA.meWidthType = rBuiltin.m_eWidthType;
2303 :
2304 0 : aDFA.mbOrientation = true;
2305 0 : aDFA.mbDevice = true;
2306 0 : aDFA.mnQuality = 50000;
2307 0 : aDFA.mbSubsettable = false;
2308 0 : aDFA.mbEmbeddable = false;
2309 0 : return aDFA;
2310 : }
2311 :
2312 0 : ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin )
2313 : : PhysicalFontFace( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ),
2314 0 : mrBuiltin( rBuiltin )
2315 0 : {}
2316 :
2317 0 : ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( FontSelectPattern& rFSD ) const
2318 : {
2319 0 : ImplFontEntry* pEntry = new ImplFontEntry( rFSD );
2320 0 : return pEntry;
2321 : }
2322 :
2323 0 : ImplDevFontList* PDFWriterImpl::filterDevFontList( ImplDevFontList* pFontList )
2324 : {
2325 : DBG_ASSERT( m_aSubsets.empty(), "Fonts changing during PDF generation, document will be invalid" );
2326 0 : ImplDevFontList* pFiltered = pFontList->Clone( true, true );
2327 :
2328 : // append the PDF builtin fonts
2329 0 : if( !m_bIsPDF_A1 && !m_bEmbedStandardFonts)
2330 0 : for( unsigned int i = 0; i < SAL_N_ELEMENTS(m_aBuiltinFonts); i++ )
2331 : {
2332 0 : PhysicalFontFace* pNewData = new ImplPdfBuiltinFontData( m_aBuiltinFonts[i] );
2333 0 : pFiltered->Add( pNewData );
2334 : }
2335 0 : return pFiltered;
2336 : }
2337 :
2338 0 : bool PDFWriterImpl::isBuiltinFont( const PhysicalFontFace* pFont ) const
2339 : {
2340 0 : const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
2341 0 : return (pFD != NULL);
2342 : }
2343 :
2344 0 : void PDFWriterImpl::getFontMetric( FontSelectPattern* pSelect, ImplFontMetricData* pMetric ) const
2345 : {
2346 0 : const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2347 0 : if( !pFD )
2348 0 : return;
2349 0 : const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2350 :
2351 0 : pMetric->mnOrientation = sal::static_int_cast<short>(pSelect->mnOrientation);
2352 0 : pMetric->meFamily = pBuiltinFont->m_eFamily;
2353 0 : pMetric->mePitch = pBuiltinFont->m_ePitch;
2354 0 : pMetric->meWeight = pBuiltinFont->m_eWeight;
2355 0 : pMetric->meItalic = pBuiltinFont->m_eItalic;
2356 0 : pMetric->mbSymbolFlag = pFD->IsSymbolFont();
2357 0 : pMetric->mnWidth = pSelect->mnHeight;
2358 0 : pMetric->mnAscent = ( pSelect->mnHeight * +pBuiltinFont->m_nAscent + 500 ) / 1000;
2359 0 : pMetric->mnDescent = ( pSelect->mnHeight * -pBuiltinFont->m_nDescent + 500 ) / 1000;
2360 0 : pMetric->mnIntLeading = 0;
2361 0 : pMetric->mnExtLeading = 0;
2362 0 : pMetric->mnSlant = 0;
2363 0 : pMetric->mbScalableFont = true;
2364 0 : pMetric->mbDevice = true;
2365 : }
2366 :
2367 : // -----------------------------------------------------------------------
2368 :
2369 : namespace vcl {
2370 :
2371 0 : class PDFSalLayout : public GenericSalLayout
2372 : {
2373 : PDFWriterImpl& mrPDFWriterImpl;
2374 : const PDFWriterImpl::BuiltinFont& mrBuiltinFont;
2375 : bool mbIsSymbolFont;
2376 : long mnPixelPerEM;
2377 : String maOrigText;
2378 :
2379 : public:
2380 : PDFSalLayout( PDFWriterImpl&,
2381 : const PDFWriterImpl::BuiltinFont&,
2382 : long nPixelPerEM, int nOrientation );
2383 :
2384 0 : void SetText( const rtl::OUString& rText ) { maOrigText = rText; }
2385 : virtual bool LayoutText( ImplLayoutArgs& );
2386 : virtual void InitFont() const;
2387 : virtual void DrawText( SalGraphics& ) const;
2388 : };
2389 :
2390 : }
2391 :
2392 : // -----------------------------------------------------------------------
2393 :
2394 0 : PDFSalLayout::PDFSalLayout( PDFWriterImpl& rPDFWriterImpl,
2395 : const PDFWriterImpl::BuiltinFont& rBuiltinFont,
2396 : long nPixelPerEM, int nOrientation )
2397 : : mrPDFWriterImpl( rPDFWriterImpl ),
2398 : mrBuiltinFont( rBuiltinFont ),
2399 0 : mnPixelPerEM( nPixelPerEM )
2400 : {
2401 0 : mbIsSymbolFont = (rBuiltinFont.m_eCharSet != RTL_TEXTENCODING_MS_1252);
2402 0 : SetOrientation( nOrientation );
2403 0 : }
2404 :
2405 : // -----------------------------------------------------------------------
2406 :
2407 0 : bool PDFSalLayout::LayoutText( ImplLayoutArgs& rArgs )
2408 : {
2409 0 : const rtl::OUString aText(rArgs.mpStr+rArgs.mnMinCharPos, rArgs.mnEndCharPos-rArgs.mnMinCharPos);
2410 0 : SetText( aText );
2411 0 : SetUnitsPerPixel( 1000 );
2412 :
2413 0 : rtl_UnicodeToTextConverter aConv = rtl_createTextToUnicodeConverter( mrBuiltinFont.m_eCharSet );
2414 :
2415 0 : Point aNewPos( 0, 0 );
2416 : bool bRightToLeft;
2417 0 : Reserve(rArgs.mnLength);
2418 0 : for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); )
2419 : {
2420 : // TODO: handle unicode surrogates
2421 : // on the other hand the PDF builtin fonts don't support them anyway
2422 0 : sal_Unicode cChar = rArgs.mpStr[ nCharPos ];
2423 0 : if( bRightToLeft )
2424 0 : cChar = static_cast<sal_Unicode>(GetMirroredChar( cChar ));
2425 :
2426 : sal_Char aBuf[4];
2427 : sal_uInt32 nInfo;
2428 : sal_Size nSrcCvtChars;
2429 :
2430 : sal_Size nConv = rtl_convertUnicodeToText( aConv,
2431 : NULL,
2432 : &cChar, 1,
2433 : aBuf, sizeof(aBuf)/sizeof(*aBuf),
2434 : RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR,
2435 0 : &nInfo, &nSrcCvtChars );
2436 : // check whether conversion was possible
2437 : // else fallback font is needed as the standard fonts
2438 : // are handled via WinAnsi encoding
2439 0 : if( nConv > 0 )
2440 0 : cChar = ((sal_Unicode)aBuf[0]) & 0x00ff;
2441 :
2442 0 : if( cChar & 0xff00 )
2443 : {
2444 0 : cChar = 0; // NotDef glyph
2445 0 : rArgs.NeedFallback( nCharPos, bRightToLeft );
2446 : }
2447 :
2448 0 : long nGlyphWidth = (long)mrBuiltinFont.m_aWidths[cChar] * mnPixelPerEM;
2449 0 : long nGlyphFlags = 0; // builtin fonts don't have diacritic glyphs
2450 0 : if( bRightToLeft )
2451 0 : nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
2452 : // TODO: get kerning from builtin fonts
2453 0 : GlyphItem aGI( nCharPos, cChar, aNewPos, nGlyphFlags, nGlyphWidth );
2454 0 : AppendGlyph( aGI );
2455 :
2456 0 : aNewPos.X() += nGlyphWidth;
2457 : }
2458 :
2459 0 : rtl_destroyUnicodeToTextConverter( aConv );
2460 :
2461 0 : return true;
2462 : }
2463 :
2464 : // -----------------------------------------------------------------------
2465 :
2466 0 : void PDFSalLayout::InitFont() const
2467 : {
2468 : // TODO: recreate font with all its attributes
2469 0 : }
2470 :
2471 : // -----------------------------------------------------------------------
2472 :
2473 0 : void PDFSalLayout::DrawText( SalGraphics& ) const
2474 : {
2475 0 : mrPDFWriterImpl.drawLayout( *const_cast<PDFSalLayout*>(this), maOrigText, true );
2476 0 : }
2477 :
2478 : // -----------------------------------------------------------------------
2479 :
2480 0 : SalLayout* PDFWriterImpl::GetTextLayout( ImplLayoutArgs& rArgs, FontSelectPattern* pSelect )
2481 : {
2482 : DBG_ASSERT( (pSelect->mpFontData != NULL),
2483 : "PDFWriterImpl::GetTextLayout mpFontData is NULL" );
2484 :
2485 0 : const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData );
2486 0 : if( !pFD )
2487 0 : return NULL;
2488 0 : const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2489 :
2490 0 : long nPixelPerEM = pSelect->mnWidth ? pSelect->mnWidth : pSelect->mnHeight;
2491 0 : int nOrientation = pSelect->mnOrientation;
2492 0 : PDFSalLayout* pLayout = new PDFSalLayout( *this, *pBuiltinFont, nPixelPerEM, nOrientation );
2493 0 : pLayout->SetText( rArgs.mpStr );
2494 0 : return pLayout;
2495 : }
2496 :
2497 0 : sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
2498 : {
2499 0 : endPage();
2500 0 : m_nCurrentPage = m_aPages.size();
2501 0 : m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) );
2502 0 : m_aPages.back().m_nPageIndex = m_nCurrentPage;
2503 0 : m_aPages.back().beginStream();
2504 :
2505 : // setup global graphics state
2506 : // linewidth is "1 pixel" by default
2507 0 : OStringBuffer aBuf( 16 );
2508 0 : appendDouble( 72.0/double(getReferenceDevice()->ImplGetDPIX()), aBuf );
2509 0 : aBuf.append( " w\n" );
2510 0 : writeBuffer( aBuf.getStr(), aBuf.getLength() );
2511 :
2512 0 : return m_nCurrentPage;
2513 : }
2514 :
2515 0 : void PDFWriterImpl::endPage()
2516 : {
2517 0 : if( m_aPages.begin() != m_aPages.end() )
2518 : {
2519 : // close eventual MC sequence
2520 0 : endStructureElementMCSeq();
2521 :
2522 : // sanity check
2523 0 : if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2524 : {
2525 : OSL_FAIL( "redirection across pages !!!" );
2526 0 : m_aOutputStreams.clear(); // leak !
2527 0 : m_aMapMode.SetOrigin( Point() );
2528 : }
2529 :
2530 0 : m_aGraphicsStack.clear();
2531 0 : m_aGraphicsStack.push_back( GraphicsState() );
2532 :
2533 : // this should pop the PDF graphics stack if necessary
2534 0 : updateGraphicsState();
2535 :
2536 0 : m_aPages.back().endStream();
2537 :
2538 : // reset the default font
2539 0 : Font aFont;
2540 0 : aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) );
2541 0 : aFont.SetSize( Size( 0, 12 ) );
2542 :
2543 0 : m_aCurrentPDFState = m_aGraphicsStack.front();
2544 0 : m_aGraphicsStack.front().m_aFont = aFont;
2545 :
2546 0 : for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin();
2547 0 : it != m_aBitmaps.end(); ++it )
2548 : {
2549 0 : if( ! it->m_aBitmap.IsEmpty() )
2550 : {
2551 0 : writeBitmapObject( *it );
2552 0 : it->m_aBitmap = BitmapEx();
2553 : }
2554 : }
2555 0 : for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg )
2556 : {
2557 0 : if( jpeg->m_pStream )
2558 : {
2559 0 : writeJPG( *jpeg );
2560 0 : delete jpeg->m_pStream;
2561 0 : jpeg->m_pStream = NULL;
2562 0 : jpeg->m_aMask = Bitmap();
2563 : }
2564 : }
2565 0 : for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin();
2566 0 : t != m_aTransparentObjects.end(); ++t )
2567 : {
2568 0 : if( t->m_pContentStream )
2569 : {
2570 0 : writeTransparentObject( *t );
2571 0 : delete t->m_pContentStream;
2572 0 : t->m_pContentStream = NULL;
2573 : }
2574 0 : }
2575 : }
2576 0 : }
2577 :
2578 0 : sal_Int32 PDFWriterImpl::createObject()
2579 : {
2580 0 : m_aObjects.push_back( ~0U );
2581 0 : return m_aObjects.size();
2582 : }
2583 :
2584 0 : bool PDFWriterImpl::updateObject( sal_Int32 n )
2585 : {
2586 0 : if( ! m_bOpen )
2587 0 : return false;
2588 :
2589 0 : sal_uInt64 nOffset = ~0U;
2590 0 : oslFileError aError = osl_getFilePos( m_aFile, &nOffset );
2591 : DBG_ASSERT( aError == osl_File_E_None, "could not register object" );
2592 0 : if( aError != osl_File_E_None )
2593 : {
2594 0 : osl_closeFile( m_aFile );
2595 0 : m_bOpen = false;
2596 : }
2597 0 : m_aObjects[ n-1 ] = nOffset;
2598 0 : return aError == osl_File_E_None;
2599 : }
2600 :
2601 : #define CHECK_RETURN( x ) if( !(x) ) return 0
2602 :
2603 0 : sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject )
2604 : {
2605 0 : if( nObject > 0 )
2606 : {
2607 0 : OStringBuffer aLine( 1024 );
2608 :
2609 0 : aLine.append( nObject );
2610 : aLine.append( " 0 obj\n"
2611 0 : "<</Nums[\n" );
2612 0 : sal_Int32 nTreeItems = m_aStructParentTree.size();
2613 0 : for( sal_Int32 n = 0; n < nTreeItems; n++ )
2614 : {
2615 0 : aLine.append( n );
2616 0 : aLine.append( ' ' );
2617 0 : aLine.append( m_aStructParentTree[n] );
2618 0 : aLine.append( "\n" );
2619 : }
2620 0 : aLine.append( "]>>\nendobj\n\n" );
2621 0 : CHECK_RETURN( updateObject( nObject ) );
2622 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2623 : }
2624 0 : return nObject;
2625 : }
2626 :
2627 0 : const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr )
2628 : {
2629 0 : static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings;
2630 : // fill maps once
2631 0 : if( aAttributeStrings.empty() )
2632 : {
2633 0 : aAttributeStrings[ PDFWriter::Placement ] = "Placement";
2634 0 : aAttributeStrings[ PDFWriter::WritingMode ] = "WritingMode";
2635 0 : aAttributeStrings[ PDFWriter::SpaceBefore ] = "SpaceBefore";
2636 0 : aAttributeStrings[ PDFWriter::SpaceAfter ] = "SpaceAfter";
2637 0 : aAttributeStrings[ PDFWriter::StartIndent ] = "StartIndent";
2638 0 : aAttributeStrings[ PDFWriter::EndIndent ] = "EndIndent";
2639 0 : aAttributeStrings[ PDFWriter::TextIndent ] = "TextIndent";
2640 0 : aAttributeStrings[ PDFWriter::TextAlign ] = "TextAlign";
2641 0 : aAttributeStrings[ PDFWriter::Width ] = "Width";
2642 0 : aAttributeStrings[ PDFWriter::Height ] = "Height";
2643 0 : aAttributeStrings[ PDFWriter::BlockAlign ] = "BlockAlign";
2644 0 : aAttributeStrings[ PDFWriter::InlineAlign ] = "InlineAlign";
2645 0 : aAttributeStrings[ PDFWriter::LineHeight ] = "LineHeight";
2646 0 : aAttributeStrings[ PDFWriter::BaselineShift ] = "BaselineShift";
2647 0 : aAttributeStrings[ PDFWriter::TextDecorationType ] = "TextDecorationType";
2648 0 : aAttributeStrings[ PDFWriter::ListNumbering ] = "ListNumbering";
2649 0 : aAttributeStrings[ PDFWriter::RowSpan ] = "RowSpan";
2650 0 : aAttributeStrings[ PDFWriter::ColSpan ] = "ColSpan";
2651 0 : aAttributeStrings[ PDFWriter::LinkAnnotation ] = "LinkAnnotation";
2652 : }
2653 :
2654 : std::map< PDFWriter::StructAttribute, const char* >::const_iterator it =
2655 0 : aAttributeStrings.find( eAttr );
2656 :
2657 : #if OSL_DEBUG_LEVEL > 1
2658 : if( it == aAttributeStrings.end() )
2659 : fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr );
2660 : #endif
2661 :
2662 0 : return it != aAttributeStrings.end() ? it->second : "";
2663 : }
2664 :
2665 0 : const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal )
2666 : {
2667 0 : static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings;
2668 :
2669 0 : if( aValueStrings.empty() )
2670 : {
2671 0 : aValueStrings[ PDFWriter::NONE ] = "None";
2672 0 : aValueStrings[ PDFWriter::Block ] = "Block";
2673 0 : aValueStrings[ PDFWriter::Inline ] = "Inline";
2674 0 : aValueStrings[ PDFWriter::Before ] = "Before";
2675 0 : aValueStrings[ PDFWriter::After ] = "After";
2676 0 : aValueStrings[ PDFWriter::Start ] = "Start";
2677 0 : aValueStrings[ PDFWriter::End ] = "End";
2678 0 : aValueStrings[ PDFWriter::LrTb ] = "LrTb";
2679 0 : aValueStrings[ PDFWriter::RlTb ] = "RlTb";
2680 0 : aValueStrings[ PDFWriter::TbRl ] = "TbRl";
2681 0 : aValueStrings[ PDFWriter::Center ] = "Center";
2682 0 : aValueStrings[ PDFWriter::Justify ] = "Justify";
2683 0 : aValueStrings[ PDFWriter::Auto ] = "Auto";
2684 0 : aValueStrings[ PDFWriter::Middle ] = "Middle";
2685 0 : aValueStrings[ PDFWriter::Normal ] = "Normal";
2686 0 : aValueStrings[ PDFWriter::Underline ] = "Underline";
2687 0 : aValueStrings[ PDFWriter::Overline ] = "Overline";
2688 0 : aValueStrings[ PDFWriter::LineThrough ] = "LineThrough";
2689 0 : aValueStrings[ PDFWriter::Disc ] = "Disc";
2690 0 : aValueStrings[ PDFWriter::Circle ] = "Circle";
2691 0 : aValueStrings[ PDFWriter::Square ] = "Square";
2692 0 : aValueStrings[ PDFWriter::Decimal ] = "Decimal";
2693 0 : aValueStrings[ PDFWriter::UpperRoman ] = "UpperRoman";
2694 0 : aValueStrings[ PDFWriter::LowerRoman ] = "LowerRoman";
2695 0 : aValueStrings[ PDFWriter::UpperAlpha ] = "UpperAlpha";
2696 0 : aValueStrings[ PDFWriter::LowerAlpha ] = "LowerAlpha";
2697 : }
2698 :
2699 : std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it =
2700 0 : aValueStrings.find( eVal );
2701 :
2702 : #if OSL_DEBUG_LEVEL > 1
2703 : if( it == aValueStrings.end() )
2704 : fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal );
2705 : #endif
2706 :
2707 0 : return it != aValueStrings.end() ? it->second : "";
2708 : }
2709 :
2710 0 : static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt )
2711 : {
2712 0 : o_rLine.append( "/" );
2713 0 : o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) );
2714 :
2715 0 : if( i_rVal.eValue != PDFWriter::Invalid )
2716 : {
2717 0 : o_rLine.append( "/" );
2718 0 : o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) );
2719 : }
2720 : else
2721 : {
2722 : // numerical value
2723 0 : o_rLine.append( " " );
2724 0 : if( i_bIsFixedInt )
2725 0 : appendFixedInt( i_rVal.nValue, o_rLine );
2726 : else
2727 0 : o_rLine.append( i_rVal.nValue );
2728 : }
2729 0 : o_rLine.append( "\n" );
2730 0 : }
2731 :
2732 0 : OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
2733 : {
2734 : // create layout, list and table attribute sets
2735 0 : OStringBuffer aLayout(256), aList(64), aTable(64);
2736 0 : for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin();
2737 0 : it != i_rEle.m_aAttributes.end(); ++it )
2738 : {
2739 0 : if( it->first == PDFWriter::ListNumbering )
2740 0 : appendStructureAttributeLine( it->first, it->second, aList, true );
2741 0 : else if( it->first == PDFWriter::RowSpan ||
2742 0 : it->first == PDFWriter::ColSpan )
2743 0 : appendStructureAttributeLine( it->first, it->second, aTable, false );
2744 0 : else if( it->first == PDFWriter::LinkAnnotation )
2745 : {
2746 0 : sal_Int32 nLink = it->second.nValue;
2747 : std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
2748 0 : m_aLinkPropertyMap.find( nLink );
2749 0 : if( link_it != m_aLinkPropertyMap.end() )
2750 0 : nLink = link_it->second;
2751 0 : if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() )
2752 : {
2753 : // update struct parent of link
2754 0 : OStringBuffer aStructParentEntry( 32 );
2755 0 : aStructParentEntry.append( i_rEle.m_nObject );
2756 0 : aStructParentEntry.append( " 0 R" );
2757 0 : m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() );
2758 0 : m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1;
2759 :
2760 0 : sal_Int32 nRefObject = createObject();
2761 0 : OStringBuffer aRef( 256 );
2762 0 : aRef.append( nRefObject );
2763 : aRef.append( " 0 obj\n"
2764 0 : "<</Type/OBJR/Obj " );
2765 0 : aRef.append( m_aLinks[ nLink ].m_nObject );
2766 : aRef.append( " 0 R>>\n"
2767 : "endobj\n\n"
2768 0 : );
2769 0 : updateObject( nRefObject );
2770 0 : writeBuffer( aRef.getStr(), aRef.getLength() );
2771 :
2772 0 : i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) );
2773 : }
2774 : else
2775 : {
2776 : OSL_FAIL( "unresolved link id for Link structure" );
2777 : #if OSL_DEBUG_LEVEL > 1
2778 : fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink );
2779 : {
2780 : OStringBuffer aLine( "unresolved link id " );
2781 : aLine.append( nLink );
2782 : aLine.append( " for Link structure" );
2783 : emitComment( aLine.getStr() );
2784 : }
2785 : #endif
2786 : }
2787 : }
2788 : else
2789 0 : appendStructureAttributeLine( it->first, it->second, aLayout, true );
2790 : }
2791 0 : if( ! i_rEle.m_aBBox.IsEmpty() )
2792 : {
2793 0 : aLayout.append( "/BBox[" );
2794 0 : appendFixedInt( i_rEle.m_aBBox.Left(), aLayout );
2795 0 : aLayout.append( " " );
2796 0 : appendFixedInt( i_rEle.m_aBBox.Top(), aLayout );
2797 0 : aLayout.append( " " );
2798 0 : appendFixedInt( i_rEle.m_aBBox.Right(), aLayout );
2799 0 : aLayout.append( " " );
2800 0 : appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout );
2801 0 : aLayout.append( "]\n" );
2802 : }
2803 :
2804 0 : std::vector< sal_Int32 > aAttribObjects;
2805 0 : if( aLayout.getLength() )
2806 : {
2807 0 : aAttribObjects.push_back( createObject() );
2808 0 : updateObject( aAttribObjects.back() );
2809 0 : OStringBuffer aObj( 64 );
2810 0 : aObj.append( aAttribObjects.back() );
2811 : aObj.append( " 0 obj\n"
2812 0 : "<</O/Layout\n" );
2813 0 : aLayout.append( ">>\nendobj\n\n" );
2814 0 : writeBuffer( aObj.getStr(), aObj.getLength() );
2815 0 : writeBuffer( aLayout.getStr(), aLayout.getLength() );
2816 : }
2817 0 : if( aList.getLength() )
2818 : {
2819 0 : aAttribObjects.push_back( createObject() );
2820 0 : updateObject( aAttribObjects.back() );
2821 0 : OStringBuffer aObj( 64 );
2822 0 : aObj.append( aAttribObjects.back() );
2823 : aObj.append( " 0 obj\n"
2824 0 : "<</O/List\n" );
2825 0 : aList.append( ">>\nendobj\n\n" );
2826 0 : writeBuffer( aObj.getStr(), aObj.getLength() );
2827 0 : writeBuffer( aList.getStr(), aList.getLength() );
2828 : }
2829 0 : if( aTable.getLength() )
2830 : {
2831 0 : aAttribObjects.push_back( createObject() );
2832 0 : updateObject( aAttribObjects.back() );
2833 0 : OStringBuffer aObj( 64 );
2834 0 : aObj.append( aAttribObjects.back() );
2835 : aObj.append( " 0 obj\n"
2836 0 : "<</O/Table\n" );
2837 0 : aTable.append( ">>\nendobj\n\n" );
2838 0 : writeBuffer( aObj.getStr(), aObj.getLength() );
2839 0 : writeBuffer( aTable.getStr(), aTable.getLength() );
2840 : }
2841 :
2842 0 : OStringBuffer aRet( 64 );
2843 0 : if( aAttribObjects.size() > 1 )
2844 0 : aRet.append( " [" );
2845 0 : for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin();
2846 0 : at_it != aAttribObjects.end(); ++at_it )
2847 : {
2848 0 : aRet.append( " " );
2849 0 : aRet.append( *at_it );
2850 0 : aRet.append( " 0 R" );
2851 : }
2852 0 : if( aAttribObjects.size() > 1 )
2853 0 : aRet.append( " ]" );
2854 0 : return aRet.makeStringAndClear();
2855 : }
2856 :
2857 0 : sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
2858 : {
2859 0 : if(
2860 : // do not emit NonStruct and its children
2861 : rEle.m_eType == PDFWriter::NonStructElement &&
2862 : rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root
2863 : )
2864 0 : return 0;
2865 :
2866 0 : for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
2867 : {
2868 0 : if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
2869 : {
2870 0 : PDFStructureElement& rChild = m_aStructure[ *it ];
2871 0 : if( rChild.m_eType != PDFWriter::NonStructElement )
2872 : {
2873 0 : if( rChild.m_nParentElement == rEle.m_nOwnElement )
2874 0 : emitStructure( rChild );
2875 : else
2876 : {
2877 : OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure element" );
2878 : #if OSL_DEBUG_LEVEL > 1
2879 : fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
2880 : #endif
2881 : }
2882 : }
2883 : }
2884 : else
2885 : {
2886 : OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
2887 : #if OSL_DEBUG_LEVEL > 1
2888 : fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
2889 : #endif
2890 : }
2891 : }
2892 :
2893 0 : OStringBuffer aLine( 512 );
2894 0 : aLine.append( rEle.m_nObject );
2895 : aLine.append( " 0 obj\n"
2896 0 : "<</Type" );
2897 0 : sal_Int32 nParentTree = -1;
2898 0 : if( rEle.m_nOwnElement == rEle.m_nParentElement )
2899 : {
2900 0 : nParentTree = createObject();
2901 0 : CHECK_RETURN( nParentTree );
2902 0 : aLine.append( "/StructTreeRoot\n" );
2903 0 : aLine.append( "/ParentTree " );
2904 0 : aLine.append( nParentTree );
2905 0 : aLine.append( " 0 R\n" );
2906 0 : if( ! m_aRoleMap.empty() )
2907 : {
2908 0 : aLine.append( "/RoleMap<<" );
2909 0 : for( boost::unordered_map<OString,OString,OStringHash>::const_iterator
2910 0 : it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it )
2911 : {
2912 0 : aLine.append( '/' );
2913 0 : aLine.append(it->first);
2914 0 : aLine.append( '/' );
2915 0 : aLine.append( it->second );
2916 0 : aLine.append( '\n' );
2917 : }
2918 0 : aLine.append( ">>\n" );
2919 : }
2920 : }
2921 : else
2922 : {
2923 : aLine.append( "/StructElem\n"
2924 0 : "/S/" );
2925 0 : if( !rEle.m_aAlias.isEmpty() )
2926 0 : aLine.append( rEle.m_aAlias );
2927 : else
2928 0 : aLine.append( getStructureTag( rEle.m_eType ) );
2929 : aLine.append( "\n"
2930 0 : "/P " );
2931 0 : aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject );
2932 : aLine.append( " 0 R\n"
2933 0 : "/Pg " );
2934 0 : aLine.append( rEle.m_nFirstPageObject );
2935 0 : aLine.append( " 0 R\n" );
2936 0 : if( !rEle.m_aActualText.isEmpty() )
2937 : {
2938 0 : aLine.append( "/ActualText" );
2939 0 : appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine );
2940 0 : aLine.append( "\n" );
2941 : }
2942 0 : if( !rEle.m_aAltText.isEmpty() )
2943 : {
2944 0 : aLine.append( "/Alt" );
2945 0 : appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine );
2946 0 : aLine.append( "\n" );
2947 : }
2948 : }
2949 0 : if( ! rEle.m_aBBox.IsEmpty() || rEle.m_aAttributes.size() )
2950 : {
2951 0 : OString aAttribs = emitStructureAttributes( rEle );
2952 0 : if( !aAttribs.isEmpty() )
2953 : {
2954 0 : aLine.append( "/A" );
2955 0 : aLine.append( aAttribs );
2956 0 : aLine.append( "\n" );
2957 0 : }
2958 : }
2959 0 : if( !rEle.m_aLocale.Language.isEmpty() )
2960 : {
2961 0 : OUStringBuffer aLocBuf( 16 );
2962 0 : aLocBuf.append( rEle.m_aLocale.Language.toAsciiLowerCase() );
2963 0 : if( !rEle.m_aLocale.Country.isEmpty() )
2964 : {
2965 0 : aLocBuf.append( sal_Unicode('-') );
2966 0 : aLocBuf.append( rEle.m_aLocale.Country );
2967 : }
2968 0 : aLine.append( "/Lang" );
2969 0 : appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine );
2970 0 : aLine.append( "\n" );
2971 : }
2972 0 : if( ! rEle.m_aKids.empty() )
2973 : {
2974 0 : unsigned int i = 0;
2975 0 : aLine.append( "/K[" );
2976 0 : for( std::list< PDFStructureElementKid >::const_iterator it =
2977 0 : rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ )
2978 : {
2979 0 : if( it->nMCID == -1 )
2980 : {
2981 0 : aLine.append( it->nObject );
2982 0 : aLine.append( " 0 R" );
2983 0 : aLine.append( ( (i & 15) == 15 ) ? "\n" : " " );
2984 : }
2985 : else
2986 : {
2987 0 : if( it->nObject == rEle.m_nFirstPageObject )
2988 : {
2989 0 : aLine.append( it->nMCID );
2990 0 : aLine.append( " " );
2991 : }
2992 : else
2993 : {
2994 0 : aLine.append( "<</Type/MCR/Pg " );
2995 0 : aLine.append( it->nObject );
2996 0 : aLine.append( " 0 R /MCID " );
2997 0 : aLine.append( it->nMCID );
2998 0 : aLine.append( ">>\n" );
2999 : }
3000 : }
3001 : }
3002 0 : aLine.append( "]\n" );
3003 : }
3004 0 : aLine.append( ">>\nendobj\n\n" );
3005 :
3006 0 : CHECK_RETURN( updateObject( rEle.m_nObject ) );
3007 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3008 :
3009 0 : CHECK_RETURN( emitStructParentTree( nParentTree ) );
3010 :
3011 0 : return rEle.m_nObject;
3012 : }
3013 :
3014 0 : bool PDFWriterImpl::emitGradients()
3015 : {
3016 0 : for( std::list<GradientEmit>::iterator it = m_aGradients.begin();
3017 0 : it != m_aGradients.end(); ++it )
3018 : {
3019 0 : CHECK_RETURN( writeGradientFunction( *it ) );
3020 : }
3021 0 : return true;
3022 : }
3023 :
3024 0 : bool PDFWriterImpl::emitTilings()
3025 : {
3026 0 : OStringBuffer aTilingObj( 1024 );
3027 :
3028 0 : for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it )
3029 : {
3030 : DBG_ASSERT( it->m_pTilingStream, "tiling without stream" );
3031 0 : if( ! it->m_pTilingStream )
3032 0 : continue;
3033 :
3034 0 : aTilingObj.setLength( 0 );
3035 :
3036 : #if OSL_DEBUG_LEVEL > 1
3037 : emitComment( "PDFWriterImpl::emitTilings" );
3038 : #endif
3039 :
3040 0 : sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left();
3041 0 : sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top();
3042 0 : sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth();
3043 0 : sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight();
3044 0 : if( it->m_aCellSize.Width() == 0 )
3045 0 : it->m_aCellSize.Width() = nW;
3046 0 : if( it->m_aCellSize.Height() == 0 )
3047 0 : it->m_aCellSize.Height() = nH;
3048 :
3049 0 : bool bDeflate = compressStream( it->m_pTilingStream );
3050 0 : it->m_pTilingStream->Seek( STREAM_SEEK_TO_END );
3051 0 : sal_Size nTilingStreamSize = it->m_pTilingStream->Tell();
3052 0 : it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN );
3053 :
3054 : // write pattern object
3055 0 : aTilingObj.append( it->m_nObject );
3056 0 : aTilingObj.append( " 0 obj\n" );
3057 : aTilingObj.append( "<</Type/Pattern/PatternType 1\n"
3058 : "/PaintType 1\n"
3059 : "/TilingType 2\n"
3060 0 : "/BBox[" );
3061 0 : appendFixedInt( nX, aTilingObj );
3062 0 : aTilingObj.append( ' ' );
3063 0 : appendFixedInt( nY, aTilingObj );
3064 0 : aTilingObj.append( ' ' );
3065 0 : appendFixedInt( nX+nW, aTilingObj );
3066 0 : aTilingObj.append( ' ' );
3067 0 : appendFixedInt( nY+nH, aTilingObj );
3068 : aTilingObj.append( "]\n"
3069 0 : "/XStep " );
3070 0 : appendFixedInt( it->m_aCellSize.Width(), aTilingObj );
3071 : aTilingObj.append( "\n"
3072 0 : "/YStep " );
3073 0 : appendFixedInt( it->m_aCellSize.Height(), aTilingObj );
3074 0 : aTilingObj.append( "\n" );
3075 0 : if( it->m_aTransform.matrix[0] != 1.0 ||
3076 0 : it->m_aTransform.matrix[1] != 0.0 ||
3077 0 : it->m_aTransform.matrix[3] != 0.0 ||
3078 0 : it->m_aTransform.matrix[4] != 1.0 ||
3079 0 : it->m_aTransform.matrix[2] != 0.0 ||
3080 0 : it->m_aTransform.matrix[5] != 0.0 )
3081 : {
3082 0 : aTilingObj.append( "/Matrix [" );
3083 : // TODO: scaling, mirroring on y, etc
3084 0 : appendDouble( it->m_aTransform.matrix[0], aTilingObj );
3085 0 : aTilingObj.append( ' ' );
3086 0 : appendDouble( it->m_aTransform.matrix[1], aTilingObj );
3087 0 : aTilingObj.append( ' ' );
3088 0 : appendDouble( it->m_aTransform.matrix[3], aTilingObj );
3089 0 : aTilingObj.append( ' ' );
3090 0 : appendDouble( it->m_aTransform.matrix[4], aTilingObj );
3091 0 : aTilingObj.append( ' ' );
3092 0 : appendDouble( it->m_aTransform.matrix[2], aTilingObj );
3093 0 : aTilingObj.append( ' ' );
3094 0 : appendDouble( it->m_aTransform.matrix[5], aTilingObj );
3095 0 : aTilingObj.append( "]\n" );
3096 : }
3097 0 : aTilingObj.append( "/Resources" );
3098 0 : it->m_aResources.append( aTilingObj, getFontDictObject() );
3099 0 : if( bDeflate )
3100 0 : aTilingObj.append( "/Filter/FlateDecode" );
3101 0 : aTilingObj.append( "/Length " );
3102 0 : aTilingObj.append( (sal_Int32)nTilingStreamSize );
3103 0 : aTilingObj.append( ">>\nstream\n" );
3104 0 : CHECK_RETURN( updateObject( it->m_nObject ) );
3105 0 : CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
3106 0 : checkAndEnableStreamEncryption( it->m_nObject );
3107 0 : nTilingStreamSize = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize );
3108 0 : delete it->m_pTilingStream;
3109 0 : it->m_pTilingStream = NULL;
3110 0 : if( nTilingStreamSize == 0 )
3111 0 : return false;
3112 0 : disableStreamEncryption();
3113 0 : aTilingObj.setLength( 0 );
3114 0 : aTilingObj.append( "\nendstream\nendobj\n\n" );
3115 0 : CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) );
3116 : }
3117 0 : return true;
3118 : }
3119 :
3120 0 : sal_Int32 PDFWriterImpl::emitBuiltinFont( const PhysicalFontFace* pFont, sal_Int32 nFontObject )
3121 : {
3122 0 : const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
3123 0 : if( !pFD )
3124 0 : return 0;
3125 0 : const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
3126 :
3127 0 : OStringBuffer aLine( 1024 );
3128 :
3129 0 : if( nFontObject <= 0 )
3130 0 : nFontObject = createObject();
3131 0 : CHECK_RETURN( updateObject( nFontObject ) );
3132 0 : aLine.append( nFontObject );
3133 : aLine.append( " 0 obj\n"
3134 0 : "<</Type/Font/Subtype/Type1/BaseFont/" );
3135 0 : appendName( pBuiltinFont->m_pPSName, aLine );
3136 0 : aLine.append( "\n" );
3137 0 : if( pBuiltinFont->m_eCharSet == RTL_TEXTENCODING_MS_1252 )
3138 0 : aLine.append( "/Encoding/WinAnsiEncoding\n" );
3139 0 : aLine.append( ">>\nendobj\n\n" );
3140 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3141 0 : return nFontObject;
3142 : }
3143 :
3144 0 : std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const PhysicalFontFace* pFont, EmbedFont& rEmbed )
3145 : {
3146 0 : std::map< sal_Int32, sal_Int32 > aRet;
3147 0 : if( isBuiltinFont( pFont ) )
3148 : {
3149 0 : aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont );
3150 : return aRet;
3151 : }
3152 :
3153 0 : sal_Int32 nFontDescriptor = 0;
3154 0 : rtl::OString aSubType( "/Type1" );
3155 0 : FontSubsetInfo aInfo;
3156 : // fill in dummy values
3157 0 : aInfo.m_nAscent = 1000;
3158 0 : aInfo.m_nDescent = 200;
3159 0 : aInfo.m_nCapHeight = 1000;
3160 0 : aInfo.m_aFontBBox = Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) );
3161 0 : aInfo.m_aPSName = pFont->maName;
3162 : sal_Int32 pWidths[256];
3163 0 : memset( pWidths, 0, sizeof(pWidths) );
3164 0 : if( pFont->IsEmbeddable() )
3165 : {
3166 0 : const unsigned char* pFontData = NULL;
3167 0 : long nFontLen = 0;
3168 : sal_Ucs nEncodedCodes[256];
3169 : sal_Int32 pEncWidths[256];
3170 0 : if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pEncWidths, aInfo, &nFontLen )) != NULL )
3171 : {
3172 0 : m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3173 0 : for( int i = 0; i < 256; i++ )
3174 : {
3175 0 : if( nEncodedCodes[i] >= 32 && nEncodedCodes[i] < 256 )
3176 : {
3177 0 : pWidths[i] = pEncWidths[ i ];
3178 : }
3179 : }
3180 : }
3181 : }
3182 0 : else if( pFont->mbSubsettable )
3183 : {
3184 0 : aSubType = rtl::OString( "/TrueType" );
3185 0 : Int32Vector aGlyphWidths;
3186 0 : Ucs2UIntMap aUnicodeMap;
3187 0 : m_pReferenceDevice->mpGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap );
3188 :
3189 0 : OUString aTmpName;
3190 0 : osl_createTempFile( NULL, NULL, &aTmpName.pData );
3191 : sal_Int32 pGlyphIDs[ 256 ];
3192 : sal_uInt8 pEncoding[ 256 ];
3193 : sal_Ucs pUnicodes[ 256 ];
3194 : sal_Int32 pDuWidths[ 256 ];
3195 :
3196 0 : memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) );
3197 0 : memset( pEncoding, 0, sizeof( pEncoding ) );
3198 0 : memset( pUnicodes, 0, sizeof( pUnicodes ) );
3199 0 : memset( pDuWidths, 0, sizeof( pDuWidths ) );
3200 :
3201 0 : for( sal_Ucs c = 32; c < 256; c++ )
3202 : {
3203 0 : pUnicodes[c] = c;
3204 0 : pEncoding[c] = c;
3205 0 : pGlyphIDs[c] = 0;
3206 0 : if( aUnicodeMap.find( c ) != aUnicodeMap.end() )
3207 0 : pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ];
3208 : }
3209 :
3210 0 : m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, pFont, pGlyphIDs, pEncoding, pDuWidths, 256, aInfo );
3211 0 : osl_removeFile( aTmpName.pData );
3212 : }
3213 : else
3214 : {
3215 : OSL_FAIL( "system font neither embeddable nor subsettable" );
3216 : }
3217 :
3218 : // write font descriptor
3219 0 : nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 );
3220 0 : if( nFontDescriptor )
3221 : {
3222 : // write font object
3223 0 : sal_Int32 nObject = createObject();
3224 0 : if( updateObject( nObject ) )
3225 : {
3226 0 : OStringBuffer aLine( 1024 );
3227 0 : aLine.append( nObject );
3228 : aLine.append( " 0 obj\n"
3229 0 : "<</Type/Font/Subtype" );
3230 0 : aLine.append( aSubType );
3231 0 : aLine.append( "/BaseFont/" );
3232 0 : appendName( aInfo.m_aPSName, aLine );
3233 0 : aLine.append( "\n" );
3234 0 : if( !pFont->mbSymbolFlag )
3235 0 : aLine.append( "/Encoding/WinAnsiEncoding\n" );
3236 : aLine.append( "/FirstChar 32 /LastChar 255\n"
3237 0 : "/Widths[" );
3238 0 : for( int i = 32; i < 256; i++ )
3239 : {
3240 0 : aLine.append( pWidths[i] );
3241 0 : aLine.append( ((i&15) == 15) ? "\n" : " " );
3242 : }
3243 : aLine.append( "]\n"
3244 0 : "/FontDescriptor " );
3245 0 : aLine.append( nFontDescriptor );
3246 : aLine.append( " 0 R>>\n"
3247 0 : "endobj\n\n" );
3248 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
3249 :
3250 0 : aRet[ rEmbed.m_nNormalFontID ] = nObject;
3251 : }
3252 : }
3253 :
3254 0 : return aRet;
3255 : }
3256 :
3257 : typedef int ThreeInts[3];
3258 0 : static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen,
3259 : ThreeInts& rSegmentLengths )
3260 : {
3261 0 : if( !pFontBytes || (nByteLen < 0) )
3262 0 : return false;
3263 0 : const unsigned char* pPtr = pFontBytes;
3264 0 : const unsigned char* pEnd = pFontBytes + nByteLen;
3265 :
3266 0 : for( int i = 0; i < 3; ++i) {
3267 : // read segment1 header
3268 0 : if( pPtr+6 >= pEnd )
3269 0 : return false;
3270 0 : if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) )
3271 0 : return false;
3272 0 : const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2];
3273 0 : if( nLen <= 0)
3274 0 : return false;
3275 0 : rSegmentLengths[i] = nLen;
3276 0 : pPtr += nLen + 6;
3277 : }
3278 :
3279 : // read segment-end header
3280 0 : if( pPtr+2 >= pEnd )
3281 0 : return false;
3282 0 : if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) )
3283 0 : return false;
3284 :
3285 0 : return true;
3286 : }
3287 :
3288 0 : struct FontException : public std::exception
3289 : {
3290 : };
3291 :
3292 : // TODO: always subset instead of embedding the full font => this method becomes obsolete then
3293 0 : std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const PhysicalFontFace* pFont, EmbedFont& rEmbed )
3294 : {
3295 0 : std::map< sal_Int32, sal_Int32 > aRet;
3296 0 : if( isBuiltinFont( pFont ) )
3297 : {
3298 0 : aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont );
3299 : return aRet;
3300 : }
3301 :
3302 0 : sal_Int32 nStreamObject = 0;
3303 0 : sal_Int32 nFontDescriptor = 0;
3304 :
3305 : // prepare font encoding
3306 0 : const Ucs2SIntMap* pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pFont, NULL );
3307 0 : sal_Int32 nToUnicodeStream = 0;
3308 : sal_uInt8 nEncoding[256];
3309 : sal_Ucs nEncodedCodes[256];
3310 0 : std::vector<sal_Ucs> aUnicodes;
3311 0 : aUnicodes.reserve( 256 );
3312 : sal_Int32 pUnicodesPerGlyph[256];
3313 : sal_Int32 pEncToUnicodeIndex[256];
3314 0 : if( pEncoding )
3315 : {
3316 0 : memset( nEncoding, 0, sizeof(nEncoding) );
3317 0 : memset( nEncodedCodes, 0, sizeof(nEncodedCodes) );
3318 0 : memset( pUnicodesPerGlyph, 0, sizeof(pUnicodesPerGlyph) );
3319 0 : memset( pEncToUnicodeIndex, 0, sizeof(pEncToUnicodeIndex) );
3320 0 : for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it )
3321 : {
3322 0 : if( it->second != -1 )
3323 : {
3324 0 : sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff);
3325 0 : nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode );
3326 0 : nEncodedCodes[ nCode ] = it->first;
3327 0 : pEncToUnicodeIndex[ nCode ] = static_cast<sal_Int32>(aUnicodes.size());
3328 0 : aUnicodes.push_back( it->first );
3329 0 : pUnicodesPerGlyph[ nCode ] = 1;
3330 : }
3331 : }
3332 : }
3333 :
3334 0 : FontSubsetInfo aInfo;
3335 : sal_Int32 pWidths[256];
3336 0 : const unsigned char* pFontData = NULL;
3337 0 : long nFontLen = 0;
3338 : sal_Int32 nLength1, nLength2;
3339 : try
3340 : {
3341 0 : if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL )
3342 : {
3343 0 : if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 )
3344 0 : throw FontException();
3345 : // see whether it is pfb or pfa; if it is a pfb, fill ranges
3346 : // of 6 bytes that are not part of the font program
3347 0 : std::list< int > aSections;
3348 0 : std::list< int >::const_iterator it;
3349 0 : int nIndex = 0;
3350 0 : while( (nIndex < nFontLen-1) && pFontData[nIndex] == 0x80 )
3351 : {
3352 0 : aSections.push_back( nIndex );
3353 0 : if( pFontData[nIndex+1] == 0x03 )
3354 0 : break;
3355 : sal_Int32 nBytes =
3356 0 : ((sal_Int32)pFontData[nIndex+2]) |
3357 0 : ((sal_Int32)pFontData[nIndex+3]) << 8 |
3358 0 : ((sal_Int32)pFontData[nIndex+4]) << 16 |
3359 0 : ((sal_Int32)pFontData[nIndex+5]) << 24;
3360 0 : nIndex += nBytes+6;
3361 : }
3362 :
3363 : // search for eexec
3364 : // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below
3365 0 : nIndex = 0;
3366 : int nEndAsciiIndex;
3367 : int nBeginBinaryIndex;
3368 : int nEndBinaryIndex;
3369 0 : do
3370 : {
3371 0 : while( nIndex < nFontLen-4 &&
3372 0 : ( pFontData[nIndex] != 'e' ||
3373 0 : pFontData[nIndex+1] != 'e' ||
3374 0 : pFontData[nIndex+2] != 'x' ||
3375 0 : pFontData[nIndex+3] != 'e' ||
3376 0 : pFontData[nIndex+4] != 'c'
3377 : )
3378 : )
3379 : {
3380 0 : ++nIndex;
3381 : }
3382 : // check whether we are in a excluded section
3383 0 : for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3384 : ;
3385 0 : } while( it != aSections.end() && nIndex < nFontLen-4 );
3386 : // this should end the ascii part
3387 0 : if( nIndex > nFontLen-5 )
3388 0 : throw FontException();
3389 :
3390 0 : nEndAsciiIndex = nIndex+4;
3391 : // now count backwards until we can account for 512 '0'
3392 : // which is the endmarker of the (hopefully) binary data
3393 : // do not count the pfb header sections
3394 0 : int nFound = 0;
3395 0 : nIndex = nFontLen-1;
3396 0 : while( nIndex > 0 && nFound < 512 )
3397 : {
3398 0 : for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3399 : ;
3400 0 : if( it == aSections.end() )
3401 : {
3402 : // inside the 512 '0' block there may only be whitespace
3403 : // according to T1 spec; probably it would be to simple
3404 : // if all fonts complied
3405 0 : if( pFontData[nIndex] == '0' )
3406 0 : nFound++;
3407 0 : else if( nFound > 0 &&
3408 0 : pFontData[nIndex] != '\r' &&
3409 0 : pFontData[nIndex] != '\t' &&
3410 0 : pFontData[nIndex] != '\n' &&
3411 0 : pFontData[nIndex] != ' ' )
3412 0 : break;
3413 : }
3414 0 : nIndex--;
3415 : }
3416 :
3417 0 : if( nIndex < 1 || nIndex <= nEndAsciiIndex )
3418 0 : throw FontException();
3419 :
3420 : // nLength3 is the rest of the file - excluding any section headers
3421 : // nIndex now points before the first of the 512 '0' characters marking the
3422 : // fixed content portion
3423 0 : sal_Int32 nLength3 = nFontLen - nIndex - 1;
3424 0 : for( it = aSections.begin(); it != aSections.end(); ++it )
3425 : {
3426 : // special case: nIndex inside a section marker
3427 0 : if( nIndex >= (*it) && (*it)+6 > nIndex )
3428 0 : nLength3 -= (*it)+6 - nIndex;
3429 0 : else if( *it >= nIndex )
3430 : {
3431 0 : if( *it < nFontLen - 6 )
3432 0 : nLength3 -= 6;
3433 : else // the last section 0x8003 is only 2 bytes after all
3434 0 : nLength3 -= (nFontLen - *it);
3435 : }
3436 : }
3437 :
3438 : // there may be whitespace to ignore before the 512 '0'
3439 0 : while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' )
3440 : {
3441 0 : nIndex--;
3442 0 : for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3443 : ;
3444 0 : if( it != aSections.end() )
3445 : {
3446 0 : nIndex = (*it)-1;
3447 0 : break; // this is surely a binary boundary, in ascii case it wouldn't matter
3448 : }
3449 : }
3450 0 : nEndBinaryIndex = nIndex;
3451 :
3452 : // search for beginning of binary section
3453 0 : nBeginBinaryIndex = nEndAsciiIndex;
3454 0 : do
3455 : {
3456 0 : nBeginBinaryIndex++;
3457 0 : for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it )
3458 : ;
3459 : } while( nBeginBinaryIndex < nEndBinaryIndex &&
3460 0 : ( pFontData[nBeginBinaryIndex] == '\r' ||
3461 0 : pFontData[nBeginBinaryIndex] == '\n' ||
3462 0 : it != aSections.end() ) );
3463 :
3464 : // it seems to be vital to copy the exact whitespace between binary data
3465 : // and eexec, else a invalid font results. so make nEndAsciiIndex
3466 : // always immediate in front of nBeginBinaryIndex
3467 0 : nEndAsciiIndex = nBeginBinaryIndex-1;
3468 0 : for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it )
3469 : ;
3470 0 : if( it != aSections.end() )
3471 0 : nEndAsciiIndex = (*it)-1;
3472 :
3473 0 : nLength1 = nEndAsciiIndex+1; // including the last character
3474 0 : for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it )
3475 0 : nLength1 -= 6; // decrease by pfb section size
3476 :
3477 : // if the first four bytes are all ascii hex characters, then binary data
3478 : // has to be converted to real binary data
3479 0 : for( nIndex = 0; nIndex < 4 &&
3480 0 : ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) ||
3481 0 : ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) ||
3482 0 : ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' )
3483 : ); ++nIndex )
3484 : ;
3485 0 : bool bConvertHexData = true;
3486 0 : if( nIndex < 4 )
3487 : {
3488 0 : bConvertHexData = false;
3489 0 : nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte
3490 0 : for( it = aSections.begin(); it != aSections.end(); ++it )
3491 0 : if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex )
3492 0 : nLength2 -= 6;
3493 : }
3494 : else
3495 : {
3496 : // count the hex ascii characters to get nLength2
3497 0 : nLength2 = 0;
3498 0 : int nNextSectionIndex = 0;
3499 0 : for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3500 : ;
3501 0 : if( it != aSections.end() )
3502 0 : nNextSectionIndex = *it;
3503 0 : for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3504 : {
3505 0 : if( nIndex == nNextSectionIndex )
3506 : {
3507 0 : nIndex += 6;
3508 0 : ++it;
3509 0 : nNextSectionIndex = (it == aSections.end() ? 0 : *it );
3510 : }
3511 0 : if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) ||
3512 0 : ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) ||
3513 0 : ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) )
3514 0 : nLength2++;
3515 : }
3516 : DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" );
3517 0 : nLength2 /= 2;
3518 : }
3519 :
3520 : // now we can actually write the font stream !
3521 : #if OSL_DEBUG_LEVEL > 1
3522 : emitComment( " PDFWriterImpl::emitEmbeddedFont" );
3523 : #endif
3524 0 : OStringBuffer aLine( 512 );
3525 0 : nStreamObject = createObject();
3526 0 : if( !updateObject(nStreamObject))
3527 0 : throw FontException();
3528 0 : sal_Int32 nStreamLengthObject = createObject();
3529 0 : aLine.append( nStreamObject );
3530 : aLine.append( " 0 obj\n"
3531 0 : "<</Length " );
3532 0 : aLine.append( nStreamLengthObject );
3533 : aLine.append( " 0 R"
3534 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3535 : "/Filter/FlateDecode"
3536 : #endif
3537 0 : "/Length1 " );
3538 0 : aLine.append( nLength1 );
3539 0 : aLine.append( " /Length2 " );
3540 0 : aLine.append( nLength2 );
3541 0 : aLine.append( " /Length3 ");
3542 0 : aLine.append( nLength3 );
3543 : aLine.append( ">>\n"
3544 0 : "stream\n" );
3545 0 : if( !writeBuffer( aLine.getStr(), aLine.getLength() ) )
3546 0 : throw FontException();
3547 :
3548 0 : sal_uInt64 nBeginStreamPos = 0;
3549 0 : osl_getFilePos( m_aFile, &nBeginStreamPos );
3550 :
3551 0 : beginCompression();
3552 0 : checkAndEnableStreamEncryption( nStreamObject );
3553 :
3554 : // write ascii section
3555 0 : if( aSections.begin() == aSections.end() )
3556 : {
3557 0 : if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) )
3558 0 : throw FontException();
3559 : }
3560 : else
3561 : {
3562 : // first section always starts at 0
3563 0 : it = aSections.begin();
3564 0 : nIndex = (*it)+6;
3565 0 : ++it;
3566 0 : while( *it < nEndAsciiIndex )
3567 : {
3568 0 : if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) )
3569 0 : throw FontException();
3570 0 : nIndex = (*it)+6;
3571 0 : ++it;
3572 : }
3573 : // write partial last section
3574 0 : if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) )
3575 0 : throw FontException();
3576 : }
3577 :
3578 : // write binary section
3579 0 : if( ! bConvertHexData )
3580 : {
3581 0 : if( aSections.begin() == aSections.end() )
3582 : {
3583 0 : if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) )
3584 0 : throw FontException();
3585 : }
3586 : else
3587 : {
3588 0 : for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it )
3589 : ;
3590 : // write first partial section
3591 0 : if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) )
3592 0 : throw FontException();
3593 : // write following sections
3594 0 : while( it != aSections.end() )
3595 : {
3596 0 : nIndex = (*it)+6;
3597 0 : ++it;
3598 0 : if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3599 : {
3600 0 : sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3601 0 : if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3602 0 : throw FontException();
3603 : }
3604 : }
3605 : }
3606 : }
3607 : else
3608 : {
3609 0 : boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] );
3610 0 : memset( pWriteBuffer.get(), 0, nLength2 );
3611 0 : int nWriteIndex = 0;
3612 :
3613 0 : int nNextSectionIndex = 0;
3614 0 : for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3615 : ;
3616 0 : if( it != aSections.end() )
3617 0 : nNextSectionIndex = *it;
3618 0 : for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3619 : {
3620 0 : if( nIndex == nNextSectionIndex )
3621 : {
3622 0 : nIndex += 6;
3623 0 : ++it;
3624 0 : nNextSectionIndex = (it == aSections.end() ? nFontLen : *it );
3625 : }
3626 0 : unsigned char cNibble = 0x80;
3627 0 : if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' )
3628 0 : cNibble = pFontData[nIndex] - '0';
3629 0 : else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' )
3630 0 : cNibble = pFontData[nIndex] - 'a' + 10;
3631 0 : else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' )
3632 0 : cNibble = pFontData[nIndex] - 'A' + 10;
3633 0 : if( cNibble != 0x80 )
3634 : {
3635 0 : if( !(nWriteIndex & 1 ) )
3636 0 : cNibble <<= 4;
3637 0 : pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble;
3638 0 : nWriteIndex++;
3639 : }
3640 : }
3641 0 : if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) )
3642 0 : throw FontException();
3643 0 : if( aSections.empty() )
3644 : {
3645 0 : if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) )
3646 0 : throw FontException();
3647 : }
3648 : else
3649 : {
3650 : // write rest of this section
3651 0 : if( nIndex < nNextSectionIndex )
3652 : {
3653 0 : if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) )
3654 0 : throw FontException();
3655 : }
3656 : // write following sections
3657 0 : while( it != aSections.end() )
3658 : {
3659 0 : nIndex = (*it)+6;
3660 0 : ++it;
3661 0 : if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3662 : {
3663 0 : sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3664 0 : if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3665 0 : throw FontException();
3666 : }
3667 : }
3668 0 : }
3669 : }
3670 0 : endCompression();
3671 0 : disableStreamEncryption();
3672 :
3673 :
3674 0 : sal_uInt64 nEndStreamPos = 0;
3675 0 : osl_getFilePos( m_aFile, &nEndStreamPos );
3676 :
3677 : // and finally close the stream
3678 0 : aLine.setLength( 0 );
3679 0 : aLine.append( "\nendstream\nendobj\n\n" );
3680 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3681 0 : throw FontException();
3682 :
3683 : // write stream length object
3684 0 : aLine.setLength( 0 );
3685 0 : if( ! updateObject( nStreamLengthObject ) )
3686 0 : throw FontException();
3687 0 : aLine.append( nStreamLengthObject );
3688 0 : aLine.append( " 0 obj\n" );
3689 0 : aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) );
3690 0 : aLine.append( "\nendobj\n\n" );
3691 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3692 0 : throw FontException();
3693 : }
3694 : else
3695 : {
3696 0 : rtl::OStringBuffer aErrorComment( 256 );
3697 0 : aErrorComment.append( "GetEmbedFontData failed for font \"" );
3698 0 : aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3699 0 : aErrorComment.append( '\"' );
3700 0 : if( pFont->GetSlant() == ITALIC_NORMAL )
3701 0 : aErrorComment.append( " italic" );
3702 0 : else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3703 0 : aErrorComment.append( " oblique" );
3704 0 : aErrorComment.append( " weight=" );
3705 0 : aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3706 0 : emitComment( aErrorComment.getStr() );
3707 : }
3708 :
3709 0 : if( nStreamObject )
3710 : {
3711 : // write font descriptor
3712 0 : nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject );
3713 : }
3714 :
3715 0 : if( nFontDescriptor )
3716 : {
3717 0 : if( pEncoding )
3718 0 : nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, SAL_N_ELEMENTS(nEncoding) );
3719 :
3720 : // write font object
3721 0 : sal_Int32 nObject = createObject();
3722 0 : if( ! updateObject( nObject ) )
3723 0 : throw FontException();
3724 :
3725 0 : OStringBuffer aLine( 1024 );
3726 0 : aLine.append( nObject );
3727 : aLine.append( " 0 obj\n"
3728 0 : "<</Type/Font/Subtype/Type1/BaseFont/" );
3729 0 : appendName( aInfo.m_aPSName, aLine );
3730 0 : aLine.append( "\n" );
3731 0 : if( !pFont->mbSymbolFlag && pEncoding == 0 )
3732 0 : aLine.append( "/Encoding/WinAnsiEncoding\n" );
3733 0 : if( nToUnicodeStream )
3734 : {
3735 0 : aLine.append( "/ToUnicode " );
3736 0 : aLine.append( nToUnicodeStream );
3737 0 : aLine.append( " 0 R\n" );
3738 : }
3739 : aLine.append( "/FirstChar 0 /LastChar 255\n"
3740 0 : "/Widths[" );
3741 0 : for( int i = 0; i < 256; i++ )
3742 : {
3743 0 : aLine.append( pWidths[i] );
3744 0 : aLine.append( ((i&15) == 15) ? "\n" : " " );
3745 : }
3746 : aLine.append( "]\n"
3747 0 : "/FontDescriptor " );
3748 0 : aLine.append( nFontDescriptor );
3749 : aLine.append( " 0 R>>\n"
3750 0 : "endobj\n\n" );
3751 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3752 0 : throw FontException();
3753 :
3754 0 : aRet[ rEmbed.m_nNormalFontID ] = nObject;
3755 :
3756 : // write additional encodings
3757 0 : for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it )
3758 : {
3759 : sal_Int32 aEncWidths[ 256 ];
3760 : // emit encoding dict
3761 0 : sal_Int32 nEncObject = createObject();
3762 0 : if( ! updateObject( nEncObject ) )
3763 0 : throw FontException();
3764 :
3765 0 : OutputDevice* pRef = getReferenceDevice();
3766 0 : pRef->Push( PUSH_FONT | PUSH_MAPMODE );
3767 0 : pRef->SetMapMode( MapMode( MAP_PIXEL ) );
3768 0 : Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) );
3769 0 : aFont.SetWeight( pFont->GetWeight() );
3770 0 : aFont.SetItalic( pFont->GetSlant() );
3771 0 : aFont.SetPitch( pFont->GetPitch() );
3772 0 : pRef->SetFont( aFont );
3773 0 : pRef->ImplNewFont();
3774 :
3775 0 : aLine.setLength( 0 );
3776 0 : aLine.append( nEncObject );
3777 : aLine.append( " 0 obj\n"
3778 0 : "<</Type/Encoding/Differences[ 0\n" );
3779 0 : int nEncoded = 0;
3780 0 : aUnicodes.clear();
3781 0 : for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it )
3782 : {
3783 0 : rtl::OUString aStr( str_it->m_aUnicode );
3784 0 : aEncWidths[nEncoded] = pRef->GetTextWidth( aStr );
3785 0 : nEncodedCodes[nEncoded] = str_it->m_aUnicode;
3786 0 : nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded);
3787 0 : pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size());
3788 0 : aUnicodes.push_back( nEncodedCodes[nEncoded] );
3789 0 : pUnicodesPerGlyph[nEncoded] = 1;
3790 :
3791 0 : aLine.append( " /" );
3792 0 : aLine.append( str_it->m_aName );
3793 0 : if( !((++nEncoded) & 15) )
3794 0 : aLine.append( "\n" );
3795 0 : }
3796 : aLine.append( "]>>\n"
3797 0 : "endobj\n\n" );
3798 :
3799 0 : pRef->Pop();
3800 :
3801 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3802 0 : throw FontException();
3803 :
3804 0 : nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded );
3805 :
3806 0 : nObject = createObject();
3807 0 : if( ! updateObject( nObject ) )
3808 0 : throw FontException();
3809 :
3810 0 : aLine.setLength( 0 );
3811 0 : aLine.append( nObject );
3812 : aLine.append( " 0 obj\n"
3813 0 : "<</Type/Font/Subtype/Type1/BaseFont/" );
3814 0 : appendName( aInfo.m_aPSName, aLine );
3815 0 : aLine.append( "\n" );
3816 0 : aLine.append( "/Encoding " );
3817 0 : aLine.append( nEncObject );
3818 0 : aLine.append( " 0 R\n" );
3819 0 : if( nToUnicodeStream )
3820 : {
3821 0 : aLine.append( "/ToUnicode " );
3822 0 : aLine.append( nToUnicodeStream );
3823 0 : aLine.append( " 0 R\n" );
3824 : }
3825 : aLine.append( "/FirstChar 0\n"
3826 0 : "/LastChar " );
3827 0 : aLine.append( (sal_Int32)(nEncoded-1) );
3828 : aLine.append( "\n"
3829 0 : "/Widths[" );
3830 0 : for( int i = 0; i < nEncoded; i++ )
3831 : {
3832 0 : aLine.append( aEncWidths[i] );
3833 0 : aLine.append( ((i&15) == 15) ? "\n" : " " );
3834 : }
3835 : aLine.append( " ]\n"
3836 0 : "/FontDescriptor " );
3837 0 : aLine.append( nFontDescriptor );
3838 : aLine.append( " 0 R>>\n"
3839 0 : "endobj\n\n" );
3840 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3841 0 : throw FontException();
3842 :
3843 0 : aRet[ enc_it->m_nFontID ] = nObject;
3844 0 : }
3845 : }
3846 : }
3847 0 : catch( FontException& )
3848 : {
3849 : // these do nothing in case there was no compression or encryption ongoing
3850 0 : endCompression();
3851 0 : disableStreamEncryption();
3852 : }
3853 :
3854 0 : if( pFontData )
3855 0 : m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3856 :
3857 0 : return aRet;
3858 : }
3859 :
3860 0 : static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
3861 : {
3862 0 : if( nSubsetID )
3863 : {
3864 0 : for( int i = 0; i < 6; i++ )
3865 : {
3866 0 : int nOffset = (nSubsetID % 26);
3867 0 : nSubsetID /= 26;
3868 0 : rBuffer.append( (sal_Char)('A'+nOffset) );
3869 : }
3870 0 : rBuffer.append( '+' );
3871 : }
3872 0 : appendName( rPSName, rBuffer );
3873 0 : }
3874 :
3875 0 : sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding,
3876 : sal_Ucs* pUnicodes,
3877 : sal_Int32* pUnicodesPerGlyph,
3878 : sal_Int32* pEncToUnicodeIndex,
3879 : int nGlyphs )
3880 : {
3881 0 : int nMapped = 0, n = 0;
3882 0 : for( n = 0; n < nGlyphs; n++ )
3883 0 : if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3884 0 : nMapped++;
3885 :
3886 0 : if( nMapped == 0 )
3887 0 : return 0;
3888 :
3889 0 : sal_Int32 nStream = createObject();
3890 0 : CHECK_RETURN( updateObject( nStream ) );
3891 :
3892 0 : OStringBuffer aContents( 1024 );
3893 : aContents.append(
3894 : "/CIDInit/ProcSet findresource begin\n"
3895 : "12 dict begin\n"
3896 : "begincmap\n"
3897 : "/CIDSystemInfo<<\n"
3898 : "/Registry (Adobe)\n"
3899 : "/Ordering (UCS)\n"
3900 : "/Supplement 0\n"
3901 : ">> def\n"
3902 : "/CMapName/Adobe-Identity-UCS def\n"
3903 : "/CMapType 2 def\n"
3904 : "1 begincodespacerange\n"
3905 : "<00> <FF>\n"
3906 : "endcodespacerange\n"
3907 0 : );
3908 0 : int nCount = 0;
3909 0 : for( n = 0; n < nGlyphs; n++ )
3910 : {
3911 0 : if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3912 : {
3913 0 : if( (nCount % 100) == 0 )
3914 : {
3915 0 : if( nCount )
3916 0 : aContents.append( "endbfchar\n" );
3917 0 : aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) );
3918 0 : aContents.append( " beginbfchar\n" );
3919 : }
3920 0 : aContents.append( '<' );
3921 0 : appendHex( (sal_Int8)pEncoding[n], aContents );
3922 0 : aContents.append( "> <" );
3923 : // TODO: handle unicodes>U+FFFF
3924 0 : sal_Int32 nIndex = pEncToUnicodeIndex[n];
3925 0 : for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ )
3926 : {
3927 0 : appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents );
3928 0 : appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents );
3929 : }
3930 0 : aContents.append( ">\n" );
3931 0 : nCount++;
3932 : }
3933 : }
3934 : aContents.append( "endbfchar\n"
3935 : "endcmap\n"
3936 : "CMapName currentdict /CMap defineresource pop\n"
3937 : "end\n"
3938 0 : "end\n" );
3939 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3940 0 : ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
3941 0 : SvMemoryStream aStream;
3942 0 : pCodec->BeginCompression();
3943 0 : pCodec->Write( aStream, (const sal_uInt8*)aContents.getStr(), aContents.getLength() );
3944 0 : pCodec->EndCompression();
3945 0 : delete pCodec;
3946 : #endif
3947 :
3948 : #if OSL_DEBUG_LEVEL > 1
3949 : emitComment( "PDFWriterImpl::createToUnicodeCMap" );
3950 : #endif
3951 0 : OStringBuffer aLine( 40 );
3952 :
3953 0 : aLine.append( nStream );
3954 0 : aLine.append( " 0 obj\n<</Length " );
3955 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3956 0 : sal_Int32 nLen = (sal_Int32)aStream.Tell();
3957 0 : aStream.Seek( 0 );
3958 0 : aLine.append( nLen );
3959 0 : aLine.append( "/Filter/FlateDecode" );
3960 : #else
3961 : aLine.append( aContents.getLength() );
3962 : #endif
3963 0 : aLine.append( ">>\nstream\n" );
3964 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3965 0 : checkAndEnableStreamEncryption( nStream );
3966 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3967 0 : CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
3968 : #else
3969 : CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
3970 : #endif
3971 0 : disableStreamEncryption();
3972 0 : aLine.setLength( 0 );
3973 : aLine.append( "\nendstream\n"
3974 0 : "endobj\n\n" );
3975 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3976 0 : return nStream;
3977 : }
3978 :
3979 0 : sal_Int32 PDFWriterImpl::emitFontDescriptor( const PhysicalFontFace* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
3980 : {
3981 0 : OStringBuffer aLine( 1024 );
3982 : // get font flags, see PDF reference 1.4 p. 358
3983 : // possibly characters outside Adobe standard encoding
3984 : // so set Symbolic flag
3985 0 : sal_Int32 nFontFlags = (1<<2);
3986 0 : if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE )
3987 0 : nFontFlags |= (1 << 6);
3988 0 : if( pFont->GetPitch() == PITCH_FIXED )
3989 0 : nFontFlags |= 1;
3990 0 : if( pFont->GetFamilyType() == FAMILY_SCRIPT )
3991 0 : nFontFlags |= (1 << 3);
3992 0 : else if( pFont->GetFamilyType() == FAMILY_ROMAN )
3993 0 : nFontFlags |= (1 << 1);
3994 :
3995 0 : sal_Int32 nFontDescriptor = createObject();
3996 0 : CHECK_RETURN( updateObject( nFontDescriptor ) );
3997 0 : aLine.setLength( 0 );
3998 0 : aLine.append( nFontDescriptor );
3999 : aLine.append( " 0 obj\n"
4000 0 : "<</Type/FontDescriptor/FontName/" );
4001 0 : appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
4002 : aLine.append( "\n"
4003 0 : "/Flags " );
4004 0 : aLine.append( nFontFlags );
4005 : aLine.append( "\n"
4006 0 : "/FontBBox[" );
4007 : // note: Top and Bottom are reversed in VCL and PDF rectangles
4008 0 : aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() );
4009 0 : aLine.append( ' ' );
4010 0 : aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() );
4011 0 : aLine.append( ' ' );
4012 0 : aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() );
4013 0 : aLine.append( ' ' );
4014 0 : aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) );
4015 0 : aLine.append( "]/ItalicAngle " );
4016 0 : if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL )
4017 0 : aLine.append( "-30" );
4018 : else
4019 0 : aLine.append( "0" );
4020 : aLine.append( "\n"
4021 0 : "/Ascent " );
4022 0 : aLine.append( (sal_Int32)rInfo.m_nAscent );
4023 : aLine.append( "\n"
4024 0 : "/Descent " );
4025 0 : aLine.append( (sal_Int32)-rInfo.m_nDescent );
4026 : aLine.append( "\n"
4027 0 : "/CapHeight " );
4028 0 : aLine.append( (sal_Int32)rInfo.m_nCapHeight );
4029 : // According to PDF reference 1.4 StemV is required
4030 : // seems a tad strange to me, but well ...
4031 : aLine.append( "\n"
4032 0 : "/StemV 80\n" );
4033 0 : if( nFontStream )
4034 : {
4035 0 : aLine.append( "/FontFile" );
4036 0 : switch( rInfo.m_nFontType )
4037 : {
4038 : case FontSubsetInfo::SFNT_TTF:
4039 0 : aLine.append( '2' );
4040 0 : break;
4041 : case FontSubsetInfo::TYPE1_PFA:
4042 : case FontSubsetInfo::TYPE1_PFB:
4043 : case FontSubsetInfo::ANY_TYPE1:
4044 0 : break;
4045 : default:
4046 : OSL_FAIL( "unknown fonttype in PDF font descriptor" );
4047 0 : return 0;
4048 : }
4049 0 : aLine.append( ' ' );
4050 0 : aLine.append( nFontStream );
4051 0 : aLine.append( " 0 R\n" );
4052 : }
4053 : aLine.append( ">>\n"
4054 0 : "endobj\n\n" );
4055 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4056 :
4057 0 : return nFontDescriptor;
4058 : }
4059 :
4060 0 : void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const
4061 : {
4062 0 : for( std::map< sal_Int32, sal_Int32 >::const_iterator it =
4063 0 : m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it )
4064 : {
4065 0 : rDict.append( m_aBuiltinFonts[it->first].getNameObject() );
4066 0 : rDict.append( ' ' );
4067 0 : rDict.append( it->second );
4068 0 : rDict.append( " 0 R" );
4069 : }
4070 0 : }
4071 :
4072 0 : bool PDFWriterImpl::emitFonts()
4073 : {
4074 0 : if( ! m_pReferenceDevice->ImplGetGraphics() )
4075 0 : return false;
4076 :
4077 0 : OStringBuffer aLine( 1024 );
4078 :
4079 0 : std::map< sal_Int32, sal_Int32 > aFontIDToObject;
4080 :
4081 0 : OUString aTmpName;
4082 0 : osl_createTempFile( NULL, NULL, &aTmpName.pData );
4083 0 : for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it )
4084 : {
4085 0 : for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit )
4086 : {
4087 : sal_Int32 pGlyphIDs[ 256 ];
4088 : sal_Int32 pWidths[ 256 ];
4089 : sal_uInt8 pEncoding[ 256 ];
4090 : sal_Int32 pEncToUnicodeIndex[ 256 ];
4091 : sal_Int32 pUnicodesPerGlyph[ 256 ];
4092 0 : std::vector<sal_Ucs> aUnicodes;
4093 0 : aUnicodes.reserve( 256 );
4094 0 : int nGlyphs = 1;
4095 : // fill arrays and prepare encoding index map
4096 0 : sal_Int32 nToUnicodeStream = 0;
4097 :
4098 0 : memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) );
4099 0 : memset( pEncoding, 0, sizeof( pEncoding ) );
4100 0 : memset( pUnicodesPerGlyph, 0, sizeof( pUnicodesPerGlyph ) );
4101 0 : memset( pEncToUnicodeIndex, 0, sizeof( pEncToUnicodeIndex ) );
4102 0 : for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit )
4103 : {
4104 0 : sal_uInt8 nEnc = fit->second.getGlyphId();
4105 :
4106 : DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" );
4107 : DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" );
4108 :
4109 0 : pGlyphIDs[ nEnc ] = fit->first;
4110 0 : pEncoding[ nEnc ] = nEnc;
4111 0 : pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size());
4112 0 : pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes();
4113 0 : for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ )
4114 0 : aUnicodes.push_back( fit->second.getCode( n ) );
4115 0 : if( fit->second.getCode(0) )
4116 0 : nToUnicodeStream = 1;
4117 0 : if( nGlyphs < 256 )
4118 0 : nGlyphs++;
4119 : else
4120 : {
4121 : OSL_FAIL( "too many glyphs for subset" );
4122 : }
4123 : }
4124 0 : FontSubsetInfo aSubsetInfo;
4125 0 : if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
4126 : {
4127 : // create font stream
4128 : oslFileHandle aFontFile;
4129 0 : CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) );
4130 : // get file size
4131 : sal_uInt64 nLength1;
4132 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) );
4133 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength1 ) ) );
4134 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
4135 :
4136 : #if OSL_DEBUG_LEVEL > 1
4137 : emitComment( "PDFWriterImpl::emitFonts" );
4138 : #endif
4139 0 : sal_Int32 nFontStream = createObject();
4140 0 : sal_Int32 nStreamLengthObject = createObject();
4141 0 : CHECK_RETURN( updateObject( nFontStream ) );
4142 0 : aLine.setLength( 0 );
4143 0 : aLine.append( nFontStream );
4144 : aLine.append( " 0 obj\n"
4145 0 : "<</Length " );
4146 0 : aLine.append( (sal_Int32)nStreamLengthObject );
4147 : aLine.append( " 0 R"
4148 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
4149 : "/Filter/FlateDecode"
4150 : #endif
4151 0 : "/Length1 " );
4152 :
4153 0 : sal_uInt64 nStartPos = 0;
4154 0 : if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF )
4155 : {
4156 0 : aLine.append( (sal_Int32)nLength1 );
4157 :
4158 : aLine.append( ">>\n"
4159 0 : "stream\n" );
4160 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4161 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
4162 :
4163 : // copy font file
4164 0 : beginCompression();
4165 0 : checkAndEnableStreamEncryption( nFontStream );
4166 0 : sal_Bool bEOF = sal_False;
4167 0 : do
4168 : {
4169 : char buf[8192];
4170 : sal_uInt64 nRead;
4171 0 : CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) );
4172 0 : CHECK_RETURN( writeBuffer( buf, nRead ) );
4173 0 : CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) );
4174 0 : } while( ! bEOF );
4175 : }
4176 0 : else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 )
4177 : {
4178 : // TODO: implement
4179 : OSL_FAIL( "PDFWriterImpl does not support CFF-font subsets yet!" );
4180 : }
4181 0 : else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA?
4182 : {
4183 0 : boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] );
4184 :
4185 0 : sal_uInt64 nBytesRead = 0;
4186 0 : CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer.get(), nLength1, &nBytesRead ) ) );
4187 : DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" );
4188 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) );
4189 : // get the PFB-segment lengths
4190 0 : ThreeInts aSegmentLengths = {0,0,0};
4191 0 : getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths );
4192 : // the lengths below are mandatory for PDF-exported Type1 fonts
4193 : // because the PFB segment headers get stripped! WhyOhWhy.
4194 0 : aLine.append( (sal_Int32)aSegmentLengths[0] );
4195 0 : aLine.append( "/Length2 " );
4196 0 : aLine.append( (sal_Int32)aSegmentLengths[1] );
4197 0 : aLine.append( "/Length3 " );
4198 0 : aLine.append( (sal_Int32)aSegmentLengths[2] );
4199 :
4200 : aLine.append( ">>\n"
4201 0 : "stream\n" );
4202 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4203 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) );
4204 :
4205 : // emit PFB-sections without section headers
4206 0 : beginCompression();
4207 0 : checkAndEnableStreamEncryption( nFontStream );
4208 0 : CHECK_RETURN( writeBuffer( &pBuffer[6], aSegmentLengths[0] ) );
4209 0 : CHECK_RETURN( writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) );
4210 0 : CHECK_RETURN( writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) );
4211 : }
4212 : else
4213 : {
4214 0 : fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType);
4215 0 : aLine.append( "0 >>\nstream\n" );
4216 : }
4217 :
4218 0 : endCompression();
4219 0 : disableStreamEncryption();
4220 : // close the file
4221 0 : osl_closeFile( aFontFile );
4222 :
4223 0 : sal_uInt64 nEndPos = 0;
4224 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) );
4225 : // end the stream
4226 0 : aLine.setLength( 0 );
4227 0 : aLine.append( "\nendstream\nendobj\n\n" );
4228 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4229 :
4230 : // emit stream length object
4231 0 : CHECK_RETURN( updateObject( nStreamLengthObject ) );
4232 0 : aLine.setLength( 0 );
4233 0 : aLine.append( nStreamLengthObject );
4234 0 : aLine.append( " 0 obj\n" );
4235 0 : aLine.append( (sal_Int64)(nEndPos-nStartPos) );
4236 0 : aLine.append( "\nendobj\n\n" );
4237 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4238 :
4239 : // write font descriptor
4240 0 : sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream );
4241 :
4242 0 : if( nToUnicodeStream )
4243 0 : nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs );
4244 :
4245 0 : sal_Int32 nFontObject = createObject();
4246 0 : CHECK_RETURN( updateObject( nFontObject ) );
4247 0 : aLine.setLength( 0 );
4248 0 : aLine.append( nFontObject );
4249 :
4250 0 : aLine.append( " 0 obj\n" );
4251 : aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ?
4252 : "<</Type/Font/Subtype/Type1/BaseFont/" :
4253 0 : "<</Type/Font/Subtype/TrueType/BaseFont/" );
4254 0 : appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine );
4255 : aLine.append( "\n"
4256 : "/FirstChar 0\n"
4257 0 : "/LastChar " );
4258 0 : aLine.append( (sal_Int32)(nGlyphs-1) );
4259 : aLine.append( "\n"
4260 0 : "/Widths[" );
4261 0 : for( int i = 0; i < nGlyphs; i++ )
4262 : {
4263 0 : aLine.append( pWidths[ i ] );
4264 0 : aLine.append( ((i & 15) == 15) ? "\n" : " " );
4265 : }
4266 : aLine.append( "]\n"
4267 0 : "/FontDescriptor " );
4268 0 : aLine.append( nFontDescriptor );
4269 0 : aLine.append( " 0 R\n" );
4270 0 : if( nToUnicodeStream )
4271 : {
4272 0 : aLine.append( "/ToUnicode " );
4273 0 : aLine.append( nToUnicodeStream );
4274 0 : aLine.append( " 0 R\n" );
4275 : }
4276 : aLine.append( ">>\n"
4277 0 : "endobj\n\n" );
4278 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4279 :
4280 0 : aFontIDToObject[ lit->m_nFontID ] = nFontObject;
4281 : }
4282 : else
4283 : {
4284 0 : const PhysicalFontFace* pFont = it->first;
4285 0 : rtl::OStringBuffer aErrorComment( 256 );
4286 0 : aErrorComment.append( "CreateFontSubset failed for font \"" );
4287 0 : aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
4288 0 : aErrorComment.append( '\"' );
4289 0 : if( pFont->GetSlant() == ITALIC_NORMAL )
4290 0 : aErrorComment.append( " italic" );
4291 0 : else if( pFont->GetSlant() == ITALIC_OBLIQUE )
4292 0 : aErrorComment.append( " oblique" );
4293 0 : aErrorComment.append( " weight=" );
4294 0 : aErrorComment.append( sal_Int32(pFont->GetWeight()) );
4295 0 : emitComment( aErrorComment.getStr() );
4296 : }
4297 0 : }
4298 : }
4299 0 : osl_removeFile( aTmpName.pData );
4300 :
4301 : // emit embedded fonts
4302 0 : for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit )
4303 : {
4304 0 : std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second );
4305 0 : for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4306 : {
4307 0 : CHECK_RETURN( fit->second );
4308 0 : aFontIDToObject[ fit->first ] = fit->second;
4309 : }
4310 0 : }
4311 :
4312 : // emit system fonts
4313 0 : for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit )
4314 : {
4315 0 : std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second );
4316 0 : for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4317 : {
4318 0 : CHECK_RETURN( fit->second );
4319 0 : aFontIDToObject[ fit->first ] = fit->second;
4320 : }
4321 0 : }
4322 :
4323 0 : OStringBuffer aFontDict( 1024 );
4324 0 : aFontDict.append( getFontDictObject() );
4325 : aFontDict.append( " 0 obj\n"
4326 0 : "<<" );
4327 0 : int ni = 0;
4328 0 : for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit )
4329 : {
4330 0 : aFontDict.append( "/F" );
4331 0 : aFontDict.append( mit->first );
4332 0 : aFontDict.append( ' ' );
4333 0 : aFontDict.append( mit->second );
4334 0 : aFontDict.append( " 0 R" );
4335 0 : if( ((++ni) & 7) == 0 )
4336 0 : aFontDict.append( '\n' );
4337 : }
4338 : // emit builtin font for widget apperances / variable text
4339 0 : for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin();
4340 0 : it != m_aBuiltinFontToObjectMap.end(); ++it )
4341 : {
4342 0 : ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]);
4343 0 : it->second = emitBuiltinFont( &aData, it->second );
4344 0 : }
4345 0 : appendBuiltinFontsToDict( aFontDict );
4346 0 : aFontDict.append( "\n>>\nendobj\n\n" );
4347 :
4348 0 : CHECK_RETURN( updateObject( getFontDictObject() ) );
4349 0 : CHECK_RETURN( writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) );
4350 0 : return true;
4351 : }
4352 :
4353 0 : sal_Int32 PDFWriterImpl::emitResources()
4354 : {
4355 : // emit shadings
4356 0 : if( ! m_aGradients.empty() )
4357 0 : CHECK_RETURN( emitGradients() );
4358 : // emit tilings
4359 0 : if( ! m_aTilings.empty() )
4360 0 : CHECK_RETURN( emitTilings() );
4361 :
4362 : // emit font dict
4363 0 : CHECK_RETURN( emitFonts() );
4364 :
4365 : // emit Resource dict
4366 0 : OStringBuffer aLine( 512 );
4367 0 : sal_Int32 nResourceDict = getResourceDictObj();
4368 0 : CHECK_RETURN( updateObject( nResourceDict ) );
4369 0 : aLine.setLength( 0 );
4370 0 : aLine.append( nResourceDict );
4371 0 : aLine.append( " 0 obj\n" );
4372 0 : m_aGlobalResourceDict.append( aLine, getFontDictObject() );
4373 0 : aLine.append( "endobj\n\n" );
4374 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4375 0 : return nResourceDict;
4376 : }
4377 :
4378 0 : sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
4379 : sal_Int32 nItemLevel,
4380 : sal_Int32 nCurrentItemId )
4381 : {
4382 : /* The /Count number of an item is
4383 : positive: the number of visible subitems
4384 : negative: the negative number of subitems that will become visible if
4385 : the item gets opened
4386 : see PDF ref 1.4 p 478
4387 : */
4388 :
4389 0 : sal_Int32 nCount = 0;
4390 :
4391 0 : if( m_aContext.OpenBookmarkLevels < 0 || // all levels arevisible
4392 : m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible
4393 : )
4394 : {
4395 0 : PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4396 0 : sal_Int32 nChildren = rItem.m_aChildren.size();
4397 0 : for( sal_Int32 i = 0; i < nChildren; i++ )
4398 0 : nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4399 0 : rCounts[nCurrentItemId] = nCount;
4400 : // return 1 (this item) + visible sub items
4401 0 : if( nCount < 0 )
4402 0 : nCount = 0;
4403 0 : nCount++;
4404 : }
4405 : else
4406 : {
4407 : // this bookmark level is invisible
4408 0 : PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4409 0 : sal_Int32 nChildren = rItem.m_aChildren.size();
4410 0 : rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
4411 0 : for( sal_Int32 i = 0; i < nChildren; i++ )
4412 0 : updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4413 0 : nCount = -1;
4414 : }
4415 :
4416 0 : return nCount;
4417 : }
4418 :
4419 0 : sal_Int32 PDFWriterImpl::emitOutline()
4420 : {
4421 0 : int i, nItems = m_aOutline.size();
4422 :
4423 : // do we have an outline at all ?
4424 0 : if( nItems < 2 )
4425 0 : return 0;
4426 :
4427 : // reserve object numbers for all outline items
4428 0 : for( i = 0; i < nItems; ++i )
4429 0 : m_aOutline[i].m_nObject = createObject();
4430 :
4431 : // update all parent, next and prev object ids
4432 0 : for( i = 0; i < nItems; ++i )
4433 : {
4434 0 : PDFOutlineEntry& rItem = m_aOutline[i];
4435 0 : int nChildren = rItem.m_aChildren.size();
4436 :
4437 0 : if( nChildren )
4438 : {
4439 0 : for( int n = 0; n < nChildren; ++n )
4440 : {
4441 0 : PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
4442 :
4443 0 : rChild.m_nParentObject = rItem.m_nObject;
4444 0 : rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
4445 0 : rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
4446 : }
4447 :
4448 : }
4449 : }
4450 :
4451 : // calculate Count entries for all items
4452 0 : std::vector< sal_Int32 > aCounts( nItems );
4453 0 : updateOutlineItemCount( aCounts, 0, 0 );
4454 :
4455 : // emit hierarchy
4456 0 : for( i = 0; i < nItems; ++i )
4457 : {
4458 0 : PDFOutlineEntry& rItem = m_aOutline[i];
4459 0 : OStringBuffer aLine( 1024 );
4460 :
4461 0 : CHECK_RETURN( updateObject( rItem.m_nObject ) );
4462 0 : aLine.append( rItem.m_nObject );
4463 0 : aLine.append( " 0 obj\n" );
4464 0 : aLine.append( "<<" );
4465 : // number of visible children (all levels)
4466 0 : if( i > 0 || aCounts[0] > 0 )
4467 : {
4468 0 : aLine.append( "/Count " );
4469 0 : aLine.append( aCounts[i] );
4470 : }
4471 0 : if( ! rItem.m_aChildren.empty() )
4472 : {
4473 : // children list: First, Last
4474 0 : aLine.append( "/First " );
4475 0 : aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
4476 0 : aLine.append( " 0 R/Last " );
4477 0 : aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
4478 0 : aLine.append( " 0 R\n" );
4479 : }
4480 0 : if( i > 0 )
4481 : {
4482 : // Title, Dest, Parent, Prev, Next
4483 0 : aLine.append( "/Title" );
4484 0 : appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
4485 0 : aLine.append( "\n" );
4486 : // Dest is not required
4487 0 : if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() )
4488 : {
4489 0 : aLine.append( "/Dest" );
4490 0 : appendDest( rItem.m_nDestID, aLine );
4491 : }
4492 0 : aLine.append( "/Parent " );
4493 0 : aLine.append( rItem.m_nParentObject );
4494 0 : aLine.append( " 0 R" );
4495 0 : if( rItem.m_nPrevObject )
4496 : {
4497 0 : aLine.append( "/Prev " );
4498 0 : aLine.append( rItem.m_nPrevObject );
4499 0 : aLine.append( " 0 R" );
4500 : }
4501 0 : if( rItem.m_nNextObject )
4502 : {
4503 0 : aLine.append( "/Next " );
4504 0 : aLine.append( rItem.m_nNextObject );
4505 0 : aLine.append( " 0 R" );
4506 : }
4507 : }
4508 0 : aLine.append( ">>\nendobj\n\n" );
4509 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4510 0 : }
4511 :
4512 0 : return m_aOutline[0].m_nObject;
4513 : }
4514 :
4515 : #undef CHECK_RETURN
4516 : #define CHECK_RETURN( x ) if( !x ) return false
4517 :
4518 0 : bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
4519 : {
4520 0 : if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() )
4521 : {
4522 : #if OSL_DEBUG_LEVEL > 1
4523 : fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID );
4524 : #endif
4525 0 : return false;
4526 : }
4527 :
4528 :
4529 0 : const PDFDest& rDest = m_aDests[ nDestID ];
4530 0 : const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
4531 :
4532 0 : rBuffer.append( '[' );
4533 0 : rBuffer.append( rDestPage.m_nPageObject );
4534 0 : rBuffer.append( " 0 R" );
4535 :
4536 0 : switch( rDest.m_eType )
4537 : {
4538 : case PDFWriter::XYZ:
4539 : default:
4540 0 : rBuffer.append( "/XYZ " );
4541 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4542 0 : rBuffer.append( ' ' );
4543 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4544 0 : rBuffer.append( " 0" );
4545 0 : break;
4546 : case PDFWriter::Fit:
4547 0 : rBuffer.append( "/Fit" );
4548 0 : break;
4549 : case PDFWriter::FitRectangle:
4550 0 : rBuffer.append( "/FitR " );
4551 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4552 0 : rBuffer.append( ' ' );
4553 0 : appendFixedInt( rDest.m_aRect.Top(), rBuffer );
4554 0 : rBuffer.append( ' ' );
4555 0 : appendFixedInt( rDest.m_aRect.Right(), rBuffer );
4556 0 : rBuffer.append( ' ' );
4557 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4558 0 : break;
4559 : case PDFWriter::FitHorizontal:
4560 0 : rBuffer.append( "/FitH " );
4561 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4562 0 : break;
4563 : case PDFWriter::FitVertical:
4564 0 : rBuffer.append( "/FitV " );
4565 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4566 0 : break;
4567 : case PDFWriter::FitPageBoundingBox:
4568 0 : rBuffer.append( "/FitB" );
4569 0 : break;
4570 : case PDFWriter::FitPageBoundingBoxHorizontal:
4571 0 : rBuffer.append( "/FitBH " );
4572 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4573 0 : break;
4574 : case PDFWriter::FitPageBoundingBoxVertical:
4575 0 : rBuffer.append( "/FitBV " );
4576 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4577 0 : break;
4578 : }
4579 0 : rBuffer.append( ']' );
4580 :
4581 0 : return true;
4582 : }
4583 :
4584 0 : bool PDFWriterImpl::emitLinkAnnotations()
4585 : {
4586 0 : int nAnnots = m_aLinks.size();
4587 0 : for( int i = 0; i < nAnnots; i++ )
4588 : {
4589 0 : const PDFLink& rLink = m_aLinks[i];
4590 0 : if( ! updateObject( rLink.m_nObject ) )
4591 0 : continue;
4592 :
4593 0 : OStringBuffer aLine( 1024 );
4594 0 : aLine.append( rLink.m_nObject );
4595 0 : aLine.append( " 0 obj\n" );
4596 : //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4597 : // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4598 0 : aLine.append( "<</Type/Annot" );
4599 0 : if( m_bIsPDF_A1 )
4600 0 : aLine.append( "/F 4" );
4601 0 : aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
4602 :
4603 0 : appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
4604 0 : aLine.append( ' ' );
4605 0 : appendFixedInt( rLink.m_aRect.Top(), aLine );
4606 0 : aLine.append( ' ' );
4607 0 : appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
4608 0 : aLine.append( ' ' );
4609 0 : appendFixedInt( rLink.m_aRect.Bottom(), aLine );
4610 0 : aLine.append( "]" );
4611 0 : if( rLink.m_nDest >= 0 )
4612 : {
4613 0 : aLine.append( "/Dest" );
4614 0 : appendDest( rLink.m_nDest, aLine );
4615 : }
4616 : else
4617 : {
4618 : /*--->i56629
4619 : destination is external to the document, so
4620 : we check in the following sequence:
4621 :
4622 : if target type is neither .pdf, nor .od[tpgs], then
4623 : check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
4624 : end processing
4625 : else if target is .od[tpgs]: then
4626 : if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file
4627 : processing continue
4628 :
4629 : if (new)target is .pdf : then
4630 : if GotToR is requested, then
4631 : convert the target in GoToR where the fragment of the URI is
4632 : considered the named destination in the target file, set relative or absolute as requested
4633 : else strip the fragment from URL and then set URI or 'launch application' as requested
4634 : */
4635 : //
4636 : // FIXME: check if the decode mechanisms for URL processing throughout this implementation
4637 : // are the correct one!!
4638 : //
4639 : // extract target file type
4640 0 : INetURLObject aDocumentURL( m_aContext.BaseURL );
4641 0 : INetURLObject aTargetURL( rLink.m_aURL );
4642 0 : sal_Int32 nSetGoToRMode = 0;
4643 0 : sal_Bool bTargetHasPDFExtension = sal_False;
4644 0 : INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
4645 0 : sal_Bool bIsUNCPath = sal_False;
4646 : // check if the protocol is a known one, or if there is no protocol at all (on target only)
4647 : // if there is no protocol, make the target relative to the current document directory
4648 : // getting the needed URL information from the current document path
4649 0 : if( eTargetProtocol == INET_PROT_NOT_VALID )
4650 : {
4651 0 : if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.compareToAscii( "\\\\\\\\", 4 ) == 0)
4652 : {
4653 0 : bIsUNCPath = sal_True;
4654 : }
4655 : else
4656 : {
4657 0 : INetURLObject aNewBase( aDocumentURL );//duplicate document URL
4658 0 : aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
4659 : //target document
4660 0 : aNewBase.insertName( rLink.m_aURL );
4661 0 : aTargetURL = aNewBase;//reassign the new target URL
4662 : //recompute the target protocol, with the new URL
4663 : //normal URL processing resumes
4664 0 : eTargetProtocol = aTargetURL.GetProtocol();
4665 : }
4666 : }
4667 :
4668 0 : rtl::OUString aFileExtension = aTargetURL.GetFileExtension();
4669 :
4670 : // Check if the URL ends in '/': if yes it's a directory,
4671 : // it will be forced to a URI link.
4672 : // possibly a malformed URI, leave it as it is, force as URI
4673 0 : if( aTargetURL.hasFinalSlash() )
4674 0 : m_aContext.DefaultLinkAction = PDFWriter::URIAction;
4675 :
4676 0 : if( !aFileExtension.isEmpty() )
4677 : {
4678 0 : if( m_aContext.ConvertOOoTargetToPDFTarget )
4679 : {
4680 0 : sal_Int32 bChangeFileExtensionToPDF = false;
4681 : //examine the file type (.odm .odt. .odp, odg, ods)
4682 0 : if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( "odm" ) ) )
4683 0 : bChangeFileExtensionToPDF = true;
4684 0 : if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( "odt" ) ) )
4685 0 : bChangeFileExtensionToPDF = true;
4686 0 : else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( "odp" ) ) )
4687 0 : bChangeFileExtensionToPDF = true;
4688 0 : else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( "odg" ) ) )
4689 0 : bChangeFileExtensionToPDF = true;
4690 0 : else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( "ods" ) ) )
4691 0 : bChangeFileExtensionToPDF = true;
4692 0 : if( bChangeFileExtensionToPDF )
4693 0 : aTargetURL.setExtension(rtl::OUString( "pdf" ) );
4694 : }
4695 : //check if extension is pdf, see if GoToR should be forced
4696 0 : bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase(rtl::OUString( "pdf" ) );
4697 0 : if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
4698 0 : nSetGoToRMode++;
4699 : }
4700 : //prepare the URL, if relative or not
4701 0 : INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
4702 : //queue the string common to all types of actions
4703 0 : aLine.append( "/A<</Type/Action/S");
4704 0 : if( bIsUNCPath ) // handle Win UNC paths
4705 : {
4706 0 : aLine.append( "/Launch/Win<</F" );
4707 : // INetURLObject is not good with UNC paths, use original path
4708 0 : appendLiteralStringEncrypt( rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4709 0 : aLine.append( ">>" );
4710 : }
4711 : else
4712 : {
4713 0 : bool bSetRelative = false;
4714 0 : bool bFileSpec = false;
4715 : //check if relative file link is requested and if the protocol is 'file://'
4716 0 : if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE )
4717 0 : bSetRelative = true;
4718 :
4719 0 : rtl::OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is,
4720 0 : if( nSetGoToRMode == 0 )
4721 : {
4722 0 : switch( m_aContext.DefaultLinkAction )
4723 : {
4724 : default:
4725 : case PDFWriter::URIAction :
4726 : case PDFWriter::URIActionDestination :
4727 0 : aLine.append( "/URI/URI" );
4728 0 : break;
4729 : case PDFWriter::LaunchAction:
4730 : // now:
4731 : // if a launch action is requested and the hyperlink target has a fragment
4732 : // and the target file does not have a pdf extension, or it's not a 'file:://' protocol
4733 : // then force the uri action on it
4734 : // This code will permit the correct opening of application on web pages, the one that
4735 : // normally have fragments (but I may be wrong...)
4736 : // and will force the use of URI when the protocol is not file://
4737 0 : if( (!aFragment.isEmpty() && !bTargetHasPDFExtension) ||
4738 : eTargetProtocol != INET_PROT_FILE )
4739 : {
4740 0 : aLine.append( "/URI/URI" );
4741 : }
4742 : else
4743 : {
4744 0 : aLine.append( "/Launch/F" );
4745 0 : bFileSpec = true;
4746 : }
4747 0 : break;
4748 : }
4749 : }
4750 : //fragment are encoded in the same way as in the named destination processing
4751 0 : if( nSetGoToRMode )
4752 : {
4753 : //add the fragment
4754 0 : rtl::OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET );
4755 0 : aLine.append("/GoToR");
4756 0 : aLine.append("/F");
4757 0 : bFileSpec = true;
4758 : appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark,
4759 : INetURLObject::WAS_ENCODED,
4760 : INetURLObject::DECODE_WITH_CHARSET ) :
4761 0 : aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4762 0 : if( !aFragment.isEmpty() )
4763 : {
4764 0 : aLine.append("/D/");
4765 0 : appendDestinationName( aFragment , aLine );
4766 0 : }
4767 : }
4768 : else
4769 : {
4770 : // change the fragment to accomodate the bookmark (only if the file extension is PDF and
4771 : // the requested action is of the correct type)
4772 0 : if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination &&
4773 0 : bTargetHasPDFExtension && !aFragment.isEmpty() )
4774 : {
4775 0 : OStringBuffer aLineLoc( 1024 );
4776 0 : appendDestinationName( aFragment , aLineLoc );
4777 : //substitute the fragment
4778 0 : aTargetURL.SetMark( aLineLoc.getStr() );
4779 : }
4780 0 : rtl::OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE );
4781 : appendLiteralStringEncrypt(bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL,
4782 : INetURLObject::WAS_ENCODED,
4783 : bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE
4784 : ) :
4785 0 : aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4786 0 : }
4787 : //<--- i56629
4788 : }
4789 0 : aLine.append( ">>\n" );
4790 : }
4791 0 : if( rLink.m_nStructParent > 0 )
4792 : {
4793 0 : aLine.append( "/StructParent " );
4794 0 : aLine.append( rLink.m_nStructParent );
4795 : }
4796 0 : aLine.append( ">>\nendobj\n\n" );
4797 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4798 0 : }
4799 :
4800 0 : return true;
4801 : }
4802 :
4803 0 : bool PDFWriterImpl::emitNoteAnnotations()
4804 : {
4805 : // emit note annotations
4806 0 : int nAnnots = m_aNotes.size();
4807 0 : for( int i = 0; i < nAnnots; i++ )
4808 : {
4809 0 : const PDFNoteEntry& rNote = m_aNotes[i];
4810 0 : if( ! updateObject( rNote.m_nObject ) )
4811 0 : return false;
4812 :
4813 0 : OStringBuffer aLine( 1024 );
4814 0 : aLine.append( rNote.m_nObject );
4815 0 : aLine.append( " 0 obj\n" );
4816 : //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4817 : // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4818 0 : aLine.append( "<</Type/Annot" );
4819 0 : if( m_bIsPDF_A1 )
4820 0 : aLine.append( "/F 4" );
4821 0 : aLine.append( "/Subtype/Text/Rect[" );
4822 :
4823 0 : appendFixedInt( rNote.m_aRect.Left(), aLine );
4824 0 : aLine.append( ' ' );
4825 0 : appendFixedInt( rNote.m_aRect.Top(), aLine );
4826 0 : aLine.append( ' ' );
4827 0 : appendFixedInt( rNote.m_aRect.Right(), aLine );
4828 0 : aLine.append( ' ' );
4829 0 : appendFixedInt( rNote.m_aRect.Bottom(), aLine );
4830 0 : aLine.append( "]" );
4831 :
4832 : // contents of the note (type text string)
4833 0 : aLine.append( "/Contents\n" );
4834 0 : appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
4835 0 : aLine.append( "\n" );
4836 :
4837 : // optional title
4838 0 : if( rNote.m_aContents.Title.Len() )
4839 : {
4840 0 : aLine.append( "/T" );
4841 0 : appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
4842 0 : aLine.append( "\n" );
4843 : }
4844 :
4845 0 : aLine.append( ">>\nendobj\n\n" );
4846 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4847 0 : }
4848 0 : return true;
4849 : }
4850 :
4851 0 : Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font& rAppSetFont )
4852 : {
4853 0 : bool bAdjustSize = false;
4854 :
4855 0 : Font aFont( rControlFont );
4856 0 : if( ! aFont.GetName().Len() )
4857 : {
4858 0 : aFont = rAppSetFont;
4859 0 : if( rControlFont.GetHeight() )
4860 0 : aFont.SetSize( Size( 0, rControlFont.GetHeight() ) );
4861 : else
4862 0 : bAdjustSize = true;
4863 0 : if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
4864 0 : aFont.SetItalic( rControlFont.GetItalic() );
4865 0 : if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
4866 0 : aFont.SetWeight( rControlFont.GetWeight() );
4867 : }
4868 0 : else if( ! aFont.GetHeight() )
4869 : {
4870 0 : aFont.SetSize( rAppSetFont.GetSize() );
4871 0 : bAdjustSize = true;
4872 : }
4873 0 : if( bAdjustSize )
4874 : {
4875 0 : Size aFontSize = aFont.GetSize();
4876 0 : OutputDevice* pDefDev = Application::GetDefaultDevice();
4877 0 : aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
4878 0 : aFont.SetSize( aFontSize );
4879 : }
4880 0 : return aFont;
4881 : }
4882 :
4883 0 : sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont )
4884 : {
4885 0 : sal_Int32 nBest = 4; // default to Helvetica
4886 0 : OUString aFontName( rFont.GetName() );
4887 0 : aFontName = aFontName.toAsciiLowerCase();
4888 :
4889 0 : if( aFontName.indexOf( "times" ) != -1 )
4890 0 : nBest = 8;
4891 0 : else if( aFontName.indexOf( "courier" ) != -1 )
4892 0 : nBest = 0;
4893 0 : else if( aFontName.indexOf( "dingbats" ) != -1 )
4894 0 : nBest = 13;
4895 0 : else if( aFontName.indexOf( "symbol" ) != -1 )
4896 0 : nBest = 12;
4897 0 : if( nBest < 12 )
4898 : {
4899 0 : if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
4900 0 : nBest += 1;
4901 0 : if( rFont.GetWeight() > WEIGHT_MEDIUM )
4902 0 : nBest += 2;
4903 : }
4904 :
4905 0 : if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() )
4906 0 : m_aBuiltinFontToObjectMap[ nBest ] = createObject();
4907 :
4908 0 : return nBest;
4909 : }
4910 :
4911 0 : static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
4912 : {
4913 0 : return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1;
4914 : }
4915 :
4916 0 : void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget )
4917 : {
4918 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4919 :
4920 : // save graphics state
4921 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
4922 :
4923 : // transform relative to control's coordinates since an
4924 : // appearance stream is a form XObject
4925 : // this relies on the m_aRect member of rButton NOT already being transformed
4926 : // to default user space
4927 0 : if( rWidget.Background || rWidget.Border )
4928 : {
4929 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) );
4930 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) );
4931 0 : drawRectangle( rWidget.Location );
4932 : }
4933 : // prepare font to use
4934 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
4935 0 : setFont( aFont );
4936 0 : setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
4937 :
4938 0 : drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
4939 :
4940 : // create DA string while local mapmode is still in place
4941 : // (that is before endRedirect())
4942 0 : OStringBuffer aDA( 256 );
4943 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
4944 0 : Font aDummyFont( String( RTL_CONSTASCII_USTRINGPARAM( "Helvetica" ) ), aFont.GetSize() );
4945 0 : sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont );
4946 0 : aDA.append( ' ' );
4947 0 : aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() );
4948 0 : aDA.append( ' ' );
4949 0 : m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4950 0 : aDA.append( " Tf" );
4951 0 : rButton.m_aDAString = aDA.makeStringAndClear();
4952 :
4953 0 : pop();
4954 :
4955 0 : rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
4956 :
4957 : /* seems like a bad hack but at least works in both AR5 and 6:
4958 : we draw the button ourselves and tell AR
4959 : the button would be totally transparent with no text
4960 :
4961 : One would expect that simply setting a normal appearance
4962 : should suffice, but no, as soon as the user actually presses
4963 : the button and an action is tied to it (gasp! a button that
4964 : does something) the appearance gets replaced by some crap that AR
4965 : creates on the fly even if no DA or MK is given. On AR6 at least
4966 : the DA and MK work as expected, but on AR5 this creates a region
4967 : filled with the background color but nor text. Urgh.
4968 : */
4969 0 : rButton.m_aMKDict = "/BC [] /BG [] /CA";
4970 0 : rButton.m_aMKDictCAString = "";
4971 0 : }
4972 :
4973 0 : Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern,
4974 : const PDFWriter::AnyWidget& rWidget,
4975 : const StyleSettings& rSettings )
4976 : {
4977 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
4978 :
4979 0 : if( rWidget.Background || rWidget.Border )
4980 : {
4981 0 : if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) )
4982 : {
4983 0 : sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500;
4984 0 : if( nDelta < 1 )
4985 0 : nDelta = 1;
4986 0 : setLineColor( Color( COL_TRANSPARENT ) );
4987 0 : Rectangle aRect = rIntern.m_aRect;
4988 0 : setFillColor( rSettings.GetLightBorderColor() );
4989 0 : drawRectangle( aRect );
4990 0 : aRect.Left() += nDelta; aRect.Top() += nDelta;
4991 0 : aRect.Right() -= nDelta; aRect.Bottom() -= nDelta;
4992 0 : setFillColor( rSettings.GetFieldColor() );
4993 0 : drawRectangle( aRect );
4994 0 : setFillColor( rSettings.GetLightColor() );
4995 0 : drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
4996 0 : drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
4997 0 : setFillColor( rSettings.GetDarkShadowColor() );
4998 0 : drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
4999 0 : drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
5000 : }
5001 : else
5002 : {
5003 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) );
5004 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5005 0 : drawRectangle( rIntern.m_aRect );
5006 : }
5007 :
5008 0 : if( rWidget.Border )
5009 : {
5010 : // adjust edit area accounting for border
5011 0 : sal_Int32 nDelta = aFont.GetHeight()/4;
5012 0 : if( nDelta < 1 )
5013 0 : nDelta = 1;
5014 0 : rIntern.m_aRect.Left() += nDelta;
5015 0 : rIntern.m_aRect.Top() += nDelta;
5016 0 : rIntern.m_aRect.Right() -= nDelta;
5017 0 : rIntern.m_aRect.Bottom()-= nDelta;
5018 : }
5019 : }
5020 0 : return aFont;
5021 : }
5022 :
5023 0 : void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget )
5024 : {
5025 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5026 0 : SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
5027 :
5028 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
5029 :
5030 : // prepare font to use, draw field border
5031 0 : Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
5032 0 : sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
5033 :
5034 : // prepare DA string
5035 0 : OStringBuffer aDA( 32 );
5036 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
5037 0 : aDA.append( ' ' );
5038 0 : if( m_aContext.FieldsUseSystemFonts )
5039 : {
5040 0 : aDA.append( "/F" );
5041 0 : aDA.append( nBest );
5042 :
5043 0 : OStringBuffer aDR( 32 );
5044 0 : aDR.append( "/Font " );
5045 0 : aDR.append( getFontDictObject() );
5046 0 : aDR.append( " 0 R" );
5047 0 : rEdit.m_aDRDict = aDR.makeStringAndClear();
5048 : }
5049 : else
5050 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5051 0 : aDA.append( ' ' );
5052 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
5053 0 : aDA.append( " Tf" );
5054 :
5055 : /* create an empty appearance stream, let the viewer create
5056 : the appearance at runtime. This is because AR5 seems to
5057 : paint the widget appearance always, and a dynamically created
5058 : appearance on top of it. AR6 is well behaved in that regard, so
5059 : that behaviour seems to be a bug. Anyway this empty appearance
5060 : relies on /NeedAppearances in the AcroForm dictionary set to "true"
5061 : */
5062 0 : beginRedirect( pEditStream, rEdit.m_aRect );
5063 0 : OStringBuffer aAppearance( 32 );
5064 0 : aAppearance.append( "/Tx BMC\nEMC\n" );
5065 0 : writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5066 :
5067 0 : endRedirect();
5068 0 : pop();
5069 :
5070 0 : rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
5071 :
5072 0 : rEdit.m_aDAString = aDA.makeStringAndClear();
5073 0 : }
5074 :
5075 0 : void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget )
5076 : {
5077 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5078 0 : SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
5079 :
5080 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
5081 :
5082 : // prepare font to use, draw field border
5083 0 : Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
5084 0 : sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
5085 :
5086 0 : beginRedirect( pListBoxStream, rBox.m_aRect );
5087 0 : OStringBuffer aAppearance( 64 );
5088 :
5089 0 : setLineColor( Color( COL_TRANSPARENT ) );
5090 0 : setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
5091 0 : drawRectangle( rBox.m_aRect );
5092 :
5093 : // empty appearance, see createDefaultEditAppearance for reference
5094 0 : aAppearance.append( "/Tx BMC\nEMC\n" );
5095 0 : writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
5096 :
5097 0 : endRedirect();
5098 0 : pop();
5099 :
5100 0 : rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
5101 :
5102 : // prepare DA string
5103 0 : OStringBuffer aDA( 256 );
5104 : // prepare DA string
5105 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
5106 0 : aDA.append( ' ' );
5107 0 : if( m_aContext.FieldsUseSystemFonts )
5108 : {
5109 0 : aDA.append( "/F" );
5110 0 : aDA.append( nBest );
5111 :
5112 0 : OStringBuffer aDR( 32 );
5113 0 : aDR.append( "/Font " );
5114 0 : aDR.append( getFontDictObject() );
5115 0 : aDR.append( " 0 R" );
5116 0 : rBox.m_aDRDict = aDR.makeStringAndClear();
5117 : }
5118 : else
5119 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5120 0 : aDA.append( ' ' );
5121 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
5122 0 : aDA.append( " Tf" );
5123 0 : rBox.m_aDAString = aDA.makeStringAndClear();
5124 0 : }
5125 :
5126 0 : void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget )
5127 : {
5128 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5129 :
5130 : // save graphics state
5131 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
5132 :
5133 0 : if( rWidget.Background || rWidget.Border )
5134 : {
5135 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5136 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5137 0 : drawRectangle( rBox.m_aRect );
5138 : }
5139 :
5140 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5141 0 : setFont( aFont );
5142 0 : Size aFontSize = aFont.GetSize();
5143 0 : if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5144 0 : aFontSize.Height() = rBox.m_aRect.GetHeight();
5145 0 : sal_Int32 nDelta = aFontSize.Height()/10;
5146 0 : if( nDelta < 1 )
5147 0 : nDelta = 1;
5148 :
5149 0 : Rectangle aCheckRect, aTextRect;
5150 0 : if( rWidget.ButtonIsLeft )
5151 : {
5152 0 : aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
5153 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5154 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5155 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5156 :
5157 : // #i74206# handle small controls without text area
5158 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5159 : {
5160 0 : aCheckRect.Right() -= nDelta;
5161 0 : aCheckRect.Top() += nDelta/2;
5162 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
5163 : }
5164 :
5165 0 : aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5166 0 : aTextRect.Top() = rBox.m_aRect.Top();
5167 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5168 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
5169 : }
5170 : else
5171 : {
5172 0 : aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5173 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5174 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5175 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5176 :
5177 : // #i74206# handle small controls without text area
5178 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5179 : {
5180 0 : aCheckRect.Left() += nDelta;
5181 0 : aCheckRect.Top() += nDelta/2;
5182 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
5183 : }
5184 :
5185 0 : aTextRect.Left() = rBox.m_aRect.Left();
5186 0 : aTextRect.Top() = rBox.m_aRect.Top();
5187 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5188 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
5189 : }
5190 0 : setLineColor( Color( COL_BLACK ) );
5191 0 : setFillColor( Color( COL_TRANSPARENT ) );
5192 0 : OStringBuffer aLW( 32 );
5193 0 : aLW.append( "q " );
5194 0 : m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
5195 0 : aLW.append( " w " );
5196 0 : writeBuffer( aLW.getStr(), aLW.getLength() );
5197 0 : drawRectangle( aCheckRect );
5198 0 : writeBuffer( " Q\n", 3 );
5199 0 : setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5200 0 : drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5201 :
5202 0 : pop();
5203 :
5204 0 : OStringBuffer aDA( 256 );
5205 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5206 0 : sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
5207 0 : aDA.append( ' ' );
5208 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5209 0 : aDA.append( " 0 Tf" );
5210 0 : rBox.m_aDAString = aDA.makeStringAndClear();
5211 0 : rBox.m_aMKDict = "/CA";
5212 0 : rBox.m_aMKDictCAString = "8";
5213 0 : rBox.m_aRect = aCheckRect;
5214 :
5215 : // create appearance streams
5216 0 : sal_Char cMark = '8';
5217 0 : sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)];
5218 0 : nCharXOffset *= aCheckRect.GetHeight();
5219 0 : nCharXOffset /= 2000;
5220 : sal_Int32 nCharYOffset = 1000-
5221 0 : (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative
5222 0 : nCharYOffset *= aCheckRect.GetHeight();
5223 0 : nCharYOffset /= 2000;
5224 :
5225 0 : SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5226 0 : beginRedirect( pCheckStream, aCheckRect );
5227 0 : aDA.append( "/Tx BMC\nq BT\n" );
5228 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5229 0 : aDA.append( ' ' );
5230 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5231 0 : aDA.append( ' ' );
5232 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5233 0 : aDA.append( " Tf\n" );
5234 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
5235 0 : aDA.append( " " );
5236 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
5237 0 : aDA.append( " Td (" );
5238 0 : aDA.append( cMark );
5239 0 : aDA.append( ") Tj\nET\nQ\nEMC\n" );
5240 0 : writeBuffer( aDA.getStr(), aDA.getLength() );
5241 0 : endRedirect();
5242 0 : rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5243 :
5244 0 : SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5245 0 : beginRedirect( pUncheckStream, aCheckRect );
5246 0 : writeBuffer( "/Tx BMC\nEMC\n", 12 );
5247 0 : endRedirect();
5248 0 : rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5249 0 : }
5250 :
5251 0 : void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget )
5252 : {
5253 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5254 :
5255 : // save graphics state
5256 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
5257 :
5258 0 : if( rWidget.Background || rWidget.Border )
5259 : {
5260 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5261 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5262 0 : drawRectangle( rBox.m_aRect );
5263 : }
5264 :
5265 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5266 0 : setFont( aFont );
5267 0 : Size aFontSize = aFont.GetSize();
5268 0 : if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5269 0 : aFontSize.Height() = rBox.m_aRect.GetHeight();
5270 0 : sal_Int32 nDelta = aFontSize.Height()/10;
5271 0 : if( nDelta < 1 )
5272 0 : nDelta = 1;
5273 :
5274 0 : Rectangle aCheckRect, aTextRect;
5275 0 : if( rWidget.ButtonIsLeft )
5276 : {
5277 0 : aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
5278 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5279 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5280 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5281 :
5282 : // #i74206# handle small controls without text area
5283 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5284 : {
5285 0 : aCheckRect.Right() -= nDelta;
5286 0 : aCheckRect.Top() += nDelta/2;
5287 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
5288 : }
5289 :
5290 0 : aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5291 0 : aTextRect.Top() = rBox.m_aRect.Top();
5292 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5293 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
5294 : }
5295 : else
5296 : {
5297 0 : aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5298 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5299 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5300 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5301 :
5302 : // #i74206# handle small controls without text area
5303 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5304 : {
5305 0 : aCheckRect.Left() += nDelta;
5306 0 : aCheckRect.Top() += nDelta/2;
5307 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
5308 : }
5309 :
5310 0 : aTextRect.Left() = rBox.m_aRect.Left();
5311 0 : aTextRect.Top() = rBox.m_aRect.Top();
5312 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5313 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
5314 : }
5315 0 : setLineColor( Color( COL_BLACK ) );
5316 0 : setFillColor( Color( COL_TRANSPARENT ) );
5317 0 : OStringBuffer aLW( 32 );
5318 0 : aLW.append( "q " );
5319 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
5320 0 : aLW.append( " w " );
5321 0 : writeBuffer( aLW.getStr(), aLW.getLength() );
5322 0 : drawEllipse( aCheckRect );
5323 0 : writeBuffer( " Q\n", 3 );
5324 0 : setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5325 0 : drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5326 :
5327 0 : pop();
5328 :
5329 0 : OStringBuffer aDA( 256 );
5330 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5331 0 : sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) );
5332 0 : aDA.append( ' ' );
5333 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5334 0 : aDA.append( " 0 Tf" );
5335 0 : rBox.m_aDAString = aDA.makeStringAndClear();
5336 : //to encrypt this (el)
5337 0 : rBox.m_aMKDict = "/CA";
5338 : //after this assignement, to m_aMKDic cannot be added anything
5339 0 : rBox.m_aMKDictCAString = "l";
5340 :
5341 0 : rBox.m_aRect = aCheckRect;
5342 :
5343 : // create appearance streams
5344 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
5345 0 : SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5346 :
5347 0 : beginRedirect( pCheckStream, aCheckRect );
5348 0 : aDA.append( "/Tx BMC\nq BT\n" );
5349 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5350 0 : aDA.append( ' ' );
5351 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5352 0 : aDA.append( ' ' );
5353 0 : m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5354 0 : aDA.append( " Tf\n0 0 Td\nET\nQ\n" );
5355 0 : writeBuffer( aDA.getStr(), aDA.getLength() );
5356 0 : setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5357 0 : setLineColor( Color( COL_TRANSPARENT ) );
5358 0 : aCheckRect.Left() += 3*nDelta;
5359 0 : aCheckRect.Top() += 3*nDelta;
5360 0 : aCheckRect.Bottom() -= 3*nDelta;
5361 0 : aCheckRect.Right() -= 3*nDelta;
5362 0 : drawEllipse( aCheckRect );
5363 0 : writeBuffer( "\nEMC\n", 5 );
5364 0 : endRedirect();
5365 :
5366 0 : pop();
5367 0 : rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5368 :
5369 0 : SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5370 0 : beginRedirect( pUncheckStream, aCheckRect );
5371 0 : writeBuffer( "/Tx BMC\nEMC\n", 12 );
5372 0 : endRedirect();
5373 0 : rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5374 0 : }
5375 :
5376 0 : bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
5377 : {
5378 :
5379 : // TODO: check and insert default streams
5380 0 : rtl::OString aStandardAppearance;
5381 0 : switch( rWidget.m_eType )
5382 : {
5383 : case PDFWriter::CheckBox:
5384 0 : aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
5385 0 : break;
5386 : default:
5387 0 : break;
5388 : }
5389 :
5390 0 : if( rWidget.m_aAppearances.size() )
5391 : {
5392 0 : rAnnotDict.append( "/AP<<\n" );
5393 0 : for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it )
5394 : {
5395 0 : rAnnotDict.append( "/" );
5396 0 : rAnnotDict.append( dict_it->first );
5397 0 : bool bUseSubDict = (dict_it->second.size() > 1);
5398 0 : rAnnotDict.append( bUseSubDict ? "<<" : " " );
5399 :
5400 0 : for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin();
5401 0 : stream_it != dict_it->second.end(); ++stream_it )
5402 : {
5403 0 : SvMemoryStream* pApppearanceStream = stream_it->second;
5404 0 : dict_it->second[ stream_it->first ] = NULL;
5405 :
5406 0 : bool bDeflate = compressStream( pApppearanceStream );
5407 :
5408 0 : pApppearanceStream->Seek( STREAM_SEEK_TO_END );
5409 0 : sal_Int64 nStreamLen = pApppearanceStream->Tell();
5410 0 : pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
5411 0 : sal_Int32 nObject = createObject();
5412 0 : CHECK_RETURN( updateObject( nObject ) );
5413 : #if OSL_DEBUG_LEVEL > 1
5414 : emitComment( "PDFWriterImpl::emitAppearances" );
5415 : #endif
5416 0 : OStringBuffer aLine;
5417 0 : aLine.append( nObject );
5418 :
5419 : aLine.append( " 0 obj\n"
5420 : "<</Type/XObject\n"
5421 : "/Subtype/Form\n"
5422 0 : "/BBox[0 0 " );
5423 0 : appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
5424 0 : aLine.append( " " );
5425 0 : appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
5426 : aLine.append( "]\n"
5427 0 : "/Resources " );
5428 0 : aLine.append( getResourceDictObj() );
5429 : aLine.append( " 0 R\n"
5430 0 : "/Length " );
5431 0 : aLine.append( nStreamLen );
5432 0 : aLine.append( "\n" );
5433 0 : if( bDeflate )
5434 0 : aLine.append( "/Filter/FlateDecode\n" );
5435 0 : aLine.append( ">>\nstream\n" );
5436 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5437 0 : checkAndEnableStreamEncryption( nObject );
5438 0 : CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
5439 0 : disableStreamEncryption();
5440 0 : CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
5441 :
5442 0 : if( bUseSubDict )
5443 : {
5444 0 : rAnnotDict.append( " /" );
5445 0 : rAnnotDict.append( stream_it->first );
5446 0 : rAnnotDict.append( " " );
5447 : }
5448 0 : rAnnotDict.append( nObject );
5449 0 : rAnnotDict.append( " 0 R" );
5450 :
5451 0 : delete pApppearanceStream;
5452 0 : }
5453 :
5454 0 : rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
5455 : }
5456 0 : rAnnotDict.append( ">>\n" );
5457 0 : if( !aStandardAppearance.isEmpty() )
5458 : {
5459 0 : rAnnotDict.append( "/AS /" );
5460 0 : rAnnotDict.append( aStandardAppearance );
5461 0 : rAnnotDict.append( "\n" );
5462 : }
5463 : }
5464 :
5465 0 : return true;
5466 : }
5467 :
5468 0 : bool PDFWriterImpl::emitWidgetAnnotations()
5469 : {
5470 0 : ensureUniqueRadioOnValues();
5471 :
5472 0 : int nAnnots = m_aWidgets.size();
5473 0 : for( int a = 0; a < nAnnots; a++ )
5474 : {
5475 0 : PDFWidget& rWidget = m_aWidgets[a];
5476 :
5477 0 : OStringBuffer aLine( 1024 );
5478 0 : OStringBuffer aValue( 256 );
5479 0 : aLine.append( rWidget.m_nObject );
5480 : aLine.append( " 0 obj\n"
5481 0 : "<<" );
5482 0 : if( rWidget.m_eType != PDFWriter::Hierarchy )
5483 : {
5484 : // emit widget annotation only for terminal fields
5485 0 : if( rWidget.m_aKids.empty() )
5486 : {
5487 : int iRectMargin;
5488 :
5489 0 : aLine.append( "/Type/Annot/Subtype/Widget/F " );
5490 :
5491 0 : if (rWidget.m_eType == PDFWriter::Signature)
5492 : {
5493 0 : aLine.append( "132\n" ); // Print & Locked
5494 0 : iRectMargin = 0;
5495 : }
5496 : else
5497 : {
5498 0 : aLine.append( "4\n" );
5499 0 : iRectMargin = 1;
5500 : }
5501 :
5502 0 : aLine.append("/Rect[" );
5503 0 : appendFixedInt( rWidget.m_aRect.Left()-iRectMargin, aLine );
5504 0 : aLine.append( ' ' );
5505 0 : appendFixedInt( rWidget.m_aRect.Top()+iRectMargin, aLine );
5506 0 : aLine.append( ' ' );
5507 0 : appendFixedInt( rWidget.m_aRect.Right()+iRectMargin, aLine );
5508 0 : aLine.append( ' ' );
5509 0 : appendFixedInt( rWidget.m_aRect.Bottom()-iRectMargin, aLine );
5510 0 : aLine.append( "]\n" );
5511 : }
5512 0 : aLine.append( "/FT/" );
5513 0 : switch( rWidget.m_eType )
5514 : {
5515 : case PDFWriter::RadioButton:
5516 : case PDFWriter::CheckBox:
5517 : // for radio buttons only the RadioButton field, not the
5518 : // CheckBox children should have a value, else acrobat reader
5519 : // does not always check the right button
5520 : // of course real check boxes (not belonging to a readio group)
5521 : // need their values, too
5522 0 : if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
5523 : {
5524 0 : aValue.append( "/" );
5525 : // check for radio group with all buttons unpressed
5526 0 : if( rWidget.m_aValue.isEmpty() )
5527 0 : aValue.append( "Off" );
5528 : else
5529 0 : appendName( rWidget.m_aValue, aValue );
5530 : }
5531 : case PDFWriter::PushButton:
5532 0 : aLine.append( "Btn" );
5533 0 : break;
5534 : case PDFWriter::ListBox:
5535 0 : if( rWidget.m_nFlags & 0x200000 ) // multiselect
5536 : {
5537 0 : aValue.append( "[" );
5538 0 : for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
5539 : {
5540 0 : sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
5541 0 : if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
5542 0 : appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
5543 : }
5544 0 : aValue.append( "]" );
5545 : }
5546 0 : else if( rWidget.m_aSelectedEntries.size() > 0 &&
5547 0 : rWidget.m_aSelectedEntries[0] >= 0 &&
5548 0 : rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
5549 : {
5550 0 : appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
5551 : }
5552 : else
5553 0 : appendUnicodeTextStringEncrypt( rtl::OUString(), rWidget.m_nObject, aValue );
5554 0 : aLine.append( "Ch" );
5555 0 : break;
5556 : case PDFWriter::ComboBox:
5557 0 : appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5558 0 : aLine.append( "Ch" );
5559 0 : break;
5560 : case PDFWriter::Edit:
5561 0 : aLine.append( "Tx" );
5562 0 : appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5563 0 : break;
5564 : case PDFWriter::Signature:
5565 0 : aLine.append( "Sig" );
5566 0 : aValue.append(OUStringToOString(rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US));
5567 0 : break;
5568 : case PDFWriter::Hierarchy: // make the compiler happy
5569 0 : break;
5570 : }
5571 0 : aLine.append( "\n" );
5572 0 : aLine.append( "/P " );
5573 0 : aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
5574 0 : aLine.append( " 0 R\n" );
5575 : }
5576 0 : if( rWidget.m_nParent )
5577 : {
5578 0 : aLine.append( "/Parent " );
5579 0 : aLine.append( rWidget.m_nParent );
5580 0 : aLine.append( " 0 R\n" );
5581 : }
5582 0 : if( rWidget.m_aKids.size() )
5583 : {
5584 0 : aLine.append( "/Kids[" );
5585 0 : for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ )
5586 : {
5587 0 : aLine.append( rWidget.m_aKids[i] );
5588 0 : aLine.append( " 0 R" );
5589 0 : aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5590 : }
5591 0 : aLine.append( "]\n" );
5592 : }
5593 0 : if( !rWidget.m_aName.isEmpty() )
5594 : {
5595 0 : aLine.append( "/T" );
5596 0 : appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
5597 0 : aLine.append( "\n" );
5598 : }
5599 0 : if( m_aContext.Version > PDFWriter::PDF_1_2 && !rWidget.m_aDescription.isEmpty() )
5600 : {
5601 : // the alternate field name should be unicode able since it is
5602 : // supposed to be used in UI
5603 0 : aLine.append( "/TU" );
5604 0 : appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
5605 0 : aLine.append( "\n" );
5606 : }
5607 :
5608 0 : if( rWidget.m_nFlags )
5609 : {
5610 0 : aLine.append( "/Ff " );
5611 0 : aLine.append( rWidget.m_nFlags );
5612 0 : aLine.append( "\n" );
5613 : }
5614 0 : if( aValue.getLength() )
5615 : {
5616 0 : OString aVal = aValue.makeStringAndClear();
5617 0 : aLine.append( "/V " );
5618 0 : aLine.append( aVal );
5619 : aLine.append( "\n"
5620 0 : "/DV " );
5621 0 : aLine.append( aVal );
5622 0 : aLine.append( "\n" );
5623 : }
5624 0 : if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
5625 : {
5626 0 : sal_Int32 nTI = -1;
5627 0 : aLine.append( "/Opt[\n" );
5628 0 : sal_Int32 i = 0;
5629 0 : for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i )
5630 : {
5631 0 : appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine );
5632 0 : aLine.append( "\n" );
5633 0 : if( *it == rWidget.m_aValue )
5634 0 : nTI = i;
5635 : }
5636 0 : aLine.append( "]\n" );
5637 0 : if( nTI > 0 )
5638 : {
5639 0 : aLine.append( "/TI " );
5640 0 : aLine.append( nTI );
5641 0 : aLine.append( "\n" );
5642 0 : if( rWidget.m_nFlags & 0x200000 ) // Multiselect
5643 : {
5644 0 : aLine.append( "/I [" );
5645 0 : aLine.append( nTI );
5646 0 : aLine.append( "]\n" );
5647 : }
5648 : }
5649 : }
5650 0 : if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
5651 : {
5652 0 : aLine.append( "/MaxLen " );
5653 0 : aLine.append( rWidget.m_nMaxLen );
5654 0 : aLine.append( "\n" );
5655 : }
5656 0 : if( rWidget.m_eType == PDFWriter::PushButton )
5657 : {
5658 0 : if(!m_bIsPDF_A1)
5659 : {
5660 0 : OStringBuffer aDest;
5661 0 : if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) )
5662 : {
5663 0 : aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
5664 0 : aLine.append( aDest.makeStringAndClear() );
5665 0 : aLine.append( ">>>>\n" );
5666 : }
5667 0 : else if( rWidget.m_aListEntries.empty() )
5668 : {
5669 : // create a reset form action
5670 0 : aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
5671 : }
5672 0 : else if( rWidget.m_bSubmit )
5673 : {
5674 : // create a submit form action
5675 0 : aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
5676 0 : appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() );
5677 0 : aLine.append( "/Flags " );
5678 :
5679 0 : sal_Int32 nFlags = 0;
5680 0 : switch( m_aContext.SubmitFormat )
5681 : {
5682 : case PDFWriter::HTML:
5683 0 : nFlags |= 4;
5684 0 : break;
5685 : case PDFWriter::XML:
5686 0 : if( m_aContext.Version > PDFWriter::PDF_1_3 )
5687 0 : nFlags |= 32;
5688 0 : break;
5689 : case PDFWriter::PDF:
5690 0 : if( m_aContext.Version > PDFWriter::PDF_1_3 )
5691 0 : nFlags |= 256;
5692 0 : break;
5693 : case PDFWriter::FDF:
5694 : default:
5695 0 : break;
5696 : }
5697 0 : if( rWidget.m_bSubmitGet )
5698 0 : nFlags |= 8;
5699 0 : aLine.append( nFlags );
5700 0 : aLine.append( ">>>>\n" );
5701 : }
5702 : else
5703 : {
5704 : // create a URI action
5705 0 : aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
5706 0 : aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
5707 0 : aLine.append( ")>>>>\n" );
5708 0 : }
5709 : }
5710 : else
5711 0 : m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA );
5712 : }
5713 0 : if( !rWidget.m_aDAString.isEmpty() )
5714 : {
5715 0 : if( !rWidget.m_aDRDict.isEmpty() )
5716 : {
5717 0 : aLine.append( "/DR<<" );
5718 0 : aLine.append( rWidget.m_aDRDict );
5719 0 : aLine.append( ">>\n" );
5720 : }
5721 : else
5722 : {
5723 0 : aLine.append( "/DR<</Font<<" );
5724 0 : appendBuiltinFontsToDict( aLine );
5725 0 : aLine.append( ">>>>\n" );
5726 : }
5727 0 : aLine.append( "/DA" );
5728 0 : appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
5729 0 : aLine.append( "\n" );
5730 0 : if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER )
5731 0 : aLine.append( "/Q 1\n" );
5732 0 : else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT )
5733 0 : aLine.append( "/Q 2\n" );
5734 : }
5735 : // appearance charactristics for terminal fields
5736 : // which are supposed to have an appearance constructed
5737 : // by the viewer application
5738 0 : if( !rWidget.m_aMKDict.isEmpty() )
5739 : {
5740 0 : aLine.append( "/MK<<" );
5741 0 : aLine.append( rWidget.m_aMKDict );
5742 : //add the CA string, encrypting it
5743 0 : appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
5744 0 : aLine.append( ">>\n" );
5745 : }
5746 :
5747 0 : CHECK_RETURN( emitAppearances( rWidget, aLine ) );
5748 :
5749 : aLine.append( ">>\n"
5750 0 : "endobj\n\n" );
5751 0 : CHECK_RETURN( updateObject( rWidget.m_nObject ) );
5752 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5753 0 : }
5754 0 : return true;
5755 : }
5756 :
5757 0 : bool PDFWriterImpl::emitAnnotations()
5758 : {
5759 0 : if( m_aPages.size() < 1 )
5760 0 : return false;
5761 :
5762 0 : CHECK_RETURN( emitLinkAnnotations() );
5763 :
5764 0 : CHECK_RETURN( emitNoteAnnotations() );
5765 :
5766 0 : CHECK_RETURN( emitWidgetAnnotations() );
5767 :
5768 0 : return true;
5769 : }
5770 :
5771 : #undef CHECK_RETURN
5772 : #define CHECK_RETURN( x ) if( !x ) return false
5773 :
5774 0 : bool PDFWriterImpl::emitCatalog()
5775 : {
5776 : // build page tree
5777 : // currently there is only one node that contains all leaves
5778 :
5779 : // first create a page tree node id
5780 0 : sal_Int32 nTreeNode = createObject();
5781 :
5782 : // emit global resource dictionary (page emit needs it)
5783 0 : CHECK_RETURN( emitResources() );
5784 :
5785 : // emit all pages
5786 0 : for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it )
5787 0 : if( ! it->emit( nTreeNode ) )
5788 0 : return false;
5789 :
5790 0 : sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
5791 :
5792 0 : sal_Int32 nOutlineDict = emitOutline();
5793 :
5794 : //emit Output intent i59651
5795 0 : sal_Int32 nOutputIntentObject = emitOutputIntent();
5796 :
5797 : //emit metadata
5798 0 : sal_Int32 nMetadataObject = emitDocumentMetadata();
5799 :
5800 0 : sal_Int32 nStructureDict = 0;
5801 0 : if(m_aStructure.size() > 1)
5802 : {
5803 : ///check if dummy structure containers are needed
5804 0 : addInternalStructureContainer(m_aStructure[0]);
5805 0 : nStructureDict = m_aStructure[0].m_nObject = createObject();
5806 0 : emitStructure( m_aStructure[ 0 ] );
5807 : }
5808 :
5809 : // adjust tree node file offset
5810 0 : if( ! updateObject( nTreeNode ) )
5811 0 : return false;
5812 :
5813 : // emit tree node
5814 0 : OStringBuffer aLine( 2048 );
5815 0 : aLine.append( nTreeNode );
5816 0 : aLine.append( " 0 obj\n" );
5817 0 : aLine.append( "<</Type/Pages\n" );
5818 0 : aLine.append( "/Resources " );
5819 0 : aLine.append( getResourceDictObj() );
5820 0 : aLine.append( " 0 R\n" );
5821 :
5822 0 : switch( m_eInheritedOrientation )
5823 : {
5824 0 : case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
5825 0 : case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
5826 :
5827 : case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant
5828 : case PDFWriter::Portrait:
5829 : default:
5830 0 : break;
5831 : }
5832 0 : sal_Int32 nMediaBoxWidth = 0;
5833 0 : sal_Int32 nMediaBoxHeight = 0;
5834 0 : if( m_aPages.empty() ) // sanity check, this should not happen
5835 : {
5836 0 : nMediaBoxWidth = m_nInheritedPageWidth;
5837 0 : nMediaBoxHeight = m_nInheritedPageHeight;
5838 : }
5839 : else
5840 : {
5841 0 : for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter )
5842 : {
5843 0 : if( iter->m_nPageWidth > nMediaBoxWidth )
5844 0 : nMediaBoxWidth = iter->m_nPageWidth;
5845 0 : if( iter->m_nPageHeight > nMediaBoxHeight )
5846 0 : nMediaBoxHeight = iter->m_nPageHeight;
5847 : }
5848 : }
5849 0 : aLine.append( "/MediaBox[ 0 0 " );
5850 0 : aLine.append( nMediaBoxWidth );
5851 0 : aLine.append( ' ' );
5852 0 : aLine.append( nMediaBoxHeight );
5853 : aLine.append( " ]\n"
5854 0 : "/Kids[ " );
5855 0 : unsigned int i = 0;
5856 0 : for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ )
5857 : {
5858 0 : aLine.append( iter->m_nPageObject );
5859 0 : aLine.append( " 0 R" );
5860 0 : aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5861 : }
5862 : aLine.append( "]\n"
5863 0 : "/Count " );
5864 0 : aLine.append( (sal_Int32)m_aPages.size() );
5865 : aLine.append( ">>\n"
5866 0 : "endobj\n\n" );
5867 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5868 :
5869 : // emit annotation objects
5870 0 : CHECK_RETURN( emitAnnotations() );
5871 :
5872 : // emit Catalog
5873 0 : m_nCatalogObject = createObject();
5874 0 : if( ! updateObject( m_nCatalogObject ) )
5875 0 : return false;
5876 0 : aLine.setLength( 0 );
5877 0 : aLine.append( m_nCatalogObject );
5878 : aLine.append( " 0 obj\n"
5879 0 : "<</Type/Catalog/Pages " );
5880 0 : aLine.append( nTreeNode );
5881 0 : aLine.append( " 0 R\n" );
5882 : //--->i56629
5883 : //check if there are named destinations to emit (root must be inside the catalog)
5884 0 : if( nNamedDestinationsDictionary )
5885 : {
5886 0 : aLine.append("/Dests ");
5887 0 : aLine.append( nNamedDestinationsDictionary );
5888 0 : aLine.append( " 0 R\n" );
5889 : }
5890 : //<----
5891 0 : if( m_aContext.PageLayout != PDFWriter::DefaultLayout )
5892 0 : switch( m_aContext.PageLayout )
5893 : {
5894 : default :
5895 : case PDFWriter::SinglePage :
5896 0 : aLine.append( "/PageLayout/SinglePage\n" );
5897 0 : break;
5898 : case PDFWriter::Continuous :
5899 0 : aLine.append( "/PageLayout/OneColumn\n" );
5900 0 : break;
5901 : case PDFWriter::ContinuousFacing :
5902 : //the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5903 0 : aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5904 0 : break;
5905 : }
5906 0 : if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode )
5907 0 : switch( m_aContext.PDFDocumentMode )
5908 : {
5909 : default :
5910 0 : aLine.append( "/PageMode/UseNone\n" );
5911 0 : break;
5912 : case PDFWriter::UseOutlines :
5913 0 : aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5914 0 : break;
5915 : case PDFWriter::UseThumbs :
5916 0 : aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5917 0 : break;
5918 : }
5919 0 : else if( m_aContext.OpenInFullScreenMode )
5920 0 : aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5921 :
5922 0 : OStringBuffer aInitPageRef;
5923 0 : if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() )
5924 : {
5925 0 : aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5926 0 : aInitPageRef.append( " 0 R" );
5927 : }
5928 : else
5929 0 : aInitPageRef.append( "0" );
5930 :
5931 0 : switch( m_aContext.PDFDocumentAction )
5932 : {
5933 : case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default
5934 : default:
5935 0 : if( aInitPageRef.getLength() > 1 )
5936 : {
5937 0 : aLine.append( "/OpenAction[" );
5938 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5939 0 : aLine.append( " /XYZ null null 0]\n" );
5940 : }
5941 0 : break;
5942 : case PDFWriter::FitInWindow :
5943 0 : aLine.append( "/OpenAction[" );
5944 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5945 0 : aLine.append( " /Fit]\n" ); //Open fit page
5946 0 : break;
5947 : case PDFWriter::FitWidth :
5948 0 : aLine.append( "/OpenAction[" );
5949 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5950 0 : aLine.append( " /FitH " );
5951 0 : aLine.append( m_nInheritedPageHeight );//Open fit width
5952 0 : aLine.append( "]\n" );
5953 0 : break;
5954 : case PDFWriter::FitVisible :
5955 0 : aLine.append( "/OpenAction[" );
5956 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5957 0 : aLine.append( " /FitBH " );
5958 0 : aLine.append( m_nInheritedPageHeight );//Open fit visible
5959 0 : aLine.append( "]\n" );
5960 0 : break;
5961 : case PDFWriter::ActionZoom :
5962 0 : aLine.append( "/OpenAction[" );
5963 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5964 0 : aLine.append( " /XYZ null null " );
5965 0 : if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5966 0 : aLine.append( (double)m_aContext.Zoom/100.0 );
5967 : else
5968 0 : aLine.append( "0" );
5969 0 : aLine.append( "]\n" );
5970 0 : break;
5971 : }
5972 : // viewer preferences, if we had some, then emit
5973 0 : if( m_aContext.HideViewerToolbar ||
5974 0 : ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) ||
5975 : m_aContext.HideViewerMenubar ||
5976 : m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
5977 : m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
5978 : m_aContext.OpenInFullScreenMode )
5979 : {
5980 0 : aLine.append( "/ViewerPreferences<<" );
5981 0 : if( m_aContext.HideViewerToolbar )
5982 0 : aLine.append( "/HideToolbar true\n" );
5983 0 : if( m_aContext.HideViewerMenubar )
5984 0 : aLine.append( "/HideMenubar true\n" );
5985 0 : if( m_aContext.HideViewerWindowControls )
5986 0 : aLine.append( "/HideWindowUI true\n" );
5987 0 : if( m_aContext.FitWindow )
5988 0 : aLine.append( "/FitWindow true\n" );
5989 0 : if( m_aContext.CenterWindow )
5990 0 : aLine.append( "/CenterWindow true\n" );
5991 0 : if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle )
5992 0 : aLine.append( "/DisplayDocTitle true\n" );
5993 0 : if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing )
5994 0 : aLine.append( "/Direction/R2L\n" );
5995 0 : if( m_aContext.OpenInFullScreenMode )
5996 0 : switch( m_aContext.PDFDocumentMode )
5997 : {
5998 : default :
5999 : case PDFWriter::ModeDefault :
6000 0 : aLine.append( "/NonFullScreenPageMode/UseNone\n" );
6001 0 : break;
6002 : case PDFWriter::UseOutlines :
6003 0 : aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
6004 0 : break;
6005 : case PDFWriter::UseThumbs :
6006 0 : aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
6007 0 : break;
6008 : }
6009 0 : aLine.append( ">>\n" );
6010 : }
6011 :
6012 0 : if( nOutlineDict )
6013 : {
6014 0 : aLine.append( "/Outlines " );
6015 0 : aLine.append( nOutlineDict );
6016 0 : aLine.append( " 0 R\n" );
6017 : }
6018 0 : if( nStructureDict )
6019 : {
6020 0 : aLine.append( "/StructTreeRoot " );
6021 0 : aLine.append( nStructureDict );
6022 0 : aLine.append( " 0 R\n" );
6023 : }
6024 0 : if( !m_aContext.DocumentLocale.Language.isEmpty() )
6025 : {
6026 0 : OUStringBuffer aLocBuf( 16 );
6027 0 : aLocBuf.append( m_aContext.DocumentLocale.Language.toAsciiLowerCase() );
6028 0 : if( !m_aContext.DocumentLocale.Country.isEmpty() )
6029 : {
6030 0 : aLocBuf.append( sal_Unicode('-') );
6031 0 : aLocBuf.append( m_aContext.DocumentLocale.Country );
6032 : }
6033 0 : aLine.append( "/Lang" );
6034 0 : appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
6035 0 : aLine.append( "\n" );
6036 : }
6037 0 : if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 )
6038 : {
6039 0 : aLine.append( "/MarkInfo<</Marked true>>\n" );
6040 : }
6041 0 : if( m_aWidgets.size() > 0 )
6042 : {
6043 0 : aLine.append( "/AcroForm<</Fields[\n" );
6044 0 : int nWidgets = m_aWidgets.size();
6045 0 : int nOut = 0;
6046 0 : for( int j = 0; j < nWidgets; j++ )
6047 : {
6048 : // output only root fields
6049 0 : if( m_aWidgets[j].m_nParent < 1 )
6050 : {
6051 0 : aLine.append( m_aWidgets[j].m_nObject );
6052 0 : aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
6053 : }
6054 : }
6055 0 : aLine.append( "\n]" );
6056 :
6057 : #if !defined(ANDROID) && !defined(IOS)
6058 0 : if (m_nSignatureObject != -1)
6059 0 : aLine.append( "/SigFlags 3");
6060 : #endif
6061 :
6062 0 : aLine.append( "/DR " );
6063 0 : aLine.append( getResourceDictObj() );
6064 0 : aLine.append( " 0 R" );
6065 : // /NeedAppearances must not be used if PDF is signed
6066 0 : if( m_bIsPDF_A1
6067 : #if !defined(ANDROID) && !defined(IOS)
6068 : || ( m_nSignatureObject != -1 )
6069 : #endif
6070 : )
6071 0 : aLine.append( ">>\n" );
6072 : else
6073 0 : aLine.append( "/NeedAppearances true>>\n" );
6074 : }
6075 : //--->i59651
6076 : //check if there is a Metadata object
6077 0 : if( nOutputIntentObject )
6078 : {
6079 0 : aLine.append("/OutputIntents[");
6080 0 : aLine.append( nOutputIntentObject );
6081 0 : aLine.append( " 0 R]" );
6082 : }
6083 0 : if( nMetadataObject )
6084 : {
6085 0 : aLine.append("/Metadata ");
6086 0 : aLine.append( nMetadataObject );
6087 0 : aLine.append( " 0 R" );
6088 : }
6089 : //<----
6090 : aLine.append( ">>\n"
6091 0 : "endobj\n\n" );
6092 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6093 :
6094 0 : return true;
6095 : }
6096 :
6097 : #if !defined(ANDROID) && !defined(IOS)
6098 :
6099 0 : bool PDFWriterImpl::emitSignature()
6100 : {
6101 0 : if( !updateObject( m_nSignatureObject ) )
6102 0 : return false;
6103 :
6104 0 : OStringBuffer aLine( 0x5000 );
6105 0 : aLine.append( m_nSignatureObject );
6106 0 : aLine.append( " 0 obj\n" );
6107 0 : aLine.append("<</Contents <" );
6108 :
6109 0 : sal_uInt64 nOffset = ~0U;
6110 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nOffset ) ) );
6111 :
6112 0 : m_nSignatureContentOffset = nOffset + aLine.getLength();
6113 :
6114 : // reserve some space for the PKCS#7 object
6115 0 : OStringBuffer aContentFiller( MAX_SIGNATURE_CONTENT_LENGTH );
6116 0 : comphelper::string::padToLength(aContentFiller, MAX_SIGNATURE_CONTENT_LENGTH, '0');
6117 0 : aLine.append( aContentFiller.makeStringAndClear() );
6118 0 : aLine.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached");
6119 :
6120 0 : if( m_aContext.DocumentInfo.Author.Len() )
6121 : {
6122 0 : aLine.append( "/Name" );
6123 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, m_nSignatureObject, aLine );
6124 : }
6125 :
6126 0 : aLine.append( " /M ");
6127 0 : appendLiteralStringEncrypt( m_aCreationDateString, m_nSignatureObject, aLine );
6128 :
6129 0 : aLine.append( " /ByteRange [ 0 ");
6130 0 : aLine.append( m_nSignatureContentOffset - 1, 10 );
6131 0 : aLine.append( " " );
6132 0 : aLine.append( m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1, 10 );
6133 0 : aLine.append( " " );
6134 :
6135 0 : m_nSignatureLastByteRangeNoOffset = nOffset + aLine.getLength();
6136 :
6137 : // mark the last ByteRange no and add some space. Now, we don't know
6138 : // how many bytes we need for this ByteRange value
6139 : // The real value will be overwritten in the finalizeSignature method
6140 0 : OStringBuffer aByteRangeFiller( 100 );
6141 0 : comphelper::string::padToLength(aByteRangeFiller, 100, ' ');
6142 0 : aLine.append( aByteRangeFiller.makeStringAndClear() );
6143 0 : aLine.append(" /Filter/Adobe.PPKMS");
6144 :
6145 : //emit reason, location and contactinfo
6146 0 : if ( !m_aContext.SignReason.isEmpty() )
6147 : {
6148 0 : aLine.append("/Reason");
6149 0 : appendUnicodeTextStringEncrypt( m_aContext.SignReason, m_nSignatureObject, aLine );
6150 : }
6151 :
6152 0 : if ( !m_aContext.SignLocation.isEmpty() )
6153 : {
6154 0 : aLine.append("/Location");
6155 0 : appendUnicodeTextStringEncrypt( m_aContext.SignLocation, m_nSignatureObject, aLine );
6156 : }
6157 :
6158 0 : if ( !m_aContext.SignContact.isEmpty() )
6159 : {
6160 0 : aLine.append("/ContactInfo");
6161 0 : appendUnicodeTextStringEncrypt( m_aContext.SignContact, m_nSignatureObject, aLine );
6162 : }
6163 :
6164 0 : aLine.append(" >>\nendobj\n\n" );
6165 :
6166 0 : if (!writeBuffer( aLine.getStr(), aLine.getLength() ))
6167 0 : return false;
6168 :
6169 0 : return true;
6170 : }
6171 :
6172 0 : char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo * /*slot*/, PRBool /*retry*/, void *arg)
6173 : {
6174 0 : return (char *)arg;
6175 : }
6176 :
6177 0 : bool PDFWriterImpl::finalizeSignature()
6178 : {
6179 :
6180 0 : if (!m_aContext.SignCertificate.is())
6181 0 : return false;
6182 :
6183 : // 1- calculate last ByteRange value
6184 0 : sal_uInt64 nOffset = ~0U;
6185 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nOffset ) ) );
6186 :
6187 0 : sal_Int64 nLastByteRangeNo = nOffset - (m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1);
6188 :
6189 : // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position
6190 0 : sal_uInt64 nWritten = 0;
6191 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureLastByteRangeNoOffset ) ) );
6192 0 : OStringBuffer aByteRangeNo( 256 );
6193 0 : aByteRangeNo.append( nLastByteRangeNo, 10);
6194 0 : aByteRangeNo.append( " ]" );
6195 :
6196 0 : if( osl_writeFile( m_aFile, aByteRangeNo.getStr(), aByteRangeNo.getLength(), &nWritten ) != osl_File_E_None )
6197 : {
6198 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, nOffset ) ) );
6199 0 : return false;
6200 : }
6201 :
6202 : // 3- create the PKCS#7 object using NSS
6203 0 : com::sun::star::uno::Sequence< sal_Int8 > derEncoded = m_aContext.SignCertificate->getEncoded();
6204 :
6205 0 : if (!derEncoded.hasElements())
6206 0 : return false;
6207 :
6208 0 : sal_Int8* n_derArray = derEncoded.getArray();
6209 0 : sal_Int32 n_derLength = derEncoded.getLength();
6210 :
6211 0 : NSS_NoDB_Init(".");
6212 :
6213 0 : CERTCertificate *cert = CERT_DecodeCertFromPackage(reinterpret_cast<char *>(n_derArray), n_derLength);
6214 :
6215 0 : if (!cert)
6216 : {
6217 : SAL_WARN("vcl.gdi", "PDF Signing: Error occured, certificate cannot be reconstructed.");
6218 0 : return false;
6219 : }
6220 :
6221 : SAL_WARN("vcl.gdi", "PDF Signing: Certificate Subject: " << cert->subjectName << "\n\tCertificate Issuer: " << cert->issuerName);
6222 :
6223 : // Prepare buffer and calculate PDF file digest
6224 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, 0) ) );
6225 :
6226 0 : HASHContext *hc = HASH_Create(HASH_AlgSHA1);
6227 :
6228 0 : if (!hc)
6229 : {
6230 : SAL_WARN("vcl.gdi", "PDF Signing: SHA1 HASH_Create failed!");
6231 0 : return false;
6232 : }
6233 :
6234 0 : HASH_Begin(hc);
6235 :
6236 0 : char *buffer = new char[m_nSignatureContentOffset + 1];
6237 : sal_uInt64 bytesRead;
6238 :
6239 : //FIXME: Check if SHA1 is calculated from the correct byterange
6240 :
6241 0 : CHECK_RETURN( (osl_File_E_None == osl_readFile( m_aFile, buffer, m_nSignatureContentOffset - 1 , &bytesRead ) ) );
6242 0 : if (bytesRead != (sal_uInt64)m_nSignatureContentOffset - 1)
6243 : SAL_WARN("vcl.gdi", "PDF Signing: First buffer read failed!");
6244 :
6245 0 : HASH_Update(hc, reinterpret_cast<const unsigned char*>(buffer), bytesRead);
6246 0 : delete[] buffer;
6247 :
6248 0 : buffer = new char[nLastByteRangeNo + 1];
6249 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1) ) );
6250 0 : CHECK_RETURN( (osl_File_E_None == osl_readFile( m_aFile, buffer, nLastByteRangeNo, &bytesRead ) ) );
6251 0 : if (bytesRead != (sal_uInt64) nLastByteRangeNo)
6252 : SAL_WARN("vcl.gdi", "PDF Signing: Second buffer read failed!");
6253 :
6254 0 : HASH_Update(hc, reinterpret_cast<const unsigned char*>(buffer), bytesRead);
6255 0 : delete[] buffer;
6256 :
6257 : SECItem digest;
6258 : unsigned char hash[SHA1_LENGTH];
6259 0 : digest.data = hash;
6260 0 : HASH_End(hc, digest.data, &digest.len, SHA1_LENGTH);
6261 0 : HASH_Destroy(hc);
6262 :
6263 0 : const char *pass = OUStringToOString( m_aContext.SignPassword, RTL_TEXTENCODING_UTF8 ).getStr();
6264 :
6265 0 : NSSCMSMessage *cms_msg = NSS_CMSMessage_Create(NULL);
6266 0 : if (!cms_msg)
6267 : {
6268 : SAL_WARN("vcl.gdi", "PDF signing: can't create new CMS message.");
6269 0 : return false;
6270 : }
6271 :
6272 0 : NSSCMSSignedData *cms_sd = NSS_CMSSignedData_Create(cms_msg);
6273 0 : if (!cms_sd)
6274 : {
6275 : SAL_WARN("vcl.gdi", "PDF signing: can't create CMS SignedData.");
6276 0 : return false;
6277 : }
6278 :
6279 0 : NSSCMSContentInfo *cms_cinfo = NSS_CMSMessage_GetContentInfo(cms_msg);
6280 0 : if (NSS_CMSContentInfo_SetContent_SignedData(cms_msg, cms_cinfo, cms_sd) != SECSuccess)
6281 : {
6282 : SAL_WARN("vcl.gdi", "PDF signing: Can't set CMS content signed data.");
6283 0 : return false;
6284 : }
6285 :
6286 0 : cms_cinfo = NSS_CMSSignedData_GetContentInfo(cms_sd);
6287 : //attach NULL data as detached data
6288 0 : if (NSS_CMSContentInfo_SetContent_Data(cms_msg, cms_cinfo, NULL, PR_TRUE) != SECSuccess)
6289 : {
6290 : SAL_WARN("vcl.gdi", "PDF signing: Can't set CMS content data.");
6291 0 : return false;
6292 : }
6293 :
6294 0 : NSSCMSSignerInfo *cms_signer = NSS_CMSSignerInfo_Create(cms_msg, cert, SEC_OID_SHA1);
6295 0 : if (!cms_signer)
6296 : {
6297 : SAL_WARN("vcl.gdi", "PDF signing: can't create CMS SignerInfo.");
6298 0 : return false;
6299 : }
6300 :
6301 0 : if (NSS_CMSSignerInfo_IncludeCerts(cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess)
6302 : {
6303 : SAL_WARN("vcl.gdi", "PDF signing: can't include cert chain.");
6304 0 : return false;
6305 : }
6306 :
6307 0 : if (NSS_CMSSignerInfo_AddSigningTime(cms_signer, PR_Now()) != SECSuccess)
6308 : {
6309 : SAL_WARN("vcl.gdi", "PDF signing: can't add signing time.");
6310 0 : return false;
6311 : }
6312 :
6313 0 : if (NSS_CMSSignedData_AddCertificate(cms_sd, cert) != SECSuccess)
6314 : {
6315 : SAL_WARN("vcl.gdi", "PDF signing: can't add signer certificate.");
6316 0 : return false;
6317 : }
6318 :
6319 0 : if (NSS_CMSSignedData_AddSignerInfo(cms_sd, cms_signer) != SECSuccess)
6320 : {
6321 : SAL_WARN("vcl.gdi", "PDF signing: can't add signer info.");
6322 0 : return false;
6323 : }
6324 :
6325 0 : if (NSS_CMSSignedData_SetDigestValue(cms_sd, SEC_OID_SHA1, &digest) != SECSuccess)
6326 : {
6327 : SAL_WARN("vcl.gdi", "PDF signing: can't set PDF digest value.");
6328 0 : return false;
6329 : }
6330 :
6331 : SAL_WARN("vcl.gdi","PKCS7 Object created successfully!");
6332 :
6333 : SECItem cms_output;
6334 0 : cms_output.data = 0;
6335 0 : cms_output.len = 0;
6336 0 : PLArenaPool *arena = PORT_NewArena(MAX_SIGNATURE_CONTENT_LENGTH);
6337 : NSSCMSEncoderContext *cms_ecx;
6338 :
6339 : //FIXME: Check if password is passed correctly to SEC_PKCS7CreateSignedData function
6340 0 : cms_ecx = NSS_CMSEncoder_Start(cms_msg, NULL, NULL, &cms_output, arena, (PK11PasswordFunc)::PDFSigningPKCS7PasswordCallback, (void *)pass, NULL, NULL, NULL, NULL);
6341 :
6342 0 : if (!cms_ecx)
6343 : {
6344 : SAL_WARN("vcl.gdi", "PDF Signing: can't start DER encoder.");
6345 0 : return false;
6346 : }
6347 : SAL_WARN("vcl.gdi", "PDF Signing: Started DER encoding.");
6348 :
6349 0 : if (NSS_CMSEncoder_Finish(cms_ecx) != SECSuccess)
6350 : {
6351 : SAL_WARN("vcl.gdi", "PDF Signing: can't finish DER encoder.");
6352 0 : return false;
6353 : }
6354 : SAL_WARN("vcl.gdi", "PDF Signing: Finished DER encoding.");
6355 :
6356 0 : OStringBuffer cms_hexbuffer;
6357 :
6358 0 : for (unsigned int i = 0; i < cms_output.len ; i++)
6359 0 : appendHex(cms_output.data[i], cms_hexbuffer);
6360 :
6361 : SAL_WARN("vcl.gdi","PKCS7 object encoded successfully!");
6362 :
6363 : // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
6364 0 : nWritten = 0;
6365 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureContentOffset) ) );
6366 0 : osl_writeFile(m_aFile, cms_hexbuffer.getStr(), cms_hexbuffer.getLength(), &nWritten);
6367 :
6368 0 : NSS_CMSMessage_Destroy(cms_msg);
6369 :
6370 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, nOffset ) ) );
6371 0 : return true;
6372 : }
6373 :
6374 : #endif
6375 :
6376 0 : sal_Int32 PDFWriterImpl::emitInfoDict( )
6377 : {
6378 0 : sal_Int32 nObject = createObject();
6379 :
6380 0 : if( updateObject( nObject ) )
6381 : {
6382 0 : OStringBuffer aLine( 1024 );
6383 0 : aLine.append( nObject );
6384 : aLine.append( " 0 obj\n"
6385 0 : "<<" );
6386 0 : if( m_aContext.DocumentInfo.Title.Len() )
6387 : {
6388 0 : aLine.append( "/Title" );
6389 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine );
6390 0 : aLine.append( "\n" );
6391 : }
6392 0 : if( m_aContext.DocumentInfo.Author.Len() )
6393 : {
6394 0 : aLine.append( "/Author" );
6395 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine );
6396 0 : aLine.append( "\n" );
6397 : }
6398 0 : if( m_aContext.DocumentInfo.Subject.Len() )
6399 : {
6400 0 : aLine.append( "/Subject" );
6401 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine );
6402 0 : aLine.append( "\n" );
6403 : }
6404 0 : if( m_aContext.DocumentInfo.Keywords.Len() )
6405 : {
6406 0 : aLine.append( "/Keywords" );
6407 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine );
6408 0 : aLine.append( "\n" );
6409 : }
6410 0 : if( m_aContext.DocumentInfo.Creator.Len() )
6411 : {
6412 0 : aLine.append( "/Creator" );
6413 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine );
6414 0 : aLine.append( "\n" );
6415 : }
6416 0 : if( m_aContext.DocumentInfo.Producer.Len() )
6417 : {
6418 0 : aLine.append( "/Producer" );
6419 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine );
6420 0 : aLine.append( "\n" );
6421 : }
6422 :
6423 0 : aLine.append( "/CreationDate" );
6424 0 : appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine );
6425 0 : aLine.append( ">>\nendobj\n\n" );
6426 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6427 0 : nObject = 0;
6428 : }
6429 : else
6430 0 : nObject = 0;
6431 :
6432 0 : return nObject;
6433 : }
6434 :
6435 : //--->i56629
6436 : // Part of this function may be shared with method appendDest.
6437 : //
6438 0 : sal_Int32 PDFWriterImpl::emitNamedDestinations()
6439 : {
6440 0 : sal_Int32 nCount = m_aNamedDests.size();
6441 0 : if( nCount <= 0 )
6442 0 : return 0;//define internal error
6443 :
6444 : //get the object number for all the destinations
6445 0 : sal_Int32 nObject = createObject();
6446 :
6447 0 : if( updateObject( nObject ) )
6448 : {
6449 : //emit the dictionary
6450 0 : OStringBuffer aLine( 1024 );
6451 0 : aLine.append( nObject );
6452 : aLine.append( " 0 obj\n"
6453 0 : "<<" );
6454 :
6455 : sal_Int32 nDestID;
6456 0 : for( nDestID = 0; nDestID < nCount; nDestID++ )
6457 : {
6458 0 : const PDFNamedDest& rDest = m_aNamedDests[ nDestID ];
6459 : // In order to correctly function both under an Internet browser and
6460 : // directly with a reader (provided the reader has the feature) we
6461 : // need to set the name of the destination the same way it will be encoded
6462 : // in an Internet link
6463 : INetURLObject aLocalURL(
6464 0 : OUString( "http://ahost.ax" ) ); //dummy location, won't be used
6465 0 : aLocalURL.SetMark( rDest.m_aDestName );
6466 :
6467 0 : const rtl::OUString aName = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as
6468 : // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
6469 0 : const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
6470 :
6471 0 : aLine.append( '/' );
6472 0 : appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
6473 0 : aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
6474 : //maps the preceeding character properly
6475 0 : aLine.append( rDestPage.m_nPageObject );
6476 0 : aLine.append( " 0 R" );
6477 :
6478 0 : switch( rDest.m_eType )
6479 : {
6480 : case PDFWriter::XYZ:
6481 : default:
6482 0 : aLine.append( "/XYZ " );
6483 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
6484 0 : aLine.append( ' ' );
6485 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6486 0 : aLine.append( " 0" );
6487 0 : break;
6488 : case PDFWriter::Fit:
6489 0 : aLine.append( "/Fit" );
6490 0 : break;
6491 : case PDFWriter::FitRectangle:
6492 0 : aLine.append( "/FitR " );
6493 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
6494 0 : aLine.append( ' ' );
6495 0 : appendFixedInt( rDest.m_aRect.Top(), aLine );
6496 0 : aLine.append( ' ' );
6497 0 : appendFixedInt( rDest.m_aRect.Right(), aLine );
6498 0 : aLine.append( ' ' );
6499 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6500 0 : break;
6501 : case PDFWriter::FitHorizontal:
6502 0 : aLine.append( "/FitH " );
6503 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6504 0 : break;
6505 : case PDFWriter::FitVertical:
6506 0 : aLine.append( "/FitV " );
6507 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
6508 0 : break;
6509 : case PDFWriter::FitPageBoundingBox:
6510 0 : aLine.append( "/FitB" );
6511 0 : break;
6512 : case PDFWriter::FitPageBoundingBoxHorizontal:
6513 0 : aLine.append( "/FitBH " );
6514 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6515 0 : break;
6516 : case PDFWriter::FitPageBoundingBoxVertical:
6517 0 : aLine.append( "/FitBV " );
6518 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
6519 0 : break;
6520 : }
6521 0 : aLine.append( "]\n" );
6522 0 : }
6523 : //close
6524 :
6525 0 : aLine.append( ">>\nendobj\n\n" );
6526 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6527 0 : nObject = 0;
6528 : }
6529 : else
6530 0 : nObject = 0;
6531 :
6532 0 : return nObject;
6533 : }
6534 : //<--- i56629
6535 :
6536 : //--->i59651
6537 : // emits the output intent dictionary
6538 :
6539 0 : sal_Int32 PDFWriterImpl::emitOutputIntent()
6540 : {
6541 0 : if( !m_bIsPDF_A1 )
6542 0 : return 0;
6543 :
6544 : //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
6545 :
6546 0 : OStringBuffer aLine( 1024 );
6547 0 : sal_Int32 nICCObject = createObject();
6548 0 : sal_Int32 nStreamLengthObject = createObject();
6549 :
6550 0 : aLine.append( nICCObject );
6551 : // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
6552 0 : aLine.append( " 0 obj\n<</N 3/Length " );
6553 0 : aLine.append( nStreamLengthObject );
6554 0 : aLine.append( " 0 R" );
6555 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
6556 0 : aLine.append( "/Filter/FlateDecode" );
6557 : #endif
6558 0 : aLine.append( ">>\nstream\n" );
6559 0 : CHECK_RETURN( updateObject( nICCObject ) );
6560 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6561 : //get file position
6562 0 : sal_uInt64 nBeginStreamPos = 0;
6563 0 : osl_getFilePos( m_aFile, &nBeginStreamPos );
6564 0 : beginCompression();
6565 0 : checkAndEnableStreamEncryption( nICCObject );
6566 0 : cmsHPROFILE hProfile = cmsCreate_sRGBProfile();
6567 : //force ICC profile version 2.1
6568 0 : cmsSetProfileVersion(hProfile, 2.1);
6569 0 : cmsUInt32Number nBytesNeeded = 0;
6570 0 : cmsSaveProfileToMem(hProfile, NULL, &nBytesNeeded);
6571 0 : if (!nBytesNeeded)
6572 0 : return 0;
6573 0 : std::vector<unsigned char> xBuffer(nBytesNeeded);
6574 0 : cmsSaveProfileToMem(hProfile, &xBuffer[0], &nBytesNeeded);
6575 0 : cmsCloseProfile(hProfile);
6576 0 : sal_Int32 nStreamSize = writeBuffer( &xBuffer[0], (sal_Int32) xBuffer.size() );
6577 0 : disableStreamEncryption();
6578 0 : endCompression();
6579 0 : sal_uInt64 nEndStreamPos = 0;
6580 0 : osl_getFilePos( m_aFile, &nEndStreamPos );
6581 :
6582 0 : if( nStreamSize == 0 )
6583 0 : return 0;
6584 0 : if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6585 0 : return 0 ;
6586 0 : aLine.setLength( 0 );
6587 :
6588 : //emit the stream length object
6589 0 : CHECK_RETURN( updateObject( nStreamLengthObject ) );
6590 0 : aLine.setLength( 0 );
6591 0 : aLine.append( nStreamLengthObject );
6592 0 : aLine.append( " 0 obj\n" );
6593 0 : aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6594 0 : aLine.append( "\nendobj\n\n" );
6595 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6596 0 : aLine.setLength( 0 );
6597 :
6598 : //emit the OutputIntent dictionary
6599 0 : sal_Int32 nOIObject = createObject();
6600 0 : CHECK_RETURN( updateObject( nOIObject ) );
6601 0 : aLine.append( nOIObject );
6602 : aLine.append( " 0 obj\n"
6603 0 : "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
6604 :
6605 0 : rtl::OUString aComment( "sRGB IEC61966-2.1" );
6606 0 : appendLiteralStringEncrypt( aComment ,nOIObject, aLine );
6607 0 : aLine.append("/DestOutputProfile ");
6608 0 : aLine.append( nICCObject );
6609 0 : aLine.append( " 0 R>>\nendobj\n\n" );;
6610 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6611 :
6612 0 : return nOIObject;
6613 : }
6614 :
6615 : // formats the string for the XML stream
6616 0 : static void escapeStringXML( const rtl::OUString& rStr, rtl::OUString &rValue)
6617 : {
6618 0 : const sal_Unicode* pUni = rStr.getStr();
6619 0 : int nLen = rStr.getLength();
6620 0 : for( ; nLen; nLen--, pUni++ )
6621 : {
6622 0 : switch( *pUni )
6623 : {
6624 : case sal_Unicode('&'):
6625 0 : rValue += rtl::OUString( "&" );
6626 0 : break;
6627 : case sal_Unicode('<'):
6628 0 : rValue += rtl::OUString( "<" );
6629 0 : break;
6630 : case sal_Unicode('>'):
6631 0 : rValue += rtl::OUString( ">" );
6632 0 : break;
6633 : case sal_Unicode('\''):
6634 0 : rValue += rtl::OUString( "'" );
6635 0 : break;
6636 : case sal_Unicode('"'):
6637 0 : rValue += rtl::OUString( """ );
6638 0 : break;
6639 : default:
6640 0 : rValue += rtl::OUString( *pUni );
6641 0 : break;
6642 : }
6643 : }
6644 0 : }
6645 :
6646 : // emits the document metadata
6647 : //
6648 0 : sal_Int32 PDFWriterImpl::emitDocumentMetadata()
6649 : {
6650 0 : if( !m_bIsPDF_A1 )
6651 0 : return 0;
6652 :
6653 : //get the object number for all the destinations
6654 0 : sal_Int32 nObject = createObject();
6655 :
6656 0 : if( updateObject( nObject ) )
6657 : {
6658 : // the following string are written in UTF-8 unicode
6659 0 : OStringBuffer aMetadataStream( 8192 );
6660 :
6661 0 : aMetadataStream.append( "<?xpacket begin=\"" );
6662 : // this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used
6663 : // as a byte-order marker.
6664 0 : aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) );
6665 0 : aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
6666 0 : aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
6667 0 : aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
6668 : //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
6669 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
6670 0 : aMetadataStream.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
6671 0 : aMetadataStream.append( " <pdfaid:part>1</pdfaid:part>\n" );
6672 0 : aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" );
6673 0 : aMetadataStream.append( " </rdf:Description>\n" );
6674 : //... Dublin Core properties go here
6675 0 : if( m_aContext.DocumentInfo.Title.Len() ||
6676 0 : m_aContext.DocumentInfo.Author.Len() ||
6677 0 : m_aContext.DocumentInfo.Subject.Len() )
6678 : {
6679 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
6680 0 : aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
6681 0 : if( m_aContext.DocumentInfo.Title.Len() )
6682 : {
6683 : // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6684 0 : aMetadataStream.append( " <dc:title>\n" );
6685 0 : aMetadataStream.append( " <rdf:Alt>\n" );
6686 0 : aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
6687 0 : rtl::OUString aTitle;
6688 0 : escapeStringXML( m_aContext.DocumentInfo.Title, aTitle );
6689 0 : aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) );
6690 0 : aMetadataStream.append( "</rdf:li>\n" );
6691 0 : aMetadataStream.append( " </rdf:Alt>\n" );
6692 0 : aMetadataStream.append( " </dc:title>\n" );
6693 : }
6694 0 : if( m_aContext.DocumentInfo.Author.Len() )
6695 : {
6696 0 : aMetadataStream.append( " <dc:creator>\n" );
6697 0 : aMetadataStream.append( " <rdf:Seq>\n" );
6698 0 : aMetadataStream.append( " <rdf:li>" );
6699 0 : rtl::OUString aAuthor;
6700 0 : escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor );
6701 0 : aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) );
6702 0 : aMetadataStream.append( "</rdf:li>\n" );
6703 0 : aMetadataStream.append( " </rdf:Seq>\n" );
6704 0 : aMetadataStream.append( " </dc:creator>\n" );
6705 : }
6706 0 : if( m_aContext.DocumentInfo.Subject.Len() )
6707 : {
6708 : // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6709 0 : aMetadataStream.append( " <dc:description>\n" );
6710 0 : aMetadataStream.append( " <rdf:Alt>\n" );
6711 0 : aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
6712 0 : rtl::OUString aSubject;
6713 0 : escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject );
6714 0 : aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) );
6715 0 : aMetadataStream.append( "</rdf:li>\n" );
6716 0 : aMetadataStream.append( " </rdf:Alt>\n" );
6717 0 : aMetadataStream.append( " </dc:description>\n" );
6718 : }
6719 0 : aMetadataStream.append( " </rdf:Description>\n" );
6720 : }
6721 :
6722 : //... PDF properties go here
6723 0 : if( m_aContext.DocumentInfo.Producer.Len() ||
6724 0 : m_aContext.DocumentInfo.Keywords.Len() )
6725 : {
6726 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
6727 0 : aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
6728 0 : if( m_aContext.DocumentInfo.Producer.Len() )
6729 : {
6730 0 : aMetadataStream.append( " <pdf:Producer>" );
6731 0 : rtl::OUString aProducer;
6732 0 : escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer );
6733 0 : aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) );
6734 0 : aMetadataStream.append( "</pdf:Producer>\n" );
6735 : }
6736 0 : if( m_aContext.DocumentInfo.Keywords.Len() )
6737 : {
6738 0 : aMetadataStream.append( " <pdf:Keywords>" );
6739 0 : rtl::OUString aKeywords;
6740 0 : escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords );
6741 0 : aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) );
6742 0 : aMetadataStream.append( "</pdf:Keywords>\n" );
6743 : }
6744 0 : aMetadataStream.append( " </rdf:Description>\n" );
6745 : }
6746 :
6747 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
6748 0 : aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
6749 0 : if( m_aContext.DocumentInfo.Creator.Len() )
6750 : {
6751 0 : aMetadataStream.append( " <xmp:CreatorTool>" );
6752 0 : rtl::OUString aCreator;
6753 0 : escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator );
6754 0 : aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) );
6755 0 : aMetadataStream.append( "</xmp:CreatorTool>\n" );
6756 : }
6757 : //creation date
6758 0 : aMetadataStream.append( " <xmp:CreateDate>" );
6759 0 : aMetadataStream.append( m_aCreationMetaDateString );
6760 0 : aMetadataStream.append( "</xmp:CreateDate>\n" );
6761 :
6762 0 : aMetadataStream.append( " </rdf:Description>\n" );
6763 0 : aMetadataStream.append( " </rdf:RDF>\n" );
6764 0 : aMetadataStream.append( "</x:xmpmeta>\n" );
6765 :
6766 : //add the padding
6767 0 : for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ )
6768 : {
6769 0 : aMetadataStream.append( " " );
6770 0 : if( nSpaces % 100 == 0 )
6771 0 : aMetadataStream.append( "\n" );
6772 : }
6773 :
6774 0 : aMetadataStream.append( "<?xpacket end=\"w\"?>\n" );
6775 :
6776 0 : OStringBuffer aMetadataObj( 1024 );
6777 :
6778 0 : aMetadataObj.append( nObject );
6779 0 : aMetadataObj.append( " 0 obj\n" );
6780 :
6781 0 : aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
6782 :
6783 0 : aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() );
6784 0 : aMetadataObj.append( ">>\nstream\n" );
6785 0 : CHECK_RETURN( writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) );
6786 : //emit the stream
6787 0 : CHECK_RETURN( writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) );
6788 :
6789 0 : aMetadataObj.setLength( 0 );
6790 0 : aMetadataObj.append( "\nendstream\nendobj\n\n" );
6791 0 : if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
6792 0 : nObject = 0;
6793 : }
6794 : else
6795 0 : nObject = 0;
6796 :
6797 0 : return nObject;
6798 : }
6799 : //<---i59651
6800 :
6801 0 : bool PDFWriterImpl::emitTrailer()
6802 : {
6803 : // emit doc info
6804 0 : sal_Int32 nDocInfoObject = emitInfoDict( );
6805 :
6806 0 : sal_Int32 nSecObject = 0;
6807 :
6808 0 : if( m_aContext.Encryption.Encrypt() )
6809 : {
6810 : //emit the security information
6811 : //must be emitted as indirect dictionary object, since
6812 : //Acrobat Reader 5 works only with this kind of implementation
6813 0 : nSecObject = createObject();
6814 :
6815 0 : if( updateObject( nSecObject ) )
6816 : {
6817 0 : OStringBuffer aLineS( 1024 );
6818 0 : aLineS.append( nSecObject );
6819 : aLineS.append( " 0 obj\n"
6820 0 : "<</Filter/Standard/V " );
6821 : // check the version
6822 0 : if( m_aContext.Encryption.Security128bit )
6823 0 : aLineS.append( "2/Length 128/R 3" );
6824 : else
6825 0 : aLineS.append( "1/R 2" );
6826 :
6827 : // emit the owner password, must not be encrypted
6828 0 : aLineS.append( "/O(" );
6829 0 : appendLiteralString( (const sal_Char*)&m_aContext.Encryption.OValue[0], sal_Int32(m_aContext.Encryption.OValue.size()), aLineS );
6830 0 : aLineS.append( ")/U(" );
6831 0 : appendLiteralString( (const sal_Char*)&m_aContext.Encryption.UValue[0], sal_Int32(m_aContext.Encryption.UValue.size()), aLineS );
6832 0 : aLineS.append( ")/P " );// the permission set
6833 0 : aLineS.append( m_nAccessPermissions );
6834 0 : aLineS.append( ">>\nendobj\n\n" );
6835 0 : if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) )
6836 0 : nSecObject = 0;
6837 : }
6838 : else
6839 0 : nSecObject = 0;
6840 : }
6841 : // emit xref table
6842 : // remember start
6843 0 : sal_uInt64 nXRefOffset = 0;
6844 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) );
6845 0 : CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
6846 :
6847 0 : sal_Int32 nObjects = m_aObjects.size();
6848 0 : OStringBuffer aLine;
6849 0 : aLine.append( "0 " );
6850 0 : aLine.append( (sal_Int32)(nObjects+1) );
6851 0 : aLine.append( "\n" );
6852 0 : aLine.append( "0000000000 65535 f \n" );
6853 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6854 :
6855 0 : for( sal_Int32 i = 0; i < nObjects; i++ )
6856 : {
6857 0 : aLine.setLength( 0 );
6858 0 : OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] );
6859 0 : for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
6860 0 : aLine.append( '0' );
6861 0 : aLine.append( aOffset );
6862 0 : aLine.append( " 00000 n \n" );
6863 : DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" );
6864 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6865 0 : }
6866 :
6867 : // prepare document checksum
6868 0 : OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
6869 0 : if( m_aDocDigest )
6870 : {
6871 : sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
6872 0 : rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) );
6873 0 : for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ )
6874 0 : appendHex( nMD5Sum[i], aDocChecksum );
6875 : }
6876 : // document id set in setDocInfo method
6877 : // emit trailer
6878 0 : aLine.setLength( 0 );
6879 : aLine.append( "trailer\n"
6880 0 : "<</Size " );
6881 0 : aLine.append( (sal_Int32)(nObjects+1) );
6882 0 : aLine.append( "/Root " );
6883 0 : aLine.append( m_nCatalogObject );
6884 0 : aLine.append( " 0 R\n" );
6885 0 : if( nSecObject )
6886 : {
6887 0 : aLine.append( "/Encrypt ");
6888 0 : aLine.append( nSecObject );
6889 0 : aLine.append( " 0 R\n" );
6890 : }
6891 0 : if( nDocInfoObject )
6892 : {
6893 0 : aLine.append( "/Info " );
6894 0 : aLine.append( nDocInfoObject );
6895 0 : aLine.append( " 0 R\n" );
6896 : }
6897 0 : if( ! m_aContext.Encryption.DocumentIdentifier.empty() )
6898 : {
6899 0 : aLine.append( "/ID [ <" );
6900 0 : for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6901 0 : it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6902 : {
6903 0 : appendHex( sal_Int8(*it), aLine );
6904 : }
6905 : aLine.append( ">\n"
6906 0 : "<" );
6907 0 : for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6908 0 : it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6909 : {
6910 0 : appendHex( sal_Int8(*it), aLine );
6911 : }
6912 0 : aLine.append( "> ]\n" );
6913 : }
6914 0 : if( aDocChecksum.getLength() )
6915 : {
6916 0 : aLine.append( "/DocChecksum /" );
6917 0 : aLine.append( aDocChecksum.makeStringAndClear() );
6918 0 : aLine.append( "\n" );
6919 : }
6920 0 : if( m_aAdditionalStreams.size() > 0 )
6921 : {
6922 0 : aLine.append( "/AdditionalStreams [" );
6923 0 : for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ )
6924 : {
6925 0 : aLine.append( "/" );
6926 0 : appendName( m_aAdditionalStreams[i].m_aMimeType, aLine );
6927 0 : aLine.append( " " );
6928 0 : aLine.append( m_aAdditionalStreams[i].m_nStreamObject );
6929 0 : aLine.append( " 0 R\n" );
6930 : }
6931 0 : aLine.append( "]\n" );
6932 : }
6933 : aLine.append( ">>\n"
6934 0 : "startxref\n" );
6935 0 : aLine.append( (sal_Int64)nXRefOffset );
6936 : aLine.append( "\n"
6937 0 : "%%EOF\n" );
6938 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6939 :
6940 0 : return true;
6941 : }
6942 :
6943 : struct AnnotationSortEntry
6944 : {
6945 : sal_Int32 nTabOrder;
6946 : sal_Int32 nObject;
6947 : sal_Int32 nWidgetIndex;
6948 :
6949 0 : AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
6950 : nTabOrder( nTab ),
6951 : nObject( nObj ),
6952 0 : nWidgetIndex( nI )
6953 0 : {}
6954 : };
6955 :
6956 0 : struct AnnotSortContainer
6957 : {
6958 : std::set< sal_Int32 > aObjects;
6959 : std::vector< AnnotationSortEntry > aSortedAnnots;
6960 : };
6961 :
6962 : struct AnnotSorterLess
6963 : {
6964 : std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets;
6965 :
6966 0 : AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {}
6967 :
6968 0 : bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
6969 : {
6970 0 : if( rLeft.nTabOrder < rRight.nTabOrder )
6971 0 : return true;
6972 0 : if( rRight.nTabOrder < rLeft.nTabOrder )
6973 0 : return false;
6974 0 : if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
6975 0 : return false;
6976 0 : if( rRight.nWidgetIndex < 0 )
6977 0 : return true;
6978 0 : if( rLeft.nWidgetIndex < 0 )
6979 0 : return false;
6980 : // remember: widget rects are in PDF coordinates, so they are ordered down up
6981 0 : if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
6982 0 : m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
6983 0 : return true;
6984 0 : if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
6985 0 : m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
6986 0 : return false;
6987 0 : if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
6988 0 : m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
6989 0 : return true;
6990 0 : return false;
6991 : }
6992 : };
6993 :
6994 0 : void PDFWriterImpl::sortWidgets()
6995 : {
6996 : // sort widget annotations on each page as per their
6997 : // TabOrder attribute
6998 0 : boost::unordered_map< sal_Int32, AnnotSortContainer > sorted;
6999 0 : int nWidgets = m_aWidgets.size();
7000 0 : for( int nW = 0; nW < nWidgets; nW++ )
7001 : {
7002 0 : const PDFWidget& rWidget = m_aWidgets[nW];
7003 0 : if( rWidget.m_nPage >= 0 )
7004 : {
7005 0 : AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
7006 : // optimize vector allocation
7007 0 : if( rCont.aSortedAnnots.empty() )
7008 0 : rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
7009 : // insert widget to tab sorter
7010 : // RadioButtons are not page annotations, only their individual check boxes are
7011 0 : if( rWidget.m_eType != PDFWriter::RadioButton )
7012 : {
7013 0 : rCont.aObjects.insert( rWidget.m_nObject );
7014 0 : rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) );
7015 : }
7016 : }
7017 : }
7018 0 : for( boost::unordered_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it )
7019 : {
7020 : // append entries for non widget annotations
7021 0 : PDFPage& rPage = m_aPages[ it->first ];
7022 0 : unsigned int nAnnots = rPage.m_aAnnotations.size();
7023 0 : for( unsigned int nA = 0; nA < nAnnots; nA++ )
7024 0 : if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end())
7025 0 : it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) );
7026 :
7027 0 : AnnotSorterLess aLess( m_aWidgets );
7028 0 : std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess );
7029 : // sanity check
7030 0 : if( it->second.aSortedAnnots.size() == nAnnots)
7031 : {
7032 0 : for( unsigned int nA = 0; nA < nAnnots; nA++ )
7033 0 : rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject;
7034 : }
7035 : else
7036 : {
7037 : DBG_ASSERT( 0, "wrong number of sorted annotations" );
7038 : #if OSL_DEBUG_LEVEL > 0
7039 : fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n"
7040 : " %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots );
7041 : #endif
7042 : }
7043 0 : }
7044 :
7045 : // FIXME: implement tab order in structure tree for PDF 1.5
7046 0 : }
7047 :
7048 : namespace vcl {
7049 : class PDFStreamIf :
7050 : public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream >
7051 : {
7052 : PDFWriterImpl* m_pWriter;
7053 : bool m_bWrite;
7054 : public:
7055 0 : PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
7056 : virtual ~PDFStreamIf();
7057 :
7058 : virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw();
7059 : virtual void SAL_CALL flush() throw();
7060 : virtual void SAL_CALL closeOutput() throw();
7061 : };
7062 : }
7063 :
7064 0 : PDFStreamIf::~PDFStreamIf()
7065 : {
7066 0 : }
7067 :
7068 0 : void SAL_CALL PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw()
7069 : {
7070 0 : if( m_bWrite && aData.getLength() )
7071 : {
7072 0 : sal_Int32 nBytes = aData.getLength();
7073 0 : m_pWriter->writeBuffer( aData.getConstArray(), nBytes );
7074 : }
7075 0 : }
7076 :
7077 0 : void SAL_CALL PDFStreamIf::flush() throw()
7078 : {
7079 0 : }
7080 :
7081 0 : void SAL_CALL PDFStreamIf::closeOutput() throw()
7082 : {
7083 0 : m_bWrite = false;
7084 0 : }
7085 :
7086 0 : bool PDFWriterImpl::emitAdditionalStreams()
7087 : {
7088 0 : unsigned int nStreams = m_aAdditionalStreams.size();
7089 0 : for( unsigned int i = 0; i < nStreams; i++ )
7090 : {
7091 0 : PDFAddStream& rStream = m_aAdditionalStreams[i];
7092 0 : rStream.m_nStreamObject = createObject();
7093 0 : sal_Int32 nSizeObject = createObject();
7094 :
7095 0 : if( ! updateObject( rStream.m_nStreamObject ) )
7096 0 : return false;
7097 :
7098 0 : OStringBuffer aLine;
7099 0 : aLine.append( rStream.m_nStreamObject );
7100 0 : aLine.append( " 0 obj\n<</Length " );
7101 0 : aLine.append( nSizeObject );
7102 0 : aLine.append( " 0 R" );
7103 0 : if( rStream.m_bCompress )
7104 0 : aLine.append( "/Filter/FlateDecode" );
7105 0 : aLine.append( ">>\nstream\n" );
7106 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
7107 0 : return false;
7108 0 : sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0;
7109 0 : if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) )
7110 : {
7111 0 : osl_closeFile( m_aFile );
7112 0 : m_bOpen = false;
7113 : }
7114 0 : if( rStream.m_bCompress )
7115 0 : beginCompression();
7116 :
7117 0 : checkAndEnableStreamEncryption( rStream.m_nStreamObject );
7118 0 : com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) );
7119 0 : rStream.m_pStream->write( xStream );
7120 0 : xStream.clear();
7121 0 : delete rStream.m_pStream;
7122 0 : rStream.m_pStream = NULL;
7123 0 : disableStreamEncryption();
7124 :
7125 0 : if( rStream.m_bCompress )
7126 0 : endCompression();
7127 :
7128 0 : if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) )
7129 : {
7130 0 : osl_closeFile( m_aFile );
7131 0 : m_bOpen = false;
7132 0 : return false;
7133 : }
7134 0 : if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
7135 0 : return false ;
7136 : // emit stream length object
7137 0 : if( ! updateObject( nSizeObject ) )
7138 0 : return false;
7139 0 : aLine.setLength( 0 );
7140 0 : aLine.append( nSizeObject );
7141 0 : aLine.append( " 0 obj\n" );
7142 0 : aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
7143 0 : aLine.append( "\nendobj\n\n" );
7144 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
7145 0 : return false;
7146 0 : }
7147 0 : return true;
7148 : }
7149 :
7150 0 : bool PDFWriterImpl::emit()
7151 : {
7152 0 : endPage();
7153 :
7154 : // resort structure tree and annotations if necessary
7155 : // needed for widget tab order
7156 0 : sortWidgets();
7157 :
7158 : #if !defined(ANDROID) && !defined(IOS)
7159 0 : if( m_aContext.SignPDF )
7160 : {
7161 : // sign the document
7162 0 : PDFWriter::SignatureWidget aSignature;
7163 0 : aSignature.Name = OUString("Signature1");
7164 0 : createControl( aSignature, 0 );
7165 : }
7166 : #endif
7167 :
7168 : // emit additional streams
7169 0 : CHECK_RETURN( emitAdditionalStreams() );
7170 :
7171 : // emit catalog
7172 0 : CHECK_RETURN( emitCatalog() );
7173 :
7174 : #if !defined(ANDROID) && !defined(IOS)
7175 0 : if (m_nSignatureObject != -1) // if document is signed, emit sigdict
7176 0 : CHECK_RETURN( emitSignature() );
7177 : #endif
7178 :
7179 : // emit trailer
7180 0 : CHECK_RETURN( emitTrailer() );
7181 :
7182 : #if !defined(ANDROID) && !defined(IOS)
7183 0 : if (m_nSignatureObject != -1) // finalize the signature
7184 0 : CHECK_RETURN( finalizeSignature() );
7185 : #endif
7186 :
7187 0 : osl_closeFile( m_aFile );
7188 0 : m_bOpen = false;
7189 :
7190 0 : return true;
7191 : }
7192 :
7193 0 : std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors()
7194 : {
7195 0 : return m_aErrors;
7196 : }
7197 :
7198 0 : sal_Int32 PDFWriterImpl::getSystemFont( const Font& i_rFont )
7199 : {
7200 0 : getReferenceDevice()->Push();
7201 0 : getReferenceDevice()->SetFont( i_rFont );
7202 0 : getReferenceDevice()->ImplNewFont();
7203 :
7204 0 : const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
7205 0 : sal_Int32 nFontID = 0;
7206 0 : FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont );
7207 0 : if( it != m_aSystemFonts.end() )
7208 0 : nFontID = it->second.m_nNormalFontID;
7209 : else
7210 : {
7211 0 : nFontID = m_nNextFID++;
7212 0 : m_aSystemFonts[ pDevFont ] = EmbedFont();
7213 0 : m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID;
7214 : }
7215 :
7216 0 : getReferenceDevice()->Pop();
7217 0 : getReferenceDevice()->ImplNewFont();
7218 :
7219 0 : return nFontID;
7220 : }
7221 :
7222 0 : void PDFWriterImpl::registerGlyphs( int nGlyphs,
7223 : sal_GlyphId* pGlyphs,
7224 : sal_Int32* pGlyphWidths,
7225 : sal_Ucs* pUnicodes,
7226 : sal_Int32* pUnicodesPerGlyph,
7227 : sal_uInt8* pMappedGlyphs,
7228 : sal_Int32* pMappedFontObjects,
7229 : const PhysicalFontFace* pFallbackFonts[] )
7230 : {
7231 0 : const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
7232 0 : sal_Ucs* pCurUnicode = pUnicodes;
7233 0 : for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ )
7234 : {
7235 0 : const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB);
7236 0 : const PhysicalFontFace* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont;
7237 :
7238 0 : if( isBuiltinFont( pCurrentFont ) )
7239 : {
7240 0 : sal_Int32 nFontID = 0;
7241 0 : FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
7242 0 : if( it != m_aEmbeddedFonts.end() )
7243 0 : nFontID = it->second.m_nNormalFontID;
7244 : else
7245 : {
7246 0 : nFontID = m_nNextFID++;
7247 0 : m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
7248 0 : m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
7249 : }
7250 :
7251 0 : pGlyphWidths[ i ] = 0;
7252 0 : pMappedGlyphs[ i ] = sal::static_int_cast<sal_Int8>( nFontGlyphId );
7253 0 : pMappedFontObjects[ i ] = nFontID;
7254 0 : const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pCurrentFont );
7255 0 : if( pFD )
7256 : {
7257 0 : const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
7258 0 : pGlyphWidths[i] = pBuiltinFont->m_aWidths[ nFontGlyphId & 0x00ff ];
7259 : }
7260 : }
7261 0 : else if( pCurrentFont->mbSubsettable )
7262 : {
7263 0 : FontSubset& rSubset = m_aSubsets[ pCurrentFont ];
7264 : // search for font specific glyphID
7265 0 : FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId );
7266 0 : if( it != rSubset.m_aMapping.end() )
7267 : {
7268 0 : pMappedFontObjects[i] = it->second.m_nFontID;
7269 0 : pMappedGlyphs[i] = it->second.m_nSubsetGlyphID;
7270 : }
7271 : else
7272 : {
7273 : // create new subset if necessary
7274 0 : if( rSubset.m_aSubsets.empty()
7275 0 : || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
7276 : {
7277 0 : rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) );
7278 : }
7279 :
7280 : // copy font id
7281 0 : pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID;
7282 : // create new glyph in subset
7283 0 : sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
7284 0 : pMappedGlyphs[i] = nNewId;
7285 :
7286 : // add new glyph to emitted font subset
7287 0 : GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
7288 0 : rNewGlyphEmit.setGlyphId( nNewId );
7289 0 : for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ )
7290 0 : rNewGlyphEmit.addCode( pCurUnicode[n] );
7291 :
7292 : // add new glyph to font mapping
7293 0 : Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
7294 0 : rNewGlyph.m_nFontID = pMappedFontObjects[i];
7295 0 : rNewGlyph.m_nSubsetGlyphID = nNewId;
7296 : }
7297 0 : getReferenceDevice()->ImplGetGraphics();
7298 0 : const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0);
7299 0 : pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont,
7300 : nFontGlyphId,
7301 : bVertical,
7302 0 : m_pReferenceDevice->mpGraphics );
7303 : }
7304 0 : else if( pCurrentFont->IsEmbeddable() )
7305 : {
7306 0 : sal_Int32 nFontID = 0;
7307 0 : FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
7308 0 : if( it != m_aEmbeddedFonts.end() )
7309 0 : nFontID = it->second.m_nNormalFontID;
7310 : else
7311 : {
7312 0 : nFontID = m_nNextFID++;
7313 0 : m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
7314 0 : m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
7315 : }
7316 0 : EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont];
7317 :
7318 0 : const Ucs2SIntMap* pEncoding = NULL;
7319 0 : const Ucs2OStrMap* pNonEncoded = NULL;
7320 0 : getReferenceDevice()->ImplGetGraphics();
7321 0 : pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded );
7322 :
7323 0 : Ucs2SIntMap::const_iterator enc_it;
7324 0 : Ucs2OStrMap::const_iterator nonenc_it;
7325 :
7326 0 : sal_Int32 nCurFontID = nFontID;
7327 0 : sal_Ucs cChar = *pCurUnicode;
7328 0 : if( pEncoding )
7329 : {
7330 0 : enc_it = pEncoding->find( cChar );
7331 0 : if( enc_it != pEncoding->end() && enc_it->second > 0 )
7332 : {
7333 : DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" );
7334 0 : cChar = (sal_Ucs)enc_it->second;
7335 : }
7336 0 : else if( (enc_it == pEncoding->end() || enc_it->second == -1) &&
7337 : pNonEncoded &&
7338 0 : (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() )
7339 : {
7340 0 : nCurFontID = 0;
7341 : // find non encoded glyph
7342 0 : for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it )
7343 : {
7344 0 : if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() )
7345 : {
7346 0 : nCurFontID = nec_it->m_nFontID;
7347 0 : cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ];
7348 0 : break;
7349 : }
7350 : }
7351 0 : if( nCurFontID == 0 ) // new nonencoded glyph
7352 : {
7353 0 : if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 )
7354 : {
7355 0 : rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() );
7356 0 : rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++;
7357 : }
7358 0 : EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back();
7359 0 : rEncoding.m_aEncVector.push_back( EmbedCode() );
7360 0 : rEncoding.m_aEncVector.back().m_aUnicode = cChar;
7361 0 : rEncoding.m_aEncVector.back().m_aName = nonenc_it->second;
7362 0 : rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1);
7363 0 : nCurFontID = rEncoding.m_nFontID;
7364 0 : cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ];
7365 : }
7366 : }
7367 : else
7368 0 : pEncoding = NULL;
7369 : }
7370 0 : if( ! pEncoding )
7371 : {
7372 0 : if( cChar & 0xff00 )
7373 : {
7374 : // some characters can be used by conversion
7375 0 : if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area
7376 0 : cChar -= 0xf000;
7377 : else
7378 : {
7379 0 : rtl::OString aChar(&cChar, 1, RTL_TEXTENCODING_MS_1252);
7380 0 : cChar = ((sal_Ucs)aChar[0]) & 0x00ff;
7381 : }
7382 : }
7383 : }
7384 :
7385 0 : pMappedGlyphs[ i ] = (sal_Int8)cChar;
7386 0 : pMappedFontObjects[ i ] = nCurFontID;
7387 0 : pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont,
7388 : (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR,
7389 : false,
7390 0 : m_pReferenceDevice->mpGraphics );
7391 : }
7392 : }
7393 0 : }
7394 :
7395 0 : void PDFWriterImpl::drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines )
7396 : {
7397 0 : push( PUSH_ALL );
7398 :
7399 0 : FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief();
7400 :
7401 0 : Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor();
7402 0 : Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7403 0 : Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7404 0 : Color aReliefColor( COL_LIGHTGRAY );
7405 0 : if( aTextColor == COL_BLACK )
7406 0 : aTextColor = Color( COL_WHITE );
7407 0 : if( aTextLineColor == COL_BLACK )
7408 0 : aTextLineColor = Color( COL_WHITE );
7409 0 : if( aOverlineColor == COL_BLACK )
7410 0 : aOverlineColor = Color( COL_WHITE );
7411 0 : if( aTextColor == COL_WHITE )
7412 0 : aReliefColor = Color( COL_BLACK );
7413 :
7414 0 : Font aSetFont = m_aCurrentPDFState.m_aFont;
7415 0 : aSetFont.SetRelief( RELIEF_NONE );
7416 0 : aSetFont.SetShadow( sal_False );
7417 :
7418 0 : aSetFont.SetColor( aReliefColor );
7419 0 : setTextLineColor( aReliefColor );
7420 0 : setOverlineColor( aReliefColor );
7421 0 : setFont( aSetFont );
7422 0 : long nOff = 1 + getReferenceDevice()->mnDPIX/300;
7423 0 : if( eRelief == RELIEF_ENGRAVED )
7424 0 : nOff = -nOff;
7425 :
7426 0 : rLayout.DrawOffset() += Point( nOff, nOff );
7427 0 : updateGraphicsState();
7428 0 : drawLayout( rLayout, rText, bTextLines );
7429 :
7430 0 : rLayout.DrawOffset() -= Point( nOff, nOff );
7431 0 : setTextLineColor( aTextLineColor );
7432 0 : setOverlineColor( aOverlineColor );
7433 0 : aSetFont.SetColor( aTextColor );
7434 0 : setFont( aSetFont );
7435 0 : updateGraphicsState();
7436 0 : drawLayout( rLayout, rText, bTextLines );
7437 :
7438 : // clean up the mess
7439 0 : pop();
7440 0 : }
7441 :
7442 0 : void PDFWriterImpl::drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines )
7443 : {
7444 0 : Font aSaveFont = m_aCurrentPDFState.m_aFont;
7445 0 : Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7446 0 : Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7447 :
7448 0 : Font& rFont = m_aCurrentPDFState.m_aFont;
7449 0 : if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 )
7450 0 : rFont.SetColor( Color( COL_LIGHTGRAY ) );
7451 : else
7452 0 : rFont.SetColor( Color( COL_BLACK ) );
7453 0 : rFont.SetShadow( sal_False );
7454 0 : rFont.SetOutline( sal_False );
7455 0 : setFont( rFont );
7456 0 : setTextLineColor( rFont.GetColor() );
7457 0 : setOverlineColor( rFont.GetColor() );
7458 0 : updateGraphicsState();
7459 :
7460 0 : long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24);
7461 0 : if( rFont.IsOutline() )
7462 0 : nOff++;
7463 0 : rLayout.DrawBase() += Point( nOff, nOff );
7464 0 : drawLayout( rLayout, rText, bTextLines );
7465 0 : rLayout.DrawBase() -= Point( nOff, nOff );
7466 :
7467 0 : setFont( aSaveFont );
7468 0 : setTextLineColor( aSaveTextLineColor );
7469 0 : setOverlineColor( aSaveOverlineColor );
7470 0 : updateGraphicsState();
7471 0 : }
7472 :
7473 0 : void PDFWriterImpl::drawVerticalGlyphs(
7474 : const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7475 : OStringBuffer& rLine,
7476 : const Point& rAlignOffset,
7477 : const Matrix3& rRotScale,
7478 : double fAngle,
7479 : double fXScale,
7480 : double fSkew,
7481 : sal_Int32 nFontHeight )
7482 : {
7483 0 : long nXOffset = 0;
7484 0 : Point aCurPos( rGlyphs[0].m_aPos );
7485 0 : aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7486 0 : aCurPos += rAlignOffset;
7487 0 : for( size_t i = 0; i < rGlyphs.size(); i++ )
7488 : {
7489 : // have to emit each glyph on its own
7490 0 : double fDeltaAngle = 0.0;
7491 0 : double fYScale = 1.0;
7492 0 : double fTempXScale = fXScale;
7493 0 : double fSkewB = fSkew;
7494 0 : double fSkewA = 0.0;
7495 :
7496 0 : Point aDeltaPos;
7497 0 : if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL )
7498 : {
7499 0 : fDeltaAngle = M_PI/2.0;
7500 0 : aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent();
7501 0 : aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale);
7502 0 : fYScale = fXScale;
7503 0 : fTempXScale = 1.0;
7504 0 : fSkewA = -fSkewB;
7505 0 : fSkewB = 0.0;
7506 : }
7507 0 : else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR )
7508 : {
7509 0 : fDeltaAngle = -M_PI/2.0;
7510 0 : aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale);
7511 0 : aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent();
7512 0 : fYScale = fXScale;
7513 0 : fTempXScale = 1.0;
7514 0 : fSkewA = fSkewB;
7515 0 : fSkewB = 0.0;
7516 : }
7517 0 : aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) );
7518 0 : if( i < rGlyphs.size()-1 )
7519 0 : nXOffset += rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
7520 0 : if( ! rGlyphs[i].m_nGlyphId )
7521 0 : continue;
7522 :
7523 0 : aDeltaPos = rRotScale.transform( aDeltaPos );
7524 :
7525 0 : Matrix3 aMat;
7526 0 : if( fSkewB != 0.0 || fSkewA != 0.0 )
7527 0 : aMat.skew( fSkewA, fSkewB );
7528 0 : aMat.scale( fTempXScale, fYScale );
7529 0 : aMat.rotate( fAngle+fDeltaAngle );
7530 0 : aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
7531 0 : aMat.append( m_aPages.back(), rLine );
7532 0 : rLine.append( " Tm" );
7533 0 : if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
7534 : {
7535 0 : rLine.append( " /F" );
7536 0 : rLine.append( rGlyphs[i].m_nMappedFontId );
7537 0 : rLine.append( ' ' );
7538 0 : m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7539 0 : rLine.append( " Tf" );
7540 : }
7541 0 : rLine.append( "<" );
7542 0 : appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
7543 0 : rLine.append( ">Tj\n" );
7544 0 : }
7545 0 : }
7546 :
7547 0 : void PDFWriterImpl::drawHorizontalGlyphs(
7548 : const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7549 : OStringBuffer& rLine,
7550 : const Point& rAlignOffset,
7551 : double fAngle,
7552 : double fXScale,
7553 : double fSkew,
7554 : sal_Int32 nFontHeight,
7555 : sal_Int32 nPixelFontHeight
7556 : )
7557 : {
7558 : // horizontal (= normal) case
7559 :
7560 : // fill in run end indices
7561 : // end is marked by index of the first glyph of the next run
7562 : // a run is marked by same mapped font id and same Y position
7563 0 : std::vector< sal_uInt32 > aRunEnds;
7564 0 : aRunEnds.reserve( rGlyphs.size() );
7565 0 : for( size_t i = 1; i < rGlyphs.size(); i++ )
7566 : {
7567 0 : if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
7568 0 : rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
7569 : {
7570 0 : aRunEnds.push_back(i);
7571 : }
7572 : }
7573 : // last run ends at last glyph
7574 0 : aRunEnds.push_back( rGlyphs.size() );
7575 :
7576 : // loop over runs of the same font
7577 0 : sal_uInt32 nBeginRun = 0;
7578 0 : for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
7579 : {
7580 : // setup text matrix
7581 0 : Point aCurPos = rGlyphs[nBeginRun].m_aPos;
7582 : // back transformation to current coordinate system
7583 0 : aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7584 0 : aCurPos += rAlignOffset;
7585 : // the first run can be set with "Td" operator
7586 : // subsequent use of that operator would move
7587 : // the texline matrix relative to what was set before
7588 : // making use of that would drive us into rounding issues
7589 0 : Matrix3 aMat;
7590 0 : if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
7591 : {
7592 0 : m_aPages.back().appendPoint( aCurPos, rLine, false );
7593 0 : rLine.append( " Td " );
7594 : }
7595 : else
7596 : {
7597 0 : if( fSkew != 0.0 )
7598 0 : aMat.skew( 0.0, fSkew );
7599 0 : aMat.scale( fXScale, 1.0 );
7600 0 : aMat.rotate( fAngle );
7601 0 : aMat.translate( aCurPos.X(), aCurPos.Y() );
7602 0 : aMat.append( m_aPages.back(), rLine );
7603 0 : rLine.append( " Tm\n" );
7604 : }
7605 : // set up correct font
7606 0 : rLine.append( "/F" );
7607 0 : rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
7608 0 : rLine.append( ' ' );
7609 0 : m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7610 0 : rLine.append( " Tf" );
7611 :
7612 : // output glyphs using Tj or TJ
7613 0 : OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
7614 0 : aKernedLine.append( "[<" );
7615 0 : aUnkernedLine.append( '<' );
7616 0 : appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
7617 0 : appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
7618 :
7619 0 : aMat.invert();
7620 0 : bool bNeedKern = false;
7621 0 : for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
7622 : {
7623 0 : appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
7624 : // check if default glyph positioning is sufficient
7625 0 : const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
7626 0 : const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
7627 0 : double fAdvance = aThisPos.X() - aPrevPos.X();
7628 0 : fAdvance *= 1000.0 / nPixelFontHeight;
7629 0 : const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5);
7630 0 : if( nAdjustment != 0 )
7631 : {
7632 : // apply individual glyph positioning
7633 0 : bNeedKern = true;
7634 0 : aKernedLine.append( ">" );
7635 0 : aKernedLine.append( nAdjustment );
7636 0 : aKernedLine.append( "<" );
7637 : }
7638 0 : appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
7639 : }
7640 0 : aKernedLine.append( ">]TJ\n" );
7641 0 : aUnkernedLine.append( ">Tj\n" );
7642 : rLine.append(
7643 0 : (bNeedKern ? aKernedLine : aUnkernedLine).makeStringAndClear() );
7644 :
7645 : // set beginning of next run
7646 0 : nBeginRun = aRunEnds[nRun];
7647 0 : }
7648 0 : }
7649 :
7650 0 : void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines )
7651 : {
7652 : // relief takes precedence over shadow (see outdev3.cxx)
7653 0 : if( m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE )
7654 : {
7655 0 : drawRelief( rLayout, rText, bTextLines );
7656 0 : return;
7657 : }
7658 0 : else if( m_aCurrentPDFState.m_aFont.IsShadow() )
7659 0 : drawShadow( rLayout, rText, bTextLines );
7660 :
7661 0 : OStringBuffer aLine( 512 );
7662 :
7663 0 : const int nMaxGlyphs = 256;
7664 :
7665 : sal_GlyphId pGlyphs[nMaxGlyphs];
7666 : sal_Int32 pGlyphWidths[nMaxGlyphs];
7667 : sal_uInt8 pMappedGlyphs[nMaxGlyphs];
7668 : sal_Int32 pMappedFontObjects[nMaxGlyphs];
7669 0 : std::vector<sal_Ucs> aUnicodes;
7670 0 : aUnicodes.reserve( nMaxGlyphs );
7671 : sal_Int32 pUnicodesPerGlyph[nMaxGlyphs];
7672 : int pCharPosAry[nMaxGlyphs];
7673 : sal_Int32 nAdvanceWidths[nMaxGlyphs];
7674 : const PhysicalFontFace* pFallbackFonts[nMaxGlyphs];
7675 0 : bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
7676 : int nGlyphs;
7677 0 : int nIndex = 0;
7678 0 : int nMinCharPos = 0, nMaxCharPos = rText.Len()-1;
7679 0 : double fXScale = 1.0;
7680 0 : double fSkew = 0.0;
7681 0 : sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight;
7682 0 : TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
7683 :
7684 : // transform font height back to current units
7685 : // note: the layout calculates in outdevs device pixel !!
7686 0 : sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
7687 0 : if( m_aCurrentPDFState.m_aFont.GetWidth() )
7688 : {
7689 0 : Font aFont( m_aCurrentPDFState.m_aFont );
7690 0 : aFont.SetWidth( 0 );
7691 0 : FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
7692 0 : if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() )
7693 : {
7694 : fXScale =
7695 0 : (double)m_aCurrentPDFState.m_aFont.GetWidth() /
7696 0 : (double)aMetric.GetWidth();
7697 : }
7698 : // force state before GetFontMetric
7699 0 : m_pReferenceDevice->ImplNewFont();
7700 : }
7701 :
7702 : // perform artificial italics if necessary
7703 0 : if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
7704 0 : m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
7705 0 : !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL ||
7706 0 : m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE )
7707 : )
7708 : {
7709 0 : fSkew = M_PI/12.0;
7710 : }
7711 :
7712 : // if the mapmode is distorted we need to adjust for that also
7713 0 : if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
7714 : {
7715 0 : fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
7716 : }
7717 :
7718 0 : int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
7719 : // normalize angles
7720 0 : while( nAngle < 0 )
7721 0 : nAngle += 3600;
7722 0 : nAngle = nAngle % 3600;
7723 0 : double fAngle = (double)nAngle * M_PI / 1800.0;
7724 :
7725 0 : Matrix3 aRotScale;
7726 0 : aRotScale.scale( fXScale, 1.0 );
7727 0 : if( fAngle != 0.0 )
7728 0 : aRotScale.rotate( -fAngle );
7729 :
7730 0 : bool bPop = false;
7731 0 : bool bABold = false;
7732 : // artificial bold necessary ?
7733 0 : if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
7734 0 : m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
7735 : {
7736 0 : if( ! bPop )
7737 0 : aLine.append( "q " );
7738 0 : bPop = true;
7739 0 : bABold = true;
7740 : }
7741 : // setup text colors (if necessary)
7742 0 : Color aStrokeColor( COL_TRANSPARENT );
7743 0 : Color aNonStrokeColor( COL_TRANSPARENT );
7744 :
7745 0 : if( m_aCurrentPDFState.m_aFont.IsOutline() )
7746 : {
7747 0 : aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7748 0 : aNonStrokeColor = Color( COL_WHITE );
7749 : }
7750 : else
7751 0 : aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7752 0 : if( bABold )
7753 0 : aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7754 :
7755 0 : if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
7756 : {
7757 0 : if( ! bPop )
7758 0 : aLine.append( "q " );
7759 0 : bPop = true;
7760 0 : appendStrokingColor( aStrokeColor, aLine );
7761 0 : aLine.append( "\n" );
7762 : }
7763 0 : if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
7764 : {
7765 0 : if( ! bPop )
7766 0 : aLine.append( "q " );
7767 0 : bPop = true;
7768 0 : appendNonStrokingColor( aNonStrokeColor, aLine );
7769 0 : aLine.append( "\n" );
7770 : }
7771 :
7772 : // begin text object
7773 0 : aLine.append( "BT\n" );
7774 : // outline attribute ?
7775 0 : if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
7776 : {
7777 : // set correct text mode, set stroke width
7778 0 : aLine.append( "2 Tr " ); // fill, then stroke
7779 :
7780 0 : if( m_aCurrentPDFState.m_aFont.IsOutline() )
7781 : {
7782 : // unclear what to do in case of outline and artificial bold
7783 : // for the time being outline wins
7784 0 : aLine.append( "0.25 w \n" );
7785 : }
7786 : else
7787 : {
7788 0 : double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0;
7789 0 : m_aPages.back().appendMappedLength( fW, aLine );
7790 0 : aLine.append ( " w\n" );
7791 : }
7792 : }
7793 :
7794 0 : FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
7795 :
7796 : // collect the glyphs into a single array
7797 0 : const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
7798 0 : std::vector< PDFGlyph > aGlyphs;
7799 0 : aGlyphs.reserve( nTmpMaxGlyphs );
7800 : // first get all the glyphs and register them; coordinates still in Pixel
7801 0 : Point aGNGlyphPos;
7802 0 : while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 )
7803 : {
7804 0 : aUnicodes.clear();
7805 0 : for( int i = 0; i < nGlyphs; i++ )
7806 : {
7807 0 : pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] );
7808 :
7809 : // default case: 1 glyph is one unicode
7810 0 : pUnicodesPerGlyph[i] = 1;
7811 0 : if( (pGlyphs[i] & GF_ISCHAR) )
7812 : {
7813 0 : aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) );
7814 : }
7815 0 : else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
7816 : {
7817 0 : int nChars = 1;
7818 0 : aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) );
7819 0 : pUnicodesPerGlyph[i] = 1;
7820 : // try to handle ligatures and such
7821 0 : if( i < nGlyphs-1 )
7822 : {
7823 0 : nChars = pCharPosAry[i+1] - pCharPosAry[i];
7824 : // #i115618# fix for simple RTL+CTL cases
7825 : // TODO: sanitize for RTL ligatures, more complex CTL, etc.
7826 0 : if( nChars < 0 )
7827 0 : nChars = -nChars;
7828 0 : else if( nChars == 0 )
7829 0 : nChars = 1;
7830 0 : pUnicodesPerGlyph[i] = nChars;
7831 0 : for( int n = 1; n < nChars; n++ )
7832 0 : aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) );
7833 : }
7834 : // #i36691# hack that is needed because currently the pGlyphs[]
7835 : // argument is ignored for embeddable fonts and so the layout
7836 : // engine's glyph work is ignored (i.e. char mirroring)
7837 : // TODO: a real solution would be to map the layout engine's
7838 : // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
7839 : // back to unicode and then to embeddable font's encoding
7840 0 : if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL )
7841 : {
7842 0 : size_t nI = aUnicodes.size()-1;
7843 0 : for( int n = 0; n < nChars; n++, nI-- )
7844 0 : aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI]));
7845 0 : }
7846 : }
7847 : else
7848 0 : aUnicodes.push_back( 0 );
7849 : // note: in case of ctl one character may result
7850 : // in multiple glyphs. The current SalLayout
7851 : // implementations set -1 then to indicate that no direct
7852 : // mapping is possible
7853 : }
7854 :
7855 0 : registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
7856 :
7857 0 : for( int i = 0; i < nGlyphs; i++ )
7858 : {
7859 : aGlyphs.push_back( PDFGlyph( aGNGlyphPos,
7860 : pGlyphWidths[i],
7861 0 : pGlyphs[i],
7862 : pMappedFontObjects[i],
7863 0 : pMappedGlyphs[i] ) );
7864 0 : if( bVertical )
7865 0 : aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7866 : else
7867 0 : aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7868 : }
7869 : }
7870 :
7871 0 : Point aAlignOffset;
7872 0 : if ( eAlign == ALIGN_BOTTOM )
7873 0 : aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
7874 0 : else if ( eAlign == ALIGN_TOP )
7875 0 : aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
7876 0 : if( aAlignOffset.X() || aAlignOffset.Y() )
7877 0 : aAlignOffset = aRotScale.transform( aAlignOffset );
7878 :
7879 : /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
7880 : string contained only on of the UTF16 BOMs
7881 : */
7882 0 : if( ! aGlyphs.empty() )
7883 : {
7884 0 : if( bVertical )
7885 0 : drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
7886 : else
7887 0 : drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
7888 : }
7889 :
7890 : // end textobject
7891 0 : aLine.append( "ET\n" );
7892 0 : if( bPop )
7893 0 : aLine.append( "Q\n" );
7894 :
7895 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
7896 :
7897 : // draw eventual textlines
7898 0 : FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
7899 0 : FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
7900 0 : FontUnderline eOverline = m_aCurrentPDFState.m_aFont.GetOverline();
7901 0 : if( bTextLines &&
7902 : (
7903 : ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) ||
7904 : ( eOverline != UNDERLINE_NONE && eOverline != UNDERLINE_DONTKNOW ) ||
7905 : ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
7906 : )
7907 : )
7908 : {
7909 0 : sal_Bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
7910 0 : if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
7911 : {
7912 0 : Point aPos, aStartPt;
7913 0 : sal_Int32 nWidth = 0, nAdvance=0;
7914 0 : for( int nStart = 0;;)
7915 : {
7916 : sal_GlyphId nGlyphIndex;
7917 0 : if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
7918 : break;
7919 :
7920 0 : if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
7921 : {
7922 0 : if( !nWidth )
7923 0 : aStartPt = aPos;
7924 :
7925 0 : nWidth += nAdvance;
7926 : }
7927 0 : else if( nWidth > 0 )
7928 : {
7929 0 : drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7930 : m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7931 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7932 0 : nWidth = 0;
7933 : }
7934 : }
7935 :
7936 0 : if( nWidth > 0 )
7937 : {
7938 0 : drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7939 : m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7940 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7941 : }
7942 : }
7943 : else
7944 : {
7945 0 : Point aStartPt = rLayout.GetDrawPosition();
7946 0 : int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
7947 0 : drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7948 : m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7949 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7950 : }
7951 : }
7952 :
7953 : // write eventual emphasis marks
7954 0 : if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
7955 : {
7956 0 : PolyPolygon aEmphPoly;
7957 0 : Rectangle aEmphRect1;
7958 0 : Rectangle aEmphRect2;
7959 : long nEmphYOff;
7960 : long nEmphWidth;
7961 : long nEmphHeight;
7962 : sal_Bool bEmphPolyLine;
7963 : FontEmphasisMark nEmphMark;
7964 :
7965 0 : push( PUSH_ALL );
7966 :
7967 0 : aLine.setLength( 0 );
7968 0 : aLine.append( "q\n" );
7969 :
7970 0 : nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
7971 0 : if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7972 0 : nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
7973 : else
7974 0 : nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
7975 : m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
7976 : bEmphPolyLine,
7977 : aEmphRect1,
7978 : aEmphRect2,
7979 : nEmphYOff,
7980 : nEmphWidth,
7981 : nEmphMark,
7982 : m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight),
7983 0 : m_pReferenceDevice->mpFontEntry->mnOrientation );
7984 0 : if ( bEmphPolyLine )
7985 : {
7986 0 : setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
7987 0 : setFillColor( Color( COL_TRANSPARENT ) );
7988 : }
7989 : else
7990 : {
7991 0 : setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
7992 0 : setLineColor( Color( COL_TRANSPARENT ) );
7993 : }
7994 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
7995 :
7996 0 : Point aOffset = Point(0,0);
7997 :
7998 0 : if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7999 0 : aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff;
8000 : else
8001 0 : aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff;
8002 :
8003 0 : long nEmphWidth2 = nEmphWidth / 2;
8004 0 : long nEmphHeight2 = nEmphHeight / 2;
8005 0 : aOffset += Point( nEmphWidth2, nEmphHeight2 );
8006 :
8007 0 : if ( eAlign == ALIGN_BOTTOM )
8008 0 : aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent;
8009 0 : else if ( eAlign == ALIGN_TOP )
8010 0 : aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent;
8011 :
8012 0 : for( int nStart = 0;;)
8013 : {
8014 0 : Point aPos;
8015 : sal_GlyphId nGlyphIndex;
8016 : sal_Int32 nAdvance;
8017 0 : if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
8018 : break;
8019 :
8020 0 : if( !rLayout.IsSpacingGlyph( nGlyphIndex ) )
8021 : {
8022 0 : Point aAdjOffset = aOffset;
8023 0 : aAdjOffset.X() += (nAdvance - nEmphWidth) / 2;
8024 0 : aAdjOffset = aRotScale.transform( aAdjOffset );
8025 :
8026 0 : aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
8027 :
8028 0 : aPos += aAdjOffset;
8029 0 : aPos = m_pReferenceDevice->PixelToLogic( aPos );
8030 0 : drawEmphasisMark( aPos.X(), aPos.Y(),
8031 : aEmphPoly, bEmphPolyLine,
8032 0 : aEmphRect1, aEmphRect2 );
8033 : }
8034 : }
8035 :
8036 0 : writeBuffer( "Q\n", 2 );
8037 0 : pop();
8038 0 : }
8039 :
8040 : }
8041 :
8042 0 : void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
8043 : const PolyPolygon& rPolyPoly, sal_Bool bPolyLine,
8044 : const Rectangle& rRect1, const Rectangle& rRect2 )
8045 : {
8046 : // TODO: pass nWidth as width of this mark
8047 : // long nWidth = 0;
8048 :
8049 0 : if ( rPolyPoly.Count() )
8050 : {
8051 0 : if ( bPolyLine )
8052 : {
8053 0 : Polygon aPoly = rPolyPoly.GetObject( 0 );
8054 0 : aPoly.Move( nX, nY );
8055 0 : drawPolyLine( aPoly );
8056 : }
8057 : else
8058 : {
8059 0 : PolyPolygon aPolyPoly = rPolyPoly;
8060 0 : aPolyPoly.Move( nX, nY );
8061 0 : drawPolyPolygon( aPolyPoly );
8062 : }
8063 : }
8064 :
8065 0 : if ( !rRect1.IsEmpty() )
8066 : {
8067 0 : Rectangle aRect( Point( nX+rRect1.Left(),
8068 0 : nY+rRect1.Top() ), rRect1.GetSize() );
8069 0 : drawRectangle( aRect );
8070 : }
8071 :
8072 0 : if ( !rRect2.IsEmpty() )
8073 : {
8074 0 : Rectangle aRect( Point( nX+rRect2.Left(),
8075 0 : nY+rRect2.Top() ), rRect2.GetSize() );
8076 :
8077 0 : drawRectangle( aRect );
8078 : }
8079 0 : }
8080 :
8081 0 : void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
8082 : {
8083 0 : MARK( "drawText" );
8084 :
8085 0 : updateGraphicsState();
8086 :
8087 : // get a layout from the OuputDevice's SalGraphics
8088 : // this also enforces font substitution and sets the font on SalGraphics
8089 0 : SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
8090 0 : if( pLayout )
8091 : {
8092 0 : drawLayout( *pLayout, rText, bTextLines );
8093 0 : pLayout->Release();
8094 : }
8095 0 : }
8096 :
8097 0 : void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
8098 : {
8099 0 : MARK( "drawText with array" );
8100 :
8101 0 : updateGraphicsState();
8102 :
8103 : // get a layout from the OuputDevice's SalGraphics
8104 : // this also enforces font substitution and sets the font on SalGraphics
8105 0 : SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
8106 0 : if( pLayout )
8107 : {
8108 0 : drawLayout( *pLayout, rText, bTextLines );
8109 0 : pLayout->Release();
8110 : }
8111 0 : }
8112 :
8113 0 : void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines )
8114 : {
8115 0 : MARK( "drawStretchText" );
8116 :
8117 0 : updateGraphicsState();
8118 :
8119 : // get a layout from the OuputDevice's SalGraphics
8120 : // this also enforces font substitution and sets the font on SalGraphics
8121 0 : SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
8122 0 : if( pLayout )
8123 : {
8124 0 : drawLayout( *pLayout, rText, bTextLines );
8125 0 : pLayout->Release();
8126 : }
8127 0 : }
8128 :
8129 0 : void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines )
8130 : {
8131 0 : long nWidth = rRect.GetWidth();
8132 0 : long nHeight = rRect.GetHeight();
8133 :
8134 0 : if ( nWidth <= 0 || nHeight <= 0 )
8135 0 : return;
8136 :
8137 0 : MARK( "drawText with rectangle" );
8138 :
8139 0 : updateGraphicsState();
8140 :
8141 : // clip with rectangle
8142 0 : OStringBuffer aLine;
8143 0 : aLine.append( "q " );
8144 0 : m_aPages.back().appendRect( rRect, aLine );
8145 0 : aLine.append( " W* n\n" );
8146 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8147 :
8148 : // if disabled text is needed, put in here
8149 :
8150 0 : Point aPos = rRect.TopLeft();
8151 :
8152 0 : long nTextHeight = m_pReferenceDevice->GetTextHeight();
8153 0 : xub_StrLen nMnemonicPos = STRING_NOTFOUND;
8154 :
8155 0 : String aStr = rOrigStr;
8156 0 : if ( nStyle & TEXT_DRAW_MNEMONIC )
8157 0 : aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos );
8158 :
8159 : // multiline text
8160 0 : if ( nStyle & TEXT_DRAW_MULTILINE )
8161 : {
8162 0 : rtl::OUString aLastLine;
8163 0 : ImplMultiTextLineInfo aMultiLineInfo;
8164 : ImplTextLineInfo* pLineInfo;
8165 : xub_StrLen i;
8166 : xub_StrLen nLines;
8167 : xub_StrLen nFormatLines;
8168 :
8169 0 : if ( nTextHeight )
8170 : {
8171 0 : ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice );
8172 0 : OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout );
8173 0 : nLines = (xub_StrLen)(nHeight/nTextHeight);
8174 0 : nFormatLines = aMultiLineInfo.Count();
8175 0 : if ( !nLines )
8176 0 : nLines = 1;
8177 0 : if ( nFormatLines > nLines )
8178 : {
8179 0 : if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
8180 : {
8181 : // handle last line
8182 0 : nFormatLines = nLines-1;
8183 :
8184 0 : pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
8185 0 : aLastLine = convertLineEnd(aStr.Copy(pLineInfo->GetIndex()), LINEEND_LF);
8186 : // replace line feed by space
8187 0 : aLastLine = aLastLine.replace(_LF, ' ');
8188 0 : aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
8189 0 : nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
8190 0 : nStyle |= TEXT_DRAW_TOP;
8191 : }
8192 : }
8193 :
8194 : // vertical alignment
8195 0 : if ( nStyle & TEXT_DRAW_BOTTOM )
8196 0 : aPos.Y() += nHeight-(nFormatLines*nTextHeight);
8197 0 : else if ( nStyle & TEXT_DRAW_VCENTER )
8198 0 : aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
8199 :
8200 : // draw all lines excluding the last
8201 0 : for ( i = 0; i < nFormatLines; i++ )
8202 : {
8203 0 : pLineInfo = aMultiLineInfo.GetLine( i );
8204 0 : if ( nStyle & TEXT_DRAW_RIGHT )
8205 0 : aPos.X() += nWidth-pLineInfo->GetWidth();
8206 0 : else if ( nStyle & TEXT_DRAW_CENTER )
8207 0 : aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
8208 0 : xub_StrLen nIndex = pLineInfo->GetIndex();
8209 0 : xub_StrLen nLineLen = pLineInfo->GetLen();
8210 0 : drawText( aPos, aStr, nIndex, nLineLen, bTextLines );
8211 : // mnemonics should not appear in documents,
8212 : // if the need arises, put them in here
8213 0 : aPos.Y() += nTextHeight;
8214 0 : aPos.X() = rRect.Left();
8215 : }
8216 :
8217 :
8218 : // output last line left adjusted since it was shortened
8219 0 : if (!aLastLine.isEmpty())
8220 0 : drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines );
8221 0 : }
8222 : }
8223 : else
8224 : {
8225 0 : long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
8226 :
8227 : // Evt. Text kuerzen
8228 0 : if ( nTextWidth > nWidth )
8229 : {
8230 0 : if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) )
8231 : {
8232 0 : aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
8233 0 : nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
8234 0 : nStyle |= TEXT_DRAW_LEFT;
8235 0 : nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
8236 : }
8237 : }
8238 :
8239 : // vertical alignment
8240 0 : if ( nStyle & TEXT_DRAW_RIGHT )
8241 0 : aPos.X() += nWidth-nTextWidth;
8242 0 : else if ( nStyle & TEXT_DRAW_CENTER )
8243 0 : aPos.X() += (nWidth-nTextWidth)/2;
8244 :
8245 0 : if ( nStyle & TEXT_DRAW_BOTTOM )
8246 0 : aPos.Y() += nHeight-nTextHeight;
8247 0 : else if ( nStyle & TEXT_DRAW_VCENTER )
8248 0 : aPos.Y() += (nHeight-nTextHeight)/2;
8249 :
8250 : // mnemonics should be inserted here if the need arises
8251 :
8252 : // draw the actual text
8253 0 : drawText( aPos, aStr, 0, STRING_LEN, bTextLines );
8254 : }
8255 :
8256 : // reset clip region to original value
8257 0 : aLine.setLength( 0 );
8258 0 : aLine.append( "Q\n" );
8259 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8260 : }
8261 :
8262 0 : void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
8263 : {
8264 0 : MARK( "drawLine" );
8265 :
8266 0 : updateGraphicsState();
8267 :
8268 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8269 0 : return;
8270 :
8271 0 : OStringBuffer aLine;
8272 0 : m_aPages.back().appendPoint( rStart, aLine );
8273 0 : aLine.append( " m " );
8274 0 : m_aPages.back().appendPoint( rStop, aLine );
8275 0 : aLine.append( " l S\n" );
8276 :
8277 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8278 : }
8279 :
8280 0 : void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
8281 : {
8282 0 : MARK( "drawLine with LineInfo" );
8283 0 : updateGraphicsState();
8284 :
8285 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8286 : return;
8287 :
8288 0 : if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 )
8289 : {
8290 0 : drawLine( rStart, rStop );
8291 : return;
8292 : }
8293 :
8294 0 : OStringBuffer aLine;
8295 :
8296 0 : aLine.append( "q " );
8297 0 : if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
8298 : {
8299 0 : m_aPages.back().appendPoint( rStart, aLine );
8300 0 : aLine.append( " m " );
8301 0 : m_aPages.back().appendPoint( rStop, aLine );
8302 0 : aLine.append( " l S Q\n" );
8303 :
8304 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8305 : }
8306 : else
8307 : {
8308 0 : PDFWriter::ExtLineInfo aInfo;
8309 0 : convertLineInfoToExtLineInfo( rInfo, aInfo );
8310 0 : Point aPolyPoints[2] = { rStart, rStop };
8311 0 : Polygon aPoly( 2, aPolyPoints );
8312 0 : drawPolyLine( aPoly, aInfo );
8313 0 : }
8314 : }
8315 :
8316 : #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
8317 :
8318 0 : void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8319 : {
8320 : // note: units in pFontEntry are ref device pixel
8321 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
8322 0 : long nLineHeight = 0;
8323 0 : long nLinePos = 0;
8324 :
8325 0 : appendStrokingColor( aColor, aLine );
8326 0 : aLine.append( "\n" );
8327 :
8328 0 : if ( bIsAbove )
8329 : {
8330 0 : if ( !pFontEntry->maMetric.mnAboveWUnderlineSize )
8331 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
8332 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize );
8333 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset );
8334 : }
8335 : else
8336 : {
8337 0 : if ( !pFontEntry->maMetric.mnWUnderlineSize )
8338 0 : m_pReferenceDevice->ImplInitTextLineSize();
8339 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize );
8340 0 : nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset );
8341 : }
8342 0 : if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
8343 0 : nLineHeight = 3;
8344 :
8345 0 : long nLineWidth = getReferenceDevice()->mnDPIX/450;
8346 0 : if ( ! nLineWidth )
8347 0 : nLineWidth = 1;
8348 :
8349 0 : if ( eTextLine == UNDERLINE_BOLDWAVE )
8350 0 : nLineWidth = 3*nLineWidth;
8351 :
8352 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
8353 0 : aLine.append( " w " );
8354 :
8355 0 : if ( eTextLine == UNDERLINE_DOUBLEWAVE )
8356 : {
8357 0 : long nOrgLineHeight = nLineHeight;
8358 0 : nLineHeight /= 3;
8359 0 : if ( nLineHeight < 2 )
8360 : {
8361 0 : if ( nOrgLineHeight > 1 )
8362 0 : nLineHeight = 2;
8363 : else
8364 0 : nLineHeight = 1;
8365 : }
8366 0 : long nLineDY = nOrgLineHeight-(nLineHeight*2);
8367 0 : if ( nLineDY < nLineWidth )
8368 0 : nLineDY = nLineWidth;
8369 0 : long nLineDY2 = nLineDY/2;
8370 0 : if ( !nLineDY2 )
8371 0 : nLineDY2 = 1;
8372 :
8373 0 : nLinePos -= nLineWidth-nLineDY2;
8374 :
8375 0 : m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8376 :
8377 0 : nLinePos += nLineWidth+nLineDY;
8378 0 : m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8379 : }
8380 : else
8381 : {
8382 0 : if ( eTextLine != UNDERLINE_BOLDWAVE )
8383 0 : nLinePos -= nLineWidth/2;
8384 0 : m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
8385 : }
8386 0 : }
8387 :
8388 0 : void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8389 : {
8390 : // note: units in pFontEntry are ref device pixel
8391 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
8392 0 : long nLineHeight = 0;
8393 0 : long nLinePos = 0;
8394 0 : long nLinePos2 = 0;
8395 :
8396 0 : if ( eTextLine > UNDERLINE_BOLDWAVE )
8397 0 : eTextLine = UNDERLINE_SINGLE;
8398 :
8399 0 : switch ( eTextLine )
8400 : {
8401 : case UNDERLINE_SINGLE:
8402 : case UNDERLINE_DOTTED:
8403 : case UNDERLINE_DASH:
8404 : case UNDERLINE_LONGDASH:
8405 : case UNDERLINE_DASHDOT:
8406 : case UNDERLINE_DASHDOTDOT:
8407 0 : if ( bIsAbove )
8408 : {
8409 0 : if ( !pFontEntry->maMetric.mnAboveUnderlineSize )
8410 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
8411 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize );
8412 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset );
8413 : }
8414 : else
8415 : {
8416 0 : if ( !pFontEntry->maMetric.mnUnderlineSize )
8417 0 : m_pReferenceDevice->ImplInitTextLineSize();
8418 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize );
8419 0 : nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset );
8420 : }
8421 0 : break;
8422 : case UNDERLINE_BOLD:
8423 : case UNDERLINE_BOLDDOTTED:
8424 : case UNDERLINE_BOLDDASH:
8425 : case UNDERLINE_BOLDLONGDASH:
8426 : case UNDERLINE_BOLDDASHDOT:
8427 : case UNDERLINE_BOLDDASHDOTDOT:
8428 0 : if ( bIsAbove )
8429 : {
8430 0 : if ( !pFontEntry->maMetric.mnAboveBUnderlineSize )
8431 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
8432 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize );
8433 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset );
8434 : }
8435 : else
8436 : {
8437 0 : if ( !pFontEntry->maMetric.mnBUnderlineSize )
8438 0 : m_pReferenceDevice->ImplInitTextLineSize();
8439 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize );
8440 0 : nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset );
8441 0 : nLinePos += nLineHeight/2;
8442 : }
8443 0 : break;
8444 : case UNDERLINE_DOUBLE:
8445 0 : if ( bIsAbove )
8446 : {
8447 0 : if ( !pFontEntry->maMetric.mnAboveDUnderlineSize )
8448 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
8449 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize );
8450 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 );
8451 0 : nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 );
8452 : }
8453 : else
8454 : {
8455 0 : if ( !pFontEntry->maMetric.mnDUnderlineSize )
8456 0 : m_pReferenceDevice->ImplInitTextLineSize();
8457 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize );
8458 0 : nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 );
8459 0 : nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 );
8460 : }
8461 : default:
8462 0 : break;
8463 : }
8464 :
8465 0 : if ( nLineHeight )
8466 : {
8467 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8468 0 : aLine.append( " w " );
8469 0 : appendStrokingColor( aColor, aLine );
8470 0 : aLine.append( "\n" );
8471 :
8472 0 : switch ( eTextLine )
8473 : {
8474 : case UNDERLINE_DOTTED:
8475 : case UNDERLINE_BOLDDOTTED:
8476 0 : aLine.append( "[ " );
8477 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8478 0 : aLine.append( " ] 0 d\n" );
8479 0 : break;
8480 : case UNDERLINE_DASH:
8481 : case UNDERLINE_LONGDASH:
8482 : case UNDERLINE_BOLDDASH:
8483 : case UNDERLINE_BOLDLONGDASH:
8484 : {
8485 0 : sal_Int32 nDashLength = 4*nLineHeight;
8486 0 : sal_Int32 nVoidLength = 2*nLineHeight;
8487 0 : if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) )
8488 0 : nDashLength = 8*nLineHeight;
8489 :
8490 0 : aLine.append( "[ " );
8491 0 : m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8492 0 : aLine.append( ' ' );
8493 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8494 0 : aLine.append( " ] 0 d\n" );
8495 : }
8496 0 : break;
8497 : case UNDERLINE_DASHDOT:
8498 : case UNDERLINE_BOLDDASHDOT:
8499 : {
8500 0 : sal_Int32 nDashLength = 4*nLineHeight;
8501 0 : sal_Int32 nVoidLength = 2*nLineHeight;
8502 0 : aLine.append( "[ " );
8503 0 : m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8504 0 : aLine.append( ' ' );
8505 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8506 0 : aLine.append( ' ' );
8507 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8508 0 : aLine.append( ' ' );
8509 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8510 0 : aLine.append( " ] 0 d\n" );
8511 : }
8512 0 : break;
8513 : case UNDERLINE_DASHDOTDOT:
8514 : case UNDERLINE_BOLDDASHDOTDOT:
8515 : {
8516 0 : sal_Int32 nDashLength = 4*nLineHeight;
8517 0 : sal_Int32 nVoidLength = 2*nLineHeight;
8518 0 : aLine.append( "[ " );
8519 0 : m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8520 0 : aLine.append( ' ' );
8521 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8522 0 : aLine.append( ' ' );
8523 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8524 0 : aLine.append( ' ' );
8525 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8526 0 : aLine.append( ' ' );
8527 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8528 0 : aLine.append( ' ' );
8529 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8530 0 : aLine.append( " ] 0 d\n" );
8531 : }
8532 0 : break;
8533 : default:
8534 0 : break;
8535 : }
8536 :
8537 0 : aLine.append( "0 " );
8538 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8539 0 : aLine.append( " m " );
8540 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8541 0 : aLine.append( ' ' );
8542 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8543 0 : aLine.append( " l S\n" );
8544 0 : if ( eTextLine == UNDERLINE_DOUBLE )
8545 : {
8546 0 : aLine.append( "0 " );
8547 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8548 0 : aLine.append( " m " );
8549 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8550 0 : aLine.append( ' ' );
8551 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8552 0 : aLine.append( " l S\n" );
8553 : }
8554 : }
8555 0 : }
8556 :
8557 0 : void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
8558 : {
8559 : // note: units in pFontEntry are ref device pixel
8560 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
8561 0 : long nLineHeight = 0;
8562 0 : long nLinePos = 0;
8563 0 : long nLinePos2 = 0;
8564 :
8565 0 : if ( eStrikeout > STRIKEOUT_X )
8566 0 : eStrikeout = STRIKEOUT_SINGLE;
8567 :
8568 0 : switch ( eStrikeout )
8569 : {
8570 : case STRIKEOUT_SINGLE:
8571 0 : if ( !pFontEntry->maMetric.mnStrikeoutSize )
8572 0 : m_pReferenceDevice->ImplInitTextLineSize();
8573 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize );
8574 0 : nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset );
8575 0 : break;
8576 : case STRIKEOUT_BOLD:
8577 0 : if ( !pFontEntry->maMetric.mnBStrikeoutSize )
8578 0 : m_pReferenceDevice->ImplInitTextLineSize();
8579 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize );
8580 0 : nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset );
8581 0 : break;
8582 : case STRIKEOUT_DOUBLE:
8583 0 : if ( !pFontEntry->maMetric.mnDStrikeoutSize )
8584 0 : m_pReferenceDevice->ImplInitTextLineSize();
8585 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize );
8586 0 : nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 );
8587 0 : nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 );
8588 0 : break;
8589 : default:
8590 0 : break;
8591 : }
8592 :
8593 0 : if ( nLineHeight )
8594 : {
8595 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8596 0 : aLine.append( " w " );
8597 0 : appendStrokingColor( aColor, aLine );
8598 0 : aLine.append( "\n" );
8599 :
8600 0 : aLine.append( "0 " );
8601 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8602 0 : aLine.append( " m " );
8603 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8604 0 : aLine.append( ' ' );
8605 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8606 0 : aLine.append( " l S\n" );
8607 :
8608 0 : if ( eStrikeout == STRIKEOUT_DOUBLE )
8609 : {
8610 0 : aLine.append( "0 " );
8611 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8612 0 : aLine.append( " m " );
8613 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8614 0 : aLine.append( ' ' );
8615 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8616 0 : aLine.append( " l S\n" );
8617 : }
8618 : }
8619 0 : }
8620 :
8621 0 : void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
8622 : {
8623 : //See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
8624 : //to tweak this
8625 :
8626 0 : rtl::OUString aStrikeoutChar = eStrikeout == STRIKEOUT_SLASH ? rtl::OUString("/") : rtl::OUString("X");
8627 0 : String aStrikeout = aStrikeoutChar;
8628 0 : while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
8629 0 : aStrikeout.Append( aStrikeout );
8630 :
8631 : // do not get broader than nWidth modulo 1 character
8632 0 : while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
8633 0 : aStrikeout.Erase( 0, 1 );
8634 0 : aStrikeout.Append( aStrikeoutChar );
8635 0 : sal_Bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
8636 0 : if ( bShadow )
8637 : {
8638 0 : Font aFont = m_aCurrentPDFState.m_aFont;
8639 0 : aFont.SetShadow( sal_False );
8640 0 : setFont( aFont );
8641 0 : updateGraphicsState();
8642 : }
8643 :
8644 : // strikeout string is left aligned non-CTL text
8645 0 : sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode();
8646 0 : m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED );
8647 :
8648 0 : push( PUSH_CLIPREGION );
8649 0 : FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
8650 0 : Rectangle aRect;
8651 0 : aRect.Left() = rPos.X();
8652 0 : aRect.Right() = aRect.Left()+nWidth;
8653 0 : aRect.Bottom() = rPos.Y()+aRefDevFontMetric.GetDescent();
8654 0 : aRect.Top() = rPos.Y()-aRefDevFontMetric.GetAscent();
8655 :
8656 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
8657 0 : if (pFontEntry->mnOrientation)
8658 : {
8659 0 : Polygon aPoly( aRect );
8660 0 : aPoly.Rotate( rPos, pFontEntry->mnOrientation);
8661 0 : aRect = aPoly.GetBoundRect();
8662 : }
8663 :
8664 0 : intersectClipRegion( aRect );
8665 0 : drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false );
8666 0 : pop();
8667 :
8668 0 : m_pReferenceDevice->SetLayoutMode( nOrigTLM );
8669 :
8670 0 : if ( bShadow )
8671 : {
8672 0 : Font aFont = m_aCurrentPDFState.m_aFont;
8673 0 : aFont.SetShadow( sal_True );
8674 0 : setFont( aFont );
8675 0 : updateGraphicsState();
8676 0 : }
8677 0 : }
8678 :
8679 0 : void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove )
8680 : {
8681 0 : if ( !nWidth ||
8682 : ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
8683 : ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) &&
8684 : ((eOverline == UNDERLINE_NONE)||(eOverline == UNDERLINE_DONTKNOW)) ) )
8685 0 : return;
8686 :
8687 0 : MARK( "drawTextLine" );
8688 0 : updateGraphicsState();
8689 :
8690 : // note: units in pFontEntry are ref device pixel
8691 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
8692 0 : Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
8693 0 : Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
8694 0 : Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
8695 0 : bool bStrikeoutDone = false;
8696 0 : bool bUnderlineDone = false;
8697 0 : bool bOverlineDone = false;
8698 :
8699 0 : if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
8700 : {
8701 0 : drawStrikeoutChar( rPos, nWidth, eStrikeout );
8702 0 : bStrikeoutDone = true;
8703 : }
8704 :
8705 0 : Point aPos( rPos );
8706 0 : TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
8707 0 : if( eAlign == ALIGN_TOP )
8708 0 : aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent );
8709 0 : else if( eAlign == ALIGN_BOTTOM )
8710 0 : aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent );
8711 :
8712 0 : OStringBuffer aLine( 512 );
8713 : // save GS
8714 0 : aLine.append( "q " );
8715 :
8716 : // rotate and translate matrix
8717 0 : double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
8718 0 : Matrix3 aMat;
8719 0 : aMat.rotate( fAngle );
8720 0 : aMat.translate( aPos.X(), aPos.Y() );
8721 0 : aMat.append( m_aPages.back(), aLine );
8722 0 : aLine.append( " cm\n" );
8723 :
8724 0 : if ( aUnderlineColor.GetTransparency() != 0 )
8725 0 : aUnderlineColor = aStrikeoutColor;
8726 :
8727 0 : if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
8728 : (eUnderline == UNDERLINE_WAVE) ||
8729 : (eUnderline == UNDERLINE_DOUBLEWAVE) ||
8730 : (eUnderline == UNDERLINE_BOLDWAVE) )
8731 : {
8732 0 : drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8733 0 : bUnderlineDone = true;
8734 : }
8735 :
8736 0 : if ( (eOverline == UNDERLINE_SMALLWAVE) ||
8737 : (eOverline == UNDERLINE_WAVE) ||
8738 : (eOverline == UNDERLINE_DOUBLEWAVE) ||
8739 : (eOverline == UNDERLINE_BOLDWAVE) )
8740 : {
8741 0 : drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8742 0 : bOverlineDone = true;
8743 : }
8744 :
8745 0 : if ( !bUnderlineDone )
8746 : {
8747 0 : drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8748 : }
8749 :
8750 0 : if ( !bOverlineDone )
8751 : {
8752 0 : drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8753 : }
8754 :
8755 0 : if ( !bStrikeoutDone )
8756 : {
8757 0 : drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
8758 : }
8759 :
8760 0 : aLine.append( "Q\n" );
8761 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8762 : }
8763 :
8764 0 : void PDFWriterImpl::drawPolygon( const Polygon& rPoly )
8765 : {
8766 0 : MARK( "drawPolygon" );
8767 :
8768 0 : updateGraphicsState();
8769 :
8770 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8771 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8772 0 : return;
8773 :
8774 0 : int nPoints = rPoly.GetSize();
8775 0 : OStringBuffer aLine( 20 * nPoints );
8776 0 : m_aPages.back().appendPolygon( rPoly, aLine );
8777 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8778 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8779 0 : aLine.append( "B*\n" );
8780 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8781 0 : aLine.append( "S\n" );
8782 : else
8783 0 : aLine.append( "f*\n" );
8784 :
8785 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8786 : }
8787 :
8788 0 : void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly )
8789 : {
8790 0 : MARK( "drawPolyPolygon" );
8791 :
8792 0 : updateGraphicsState();
8793 :
8794 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8795 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8796 0 : return;
8797 :
8798 0 : int nPolygons = rPolyPoly.Count();
8799 :
8800 0 : OStringBuffer aLine( 40 * nPolygons );
8801 0 : m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
8802 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8803 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8804 0 : aLine.append( "B*\n" );
8805 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8806 0 : aLine.append( "S\n" );
8807 : else
8808 0 : aLine.append( "f*\n" );
8809 :
8810 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8811 : }
8812 :
8813 0 : void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
8814 : {
8815 : DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8816 0 : nTransparentPercent = nTransparentPercent % 100;
8817 :
8818 0 : MARK( "drawTransparent" );
8819 :
8820 0 : updateGraphicsState();
8821 :
8822 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8823 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8824 : return;
8825 :
8826 0 : if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 )
8827 : {
8828 : m_aErrors.insert( m_bIsPDF_A1 ?
8829 : PDFWriter::Warning_Transparency_Omitted_PDFA :
8830 0 : PDFWriter::Warning_Transparency_Omitted_PDF13 );
8831 :
8832 0 : drawPolyPolygon( rPolyPoly );
8833 : return;
8834 : }
8835 :
8836 : // create XObject
8837 0 : m_aTransparentObjects.push_back( TransparencyEmit() );
8838 : // FIXME: polygons with beziers may yield incorrect bound rect
8839 0 : m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect();
8840 : // convert rectangle to default user space
8841 0 : m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8842 0 : m_aTransparentObjects.back().m_nObject = createObject();
8843 0 : m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8844 0 : m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
8845 0 : m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 );
8846 : // create XObject's content stream
8847 0 : OStringBuffer aContent( 256 );
8848 0 : m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
8849 0 : if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
8850 0 : m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
8851 0 : aContent.append( " B*\n" );
8852 0 : else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
8853 0 : aContent.append( " S\n" );
8854 : else
8855 0 : aContent.append( " f*\n" );
8856 0 : m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() );
8857 :
8858 0 : OStringBuffer aObjName( 16 );
8859 0 : aObjName.append( "Tr" );
8860 0 : aObjName.append( m_aTransparentObjects.back().m_nObject );
8861 0 : OString aTrName( aObjName.makeStringAndClear() );
8862 0 : aObjName.append( "EGS" );
8863 0 : aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8864 0 : OString aExtName( aObjName.makeStringAndClear() );
8865 :
8866 0 : OStringBuffer aLine( 80 );
8867 : // insert XObject
8868 0 : aLine.append( "q /" );
8869 0 : aLine.append( aExtName );
8870 0 : aLine.append( " gs /" );
8871 0 : aLine.append( aTrName );
8872 0 : aLine.append( " Do Q\n" );
8873 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8874 :
8875 0 : pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8876 0 : pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8877 : }
8878 :
8879 0 : void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
8880 : {
8881 0 : if( nObject >= 0 )
8882 : {
8883 0 : switch( eKind )
8884 : {
8885 : case ResXObject:
8886 0 : m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
8887 0 : if( ! m_aOutputStreams.empty() )
8888 0 : m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
8889 0 : break;
8890 : case ResExtGState:
8891 0 : m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
8892 0 : if( ! m_aOutputStreams.empty() )
8893 0 : m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
8894 0 : break;
8895 : case ResShading:
8896 0 : m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
8897 0 : if( ! m_aOutputStreams.empty() )
8898 0 : m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
8899 0 : break;
8900 : case ResPattern:
8901 0 : m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
8902 0 : if( ! m_aOutputStreams.empty() )
8903 0 : m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
8904 0 : break;
8905 : }
8906 : }
8907 0 : }
8908 :
8909 0 : void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect )
8910 : {
8911 0 : push( PUSH_ALL );
8912 :
8913 : // force reemitting clip region
8914 0 : clearClipRegion();
8915 0 : updateGraphicsState();
8916 :
8917 0 : m_aOutputStreams.push_front( StreamRedirect() );
8918 0 : m_aOutputStreams.front().m_pStream = pStream;
8919 0 : m_aOutputStreams.front().m_aMapMode = m_aMapMode;
8920 :
8921 0 : if( !rTargetRect.IsEmpty() )
8922 : {
8923 0 : m_aOutputStreams.front().m_aTargetRect =
8924 0 : lcl_convert( m_aGraphicsStack.front().m_aMapMode,
8925 : m_aMapMode,
8926 : getReferenceDevice(),
8927 0 : rTargetRect );
8928 0 : Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
8929 0 : long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
8930 0 : aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
8931 0 : m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
8932 : }
8933 :
8934 : // setup graphics state for independent object stream
8935 :
8936 : // force reemitting colors
8937 0 : m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8938 0 : m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8939 0 : }
8940 :
8941 0 : SvStream* PDFWriterImpl::endRedirect()
8942 : {
8943 0 : SvStream* pStream = NULL;
8944 0 : if( ! m_aOutputStreams.empty() )
8945 : {
8946 0 : pStream = m_aOutputStreams.front().m_pStream;
8947 0 : m_aMapMode = m_aOutputStreams.front().m_aMapMode;
8948 0 : m_aOutputStreams.pop_front();
8949 : }
8950 :
8951 0 : pop();
8952 : // force reemitting colors and clip region
8953 0 : clearClipRegion();
8954 0 : m_aCurrentPDFState.m_bClipRegion = m_aGraphicsStack.front().m_bClipRegion;
8955 0 : m_aCurrentPDFState.m_aClipRegion = m_aGraphicsStack.front().m_aClipRegion;
8956 0 : m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8957 0 : m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8958 :
8959 0 : updateGraphicsState();
8960 :
8961 0 : return pStream;
8962 : }
8963 :
8964 0 : void PDFWriterImpl::beginTransparencyGroup()
8965 : {
8966 0 : updateGraphicsState();
8967 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8968 0 : beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8969 0 : }
8970 :
8971 0 : void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
8972 : {
8973 : DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8974 0 : nTransparentPercent = nTransparentPercent % 100;
8975 :
8976 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8977 : {
8978 : // create XObject
8979 0 : m_aTransparentObjects.push_back( TransparencyEmit() );
8980 0 : m_aTransparentObjects.back().m_aBoundRect = rBoundingBox;
8981 : // convert rectangle to default user space
8982 0 : m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8983 0 : m_aTransparentObjects.back().m_nObject = createObject();
8984 0 : m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
8985 : // get XObject's content stream
8986 0 : m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8987 0 : m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8988 :
8989 0 : OStringBuffer aObjName( 16 );
8990 0 : aObjName.append( "Tr" );
8991 0 : aObjName.append( m_aTransparentObjects.back().m_nObject );
8992 0 : OString aTrName( aObjName.makeStringAndClear() );
8993 0 : aObjName.append( "EGS" );
8994 0 : aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8995 0 : OString aExtName( aObjName.makeStringAndClear() );
8996 :
8997 0 : OStringBuffer aLine( 80 );
8998 : // insert XObject
8999 0 : aLine.append( "q /" );
9000 0 : aLine.append( aExtName );
9001 0 : aLine.append( " gs /" );
9002 0 : aLine.append( aTrName );
9003 0 : aLine.append( " Do Q\n" );
9004 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9005 :
9006 0 : pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
9007 0 : pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
9008 : }
9009 0 : }
9010 :
9011 0 : void PDFWriterImpl::drawRectangle( const Rectangle& rRect )
9012 : {
9013 0 : MARK( "drawRectangle" );
9014 :
9015 0 : updateGraphicsState();
9016 :
9017 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
9018 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
9019 0 : return;
9020 :
9021 0 : OStringBuffer aLine( 40 );
9022 0 : m_aPages.back().appendRect( rRect, aLine );
9023 :
9024 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
9025 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
9026 0 : aLine.append( " B*\n" );
9027 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9028 0 : aLine.append( " S\n" );
9029 : else
9030 0 : aLine.append( " f*\n" );
9031 :
9032 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9033 : }
9034 :
9035 0 : void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
9036 : {
9037 0 : MARK( "drawRectangle with rounded edges" );
9038 :
9039 0 : if( !nHorzRound && !nVertRound )
9040 0 : drawRectangle( rRect );
9041 :
9042 0 : updateGraphicsState();
9043 :
9044 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
9045 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
9046 0 : return;
9047 :
9048 0 : if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
9049 0 : nHorzRound = rRect.GetWidth()/2;
9050 0 : if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
9051 0 : nVertRound = rRect.GetHeight()/2;
9052 :
9053 0 : Point aPoints[16];
9054 0 : const double kappa = 0.5522847498;
9055 0 : const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
9056 0 : const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
9057 :
9058 0 : aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
9059 0 : aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
9060 0 : aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
9061 0 : aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() );
9062 :
9063 0 : aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
9064 0 : aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky );
9065 0 : aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
9066 0 : aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky );
9067 :
9068 0 : aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
9069 0 : aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() );
9070 0 : aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
9071 0 : aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
9072 :
9073 0 : aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
9074 0 : aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
9075 0 : aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
9076 0 : aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
9077 :
9078 :
9079 0 : OStringBuffer aLine( 80 );
9080 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
9081 0 : aLine.append( " m " );
9082 0 : m_aPages.back().appendPoint( aPoints[2], aLine );
9083 0 : aLine.append( " l " );
9084 0 : m_aPages.back().appendPoint( aPoints[3], aLine );
9085 0 : aLine.append( ' ' );
9086 0 : m_aPages.back().appendPoint( aPoints[4], aLine );
9087 0 : aLine.append( ' ' );
9088 0 : m_aPages.back().appendPoint( aPoints[5], aLine );
9089 0 : aLine.append( " c\n" );
9090 0 : m_aPages.back().appendPoint( aPoints[6], aLine );
9091 0 : aLine.append( " l " );
9092 0 : m_aPages.back().appendPoint( aPoints[7], aLine );
9093 0 : aLine.append( ' ' );
9094 0 : m_aPages.back().appendPoint( aPoints[8], aLine );
9095 0 : aLine.append( ' ' );
9096 0 : m_aPages.back().appendPoint( aPoints[9], aLine );
9097 0 : aLine.append( " c\n" );
9098 0 : m_aPages.back().appendPoint( aPoints[10], aLine );
9099 0 : aLine.append( " l " );
9100 0 : m_aPages.back().appendPoint( aPoints[11], aLine );
9101 0 : aLine.append( ' ' );
9102 0 : m_aPages.back().appendPoint( aPoints[12], aLine );
9103 0 : aLine.append( ' ' );
9104 0 : m_aPages.back().appendPoint( aPoints[13], aLine );
9105 0 : aLine.append( " c\n" );
9106 0 : m_aPages.back().appendPoint( aPoints[14], aLine );
9107 0 : aLine.append( " l " );
9108 0 : m_aPages.back().appendPoint( aPoints[15], aLine );
9109 0 : aLine.append( ' ' );
9110 0 : m_aPages.back().appendPoint( aPoints[0], aLine );
9111 0 : aLine.append( ' ' );
9112 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
9113 0 : aLine.append( " c " );
9114 :
9115 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
9116 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
9117 0 : aLine.append( "b*\n" );
9118 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9119 0 : aLine.append( "s\n" );
9120 : else
9121 0 : aLine.append( "f*\n" );
9122 :
9123 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9124 : }
9125 :
9126 0 : void PDFWriterImpl::drawEllipse( const Rectangle& rRect )
9127 : {
9128 0 : MARK( "drawEllipse" );
9129 :
9130 0 : updateGraphicsState();
9131 :
9132 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
9133 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
9134 0 : return;
9135 :
9136 0 : Point aPoints[12];
9137 0 : const double kappa = 0.5522847498;
9138 0 : const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
9139 0 : const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
9140 :
9141 0 : aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
9142 0 : aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
9143 0 : aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() );
9144 :
9145 0 : aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
9146 0 : aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky );
9147 0 : aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky );
9148 :
9149 0 : aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
9150 0 : aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() );
9151 0 : aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() );
9152 :
9153 0 : aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
9154 0 : aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky );
9155 0 : aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
9156 :
9157 0 : OStringBuffer aLine( 80 );
9158 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
9159 0 : aLine.append( " m " );
9160 0 : m_aPages.back().appendPoint( aPoints[2], aLine );
9161 0 : aLine.append( ' ' );
9162 0 : m_aPages.back().appendPoint( aPoints[3], aLine );
9163 0 : aLine.append( ' ' );
9164 0 : m_aPages.back().appendPoint( aPoints[4], aLine );
9165 0 : aLine.append( " c\n" );
9166 0 : m_aPages.back().appendPoint( aPoints[5], aLine );
9167 0 : aLine.append( ' ' );
9168 0 : m_aPages.back().appendPoint( aPoints[6], aLine );
9169 0 : aLine.append( ' ' );
9170 0 : m_aPages.back().appendPoint( aPoints[7], aLine );
9171 0 : aLine.append( " c\n" );
9172 0 : m_aPages.back().appendPoint( aPoints[8], aLine );
9173 0 : aLine.append( ' ' );
9174 0 : m_aPages.back().appendPoint( aPoints[9], aLine );
9175 0 : aLine.append( ' ' );
9176 0 : m_aPages.back().appendPoint( aPoints[10], aLine );
9177 0 : aLine.append( " c\n" );
9178 0 : m_aPages.back().appendPoint( aPoints[11], aLine );
9179 0 : aLine.append( ' ' );
9180 0 : m_aPages.back().appendPoint( aPoints[0], aLine );
9181 0 : aLine.append( ' ' );
9182 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
9183 0 : aLine.append( " c " );
9184 :
9185 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
9186 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
9187 0 : aLine.append( "b*\n" );
9188 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9189 0 : aLine.append( "s\n" );
9190 : else
9191 0 : aLine.append( "f*\n" );
9192 :
9193 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9194 : }
9195 :
9196 0 : static double calcAngle( const Rectangle& rRect, const Point& rPoint )
9197 : {
9198 0 : Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
9199 0 : (rRect.Top()+rRect.Bottom()+1)/2);
9200 0 : Point aPoint = rPoint - aOrigin;
9201 :
9202 0 : double fX = (double)aPoint.X();
9203 0 : double fY = (double)-aPoint.Y();
9204 :
9205 0 : if( rRect.GetWidth() > rRect.GetHeight() )
9206 0 : fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
9207 0 : else if( rRect.GetHeight() > rRect.GetWidth() )
9208 0 : fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
9209 0 : return atan2( fY, fX );
9210 : }
9211 :
9212 0 : void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
9213 : {
9214 0 : MARK( "drawArc" );
9215 :
9216 0 : updateGraphicsState();
9217 :
9218 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
9219 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
9220 0 : return;
9221 :
9222 : // calculate start and stop angles
9223 0 : const double fStartAngle = calcAngle( rRect, rStart );
9224 0 : double fStopAngle = calcAngle( rRect, rStop );
9225 0 : while( fStopAngle < fStartAngle )
9226 0 : fStopAngle += 2.0*M_PI;
9227 0 : const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
9228 0 : const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
9229 0 : const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
9230 0 : const double halfWidth = (double)rRect.GetWidth()/2.0;
9231 0 : const double halfHeight = (double)rRect.GetHeight()/2.0;
9232 :
9233 0 : const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
9234 0 : (rRect.Top()+rRect.Bottom()+1)/2 );
9235 :
9236 0 : OStringBuffer aLine( 30*nFragments );
9237 0 : Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
9238 0 : -(int)(halfHeight * sin(fStartAngle) ) );
9239 0 : aPoint += aCenter;
9240 0 : m_aPages.back().appendPoint( aPoint, aLine );
9241 0 : aLine.append( " m " );
9242 0 : if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
9243 : {
9244 0 : for( int i = 0; i < nFragments; i++ )
9245 : {
9246 0 : const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
9247 0 : const double fStopFragment = fStartFragment + fFragmentDelta;
9248 0 : aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
9249 0 : -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
9250 0 : aPoint += aCenter;
9251 0 : m_aPages.back().appendPoint( aPoint, aLine );
9252 0 : aLine.append( ' ' );
9253 :
9254 0 : aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
9255 0 : -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
9256 0 : aPoint += aCenter;
9257 0 : m_aPages.back().appendPoint( aPoint, aLine );
9258 0 : aLine.append( ' ' );
9259 :
9260 0 : aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
9261 0 : -(int)(halfHeight * sin(fStopFragment) ) );
9262 0 : aPoint += aCenter;
9263 0 : m_aPages.back().appendPoint( aPoint, aLine );
9264 0 : aLine.append( " c\n" );
9265 : }
9266 : }
9267 0 : if( bWithChord || bWithPie )
9268 : {
9269 0 : if( bWithPie )
9270 : {
9271 0 : m_aPages.back().appendPoint( aCenter, aLine );
9272 0 : aLine.append( " l " );
9273 : }
9274 0 : aLine.append( "h " );
9275 : }
9276 0 : if( ! bWithChord && ! bWithPie )
9277 0 : aLine.append( "S\n" );
9278 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
9279 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
9280 0 : aLine.append( "B*\n" );
9281 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9282 0 : aLine.append( "S\n" );
9283 : else
9284 0 : aLine.append( "f*\n" );
9285 :
9286 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9287 : }
9288 :
9289 0 : void PDFWriterImpl::drawPolyLine( const Polygon& rPoly )
9290 : {
9291 0 : MARK( "drawPolyLine" );
9292 :
9293 0 : sal_uInt16 nPoints = rPoly.GetSize();
9294 0 : if( nPoints < 2 )
9295 : return;
9296 :
9297 0 : updateGraphicsState();
9298 :
9299 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9300 : return;
9301 :
9302 0 : OStringBuffer aLine( 20 * nPoints );
9303 0 : m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
9304 0 : aLine.append( "S\n" );
9305 :
9306 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9307 : }
9308 :
9309 0 : void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo )
9310 : {
9311 0 : MARK( "drawPolyLine with LineInfo" );
9312 :
9313 0 : updateGraphicsState();
9314 :
9315 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9316 0 : return;
9317 :
9318 0 : OStringBuffer aLine;
9319 0 : aLine.append( "q " );
9320 0 : if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
9321 : {
9322 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9323 0 : drawPolyLine( rPoly );
9324 0 : writeBuffer( "Q\n", 2 );
9325 : }
9326 : else
9327 : {
9328 0 : PDFWriter::ExtLineInfo aInfo;
9329 0 : convertLineInfoToExtLineInfo( rInfo, aInfo );
9330 0 : drawPolyLine( rPoly, aInfo );
9331 0 : }
9332 : }
9333 :
9334 0 : void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
9335 : {
9336 : DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" );
9337 0 : rOut.m_fLineWidth = rIn.GetWidth();
9338 0 : rOut.m_fTransparency = 0.0;
9339 0 : rOut.m_eCap = PDFWriter::capButt;
9340 0 : rOut.m_eJoin = PDFWriter::joinMiter;
9341 0 : rOut.m_fMiterLimit = 10;
9342 0 : rOut.m_aDashArray.clear();
9343 :
9344 : // add DashDot to DashArray
9345 0 : const int nDashes = rIn.GetDashCount();
9346 0 : const int nDashLen = rIn.GetDashLen();
9347 0 : const int nDistance = rIn.GetDistance();
9348 :
9349 0 : for( int n = 0; n < nDashes; n++ )
9350 : {
9351 0 : rOut.m_aDashArray.push_back( nDashLen );
9352 0 : rOut.m_aDashArray.push_back( nDistance );
9353 : }
9354 0 : const int nDots = rIn.GetDotCount();
9355 0 : const int nDotLen = rIn.GetDotLen();
9356 :
9357 0 : for( int n = 0; n < nDots; n++ )
9358 : {
9359 0 : rOut.m_aDashArray.push_back( nDotLen );
9360 0 : rOut.m_aDashArray.push_back( nDistance );
9361 : }
9362 :
9363 : // add LineJoin
9364 0 : switch(rIn.GetLineJoin())
9365 : {
9366 : case basegfx::B2DLINEJOIN_BEVEL :
9367 : {
9368 0 : rOut.m_eJoin = PDFWriter::joinBevel;
9369 0 : break;
9370 : }
9371 : default : // basegfx::B2DLINEJOIN_NONE :
9372 : // Pdf has no 'none' lineJoin, default is miter
9373 : case basegfx::B2DLINEJOIN_MIDDLE :
9374 : case basegfx::B2DLINEJOIN_MITER :
9375 : {
9376 0 : rOut.m_eJoin = PDFWriter::joinMiter;
9377 0 : break;
9378 : }
9379 : case basegfx::B2DLINEJOIN_ROUND :
9380 : {
9381 0 : rOut.m_eJoin = PDFWriter::joinRound;
9382 0 : break;
9383 : }
9384 : }
9385 :
9386 : // add LineCap
9387 0 : switch(rIn.GetLineCap())
9388 : {
9389 : default: /* com::sun::star::drawing::LineCap_BUTT */
9390 : {
9391 0 : rOut.m_eCap = PDFWriter::capButt;
9392 0 : break;
9393 : }
9394 : case com::sun::star::drawing::LineCap_ROUND:
9395 : {
9396 0 : rOut.m_eCap = PDFWriter::capRound;
9397 0 : break;
9398 : }
9399 : case com::sun::star::drawing::LineCap_SQUARE:
9400 : {
9401 0 : rOut.m_eCap = PDFWriter::capSquare;
9402 0 : break;
9403 : }
9404 : }
9405 0 : }
9406 :
9407 0 : void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
9408 : {
9409 0 : MARK( "drawPolyLine with ExtLineInfo" );
9410 :
9411 0 : updateGraphicsState();
9412 :
9413 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9414 : return;
9415 :
9416 0 : if( rInfo.m_fTransparency >= 1.0 )
9417 : return;
9418 :
9419 0 : if( rInfo.m_fTransparency != 0.0 )
9420 0 : beginTransparencyGroup();
9421 :
9422 0 : OStringBuffer aLine;
9423 0 : aLine.append( "q " );
9424 0 : m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
9425 0 : aLine.append( " w" );
9426 0 : if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader
9427 : {
9428 0 : switch( rInfo.m_eCap )
9429 : {
9430 : default:
9431 0 : case PDFWriter::capButt: aLine.append( " 0 J" );break;
9432 0 : case PDFWriter::capRound: aLine.append( " 1 J" );break;
9433 0 : case PDFWriter::capSquare: aLine.append( " 2 J" );break;
9434 : }
9435 0 : switch( rInfo.m_eJoin )
9436 : {
9437 : default:
9438 : case PDFWriter::joinMiter:
9439 : {
9440 0 : double fLimit = rInfo.m_fMiterLimit;
9441 0 : if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
9442 0 : fLimit = fLimit / rInfo.m_fLineWidth;
9443 0 : if( fLimit < 1.0 )
9444 0 : fLimit = 1.0;
9445 0 : aLine.append( " 0 j " );
9446 0 : appendDouble( fLimit, aLine );
9447 0 : aLine.append( " M" );
9448 : }
9449 0 : break;
9450 0 : case PDFWriter::joinRound: aLine.append( " 1 j" );break;
9451 0 : case PDFWriter::joinBevel: aLine.append( " 2 j" );break;
9452 : }
9453 0 : if( rInfo.m_aDashArray.size() > 0 )
9454 : {
9455 0 : aLine.append( " [ " );
9456 0 : for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
9457 0 : it != rInfo.m_aDashArray.end(); ++it )
9458 : {
9459 0 : m_aPages.back().appendMappedLength( *it, aLine );
9460 0 : aLine.append( ' ' );
9461 : }
9462 0 : aLine.append( "] 0 d" );
9463 : }
9464 0 : aLine.append( "\n" );
9465 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9466 0 : drawPolyLine( rPoly );
9467 : }
9468 : else
9469 : {
9470 0 : basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
9471 0 : basegfx::B2DPolyPolygon aPolyPoly;
9472 :
9473 0 : basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
9474 :
9475 : // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
9476 : // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
9477 : // this line needs to be removed and the loop below adapted accordingly
9478 0 : aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
9479 :
9480 0 : const sal_uInt32 nPolygonCount(aPolyPoly.count());
9481 :
9482 0 : for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
9483 : {
9484 0 : aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
9485 0 : aPoly = aPolyPoly.getB2DPolygon( nPoly );
9486 0 : const sal_uInt32 nPointCount(aPoly.count());
9487 :
9488 0 : if(nPointCount)
9489 : {
9490 0 : const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
9491 0 : basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
9492 :
9493 0 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
9494 : {
9495 0 : if( a > 0 )
9496 0 : aLine.append( " " );
9497 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
9498 0 : const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
9499 :
9500 0 : m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
9501 : FRound(aCurrent.getY()) ),
9502 0 : aLine );
9503 0 : aLine.append( " m " );
9504 0 : m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
9505 : FRound(aNext.getY()) ),
9506 0 : aLine );
9507 0 : aLine.append( " l" );
9508 :
9509 : // prepare next edge
9510 0 : aCurrent = aNext;
9511 0 : }
9512 : }
9513 : }
9514 0 : aLine.append( " S " );
9515 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9516 : }
9517 0 : writeBuffer( "Q\n", 2 );
9518 :
9519 0 : if( rInfo.m_fTransparency != 0.0 )
9520 : {
9521 : // FIXME: actually this may be incorrect with bezier polygons
9522 0 : Rectangle aBoundRect( rPoly.GetBoundRect() );
9523 : // avoid clipping with thick lines
9524 0 : if( rInfo.m_fLineWidth > 0.0 )
9525 : {
9526 0 : sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
9527 0 : aBoundRect.Top() -= nLW;
9528 0 : aBoundRect.Left() -= nLW;
9529 0 : aBoundRect.Right() += nLW;
9530 0 : aBoundRect.Bottom() += nLW;
9531 : }
9532 0 : endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) );
9533 0 : }
9534 : }
9535 :
9536 0 : void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
9537 : {
9538 0 : MARK( "drawPixel" );
9539 :
9540 0 : Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
9541 :
9542 0 : if( aColor == Color( COL_TRANSPARENT ) )
9543 0 : return;
9544 :
9545 : // pixels are drawn in line color, so have to set
9546 : // the nonstroking color to line color
9547 0 : Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
9548 0 : setFillColor( aColor );
9549 :
9550 0 : updateGraphicsState();
9551 :
9552 0 : OStringBuffer aLine( 20 );
9553 0 : m_aPages.back().appendPoint( rPoint, aLine );
9554 0 : aLine.append( ' ' );
9555 0 : appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine );
9556 0 : aLine.append( ' ' );
9557 0 : appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine );
9558 0 : aLine.append( " re f\n" );
9559 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9560 :
9561 0 : setFillColor( aOldFillColor );
9562 : }
9563 :
9564 : class AccessReleaser
9565 : {
9566 : BitmapReadAccess* m_pAccess;
9567 : public:
9568 0 : AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){}
9569 0 : ~AccessReleaser() { delete m_pAccess; }
9570 : };
9571 :
9572 0 : bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
9573 : {
9574 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
9575 :
9576 0 : bool bFlateFilter = compressStream( rObject.m_pContentStream );
9577 0 : rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
9578 0 : sal_uLong nSize = rObject.m_pContentStream->Tell();
9579 0 : rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
9580 : #if OSL_DEBUG_LEVEL > 1
9581 : emitComment( "PDFWriterImpl::writeTransparentObject" );
9582 : #endif
9583 0 : OStringBuffer aLine( 512 );
9584 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
9585 0 : aLine.append( rObject.m_nObject );
9586 : aLine.append( " 0 obj\n"
9587 : "<</Type/XObject\n"
9588 : "/Subtype/Form\n"
9589 0 : "/BBox[ " );
9590 0 : appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
9591 0 : aLine.append( ' ' );
9592 0 : appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
9593 0 : aLine.append( ' ' );
9594 0 : appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
9595 0 : aLine.append( ' ' );
9596 0 : appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
9597 0 : aLine.append( " ]\n" );
9598 0 : if( ! rObject.m_pSoftMaskStream )
9599 : {
9600 0 : if( ! m_bIsPDF_A1 )
9601 : {
9602 0 : aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
9603 : }
9604 : }
9605 : /* #i42884# the PDF reference recommends that each Form XObject
9606 : * should have a resource dict; alas if that is the same object
9607 : * as the one of the page it triggers an endless recursion in
9608 : * acroread 5 (6 and up have that fixed). Since we have only one
9609 : * resource dict anyway, let's use the one from the page by NOT
9610 : * emitting a Resources entry.
9611 : */
9612 :
9613 0 : aLine.append( "/Length " );
9614 0 : aLine.append( (sal_Int32)(nSize) );
9615 0 : aLine.append( "\n" );
9616 0 : if( bFlateFilter )
9617 0 : aLine.append( "/Filter/FlateDecode\n" );
9618 : aLine.append( ">>\n"
9619 0 : "stream\n" );
9620 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9621 0 : checkAndEnableStreamEncryption( rObject.m_nObject );
9622 0 : CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
9623 0 : disableStreamEncryption();
9624 0 : aLine.setLength( 0 );
9625 : aLine.append( "\n"
9626 : "endstream\n"
9627 0 : "endobj\n\n" );
9628 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9629 :
9630 : // write ExtGState dict for this XObject
9631 0 : aLine.setLength( 0 );
9632 0 : aLine.append( rObject.m_nExtGStateObject );
9633 : aLine.append( " 0 obj\n"
9634 0 : "<<" );
9635 0 : if( ! rObject.m_pSoftMaskStream )
9636 : {
9637 : //i59651
9638 0 : if( m_bIsPDF_A1 )
9639 : {
9640 0 : aLine.append( "/CA 1.0/ca 1.0" );
9641 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9642 : }
9643 : else
9644 : {
9645 0 : aLine.append( "/CA " );
9646 0 : appendDouble( rObject.m_fAlpha, aLine );
9647 : aLine.append( "\n"
9648 0 : " /ca " );
9649 0 : appendDouble( rObject.m_fAlpha, aLine );
9650 : }
9651 0 : aLine.append( "\n" );
9652 : }
9653 : else
9654 : {
9655 0 : if( m_bIsPDF_A1 )
9656 : {
9657 0 : aLine.append( "/SMask/None" );
9658 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9659 : }
9660 : else
9661 : {
9662 0 : rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
9663 0 : sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
9664 0 : rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
9665 0 : sal_Int32 nMaskObject = createObject();
9666 0 : aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
9667 0 : aLine.append( nMaskObject );
9668 0 : aLine.append( " 0 R>>\n" );
9669 :
9670 0 : OStringBuffer aMask;
9671 0 : aMask.append( nMaskObject );
9672 : aMask.append( " 0 obj\n"
9673 : "<</Type/XObject\n"
9674 : "/Subtype/Form\n"
9675 0 : "/BBox[" );
9676 0 : appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
9677 0 : aMask.append( ' ' );
9678 0 : appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
9679 0 : aMask.append( ' ' );
9680 0 : appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
9681 0 : aMask.append( ' ' );
9682 0 : appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
9683 0 : aMask.append( "]\n" );
9684 :
9685 : /* #i42884# see above */
9686 0 : aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
9687 0 : aMask.append( "/Length " );
9688 0 : aMask.append( nMaskSize );
9689 : aMask.append( ">>\n"
9690 0 : "stream\n" );
9691 0 : CHECK_RETURN( updateObject( nMaskObject ) );
9692 0 : checkAndEnableStreamEncryption( nMaskObject );
9693 0 : CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9694 0 : CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
9695 0 : disableStreamEncryption();
9696 0 : aMask.setLength( 0 );
9697 : aMask.append( "\nendstream\n"
9698 0 : "endobj\n\n" );
9699 0 : CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9700 : }
9701 : }
9702 : aLine.append( ">>\n"
9703 0 : "endobj\n\n" );
9704 0 : CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) );
9705 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9706 :
9707 0 : return true;
9708 : }
9709 :
9710 0 : bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
9711 : {
9712 0 : sal_Int32 nFunctionObject = createObject();
9713 0 : CHECK_RETURN( updateObject( nFunctionObject ) );
9714 :
9715 0 : VirtualDevice aDev;
9716 0 : aDev.SetOutputSizePixel( rObject.m_aSize );
9717 0 : aDev.SetMapMode( MapMode( MAP_PIXEL ) );
9718 0 : if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9719 0 : aDev.SetDrawMode( aDev.GetDrawMode() |
9720 : ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT |
9721 0 : DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) );
9722 0 : aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
9723 :
9724 0 : Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize );
9725 0 : BitmapReadAccess* pAccess = aSample.AcquireReadAccess();
9726 0 : AccessReleaser aReleaser( pAccess );
9727 :
9728 0 : Size aSize = aSample.GetSizePixel();
9729 :
9730 0 : sal_Int32 nStreamLengthObject = createObject();
9731 : #if OSL_DEBUG_LEVEL > 1
9732 : emitComment( "PDFWriterImpl::writeGradientFunction" );
9733 : #endif
9734 0 : OStringBuffer aLine( 120 );
9735 0 : aLine.append( nFunctionObject );
9736 : aLine.append( " 0 obj\n"
9737 : "<</FunctionType 0\n"
9738 : "/Domain[ 0 1 0 1 ]\n"
9739 0 : "/Size[ " );
9740 0 : aLine.append( (sal_Int32)aSize.Width() );
9741 0 : aLine.append( ' ' );
9742 0 : aLine.append( (sal_Int32)aSize.Height() );
9743 : aLine.append( " ]\n"
9744 : "/BitsPerSample 8\n"
9745 : "/Range[ 0 1 0 1 0 1 ]\n"
9746 : "/Order 3\n"
9747 0 : "/Length " );
9748 0 : aLine.append( nStreamLengthObject );
9749 : aLine.append( " 0 R\n"
9750 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9751 : "/Filter/FlateDecode"
9752 : #endif
9753 : ">>\n"
9754 0 : "stream\n" );
9755 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9756 :
9757 0 : sal_uInt64 nStartStreamPos = 0;
9758 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) );
9759 :
9760 0 : checkAndEnableStreamEncryption( nFunctionObject );
9761 0 : beginCompression();
9762 0 : for( int y = aSize.Height()-1; y >= 0; y-- )
9763 : {
9764 0 : for( int x = 0; x < aSize.Width(); x++ )
9765 : {
9766 : sal_uInt8 aCol[3];
9767 0 : BitmapColor aColor = pAccess->GetColor( y, x );
9768 0 : aCol[0] = aColor.GetRed();
9769 0 : aCol[1] = aColor.GetGreen();
9770 0 : aCol[2] = aColor.GetBlue();
9771 0 : CHECK_RETURN( writeBuffer( aCol, 3 ) );
9772 0 : }
9773 : }
9774 0 : endCompression();
9775 0 : disableStreamEncryption();
9776 :
9777 0 : sal_uInt64 nEndStreamPos = 0;
9778 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) );
9779 :
9780 0 : aLine.setLength( 0 );
9781 0 : aLine.append( "\nendstream\nendobj\n\n" );
9782 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9783 :
9784 : // write stream length
9785 0 : CHECK_RETURN( updateObject( nStreamLengthObject ) );
9786 0 : aLine.setLength( 0 );
9787 0 : aLine.append( nStreamLengthObject );
9788 0 : aLine.append( " 0 obj\n" );
9789 0 : aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
9790 0 : aLine.append( "\nendobj\n\n" );
9791 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9792 :
9793 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
9794 0 : aLine.setLength( 0 );
9795 0 : aLine.append( rObject.m_nObject );
9796 : aLine.append( " 0 obj\n"
9797 : "<</ShadingType 1\n"
9798 : "/ColorSpace/DeviceRGB\n"
9799 : "/AntiAlias true\n"
9800 : "/Domain[ 0 1 0 1 ]\n"
9801 0 : "/Matrix[ " );
9802 0 : aLine.append( (sal_Int32)aSize.Width() );
9803 0 : aLine.append( " 0 0 " );
9804 0 : aLine.append( (sal_Int32)aSize.Height() );
9805 : aLine.append( " 0 0 ]\n"
9806 0 : "/Function " );
9807 0 : aLine.append( nFunctionObject );
9808 : aLine.append( " 0 R\n"
9809 : ">>\n"
9810 0 : "endobj\n\n" );
9811 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9812 :
9813 0 : return true;
9814 : }
9815 :
9816 0 : bool PDFWriterImpl::writeJPG( JPGEmit& rObject )
9817 : {
9818 0 : CHECK_RETURN( rObject.m_pStream );
9819 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
9820 :
9821 0 : sal_Int32 nLength = 0;
9822 0 : rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
9823 0 : nLength = rObject.m_pStream->Tell();
9824 0 : rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
9825 :
9826 0 : sal_Int32 nMaskObject = 0;
9827 0 : if( !!rObject.m_aMask )
9828 : {
9829 0 : if( rObject.m_aMask.GetBitCount() == 1 ||
9830 0 : ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651
9831 : )
9832 : {
9833 0 : nMaskObject = createObject();
9834 : }
9835 0 : else if( m_bIsPDF_A1 )
9836 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9837 0 : else if( m_aContext.Version < PDFWriter::PDF_1_4 )
9838 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
9839 :
9840 : }
9841 : #if OSL_DEBUG_LEVEL > 1
9842 : emitComment( "PDFWriterImpl::writeJPG" );
9843 : #endif
9844 :
9845 0 : OStringBuffer aLine(200);
9846 0 : aLine.append( rObject.m_nObject );
9847 : aLine.append( " 0 obj\n"
9848 0 : "<</Type/XObject/Subtype/Image/Width " );
9849 0 : aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
9850 0 : aLine.append( " /Height " );
9851 0 : aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
9852 0 : aLine.append( " /BitsPerComponent 8 " );
9853 0 : if( rObject.m_bTrueColor )
9854 0 : aLine.append( "/ColorSpace/DeviceRGB" );
9855 : else
9856 0 : aLine.append( "/ColorSpace/DeviceGray" );
9857 0 : aLine.append( "/Filter/DCTDecode/Length " );
9858 0 : aLine.append( nLength );
9859 0 : if( nMaskObject )
9860 : {
9861 0 : aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
9862 0 : aLine.append( nMaskObject );
9863 0 : aLine.append( " 0 R " );
9864 : }
9865 0 : aLine.append( ">>\nstream\n" );
9866 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9867 :
9868 0 : checkAndEnableStreamEncryption( rObject.m_nObject );
9869 0 : CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
9870 0 : disableStreamEncryption();
9871 :
9872 0 : aLine.setLength( 0 );
9873 0 : aLine.append( "\nendstream\nendobj\n\n" );
9874 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9875 :
9876 0 : if( nMaskObject )
9877 : {
9878 0 : BitmapEmit aEmit;
9879 0 : aEmit.m_nObject = nMaskObject;
9880 0 : if( rObject.m_aMask.GetBitCount() == 1 )
9881 0 : aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
9882 0 : else if( rObject.m_aMask.GetBitCount() == 8 )
9883 0 : aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
9884 0 : writeBitmapObject( aEmit, true );
9885 : }
9886 :
9887 0 : return true;
9888 : }
9889 :
9890 0 : bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
9891 : {
9892 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
9893 :
9894 0 : Bitmap aBitmap;
9895 0 : Color aTransparentColor( COL_TRANSPARENT );
9896 0 : bool bWriteMask = false;
9897 0 : if( ! bMask )
9898 : {
9899 0 : aBitmap = rObject.m_aBitmap.GetBitmap();
9900 0 : if( rObject.m_aBitmap.IsAlpha() )
9901 : {
9902 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 )
9903 0 : bWriteMask = true;
9904 : // else draw without alpha channel
9905 : }
9906 : else
9907 : {
9908 0 : switch( rObject.m_aBitmap.GetTransparentType() )
9909 : {
9910 : case TRANSPARENT_NONE:
9911 : // comes from drawMask function
9912 0 : if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask )
9913 0 : bMask = true;
9914 0 : break;
9915 : case TRANSPARENT_COLOR:
9916 0 : aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
9917 0 : break;
9918 : case TRANSPARENT_BITMAP:
9919 0 : bWriteMask = true;
9920 0 : break;
9921 : }
9922 : }
9923 : }
9924 : else
9925 : {
9926 0 : if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
9927 : {
9928 0 : aBitmap = rObject.m_aBitmap.GetMask();
9929 0 : aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
9930 : DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
9931 : }
9932 0 : else if( aBitmap.GetBitCount() != 8 )
9933 : {
9934 0 : aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap();
9935 0 : aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
9936 : DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" );
9937 : }
9938 : }
9939 :
9940 0 : BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();
9941 0 : AccessReleaser aReleaser( pAccess );
9942 :
9943 : bool bTrueColor;
9944 : sal_Int32 nBitsPerComponent;
9945 0 : switch( aBitmap.GetBitCount() )
9946 : {
9947 : case 1:
9948 : case 2:
9949 : case 4:
9950 : case 8:
9951 0 : bTrueColor = false;
9952 0 : nBitsPerComponent = aBitmap.GetBitCount();
9953 0 : break;
9954 : default:
9955 0 : bTrueColor = true;
9956 0 : nBitsPerComponent = 8;
9957 0 : break;
9958 : }
9959 :
9960 0 : sal_Int32 nStreamLengthObject = createObject();
9961 0 : sal_Int32 nMaskObject = 0;
9962 :
9963 : #if OSL_DEBUG_LEVEL > 1
9964 : emitComment( "PDFWriterImpl::writeBitmapObject" );
9965 : #endif
9966 0 : OStringBuffer aLine(1024);
9967 0 : aLine.append( rObject.m_nObject );
9968 : aLine.append( " 0 obj\n"
9969 0 : "<</Type/XObject/Subtype/Image/Width " );
9970 0 : aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9971 0 : aLine.append( "/Height " );
9972 0 : aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
9973 0 : aLine.append( "/BitsPerComponent " );
9974 0 : aLine.append( nBitsPerComponent );
9975 0 : aLine.append( "/Length " );
9976 0 : aLine.append( nStreamLengthObject );
9977 0 : aLine.append( " 0 R\n" );
9978 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9979 0 : if( nBitsPerComponent != 1 )
9980 : {
9981 0 : aLine.append( "/Filter/FlateDecode" );
9982 : }
9983 : else
9984 : {
9985 0 : aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
9986 0 : aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9987 0 : aLine.append( ">>\n" );
9988 : }
9989 : #endif
9990 0 : if( ! bMask )
9991 : {
9992 0 : aLine.append( "/ColorSpace" );
9993 0 : if( bTrueColor )
9994 0 : aLine.append( "/DeviceRGB\n" );
9995 0 : else if( aBitmap.HasGreyPalette() )
9996 : {
9997 0 : aLine.append( "/DeviceGray\n" );
9998 0 : if( aBitmap.GetBitCount() == 1 )
9999 : {
10000 : // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
10001 0 : sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
10002 : DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
10003 0 : if( nBlackIndex == 1 )
10004 0 : aLine.append( "/Decode[1 0]\n" );
10005 : }
10006 : }
10007 : else
10008 : {
10009 0 : aLine.append( "[ /Indexed/DeviceRGB " );
10010 0 : aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
10011 0 : aLine.append( "\n<" );
10012 0 : if( m_aContext.Encryption.Encrypt() )
10013 : {
10014 0 : enableStringEncryption( rObject.m_nObject );
10015 : //check encryption buffer size
10016 0 : if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
10017 : {
10018 0 : int nChar = 0;
10019 : //fill the encryption buffer
10020 0 : for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
10021 : {
10022 0 : const BitmapColor& rColor = pAccess->GetPaletteColor( i );
10023 0 : m_pEncryptionBuffer[nChar++] = rColor.GetRed();
10024 0 : m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
10025 0 : m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
10026 : }
10027 : //encrypt the colorspace lookup table
10028 0 : rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
10029 : //now queue the data for output
10030 0 : nChar = 0;
10031 0 : for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
10032 : {
10033 0 : appendHex(m_pEncryptionBuffer[nChar++], aLine );
10034 0 : appendHex(m_pEncryptionBuffer[nChar++], aLine );
10035 0 : appendHex(m_pEncryptionBuffer[nChar++], aLine );
10036 : }
10037 : }
10038 : }
10039 : else //no encryption requested (PDF/A-1a program flow drops here)
10040 : {
10041 0 : for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
10042 : {
10043 0 : const BitmapColor& rColor = pAccess->GetPaletteColor( i );
10044 0 : appendHex( rColor.GetRed(), aLine );
10045 0 : appendHex( rColor.GetGreen(), aLine );
10046 0 : appendHex( rColor.GetBlue(), aLine );
10047 : }
10048 : }
10049 0 : aLine.append( ">\n]\n" );
10050 : }
10051 : }
10052 : else
10053 : {
10054 0 : if( aBitmap.GetBitCount() == 1 )
10055 : {
10056 0 : aLine.append( "/ImageMask true\n" );
10057 0 : sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
10058 : DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
10059 0 : if( nBlackIndex )
10060 0 : aLine.append( "/Decode[ 1 0 ]\n" );
10061 : else
10062 0 : aLine.append( "/Decode[ 0 1 ]\n" );
10063 : }
10064 0 : else if( aBitmap.GetBitCount() == 8 )
10065 : {
10066 : aLine.append( "/ColorSpace/DeviceGray\n"
10067 0 : "/Decode [ 1 0 ]\n" );
10068 : }
10069 : }
10070 :
10071 0 : if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651
10072 : {
10073 0 : if( bWriteMask )
10074 : {
10075 0 : nMaskObject = createObject();
10076 0 : if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 )
10077 0 : aLine.append( "/SMask " );
10078 : else
10079 0 : aLine.append( "/Mask " );
10080 0 : aLine.append( nMaskObject );
10081 0 : aLine.append( " 0 R\n" );
10082 : }
10083 0 : else if( aTransparentColor != Color( COL_TRANSPARENT ) )
10084 : {
10085 0 : aLine.append( "/Mask[ " );
10086 0 : if( bTrueColor )
10087 : {
10088 0 : aLine.append( (sal_Int32)aTransparentColor.GetRed() );
10089 0 : aLine.append( ' ' );
10090 0 : aLine.append( (sal_Int32)aTransparentColor.GetRed() );
10091 0 : aLine.append( ' ' );
10092 0 : aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
10093 0 : aLine.append( ' ' );
10094 0 : aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
10095 0 : aLine.append( ' ' );
10096 0 : aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
10097 0 : aLine.append( ' ' );
10098 0 : aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
10099 : }
10100 : else
10101 : {
10102 0 : sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
10103 0 : aLine.append( nIndex );
10104 : }
10105 0 : aLine.append( " ]\n" );
10106 0 : }
10107 : }
10108 0 : else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) )
10109 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
10110 :
10111 : aLine.append( ">>\n"
10112 0 : "stream\n" );
10113 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10114 0 : sal_uInt64 nStartPos = 0;
10115 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) );
10116 :
10117 0 : checkAndEnableStreamEncryption( rObject.m_nObject );
10118 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
10119 0 : if( nBitsPerComponent == 1 )
10120 : {
10121 0 : writeG4Stream( pAccess );
10122 : }
10123 : else
10124 : #endif
10125 : {
10126 0 : beginCompression();
10127 0 : if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
10128 : {
10129 0 : const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U );
10130 :
10131 0 : for( int i = 0; i < pAccess->Height(); i++ )
10132 : {
10133 0 : CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
10134 : }
10135 : }
10136 : else
10137 : {
10138 0 : const int nScanLineBytes = pAccess->Width()*3;
10139 0 : boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] );
10140 0 : for( int y = 0; y < pAccess->Height(); y++ )
10141 : {
10142 0 : for( int x = 0; x < pAccess->Width(); x++ )
10143 : {
10144 0 : BitmapColor aColor = pAccess->GetColor( y, x );
10145 0 : pCol[3*x+0] = aColor.GetRed();
10146 0 : pCol[3*x+1] = aColor.GetGreen();
10147 0 : pCol[3*x+2] = aColor.GetBlue();
10148 0 : }
10149 0 : CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) );
10150 0 : }
10151 : }
10152 0 : endCompression();
10153 : }
10154 0 : disableStreamEncryption();
10155 :
10156 0 : sal_uInt64 nEndPos = 0;
10157 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) );
10158 0 : aLine.setLength( 0 );
10159 0 : aLine.append( "\nendstream\nendobj\n\n" );
10160 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10161 0 : CHECK_RETURN( updateObject( nStreamLengthObject ) );
10162 0 : aLine.setLength( 0 );
10163 0 : aLine.append( nStreamLengthObject );
10164 0 : aLine.append( " 0 obj\n" );
10165 0 : aLine.append( (sal_Int64)(nEndPos-nStartPos) );
10166 0 : aLine.append( "\nendobj\n\n" );
10167 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10168 :
10169 0 : if( nMaskObject )
10170 : {
10171 0 : BitmapEmit aEmit;
10172 0 : aEmit.m_nObject = nMaskObject;
10173 0 : aEmit.m_aBitmap = rObject.m_aBitmap;
10174 0 : return writeBitmapObject( aEmit, true );
10175 : }
10176 :
10177 0 : return true;
10178 : }
10179 :
10180 0 : void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask )
10181 : {
10182 0 : MARK( "drawJPGBitmap" );
10183 :
10184 0 : OStringBuffer aLine( 80 );
10185 0 : updateGraphicsState();
10186 :
10187 : // #i40055# sanity check
10188 0 : if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
10189 : return;
10190 0 : if( ! (rSizePixel.Width() && rSizePixel.Height()) )
10191 : return;
10192 :
10193 0 : rDCTData.Seek( 0 );
10194 0 : if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale )
10195 : {
10196 : // need to convert to grayscale;
10197 : // load stream to bitmap and draw the bitmap instead
10198 0 : Graphic aGraphic;
10199 0 : GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG );
10200 0 : Bitmap aBmp( aGraphic.GetBitmap() );
10201 0 : if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() )
10202 : {
10203 0 : BitmapEx aBmpEx( aBmp, rMask );
10204 0 : drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx );
10205 : }
10206 : else
10207 0 : drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp );
10208 0 : return;
10209 : }
10210 :
10211 0 : SvMemoryStream* pStream = new SvMemoryStream;
10212 0 : *pStream << rDCTData;
10213 0 : pStream->Seek( STREAM_SEEK_TO_END );
10214 :
10215 0 : BitmapID aID;
10216 0 : aID.m_aPixelSize = rSizePixel;
10217 0 : aID.m_nSize = pStream->Tell();
10218 0 : pStream->Seek( STREAM_SEEK_TO_BEGIN );
10219 0 : aID.m_nChecksum = rtl_crc32( 0, pStream->GetData(), aID.m_nSize );
10220 0 : if( ! rMask.IsEmpty() )
10221 0 : aID.m_nMaskChecksum = rMask.GetChecksum();
10222 :
10223 0 : std::list< JPGEmit >::const_iterator it;
10224 0 : for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
10225 : ;
10226 0 : if( it == m_aJPGs.end() )
10227 : {
10228 0 : m_aJPGs.push_front( JPGEmit() );
10229 0 : JPGEmit& rEmit = m_aJPGs.front();
10230 0 : rEmit.m_nObject = createObject();
10231 0 : rEmit.m_aID = aID;
10232 0 : rEmit.m_pStream = pStream;
10233 0 : rEmit.m_bTrueColor = bIsTrueColor;
10234 0 : if( !! rMask && rMask.GetSizePixel() == rSizePixel )
10235 0 : rEmit.m_aMask = rMask;
10236 :
10237 0 : it = m_aJPGs.begin();
10238 : }
10239 : else
10240 0 : delete pStream;
10241 :
10242 0 : aLine.append( "q " );
10243 0 : sal_Int32 nCheckWidth = 0;
10244 0 : m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth );
10245 0 : aLine.append( " 0 0 " );
10246 0 : sal_Int32 nCheckHeight = 0;
10247 0 : m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight );
10248 0 : aLine.append( ' ' );
10249 0 : m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
10250 0 : aLine.append( " cm\n/Im" );
10251 0 : aLine.append( it->m_nObject );
10252 0 : aLine.append( " Do Q\n" );
10253 0 : if( nCheckWidth == 0 || nCheckHeight == 0 )
10254 : {
10255 : // #i97512# avoid invalid current matrix
10256 0 : aLine.setLength( 0 );
10257 0 : aLine.append( "\n%jpeg image /Im" );
10258 0 : aLine.append( it->m_nObject );
10259 0 : aLine.append( " scaled to zero size, omitted\n" );
10260 : }
10261 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10262 :
10263 0 : OStringBuffer aObjName( 16 );
10264 0 : aObjName.append( "Im" );
10265 0 : aObjName.append( it->m_nObject );
10266 0 : pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10267 :
10268 : }
10269 :
10270 0 : void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
10271 : {
10272 0 : OStringBuffer aLine( 80 );
10273 0 : updateGraphicsState();
10274 :
10275 0 : aLine.append( "q " );
10276 0 : if( rFillColor != Color( COL_TRANSPARENT ) )
10277 : {
10278 0 : appendNonStrokingColor( rFillColor, aLine );
10279 0 : aLine.append( ' ' );
10280 : }
10281 0 : sal_Int32 nCheckWidth = 0;
10282 0 : m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth );
10283 0 : aLine.append( " 0 0 " );
10284 0 : sal_Int32 nCheckHeight = 0;
10285 0 : m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight );
10286 0 : aLine.append( ' ' );
10287 0 : m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
10288 0 : aLine.append( " cm\n/Im" );
10289 0 : aLine.append( rBitmap.m_nObject );
10290 0 : aLine.append( " Do Q\n" );
10291 0 : if( nCheckWidth == 0 || nCheckHeight == 0 )
10292 : {
10293 : // #i97512# avoid invalid current matrix
10294 0 : aLine.setLength( 0 );
10295 0 : aLine.append( "\n%bitmap image /Im" );
10296 0 : aLine.append( rBitmap.m_nObject );
10297 0 : aLine.append( " scaled to zero size, omitted\n" );
10298 : }
10299 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10300 0 : }
10301 :
10302 0 : const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask )
10303 : {
10304 0 : BitmapEx aBitmap( i_rBitmap );
10305 0 : if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
10306 : {
10307 0 : BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS;
10308 0 : int nDepth = aBitmap.GetBitmap().GetBitCount();
10309 0 : if( nDepth <= 4 )
10310 0 : eConv = BMP_CONVERSION_4BIT_GREYS;
10311 0 : if( nDepth > 1 )
10312 0 : aBitmap.Convert( eConv );
10313 : }
10314 0 : BitmapID aID;
10315 0 : aID.m_aPixelSize = aBitmap.GetSizePixel();
10316 0 : aID.m_nSize = aBitmap.GetBitCount();
10317 0 : aID.m_nChecksum = aBitmap.GetBitmap().GetChecksum();
10318 0 : aID.m_nMaskChecksum = 0;
10319 0 : if( aBitmap.IsAlpha() )
10320 0 : aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum();
10321 : else
10322 : {
10323 0 : Bitmap aMask = aBitmap.GetMask();
10324 0 : if( ! aMask.IsEmpty() )
10325 0 : aID.m_nMaskChecksum = aMask.GetChecksum();
10326 : }
10327 0 : std::list< BitmapEmit >::const_iterator it;
10328 0 : for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
10329 : {
10330 0 : if( aID == it->m_aID )
10331 0 : break;
10332 : }
10333 0 : if( it == m_aBitmaps.end() )
10334 : {
10335 0 : m_aBitmaps.push_front( BitmapEmit() );
10336 0 : m_aBitmaps.front().m_aID = aID;
10337 0 : m_aBitmaps.front().m_aBitmap = aBitmap;
10338 0 : m_aBitmaps.front().m_nObject = createObject();
10339 0 : m_aBitmaps.front().m_bDrawMask = bDrawMask;
10340 0 : it = m_aBitmaps.begin();
10341 : }
10342 :
10343 0 : OStringBuffer aObjName( 16 );
10344 0 : aObjName.append( "Im" );
10345 0 : aObjName.append( it->m_nObject );
10346 0 : pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10347 :
10348 0 : return *it;
10349 : }
10350 :
10351 0 : void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap )
10352 : {
10353 0 : MARK( "drawBitmap (Bitmap)" );
10354 :
10355 : // #i40055# sanity check
10356 0 : if( ! (rDestSize.Width() && rDestSize.Height()) )
10357 0 : return;
10358 :
10359 0 : const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) );
10360 0 : drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10361 : }
10362 :
10363 0 : void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
10364 : {
10365 0 : MARK( "drawBitmap (BitmapEx)" );
10366 :
10367 : // #i40055# sanity check
10368 0 : if( ! (rDestSize.Width() && rDestSize.Height()) )
10369 0 : return;
10370 :
10371 0 : const BitmapEmit& rEmit = createBitmapEmit( rBitmap );
10372 0 : drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10373 : }
10374 :
10375 0 : sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
10376 : {
10377 0 : Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10378 : MapMode( MAP_POINT ),
10379 : getReferenceDevice(),
10380 0 : rSize ) );
10381 : // check if we already have this gradient
10382 0 : std::list<GradientEmit>::iterator it;
10383 : // rounding to point will generally lose some pixels
10384 : // round up to point boundary
10385 0 : aPtSize.Width()++;
10386 0 : aPtSize.Height()++;
10387 0 : for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
10388 : {
10389 0 : if( it->m_aGradient == rGradient )
10390 : {
10391 0 : if( it->m_aSize == aPtSize )
10392 0 : break;
10393 : }
10394 : }
10395 0 : if( it == m_aGradients.end() )
10396 : {
10397 0 : m_aGradients.push_front( GradientEmit() );
10398 0 : m_aGradients.front().m_aGradient = rGradient;
10399 0 : m_aGradients.front().m_nObject = createObject();
10400 0 : m_aGradients.front().m_aSize = aPtSize;
10401 0 : it = m_aGradients.begin();
10402 : }
10403 :
10404 0 : OStringBuffer aObjName( 16 );
10405 0 : aObjName.append( 'P' );
10406 0 : aObjName.append( it->m_nObject );
10407 0 : pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
10408 :
10409 0 : return it->m_nObject;
10410 : }
10411 :
10412 0 : void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient )
10413 : {
10414 0 : MARK( "drawGradient (Rectangle)" );
10415 :
10416 0 : if( m_aContext.Version == PDFWriter::PDF_1_2 )
10417 : {
10418 0 : drawRectangle( rRect );
10419 0 : return;
10420 : }
10421 :
10422 0 : sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
10423 :
10424 0 : Point aTranslate( rRect.BottomLeft() );
10425 0 : aTranslate += Point( 0, 1 );
10426 :
10427 0 : updateGraphicsState();
10428 :
10429 0 : OStringBuffer aLine( 80 );
10430 0 : aLine.append( "q 1 0 0 1 " );
10431 0 : m_aPages.back().appendPoint( aTranslate, aLine );
10432 0 : aLine.append( " cm " );
10433 : // if a stroke is appended reset the clip region before stroke
10434 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10435 0 : aLine.append( "q " );
10436 0 : aLine.append( "0 0 " );
10437 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10438 0 : aLine.append( ' ' );
10439 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10440 0 : aLine.append( " re W n\n" );
10441 :
10442 0 : aLine.append( "/P" );
10443 0 : aLine.append( nGradient );
10444 0 : aLine.append( " sh " );
10445 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10446 : {
10447 0 : aLine.append( "Q 0 0 " );
10448 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10449 0 : aLine.append( ' ' );
10450 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10451 0 : aLine.append( " re S " );
10452 : }
10453 0 : aLine.append( "Q\n" );
10454 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10455 : }
10456 :
10457 0 : void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
10458 : {
10459 0 : MARK( "drawHatch" );
10460 :
10461 0 : updateGraphicsState();
10462 :
10463 0 : if( rPolyPoly.Count() )
10464 : {
10465 0 : PolyPolygon aPolyPoly( rPolyPoly );
10466 :
10467 0 : aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
10468 0 : push( PUSH_LINECOLOR );
10469 0 : setLineColor( rHatch.GetColor() );
10470 0 : getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, sal_False );
10471 0 : pop();
10472 : }
10473 0 : }
10474 :
10475 0 : void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall )
10476 : {
10477 0 : MARK( "drawWallpaper" );
10478 :
10479 0 : bool bDrawColor = false;
10480 0 : bool bDrawGradient = false;
10481 0 : bool bDrawBitmap = false;
10482 :
10483 0 : BitmapEx aBitmap;
10484 0 : Point aBmpPos = rRect.TopLeft();
10485 0 : Size aBmpSize;
10486 0 : if( rWall.IsBitmap() )
10487 : {
10488 0 : aBitmap = rWall.GetBitmap();
10489 0 : aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
10490 0 : getMapMode(),
10491 : getReferenceDevice(),
10492 0 : aBitmap.GetPrefSize() );
10493 0 : Rectangle aRect( rRect );
10494 0 : if( rWall.IsRect() )
10495 : {
10496 0 : aRect = rWall.GetRect();
10497 0 : aBmpPos = aRect.TopLeft();
10498 0 : aBmpSize = aRect.GetSize();
10499 : }
10500 0 : if( rWall.GetStyle() != WALLPAPER_SCALE )
10501 : {
10502 0 : if( rWall.GetStyle() != WALLPAPER_TILE )
10503 : {
10504 0 : bDrawBitmap = true;
10505 0 : if( rWall.IsGradient() )
10506 0 : bDrawGradient = true;
10507 : else
10508 0 : bDrawColor = true;
10509 0 : switch( rWall.GetStyle() )
10510 : {
10511 : case WALLPAPER_TOPLEFT:
10512 0 : break;
10513 : case WALLPAPER_TOP:
10514 0 : aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10515 0 : break;
10516 : case WALLPAPER_LEFT:
10517 0 : aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10518 0 : break;
10519 : case WALLPAPER_TOPRIGHT:
10520 0 : aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10521 0 : break;
10522 : case WALLPAPER_CENTER:
10523 0 : aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10524 0 : aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10525 0 : break;
10526 : case WALLPAPER_RIGHT:
10527 0 : aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10528 0 : aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10529 0 : break;
10530 : case WALLPAPER_BOTTOMLEFT:
10531 0 : aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10532 0 : break;
10533 : case WALLPAPER_BOTTOM:
10534 0 : aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10535 0 : aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10536 0 : break;
10537 : case WALLPAPER_BOTTOMRIGHT:
10538 0 : aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10539 0 : aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10540 0 : break;
10541 : default: ;
10542 : }
10543 : }
10544 : else
10545 : {
10546 : // push the bitmap
10547 0 : const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) );
10548 :
10549 : // convert to page coordinates; this needs to be done here
10550 : // since the emit does not know the page anymore
10551 0 : Rectangle aConvertRect( aBmpPos, aBmpSize );
10552 0 : m_aPages.back().convertRect( aConvertRect );
10553 :
10554 0 : OStringBuffer aNameBuf(16);
10555 0 : aNameBuf.append( "Im" );
10556 0 : aNameBuf.append( rEmit.m_nObject );
10557 0 : OString aImageName( aNameBuf.makeStringAndClear() );
10558 :
10559 : // push the pattern
10560 0 : OStringBuffer aTilingStream( 32 );
10561 0 : appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
10562 0 : aTilingStream.append( " 0 0 " );
10563 0 : appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
10564 0 : aTilingStream.append( " 0 0 cm\n/" );
10565 0 : aTilingStream.append( aImageName );
10566 0 : aTilingStream.append( " Do\n" );
10567 :
10568 0 : m_aTilings.push_back( TilingEmit() );
10569 0 : m_aTilings.back().m_nObject = createObject();
10570 0 : m_aTilings.back().m_aRectangle = Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
10571 0 : m_aTilings.back().m_pTilingStream = new SvMemoryStream();
10572 0 : m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() );
10573 : // phase the tiling so wallpaper begins on upper left
10574 0 : m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
10575 0 : m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
10576 0 : m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
10577 :
10578 0 : updateGraphicsState();
10579 :
10580 0 : OStringBuffer aObjName( 16 );
10581 0 : aObjName.append( 'P' );
10582 0 : aObjName.append( m_aTilings.back().m_nObject );
10583 0 : OString aPatternName( aObjName.makeStringAndClear() );
10584 0 : pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
10585 :
10586 : // fill a rRect with the pattern
10587 0 : OStringBuffer aLine( 100 );
10588 0 : aLine.append( "q /Pattern cs /" );
10589 0 : aLine.append( aPatternName );
10590 0 : aLine.append( " scn " );
10591 0 : m_aPages.back().appendRect( rRect, aLine );
10592 0 : aLine.append( " f Q\n" );
10593 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10594 : }
10595 : }
10596 : else
10597 : {
10598 0 : aBmpPos = aRect.TopLeft();
10599 0 : aBmpSize = aRect.GetSize();
10600 0 : bDrawBitmap = true;
10601 : }
10602 :
10603 0 : if( aBitmap.IsTransparent() )
10604 : {
10605 0 : if( rWall.IsGradient() )
10606 0 : bDrawGradient = true;
10607 : else
10608 0 : bDrawColor = true;
10609 : }
10610 : }
10611 0 : else if( rWall.IsGradient() )
10612 0 : bDrawGradient = true;
10613 : else
10614 0 : bDrawColor = true;
10615 :
10616 0 : if( bDrawGradient )
10617 : {
10618 0 : drawGradient( rRect, rWall.GetGradient() );
10619 : }
10620 0 : if( bDrawColor )
10621 : {
10622 0 : Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
10623 0 : Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
10624 0 : setLineColor( Color( COL_TRANSPARENT ) );
10625 0 : setFillColor( rWall.GetColor() );
10626 0 : drawRectangle( rRect );
10627 0 : setLineColor( aOldLineColor );
10628 0 : setFillColor( aOldFillColor );
10629 : }
10630 0 : if( bDrawBitmap )
10631 : {
10632 : // set temporary clip region since aBmpPos and aBmpSize
10633 : // may be outside rRect
10634 0 : OStringBuffer aLine( 20 );
10635 0 : aLine.append( "q " );
10636 0 : m_aPages.back().appendRect( rRect, aLine );
10637 0 : aLine.append( " W n\n" );
10638 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10639 0 : drawBitmap( aBmpPos, aBmpSize, aBitmap );
10640 0 : writeBuffer( "Q\n", 2 );
10641 0 : }
10642 0 : }
10643 :
10644 0 : void PDFWriterImpl::updateGraphicsState()
10645 : {
10646 0 : OStringBuffer aLine( 256 );
10647 0 : GraphicsState& rNewState = m_aGraphicsStack.front();
10648 : // first set clip region since it might invalidate everything else
10649 :
10650 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) )
10651 : {
10652 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion;
10653 :
10654 0 : if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion ||
10655 0 : ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) )
10656 : {
10657 0 : if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() )
10658 : {
10659 0 : aLine.append( "Q " );
10660 : // invalidate everything but the clip region
10661 0 : m_aCurrentPDFState = GraphicsState();
10662 0 : rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion);
10663 : }
10664 0 : if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() )
10665 : {
10666 : // clip region is always stored in private PDF mapmode
10667 0 : MapMode aNewMapMode = rNewState.m_aMapMode;
10668 0 : rNewState.m_aMapMode = m_aMapMode;
10669 0 : getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10670 0 : m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10671 :
10672 0 : aLine.append( "q " );
10673 0 : m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine );
10674 0 : aLine.append( "W* n\n" );
10675 0 : rNewState.m_aMapMode = aNewMapMode;
10676 0 : getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10677 0 : m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10678 : }
10679 : }
10680 : }
10681 :
10682 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) )
10683 : {
10684 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode;
10685 0 : getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10686 : }
10687 :
10688 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) )
10689 : {
10690 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont;
10691 0 : getReferenceDevice()->SetFont( rNewState.m_aFont );
10692 0 : getReferenceDevice()->ImplNewFont();
10693 : }
10694 :
10695 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) )
10696 : {
10697 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode;
10698 0 : getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
10699 : }
10700 :
10701 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) )
10702 : {
10703 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage;
10704 0 : getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
10705 : }
10706 :
10707 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) )
10708 : {
10709 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor;
10710 0 : if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
10711 0 : rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
10712 : {
10713 0 : appendStrokingColor( rNewState.m_aLineColor, aLine );
10714 0 : aLine.append( "\n" );
10715 : }
10716 : }
10717 :
10718 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) )
10719 : {
10720 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor;
10721 0 : if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
10722 0 : rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
10723 : {
10724 0 : appendNonStrokingColor( rNewState.m_aFillColor, aLine );
10725 0 : aLine.append( "\n" );
10726 : }
10727 : }
10728 :
10729 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) )
10730 : {
10731 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent;
10732 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent )
10733 : {
10734 : // TODO: switch extended graphicsstate
10735 : }
10736 : }
10737 :
10738 : // everything is up to date now
10739 0 : m_aCurrentPDFState = m_aGraphicsStack.front();
10740 0 : if( aLine.getLength() )
10741 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10742 0 : }
10743 :
10744 : /* #i47544# imitate OutputDevice behaviour:
10745 : * if a font with a nontransparent color is set, it overwrites the current
10746 : * text color. OTOH setting the text color will overwrite the color of the font.
10747 : */
10748 0 : void PDFWriterImpl::setFont( const Font& rFont )
10749 : {
10750 0 : Color aColor = rFont.GetColor();
10751 0 : if( aColor == Color( COL_TRANSPARENT ) )
10752 0 : aColor = m_aGraphicsStack.front().m_aFont.GetColor();
10753 0 : m_aGraphicsStack.front().m_aFont = rFont;
10754 0 : m_aGraphicsStack.front().m_aFont.SetColor( aColor );
10755 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont;
10756 0 : }
10757 :
10758 0 : void PDFWriterImpl::push( sal_uInt16 nFlags )
10759 : {
10760 : OSL_ENSURE( m_aGraphicsStack.size() > 0, "invalid graphics stack" );
10761 0 : m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
10762 0 : m_aGraphicsStack.front().m_nFlags = nFlags;
10763 0 : }
10764 :
10765 0 : void PDFWriterImpl::pop()
10766 : {
10767 : OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" );
10768 0 : if( m_aGraphicsStack.size() < 2 )
10769 0 : return;
10770 :
10771 0 : GraphicsState aState = m_aGraphicsStack.front();
10772 0 : m_aGraphicsStack.pop_front();
10773 0 : GraphicsState& rOld = m_aGraphicsStack.front();
10774 :
10775 : // move those parameters back that were not pushed
10776 : // in the first place
10777 0 : if( ! (aState.m_nFlags & PUSH_LINECOLOR) )
10778 0 : setLineColor( aState.m_aLineColor );
10779 0 : if( ! (aState.m_nFlags & PUSH_FILLCOLOR) )
10780 0 : setFillColor( aState.m_aFillColor );
10781 0 : if( ! (aState.m_nFlags & PUSH_FONT) )
10782 0 : setFont( aState.m_aFont );
10783 0 : if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) )
10784 0 : setTextColor( aState.m_aFont.GetColor() );
10785 0 : if( ! (aState.m_nFlags & PUSH_MAPMODE) )
10786 0 : setMapMode( aState.m_aMapMode );
10787 0 : if( ! (aState.m_nFlags & PUSH_CLIPREGION) )
10788 : {
10789 : // do not use setClipRegion here
10790 : // it would convert again assuming the current mapmode
10791 0 : rOld.m_aClipRegion = aState.m_aClipRegion;
10792 0 : rOld.m_bClipRegion = aState.m_bClipRegion;
10793 : }
10794 0 : if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) )
10795 0 : setTextLineColor( aState.m_aTextLineColor );
10796 0 : if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) )
10797 0 : setOverlineColor( aState.m_aOverlineColor );
10798 0 : if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) )
10799 0 : setTextAlign( aState.m_aFont.GetAlign() );
10800 0 : if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) )
10801 0 : setTextFillColor( aState.m_aFont.GetFillColor() );
10802 0 : if( ! (aState.m_nFlags & PUSH_REFPOINT) )
10803 : {
10804 : // what ?
10805 : }
10806 : // invalidate graphics state
10807 0 : m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U);
10808 : }
10809 :
10810 0 : void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
10811 : {
10812 0 : m_aGraphicsStack.front().m_aMapMode = rMapMode;
10813 0 : getReferenceDevice()->SetMapMode( rMapMode );
10814 0 : m_aCurrentPDFState.m_aMapMode = rMapMode;
10815 0 : }
10816 :
10817 0 : void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10818 : {
10819 0 : basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10820 0 : aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10821 0 : m_aGraphicsStack.front().m_aClipRegion = aRegion;
10822 0 : m_aGraphicsStack.front().m_bClipRegion = true;
10823 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10824 0 : }
10825 :
10826 0 : void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
10827 : {
10828 0 : if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() )
10829 : {
10830 0 : Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10831 : m_aMapMode,
10832 : getReferenceDevice(),
10833 0 : Point( nX, nY ) ) );
10834 0 : aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10835 : m_aMapMode,
10836 : getReferenceDevice(),
10837 0 : Point() );
10838 0 : basegfx::B2DHomMatrix aMat;
10839 0 : aMat.translate( aPoint.X(), aPoint.Y() );
10840 0 : m_aGraphicsStack.front().m_aClipRegion.transform( aMat );
10841 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10842 : }
10843 0 : }
10844 :
10845 0 : bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect )
10846 : {
10847 : basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect(
10848 0 : basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) );
10849 0 : return intersectClipRegion( aRect );
10850 : }
10851 :
10852 :
10853 0 : bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10854 : {
10855 0 : basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) );
10856 0 : aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10857 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10858 0 : if( m_aGraphicsStack.front().m_bClipRegion )
10859 : {
10860 0 : basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) );
10861 0 : aRegion = basegfx::tools::prepareForPolygonOperation( aRegion );
10862 0 : m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion );
10863 : }
10864 : else
10865 : {
10866 0 : m_aGraphicsStack.front().m_aClipRegion = aRegion;
10867 0 : m_aGraphicsStack.front().m_bClipRegion = true;
10868 : }
10869 0 : return true;
10870 : }
10871 :
10872 0 : void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
10873 : {
10874 0 : if( nPageNr < 0 )
10875 0 : nPageNr = m_nCurrentPage;
10876 :
10877 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10878 0 : return;
10879 :
10880 0 : m_aNotes.push_back( PDFNoteEntry() );
10881 0 : m_aNotes.back().m_nObject = createObject();
10882 0 : m_aNotes.back().m_aContents = rNote;
10883 0 : m_aNotes.back().m_aRect = rRect;
10884 : // convert to default user space now, since the mapmode may change
10885 0 : m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
10886 :
10887 : // insert note to page's annotation list
10888 0 : m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
10889 : }
10890 :
10891 0 : sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr )
10892 : {
10893 0 : if( nPageNr < 0 )
10894 0 : nPageNr = m_nCurrentPage;
10895 :
10896 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10897 0 : return -1;
10898 :
10899 0 : sal_Int32 nRet = m_aLinks.size();
10900 :
10901 0 : m_aLinks.push_back( PDFLink() );
10902 0 : m_aLinks.back().m_nObject = createObject();
10903 0 : m_aLinks.back().m_nPage = nPageNr;
10904 0 : m_aLinks.back().m_aRect = rRect;
10905 : // convert to default user space now, since the mapmode may change
10906 0 : m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
10907 :
10908 : // insert link to page's annotation list
10909 0 : m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
10910 :
10911 0 : return nRet;
10912 : }
10913 :
10914 : //--->i56629
10915 0 : sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10916 : {
10917 0 : if( nPageNr < 0 )
10918 0 : nPageNr = m_nCurrentPage;
10919 :
10920 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10921 0 : return -1;
10922 :
10923 0 : sal_Int32 nRet = m_aNamedDests.size();
10924 :
10925 0 : m_aNamedDests.push_back( PDFNamedDest() );
10926 0 : m_aNamedDests.back().m_aDestName = sDestName;
10927 0 : m_aNamedDests.back().m_nPage = nPageNr;
10928 0 : m_aNamedDests.back().m_eType = eType;
10929 0 : m_aNamedDests.back().m_aRect = rRect;
10930 : // convert to default user space now, since the mapmode may change
10931 0 : m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
10932 :
10933 0 : return nRet;
10934 : }
10935 : //<---i56629
10936 :
10937 0 : sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10938 : {
10939 0 : if( nPageNr < 0 )
10940 0 : nPageNr = m_nCurrentPage;
10941 :
10942 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10943 0 : return -1;
10944 :
10945 0 : sal_Int32 nRet = m_aDests.size();
10946 :
10947 0 : m_aDests.push_back( PDFDest() );
10948 0 : m_aDests.back().m_nPage = nPageNr;
10949 0 : m_aDests.back().m_eType = eType;
10950 0 : m_aDests.back().m_aRect = rRect;
10951 : // convert to default user space now, since the mapmode may change
10952 0 : m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
10953 :
10954 0 : return nRet;
10955 : }
10956 :
10957 0 : sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10958 : {
10959 0 : return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType );
10960 : }
10961 :
10962 0 : sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
10963 : {
10964 0 : if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10965 0 : return -1;
10966 0 : if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
10967 0 : return -2;
10968 :
10969 0 : m_aLinks[ nLinkId ].m_nDest = nDestId;
10970 :
10971 0 : return 0;
10972 : }
10973 :
10974 0 : sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
10975 : {
10976 0 : if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10977 0 : return -1;
10978 :
10979 0 : m_aLinks[ nLinkId ].m_nDest = -1;
10980 :
10981 : using namespace ::com::sun::star;
10982 :
10983 0 : if (!m_xTrans.is())
10984 : {
10985 0 : uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
10986 0 : m_xTrans = util::URLTransformer::create(xContext);;
10987 : }
10988 :
10989 0 : util::URL aURL;
10990 0 : aURL.Complete = rURL;
10991 :
10992 0 : m_xTrans->parseStrict( aURL );
10993 :
10994 0 : m_aLinks[ nLinkId ].m_aURL = aURL.Complete;
10995 :
10996 0 : return 0;
10997 : }
10998 :
10999 0 : void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
11000 : {
11001 0 : m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
11002 0 : }
11003 :
11004 0 : sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
11005 : {
11006 : // create new item
11007 0 : sal_Int32 nNewItem = m_aOutline.size();
11008 0 : m_aOutline.push_back( PDFOutlineEntry() );
11009 :
11010 : // set item attributes
11011 0 : setOutlineItemParent( nNewItem, nParent );
11012 0 : setOutlineItemText( nNewItem, rText );
11013 0 : setOutlineItemDest( nNewItem, nDestID );
11014 :
11015 0 : return nNewItem;
11016 : }
11017 :
11018 0 : sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
11019 : {
11020 0 : if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
11021 0 : return -1;
11022 :
11023 0 : int nRet = 0;
11024 :
11025 0 : if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
11026 : {
11027 0 : nNewParent = 0;
11028 0 : nRet = -2;
11029 : }
11030 : // remove item from previous parent
11031 0 : sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID;
11032 0 : if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() )
11033 : {
11034 0 : PDFOutlineEntry& rParent = m_aOutline[ nParentID ];
11035 :
11036 0 : for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin();
11037 0 : it != rParent.m_aChildren.end(); ++it )
11038 : {
11039 0 : if( *it == nItem )
11040 : {
11041 0 : rParent.m_aChildren.erase( it );
11042 0 : break;
11043 : }
11044 : }
11045 : }
11046 :
11047 : // insert item to new parent's list of children
11048 0 : m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
11049 :
11050 0 : return nRet;
11051 : }
11052 :
11053 0 : sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
11054 : {
11055 0 : if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
11056 0 : return -1;
11057 :
11058 0 : m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText );
11059 0 : return 0;
11060 : }
11061 :
11062 0 : sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
11063 : {
11064 0 : if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
11065 0 : return -1;
11066 0 : if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
11067 0 : return -2;
11068 0 : m_aOutline[nItem].m_nDestID = nDestID;
11069 0 : return 0;
11070 : }
11071 :
11072 0 : const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
11073 : {
11074 0 : static std::map< PDFWriter::StructElement, const char* > aTagStrings;
11075 0 : if( aTagStrings.empty() )
11076 : {
11077 0 : aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
11078 0 : aTagStrings[ PDFWriter::Document ] = "Document";
11079 0 : aTagStrings[ PDFWriter::Part ] = "Part";
11080 0 : aTagStrings[ PDFWriter::Article ] = "Art";
11081 0 : aTagStrings[ PDFWriter::Section ] = "Sect";
11082 0 : aTagStrings[ PDFWriter::Division ] = "Div";
11083 0 : aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote";
11084 0 : aTagStrings[ PDFWriter::Caption ] = "Caption";
11085 0 : aTagStrings[ PDFWriter::TOC ] = "TOC";
11086 0 : aTagStrings[ PDFWriter::TOCI ] = "TOCI";
11087 0 : aTagStrings[ PDFWriter::Index ] = "Index";
11088 0 : aTagStrings[ PDFWriter::Paragraph ] = "P";
11089 0 : aTagStrings[ PDFWriter::Heading ] = "H";
11090 0 : aTagStrings[ PDFWriter::H1 ] = "H1";
11091 0 : aTagStrings[ PDFWriter::H2 ] = "H2";
11092 0 : aTagStrings[ PDFWriter::H3 ] = "H3";
11093 0 : aTagStrings[ PDFWriter::H4 ] = "H4";
11094 0 : aTagStrings[ PDFWriter::H5 ] = "H5";
11095 0 : aTagStrings[ PDFWriter::H6 ] = "H6";
11096 0 : aTagStrings[ PDFWriter::List ] = "L";
11097 0 : aTagStrings[ PDFWriter::ListItem ] = "LI";
11098 0 : aTagStrings[ PDFWriter::LILabel ] = "Lbl";
11099 0 : aTagStrings[ PDFWriter::LIBody ] = "LBody";
11100 0 : aTagStrings[ PDFWriter::Table ] = "Table";
11101 0 : aTagStrings[ PDFWriter::TableRow ] = "TR";
11102 0 : aTagStrings[ PDFWriter::TableHeader ] = "TH";
11103 0 : aTagStrings[ PDFWriter::TableData ] = "TD";
11104 0 : aTagStrings[ PDFWriter::Span ] = "Span";
11105 0 : aTagStrings[ PDFWriter::Quote ] = "Quote";
11106 0 : aTagStrings[ PDFWriter::Note ] = "Note";
11107 0 : aTagStrings[ PDFWriter::Reference ] = "Reference";
11108 0 : aTagStrings[ PDFWriter::BibEntry ] = "BibEntry";
11109 0 : aTagStrings[ PDFWriter::Code ] = "Code";
11110 0 : aTagStrings[ PDFWriter::Link ] = "Link";
11111 0 : aTagStrings[ PDFWriter::Figure ] = "Figure";
11112 0 : aTagStrings[ PDFWriter::Formula ] = "Formula";
11113 0 : aTagStrings[ PDFWriter::Form ] = "Form";
11114 : }
11115 :
11116 0 : std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
11117 :
11118 0 : return it != aTagStrings.end() ? it->second : "Div";
11119 : }
11120 :
11121 0 : void PDFWriterImpl::beginStructureElementMCSeq()
11122 : {
11123 0 : if( m_bEmitStructure &&
11124 : m_nCurrentStructElement > 0 && // StructTreeRoot
11125 0 : ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11126 : )
11127 : {
11128 0 : PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
11129 0 : OStringBuffer aLine( 128 );
11130 0 : sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
11131 0 : aLine.append( "/" );
11132 0 : if( !rEle.m_aAlias.isEmpty() )
11133 0 : aLine.append( rEle.m_aAlias );
11134 : else
11135 0 : aLine.append( getStructureTag( rEle.m_eType ) );
11136 0 : aLine.append( "<</MCID " );
11137 0 : aLine.append( nMCID );
11138 0 : aLine.append( ">>BDC\n" );
11139 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
11140 :
11141 : // update the element's content list
11142 : #if OSL_DEBUG_LEVEL > 1
11143 : fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n",
11144 : nMCID,
11145 : m_aPages[ m_nCurrentPage ].m_nPageObject,
11146 : rEle.m_nFirstPageObject );
11147 : #endif
11148 0 : rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) );
11149 : // update the page's mcid parent list
11150 0 : m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
11151 : // mark element MC sequence as open
11152 0 : rEle.m_bOpenMCSeq = true;
11153 : }
11154 : // handle artifacts
11155 0 : else if( ! m_bEmitStructure && m_aContext.Tagged &&
11156 : m_nCurrentStructElement > 0 &&
11157 0 : m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
11158 0 : ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11159 : )
11160 : {
11161 0 : OStringBuffer aLine( 128 );
11162 0 : aLine.append( "/Artifact BMC\n" );
11163 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
11164 : // mark element MC sequence as open
11165 0 : m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
11166 : }
11167 0 : }
11168 :
11169 0 : void PDFWriterImpl::endStructureElementMCSeq()
11170 : {
11171 0 : if( m_nCurrentStructElement > 0 && // StructTreeRoot
11172 0 : ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
11173 0 : m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
11174 : )
11175 : {
11176 0 : writeBuffer( "EMC\n", 4 );
11177 0 : m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
11178 : }
11179 0 : }
11180 :
11181 0 : bool PDFWriterImpl::checkEmitStructure()
11182 : {
11183 0 : bool bEmit = false;
11184 0 : if( m_aContext.Tagged )
11185 : {
11186 0 : bEmit = true;
11187 0 : sal_Int32 nEle = m_nCurrentStructElement;
11188 0 : while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
11189 : {
11190 0 : if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
11191 : {
11192 0 : bEmit = false;
11193 0 : break;
11194 : }
11195 0 : nEle = m_aStructure[ nEle ].m_nParentElement;
11196 : }
11197 : }
11198 0 : return bEmit;
11199 : }
11200 :
11201 0 : sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias )
11202 : {
11203 0 : if( m_nCurrentPage < 0 )
11204 0 : return -1;
11205 :
11206 0 : if( ! m_aContext.Tagged )
11207 0 : return -1;
11208 :
11209 : // close eventual current MC sequence
11210 0 : endStructureElementMCSeq();
11211 :
11212 0 : if( m_nCurrentStructElement == 0 &&
11213 : eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
11214 : {
11215 : // struct tree root hit, but not beginning document
11216 : // this might happen with setCurrentStructureElement
11217 : // silently insert structure into document again if one properly exists
11218 0 : if( ! m_aStructure[ 0 ].m_aChildren.empty() )
11219 : {
11220 0 : PDFWriter::StructElement childType = PDFWriter::NonStructElement;
11221 0 : sal_Int32 nNewCurElement = 0;
11222 0 : const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
11223 0 : for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
11224 0 : childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
11225 : {
11226 0 : nNewCurElement = *it;
11227 0 : childType = m_aStructure[ nNewCurElement ].m_eType;
11228 : }
11229 0 : if( childType == PDFWriter::Document )
11230 : {
11231 0 : m_nCurrentStructElement = nNewCurElement;
11232 : DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" );
11233 : }
11234 : else {
11235 : OSL_FAIL( "document structure in disorder !" );
11236 : }
11237 : }
11238 : else {
11239 : OSL_FAIL( "PDF document structure MUST be contained in a Document element" );
11240 : }
11241 : }
11242 :
11243 0 : sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11244 0 : m_aStructure.push_back( PDFStructureElement() );
11245 0 : PDFStructureElement& rEle = m_aStructure.back();
11246 0 : rEle.m_eType = eType;
11247 0 : rEle.m_nOwnElement = nNewId;
11248 0 : rEle.m_nParentElement = m_nCurrentStructElement;
11249 0 : rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject;
11250 0 : m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
11251 0 : m_nCurrentStructElement = nNewId;
11252 :
11253 : // handle alias names
11254 0 : if( !rAlias.isEmpty() && eType != PDFWriter::NonStructElement )
11255 : {
11256 0 : OStringBuffer aNameBuf( rAlias.getLength() );
11257 0 : appendName( rAlias, aNameBuf );
11258 0 : OString aAliasName( aNameBuf.makeStringAndClear() );
11259 0 : rEle.m_aAlias = aAliasName;
11260 0 : m_aRoleMap[ aAliasName ] = getStructureTag( eType );
11261 : }
11262 :
11263 : #if OSL_DEBUG_LEVEL > 1
11264 : OStringBuffer aLine( "beginStructureElement " );
11265 : aLine.append( m_nCurrentStructElement );
11266 : aLine.append( ": " );
11267 : aLine.append( getStructureTag( eType ) );
11268 : if( !rEle.m_aAlias.isEmpty() )
11269 : {
11270 : aLine.append( " aliased as \"" );
11271 : aLine.append( rEle.m_aAlias );
11272 : aLine.append( '\"' );
11273 : }
11274 : emitComment( aLine.getStr() );
11275 : #endif
11276 :
11277 : // check whether to emit structure henceforth
11278 0 : m_bEmitStructure = checkEmitStructure();
11279 :
11280 0 : if( m_bEmitStructure ) // don't create nonexistant objects
11281 : {
11282 0 : rEle.m_nObject = createObject();
11283 : // update parent's kids list
11284 0 : m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject );
11285 : }
11286 0 : return nNewId;
11287 : }
11288 :
11289 0 : void PDFWriterImpl::endStructureElement()
11290 : {
11291 0 : if( m_nCurrentPage < 0 )
11292 0 : return;
11293 :
11294 0 : if( ! m_aContext.Tagged )
11295 0 : return;
11296 :
11297 0 : if( m_nCurrentStructElement == 0 )
11298 : {
11299 : // hit the struct tree root, that means there is an endStructureElement
11300 : // without corresponding beginStructureElement
11301 0 : return;
11302 : }
11303 :
11304 : // end the marked content sequence
11305 0 : endStructureElementMCSeq();
11306 :
11307 : #if OSL_DEBUG_LEVEL > 1
11308 : OStringBuffer aLine( "endStructureElement " );
11309 : aLine.append( m_nCurrentStructElement );
11310 : aLine.append( ": " );
11311 : aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11312 : if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
11313 : {
11314 : aLine.append( " aliased as \"" );
11315 : aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11316 : aLine.append( '\"' );
11317 : }
11318 : #endif
11319 :
11320 : // "end" the structure element, the parent becomes current element
11321 0 : m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
11322 :
11323 : // check whether to emit structure henceforth
11324 0 : m_bEmitStructure = checkEmitStructure();
11325 :
11326 : #if OSL_DEBUG_LEVEL > 1
11327 : if( m_bEmitStructure )
11328 : emitComment( aLine.getStr() );
11329 : #endif
11330 : }
11331 :
11332 : //---> i94258
11333 : /*
11334 : * This function adds an internal structure list container to overcome the 8191 elements array limitation
11335 : * in kids element emission.
11336 : * Recursive function
11337 : *
11338 : */
11339 0 : void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
11340 : {
11341 0 : if( rEle.m_eType == PDFWriter::NonStructElement &&
11342 : rEle.m_nOwnElement != rEle.m_nParentElement )
11343 0 : return;
11344 :
11345 0 : for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
11346 : {
11347 0 : if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
11348 : {
11349 0 : PDFStructureElement& rChild = m_aStructure[ *it ];
11350 0 : if( rChild.m_eType != PDFWriter::NonStructElement )
11351 : {
11352 : //triggered when a child of the rEle element is found
11353 0 : if( rChild.m_nParentElement == rEle.m_nOwnElement )
11354 0 : addInternalStructureContainer( rChild );//examine the child
11355 : else
11356 : {
11357 : OSL_FAIL( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
11358 : #if OSL_DEBUG_LEVEL > 1
11359 : fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
11360 : #endif
11361 : }
11362 : }
11363 : }
11364 : else
11365 : {
11366 : OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
11367 : #if OSL_DEBUG_LEVEL > 1
11368 : fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
11369 : #endif
11370 : }
11371 : }
11372 :
11373 0 : if( rEle.m_nOwnElement != rEle.m_nParentElement )
11374 : {
11375 0 : if( !rEle.m_aKids.empty() )
11376 : {
11377 0 : if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
11378 : //then we need to add the containers for the kids elements
11379 : // a list to be used for the new kid element
11380 0 : std::list< PDFStructureElementKid > aNewKids;
11381 0 : std::list< sal_Int32 > aNewChildren;
11382 :
11383 : // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
11384 0 : OStringBuffer aNameBuf( "Div" );
11385 0 : OString aAliasName( aNameBuf.makeStringAndClear() );
11386 0 : m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
11387 :
11388 0 : while( rEle.m_aKids.size() > ncMaxPDFArraySize )
11389 : {
11390 0 : sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
11391 0 : sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11392 0 : m_aStructure.push_back( PDFStructureElement() );
11393 0 : PDFStructureElement& rEleNew = m_aStructure.back();
11394 0 : rEleNew.m_aAlias = aAliasName;
11395 0 : rEleNew.m_eType = PDFWriter::Division; // a new Div type container
11396 0 : rEleNew.m_nOwnElement = nNewId;
11397 0 : rEleNew.m_nParentElement = nCurrentStructElement;
11398 : //inherit the same page as the first child to be reparented
11399 0 : rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
11400 0 : rEleNew.m_nObject = createObject();//assign a PDF object number
11401 : //add the object to the kid list of the parent
11402 0 : aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) );
11403 0 : aNewChildren.push_back( nNewId );
11404 :
11405 0 : std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
11406 0 : std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
11407 0 : advance( aChildEndIt, ncMaxPDFArraySize );
11408 0 : advance( aKidEndIt, ncMaxPDFArraySize );
11409 :
11410 : rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
11411 : rEle.m_aKids,
11412 : rEle.m_aKids.begin(),
11413 0 : aKidEndIt );
11414 : rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
11415 : rEle.m_aChildren,
11416 : rEle.m_aChildren.begin(),
11417 0 : aChildEndIt );
11418 : // set the kid's new parent
11419 0 : for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
11420 0 : it != rEleNew.m_aChildren.end(); ++it )
11421 : {
11422 0 : m_aStructure[ *it ].m_nParentElement = nNewId;
11423 : }
11424 : }
11425 : //finally add the new kids resulting from the container added
11426 0 : rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
11427 0 : rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
11428 : }
11429 : }
11430 : }
11431 : }
11432 : //<--- i94258
11433 :
11434 0 : bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
11435 : {
11436 0 : bool bSuccess = false;
11437 :
11438 0 : if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
11439 : {
11440 : // end eventual previous marked content sequence
11441 0 : endStructureElementMCSeq();
11442 :
11443 0 : m_nCurrentStructElement = nEle;
11444 0 : m_bEmitStructure = checkEmitStructure();
11445 : #if OSL_DEBUG_LEVEL > 1
11446 : OStringBuffer aLine( "setCurrentStructureElement " );
11447 : aLine.append( m_nCurrentStructElement );
11448 : aLine.append( ": " );
11449 : aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11450 : if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
11451 : {
11452 : aLine.append( " aliased as \"" );
11453 : aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11454 : aLine.append( '\"' );
11455 : }
11456 : if( ! m_bEmitStructure )
11457 : aLine.append( " (inside NonStruct)" );
11458 : emitComment( aLine.getStr() );
11459 : #endif
11460 0 : bSuccess = true;
11461 : }
11462 :
11463 0 : return bSuccess;
11464 : }
11465 :
11466 0 : bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
11467 : {
11468 0 : if( !m_aContext.Tagged )
11469 0 : return false;
11470 :
11471 0 : bool bInsert = false;
11472 0 : if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11473 : {
11474 0 : PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11475 0 : switch( eAttr )
11476 : {
11477 : case PDFWriter::Placement:
11478 0 : if( eVal == PDFWriter::Block ||
11479 : eVal == PDFWriter::Inline ||
11480 : eVal == PDFWriter::Before ||
11481 : eVal == PDFWriter::Start ||
11482 : eVal == PDFWriter::End )
11483 0 : bInsert = true;
11484 0 : break;
11485 : case PDFWriter::WritingMode:
11486 0 : if( eVal == PDFWriter::LrTb ||
11487 : eVal == PDFWriter::RlTb ||
11488 : eVal == PDFWriter::TbRl )
11489 : {
11490 0 : bInsert = true;
11491 : }
11492 0 : break;
11493 : case PDFWriter::TextAlign:
11494 0 : if( eVal == PDFWriter::Start ||
11495 : eVal == PDFWriter::Center ||
11496 : eVal == PDFWriter::End ||
11497 : eVal == PDFWriter::Justify )
11498 : {
11499 0 : if( eType == PDFWriter::Paragraph ||
11500 : eType == PDFWriter::Heading ||
11501 : eType == PDFWriter::H1 ||
11502 : eType == PDFWriter::H2 ||
11503 : eType == PDFWriter::H3 ||
11504 : eType == PDFWriter::H4 ||
11505 : eType == PDFWriter::H5 ||
11506 : eType == PDFWriter::H6 ||
11507 : eType == PDFWriter::List ||
11508 : eType == PDFWriter::ListItem ||
11509 : eType == PDFWriter::LILabel ||
11510 : eType == PDFWriter::LIBody ||
11511 : eType == PDFWriter::Table ||
11512 : eType == PDFWriter::TableRow ||
11513 : eType == PDFWriter::TableHeader ||
11514 : eType == PDFWriter::TableData )
11515 : {
11516 0 : bInsert = true;
11517 : }
11518 : }
11519 0 : break;
11520 : case PDFWriter::Width:
11521 : case PDFWriter::Height:
11522 0 : if( eVal == PDFWriter::Auto )
11523 : {
11524 0 : if( eType == PDFWriter::Figure ||
11525 : eType == PDFWriter::Formula ||
11526 : eType == PDFWriter::Form ||
11527 : eType == PDFWriter::Table ||
11528 : eType == PDFWriter::TableHeader ||
11529 : eType == PDFWriter::TableData )
11530 : {
11531 0 : bInsert = true;
11532 : }
11533 : }
11534 0 : break;
11535 : case PDFWriter::BlockAlign:
11536 0 : if( eVal == PDFWriter::Before ||
11537 : eVal == PDFWriter::Middle ||
11538 : eVal == PDFWriter::After ||
11539 : eVal == PDFWriter::Justify )
11540 : {
11541 0 : if( eType == PDFWriter::TableHeader ||
11542 : eType == PDFWriter::TableData )
11543 : {
11544 0 : bInsert = true;
11545 : }
11546 : }
11547 0 : break;
11548 : case PDFWriter::InlineAlign:
11549 0 : if( eVal == PDFWriter::Start ||
11550 : eVal == PDFWriter::Center ||
11551 : eVal == PDFWriter::End )
11552 : {
11553 0 : if( eType == PDFWriter::TableHeader ||
11554 : eType == PDFWriter::TableData )
11555 : {
11556 0 : bInsert = true;
11557 : }
11558 : }
11559 0 : break;
11560 : case PDFWriter::LineHeight:
11561 0 : if( eVal == PDFWriter::Normal ||
11562 : eVal == PDFWriter::Auto )
11563 : {
11564 : // only for ILSE and BLSE
11565 0 : if( eType == PDFWriter::Paragraph ||
11566 : eType == PDFWriter::Heading ||
11567 : eType == PDFWriter::H1 ||
11568 : eType == PDFWriter::H2 ||
11569 : eType == PDFWriter::H3 ||
11570 : eType == PDFWriter::H4 ||
11571 : eType == PDFWriter::H5 ||
11572 : eType == PDFWriter::H6 ||
11573 : eType == PDFWriter::List ||
11574 : eType == PDFWriter::ListItem ||
11575 : eType == PDFWriter::LILabel ||
11576 : eType == PDFWriter::LIBody ||
11577 : eType == PDFWriter::Table ||
11578 : eType == PDFWriter::TableRow ||
11579 : eType == PDFWriter::TableHeader ||
11580 : eType == PDFWriter::TableData ||
11581 : eType == PDFWriter::Span ||
11582 : eType == PDFWriter::Quote ||
11583 : eType == PDFWriter::Note ||
11584 : eType == PDFWriter::Reference ||
11585 : eType == PDFWriter::BibEntry ||
11586 : eType == PDFWriter::Code ||
11587 : eType == PDFWriter::Link )
11588 : {
11589 0 : bInsert = true;
11590 : }
11591 : }
11592 0 : break;
11593 : case PDFWriter::TextDecorationType:
11594 0 : if( eVal == PDFWriter::NONE ||
11595 : eVal == PDFWriter::Underline ||
11596 : eVal == PDFWriter::Overline ||
11597 : eVal == PDFWriter::LineThrough )
11598 : {
11599 : // only for ILSE and BLSE
11600 0 : if( eType == PDFWriter::Paragraph ||
11601 : eType == PDFWriter::Heading ||
11602 : eType == PDFWriter::H1 ||
11603 : eType == PDFWriter::H2 ||
11604 : eType == PDFWriter::H3 ||
11605 : eType == PDFWriter::H4 ||
11606 : eType == PDFWriter::H5 ||
11607 : eType == PDFWriter::H6 ||
11608 : eType == PDFWriter::List ||
11609 : eType == PDFWriter::ListItem ||
11610 : eType == PDFWriter::LILabel ||
11611 : eType == PDFWriter::LIBody ||
11612 : eType == PDFWriter::Table ||
11613 : eType == PDFWriter::TableRow ||
11614 : eType == PDFWriter::TableHeader ||
11615 : eType == PDFWriter::TableData ||
11616 : eType == PDFWriter::Span ||
11617 : eType == PDFWriter::Quote ||
11618 : eType == PDFWriter::Note ||
11619 : eType == PDFWriter::Reference ||
11620 : eType == PDFWriter::BibEntry ||
11621 : eType == PDFWriter::Code ||
11622 : eType == PDFWriter::Link )
11623 : {
11624 0 : bInsert = true;
11625 : }
11626 : }
11627 0 : break;
11628 : case PDFWriter::ListNumbering:
11629 0 : if( eVal == PDFWriter::NONE ||
11630 : eVal == PDFWriter::Disc ||
11631 : eVal == PDFWriter::Circle ||
11632 : eVal == PDFWriter::Square ||
11633 : eVal == PDFWriter::Decimal ||
11634 : eVal == PDFWriter::UpperRoman ||
11635 : eVal == PDFWriter::LowerRoman ||
11636 : eVal == PDFWriter::UpperAlpha ||
11637 : eVal == PDFWriter::LowerAlpha )
11638 : {
11639 0 : if( eType == PDFWriter::List )
11640 0 : bInsert = true;
11641 : }
11642 0 : break;
11643 0 : default: break;
11644 : }
11645 : }
11646 :
11647 0 : if( bInsert )
11648 0 : m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
11649 : #if OSL_DEBUG_LEVEL > 1
11650 : else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11651 : fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
11652 : getAttributeTag( eAttr ),
11653 : getAttributeValueTag( eVal ),
11654 : getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11655 : m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
11656 : );
11657 : #endif
11658 :
11659 0 : return bInsert;
11660 : }
11661 :
11662 0 : bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
11663 : {
11664 0 : if( ! m_aContext.Tagged )
11665 0 : return false;
11666 :
11667 0 : bool bInsert = false;
11668 0 : if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11669 : {
11670 0 : if( eAttr == PDFWriter::Language )
11671 : {
11672 0 : m_aStructure[ m_nCurrentStructElement ].m_aLocale = LanguageTag( (LanguageType)nValue ).getLocale();
11673 0 : return true;
11674 : }
11675 :
11676 0 : PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11677 0 : switch( eAttr )
11678 : {
11679 : case PDFWriter::SpaceBefore:
11680 : case PDFWriter::SpaceAfter:
11681 : case PDFWriter::StartIndent:
11682 : case PDFWriter::EndIndent:
11683 : // just for BLSE
11684 0 : if( eType == PDFWriter::Paragraph ||
11685 : eType == PDFWriter::Heading ||
11686 : eType == PDFWriter::H1 ||
11687 : eType == PDFWriter::H2 ||
11688 : eType == PDFWriter::H3 ||
11689 : eType == PDFWriter::H4 ||
11690 : eType == PDFWriter::H5 ||
11691 : eType == PDFWriter::H6 ||
11692 : eType == PDFWriter::List ||
11693 : eType == PDFWriter::ListItem ||
11694 : eType == PDFWriter::LILabel ||
11695 : eType == PDFWriter::LIBody ||
11696 : eType == PDFWriter::Table ||
11697 : eType == PDFWriter::TableRow ||
11698 : eType == PDFWriter::TableHeader ||
11699 : eType == PDFWriter::TableData )
11700 : {
11701 0 : bInsert = true;
11702 : }
11703 0 : break;
11704 : case PDFWriter::TextIndent:
11705 : // paragraph like BLSE and additional elements
11706 0 : if( eType == PDFWriter::Paragraph ||
11707 : eType == PDFWriter::Heading ||
11708 : eType == PDFWriter::H1 ||
11709 : eType == PDFWriter::H2 ||
11710 : eType == PDFWriter::H3 ||
11711 : eType == PDFWriter::H4 ||
11712 : eType == PDFWriter::H5 ||
11713 : eType == PDFWriter::H6 ||
11714 : eType == PDFWriter::LILabel ||
11715 : eType == PDFWriter::LIBody ||
11716 : eType == PDFWriter::TableHeader ||
11717 : eType == PDFWriter::TableData )
11718 : {
11719 0 : bInsert = true;
11720 : }
11721 0 : break;
11722 : case PDFWriter::Width:
11723 : case PDFWriter::Height:
11724 0 : if( eType == PDFWriter::Figure ||
11725 : eType == PDFWriter::Formula ||
11726 : eType == PDFWriter::Form ||
11727 : eType == PDFWriter::Table ||
11728 : eType == PDFWriter::TableHeader ||
11729 : eType == PDFWriter::TableData )
11730 : {
11731 0 : bInsert = true;
11732 : }
11733 0 : break;
11734 : case PDFWriter::LineHeight:
11735 : case PDFWriter::BaselineShift:
11736 : // only for ILSE and BLSE
11737 0 : if( eType == PDFWriter::Paragraph ||
11738 : eType == PDFWriter::Heading ||
11739 : eType == PDFWriter::H1 ||
11740 : eType == PDFWriter::H2 ||
11741 : eType == PDFWriter::H3 ||
11742 : eType == PDFWriter::H4 ||
11743 : eType == PDFWriter::H5 ||
11744 : eType == PDFWriter::H6 ||
11745 : eType == PDFWriter::List ||
11746 : eType == PDFWriter::ListItem ||
11747 : eType == PDFWriter::LILabel ||
11748 : eType == PDFWriter::LIBody ||
11749 : eType == PDFWriter::Table ||
11750 : eType == PDFWriter::TableRow ||
11751 : eType == PDFWriter::TableHeader ||
11752 : eType == PDFWriter::TableData ||
11753 : eType == PDFWriter::Span ||
11754 : eType == PDFWriter::Quote ||
11755 : eType == PDFWriter::Note ||
11756 : eType == PDFWriter::Reference ||
11757 : eType == PDFWriter::BibEntry ||
11758 : eType == PDFWriter::Code ||
11759 : eType == PDFWriter::Link )
11760 : {
11761 0 : bInsert = true;
11762 : }
11763 0 : break;
11764 : case PDFWriter::RowSpan:
11765 : case PDFWriter::ColSpan:
11766 : // only for table cells
11767 0 : if( eType == PDFWriter::TableHeader ||
11768 : eType == PDFWriter::TableData )
11769 : {
11770 0 : bInsert = true;
11771 : }
11772 0 : break;
11773 : case PDFWriter::LinkAnnotation:
11774 0 : if( eType == PDFWriter::Link )
11775 0 : bInsert = true;
11776 0 : break;
11777 0 : default: break;
11778 : }
11779 : }
11780 :
11781 0 : if( bInsert )
11782 0 : m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
11783 : #if OSL_DEBUG_LEVEL > 1
11784 : else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11785 : fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
11786 : getAttributeTag( eAttr ),
11787 : (int)nValue,
11788 : getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11789 : m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() );
11790 : #endif
11791 :
11792 0 : return bInsert;
11793 : }
11794 :
11795 0 : void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect )
11796 : {
11797 0 : sal_Int32 nPageNr = m_nCurrentPage;
11798 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
11799 0 : return;
11800 :
11801 :
11802 0 : if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11803 : {
11804 0 : PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11805 0 : if( eType == PDFWriter::Figure ||
11806 : eType == PDFWriter::Formula ||
11807 : eType == PDFWriter::Form ||
11808 : eType == PDFWriter::Table )
11809 : {
11810 0 : m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
11811 : // convert to default user space now, since the mapmode may change
11812 0 : m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
11813 : }
11814 : }
11815 : }
11816 :
11817 0 : void PDFWriterImpl::setActualText( const String& rText )
11818 : {
11819 0 : if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11820 : {
11821 0 : m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
11822 : }
11823 0 : }
11824 :
11825 0 : void PDFWriterImpl::setAlternateText( const String& rText )
11826 : {
11827 0 : if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11828 : {
11829 0 : m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
11830 : }
11831 0 : }
11832 :
11833 0 : void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
11834 : {
11835 0 : if( nPageNr < 0 )
11836 0 : nPageNr = m_nCurrentPage;
11837 :
11838 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11839 0 : return;
11840 :
11841 0 : m_aPages[ nPageNr ].m_nDuration = nSeconds;
11842 : }
11843 :
11844 0 : void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
11845 : {
11846 0 : if( nPageNr < 0 )
11847 0 : nPageNr = m_nCurrentPage;
11848 :
11849 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11850 0 : return;
11851 :
11852 0 : m_aPages[ nPageNr ].m_eTransition = eType;
11853 0 : m_aPages[ nPageNr ].m_nTransTime = nMilliSec;
11854 : }
11855 :
11856 0 : void PDFWriterImpl::ensureUniqueRadioOnValues()
11857 : {
11858 : // loop over radio groups
11859 0 : for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
11860 0 : group != m_aRadioGroupWidgets.end(); ++group )
11861 : {
11862 0 : PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
11863 : // check whether all kids have a unique OnValue
11864 0 : boost::unordered_map< OUString, sal_Int32, OUStringHash > aOnValues;
11865 0 : int nChildren = rGroupWidget.m_aKidsIndex.size();
11866 0 : bool bIsUnique = true;
11867 0 : for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
11868 : {
11869 0 : int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11870 0 : const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
11871 : #if OSL_DEBUG_LEVEL > 1
11872 : fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() );
11873 : #endif
11874 0 : if( aOnValues.find( rVal ) == aOnValues.end() )
11875 : {
11876 0 : aOnValues[ rVal ] = 1;
11877 : }
11878 : else
11879 : {
11880 0 : bIsUnique = false;
11881 : }
11882 : }
11883 0 : if( ! bIsUnique )
11884 : {
11885 : #if OSL_DEBUG_LEVEL > 1
11886 : fprintf( stderr, "enforcing unique OnValues\n" );
11887 : #endif
11888 : // make unique by using ascending OnValues
11889 0 : for( int nKid = 0; nKid < nChildren; nKid++ )
11890 : {
11891 0 : int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11892 0 : PDFWidget& rKid = m_aWidgets[nKidIndex];
11893 0 : rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) );
11894 0 : if( rKid.m_aValue != "Off" )
11895 0 : rKid.m_aValue = rKid.m_aOnValue;
11896 : }
11897 : }
11898 : // finally move the "Yes" appearance to the OnValue appearance
11899 0 : for( int nKid = 0; nKid < nChildren; nKid++ )
11900 : {
11901 0 : int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11902 0 : PDFWidget& rKid = m_aWidgets[nKidIndex];
11903 0 : PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
11904 0 : if( app_it != rKid.m_aAppearances.end() )
11905 : {
11906 0 : PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
11907 0 : if( stream_it != app_it->second.end() )
11908 : {
11909 0 : SvMemoryStream* pStream = stream_it->second;
11910 0 : app_it->second.erase( stream_it );
11911 0 : OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
11912 0 : appendName( rKid.m_aOnValue, aBuf );
11913 0 : (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11914 : }
11915 : #if OSL_DEBUG_LEVEL > 1
11916 : else
11917 : fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" );
11918 : #endif
11919 : }
11920 : // update selected radio button
11921 0 : if( rKid.m_aValue != "Off" )
11922 : {
11923 0 : rGroupWidget.m_aValue = rKid.m_aValue;
11924 : }
11925 : }
11926 0 : }
11927 0 : }
11928 :
11929 0 : sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
11930 : {
11931 0 : sal_Int32 nRadioGroupWidget = -1;
11932 :
11933 0 : std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
11934 :
11935 0 : if( it == m_aRadioGroupWidgets.end() )
11936 : {
11937 0 : m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
11938 0 : sal_Int32(m_aWidgets.size());
11939 :
11940 : // new group, insert the radiobutton
11941 0 : m_aWidgets.push_back( PDFWidget() );
11942 0 : m_aWidgets.back().m_nObject = createObject();
11943 0 : m_aWidgets.back().m_nPage = m_nCurrentPage;
11944 0 : m_aWidgets.back().m_eType = PDFWriter::RadioButton;
11945 0 : m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
11946 0 : m_aWidgets.back().m_nFlags |= 0x0000C000; // NoToggleToOff and Radio bits
11947 :
11948 0 : createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn );
11949 : }
11950 : else
11951 0 : nRadioGroupWidget = it->second;
11952 :
11953 0 : return nRadioGroupWidget;
11954 : }
11955 :
11956 0 : sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
11957 : {
11958 0 : if( nPageNr < 0 )
11959 0 : nPageNr = m_nCurrentPage;
11960 :
11961 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11962 0 : return -1;
11963 :
11964 0 : bool sigHidden(true);
11965 0 : sal_Int32 nNewWidget = m_aWidgets.size();
11966 0 : m_aWidgets.push_back( PDFWidget() );
11967 :
11968 0 : m_aWidgets.back().m_nObject = createObject();
11969 0 : m_aWidgets.back().m_aRect = rControl.Location;
11970 0 : m_aWidgets.back().m_nPage = nPageNr;
11971 0 : m_aWidgets.back().m_eType = rControl.getType();
11972 :
11973 0 : sal_Int32 nRadioGroupWidget = -1;
11974 : // for unknown reasons the radio buttons of a radio group must not have a
11975 : // field name, else the buttons are in fact check boxes -
11976 : // that is multiple buttons of the radio group can be selected
11977 0 : if( rControl.getType() == PDFWriter::RadioButton )
11978 0 : nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
11979 : else
11980 : {
11981 0 : createWidgetFieldName( nNewWidget, rControl );
11982 : }
11983 :
11984 : // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
11985 0 : PDFWidget& rNewWidget = m_aWidgets[nNewWidget];
11986 0 : rNewWidget.m_aDescription = rControl.Description;
11987 0 : rNewWidget.m_aText = rControl.Text;
11988 : rNewWidget.m_nTextStyle = rControl.TextStyle &
11989 : ( TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP |
11990 : TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM |
11991 0 : TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
11992 0 : rNewWidget.m_nTabOrder = rControl.TabOrder;
11993 :
11994 : // various properties are set via the flags (/Ff) property of the field dict
11995 0 : if( rControl.ReadOnly )
11996 0 : rNewWidget.m_nFlags |= 1;
11997 0 : if( rControl.getType() == PDFWriter::PushButton )
11998 : {
11999 0 : const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
12000 0 : if( rNewWidget.m_nTextStyle == 0 )
12001 : rNewWidget.m_nTextStyle =
12002 : TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER |
12003 0 : TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12004 :
12005 0 : rNewWidget.m_nFlags |= 0x00010000;
12006 0 : if( !rBtn.URL.isEmpty() )
12007 0 : rNewWidget.m_aListEntries.push_back( rBtn.URL );
12008 0 : rNewWidget.m_bSubmit = rBtn.Submit;
12009 0 : rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
12010 0 : rNewWidget.m_nDest = rBtn.Dest;
12011 0 : createDefaultPushButtonAppearance( rNewWidget, rBtn );
12012 : }
12013 0 : else if( rControl.getType() == PDFWriter::RadioButton )
12014 : {
12015 0 : const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
12016 0 : if( rNewWidget.m_nTextStyle == 0 )
12017 : rNewWidget.m_nTextStyle =
12018 0 : TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12019 : /* PDF sees a RadioButton group as one radio button with
12020 : * children which are in turn check boxes
12021 : *
12022 : * so we need to create a radio button on demand for a new group
12023 : * and insert a checkbox for each RadioButtonWidget as its child
12024 : */
12025 0 : rNewWidget.m_eType = PDFWriter::CheckBox;
12026 0 : rNewWidget.m_nRadioGroup = rBtn.RadioGroup;
12027 :
12028 : DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" );
12029 :
12030 0 : PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
12031 0 : rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
12032 0 : rRadioButton.m_aKidsIndex.push_back( nNewWidget );
12033 0 : rNewWidget.m_nParent = rRadioButton.m_nObject;
12034 :
12035 0 : rNewWidget.m_aValue = OUString( "Off" );
12036 0 : rNewWidget.m_aOnValue = rBtn.OnValue;
12037 0 : if( rRadioButton.m_aValue.isEmpty() && rBtn.Selected )
12038 : {
12039 0 : rNewWidget.m_aValue = rNewWidget.m_aOnValue;
12040 0 : rRadioButton.m_aValue = rNewWidget.m_aOnValue;
12041 : }
12042 0 : createDefaultRadioButtonAppearance( rNewWidget, rBtn );
12043 :
12044 : // union rect of radio group
12045 0 : Rectangle aRect = rNewWidget.m_aRect;
12046 0 : m_aPages[ nPageNr ].convertRect( aRect );
12047 0 : rRadioButton.m_aRect.Union( aRect );
12048 : }
12049 0 : else if( rControl.getType() == PDFWriter::CheckBox )
12050 : {
12051 0 : const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
12052 0 : if( rNewWidget.m_nTextStyle == 0 )
12053 : rNewWidget.m_nTextStyle =
12054 0 : TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12055 :
12056 0 : rNewWidget.m_aValue = rBox.Checked ? OUString("Yes") : OUString("Off" );
12057 : // create default appearance before m_aRect gets transformed
12058 0 : createDefaultCheckBoxAppearance( rNewWidget, rBox );
12059 : }
12060 0 : else if( rControl.getType() == PDFWriter::ListBox )
12061 : {
12062 0 : if( rNewWidget.m_nTextStyle == 0 )
12063 0 : rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
12064 :
12065 0 : const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
12066 0 : rNewWidget.m_aListEntries = rLstBox.Entries;
12067 0 : rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
12068 0 : rNewWidget.m_aValue = rLstBox.Text;
12069 0 : if( rLstBox.DropDown )
12070 0 : rNewWidget.m_nFlags |= 0x00020000;
12071 0 : if( rLstBox.Sort )
12072 0 : rNewWidget.m_nFlags |= 0x00080000;
12073 0 : if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 )
12074 0 : rNewWidget.m_nFlags |= 0x00200000;
12075 :
12076 0 : createDefaultListBoxAppearance( rNewWidget, rLstBox );
12077 : }
12078 0 : else if( rControl.getType() == PDFWriter::ComboBox )
12079 : {
12080 0 : if( rNewWidget.m_nTextStyle == 0 )
12081 0 : rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
12082 :
12083 0 : const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
12084 0 : rNewWidget.m_aValue = rBox.Text;
12085 0 : rNewWidget.m_aListEntries = rBox.Entries;
12086 0 : rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
12087 0 : if( rBox.Sort )
12088 0 : rNewWidget.m_nFlags |= 0x00080000;
12089 :
12090 0 : PDFWriter::ListBoxWidget aLBox;
12091 0 : aLBox.Name = rBox.Name;
12092 0 : aLBox.Description = rBox.Description;
12093 0 : aLBox.Text = rBox.Text;
12094 0 : aLBox.TextStyle = rBox.TextStyle;
12095 0 : aLBox.ReadOnly = rBox.ReadOnly;
12096 0 : aLBox.Border = rBox.Border;
12097 0 : aLBox.BorderColor = rBox.BorderColor;
12098 0 : aLBox.Background = rBox.Background;
12099 0 : aLBox.BackgroundColor = rBox.BackgroundColor;
12100 0 : aLBox.TextFont = rBox.TextFont;
12101 0 : aLBox.TextColor = rBox.TextColor;
12102 0 : aLBox.DropDown = true;
12103 0 : aLBox.Sort = rBox.Sort;
12104 0 : aLBox.MultiSelect = false;
12105 0 : aLBox.Entries = rBox.Entries;
12106 :
12107 0 : createDefaultListBoxAppearance( rNewWidget, aLBox );
12108 : }
12109 0 : else if( rControl.getType() == PDFWriter::Edit )
12110 : {
12111 0 : if( rNewWidget.m_nTextStyle == 0 )
12112 0 : rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
12113 :
12114 0 : const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl);
12115 0 : if( rEdit.MultiLine )
12116 : {
12117 0 : rNewWidget.m_nFlags |= 0x00001000;
12118 0 : rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
12119 : }
12120 0 : if( rEdit.Password )
12121 0 : rNewWidget.m_nFlags |= 0x00002000;
12122 0 : if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 )
12123 0 : rNewWidget.m_nFlags |= 0x00100000;
12124 0 : rNewWidget.m_nMaxLen = rEdit.MaxLen;
12125 0 : rNewWidget.m_aValue = rEdit.Text;
12126 :
12127 0 : createDefaultEditAppearance( rNewWidget, rEdit );
12128 : }
12129 : #if !defined(ANDROID) && !defined(IOS)
12130 0 : else if( rControl.getType() == PDFWriter::Signature)
12131 : {
12132 0 : const PDFWriter::SignatureWidget& rSig = static_cast<const PDFWriter::SignatureWidget&>(rControl);
12133 0 : sigHidden = rSig.SigHidden;
12134 :
12135 0 : if ( sigHidden )
12136 0 : rNewWidget.m_aRect = Rectangle(0, 0, 0, 0);
12137 :
12138 0 : m_nSignatureObject = createObject();
12139 0 : rNewWidget.m_aValue = OUString::valueOf( m_nSignatureObject );
12140 0 : rNewWidget.m_aValue += OUString(" 0 R");
12141 : //createDefaultSignatureAppearance( rNewWidget, rSig );
12142 : // let's add a fake appearance
12143 0 : rNewWidget.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
12144 : }
12145 : #endif
12146 :
12147 : // if control is a hidden signature, do not convert coordinates since we
12148 : // need /Rect [ 0 0 0 0 ]
12149 0 : if ( ! ( ( rControl.getType() == PDFWriter::Signature ) && ( sigHidden ) ) )
12150 : {
12151 : // convert to default user space now, since the mapmode may change
12152 : // note: create default appearances before m_aRect gets transformed
12153 0 : m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
12154 : }
12155 :
12156 : // insert widget to page's annotation list
12157 0 : m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
12158 :
12159 : // mark page as having widgets
12160 0 : m_aPages[ nPageNr ].m_bHasWidgets = true;
12161 :
12162 0 : return nNewWidget;
12163 : }
12164 :
12165 0 : void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress )
12166 : {
12167 0 : if( pStream )
12168 : {
12169 0 : m_aAdditionalStreams.push_back( PDFAddStream() );
12170 0 : PDFAddStream& rStream = m_aAdditionalStreams.back();
12171 0 : rStream.m_aMimeType = rMimeType.Len()
12172 : ? OUString( rMimeType )
12173 0 : : OUString( "application/octet-stream" );
12174 0 : rStream.m_pStream = pStream;
12175 0 : rStream.m_bCompress = bCompress;
12176 : }
12177 0 : }
12178 :
12179 :
12180 :
12181 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|