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 : #include <sal/types.h>
21 :
22 : #include <math.h>
23 : #include <algorithm>
24 : #include <lcms2.h>
25 :
26 : #include <basegfx/matrix/b2dhommatrix.hxx>
27 : #include <basegfx/polygon/b2dpolygon.hxx>
28 : #include <basegfx/polygon/b2dpolygontools.hxx>
29 : #include <basegfx/polygon/b2dpolypolygon.hxx>
30 : #include <basegfx/polygon/b2dpolypolygoncutter.hxx>
31 : #include <basegfx/polygon/b2dpolypolygontools.hxx>
32 : #include <boost/scoped_array.hpp>
33 : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
34 : #include <com/sun/star/util/URL.hpp>
35 : #include <com/sun/star/util/URLTransformer.hpp>
36 : #include <comphelper/processfactory.hxx>
37 : #include <comphelper/random.hxx>
38 : #include <comphelper/string.hxx>
39 : #include <cppuhelper/implbase1.hxx>
40 : #include <i18nlangtag/languagetag.hxx>
41 : #include <o3tl/numeric.hxx>
42 : #include <osl/file.hxx>
43 : #include <osl/thread.h>
44 : #include <rtl/crc.h>
45 : #include <rtl/digest.h>
46 : #include <rtl/ustrbuf.hxx>
47 : #include <tools/debug.hxx>
48 : #include <tools/fract.hxx>
49 : #include <tools/stream.hxx>
50 : #include <tools/urlobj.hxx>
51 : #include <tools/zcodec.hxx>
52 : #include <vcl/bitmapex.hxx>
53 : #include <vcl/bmpacc.hxx>
54 : #include <vcl/cvtgrf.hxx>
55 : #include <vcl/image.hxx>
56 : #include <vcl/lineinfo.hxx>
57 : #include <vcl/metric.hxx>
58 : #include <vcl/settings.hxx>
59 : #include <vcl/strhelper.hxx>
60 : #include <vcl/svapp.hxx>
61 : #include <vcl/virdev.hxx>
62 :
63 : #include "fontsubset.hxx"
64 : #include "outdev.h"
65 : #include "PhysicalFontFace.hxx"
66 : #include "salgdi.hxx"
67 : #include "sallayout.hxx"
68 : #include "textlayout.hxx"
69 :
70 : #include "pdfwriter_impl.hxx"
71 :
72 : #if !defined(ANDROID) && !defined(IOS) && !defined(_WIN32)
73 : // NSS headers for PDF signing
74 : #include "nss.h"
75 : #include "cert.h"
76 : #include "hasht.h"
77 : #include "secerr.h"
78 : #include "sechash.h"
79 : #include "cms.h"
80 : #include "cmst.h"
81 :
82 : // We use curl for RFC3161 time stamp requests
83 : #include <curl/curl.h>
84 : #endif
85 :
86 : #ifdef _WIN32
87 : // WinCrypt headers for PDF signing
88 : // Note: this uses Windows 7 APIs and requires the relevant data types;
89 : // the functions that don't exist in WinXP must be looked up at runtime!
90 : #undef _WIN32_WINNT
91 : #define _WIN32_WINNT _WIN32_WINNT_WIN7
92 : #include <prewin.h>
93 : #include <wincrypt.h>
94 : #include <postwin.h>
95 : #endif
96 :
97 : #include <config_eot.h>
98 :
99 : #if ENABLE_EOT
100 : #include <libeot/libeot.h>
101 : #endif
102 :
103 : using namespace vcl;
104 :
105 : #if (OSL_DEBUG_LEVEL < 3)
106 : #define COMPRESS_PAGES
107 : #else
108 : #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams
109 : #endif
110 :
111 : #if !defined(ANDROID) && !defined(IOS)
112 : // Is this length truly the maximum possible, or just a number that
113 : // seemed large enough when the author tested this (with some type of
114 : // certificates)? I suspect the latter.
115 :
116 : // Used to be 0x4000 = 16384, but a sample signed PDF (produced by
117 : // some other software) provided by the customer has a signature
118 : // content that is 30000 bytes. The SampleSignedPDFDocument.pdf from
119 : // Adobe has one that is 21942 bytes. So let's be careful. Pity this
120 : // can't be dynamic, at least not without restructuring the code. Also
121 : // note that the checks in the code for this being too small
122 : // apparently are broken, if this overflows you end up with an invalid
123 : // PDF. Need to fix that.
124 :
125 : #define MAX_SIGNATURE_CONTENT_LENGTH 50000
126 : #endif
127 :
128 : #ifdef DO_TEST_PDF
129 : class PDFTestOutputStream : public PDFOutputStream
130 : {
131 : public:
132 : virtual ~PDFTestOutputStream();
133 : virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream );
134 : };
135 :
136 : PDFTestOutputStream::~PDFTestOutputStream()
137 : {
138 : }
139 :
140 : void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream )
141 : {
142 : OString aStr( "lalala\ntest\ntest\ntest" );
143 : com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() );
144 : memcpy( aData.getArray(), aStr.getStr(), aStr.getLength() );
145 : xStream->writeBytes( aData );
146 : }
147 :
148 : // this test code cannot be used to test PDF/A-1 because it forces
149 : // control item (widgets) to bypass the structure controlling
150 : // the embedding of such elements in actual run
151 : void doTestCode()
152 : {
153 : static const char* pHome = getenv( "HOME" );
154 : OUString aTestFile( "file://" );
155 : aTestFile += OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 );
156 : aTestFile += "/pdf_export_test.pdf";
157 :
158 : PDFWriter::PDFWriterContext aContext;
159 : aContext.URL = aTestFile;
160 : aContext.Version = PDFWriter::PDF_1_4;
161 : aContext.Tagged = true;
162 : aContext.InitialPage = 2;
163 : aContext.DocumentInfo.Title = "PDF export test document";
164 : aContext.DocumentInfo.Producer = "VCL";
165 :
166 : aContext.SignPDF = true;
167 : aContext.SignLocation = "Burdur";
168 : aContext.SignReason = "Some valid reason to sign";
169 : aContext.SignContact = "signer@example.com";
170 :
171 : com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder > xEnc;
172 : PDFWriter aWriter( aContext, xEnc );
173 : aWriter.NewPage( 595, 842 );
174 : aWriter.BeginStructureElement( PDFWriter::Document );
175 : // set duration of 3 sec for first page
176 : aWriter.SetAutoAdvanceTime( 3 );
177 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
178 :
179 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
180 : aWriter.SetLineColor( Color( COL_LIGHTGREEN ) );
181 : aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
182 :
183 : aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
184 : aWriter.SetTextColor( Color( COL_BLACK ) );
185 : aWriter.SetLineColor( Color( COL_BLACK ) );
186 : aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
187 :
188 : Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
189 : aWriter.DrawRect( aRect );
190 : aWriter.DrawText( aRect, OUString( "Link annot 1" ) );
191 : sal_Int32 nFirstLink = aWriter.CreateLink( aRect );
192 : PDFNote aNote;
193 : aNote.Title = "A small test note";
194 : aNote.Contents = "There is no business like show business like no business i know. Everything about it is appealing.";
195 : aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote );
196 :
197 : Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
198 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
199 : aWriter.DrawRect( aTargetRect );
200 : aWriter.DrawText( aTargetRect, "Dest second link" );
201 : sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect );
202 :
203 : aWriter.BeginStructureElement( PDFWriter::Section );
204 : aWriter.BeginStructureElement( PDFWriter::Heading );
205 : aWriter.DrawText( Point(4500, 9000), "A small structure test" );
206 : aWriter.EndStructureElement();
207 : aWriter.BeginStructureElement( PDFWriter::Paragraph );
208 : aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
209 : aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline );
210 : aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
211 : "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.",
212 : DrawTextFlags::MultiLine | DrawTextFlags::WordBreak
213 : );
214 : aWriter.SetActualText( "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." );
215 : aWriter.SetAlternateText( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." );
216 : aWriter.EndStructureElement();
217 : aWriter.BeginStructureElement( PDFWriter::Paragraph );
218 : aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
219 : aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
220 : "This paragraph is nothing special either but ends on the next page structurewise",
221 : DrawTextFlags::MultiLine | DrawTextFlags::WordBreak
222 : );
223 :
224 : aWriter.NewPage( 595, 842 );
225 : // test AddStream interface
226 : aWriter.AddStream( "text/plain", new PDFTestOutputStream(), true );
227 : // set transitional mode
228 : aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 );
229 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
230 : aWriter.SetTextColor( Color( COL_BLACK ) );
231 : aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
232 : aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
233 : "Here's where all things come to an end ... well at least the paragraph from the last page.",
234 : DrawTextFlags::MultiLine | DrawTextFlags::WordBreak
235 : );
236 : aWriter.EndStructureElement();
237 :
238 : aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
239 : // disable structure
240 : aWriter.BeginStructureElement( PDFWriter::NonStructElement );
241 : aWriter.DrawRect( aRect );
242 : aWriter.BeginStructureElement( PDFWriter::Paragraph );
243 : aWriter.DrawText( aRect, "Link annot 2" );
244 : sal_Int32 nSecondLink = aWriter.CreateLink( aRect );
245 :
246 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
247 : aWriter.BeginStructureElement( PDFWriter::ListItem );
248 : aWriter.DrawRect( aTargetRect );
249 : aWriter.DrawText( aTargetRect, "Dest first link" );
250 : sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect );
251 : // enable structure
252 : aWriter.EndStructureElement();
253 :
254 : aWriter.EndStructureElement();
255 : aWriter.EndStructureElement();
256 : aWriter.BeginStructureElement( PDFWriter::Figure );
257 : aWriter.BeginStructureElement( PDFWriter::Caption );
258 : aWriter.DrawText( Point( 4500, 9000 ), "Some drawing stuff inside the structure" );
259 : aWriter.EndStructureElement();
260 :
261 : // test clipping
262 : basegfx::B2DPolyPolygon aClip;
263 : basegfx::B2DPolygon aClipPoly;
264 : aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) );
265 : aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) );
266 : aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) );
267 : aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) );
268 : aClipPoly.setClosed( true );
269 : aClip.append( aClipPoly );
270 :
271 : aWriter.Push( PushFlags::CLIPREGION | PushFlags::FILLCOLOR );
272 : aWriter.SetClipRegion( aClip );
273 : aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
274 : aWriter.MoveClipRegion( 1000, 500 );
275 : aWriter.SetFillColor( Color( COL_RED ) );
276 : aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
277 : aWriter.Pop();
278 : // test transparency
279 : // draw background
280 : Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
281 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
282 : aWriter.DrawRect( aTranspRect );
283 : aWriter.BeginTransparencyGroup();
284 :
285 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
286 : aWriter.DrawEllipse( aTranspRect );
287 : aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
288 : aWriter.DrawText( aTranspRect,
289 : "Some transparent text",
290 : DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
291 :
292 : aWriter.EndTransparencyGroup( aTranspRect, 50 );
293 :
294 : // prepare an alpha mask
295 : Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
296 : BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess();
297 : for( int nX = 0; nX < 256; nX++ )
298 : for( int nY = 0; nY < 256; nY++ )
299 : pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) );
300 : aTransMask.ReleaseAccess( pAcc );
301 : aTransMask.SetPrefMapMode( MAP_MM );
302 : aTransMask.SetPrefSize( Size( 10, 10 ) );
303 :
304 : aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask );
305 :
306 : aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
307 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
308 : aWriter.DrawRect( aTranspRect );
309 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
310 : aWriter.DrawEllipse( aTranspRect );
311 : aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
312 : aWriter.DrawText( aTranspRect,
313 : "Some transparent text",
314 : DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
315 : aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
316 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
317 : aWriter.DrawRect( aTranspRect );
318 :
319 : Bitmap aImageBmp( Size( 256, 256 ), 24 );
320 : pAcc = aImageBmp.AcquireWriteAccess();
321 : pAcc->SetFillColor( Color( 0xff, 0, 0xff ) );
322 : pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
323 : aImageBmp.ReleaseAccess( pAcc );
324 : BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) );
325 : aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx );
326 :
327 : aWriter.EndStructureElement();
328 : aWriter.EndStructureElement();
329 :
330 : LineInfo aLI( LINE_DASH, 3 );
331 : aLI.SetDashCount( 2 );
332 : aLI.SetDashLen( 50 );
333 : aLI.SetDotCount( 2 );
334 : aLI.SetDotLen( 25 );
335 : aLI.SetDistance( 15 );
336 : Point aLIPoints[] = { Point( 4000, 10000 ),
337 : Point( 8000, 12000 ),
338 : Point( 3000, 19000 ) };
339 : Polygon aLIPoly( 3, aLIPoints );
340 : aWriter.SetLineColor( Color( COL_BLUE ) );
341 : aWriter.SetFillColor();
342 : aWriter.DrawPolyLine( aLIPoly, aLI );
343 :
344 : aLI.SetDashCount( 4 );
345 : aLIPoly.Move( 1000, 1000 );
346 : aWriter.DrawPolyLine( aLIPoly, aLI );
347 :
348 : aWriter.NewPage( 595, 842 );
349 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
350 : Wallpaper aWall( aTransMask );
351 : aWall.SetStyle( WALLPAPER_TILE );
352 : aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall );
353 :
354 : aWriter.NewPage( 595, 842 );
355 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
356 : aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
357 : aWriter.SetTextColor( Color( COL_BLACK ) );
358 : aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
359 : aWriter.DrawRect( aRect );
360 : aWriter.DrawText( aRect, "www.heise.de" );
361 : sal_Int32 nURILink = aWriter.CreateLink( aRect );
362 : aWriter.SetLinkURL( nURILink, OUString( "http://www.heise.de" ) );
363 :
364 : aWriter.SetLinkDest( nFirstLink, nFirstDest );
365 : aWriter.SetLinkDest( nSecondLink, nSecondDest );
366 :
367 : // include a button
368 : PDFWriter::PushButtonWidget aBtn;
369 : aBtn.Name = "testButton";
370 : aBtn.Description = "A test button";
371 : aBtn.Text = "hit me";
372 : aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
373 : aBtn.Border = aBtn.Background = true;
374 : aWriter.CreateControl( aBtn );
375 :
376 : // include a uri button
377 : PDFWriter::PushButtonWidget aUriBtn;
378 : aUriBtn.Name = "wwwButton";
379 : aUriBtn.Description = "A URI button";
380 : aUriBtn.Text = "to www";
381 : aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
382 : aUriBtn.Border = aUriBtn.Background = true;
383 : aUriBtn.URL = "http://www.heise.de";
384 : aWriter.CreateControl( aUriBtn );
385 :
386 : // include a dest button
387 : PDFWriter::PushButtonWidget aDstBtn;
388 : aDstBtn.Name = "destButton";
389 : aDstBtn.Description = "A Dest button";
390 : aDstBtn.Text = "to paragraph";
391 : aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
392 : aDstBtn.Border = aDstBtn.Background = true;
393 : aDstBtn.Dest = nFirstDest;
394 : aWriter.CreateControl( aDstBtn );
395 :
396 : PDFWriter::CheckBoxWidget aCBox;
397 : aCBox.Name = "textCheckBox";
398 : aCBox.Description = "A test check box";
399 : aCBox.Text = "check me";
400 : aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
401 : aCBox.Checked = true;
402 : aCBox.Border = aCBox.Background = false;
403 : aWriter.CreateControl( aCBox );
404 :
405 : PDFWriter::CheckBoxWidget aCBox2;
406 : aCBox2.Name = "textCheckBox2";
407 : aCBox2.Description = "Another test check box";
408 : aCBox2.Text = "check me right";
409 : aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
410 : aCBox2.Checked = true;
411 : aCBox2.Border = aCBox2.Background = false;
412 : aCBox2.ButtonIsLeft = false;
413 : aWriter.CreateControl( aCBox2 );
414 :
415 : PDFWriter::RadioButtonWidget aRB1;
416 : aRB1.Name = "rb1_1";
417 : aRB1.Description = "radio 1 button 1";
418 : aRB1.Text = "Despair";
419 : aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
420 : aRB1.Selected = true;
421 : aRB1.RadioGroup = 1;
422 : aRB1.Border = aRB1.Background = true;
423 : aRB1.ButtonIsLeft = false;
424 : aRB1.BorderColor = Color( COL_LIGHTGREEN );
425 : aRB1.BackgroundColor = Color( COL_LIGHTBLUE );
426 : aRB1.TextColor = Color( COL_LIGHTRED );
427 : aRB1.TextFont = Font( OUString( "Courier" ), Size( 0, 800 ) );
428 : aWriter.CreateControl( aRB1 );
429 :
430 : PDFWriter::RadioButtonWidget aRB2;
431 : aRB2.Name = "rb2_1";
432 : aRB2.Description = "radio 2 button 1";
433 : aRB2.Text = "Joy";
434 : aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
435 : aRB2.Selected = true;
436 : aRB2.RadioGroup = 2;
437 : aWriter.CreateControl( aRB2 );
438 :
439 : PDFWriter::RadioButtonWidget aRB3;
440 : aRB3.Name = "rb1_2";
441 : aRB3.Description = "radio 1 button 2";
442 : aRB3.Text = "Desperation";
443 : aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
444 : aRB3.Selected = true;
445 : aRB3.RadioGroup = 1;
446 : aWriter.CreateControl( aRB3 );
447 :
448 : PDFWriter::EditWidget aEditBox;
449 : aEditBox.Name = "testEdit";
450 : aEditBox.Description = "A test edit field";
451 : aEditBox.Text = "A little test text";
452 : aEditBox.TextStyle = DrawTextFlags::Left | DrawTextFlags::VCenter;
453 : aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
454 : aEditBox.MaxLen = 100;
455 : aEditBox.Border = aEditBox.Background = true;
456 : aEditBox.BorderColor = Color( COL_BLACK );
457 : aWriter.CreateControl( aEditBox );
458 :
459 : // normal list box
460 : PDFWriter::ListBoxWidget aLstBox;
461 : aLstBox.Name = "testListBox";
462 : aLstBox.Text = "One";
463 : aLstBox.Description = "select me";
464 : aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
465 : aLstBox.Sort = true;
466 : aLstBox.MultiSelect = true;
467 : aLstBox.Border = aLstBox.Background = true;
468 : aLstBox.BorderColor = Color( COL_BLACK );
469 : aLstBox.Entries.push_back( OUString( "One" ) );
470 : aLstBox.Entries.push_back( OUString( "Two" ) );
471 : aLstBox.Entries.push_back( OUString( "Three" ) );
472 : aLstBox.Entries.push_back( OUString( "Four" ) );
473 : aLstBox.SelectedEntries.push_back( 1 );
474 : aLstBox.SelectedEntries.push_back( 2 );
475 : aWriter.CreateControl( aLstBox );
476 :
477 : // dropdown list box
478 : aLstBox.Name = "testDropDownListBox";
479 : aLstBox.DropDown = true;
480 : aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
481 : aWriter.CreateControl( aLstBox );
482 :
483 : // combo box
484 : PDFWriter::ComboBoxWidget aComboBox;
485 : aComboBox.Name = "testComboBox";
486 : aComboBox.Text = "test a combobox";
487 : aComboBox.Entries.push_back( OUString( "Larry" ) );
488 : aComboBox.Entries.push_back( OUString( "Curly" ) );
489 : aComboBox.Entries.push_back( OUString( "Moe" ) );
490 : aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
491 : aWriter.CreateControl( aComboBox );
492 :
493 : // test outlines
494 : sal_Int32 nPage1OL = aWriter.CreateOutlineItem();
495 : aWriter.SetOutlineItemText( nPage1OL, OUString( "Page 1" ) );
496 : aWriter.SetOutlineItemDest( nPage1OL, nSecondDest );
497 : aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2" ), nSecondDest );
498 : aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 revisited" ), nSecondDest );
499 : aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 again" ), nSecondDest );
500 : sal_Int32 nPage2OL = aWriter.CreateOutlineItem();
501 : aWriter.SetOutlineItemText( nPage2OL, OUString( "Page 2" ) );
502 : aWriter.CreateOutlineItem( nPage2OL, OUString( "Dest 1" ), nFirstDest );
503 :
504 : aWriter.EndStructureElement(); // close document
505 :
506 : aWriter.Emit();
507 : }
508 : #endif
509 :
510 : static const sal_Int32 nLog10Divisor = 1;
511 : static const double fDivisor = 10.0;
512 :
513 0 : static inline double pixelToPoint( double px ) { return px/fDivisor; }
514 0 : static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); }
515 :
516 : const sal_uInt8 PDFWriterImpl::s_nPadString[32] =
517 : {
518 : 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
519 : 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
520 : };
521 :
522 0 : static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
523 : {
524 : static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
525 : '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
526 0 : rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
527 0 : rBuffer.append( pHexDigits[ nInt & 15 ] );
528 0 : }
529 :
530 0 : static void appendName( const OUString& rStr, OStringBuffer& rBuffer )
531 : {
532 : // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
533 : // I guess than when reading the #xx sequence it will count for a single character.
534 0 : OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) );
535 0 : const sal_Char* pStr = aStr.getStr();
536 0 : int nLen = aStr.getLength();
537 0 : for( int i = 0; i < nLen; i++ )
538 : {
539 : /* #i16920# PDF recommendation: output UTF8, any byte
540 : * outside the interval [33(=ASCII'!');126(=ASCII'~')]
541 : * should be escaped hexadecimal
542 : * for the sake of ghostscript which also reads PDF
543 : * but has a narrower acceptance rate we only pass
544 : * alphanumerics and '-' literally.
545 : */
546 0 : if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) ||
547 0 : (pStr[i] >= 'a' && pStr[i] <= 'z' ) ||
548 0 : (pStr[i] >= '0' && pStr[i] <= '9' ) ||
549 0 : pStr[i] == '-' )
550 : {
551 0 : rBuffer.append( pStr[i] );
552 : }
553 : else
554 : {
555 0 : rBuffer.append( '#' );
556 0 : appendHex( (sal_Int8)pStr[i], rBuffer );
557 : }
558 0 : }
559 0 : }
560 :
561 0 : static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer )
562 : {
563 : //FIXME i59651 see above
564 0 : while( pStr && *pStr )
565 : {
566 0 : if( (*pStr >= 'A' && *pStr <= 'Z' ) ||
567 0 : (*pStr >= 'a' && *pStr <= 'z' ) ||
568 0 : (*pStr >= '0' && *pStr <= '9' ) ||
569 0 : *pStr == '-' )
570 : {
571 0 : rBuffer.append( *pStr );
572 : }
573 : else
574 : {
575 0 : rBuffer.append( '#' );
576 0 : appendHex( (sal_Int8)*pStr, rBuffer );
577 : }
578 0 : pStr++;
579 : }
580 0 : }
581 :
582 : //used only to emit encoded passwords
583 0 : static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer )
584 : {
585 0 : while( nLength )
586 : {
587 0 : switch( *pStr )
588 : {
589 : case '\n' :
590 0 : rBuffer.append( "\\n" );
591 0 : break;
592 : case '\r' :
593 0 : rBuffer.append( "\\r" );
594 0 : break;
595 : case '\t' :
596 0 : rBuffer.append( "\\t" );
597 0 : break;
598 : case '\b' :
599 0 : rBuffer.append( "\\b" );
600 0 : break;
601 : case '\f' :
602 0 : rBuffer.append( "\\f" );
603 0 : break;
604 : case '(' :
605 : case ')' :
606 : case '\\' :
607 0 : rBuffer.append( "\\" );
608 0 : rBuffer.append( (sal_Char) *pStr );
609 0 : break;
610 : default:
611 0 : rBuffer.append( (sal_Char) *pStr );
612 0 : break;
613 : }
614 0 : pStr++;
615 0 : nLength--;
616 : }
617 0 : }
618 :
619 : /**--->i56629
620 : * Convert a string before using it.
621 : *
622 : * This string conversion function is needed because the destination name
623 : * in a PDF file seen through an Internet browser should be
624 : * specially crafted, in order to be used directly by the browser.
625 : * In this way the fragment part of a hyperlink to a PDF file (e.g. something
626 : * as 'test1/test2/a-file.pdf\#thefragment) will be (hopefully) interpreted by the
627 : * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
628 : * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
629 : * and go to named destination thefragment using default zoom'.
630 : * The conversion is needed because in case of a fragment in the form: Slide%201
631 : * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
632 : * using this conversion, in both the generated named destinations, fragment and GoToR
633 : * destination.
634 : *
635 : * The names for destinations are name objects and so they don't need to be encrypted
636 : * even though they expose the content of PDF file (e.g. guessing the PDF content from the
637 : * destination name).
638 : *
639 : * Fhurter limitation: it is advisable to use standard ASCII characters for
640 : * OOo bookmarks.
641 : */
642 0 : static void appendDestinationName( const OUString& rString, OStringBuffer& rBuffer )
643 : {
644 0 : const sal_Unicode* pStr = rString.getStr();
645 0 : sal_Int32 nLen = rString.getLength();
646 0 : for( int i = 0; i < nLen; i++ )
647 : {
648 0 : sal_Unicode aChar = pStr[i];
649 0 : if( (aChar >= '0' && aChar <= '9' ) ||
650 0 : (aChar >= 'a' && aChar <= 'z' ) ||
651 0 : (aChar >= 'A' && aChar <= 'Z' ) ||
652 : aChar == '-' )
653 : {
654 0 : rBuffer.append((sal_Char)aChar);
655 : }
656 : else
657 : {
658 0 : sal_Int8 aValueHigh = sal_Int8(aChar >> 8);
659 0 : if(aValueHigh > 0)
660 0 : appendHex( aValueHigh, rBuffer );
661 0 : appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
662 : }
663 : }
664 0 : }
665 : //<--- i56629
666 :
667 0 : static void appendUnicodeTextString( const OUString& rString, OStringBuffer& rBuffer )
668 : {
669 0 : rBuffer.append( "FEFF" );
670 0 : const sal_Unicode* pStr = rString.getStr();
671 0 : sal_Int32 nLen = rString.getLength();
672 0 : for( int i = 0; i < nLen; i++ )
673 : {
674 0 : sal_Unicode aChar = pStr[i];
675 0 : appendHex( (sal_Int8)(aChar >> 8), rBuffer );
676 0 : appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
677 : }
678 0 : }
679 :
680 0 : void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl )
681 : {
682 : /* #i80258# previously we use appendName here
683 : however we need a slightly different coding scheme than the normal
684 : name encoding for field names
685 : */
686 0 : const OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ? i_rControl.Name : i_rControl.Text;
687 0 : OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) );
688 0 : const sal_Char* pStr = aStr.getStr();
689 0 : int nLen = aStr.getLength();
690 :
691 0 : OStringBuffer aBuffer( rName.getLength()+64 );
692 0 : for( int i = 0; i < nLen; i++ )
693 : {
694 : /* #i16920# PDF recommendation: output UTF8, any byte
695 : * outside the interval [32(=ASCII' ');126(=ASCII'~')]
696 : * should be escaped hexadecimal
697 : */
698 0 : if( (pStr[i] >= 32 && pStr[i] <= 126 ) )
699 0 : aBuffer.append( pStr[i] );
700 : else
701 : {
702 0 : aBuffer.append( '#' );
703 0 : appendHex( (sal_Int8)pStr[i], aBuffer );
704 : }
705 : }
706 :
707 0 : OString aFullName( aBuffer.makeStringAndClear() );
708 :
709 : /* #i82785# create hierarchical fields down to the for each dot in i_rName */
710 0 : sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0;
711 0 : OString aPartialName;
712 0 : OString aDomain;
713 0 : do
714 : {
715 0 : nLastTokenIndex = nTokenIndex;
716 0 : aPartialName = aFullName.getToken( 0, '.', nTokenIndex );
717 0 : if( nTokenIndex != -1 )
718 : {
719 : // find or create a hierarchical field
720 : // first find the fully qualified name up to this field
721 0 : aDomain = aFullName.copy( 0, nTokenIndex-1 );
722 0 : std::unordered_map< OString, sal_Int32, OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
723 0 : if( it == m_aFieldNameMap.end() )
724 : {
725 : // create new hierarchy field
726 0 : sal_Int32 nNewWidget = m_aWidgets.size();
727 0 : m_aWidgets.push_back( PDFWidget() );
728 0 : m_aWidgets[nNewWidget].m_nObject = createObject();
729 0 : m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy;
730 0 : m_aWidgets[nNewWidget].m_aName = aPartialName;
731 0 : m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
732 0 : m_aFieldNameMap[aDomain] = nNewWidget;
733 0 : m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
734 0 : if( nLastTokenIndex > 0 )
735 : {
736 : // this field is not a root field and
737 : // needs to be inserted to its parent
738 0 : OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) );
739 0 : it = m_aFieldNameMap.find( aParentDomain );
740 : OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" );
741 0 : if( it != m_aFieldNameMap.end() )
742 : {
743 : OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" );
744 0 : if( it->second < sal_Int32(m_aWidgets.size()) )
745 : {
746 0 : PDFWidget& rParentField( m_aWidgets[it->second] );
747 0 : rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject );
748 0 : rParentField.m_aKidsIndex.push_back( nNewWidget );
749 0 : m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject;
750 : }
751 0 : }
752 : }
753 : }
754 0 : else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy )
755 : {
756 : // this is invalid, someone tries to have a terminal field as parent
757 : // example: a button with the name foo.bar exists and
758 : // another button is named foo.bar.no
759 : // workaround: put the second terminal field as much up in the hierarchy as
760 : // necessary to have a non-terminal field as parent (or none at all)
761 : // since it->second already is terminal, we just need to use its parent
762 0 : aDomain.clear();
763 0 : aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
764 0 : if( nLastTokenIndex > 0 )
765 : {
766 0 : aDomain = aFullName.copy( 0, nLastTokenIndex-1 );
767 0 : OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() );
768 0 : aBuf.append( aDomain );
769 0 : aBuf.append( '.' );
770 0 : aBuf.append( aPartialName );
771 0 : aFullName = aBuf.makeStringAndClear();
772 : }
773 : else
774 0 : aFullName = aPartialName;
775 0 : break;
776 : }
777 : }
778 0 : } while( nTokenIndex != -1 );
779 :
780 : // insert widget into its hierarchy field
781 0 : if( !aDomain.isEmpty() )
782 : {
783 0 : std::unordered_map< OString, sal_Int32, OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
784 0 : if( it != m_aFieldNameMap.end() )
785 : {
786 : OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" );
787 0 : if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) )
788 : {
789 0 : m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject;
790 0 : m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject);
791 0 : m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex );
792 : }
793 : }
794 : }
795 :
796 0 : if( aPartialName.isEmpty() )
797 : {
798 : // how funny, an empty field name
799 0 : if( i_rControl.getType() == PDFWriter::RadioButton )
800 : {
801 0 : aPartialName = "RadioGroup";
802 0 : aPartialName += OString::number( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup );
803 : }
804 : else
805 0 : aPartialName = OString( "Widget" );
806 : }
807 :
808 0 : if( ! m_aContext.AllowDuplicateFieldNames )
809 : {
810 0 : std::unordered_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aFullName );
811 :
812 0 : if( it != m_aFieldNameMap.end() ) // not unique
813 : {
814 0 : std::unordered_map< OString, sal_Int32, OStringHash >::const_iterator check_it;
815 0 : OString aTry;
816 0 : sal_Int32 nTry = 2;
817 0 : do
818 : {
819 0 : OStringBuffer aUnique( aFullName.getLength() + 16 );
820 0 : aUnique.append( aFullName );
821 0 : aUnique.append( '_' );
822 0 : aUnique.append( nTry++ );
823 0 : aTry = aUnique.makeStringAndClear();
824 0 : check_it = m_aFieldNameMap.find( aTry );
825 0 : } while( check_it != m_aFieldNameMap.end() );
826 0 : aFullName = aTry;
827 0 : m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
828 0 : aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
829 : }
830 : else
831 0 : m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
832 : }
833 :
834 : // finally
835 0 : m_aWidgets[i_nWidgetIndex].m_aName = aPartialName;
836 0 : }
837 :
838 0 : static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor )
839 : {
840 0 : if( nValue < 0 )
841 : {
842 0 : rBuffer.append( '-' );
843 0 : nValue = -nValue;
844 : }
845 0 : sal_Int32 nFactor = 1, nDiv = nPrecision;
846 0 : while( nDiv-- )
847 0 : nFactor *= 10;
848 :
849 0 : sal_Int32 nInt = nValue / nFactor;
850 0 : rBuffer.append( nInt );
851 0 : if( nFactor > 1 )
852 : {
853 0 : sal_Int32 nDecimal = nValue % nFactor;
854 0 : if( nDecimal )
855 : {
856 0 : rBuffer.append( '.' );
857 : // omit trailing zeros
858 0 : while( (nDecimal % 10) == 0 )
859 0 : nDecimal /= 10;
860 0 : rBuffer.append( nDecimal );
861 : }
862 : }
863 0 : }
864 :
865 : // appends a double. PDF does not accept exponential format, only fixed point
866 0 : static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 )
867 : {
868 0 : bool bNeg = false;
869 0 : if( fValue < 0.0 )
870 : {
871 0 : bNeg = true;
872 0 : fValue=-fValue;
873 : }
874 :
875 0 : sal_Int64 nInt = (sal_Int64)fValue;
876 0 : fValue -= (double)nInt;
877 : // optimizing hardware may lead to a value of 1.0 after the subtraction
878 0 : if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision )
879 : {
880 0 : nInt++;
881 0 : fValue = 0.0;
882 : }
883 0 : sal_Int64 nFrac = 0;
884 0 : if( fValue )
885 : {
886 0 : fValue *= pow( 10.0, (double)nPrecision );
887 0 : nFrac = (sal_Int64)fValue;
888 : }
889 0 : if( bNeg && ( nInt || nFrac ) )
890 0 : rBuffer.append( '-' );
891 0 : rBuffer.append( nInt );
892 0 : if( nFrac )
893 : {
894 : int i;
895 0 : rBuffer.append( '.' );
896 0 : sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5);
897 0 : for ( i = 0; ( i < nPrecision ) && nFrac; i++ )
898 : {
899 0 : sal_Int64 nNumb = nFrac / nBound;
900 0 : nFrac -= nNumb * nBound;
901 0 : rBuffer.append( nNumb );
902 0 : nBound /= 10;
903 : }
904 : }
905 0 : }
906 :
907 0 : static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey = false )
908 : {
909 :
910 0 : if( rColor != Color( COL_TRANSPARENT ) )
911 : {
912 0 : if( bConvertToGrey )
913 : {
914 0 : sal_uInt8 cByte = rColor.GetLuminance();
915 0 : appendDouble( (double)cByte / 255.0, rBuffer );
916 : }
917 : else
918 : {
919 0 : appendDouble( (double)rColor.GetRed() / 255.0, rBuffer );
920 0 : rBuffer.append( ' ' );
921 0 : appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer );
922 0 : rBuffer.append( ' ' );
923 0 : appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer );
924 : }
925 : }
926 0 : }
927 :
928 0 : void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
929 : {
930 0 : if( rColor != Color( COL_TRANSPARENT ) )
931 : {
932 0 : bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
933 0 : appendColor( rColor, rBuffer, bGrey );
934 0 : rBuffer.append( bGrey ? " G" : " RG" );
935 : }
936 0 : }
937 :
938 0 : void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
939 : {
940 0 : if( rColor != Color( COL_TRANSPARENT ) )
941 : {
942 0 : bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
943 0 : appendColor( rColor, rBuffer, bGrey );
944 0 : rBuffer.append( bGrey ? " g" : " rg" );
945 : }
946 0 : }
947 :
948 : // matrix helper class
949 : // TODO: use basegfx matrix class instead or derive from it
950 : namespace vcl // TODO: use anonymous namespace to keep this class local
951 : {
952 : /* for sparse matrices of the form (2D linear transformations)
953 : * f[0] f[1] 0
954 : * f[2] f[3] 0
955 : * f[4] f[5] 1
956 : */
957 : class Matrix3
958 : {
959 : double f[6];
960 :
961 0 : void set( double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; }
962 : public:
963 : Matrix3();
964 0 : ~Matrix3() {}
965 :
966 : void skew( double alpha, double beta );
967 : void scale( double sx, double sy );
968 : void rotate( double angle );
969 : void translate( double tx, double ty );
970 : bool invert();
971 :
972 : void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL );
973 :
974 : Point transform( const Point& rPoint ) const;
975 : };
976 : }
977 :
978 0 : Matrix3::Matrix3()
979 : {
980 : // initialize to unity
981 0 : f[0] = 1.0;
982 0 : f[1] = 0.0;
983 0 : f[2] = 0.0;
984 0 : f[3] = 1.0;
985 0 : f[4] = 0.0;
986 0 : f[5] = 0.0;
987 0 : }
988 :
989 0 : Point Matrix3::transform( const Point& rOrig ) const
990 : {
991 0 : double x = (double)rOrig.X(), y = (double)rOrig.Y();
992 0 : return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) );
993 : }
994 :
995 0 : void Matrix3::skew( double alpha, double beta )
996 : {
997 : double fn[6];
998 0 : double tb = tan( beta );
999 0 : fn[0] = f[0] + f[2]*tb;
1000 0 : fn[1] = f[1];
1001 0 : fn[2] = f[2] + f[3]*tb;
1002 0 : fn[3] = f[3];
1003 0 : fn[4] = f[4] + f[5]*tb;
1004 0 : fn[5] = f[5];
1005 0 : if( alpha != 0.0 )
1006 : {
1007 0 : double ta = tan( alpha );
1008 0 : fn[1] += f[0]*ta;
1009 0 : fn[3] += f[2]*ta;
1010 0 : fn[5] += f[4]*ta;
1011 : }
1012 0 : set( fn );
1013 0 : }
1014 :
1015 0 : void Matrix3::scale( double sx, double sy )
1016 : {
1017 : double fn[6];
1018 0 : fn[0] = sx*f[0];
1019 0 : fn[1] = sy*f[1];
1020 0 : fn[2] = sx*f[2];
1021 0 : fn[3] = sy*f[3];
1022 0 : fn[4] = sx*f[4];
1023 0 : fn[5] = sy*f[5];
1024 0 : set( fn );
1025 0 : }
1026 :
1027 0 : void Matrix3::rotate( double angle )
1028 : {
1029 : double fn[6];
1030 0 : double fSin = sin(angle);
1031 0 : double fCos = cos(angle);
1032 0 : fn[0] = f[0]*fCos - f[1]*fSin;
1033 0 : fn[1] = f[0]*fSin + f[1]*fCos;
1034 0 : fn[2] = f[2]*fCos - f[3]*fSin;
1035 0 : fn[3] = f[2]*fSin + f[3]*fCos;
1036 0 : fn[4] = f[4]*fCos - f[5]*fSin;
1037 0 : fn[5] = f[4]*fSin + f[5]*fCos;
1038 0 : set( fn );
1039 0 : }
1040 :
1041 0 : void Matrix3::translate( double tx, double ty )
1042 : {
1043 0 : f[4] += tx;
1044 0 : f[5] += ty;
1045 0 : }
1046 :
1047 0 : bool Matrix3::invert()
1048 : {
1049 : // short circuit trivial cases
1050 0 : if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 )
1051 : {
1052 0 : f[4] = -f[4];
1053 0 : f[5] = -f[5];
1054 0 : return true;
1055 : }
1056 :
1057 : // check determinant
1058 0 : const double fDet = f[0]*f[3]-f[1]*f[2];
1059 0 : if( fDet == 0.0 )
1060 0 : return false;
1061 :
1062 : // invert the matrix
1063 : double fn[6];
1064 0 : fn[0] = +f[3] / fDet;
1065 0 : fn[1] = -f[1] / fDet;
1066 0 : fn[2] = -f[2] / fDet;
1067 0 : fn[3] = +f[0] / fDet;
1068 :
1069 : // apply inversion to translation
1070 0 : fn[4] = -(f[4]*fn[0] + f[5]*fn[2]);
1071 0 : fn[5] = -(f[4]*fn[1] + f[5]*fn[3]);
1072 :
1073 0 : set( fn );
1074 0 : return true;
1075 : }
1076 :
1077 0 : void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack )
1078 : {
1079 0 : appendDouble( f[0], rBuffer );
1080 0 : rBuffer.append( ' ' );
1081 0 : appendDouble( f[1], rBuffer );
1082 0 : rBuffer.append( ' ' );
1083 0 : appendDouble( f[2], rBuffer );
1084 0 : rBuffer.append( ' ' );
1085 0 : appendDouble( f[3], rBuffer );
1086 0 : rBuffer.append( ' ' );
1087 0 : rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack );
1088 0 : }
1089 :
1090 0 : static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList )
1091 : {
1092 0 : if( rList.empty() )
1093 0 : return;
1094 0 : rBuf.append( '/' );
1095 0 : rBuf.append( pPrefix );
1096 0 : rBuf.append( "<<" );
1097 0 : int ni = 0;
1098 0 : for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it )
1099 : {
1100 0 : if( !it->first.isEmpty() && it->second > 0 )
1101 : {
1102 0 : rBuf.append( '/' );
1103 0 : rBuf.append( it->first );
1104 0 : rBuf.append( ' ' );
1105 0 : rBuf.append( it->second );
1106 0 : rBuf.append( " 0 R" );
1107 0 : if( ((++ni) & 7) == 0 )
1108 0 : rBuf.append( '\n' );
1109 : }
1110 : }
1111 0 : rBuf.append( ">>\n" );
1112 : }
1113 :
1114 0 : void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject )
1115 : {
1116 0 : rBuf.append( "<</Font " );
1117 0 : rBuf.append( nFontDictObject );
1118 0 : rBuf.append( " 0 R\n" );
1119 0 : appendResourceMap( rBuf, "XObject", m_aXObjects );
1120 0 : appendResourceMap( rBuf, "ExtGState", m_aExtGStates );
1121 0 : appendResourceMap( rBuf, "Shading", m_aShadings );
1122 0 : appendResourceMap( rBuf, "Pattern", m_aPatterns );
1123 0 : rBuf.append( "/ProcSet[/PDF/Text" );
1124 0 : if( !m_aXObjects.empty() )
1125 0 : rBuf.append( "/ImageC/ImageI/ImageB" );
1126 0 : rBuf.append( "]\n>>\n" );
1127 0 : };
1128 :
1129 0 : PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
1130 : :
1131 : m_pWriter( pWriter ),
1132 : m_nPageWidth( nPageWidth ),
1133 : m_nPageHeight( nPageHeight ),
1134 : m_eOrientation( eOrientation ),
1135 : m_nPageObject( 0 ), // invalid object number
1136 : m_nPageIndex( -1 ), // invalid index
1137 : m_nStreamLengthObject( 0 ),
1138 : m_nBeginStreamPos( 0 ),
1139 : m_eTransition( PDFWriter::Regular ),
1140 : m_nTransTime( 0 ),
1141 : m_nDuration( 0 ),
1142 0 : m_bHasWidgets( false )
1143 : {
1144 : // object ref must be only ever updated in emit()
1145 0 : m_nPageObject = m_pWriter->createObject();
1146 0 : }
1147 :
1148 0 : PDFWriterImpl::PDFPage::~PDFPage()
1149 : {
1150 0 : }
1151 :
1152 0 : void PDFWriterImpl::PDFPage::beginStream()
1153 : {
1154 : #if OSL_DEBUG_LEVEL > 1
1155 : {
1156 : OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" );
1157 : m_pWriter->emitComment( aLine.getStr() );
1158 : }
1159 : #endif
1160 0 : m_aStreamObjects.push_back(m_pWriter->createObject());
1161 0 : if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) )
1162 0 : return;
1163 :
1164 0 : m_nStreamLengthObject = m_pWriter->createObject();
1165 : // write content stream header
1166 0 : OStringBuffer aLine;
1167 0 : aLine.append( m_aStreamObjects.back() );
1168 0 : aLine.append( " 0 obj\n<</Length " );
1169 0 : aLine.append( m_nStreamLengthObject );
1170 0 : aLine.append( " 0 R" );
1171 : #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1172 0 : aLine.append( "/Filter/FlateDecode" );
1173 : #endif
1174 0 : aLine.append( ">>\nstream\n" );
1175 0 : if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) )
1176 0 : return;
1177 0 : if (osl::File::E_None != m_pWriter->m_aFile.getPos(m_nBeginStreamPos))
1178 : {
1179 0 : m_pWriter->m_aFile.close();
1180 0 : m_pWriter->m_bOpen = false;
1181 : }
1182 : #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1183 0 : m_pWriter->beginCompression();
1184 : #endif
1185 0 : m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() );
1186 : }
1187 :
1188 0 : void PDFWriterImpl::PDFPage::endStream()
1189 : {
1190 : #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1191 0 : m_pWriter->endCompression();
1192 : #endif
1193 : sal_uInt64 nEndStreamPos;
1194 0 : if (osl::File::E_None != m_pWriter->m_aFile.getPos(nEndStreamPos))
1195 : {
1196 0 : m_pWriter->m_aFile.close();
1197 0 : m_pWriter->m_bOpen = false;
1198 0 : return;
1199 : }
1200 0 : m_pWriter->disableStreamEncryption();
1201 0 : if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1202 0 : return;
1203 : // emit stream length object
1204 0 : if( ! m_pWriter->updateObject( m_nStreamLengthObject ) )
1205 0 : return;
1206 0 : OStringBuffer aLine;
1207 0 : aLine.append( m_nStreamLengthObject );
1208 0 : aLine.append( " 0 obj\n" );
1209 0 : aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) );
1210 0 : aLine.append( "\nendobj\n\n" );
1211 0 : m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1212 : }
1213 :
1214 0 : bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject )
1215 : {
1216 : // emit page object
1217 0 : if( ! m_pWriter->updateObject( m_nPageObject ) )
1218 0 : return false;
1219 0 : OStringBuffer aLine;
1220 :
1221 0 : aLine.append( m_nPageObject );
1222 : aLine.append( " 0 obj\n"
1223 0 : "<</Type/Page/Parent " );
1224 0 : aLine.append( nParentObject );
1225 0 : aLine.append( " 0 R" );
1226 0 : aLine.append( "/Resources " );
1227 0 : aLine.append( m_pWriter->getResourceDictObj() );
1228 0 : aLine.append( " 0 R" );
1229 0 : if( m_nPageWidth && m_nPageHeight )
1230 : {
1231 0 : aLine.append( "/MediaBox[0 0 " );
1232 0 : aLine.append( m_nPageWidth );
1233 0 : aLine.append( ' ' );
1234 0 : aLine.append( m_nPageHeight );
1235 0 : aLine.append( "]" );
1236 : }
1237 0 : switch( m_eOrientation )
1238 : {
1239 0 : case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
1240 0 : case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
1241 0 : case PDFWriter::Portrait: aLine.append( "/Rotate 0\n" );break;
1242 :
1243 : case PDFWriter::Inherit:
1244 : default:
1245 0 : break;
1246 : }
1247 0 : int nAnnots = m_aAnnotations.size();
1248 0 : if( nAnnots > 0 )
1249 : {
1250 0 : aLine.append( "/Annots[\n" );
1251 0 : for( int i = 0; i < nAnnots; i++ )
1252 : {
1253 0 : aLine.append( m_aAnnotations[i] );
1254 0 : aLine.append( " 0 R" );
1255 0 : aLine.append( ((i+1)%15) ? " " : "\n" );
1256 : }
1257 0 : aLine.append( "]\n" );
1258 : }
1259 0 : if( m_aMCIDParents.size() > 0 )
1260 : {
1261 0 : OStringBuffer aStructParents( 1024 );
1262 0 : aStructParents.append( "[ " );
1263 0 : int nParents = m_aMCIDParents.size();
1264 0 : for( int i = 0; i < nParents; i++ )
1265 : {
1266 0 : aStructParents.append( m_aMCIDParents[i] );
1267 0 : aStructParents.append( " 0 R" );
1268 0 : aStructParents.append( ((i%10) == 9) ? "\n" : " " );
1269 : }
1270 0 : aStructParents.append( "]" );
1271 0 : m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() );
1272 :
1273 0 : aLine.append( "/StructParents " );
1274 0 : aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) );
1275 0 : aLine.append( "\n" );
1276 : }
1277 0 : if( m_nDuration > 0 )
1278 : {
1279 0 : aLine.append( "/Dur " );
1280 0 : aLine.append( (sal_Int32)m_nDuration );
1281 0 : aLine.append( "\n" );
1282 : }
1283 0 : if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 )
1284 : {
1285 : // transition duration
1286 0 : aLine.append( "/Trans<</D " );
1287 0 : appendDouble( (double)m_nTransTime/1000.0, aLine, 3 );
1288 0 : aLine.append( "\n" );
1289 0 : const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL;
1290 0 : switch( m_eTransition )
1291 : {
1292 : case PDFWriter::SplitHorizontalInward:
1293 0 : pStyle = "Split"; pDm = "H"; pM = "I"; break;
1294 : case PDFWriter::SplitHorizontalOutward:
1295 0 : pStyle = "Split"; pDm = "H"; pM = "O"; break;
1296 : case PDFWriter::SplitVerticalInward:
1297 0 : pStyle = "Split"; pDm = "V"; pM = "I"; break;
1298 : case PDFWriter::SplitVerticalOutward:
1299 0 : pStyle = "Split"; pDm = "V"; pM = "O"; break;
1300 : case PDFWriter::BlindsHorizontal:
1301 0 : pStyle = "Blinds"; pDm = "H"; break;
1302 : case PDFWriter::BlindsVertical:
1303 0 : pStyle = "Blinds"; pDm = "V"; break;
1304 : case PDFWriter::BoxInward:
1305 0 : pStyle = "Box"; pM = "I"; break;
1306 : case PDFWriter::BoxOutward:
1307 0 : pStyle = "Box"; pM = "O"; break;
1308 : case PDFWriter::WipeLeftToRight:
1309 0 : pStyle = "Wipe"; pDi = "0"; break;
1310 : case PDFWriter::WipeBottomToTop:
1311 0 : pStyle = "Wipe"; pDi = "90"; break;
1312 : case PDFWriter::WipeRightToLeft:
1313 0 : pStyle = "Wipe"; pDi = "180"; break;
1314 : case PDFWriter::WipeTopToBottom:
1315 0 : pStyle = "Wipe"; pDi = "270"; break;
1316 : case PDFWriter::Dissolve:
1317 0 : pStyle = "Dissolve"; break;
1318 : case PDFWriter::GlitterLeftToRight:
1319 0 : pStyle = "Glitter"; pDi = "0"; break;
1320 : case PDFWriter::GlitterTopToBottom:
1321 0 : pStyle = "Glitter"; pDi = "270"; break;
1322 : case PDFWriter::GlitterTopLeftToBottomRight:
1323 0 : pStyle = "Glitter"; pDi = "315"; break;
1324 : case PDFWriter::Regular:
1325 0 : break;
1326 : }
1327 : // transition style
1328 0 : if( pStyle )
1329 : {
1330 0 : aLine.append( "/S/" );
1331 0 : aLine.append( pStyle );
1332 0 : aLine.append( "\n" );
1333 : }
1334 0 : if( pDm )
1335 : {
1336 0 : aLine.append( "/Dm/" );
1337 0 : aLine.append( pDm );
1338 0 : aLine.append( "\n" );
1339 : }
1340 0 : if( pM )
1341 : {
1342 0 : aLine.append( "/M/" );
1343 0 : aLine.append( pM );
1344 0 : aLine.append( "\n" );
1345 : }
1346 0 : if( pDi )
1347 : {
1348 0 : aLine.append( "/Di " );
1349 0 : aLine.append( pDi );
1350 0 : aLine.append( "\n" );
1351 : }
1352 0 : aLine.append( ">>\n" );
1353 : }
1354 0 : if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 )
1355 : {
1356 0 : aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1357 : }
1358 0 : aLine.append( "/Contents" );
1359 0 : unsigned int nStreamObjects = m_aStreamObjects.size();
1360 0 : if( nStreamObjects > 1 )
1361 0 : aLine.append( '[' );
1362 0 : for( size_t i = 0; i < m_aStreamObjects.size(); i++ )
1363 : {
1364 0 : aLine.append( ' ' );
1365 0 : aLine.append( m_aStreamObjects[i] );
1366 0 : aLine.append( " 0 R" );
1367 : }
1368 0 : if( nStreamObjects > 1 )
1369 0 : aLine.append( ']' );
1370 0 : aLine.append( ">>\nendobj\n\n" );
1371 0 : return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1372 : }
1373 :
1374 : namespace vcl
1375 : {
1376 : template < class GEOMETRY >
1377 0 : GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
1378 : {
1379 0 : GEOMETRY aPoint;
1380 0 : if ( MAP_PIXEL == _rSource.GetMapUnit() )
1381 : {
1382 0 : aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
1383 : }
1384 : else
1385 : {
1386 0 : aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
1387 : }
1388 0 : return aPoint;
1389 : }
1390 : }
1391 :
1392 0 : void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const
1393 : {
1394 0 : if( pOutPoint )
1395 : {
1396 0 : Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1397 : m_pWriter->m_aMapMode,
1398 : m_pWriter->getReferenceDevice(),
1399 0 : rPoint ) );
1400 0 : *pOutPoint = aPoint;
1401 : }
1402 :
1403 0 : Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1404 : m_pWriter->m_aMapMode,
1405 : m_pWriter->getReferenceDevice(),
1406 0 : rPoint ) );
1407 :
1408 0 : sal_Int32 nValue = aPoint.X();
1409 0 : if( bNeg )
1410 0 : nValue = -nValue;
1411 :
1412 0 : appendFixedInt( nValue, rBuffer );
1413 :
1414 0 : rBuffer.append( ' ' );
1415 :
1416 0 : nValue = pointToPixel(getHeight()) - aPoint.Y();
1417 0 : if( bNeg )
1418 0 : nValue = -nValue;
1419 :
1420 0 : appendFixedInt( nValue, rBuffer );
1421 0 : }
1422 :
1423 0 : void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const
1424 : {
1425 0 : double fValue = pixelToPoint(rPoint.getX());
1426 :
1427 0 : appendDouble( fValue, rBuffer, nLog10Divisor );
1428 0 : rBuffer.append( ' ' );
1429 0 : fValue = double(getHeight()) - pixelToPoint(rPoint.getY());
1430 0 : appendDouble( fValue, rBuffer, nLog10Divisor );
1431 0 : }
1432 :
1433 0 : void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const
1434 : {
1435 0 : appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer );
1436 0 : rBuffer.append( ' ' );
1437 0 : appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false );
1438 0 : rBuffer.append( ' ' );
1439 0 : appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true );
1440 0 : rBuffer.append( " re" );
1441 0 : }
1442 :
1443 0 : void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const
1444 : {
1445 0 : Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1446 : m_pWriter->m_aMapMode,
1447 : m_pWriter->getReferenceDevice(),
1448 0 : rRect.BottomLeft() + Point( 0, 1 )
1449 0 : );
1450 0 : Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1451 : m_pWriter->m_aMapMode,
1452 : m_pWriter->getReferenceDevice(),
1453 0 : rRect.GetSize() );
1454 0 : rRect.Left() = aLL.X();
1455 0 : rRect.Right() = aLL.X() + aSize.Width();
1456 0 : rRect.Top() = pointToPixel(getHeight()) - aLL.Y();
1457 0 : rRect.Bottom() = rRect.Top() + aSize.Height();
1458 0 : }
1459 :
1460 0 : void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1461 : {
1462 0 : sal_uInt16 nPoints = rPoly.GetSize();
1463 : /*
1464 : * #108582# applications do weird things
1465 : */
1466 0 : sal_uInt32 nBufLen = rBuffer.getLength();
1467 0 : if( nPoints > 0 )
1468 : {
1469 0 : const sal_uInt8* pFlagArray = rPoly.GetConstFlagAry();
1470 0 : appendPoint( rPoly[0], rBuffer );
1471 0 : rBuffer.append( " m\n" );
1472 0 : for( sal_uInt16 i = 1; i < nPoints; i++ )
1473 : {
1474 0 : if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 )
1475 : {
1476 : // bezier
1477 : DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" );
1478 0 : appendPoint( rPoly[i], rBuffer );
1479 0 : rBuffer.append( " " );
1480 0 : appendPoint( rPoly[i+1], rBuffer );
1481 0 : rBuffer.append( " " );
1482 0 : appendPoint( rPoly[i+2], rBuffer );
1483 0 : rBuffer.append( " c" );
1484 0 : i += 2; // add additionally consumed points
1485 : }
1486 : else
1487 : {
1488 : // line
1489 0 : appendPoint( rPoly[i], rBuffer );
1490 0 : rBuffer.append( " l" );
1491 : }
1492 0 : if( (rBuffer.getLength() - nBufLen) > 65 )
1493 : {
1494 0 : rBuffer.append( "\n" );
1495 0 : nBufLen = rBuffer.getLength();
1496 : }
1497 : else
1498 0 : rBuffer.append( " " );
1499 : }
1500 0 : if( bClose )
1501 0 : rBuffer.append( "h\n" );
1502 : }
1503 0 : }
1504 :
1505 0 : void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1506 : {
1507 0 : basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1508 : m_pWriter->m_aMapMode,
1509 : m_pWriter->getReferenceDevice(),
1510 0 : rPoly ) );
1511 :
1512 0 : if( basegfx::tools::isRectangle( aPoly ) )
1513 : {
1514 0 : basegfx::B2DRange aRange( aPoly.getB2DRange() );
1515 0 : basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() );
1516 0 : appendPixelPoint( aBL, rBuffer );
1517 0 : rBuffer.append( ' ' );
1518 0 : appendMappedLength( aRange.getWidth(), rBuffer, false, NULL, nLog10Divisor );
1519 0 : rBuffer.append( ' ' );
1520 0 : appendMappedLength( aRange.getHeight(), rBuffer, true, NULL, nLog10Divisor );
1521 0 : rBuffer.append( " re\n" );
1522 0 : return;
1523 : }
1524 0 : sal_uInt32 nPoints = aPoly.count();
1525 0 : if( nPoints > 0 )
1526 : {
1527 0 : sal_uInt32 nBufLen = rBuffer.getLength();
1528 0 : basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) );
1529 0 : appendPixelPoint( aLastPoint, rBuffer );
1530 0 : rBuffer.append( " m\n" );
1531 0 : for( sal_uInt32 i = 1; i <= nPoints; i++ )
1532 : {
1533 0 : if( i != nPoints || aPoly.isClosed() )
1534 : {
1535 0 : sal_uInt32 nCurPoint = i % nPoints;
1536 0 : sal_uInt32 nLastPoint = i-1;
1537 0 : basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) );
1538 0 : if( aPoly.isNextControlPointUsed( nLastPoint ) &&
1539 0 : aPoly.isPrevControlPointUsed( nCurPoint ) )
1540 : {
1541 0 : appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1542 0 : rBuffer.append( ' ' );
1543 0 : appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1544 0 : rBuffer.append( ' ' );
1545 0 : appendPixelPoint( aPoint, rBuffer );
1546 0 : rBuffer.append( " c" );
1547 : }
1548 0 : else if( aPoly.isNextControlPointUsed( nLastPoint ) )
1549 : {
1550 0 : appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1551 0 : rBuffer.append( ' ' );
1552 0 : appendPixelPoint( aPoint, rBuffer );
1553 0 : rBuffer.append( " y" );
1554 : }
1555 0 : else if( aPoly.isPrevControlPointUsed( nCurPoint ) )
1556 : {
1557 0 : appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1558 0 : rBuffer.append( ' ' );
1559 0 : appendPixelPoint( aPoint, rBuffer );
1560 0 : rBuffer.append( " v" );
1561 : }
1562 : else
1563 : {
1564 0 : appendPixelPoint( aPoint, rBuffer );
1565 0 : rBuffer.append( " l" );
1566 : }
1567 0 : if( (rBuffer.getLength() - nBufLen) > 65 )
1568 : {
1569 0 : rBuffer.append( "\n" );
1570 0 : nBufLen = rBuffer.getLength();
1571 : }
1572 : else
1573 0 : rBuffer.append( " " );
1574 : }
1575 : }
1576 0 : if( bClose )
1577 0 : rBuffer.append( "h\n" );
1578 0 : }
1579 : }
1580 :
1581 0 : void PDFWriterImpl::PDFPage::appendPolyPolygon( const tools::PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1582 : {
1583 0 : sal_uInt16 nPolygons = rPolyPoly.Count();
1584 0 : for( sal_uInt16 n = 0; n < nPolygons; n++ )
1585 0 : appendPolygon( rPolyPoly[n], rBuffer, bClose );
1586 0 : }
1587 :
1588 0 : void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1589 : {
1590 0 : sal_uInt32 nPolygons = rPolyPoly.count();
1591 0 : for( sal_uInt32 n = 0; n < nPolygons; n++ )
1592 0 : appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer, bClose );
1593 0 : }
1594 :
1595 0 : void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1596 : {
1597 0 : sal_Int32 nValue = nLength;
1598 0 : if ( nLength < 0 )
1599 : {
1600 0 : rBuffer.append( '-' );
1601 0 : nValue = -nLength;
1602 : }
1603 0 : Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1604 : m_pWriter->m_aMapMode,
1605 : m_pWriter->getReferenceDevice(),
1606 0 : Size( nValue, nValue ) ) );
1607 0 : nValue = bVertical ? aSize.Height() : aSize.Width();
1608 0 : if( pOutLength )
1609 0 : *pOutLength = ((nLength < 0 ) ? -nValue : nValue);
1610 :
1611 0 : appendFixedInt( nValue, rBuffer, 1 );
1612 0 : }
1613 :
1614 0 : void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength, sal_Int32 nPrecision ) const
1615 : {
1616 0 : Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1617 : m_pWriter->m_aMapMode,
1618 : m_pWriter->getReferenceDevice(),
1619 0 : Size( 1000, 1000 ) ) );
1620 0 : if( pOutLength )
1621 0 : *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0);
1622 0 : fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0);
1623 0 : appendDouble( fLength, rBuffer, nPrecision );
1624 0 : }
1625 :
1626 0 : bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const
1627 : {
1628 0 : if(LINE_DASH == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen())
1629 : {
1630 : // dashed and non-degraded case, check for implementation limits of dash array
1631 : // in PDF reader apps (e.g. acroread)
1632 0 : if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10)
1633 : {
1634 0 : return false;
1635 : }
1636 : }
1637 :
1638 0 : if(basegfx::B2DLineJoin::NONE != rInfo.GetLineJoin())
1639 : {
1640 : // LineJoin used, ExtLineInfo required
1641 0 : return false;
1642 : }
1643 :
1644 0 : if(com::sun::star::drawing::LineCap_BUTT != rInfo.GetLineCap())
1645 : {
1646 : // LineCap used, ExtLineInfo required
1647 0 : return false;
1648 : }
1649 :
1650 0 : if( rInfo.GetStyle() == LINE_DASH )
1651 : {
1652 0 : rBuffer.append( "[ " );
1653 0 : if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case
1654 : {
1655 0 : appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1656 0 : rBuffer.append( ' ' );
1657 0 : appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1658 0 : rBuffer.append( ' ' );
1659 : }
1660 : else
1661 : {
1662 0 : for( int n = 0; n < rInfo.GetDashCount(); n++ )
1663 : {
1664 0 : appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1665 0 : rBuffer.append( ' ' );
1666 0 : appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1667 0 : rBuffer.append( ' ' );
1668 : }
1669 0 : for( int m = 0; m < rInfo.GetDotCount(); m++ )
1670 : {
1671 0 : appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer );
1672 0 : rBuffer.append( ' ' );
1673 0 : appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1674 0 : rBuffer.append( ' ' );
1675 : }
1676 : }
1677 0 : rBuffer.append( "] 0 d\n" );
1678 : }
1679 :
1680 0 : if( rInfo.GetWidth() > 1 )
1681 : {
1682 0 : appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer );
1683 0 : rBuffer.append( " w\n" );
1684 : }
1685 0 : else if( rInfo.GetWidth() == 0 )
1686 : {
1687 : // "pixel" line
1688 0 : appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->GetDPIX()), rBuffer );
1689 0 : rBuffer.append( " w\n" );
1690 : }
1691 :
1692 0 : return true;
1693 : }
1694 :
1695 0 : void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const
1696 : {
1697 0 : if( nWidth <= 0 )
1698 0 : return;
1699 0 : if( nDelta < 1 )
1700 0 : nDelta = 1;
1701 :
1702 0 : rBuffer.append( "0 " );
1703 0 : appendMappedLength( nY, rBuffer, true );
1704 0 : rBuffer.append( " m\n" );
1705 0 : for( sal_Int32 n = 0; n < nWidth; )
1706 : {
1707 0 : n += nDelta;
1708 0 : appendMappedLength( n, rBuffer, false );
1709 0 : rBuffer.append( ' ' );
1710 0 : appendMappedLength( nDelta+nY, rBuffer, true );
1711 0 : rBuffer.append( ' ' );
1712 0 : n += nDelta;
1713 0 : appendMappedLength( n, rBuffer, false );
1714 0 : rBuffer.append( ' ' );
1715 0 : appendMappedLength( nY, rBuffer, true );
1716 0 : rBuffer.append( " v " );
1717 0 : if( n < nWidth )
1718 : {
1719 0 : n += nDelta;
1720 0 : appendMappedLength( n, rBuffer, false );
1721 0 : rBuffer.append( ' ' );
1722 0 : appendMappedLength( nY-nDelta, rBuffer, true );
1723 0 : rBuffer.append( ' ' );
1724 0 : n += nDelta;
1725 0 : appendMappedLength( n, rBuffer, false );
1726 0 : rBuffer.append( ' ' );
1727 0 : appendMappedLength( nY, rBuffer, true );
1728 0 : rBuffer.append( " v\n" );
1729 : }
1730 : }
1731 0 : rBuffer.append( "S\n" );
1732 : }
1733 :
1734 0 : PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext,
1735 : const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc,
1736 : PDFWriter& i_rOuterFace)
1737 : :
1738 : m_pReferenceDevice( NULL ),
1739 0 : m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ),
1740 : m_nCurrentStructElement( 0 ),
1741 : m_bEmitStructure( true ),
1742 : m_bNewMCID( false ),
1743 : m_nNextFID( 1 ),
1744 : m_nInheritedPageWidth( 595 ), // default A4
1745 : m_nInheritedPageHeight( 842 ), // default A4
1746 : m_eInheritedOrientation( PDFWriter::Portrait ),
1747 : m_nCurrentPage( -1 ),
1748 : m_nCatalogObject(0),
1749 : m_nSignatureObject( -1 ),
1750 : m_nSignatureContentOffset( 0 ),
1751 : m_nSignatureLastByteRangeNoOffset( 0 ),
1752 : m_nResourceDict( -1 ),
1753 : m_nFontDictObject( -1 ),
1754 : m_aContext(rContext),
1755 : m_aFile(m_aContext.URL),
1756 : m_bOpen(false),
1757 : m_pCodec( NULL ),
1758 : m_pMemStream(NULL),
1759 0 : m_aDocDigest( rtl_digest_createMD5() ),
1760 : m_aCipher( nullptr ),
1761 : m_aDigest( NULL ),
1762 : m_nKeyLength(0),
1763 : m_nRC4KeyLength(0),
1764 : m_bEncryptThisStream( false ),
1765 : m_nAccessPermissions(0),
1766 : m_pEncryptionBuffer( NULL ),
1767 : m_nEncryptionBufferSize( 0 ),
1768 : m_bIsPDF_A1( false ),
1769 0 : m_rOuterFace( i_rOuterFace )
1770 : {
1771 : #ifdef DO_TEST_PDF
1772 : static bool bOnce = true;
1773 : if( bOnce )
1774 : {
1775 : bOnce = false;
1776 : doTestCode();
1777 : }
1778 : #endif
1779 0 : m_aStructure.push_back( PDFStructureElement() );
1780 0 : m_aStructure[0].m_nOwnElement = 0;
1781 0 : m_aStructure[0].m_nParentElement = 0;
1782 :
1783 0 : Font aFont;
1784 0 : aFont.SetName( OUString( "Times" ) );
1785 0 : aFont.SetSize( Size( 0, 12 ) );
1786 :
1787 0 : GraphicsState aState;
1788 0 : aState.m_aMapMode = m_aMapMode;
1789 0 : aState.m_aFont = aFont;
1790 0 : m_aGraphicsStack.push_front( aState );
1791 :
1792 0 : osl::File::RC aError = m_aFile.open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
1793 0 : if (aError != osl::File::E_None)
1794 : {
1795 0 : if (aError == osl::File::E_EXIST)
1796 : {
1797 0 : aError = m_aFile.open(osl_File_OpenFlag_Write);
1798 0 : if (aError == osl::File::E_None)
1799 0 : aError = m_aFile.setSize(0);
1800 : }
1801 : }
1802 0 : if (aError != osl::File::E_None)
1803 0 : return;
1804 :
1805 0 : m_bOpen = true;
1806 :
1807 : // setup DocInfo
1808 0 : setupDocInfo();
1809 :
1810 : /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1811 0 : m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1812 0 : m_aDigest = rtl_digest_createMD5();
1813 :
1814 : /* the size of the Codec default maximum */
1815 : /* is this 0x4000 required to be the same as MAX_SIGNATURE_CONTENT_LENGTH or just coincidentally the same at the moment? */
1816 0 : if (!checkEncryptionBufferSize(0x4000))
1817 : {
1818 0 : m_aFile.close();
1819 0 : m_bOpen = false;
1820 0 : return;
1821 : }
1822 :
1823 0 : if( xEnc.is() )
1824 0 : prepareEncryption( xEnc );
1825 :
1826 0 : if( m_aContext.Encryption.Encrypt() )
1827 : {
1828 : // sanity check
1829 0 : if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE ||
1830 0 : m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE ||
1831 0 : m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH
1832 : )
1833 : {
1834 : // the field lengths are invalid ? This was not setup by initEncryption.
1835 : // do not encrypt after all
1836 0 : m_aContext.Encryption.OValue.clear();
1837 0 : m_aContext.Encryption.UValue.clear();
1838 : OSL_ENSURE( false, "encryption data failed sanity check, encryption disabled" );
1839 : }
1840 : else // setup key lengths
1841 0 : m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength );
1842 : }
1843 :
1844 : // write header
1845 0 : OStringBuffer aBuffer( 20 );
1846 0 : aBuffer.append( "%PDF-" );
1847 0 : switch( m_aContext.Version )
1848 : {
1849 0 : case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break;
1850 0 : case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break;
1851 : case PDFWriter::PDF_A_1:
1852 : default:
1853 0 : case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break;
1854 0 : case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break;
1855 : }
1856 : // append something binary as comment (suggested in PDF Reference)
1857 0 : aBuffer.append( "\n%\303\244\303\274\303\266\303\237\n" );
1858 0 : if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) )
1859 : {
1860 0 : m_aFile.close();
1861 0 : m_bOpen = false;
1862 0 : return;
1863 : }
1864 :
1865 : // insert outline root
1866 0 : m_aOutline.push_back( PDFOutlineEntry() );
1867 :
1868 0 : m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1);
1869 0 : if( m_bIsPDF_A1 )
1870 0 : m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour
1871 : }
1872 :
1873 0 : PDFWriterImpl::~PDFWriterImpl()
1874 : {
1875 0 : if( m_aDocDigest )
1876 0 : rtl_digest_destroyMD5( m_aDocDigest );
1877 0 : m_pReferenceDevice.disposeAndClear();
1878 :
1879 0 : if( m_aCipher )
1880 0 : rtl_cipher_destroyARCFOUR( m_aCipher );
1881 0 : if( m_aDigest )
1882 0 : rtl_digest_destroyMD5( m_aDigest );
1883 :
1884 0 : rtl_freeMemory( m_pEncryptionBuffer );
1885 0 : }
1886 :
1887 0 : void PDFWriterImpl::setupDocInfo()
1888 : {
1889 0 : std::vector< sal_uInt8 > aId;
1890 0 : computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString );
1891 0 : if( m_aContext.Encryption.DocumentIdentifier.empty() )
1892 0 : m_aContext.Encryption.DocumentIdentifier = aId;
1893 0 : }
1894 :
1895 0 : void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier,
1896 : const vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
1897 : OString& o_rCString1,
1898 : OString& o_rCString2
1899 : )
1900 : {
1901 0 : o_rIdentifier.clear();
1902 :
1903 : //build the document id
1904 0 : OString aInfoValuesOut;
1905 0 : OStringBuffer aID( 1024 );
1906 0 : if( !i_rDocInfo.Title.isEmpty() )
1907 0 : appendUnicodeTextString( i_rDocInfo.Title, aID );
1908 0 : if( !i_rDocInfo.Author.isEmpty() )
1909 0 : appendUnicodeTextString( i_rDocInfo.Author, aID );
1910 0 : if( !i_rDocInfo.Subject.isEmpty() )
1911 0 : appendUnicodeTextString( i_rDocInfo.Subject, aID );
1912 0 : if( !i_rDocInfo.Keywords.isEmpty() )
1913 0 : appendUnicodeTextString( i_rDocInfo.Keywords, aID );
1914 0 : if( !i_rDocInfo.Creator.isEmpty() )
1915 0 : appendUnicodeTextString( i_rDocInfo.Creator, aID );
1916 0 : if( !i_rDocInfo.Producer.isEmpty() )
1917 0 : appendUnicodeTextString( i_rDocInfo.Producer, aID );
1918 :
1919 : TimeValue aTVal, aGMT;
1920 : oslDateTime aDT;
1921 0 : osl_getSystemTime( &aGMT );
1922 0 : osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
1923 0 : osl_getDateTimeFromTimeValue( &aTVal, &aDT );
1924 0 : OStringBuffer aCreationDateString(64), aCreationMetaDateString(64);
1925 0 : aCreationDateString.append( "D:" );
1926 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1927 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1928 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1929 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1930 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1931 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1932 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1933 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1934 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1935 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1936 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1937 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1938 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1939 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1940 :
1941 : //--> i59651, we fill the Metadata date string as well, if PDF/A is requested
1942 : // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1943 : // local time zone offset UTC only, whereas Acrobat 8 seems
1944 : // to use the localtime notation only
1945 : // according to a recommendation in XMP Specification (Jan 2004, page 75)
1946 : // the Acrobat way seems the right approach
1947 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1948 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1949 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1950 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1951 0 : aCreationMetaDateString.append( "-" );
1952 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1953 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1954 0 : aCreationMetaDateString.append( "-" );
1955 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1956 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1957 0 : aCreationMetaDateString.append( "T" );
1958 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1959 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1960 0 : aCreationMetaDateString.append( ":" );
1961 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1962 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1963 0 : aCreationMetaDateString.append( ":" );
1964 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1965 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1966 :
1967 0 : sal_uInt32 nDelta = 0;
1968 0 : if( aGMT.Seconds > aTVal.Seconds )
1969 : {
1970 0 : aCreationDateString.append( "-" );
1971 0 : nDelta = aGMT.Seconds-aTVal.Seconds;
1972 0 : aCreationMetaDateString.append( "-" );
1973 : }
1974 0 : else if( aGMT.Seconds < aTVal.Seconds )
1975 : {
1976 0 : aCreationDateString.append( "+" );
1977 0 : nDelta = aTVal.Seconds-aGMT.Seconds;
1978 0 : aCreationMetaDateString.append( "+" );
1979 : }
1980 : else
1981 : {
1982 0 : aCreationDateString.append( "Z" );
1983 0 : aCreationMetaDateString.append( "Z" );
1984 :
1985 : }
1986 0 : if( nDelta )
1987 : {
1988 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1989 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1990 0 : aCreationDateString.append( "'" );
1991 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1992 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1993 :
1994 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1995 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1996 0 : aCreationMetaDateString.append( ":" );
1997 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1998 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1999 : }
2000 0 : aCreationDateString.append( "'" );
2001 0 : aID.append( aCreationDateString.getStr(), aCreationDateString.getLength() );
2002 :
2003 0 : aInfoValuesOut = aID.makeStringAndClear();
2004 0 : o_rCString1 = aCreationDateString.makeStringAndClear();
2005 0 : o_rCString2 = aCreationMetaDateString.makeStringAndClear();
2006 :
2007 0 : rtlDigest aDigest = rtl_digest_createMD5();
2008 : OSL_ENSURE( aDigest != NULL, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" );
2009 0 : if( aDigest )
2010 : {
2011 0 : rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) );
2012 0 : if( nError == rtl_Digest_E_None )
2013 0 : nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() );
2014 0 : if( nError == rtl_Digest_E_None )
2015 : {
2016 0 : o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 );
2017 : //the binary form of the doc id is needed for encryption stuff
2018 0 : rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 );
2019 : }
2020 0 : rtl_digest_destroyMD5(aDigest);
2021 0 : }
2022 0 : }
2023 :
2024 : /* i12626 methods */
2025 : /*
2026 : check if the Unicode string must be encrypted or not, perform the requested task,
2027 : append the string as unicode hex, encrypted if needed
2028 : */
2029 0 : inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2030 : {
2031 0 : rOutBuffer.append( "<" );
2032 0 : if( m_aContext.Encryption.Encrypt() )
2033 : {
2034 0 : const sal_Unicode* pStr = rInString.getStr();
2035 0 : sal_Int32 nLen = rInString.getLength();
2036 : //prepare a unicode string, encrypt it
2037 0 : if( checkEncryptionBufferSize( nLen*2 ) )
2038 : {
2039 0 : enableStringEncryption( nInObjectNumber );
2040 0 : sal_uInt8 *pCopy = m_pEncryptionBuffer;
2041 0 : sal_Int32 nChars = 2;
2042 0 : *pCopy++ = 0xFE;
2043 0 : *pCopy++ = 0xFF;
2044 : // we need to prepare a byte stream from the unicode string buffer
2045 0 : for( int i = 0; i < nLen; i++ )
2046 : {
2047 0 : sal_Unicode aUnChar = pStr[i];
2048 0 : *pCopy++ = (sal_uInt8)( aUnChar >> 8 );
2049 0 : *pCopy++ = (sal_uInt8)( aUnChar & 255 );
2050 0 : nChars += 2;
2051 : }
2052 : //encrypt in place
2053 0 : rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars );
2054 : //now append, hexadecimal (appendHex), the encrypted result
2055 0 : for(int i = 0; i < nChars; i++)
2056 0 : appendHex( m_pEncryptionBuffer[i], rOutBuffer );
2057 : }
2058 : }
2059 : else
2060 0 : appendUnicodeTextString( rInString, rOutBuffer );
2061 0 : rOutBuffer.append( ">" );
2062 0 : }
2063 :
2064 0 : inline void PDFWriterImpl::appendLiteralStringEncrypt( OStringBuffer& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2065 : {
2066 0 : rOutBuffer.append( "(" );
2067 0 : sal_Int32 nChars = rInString.getLength();
2068 : //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
2069 0 : if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) )
2070 : {
2071 : //encrypt the string in a buffer, then append it
2072 0 : enableStringEncryption( nInObjectNumber );
2073 0 : rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars );
2074 0 : appendLiteralString( reinterpret_cast<sal_Char*>(m_pEncryptionBuffer), nChars, rOutBuffer );
2075 : }
2076 : else
2077 0 : appendLiteralString( rInString.getStr(), nChars , rOutBuffer );
2078 0 : rOutBuffer.append( ")" );
2079 0 : }
2080 :
2081 0 : inline void PDFWriterImpl::appendLiteralStringEncrypt( const OString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2082 : {
2083 0 : OStringBuffer aBufferString( rInString );
2084 0 : appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2085 0 : }
2086 :
2087 0 : void PDFWriterImpl::appendLiteralStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc )
2088 : {
2089 0 : OString aBufferString( OUStringToOString( rInString, nEnc ) );
2090 0 : sal_Int32 nLen = aBufferString.getLength();
2091 0 : OStringBuffer aBuf( nLen );
2092 0 : const sal_Char* pT = aBufferString.getStr();
2093 :
2094 0 : for( sal_Int32 i = 0; i < nLen; i++, pT++ )
2095 : {
2096 0 : if( (*pT & 0x80) == 0 )
2097 0 : aBuf.append( *pT );
2098 : else
2099 : {
2100 0 : aBuf.append( '<' );
2101 0 : appendHex( *pT, aBuf );
2102 0 : aBuf.append( '>' );
2103 : }
2104 : }
2105 0 : aBufferString = aBuf.makeStringAndClear();
2106 0 : appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2107 0 : }
2108 :
2109 : /* end i12626 methods */
2110 :
2111 0 : void PDFWriterImpl::emitComment( const char* pComment )
2112 : {
2113 0 : OStringBuffer aLine( 64 );
2114 0 : aLine.append( "% " );
2115 0 : aLine.append( pComment );
2116 0 : aLine.append( "\n" );
2117 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
2118 0 : }
2119 :
2120 0 : bool PDFWriterImpl::compressStream( SvMemoryStream* pStream )
2121 : {
2122 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2123 0 : pStream->Seek( STREAM_SEEK_TO_END );
2124 0 : sal_uLong nEndPos = pStream->Tell();
2125 0 : pStream->Seek( STREAM_SEEK_TO_BEGIN );
2126 0 : ZCodec pCodec( 0x4000, 0x4000 );
2127 0 : SvMemoryStream aStream;
2128 0 : pCodec.BeginCompression();
2129 0 : pCodec.Write( aStream, static_cast<const sal_uInt8*>(pStream->GetData()), nEndPos );
2130 0 : pCodec.EndCompression();
2131 0 : nEndPos = aStream.Tell();
2132 0 : pStream->Seek( STREAM_SEEK_TO_BEGIN );
2133 0 : aStream.Seek( STREAM_SEEK_TO_BEGIN );
2134 0 : pStream->SetStreamSize( nEndPos );
2135 0 : pStream->Write( aStream.GetData(), nEndPos );
2136 0 : return true;
2137 : #else
2138 : (void)pStream;
2139 : return false;
2140 : #endif
2141 : }
2142 :
2143 0 : void PDFWriterImpl::beginCompression()
2144 : {
2145 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2146 0 : m_pCodec = new ZCodec( 0x4000, 0x4000 );
2147 0 : m_pMemStream = new SvMemoryStream();
2148 0 : m_pCodec->BeginCompression();
2149 : #endif
2150 0 : }
2151 :
2152 0 : void PDFWriterImpl::endCompression()
2153 : {
2154 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2155 0 : if( m_pCodec )
2156 : {
2157 0 : m_pCodec->EndCompression();
2158 0 : delete m_pCodec;
2159 0 : m_pCodec = NULL;
2160 0 : sal_uInt64 nLen = m_pMemStream->Tell();
2161 0 : m_pMemStream->Seek( 0 );
2162 0 : writeBuffer( m_pMemStream->GetData(), nLen );
2163 0 : delete m_pMemStream;
2164 0 : m_pMemStream = NULL;
2165 : }
2166 : #endif
2167 0 : }
2168 :
2169 0 : bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes )
2170 : {
2171 0 : if( ! m_bOpen ) // we are already down the drain
2172 0 : return false;
2173 :
2174 0 : if( ! nBytes ) // huh ?
2175 0 : return true;
2176 :
2177 0 : if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2178 : {
2179 0 : m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END );
2180 0 : m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) );
2181 0 : return true;
2182 : }
2183 :
2184 : sal_uInt64 nWritten;
2185 0 : if( m_pCodec )
2186 : {
2187 0 : m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes );
2188 0 : nWritten = nBytes;
2189 : }
2190 : else
2191 : {
2192 0 : bool buffOK = true;
2193 0 : if( m_bEncryptThisStream )
2194 : {
2195 : /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
2196 0 : if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) )
2197 : rtl_cipher_encodeARCFOUR( m_aCipher,
2198 : pBuffer, static_cast<sal_Size>(nBytes),
2199 0 : m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) );
2200 : }
2201 :
2202 0 : const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer : pBuffer;
2203 0 : if( m_aDocDigest )
2204 0 : rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) );
2205 :
2206 0 : if (m_aFile.write(pWriteBuffer, nBytes, nWritten) != osl::File::E_None)
2207 0 : nWritten = 0;
2208 :
2209 0 : if( nWritten != nBytes )
2210 : {
2211 0 : m_aFile.close();
2212 0 : m_bOpen = false;
2213 : }
2214 : }
2215 :
2216 0 : return nWritten == nBytes;
2217 : }
2218 :
2219 0 : OutputDevice* PDFWriterImpl::getReferenceDevice()
2220 : {
2221 0 : if( ! m_pReferenceDevice )
2222 : {
2223 0 : VclPtrInstance<VirtualDevice> pVDev( 0 );
2224 :
2225 0 : m_pReferenceDevice = pVDev;
2226 :
2227 0 : if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 )
2228 0 : pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 );
2229 : else
2230 0 : pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy );
2231 :
2232 0 : pVDev->SetOutputSizePixel( Size( 640, 480 ) );
2233 0 : pVDev->SetMapMode( MAP_MM );
2234 :
2235 0 : m_pReferenceDevice->mpPDFWriter = this;
2236 0 : m_pReferenceDevice->ImplUpdateFontData( true );
2237 : }
2238 0 : return m_pReferenceDevice;
2239 : }
2240 :
2241 0 : class ImplPdfBuiltinFontData : public PhysicalFontFace
2242 : {
2243 : private:
2244 : const PDFWriterImpl::BuiltinFont& mrBuiltin;
2245 :
2246 : public:
2247 : enum {PDF_FONT_MAGIC = 0xBDFF0A1C };
2248 : explicit ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& );
2249 0 : const PDFWriterImpl::BuiltinFont& GetBuiltinFont() const { return mrBuiltin; }
2250 :
2251 0 : virtual PhysicalFontFace* Clone() const SAL_OVERRIDE { return new ImplPdfBuiltinFontData(*this); }
2252 : virtual ImplFontEntry* CreateFontInstance( FontSelectPattern& ) const SAL_OVERRIDE;
2253 0 : virtual sal_IntPtr GetFontId() const SAL_OVERRIDE { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); }
2254 : };
2255 :
2256 0 : inline const ImplPdfBuiltinFontData* GetPdfFontData( const PhysicalFontFace* pFontData )
2257 : {
2258 0 : const ImplPdfBuiltinFontData* pFD = NULL;
2259 0 : if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) )
2260 0 : pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData );
2261 0 : return pFD;
2262 : }
2263 :
2264 0 : static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin )
2265 : {
2266 0 : ImplDevFontAttributes aDFA;
2267 0 : aDFA.SetFamilyName( OUString::createFromAscii( rBuiltin.m_pName ) );
2268 0 : aDFA.SetStyleName( OUString::createFromAscii( rBuiltin.m_pStyleName ) );
2269 0 : aDFA.SetFamilyType( rBuiltin.m_eFamily );
2270 0 : aDFA.SetSymbolFlag( rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 );
2271 0 : aDFA.SetPitch( rBuiltin.m_ePitch );
2272 0 : aDFA.SetWeight( rBuiltin.m_eWeight );
2273 0 : aDFA.SetItalic( rBuiltin.m_eItalic );
2274 0 : aDFA.SetWidthType( rBuiltin.m_eWidthType );
2275 :
2276 0 : aDFA.mbOrientation = true;
2277 0 : aDFA.mbDevice = true;
2278 0 : aDFA.mnQuality = 50000;
2279 0 : aDFA.mbSubsettable = false;
2280 0 : aDFA.mbEmbeddable = false;
2281 0 : return aDFA;
2282 : }
2283 :
2284 0 : ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin )
2285 : : PhysicalFontFace( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ),
2286 0 : mrBuiltin( rBuiltin )
2287 0 : {}
2288 :
2289 0 : ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( FontSelectPattern& rFSD ) const
2290 : {
2291 0 : ImplFontEntry* pEntry = new ImplFontEntry( rFSD );
2292 0 : return pEntry;
2293 : }
2294 :
2295 : // - PDFWriterImpl -
2296 :
2297 0 : sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
2298 : {
2299 0 : endPage();
2300 0 : m_nCurrentPage = m_aPages.size();
2301 0 : m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) );
2302 0 : m_aPages.back().m_nPageIndex = m_nCurrentPage;
2303 0 : m_aPages.back().beginStream();
2304 :
2305 : // setup global graphics state
2306 : // linewidth is "1 pixel" by default
2307 0 : OStringBuffer aBuf( 16 );
2308 0 : appendDouble( 72.0/double(getReferenceDevice()->GetDPIX()), aBuf );
2309 0 : aBuf.append( " w\n" );
2310 0 : writeBuffer( aBuf.getStr(), aBuf.getLength() );
2311 :
2312 0 : return m_nCurrentPage;
2313 : }
2314 :
2315 0 : void PDFWriterImpl::endPage()
2316 : {
2317 0 : if( m_aPages.begin() != m_aPages.end() )
2318 : {
2319 : // close eventual MC sequence
2320 0 : endStructureElementMCSeq();
2321 :
2322 : // sanity check
2323 0 : if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2324 : {
2325 : OSL_FAIL( "redirection across pages !!!" );
2326 0 : m_aOutputStreams.clear(); // leak !
2327 0 : m_aMapMode.SetOrigin( Point() );
2328 : }
2329 :
2330 0 : m_aGraphicsStack.clear();
2331 0 : m_aGraphicsStack.push_back( GraphicsState() );
2332 :
2333 : // this should pop the PDF graphics stack if necessary
2334 0 : updateGraphicsState();
2335 :
2336 0 : m_aPages.back().endStream();
2337 :
2338 : // reset the default font
2339 0 : Font aFont;
2340 0 : aFont.SetName( OUString( "Times" ) );
2341 0 : aFont.SetSize( Size( 0, 12 ) );
2342 :
2343 0 : m_aCurrentPDFState = m_aGraphicsStack.front();
2344 0 : m_aGraphicsStack.front().m_aFont = aFont;
2345 :
2346 0 : for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin();
2347 0 : it != m_aBitmaps.end(); ++it )
2348 : {
2349 0 : if( ! it->m_aBitmap.IsEmpty() )
2350 : {
2351 0 : writeBitmapObject( *it );
2352 0 : it->m_aBitmap = BitmapEx();
2353 : }
2354 : }
2355 0 : for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg )
2356 : {
2357 0 : if( jpeg->m_pStream )
2358 : {
2359 0 : writeJPG( *jpeg );
2360 0 : delete jpeg->m_pStream;
2361 0 : jpeg->m_pStream = NULL;
2362 0 : jpeg->m_aMask = Bitmap();
2363 : }
2364 : }
2365 0 : for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin();
2366 0 : t != m_aTransparentObjects.end(); ++t )
2367 : {
2368 0 : if( t->m_pContentStream )
2369 : {
2370 0 : writeTransparentObject( *t );
2371 0 : delete t->m_pContentStream;
2372 0 : t->m_pContentStream = NULL;
2373 : }
2374 0 : }
2375 : }
2376 0 : }
2377 :
2378 0 : sal_Int32 PDFWriterImpl::createObject()
2379 : {
2380 0 : m_aObjects.push_back( ~0U );
2381 0 : return m_aObjects.size();
2382 : }
2383 :
2384 0 : bool PDFWriterImpl::updateObject( sal_Int32 n )
2385 : {
2386 0 : if( ! m_bOpen )
2387 0 : return false;
2388 :
2389 0 : sal_uInt64 nOffset = ~0U;
2390 0 : osl::File::RC aError = m_aFile.getPos(nOffset);
2391 : DBG_ASSERT( aError == osl::File::E_None, "could not register object" );
2392 0 : if (aError != osl::File::E_None)
2393 : {
2394 0 : m_aFile.close();
2395 0 : m_bOpen = false;
2396 : }
2397 0 : m_aObjects[ n-1 ] = nOffset;
2398 0 : return aError == osl::File::E_None;
2399 : }
2400 :
2401 : #define CHECK_RETURN( x ) if( !(x) ) return 0
2402 :
2403 0 : sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject )
2404 : {
2405 0 : if( nObject > 0 )
2406 : {
2407 0 : OStringBuffer aLine( 1024 );
2408 :
2409 0 : aLine.append( nObject );
2410 : aLine.append( " 0 obj\n"
2411 0 : "<</Nums[\n" );
2412 0 : sal_Int32 nTreeItems = m_aStructParentTree.size();
2413 0 : for( sal_Int32 n = 0; n < nTreeItems; n++ )
2414 : {
2415 0 : aLine.append( n );
2416 0 : aLine.append( ' ' );
2417 0 : aLine.append( m_aStructParentTree[n] );
2418 0 : aLine.append( "\n" );
2419 : }
2420 0 : aLine.append( "]>>\nendobj\n\n" );
2421 0 : CHECK_RETURN( updateObject( nObject ) );
2422 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2423 : }
2424 0 : return nObject;
2425 : }
2426 :
2427 0 : const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr )
2428 : {
2429 0 : static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings;
2430 : // fill maps once
2431 0 : if( aAttributeStrings.empty() )
2432 : {
2433 0 : aAttributeStrings[ PDFWriter::Placement ] = "Placement";
2434 0 : aAttributeStrings[ PDFWriter::WritingMode ] = "WritingMode";
2435 0 : aAttributeStrings[ PDFWriter::SpaceBefore ] = "SpaceBefore";
2436 0 : aAttributeStrings[ PDFWriter::SpaceAfter ] = "SpaceAfter";
2437 0 : aAttributeStrings[ PDFWriter::StartIndent ] = "StartIndent";
2438 0 : aAttributeStrings[ PDFWriter::EndIndent ] = "EndIndent";
2439 0 : aAttributeStrings[ PDFWriter::TextIndent ] = "TextIndent";
2440 0 : aAttributeStrings[ PDFWriter::TextAlign ] = "TextAlign";
2441 0 : aAttributeStrings[ PDFWriter::Width ] = "Width";
2442 0 : aAttributeStrings[ PDFWriter::Height ] = "Height";
2443 0 : aAttributeStrings[ PDFWriter::BlockAlign ] = "BlockAlign";
2444 0 : aAttributeStrings[ PDFWriter::InlineAlign ] = "InlineAlign";
2445 0 : aAttributeStrings[ PDFWriter::LineHeight ] = "LineHeight";
2446 0 : aAttributeStrings[ PDFWriter::BaselineShift ] = "BaselineShift";
2447 0 : aAttributeStrings[ PDFWriter::TextDecorationType ] = "TextDecorationType";
2448 0 : aAttributeStrings[ PDFWriter::ListNumbering ] = "ListNumbering";
2449 0 : aAttributeStrings[ PDFWriter::RowSpan ] = "RowSpan";
2450 0 : aAttributeStrings[ PDFWriter::ColSpan ] = "ColSpan";
2451 0 : aAttributeStrings[ PDFWriter::LinkAnnotation ] = "LinkAnnotation";
2452 : }
2453 :
2454 : std::map< PDFWriter::StructAttribute, const char* >::const_iterator it =
2455 0 : aAttributeStrings.find( eAttr );
2456 :
2457 : #if OSL_DEBUG_LEVEL > 1
2458 : if( it == aAttributeStrings.end() )
2459 : fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr );
2460 : #endif
2461 :
2462 0 : return it != aAttributeStrings.end() ? it->second : "";
2463 : }
2464 :
2465 0 : const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal )
2466 : {
2467 0 : static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings;
2468 :
2469 0 : if( aValueStrings.empty() )
2470 : {
2471 0 : aValueStrings[ PDFWriter::NONE ] = "None";
2472 0 : aValueStrings[ PDFWriter::Block ] = "Block";
2473 0 : aValueStrings[ PDFWriter::Inline ] = "Inline";
2474 0 : aValueStrings[ PDFWriter::Before ] = "Before";
2475 0 : aValueStrings[ PDFWriter::After ] = "After";
2476 0 : aValueStrings[ PDFWriter::Start ] = "Start";
2477 0 : aValueStrings[ PDFWriter::End ] = "End";
2478 0 : aValueStrings[ PDFWriter::LrTb ] = "LrTb";
2479 0 : aValueStrings[ PDFWriter::RlTb ] = "RlTb";
2480 0 : aValueStrings[ PDFWriter::TbRl ] = "TbRl";
2481 0 : aValueStrings[ PDFWriter::Center ] = "Center";
2482 0 : aValueStrings[ PDFWriter::Justify ] = "Justify";
2483 0 : aValueStrings[ PDFWriter::Auto ] = "Auto";
2484 0 : aValueStrings[ PDFWriter::Middle ] = "Middle";
2485 0 : aValueStrings[ PDFWriter::Normal ] = "Normal";
2486 0 : aValueStrings[ PDFWriter::Underline ] = "Underline";
2487 0 : aValueStrings[ PDFWriter::Overline ] = "Overline";
2488 0 : aValueStrings[ PDFWriter::LineThrough ] = "LineThrough";
2489 0 : aValueStrings[ PDFWriter::Disc ] = "Disc";
2490 0 : aValueStrings[ PDFWriter::Circle ] = "Circle";
2491 0 : aValueStrings[ PDFWriter::Square ] = "Square";
2492 0 : aValueStrings[ PDFWriter::Decimal ] = "Decimal";
2493 0 : aValueStrings[ PDFWriter::UpperRoman ] = "UpperRoman";
2494 0 : aValueStrings[ PDFWriter::LowerRoman ] = "LowerRoman";
2495 0 : aValueStrings[ PDFWriter::UpperAlpha ] = "UpperAlpha";
2496 0 : aValueStrings[ PDFWriter::LowerAlpha ] = "LowerAlpha";
2497 : }
2498 :
2499 : std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it =
2500 0 : aValueStrings.find( eVal );
2501 :
2502 : #if OSL_DEBUG_LEVEL > 1
2503 : if( it == aValueStrings.end() )
2504 : fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal );
2505 : #endif
2506 :
2507 0 : return it != aValueStrings.end() ? it->second : "";
2508 : }
2509 :
2510 0 : static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt )
2511 : {
2512 0 : o_rLine.append( "/" );
2513 0 : o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) );
2514 :
2515 0 : if( i_rVal.eValue != PDFWriter::Invalid )
2516 : {
2517 0 : o_rLine.append( "/" );
2518 0 : o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) );
2519 : }
2520 : else
2521 : {
2522 : // numerical value
2523 0 : o_rLine.append( " " );
2524 0 : if( i_bIsFixedInt )
2525 0 : appendFixedInt( i_rVal.nValue, o_rLine );
2526 : else
2527 0 : o_rLine.append( i_rVal.nValue );
2528 : }
2529 0 : o_rLine.append( "\n" );
2530 0 : }
2531 :
2532 0 : OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
2533 : {
2534 : // create layout, list and table attribute sets
2535 0 : OStringBuffer aLayout(256), aList(64), aTable(64);
2536 0 : for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin();
2537 0 : it != i_rEle.m_aAttributes.end(); ++it )
2538 : {
2539 0 : if( it->first == PDFWriter::ListNumbering )
2540 0 : appendStructureAttributeLine( it->first, it->second, aList, true );
2541 0 : else if( it->first == PDFWriter::RowSpan ||
2542 0 : it->first == PDFWriter::ColSpan )
2543 0 : appendStructureAttributeLine( it->first, it->second, aTable, false );
2544 0 : else if( it->first == PDFWriter::LinkAnnotation )
2545 : {
2546 0 : sal_Int32 nLink = it->second.nValue;
2547 : std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
2548 0 : m_aLinkPropertyMap.find( nLink );
2549 0 : if( link_it != m_aLinkPropertyMap.end() )
2550 0 : nLink = link_it->second;
2551 0 : if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() )
2552 : {
2553 : // update struct parent of link
2554 0 : OStringBuffer aStructParentEntry( 32 );
2555 0 : aStructParentEntry.append( i_rEle.m_nObject );
2556 0 : aStructParentEntry.append( " 0 R" );
2557 0 : m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() );
2558 0 : m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1;
2559 :
2560 0 : sal_Int32 nRefObject = createObject();
2561 0 : OStringBuffer aRef( 256 );
2562 0 : aRef.append( nRefObject );
2563 : aRef.append( " 0 obj\n"
2564 0 : "<</Type/OBJR/Obj " );
2565 0 : aRef.append( m_aLinks[ nLink ].m_nObject );
2566 : aRef.append( " 0 R>>\n"
2567 : "endobj\n\n"
2568 0 : );
2569 0 : if (updateObject(nRefObject))
2570 : {
2571 0 : writeBuffer( aRef.getStr(), aRef.getLength() );
2572 : }
2573 :
2574 0 : i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) );
2575 : }
2576 : else
2577 : {
2578 : OSL_FAIL( "unresolved link id for Link structure" );
2579 : #if OSL_DEBUG_LEVEL > 1
2580 : fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink );
2581 : {
2582 : OStringBuffer aLine( "unresolved link id " );
2583 : aLine.append( nLink );
2584 : aLine.append( " for Link structure" );
2585 : emitComment( aLine.getStr() );
2586 : }
2587 : #endif
2588 : }
2589 : }
2590 : else
2591 0 : appendStructureAttributeLine( it->first, it->second, aLayout, true );
2592 : }
2593 0 : if( ! i_rEle.m_aBBox.IsEmpty() )
2594 : {
2595 0 : aLayout.append( "/BBox[" );
2596 0 : appendFixedInt( i_rEle.m_aBBox.Left(), aLayout );
2597 0 : aLayout.append( " " );
2598 0 : appendFixedInt( i_rEle.m_aBBox.Top(), aLayout );
2599 0 : aLayout.append( " " );
2600 0 : appendFixedInt( i_rEle.m_aBBox.Right(), aLayout );
2601 0 : aLayout.append( " " );
2602 0 : appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout );
2603 0 : aLayout.append( "]\n" );
2604 : }
2605 :
2606 0 : std::vector< sal_Int32 > aAttribObjects;
2607 0 : if( !aLayout.isEmpty() )
2608 : {
2609 0 : aAttribObjects.push_back( createObject() );
2610 0 : if (updateObject( aAttribObjects.back() ))
2611 : {
2612 0 : OStringBuffer aObj( 64 );
2613 0 : aObj.append( aAttribObjects.back() );
2614 : aObj.append( " 0 obj\n"
2615 0 : "<</O/Layout\n" );
2616 0 : aLayout.append( ">>\nendobj\n\n" );
2617 0 : writeBuffer( aObj.getStr(), aObj.getLength() );
2618 0 : writeBuffer( aLayout.getStr(), aLayout.getLength() );
2619 : }
2620 : }
2621 0 : if( !aList.isEmpty() )
2622 : {
2623 0 : aAttribObjects.push_back( createObject() );
2624 0 : if (updateObject( aAttribObjects.back() ))
2625 : {
2626 0 : OStringBuffer aObj( 64 );
2627 0 : aObj.append( aAttribObjects.back() );
2628 : aObj.append( " 0 obj\n"
2629 0 : "<</O/List\n" );
2630 0 : aList.append( ">>\nendobj\n\n" );
2631 0 : writeBuffer( aObj.getStr(), aObj.getLength() );
2632 0 : writeBuffer( aList.getStr(), aList.getLength() );
2633 : }
2634 : }
2635 0 : if( !aTable.isEmpty() )
2636 : {
2637 0 : aAttribObjects.push_back( createObject() );
2638 0 : if (updateObject( aAttribObjects.back() ))
2639 : {
2640 0 : OStringBuffer aObj( 64 );
2641 0 : aObj.append( aAttribObjects.back() );
2642 : aObj.append( " 0 obj\n"
2643 0 : "<</O/Table\n" );
2644 0 : aTable.append( ">>\nendobj\n\n" );
2645 0 : writeBuffer( aObj.getStr(), aObj.getLength() );
2646 0 : writeBuffer( aTable.getStr(), aTable.getLength() );
2647 : }
2648 : }
2649 :
2650 0 : OStringBuffer aRet( 64 );
2651 0 : if( aAttribObjects.size() > 1 )
2652 0 : aRet.append( " [" );
2653 0 : for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin();
2654 0 : at_it != aAttribObjects.end(); ++at_it )
2655 : {
2656 0 : aRet.append( " " );
2657 0 : aRet.append( *at_it );
2658 0 : aRet.append( " 0 R" );
2659 : }
2660 0 : if( aAttribObjects.size() > 1 )
2661 0 : aRet.append( " ]" );
2662 0 : return aRet.makeStringAndClear();
2663 : }
2664 :
2665 0 : sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
2666 : {
2667 0 : if(
2668 : // do not emit NonStruct and its children
2669 0 : rEle.m_eType == PDFWriter::NonStructElement &&
2670 0 : rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root
2671 : )
2672 0 : return 0;
2673 :
2674 0 : for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
2675 : {
2676 0 : if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
2677 : {
2678 0 : PDFStructureElement& rChild = m_aStructure[ *it ];
2679 0 : if( rChild.m_eType != PDFWriter::NonStructElement )
2680 : {
2681 0 : if( rChild.m_nParentElement == rEle.m_nOwnElement )
2682 0 : emitStructure( rChild );
2683 : else
2684 : {
2685 : OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure element" );
2686 : #if OSL_DEBUG_LEVEL > 1
2687 : fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
2688 : #endif
2689 : }
2690 : }
2691 : }
2692 : else
2693 : {
2694 : OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
2695 : #if OSL_DEBUG_LEVEL > 1
2696 : fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
2697 : #endif
2698 : }
2699 : }
2700 :
2701 0 : OStringBuffer aLine( 512 );
2702 0 : aLine.append( rEle.m_nObject );
2703 : aLine.append( " 0 obj\n"
2704 0 : "<</Type" );
2705 0 : sal_Int32 nParentTree = -1;
2706 0 : if( rEle.m_nOwnElement == rEle.m_nParentElement )
2707 : {
2708 0 : nParentTree = createObject();
2709 0 : CHECK_RETURN( nParentTree );
2710 0 : aLine.append( "/StructTreeRoot\n" );
2711 0 : aLine.append( "/ParentTree " );
2712 0 : aLine.append( nParentTree );
2713 0 : aLine.append( " 0 R\n" );
2714 0 : if( ! m_aRoleMap.empty() )
2715 : {
2716 0 : aLine.append( "/RoleMap<<" );
2717 0 : for( std::unordered_map<OString,OString,OStringHash>::const_iterator
2718 0 : it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it )
2719 : {
2720 0 : aLine.append( '/' );
2721 0 : aLine.append(it->first);
2722 0 : aLine.append( '/' );
2723 0 : aLine.append( it->second );
2724 0 : aLine.append( '\n' );
2725 : }
2726 0 : aLine.append( ">>\n" );
2727 : }
2728 : }
2729 : else
2730 : {
2731 : aLine.append( "/StructElem\n"
2732 0 : "/S/" );
2733 0 : if( !rEle.m_aAlias.isEmpty() )
2734 0 : aLine.append( rEle.m_aAlias );
2735 : else
2736 0 : aLine.append( getStructureTag( rEle.m_eType ) );
2737 : aLine.append( "\n"
2738 0 : "/P " );
2739 0 : aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject );
2740 : aLine.append( " 0 R\n"
2741 0 : "/Pg " );
2742 0 : aLine.append( rEle.m_nFirstPageObject );
2743 0 : aLine.append( " 0 R\n" );
2744 0 : if( !rEle.m_aActualText.isEmpty() )
2745 : {
2746 0 : aLine.append( "/ActualText" );
2747 0 : appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine );
2748 0 : aLine.append( "\n" );
2749 : }
2750 0 : if( !rEle.m_aAltText.isEmpty() )
2751 : {
2752 0 : aLine.append( "/Alt" );
2753 0 : appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine );
2754 0 : aLine.append( "\n" );
2755 : }
2756 : }
2757 0 : if( (! rEle.m_aBBox.IsEmpty()) || (! rEle.m_aAttributes.empty()) )
2758 : {
2759 0 : OString aAttribs = emitStructureAttributes( rEle );
2760 0 : if( !aAttribs.isEmpty() )
2761 : {
2762 0 : aLine.append( "/A" );
2763 0 : aLine.append( aAttribs );
2764 0 : aLine.append( "\n" );
2765 0 : }
2766 : }
2767 0 : if( !rEle.m_aLocale.Language.isEmpty() )
2768 : {
2769 : /* PDF allows only RFC 3066, which is only partly BCP 47 and does not
2770 : * include script tags and others.
2771 : * http://pdf.editme.com/pdfua-naturalLanguageSpecification
2772 : * http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf#page=886
2773 : * https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf#M13.9.19332.1Heading.97.Natural.Language.Specification
2774 : * */
2775 0 : LanguageTag aLanguageTag( rEle.m_aLocale);
2776 0 : OUString aLanguage, aScript, aCountry;
2777 0 : aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
2778 0 : if (!aLanguage.isEmpty())
2779 : {
2780 0 : OUStringBuffer aLocBuf( 16 );
2781 0 : aLocBuf.append( aLanguage );
2782 0 : if( !aCountry.isEmpty() )
2783 : {
2784 0 : aLocBuf.append( '-' );
2785 0 : aLocBuf.append( aCountry );
2786 : }
2787 0 : aLine.append( "/Lang" );
2788 0 : appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine );
2789 0 : aLine.append( "\n" );
2790 0 : }
2791 : }
2792 0 : if( ! rEle.m_aKids.empty() )
2793 : {
2794 0 : unsigned int i = 0;
2795 0 : aLine.append( "/K[" );
2796 0 : for( std::list< PDFStructureElementKid >::const_iterator it =
2797 0 : rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ )
2798 : {
2799 0 : if( it->nMCID == -1 )
2800 : {
2801 0 : aLine.append( it->nObject );
2802 0 : aLine.append( " 0 R" );
2803 0 : aLine.append( ( (i & 15) == 15 ) ? "\n" : " " );
2804 : }
2805 : else
2806 : {
2807 0 : if( it->nObject == rEle.m_nFirstPageObject )
2808 : {
2809 0 : aLine.append( it->nMCID );
2810 0 : aLine.append( " " );
2811 : }
2812 : else
2813 : {
2814 0 : aLine.append( "<</Type/MCR/Pg " );
2815 0 : aLine.append( it->nObject );
2816 0 : aLine.append( " 0 R /MCID " );
2817 0 : aLine.append( it->nMCID );
2818 0 : aLine.append( ">>\n" );
2819 : }
2820 : }
2821 : }
2822 0 : aLine.append( "]\n" );
2823 : }
2824 0 : aLine.append( ">>\nendobj\n\n" );
2825 :
2826 0 : CHECK_RETURN( updateObject( rEle.m_nObject ) );
2827 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2828 :
2829 0 : CHECK_RETURN( emitStructParentTree( nParentTree ) );
2830 :
2831 0 : return rEle.m_nObject;
2832 : }
2833 :
2834 0 : bool PDFWriterImpl::emitGradients()
2835 : {
2836 0 : for( std::list<GradientEmit>::iterator it = m_aGradients.begin();
2837 0 : it != m_aGradients.end(); ++it )
2838 : {
2839 0 : if ( !writeGradientFunction( *it ) ) return false;
2840 : }
2841 0 : return true;
2842 : }
2843 :
2844 0 : bool PDFWriterImpl::emitTilings()
2845 : {
2846 0 : OStringBuffer aTilingObj( 1024 );
2847 :
2848 0 : for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it )
2849 : {
2850 : DBG_ASSERT( it->m_pTilingStream, "tiling without stream" );
2851 0 : if( ! it->m_pTilingStream )
2852 0 : continue;
2853 :
2854 0 : aTilingObj.setLength( 0 );
2855 :
2856 : #if OSL_DEBUG_LEVEL > 1
2857 : emitComment( "PDFWriterImpl::emitTilings" );
2858 : #endif
2859 :
2860 0 : sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left();
2861 0 : sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top();
2862 0 : sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth();
2863 0 : sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight();
2864 0 : if( it->m_aCellSize.Width() == 0 )
2865 0 : it->m_aCellSize.Width() = nW;
2866 0 : if( it->m_aCellSize.Height() == 0 )
2867 0 : it->m_aCellSize.Height() = nH;
2868 :
2869 0 : bool bDeflate = compressStream( it->m_pTilingStream );
2870 0 : it->m_pTilingStream->Seek( STREAM_SEEK_TO_END );
2871 0 : sal_Size nTilingStreamSize = it->m_pTilingStream->Tell();
2872 0 : it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN );
2873 :
2874 : // write pattern object
2875 0 : aTilingObj.append( it->m_nObject );
2876 0 : aTilingObj.append( " 0 obj\n" );
2877 : aTilingObj.append( "<</Type/Pattern/PatternType 1\n"
2878 : "/PaintType 1\n"
2879 : "/TilingType 2\n"
2880 0 : "/BBox[" );
2881 0 : appendFixedInt( nX, aTilingObj );
2882 0 : aTilingObj.append( ' ' );
2883 0 : appendFixedInt( nY, aTilingObj );
2884 0 : aTilingObj.append( ' ' );
2885 0 : appendFixedInt( nX+nW, aTilingObj );
2886 0 : aTilingObj.append( ' ' );
2887 0 : appendFixedInt( nY+nH, aTilingObj );
2888 : aTilingObj.append( "]\n"
2889 0 : "/XStep " );
2890 0 : appendFixedInt( it->m_aCellSize.Width(), aTilingObj );
2891 : aTilingObj.append( "\n"
2892 0 : "/YStep " );
2893 0 : appendFixedInt( it->m_aCellSize.Height(), aTilingObj );
2894 0 : aTilingObj.append( "\n" );
2895 0 : if( it->m_aTransform.matrix[0] != 1.0 ||
2896 0 : it->m_aTransform.matrix[1] != 0.0 ||
2897 0 : it->m_aTransform.matrix[3] != 0.0 ||
2898 0 : it->m_aTransform.matrix[4] != 1.0 ||
2899 0 : it->m_aTransform.matrix[2] != 0.0 ||
2900 0 : it->m_aTransform.matrix[5] != 0.0 )
2901 : {
2902 0 : aTilingObj.append( "/Matrix [" );
2903 : // TODO: scaling, mirroring on y, etc
2904 0 : appendDouble( it->m_aTransform.matrix[0], aTilingObj );
2905 0 : aTilingObj.append( ' ' );
2906 0 : appendDouble( it->m_aTransform.matrix[1], aTilingObj );
2907 0 : aTilingObj.append( ' ' );
2908 0 : appendDouble( it->m_aTransform.matrix[3], aTilingObj );
2909 0 : aTilingObj.append( ' ' );
2910 0 : appendDouble( it->m_aTransform.matrix[4], aTilingObj );
2911 0 : aTilingObj.append( ' ' );
2912 0 : appendDouble( it->m_aTransform.matrix[2], aTilingObj );
2913 0 : aTilingObj.append( ' ' );
2914 0 : appendDouble( it->m_aTransform.matrix[5], aTilingObj );
2915 0 : aTilingObj.append( "]\n" );
2916 : }
2917 0 : aTilingObj.append( "/Resources" );
2918 0 : it->m_aResources.append( aTilingObj, getFontDictObject() );
2919 0 : if( bDeflate )
2920 0 : aTilingObj.append( "/Filter/FlateDecode" );
2921 0 : aTilingObj.append( "/Length " );
2922 0 : aTilingObj.append( (sal_Int32)nTilingStreamSize );
2923 0 : aTilingObj.append( ">>\nstream\n" );
2924 0 : if ( !updateObject( it->m_nObject ) ) return false;
2925 0 : if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false;
2926 0 : checkAndEnableStreamEncryption( it->m_nObject );
2927 0 : bool written = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize );
2928 0 : delete it->m_pTilingStream;
2929 0 : it->m_pTilingStream = NULL;
2930 0 : if( !written )
2931 0 : return false;
2932 0 : disableStreamEncryption();
2933 0 : aTilingObj.setLength( 0 );
2934 0 : aTilingObj.append( "\nendstream\nendobj\n\n" );
2935 0 : if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false;
2936 : }
2937 0 : return true;
2938 : }
2939 :
2940 0 : sal_Int32 PDFWriterImpl::emitBuiltinFont( const PhysicalFontFace* pFont, sal_Int32 nFontObject )
2941 : {
2942 0 : const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
2943 0 : if( !pFD )
2944 0 : return 0;
2945 0 : const BuiltinFont& rBuiltinFont = pFD->GetBuiltinFont();
2946 :
2947 0 : OStringBuffer aLine( 1024 );
2948 :
2949 0 : if( nFontObject <= 0 )
2950 0 : nFontObject = createObject();
2951 0 : CHECK_RETURN( updateObject( nFontObject ) );
2952 0 : aLine.append( nFontObject );
2953 : aLine.append( " 0 obj\n"
2954 0 : "<</Type/Font/Subtype/Type1/BaseFont/" );
2955 0 : appendName( rBuiltinFont.m_pPSName, aLine );
2956 0 : aLine.append( "\n" );
2957 0 : if( rBuiltinFont.m_eCharSet == RTL_TEXTENCODING_MS_1252 )
2958 0 : aLine.append( "/Encoding/WinAnsiEncoding\n" );
2959 0 : aLine.append( ">>\nendobj\n\n" );
2960 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2961 0 : return nFontObject;
2962 : }
2963 :
2964 0 : std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const PhysicalFontFace* pFont, EmbedFont& rEmbed )
2965 : {
2966 0 : std::map< sal_Int32, sal_Int32 > aRet;
2967 :
2968 0 : sal_Int32 nFontDescriptor = 0;
2969 0 : OString aSubType( "/Type1" );
2970 0 : FontSubsetInfo aInfo;
2971 : // fill in dummy values
2972 0 : aInfo.m_nAscent = 1000;
2973 0 : aInfo.m_nDescent = 200;
2974 0 : aInfo.m_nCapHeight = 1000;
2975 0 : aInfo.m_aFontBBox = Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) );
2976 0 : aInfo.m_aPSName = pFont->GetFamilyName();
2977 : sal_Int32 pWidths[256];
2978 0 : memset( pWidths, 0, sizeof(pWidths) );
2979 :
2980 0 : SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics();
2981 :
2982 : assert(pGraphics);
2983 :
2984 0 : if( pFont->IsEmbeddable() )
2985 : {
2986 0 : const unsigned char* pFontData = NULL;
2987 0 : long nFontLen = 0;
2988 : sal_Ucs nEncodedCodes[256];
2989 : sal_Int32 pEncWidths[256];
2990 :
2991 : //TODO: surely this is utterly broken because GetEmbedFontData loops over the uninitialized nEncodedCodes as input
2992 0 : pFontData = static_cast<const unsigned char*>(pGraphics->GetEmbedFontData( pFont, nEncodedCodes, pEncWidths, 256, aInfo, &nFontLen ));
2993 :
2994 0 : if( pFontData )
2995 : {
2996 0 : pGraphics->FreeEmbedFontData( pFontData, nFontLen );
2997 0 : for( int i = 0; i < 256; i++ )
2998 : {
2999 0 : if( nEncodedCodes[i] >= 32 && nEncodedCodes[i] < 256 )
3000 : {
3001 0 : pWidths[i] = pEncWidths[ i ];
3002 : }
3003 : }
3004 : }
3005 : }
3006 0 : else if( pFont->mbSubsettable )
3007 : {
3008 0 : aSubType = OString( "/TrueType" );
3009 0 : Int32Vector aGlyphWidths;
3010 0 : Ucs2UIntMap aUnicodeMap;
3011 0 : pGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap );
3012 :
3013 0 : OUString aTmpName;
3014 0 : osl_createTempFile( NULL, NULL, &aTmpName.pData );
3015 : sal_GlyphId aGlyphIds[ 256 ];
3016 : sal_uInt8 pEncoding[ 256 ];
3017 : sal_Int32 pDuWidths[ 256 ];
3018 :
3019 0 : memset( aGlyphIds, 0, sizeof( aGlyphIds ) );
3020 0 : memset( pEncoding, 0, sizeof( pEncoding ) );
3021 0 : memset( pDuWidths, 0, sizeof( pDuWidths ) );
3022 :
3023 0 : for( sal_Ucs c = 32; c < 256; c++ )
3024 : {
3025 0 : pEncoding[c] = c;
3026 0 : aGlyphIds[c] = 0;
3027 0 : if( aUnicodeMap.find( c ) != aUnicodeMap.end() )
3028 0 : pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ];
3029 : }
3030 : //TODO: surely this is utterly broken because aGlyphIds is just all zeros, if we
3031 : //had the right glyphids here then I imagine we could replace pDuWidths with
3032 : //pWidths and remove pWidths assignment above. i.e. start with the glyph ids
3033 : //and map those to unicode rather than try and reverse map them ?
3034 0 : pGraphics->CreateFontSubset( aTmpName, pFont, aGlyphIds, pEncoding, pDuWidths, 256, aInfo );
3035 0 : osl_removeFile( aTmpName.pData );
3036 : }
3037 : else
3038 : {
3039 : OSL_FAIL( "system font neither embeddable nor subsettable" );
3040 : }
3041 :
3042 : // write font descriptor
3043 0 : nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 );
3044 0 : if( nFontDescriptor )
3045 : {
3046 : // write font object
3047 0 : sal_Int32 nObject = createObject();
3048 0 : if( updateObject( nObject ) )
3049 : {
3050 0 : OStringBuffer aLine( 1024 );
3051 0 : aLine.append( nObject );
3052 : aLine.append( " 0 obj\n"
3053 0 : "<</Type/Font/Subtype" );
3054 0 : aLine.append( aSubType );
3055 0 : aLine.append( "/BaseFont/" );
3056 0 : appendName( aInfo.m_aPSName, aLine );
3057 0 : aLine.append( "\n" );
3058 0 : if( !pFont->IsSymbolFont() )
3059 0 : aLine.append( "/Encoding/WinAnsiEncoding\n" );
3060 : aLine.append( "/FirstChar 32 /LastChar 255\n"
3061 0 : "/Widths[" );
3062 0 : for( int i = 32; i < 256; i++ )
3063 : {
3064 0 : aLine.append( pWidths[i] );
3065 0 : aLine.append( ((i&15) == 15) ? "\n" : " " );
3066 : }
3067 : aLine.append( "]\n"
3068 0 : "/FontDescriptor " );
3069 0 : aLine.append( nFontDescriptor );
3070 : aLine.append( " 0 R>>\n"
3071 0 : "endobj\n\n" );
3072 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
3073 :
3074 0 : aRet[ rEmbed.m_nNormalFontID ] = nObject;
3075 : }
3076 : }
3077 :
3078 0 : return aRet;
3079 : }
3080 :
3081 : typedef int ThreeInts[3];
3082 0 : static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen,
3083 : ThreeInts& rSegmentLengths )
3084 : {
3085 0 : if( !pFontBytes || (nByteLen < 0) )
3086 0 : return false;
3087 0 : const unsigned char* pPtr = pFontBytes;
3088 0 : const unsigned char* pEnd = pFontBytes + nByteLen;
3089 :
3090 0 : for( int i = 0; i < 3; ++i) {
3091 : // read segment1 header
3092 0 : if( pPtr+6 >= pEnd )
3093 0 : return false;
3094 0 : if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) )
3095 0 : return false;
3096 0 : const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2];
3097 0 : if( nLen <= 0)
3098 0 : return false;
3099 0 : rSegmentLengths[i] = nLen;
3100 0 : pPtr += nLen + 6;
3101 : }
3102 :
3103 : // read segment-end header
3104 0 : if( pPtr+2 >= pEnd )
3105 0 : return false;
3106 0 : if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) )
3107 0 : return false;
3108 :
3109 0 : return true;
3110 : }
3111 :
3112 0 : struct FontException : public std::exception
3113 : {
3114 : };
3115 :
3116 : // TODO: always subset instead of embedding the full font => this method becomes obsolete then
3117 0 : std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const PhysicalFontFace* pFont, EmbedFont& rEmbed )
3118 : {
3119 0 : std::map< sal_Int32, sal_Int32 > aRet;
3120 :
3121 0 : sal_Int32 nStreamObject = 0;
3122 0 : sal_Int32 nFontDescriptor = 0;
3123 :
3124 0 : SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics();
3125 :
3126 : assert(pGraphics);
3127 :
3128 : // prepare font encoding
3129 0 : std::set<sal_Unicode> const * pPriority(0);
3130 : const Ucs2SIntMap *const pEncoding =
3131 0 : pGraphics->GetFontEncodingVector( pFont, nullptr, &pPriority );
3132 0 : sal_Int32 nToUnicodeStream = 0;
3133 : sal_uInt8 nEncoding[256];
3134 : sal_Ucs nEncodedCodes[256];
3135 0 : std::vector<sal_Ucs> aUnicodes;
3136 0 : aUnicodes.reserve( 256 );
3137 : sal_Int32 pUnicodesPerGlyph[256];
3138 : sal_Int32 pEncToUnicodeIndex[256];
3139 0 : if( pEncoding )
3140 : {
3141 0 : memset( nEncoding, 0, sizeof(nEncoding) );
3142 0 : memset( nEncodedCodes, 0, sizeof(nEncodedCodes) );
3143 0 : memset( pUnicodesPerGlyph, 0, sizeof(pUnicodesPerGlyph) );
3144 0 : memset( pEncToUnicodeIndex, 0, sizeof(pEncToUnicodeIndex) );
3145 0 : for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it )
3146 : {
3147 0 : if(it->second == -1)
3148 0 : continue;
3149 0 : sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff);
3150 : SAL_WARN_IF(nCode != it->second, "vcl.gdi", "emitEmbeddedFont: FIXME: cannot handle Type 1 font with code points > 256");
3151 : //We're not doing this right here. We have taken a unicode-to-font_index map
3152 : //and are trying to generate a font_index-to-unicode mapping from it
3153 : //Which assumes that there is a 1-to-1 mapping there, but that might not be
3154 : //true.
3155 : //
3156 : //Instead perhaps we could try and get the GetFontCharMap and loop
3157 : //over sal_UCS4 GetCharFromIndex( int nCharIndex ) const from 0 to 255
3158 : //to build it up
3159 0 : if (nEncoding[nCode] != 0)
3160 : {
3161 : // should not have 2 identical mappings
3162 : assert(nEncodedCodes[nCode] != it->first);
3163 0 : if (pPriority)
3164 : {
3165 0 : bool bExist = pPriority->find(nEncodedCodes[nCode]) != pPriority->end();
3166 0 : bool bIter = pPriority->find(it->first) != pPriority->end();
3167 : SAL_WARN_IF(bExist && bIter, "vcl.gdi", "both are preferred? odd...");
3168 0 : if (bExist)
3169 : {
3170 0 : continue;
3171 : }
3172 : // note: aUnicodes will contain the old one but that
3173 : // does not matter because there's nothing iterating it
3174 : }
3175 : else
3176 : {
3177 : // is this fallback important? let's prefer lower one.
3178 : // actually the map is sorted so just rely on that
3179 : assert(nEncodedCodes[nCode] < it->first);
3180 : SAL_WARN("vcl.gdi", "emitEmbeddedFont: ignoring code " << nCode << " mapping to " << it->first << " in favor of " << nEncodedCodes[nCode]);
3181 0 : continue;
3182 : }
3183 : }
3184 0 : nEncodedCodes[ nCode ] = it->first;
3185 0 : nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode );
3186 0 : pEncToUnicodeIndex[ nCode ] = static_cast<sal_Int32>(aUnicodes.size());
3187 0 : aUnicodes.push_back( it->first );
3188 0 : pUnicodesPerGlyph[ nCode ] = 1;
3189 : }
3190 : }
3191 :
3192 0 : FontSubsetInfo aInfo;
3193 : sal_Int32 pWidths[256];
3194 0 : const unsigned char* pFontData = NULL;
3195 0 : long nFontLen = 0;
3196 : sal_Int32 nLength1, nLength2;
3197 : try
3198 : {
3199 0 : if( (pFontData = static_cast<const unsigned char*>(pGraphics->GetEmbedFontData(pFont, nEncodedCodes, pWidths, 256, aInfo, &nFontLen))) != NULL )
3200 : {
3201 0 : if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 )
3202 0 : throw FontException();
3203 : // see whether it is pfb or pfa; if it is a pfb, fill ranges
3204 : // of 6 bytes that are not part of the font program
3205 0 : std::list< int > aSections;
3206 0 : std::list< int >::const_iterator it;
3207 0 : int nIndex = 0;
3208 0 : while( (nIndex < nFontLen-1) && pFontData[nIndex] == 0x80 )
3209 : {
3210 0 : aSections.push_back( nIndex );
3211 0 : if( pFontData[nIndex+1] == 0x03 )
3212 0 : break;
3213 : sal_Int32 nBytes =
3214 0 : ((sal_Int32)pFontData[nIndex+2]) |
3215 0 : ((sal_Int32)pFontData[nIndex+3]) << 8 |
3216 0 : ((sal_Int32)pFontData[nIndex+4]) << 16 |
3217 0 : ((sal_Int32)pFontData[nIndex+5]) << 24;
3218 0 : nIndex += nBytes+6;
3219 : }
3220 :
3221 : // search for eexec
3222 : // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below
3223 0 : nIndex = 0;
3224 : int nEndAsciiIndex;
3225 : int nBeginBinaryIndex;
3226 : int nEndBinaryIndex;
3227 0 : do
3228 : {
3229 0 : while( nIndex < nFontLen-4 &&
3230 0 : ( pFontData[nIndex] != 'e' ||
3231 0 : pFontData[nIndex+1] != 'e' ||
3232 0 : pFontData[nIndex+2] != 'x' ||
3233 0 : pFontData[nIndex+3] != 'e' ||
3234 0 : pFontData[nIndex+4] != 'c'
3235 : )
3236 : )
3237 : {
3238 0 : ++nIndex;
3239 : }
3240 : // check whether we are in a excluded section
3241 0 : for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3242 : ;
3243 0 : } while( it != aSections.end() && nIndex < nFontLen-4 );
3244 : // this should end the ascii part
3245 0 : if( nIndex > nFontLen-5 )
3246 0 : throw FontException();
3247 :
3248 0 : nEndAsciiIndex = nIndex+4;
3249 : // now count backwards until we can account for 512 '0'
3250 : // which is the endmarker of the (hopefully) binary data
3251 : // do not count the pfb header sections
3252 0 : int nFound = 0;
3253 0 : nIndex = nFontLen-1;
3254 0 : while( nIndex > 0 && nFound < 512 )
3255 : {
3256 0 : for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3257 : ;
3258 0 : if( it == aSections.end() )
3259 : {
3260 : // inside the 512 '0' block there may only be whitespace
3261 : // according to T1 spec; probably it would be to simple
3262 : // if all fonts complied
3263 0 : if( pFontData[nIndex] == '0' )
3264 0 : nFound++;
3265 0 : else if( nFound > 0 &&
3266 0 : pFontData[nIndex] != '\r' &&
3267 0 : pFontData[nIndex] != '\t' &&
3268 0 : pFontData[nIndex] != '\n' &&
3269 0 : pFontData[nIndex] != ' ' )
3270 0 : break;
3271 : }
3272 0 : nIndex--;
3273 : }
3274 :
3275 0 : if( nIndex < 1 || nIndex <= nEndAsciiIndex )
3276 0 : throw FontException();
3277 :
3278 : // nLength3 is the rest of the file - excluding any section headers
3279 : // nIndex now points before the first of the 512 '0' characters marking the
3280 : // fixed content portion
3281 0 : sal_Int32 nLength3 = nFontLen - nIndex - 1;
3282 0 : for( it = aSections.begin(); it != aSections.end(); ++it )
3283 : {
3284 : // special case: nIndex inside a section marker
3285 0 : if( nIndex >= (*it) && (*it)+6 > nIndex )
3286 0 : nLength3 -= (*it)+6 - nIndex;
3287 0 : else if( *it >= nIndex )
3288 : {
3289 0 : if( *it < nFontLen - 6 )
3290 0 : nLength3 -= 6;
3291 : else // the last section 0x8003 is only 2 bytes after all
3292 0 : nLength3 -= (nFontLen - *it);
3293 : }
3294 : }
3295 :
3296 : // there may be whitespace to ignore before the 512 '0'
3297 0 : while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' )
3298 : {
3299 0 : nIndex--;
3300 0 : for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3301 : ;
3302 0 : if( it != aSections.end() )
3303 : {
3304 0 : nIndex = (*it)-1;
3305 0 : break; // this is surely a binary boundary, in ascii case it wouldn't matter
3306 : }
3307 : }
3308 0 : nEndBinaryIndex = nIndex;
3309 :
3310 : // search for beginning of binary section
3311 0 : nBeginBinaryIndex = nEndAsciiIndex;
3312 0 : do
3313 : {
3314 0 : nBeginBinaryIndex++;
3315 0 : for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it )
3316 : ;
3317 0 : } while( nBeginBinaryIndex < nEndBinaryIndex &&
3318 0 : ( pFontData[nBeginBinaryIndex] == '\r' ||
3319 0 : pFontData[nBeginBinaryIndex] == '\n' ||
3320 0 : it != aSections.end() ) );
3321 :
3322 : // it seems to be vital to copy the exact whitespace between binary data
3323 : // and eexec, else a invalid font results. so make nEndAsciiIndex
3324 : // always immediate in front of nBeginBinaryIndex
3325 0 : nEndAsciiIndex = nBeginBinaryIndex-1;
3326 0 : for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it )
3327 : ;
3328 0 : if( it != aSections.end() )
3329 0 : nEndAsciiIndex = (*it)-1;
3330 :
3331 0 : nLength1 = nEndAsciiIndex+1; // including the last character
3332 0 : for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it )
3333 0 : nLength1 -= 6; // decrease by pfb section size
3334 :
3335 : // if the first four bytes are all ascii hex characters, then binary data
3336 : // has to be converted to real binary data
3337 0 : for( nIndex = 0; nIndex < 4 &&
3338 0 : ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) ||
3339 0 : ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) ||
3340 0 : ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' )
3341 : ); ++nIndex )
3342 : ;
3343 0 : bool bConvertHexData = true;
3344 0 : if( nIndex < 4 )
3345 : {
3346 0 : bConvertHexData = false;
3347 0 : nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte
3348 0 : for( it = aSections.begin(); it != aSections.end(); ++it )
3349 0 : if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex )
3350 0 : nLength2 -= 6;
3351 : }
3352 : else
3353 : {
3354 : // count the hex ascii characters to get nLength2
3355 0 : nLength2 = 0;
3356 0 : int nNextSectionIndex = 0;
3357 0 : for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3358 : ;
3359 0 : if( it != aSections.end() )
3360 0 : nNextSectionIndex = *it;
3361 0 : for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3362 : {
3363 0 : if( nIndex == nNextSectionIndex )
3364 : {
3365 0 : nIndex += 6;
3366 0 : ++it;
3367 0 : nNextSectionIndex = (it == aSections.end() ? 0 : *it );
3368 : }
3369 0 : if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) ||
3370 0 : ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) ||
3371 0 : ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) )
3372 0 : nLength2++;
3373 : }
3374 : DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" );
3375 0 : nLength2 /= 2;
3376 : }
3377 :
3378 : // now we can actually write the font stream !
3379 : #if OSL_DEBUG_LEVEL > 1
3380 : emitComment( " PDFWriterImpl::emitEmbeddedFont" );
3381 : #endif
3382 0 : OStringBuffer aLine( 512 );
3383 0 : nStreamObject = createObject();
3384 0 : if( !updateObject(nStreamObject))
3385 0 : throw FontException();
3386 0 : sal_Int32 nStreamLengthObject = createObject();
3387 0 : aLine.append( nStreamObject );
3388 : aLine.append( " 0 obj\n"
3389 0 : "<</Length " );
3390 0 : aLine.append( nStreamLengthObject );
3391 : aLine.append( " 0 R"
3392 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3393 : "/Filter/FlateDecode"
3394 : #endif
3395 0 : "/Length1 " );
3396 0 : aLine.append( nLength1 );
3397 0 : aLine.append( " /Length2 " );
3398 0 : aLine.append( nLength2 );
3399 0 : aLine.append( " /Length3 ");
3400 0 : aLine.append( nLength3 );
3401 : aLine.append( ">>\n"
3402 0 : "stream\n" );
3403 0 : if( !writeBuffer( aLine.getStr(), aLine.getLength() ) )
3404 0 : throw FontException();
3405 :
3406 0 : sal_uInt64 nBeginStreamPos = 0;
3407 0 : m_aFile.getPos(nBeginStreamPos);
3408 :
3409 0 : beginCompression();
3410 0 : checkAndEnableStreamEncryption( nStreamObject );
3411 :
3412 : // write ascii section
3413 0 : if( aSections.begin() == aSections.end() )
3414 : {
3415 0 : if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) )
3416 0 : throw FontException();
3417 : }
3418 : else
3419 : {
3420 : // first section always starts at 0
3421 0 : it = aSections.begin();
3422 0 : nIndex = (*it)+6;
3423 0 : ++it;
3424 0 : while( *it < nEndAsciiIndex )
3425 : {
3426 0 : if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) )
3427 0 : throw FontException();
3428 0 : nIndex = (*it)+6;
3429 0 : ++it;
3430 : }
3431 : // write partial last section
3432 0 : if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) )
3433 0 : throw FontException();
3434 : }
3435 :
3436 : // write binary section
3437 0 : if( ! bConvertHexData )
3438 : {
3439 0 : if( aSections.begin() == aSections.end() )
3440 : {
3441 0 : if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) )
3442 0 : throw FontException();
3443 : }
3444 : else
3445 : {
3446 0 : for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it )
3447 : ;
3448 : // write first partial section
3449 0 : if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) )
3450 0 : throw FontException();
3451 : // write following sections
3452 0 : while( it != aSections.end() )
3453 : {
3454 0 : nIndex = (*it)+6;
3455 0 : ++it;
3456 0 : if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3457 : {
3458 0 : sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3459 0 : if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3460 0 : throw FontException();
3461 : }
3462 : }
3463 : }
3464 : }
3465 : else
3466 : {
3467 0 : boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] );
3468 0 : memset( pWriteBuffer.get(), 0, nLength2 );
3469 0 : int nWriteIndex = 0;
3470 :
3471 0 : int nNextSectionIndex = 0;
3472 0 : for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3473 : ;
3474 0 : if( it != aSections.end() )
3475 0 : nNextSectionIndex = *it;
3476 0 : for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3477 : {
3478 0 : if( nIndex == nNextSectionIndex )
3479 : {
3480 0 : nIndex += 6;
3481 0 : ++it;
3482 0 : nNextSectionIndex = (it == aSections.end() ? nFontLen : *it );
3483 : }
3484 0 : unsigned char cNibble = 0x80;
3485 0 : if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' )
3486 0 : cNibble = pFontData[nIndex] - '0';
3487 0 : else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' )
3488 0 : cNibble = pFontData[nIndex] - 'a' + 10;
3489 0 : else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' )
3490 0 : cNibble = pFontData[nIndex] - 'A' + 10;
3491 0 : if( cNibble != 0x80 )
3492 : {
3493 0 : if( !(nWriteIndex & 1 ) )
3494 0 : cNibble <<= 4;
3495 0 : pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble;
3496 0 : nWriteIndex++;
3497 : }
3498 : }
3499 0 : if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) )
3500 0 : throw FontException();
3501 0 : if( aSections.empty() )
3502 : {
3503 0 : if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) )
3504 0 : throw FontException();
3505 : }
3506 : else
3507 : {
3508 : // write rest of this section
3509 0 : if( nIndex < nNextSectionIndex )
3510 : {
3511 0 : if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) )
3512 0 : throw FontException();
3513 : }
3514 : // write following sections
3515 0 : while( it != aSections.end() )
3516 : {
3517 0 : nIndex = (*it)+6;
3518 0 : ++it;
3519 0 : if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3520 : {
3521 0 : sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3522 0 : if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3523 0 : throw FontException();
3524 : }
3525 : }
3526 0 : }
3527 : }
3528 0 : endCompression();
3529 0 : disableStreamEncryption();
3530 :
3531 0 : sal_uInt64 nEndStreamPos = 0;
3532 0 : m_aFile.getPos(nEndStreamPos);
3533 :
3534 : // and finally close the stream
3535 0 : aLine.setLength( 0 );
3536 0 : aLine.append( "\nendstream\nendobj\n\n" );
3537 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3538 0 : throw FontException();
3539 :
3540 : // write stream length object
3541 0 : aLine.setLength( 0 );
3542 0 : if( ! updateObject( nStreamLengthObject ) )
3543 0 : throw FontException();
3544 0 : aLine.append( nStreamLengthObject );
3545 0 : aLine.append( " 0 obj\n" );
3546 0 : aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) );
3547 0 : aLine.append( "\nendobj\n\n" );
3548 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3549 0 : throw FontException();
3550 : }
3551 : else
3552 : {
3553 0 : OStringBuffer aErrorComment( 256 );
3554 0 : aErrorComment.append( "GetEmbedFontData failed for font \"" );
3555 0 : aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3556 0 : aErrorComment.append( '\"' );
3557 0 : if( pFont->GetSlant() == ITALIC_NORMAL )
3558 0 : aErrorComment.append( " italic" );
3559 0 : else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3560 0 : aErrorComment.append( " oblique" );
3561 0 : aErrorComment.append( " weight=" );
3562 0 : aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3563 0 : emitComment( aErrorComment.getStr() );
3564 : }
3565 :
3566 0 : if( nStreamObject )
3567 : {
3568 : // write font descriptor
3569 0 : nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject );
3570 : }
3571 :
3572 0 : if( nFontDescriptor )
3573 : {
3574 0 : if( pEncoding )
3575 0 : nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, SAL_N_ELEMENTS(nEncoding) );
3576 :
3577 : // write font object
3578 0 : sal_Int32 nObject = createObject();
3579 0 : if( ! updateObject( nObject ) )
3580 0 : throw FontException();
3581 :
3582 0 : OStringBuffer aLine( 1024 );
3583 0 : aLine.append( nObject );
3584 : aLine.append( " 0 obj\n"
3585 0 : "<</Type/Font/Subtype/Type1/BaseFont/" );
3586 0 : appendName( aInfo.m_aPSName, aLine );
3587 0 : aLine.append( "\n" );
3588 0 : if( !pFont->IsSymbolFont() && pEncoding == 0 )
3589 0 : aLine.append( "/Encoding/WinAnsiEncoding\n" );
3590 0 : if( nToUnicodeStream )
3591 : {
3592 0 : aLine.append( "/ToUnicode " );
3593 0 : aLine.append( nToUnicodeStream );
3594 0 : aLine.append( " 0 R\n" );
3595 : }
3596 : aLine.append( "/FirstChar 0 /LastChar 255\n"
3597 0 : "/Widths[" );
3598 0 : for( int i = 0; i < 256; i++ )
3599 : {
3600 0 : aLine.append( pWidths[i] );
3601 0 : aLine.append( ((i&15) == 15) ? "\n" : " " );
3602 : }
3603 : aLine.append( "]\n"
3604 0 : "/FontDescriptor " );
3605 0 : aLine.append( nFontDescriptor );
3606 : aLine.append( " 0 R>>\n"
3607 0 : "endobj\n\n" );
3608 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3609 0 : throw FontException();
3610 :
3611 0 : aRet[ rEmbed.m_nNormalFontID ] = nObject;
3612 :
3613 : // write additional encodings
3614 0 : for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it )
3615 : {
3616 : sal_Int32 aEncWidths[ 256 ];
3617 : // emit encoding dict
3618 0 : sal_Int32 nEncObject = createObject();
3619 0 : if( ! updateObject( nEncObject ) )
3620 0 : throw FontException();
3621 :
3622 0 : OutputDevice* pRef = getReferenceDevice();
3623 0 : pRef->Push( PushFlags::FONT | PushFlags::MAPMODE );
3624 0 : pRef->SetMapMode( MapMode( MAP_PIXEL ) );
3625 0 : Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) );
3626 0 : aFont.SetWeight( pFont->GetWeight() );
3627 0 : aFont.SetItalic( pFont->GetSlant() );
3628 0 : aFont.SetPitch( pFont->GetPitch() );
3629 0 : pRef->SetFont( aFont );
3630 0 : pRef->ImplNewFont();
3631 :
3632 0 : aLine.setLength( 0 );
3633 0 : aLine.append( nEncObject );
3634 : aLine.append( " 0 obj\n"
3635 0 : "<</Type/Encoding/Differences[ 0\n" );
3636 0 : int nEncoded = 0;
3637 0 : aUnicodes.clear();
3638 0 : for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it )
3639 : {
3640 0 : OUString aStr( str_it->m_aUnicode );
3641 0 : aEncWidths[nEncoded] = pRef->GetTextWidth( aStr );
3642 0 : nEncodedCodes[nEncoded] = str_it->m_aUnicode;
3643 0 : nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded);
3644 0 : pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size());
3645 0 : aUnicodes.push_back( nEncodedCodes[nEncoded] );
3646 0 : pUnicodesPerGlyph[nEncoded] = 1;
3647 :
3648 0 : aLine.append( " /" );
3649 0 : aLine.append( str_it->m_aName );
3650 0 : if( !((++nEncoded) & 15) )
3651 0 : aLine.append( "\n" );
3652 0 : }
3653 : aLine.append( "]>>\n"
3654 0 : "endobj\n\n" );
3655 :
3656 0 : pRef->Pop();
3657 :
3658 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3659 0 : throw FontException();
3660 :
3661 0 : nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded );
3662 :
3663 0 : nObject = createObject();
3664 0 : if( ! updateObject( nObject ) )
3665 0 : throw FontException();
3666 :
3667 0 : aLine.setLength( 0 );
3668 0 : aLine.append( nObject );
3669 : aLine.append( " 0 obj\n"
3670 0 : "<</Type/Font/Subtype/Type1/BaseFont/" );
3671 0 : appendName( aInfo.m_aPSName, aLine );
3672 0 : aLine.append( "\n" );
3673 0 : aLine.append( "/Encoding " );
3674 0 : aLine.append( nEncObject );
3675 0 : aLine.append( " 0 R\n" );
3676 0 : if( nToUnicodeStream )
3677 : {
3678 0 : aLine.append( "/ToUnicode " );
3679 0 : aLine.append( nToUnicodeStream );
3680 0 : aLine.append( " 0 R\n" );
3681 : }
3682 : aLine.append( "/FirstChar 0\n"
3683 0 : "/LastChar " );
3684 0 : aLine.append( (sal_Int32)(nEncoded-1) );
3685 : aLine.append( "\n"
3686 0 : "/Widths[" );
3687 0 : for( int i = 0; i < nEncoded; i++ )
3688 : {
3689 0 : aLine.append( aEncWidths[i] );
3690 0 : aLine.append( ((i&15) == 15) ? "\n" : " " );
3691 : }
3692 : aLine.append( " ]\n"
3693 0 : "/FontDescriptor " );
3694 0 : aLine.append( nFontDescriptor );
3695 : aLine.append( " 0 R>>\n"
3696 0 : "endobj\n\n" );
3697 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3698 0 : throw FontException();
3699 :
3700 0 : aRet[ enc_it->m_nFontID ] = nObject;
3701 0 : }
3702 : }
3703 : }
3704 0 : catch( FontException& )
3705 : {
3706 : // these do nothing in case there was no compression or encryption ongoing
3707 0 : endCompression();
3708 0 : disableStreamEncryption();
3709 : }
3710 :
3711 0 : if( pFontData )
3712 0 : pGraphics->FreeEmbedFontData( pFontData, nFontLen );
3713 :
3714 0 : return aRet;
3715 : }
3716 :
3717 0 : static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
3718 : {
3719 0 : if( nSubsetID )
3720 : {
3721 0 : for( int i = 0; i < 6; i++ )
3722 : {
3723 0 : int nOffset = (nSubsetID % 26);
3724 0 : nSubsetID /= 26;
3725 0 : rBuffer.append( (sal_Char)('A'+nOffset) );
3726 : }
3727 0 : rBuffer.append( '+' );
3728 : }
3729 0 : appendName( rPSName, rBuffer );
3730 0 : }
3731 :
3732 0 : sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding,
3733 : sal_Ucs* pUnicodes,
3734 : sal_Int32* pUnicodesPerGlyph,
3735 : sal_Int32* pEncToUnicodeIndex,
3736 : int nGlyphs )
3737 : {
3738 0 : int nMapped = 0, n = 0;
3739 0 : for( n = 0; n < nGlyphs; n++ )
3740 0 : if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3741 0 : nMapped++;
3742 :
3743 0 : if( nMapped == 0 )
3744 0 : return 0;
3745 :
3746 0 : sal_Int32 nStream = createObject();
3747 0 : CHECK_RETURN( updateObject( nStream ) );
3748 :
3749 0 : OStringBuffer aContents( 1024 );
3750 : aContents.append(
3751 : "/CIDInit/ProcSet findresource begin\n"
3752 : "12 dict begin\n"
3753 : "begincmap\n"
3754 : "/CIDSystemInfo<<\n"
3755 : "/Registry (Adobe)\n"
3756 : "/Ordering (UCS)\n"
3757 : "/Supplement 0\n"
3758 : ">> def\n"
3759 : "/CMapName/Adobe-Identity-UCS def\n"
3760 : "/CMapType 2 def\n"
3761 : "1 begincodespacerange\n"
3762 : "<00> <FF>\n"
3763 : "endcodespacerange\n"
3764 0 : );
3765 0 : int nCount = 0;
3766 0 : for( n = 0; n < nGlyphs; n++ )
3767 : {
3768 0 : if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3769 : {
3770 0 : if( (nCount % 100) == 0 )
3771 : {
3772 0 : if( nCount )
3773 0 : aContents.append( "endbfchar\n" );
3774 0 : aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) );
3775 0 : aContents.append( " beginbfchar\n" );
3776 : }
3777 0 : aContents.append( '<' );
3778 0 : appendHex( (sal_Int8)pEncoding[n], aContents );
3779 0 : aContents.append( "> <" );
3780 : // TODO: handle unicodes>U+FFFF
3781 0 : sal_Int32 nIndex = pEncToUnicodeIndex[n];
3782 0 : for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ )
3783 : {
3784 0 : appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents );
3785 0 : appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents );
3786 : }
3787 0 : aContents.append( ">\n" );
3788 0 : nCount++;
3789 : }
3790 : }
3791 : aContents.append( "endbfchar\n"
3792 : "endcmap\n"
3793 : "CMapName currentdict /CMap defineresource pop\n"
3794 : "end\n"
3795 0 : "end\n" );
3796 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3797 0 : ZCodec pCodec( 0x4000, 0x4000 );
3798 0 : SvMemoryStream aStream;
3799 0 : pCodec.BeginCompression();
3800 0 : pCodec.Write( aStream, reinterpret_cast<const sal_uInt8*>(aContents.getStr()), aContents.getLength() );
3801 0 : pCodec.EndCompression();
3802 : #endif
3803 :
3804 : #if OSL_DEBUG_LEVEL > 1
3805 : emitComment( "PDFWriterImpl::createToUnicodeCMap" );
3806 : #endif
3807 0 : OStringBuffer aLine( 40 );
3808 :
3809 0 : aLine.append( nStream );
3810 0 : aLine.append( " 0 obj\n<</Length " );
3811 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3812 0 : sal_Int32 nLen = (sal_Int32)aStream.Tell();
3813 0 : aStream.Seek( 0 );
3814 0 : aLine.append( nLen );
3815 0 : aLine.append( "/Filter/FlateDecode" );
3816 : #else
3817 : aLine.append( aContents.getLength() );
3818 : #endif
3819 0 : aLine.append( ">>\nstream\n" );
3820 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3821 0 : checkAndEnableStreamEncryption( nStream );
3822 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3823 0 : CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
3824 : #else
3825 : CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
3826 : #endif
3827 0 : disableStreamEncryption();
3828 0 : aLine.setLength( 0 );
3829 : aLine.append( "\nendstream\n"
3830 0 : "endobj\n\n" );
3831 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3832 0 : return nStream;
3833 : }
3834 :
3835 0 : sal_Int32 PDFWriterImpl::emitFontDescriptor( const PhysicalFontFace* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
3836 : {
3837 0 : OStringBuffer aLine( 1024 );
3838 : // get font flags, see PDF reference 1.4 p. 358
3839 : // possibly characters outside Adobe standard encoding
3840 : // so set Symbolic flag
3841 0 : sal_Int32 nFontFlags = (1<<2);
3842 0 : if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE )
3843 0 : nFontFlags |= (1 << 6);
3844 0 : if( pFont->GetPitch() == PITCH_FIXED )
3845 0 : nFontFlags |= 1;
3846 0 : if( pFont->GetFamilyType() == FAMILY_SCRIPT )
3847 0 : nFontFlags |= (1 << 3);
3848 0 : else if( pFont->GetFamilyType() == FAMILY_ROMAN )
3849 0 : nFontFlags |= (1 << 1);
3850 :
3851 0 : sal_Int32 nFontDescriptor = createObject();
3852 0 : CHECK_RETURN( updateObject( nFontDescriptor ) );
3853 0 : aLine.setLength( 0 );
3854 0 : aLine.append( nFontDescriptor );
3855 : aLine.append( " 0 obj\n"
3856 0 : "<</Type/FontDescriptor/FontName/" );
3857 0 : appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
3858 : aLine.append( "\n"
3859 0 : "/Flags " );
3860 0 : aLine.append( nFontFlags );
3861 : aLine.append( "\n"
3862 0 : "/FontBBox[" );
3863 : // note: Top and Bottom are reversed in VCL and PDF rectangles
3864 0 : aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() );
3865 0 : aLine.append( ' ' );
3866 0 : aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() );
3867 0 : aLine.append( ' ' );
3868 0 : aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() );
3869 0 : aLine.append( ' ' );
3870 0 : aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) );
3871 0 : aLine.append( "]/ItalicAngle " );
3872 0 : if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL )
3873 0 : aLine.append( "-30" );
3874 : else
3875 0 : aLine.append( "0" );
3876 : aLine.append( "\n"
3877 0 : "/Ascent " );
3878 0 : aLine.append( (sal_Int32)rInfo.m_nAscent );
3879 : aLine.append( "\n"
3880 0 : "/Descent " );
3881 0 : aLine.append( (sal_Int32)-rInfo.m_nDescent );
3882 : aLine.append( "\n"
3883 0 : "/CapHeight " );
3884 0 : aLine.append( (sal_Int32)rInfo.m_nCapHeight );
3885 : // According to PDF reference 1.4 StemV is required
3886 : // seems a tad strange to me, but well ...
3887 : aLine.append( "\n"
3888 0 : "/StemV 80\n" );
3889 0 : if( nFontStream )
3890 : {
3891 0 : aLine.append( "/FontFile" );
3892 0 : switch( rInfo.m_nFontType )
3893 : {
3894 : case FontSubsetInfo::SFNT_TTF:
3895 0 : aLine.append( '2' );
3896 0 : break;
3897 : case FontSubsetInfo::TYPE1_PFA:
3898 : case FontSubsetInfo::TYPE1_PFB:
3899 : case FontSubsetInfo::ANY_TYPE1:
3900 0 : break;
3901 : default:
3902 : OSL_FAIL( "unknown fonttype in PDF font descriptor" );
3903 0 : return 0;
3904 : }
3905 0 : aLine.append( ' ' );
3906 0 : aLine.append( nFontStream );
3907 0 : aLine.append( " 0 R\n" );
3908 : }
3909 : aLine.append( ">>\n"
3910 0 : "endobj\n\n" );
3911 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3912 :
3913 0 : return nFontDescriptor;
3914 : }
3915 :
3916 0 : void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const
3917 : {
3918 0 : for( std::map< sal_Int32, sal_Int32 >::const_iterator it =
3919 0 : m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it )
3920 : {
3921 0 : rDict.append( m_aBuiltinFonts[it->first].getNameObject() );
3922 0 : rDict.append( ' ' );
3923 0 : rDict.append( it->second );
3924 0 : rDict.append( " 0 R" );
3925 : }
3926 0 : }
3927 :
3928 0 : bool PDFWriterImpl::emitFonts()
3929 : {
3930 0 : SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics();
3931 :
3932 0 : if (!pGraphics)
3933 0 : return false;
3934 :
3935 0 : OStringBuffer aLine( 1024 );
3936 :
3937 0 : std::map< sal_Int32, sal_Int32 > aFontIDToObject;
3938 :
3939 0 : OUString aTmpName;
3940 0 : osl_createTempFile( NULL, NULL, &aTmpName.pData );
3941 0 : for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it )
3942 : {
3943 0 : for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit )
3944 : {
3945 : sal_GlyphId aGlyphIds[ 256 ];
3946 : sal_Int32 pWidths[ 256 ];
3947 : sal_uInt8 pEncoding[ 256 ];
3948 : sal_Int32 pEncToUnicodeIndex[ 256 ];
3949 : sal_Int32 pUnicodesPerGlyph[ 256 ];
3950 0 : std::vector<sal_Ucs> aUnicodes;
3951 0 : aUnicodes.reserve( 256 );
3952 0 : int nGlyphs = 1;
3953 : // fill arrays and prepare encoding index map
3954 0 : sal_Int32 nToUnicodeStream = 0;
3955 :
3956 0 : memset( aGlyphIds, 0, sizeof( aGlyphIds ) );
3957 0 : memset( pEncoding, 0, sizeof( pEncoding ) );
3958 0 : memset( pUnicodesPerGlyph, 0, sizeof( pUnicodesPerGlyph ) );
3959 0 : memset( pEncToUnicodeIndex, 0, sizeof( pEncToUnicodeIndex ) );
3960 0 : for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit )
3961 : {
3962 0 : sal_uInt8 nEnc = fit->second.getGlyphId();
3963 :
3964 : DBG_ASSERT( aGlyphIds[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" );
3965 : DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" );
3966 :
3967 0 : aGlyphIds[ nEnc ] = fit->first;
3968 0 : pEncoding[ nEnc ] = nEnc;
3969 0 : pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size());
3970 0 : pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes();
3971 0 : for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ )
3972 0 : aUnicodes.push_back( fit->second.getCode( n ) );
3973 0 : if( fit->second.getCode(0) )
3974 0 : nToUnicodeStream = 1;
3975 0 : if( nGlyphs < 256 )
3976 0 : nGlyphs++;
3977 : else
3978 : {
3979 : OSL_FAIL( "too many glyphs for subset" );
3980 : }
3981 : }
3982 0 : FontSubsetInfo aSubsetInfo;
3983 0 : if( pGraphics->CreateFontSubset( aTmpName, it->first, aGlyphIds, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
3984 : {
3985 : // create font stream
3986 0 : osl::File aFontFile(aTmpName);
3987 0 : if (osl::File::E_None != aFontFile.open(osl_File_OpenFlag_Read)) return false;
3988 : // get file size
3989 : sal_uInt64 nLength1;
3990 0 : if ( osl::File::E_None != aFontFile.setPos(osl_Pos_End, 0) ) return false;
3991 0 : if ( osl::File::E_None != aFontFile.getPos(nLength1) ) return false;
3992 0 : if ( osl::File::E_None != aFontFile.setPos(osl_Pos_Absolut, 0) ) return false;
3993 :
3994 : #if OSL_DEBUG_LEVEL > 1
3995 : emitComment( "PDFWriterImpl::emitFonts" );
3996 : #endif
3997 0 : sal_Int32 nFontStream = createObject();
3998 0 : sal_Int32 nStreamLengthObject = createObject();
3999 0 : if ( !updateObject( nFontStream ) ) return false;
4000 0 : aLine.setLength( 0 );
4001 0 : aLine.append( nFontStream );
4002 : aLine.append( " 0 obj\n"
4003 0 : "<</Length " );
4004 0 : aLine.append( (sal_Int32)nStreamLengthObject );
4005 : aLine.append( " 0 R"
4006 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
4007 : "/Filter/FlateDecode"
4008 : #endif
4009 0 : "/Length1 " );
4010 :
4011 0 : sal_uInt64 nStartPos = 0;
4012 0 : if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF )
4013 : {
4014 0 : aLine.append( (sal_Int32)nLength1 );
4015 :
4016 : aLine.append( ">>\n"
4017 0 : "stream\n" );
4018 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
4019 0 : if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false;
4020 :
4021 : // copy font file
4022 0 : beginCompression();
4023 0 : checkAndEnableStreamEncryption( nFontStream );
4024 0 : sal_Bool bEOF = sal_False;
4025 0 : do
4026 : {
4027 : char buf[8192];
4028 : sal_uInt64 nRead;
4029 0 : if ( osl::File::E_None != aFontFile.read(buf, sizeof(buf), nRead) ) return false;
4030 0 : if ( !writeBuffer( buf, nRead ) ) return false;
4031 0 : if ( osl::File::E_None != aFontFile.isEndOfFile(&bEOF) ) return false;
4032 0 : } while( ! bEOF );
4033 : }
4034 0 : else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 )
4035 : {
4036 : // TODO: implement
4037 : OSL_FAIL( "PDFWriterImpl does not support CFF-font subsets yet!" );
4038 : }
4039 0 : else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA?
4040 : {
4041 0 : boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] );
4042 :
4043 0 : sal_uInt64 nBytesRead = 0;
4044 0 : if ( osl::File::E_None != aFontFile.read(pBuffer.get(), nLength1, nBytesRead) ) return false;
4045 : DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" );
4046 0 : if ( osl::File::E_None != aFontFile.setPos(osl_Pos_Absolut, 0) ) return false;
4047 : // get the PFB-segment lengths
4048 0 : ThreeInts aSegmentLengths = {0,0,0};
4049 0 : getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths );
4050 : // the lengths below are mandatory for PDF-exported Type1 fonts
4051 : // because the PFB segment headers get stripped! WhyOhWhy.
4052 0 : aLine.append( (sal_Int32)aSegmentLengths[0] );
4053 0 : aLine.append( "/Length2 " );
4054 0 : aLine.append( (sal_Int32)aSegmentLengths[1] );
4055 0 : aLine.append( "/Length3 " );
4056 0 : aLine.append( (sal_Int32)aSegmentLengths[2] );
4057 :
4058 : aLine.append( ">>\n"
4059 0 : "stream\n" );
4060 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
4061 0 : if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false;
4062 :
4063 : // emit PFB-sections without section headers
4064 0 : beginCompression();
4065 0 : checkAndEnableStreamEncryption( nFontStream );
4066 0 : if ( !writeBuffer( &pBuffer[6], aSegmentLengths[0] ) ) return false;
4067 0 : if ( !writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) ) return false;
4068 0 : if ( !writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ) return false;
4069 : }
4070 : else
4071 : {
4072 0 : fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType);
4073 0 : aLine.append( "0 >>\nstream\n" );
4074 : }
4075 :
4076 0 : endCompression();
4077 0 : disableStreamEncryption();
4078 : // close the file
4079 0 : aFontFile.close();
4080 :
4081 0 : sal_uInt64 nEndPos = 0;
4082 0 : if ( osl::File::E_None != m_aFile.getPos(nEndPos) ) return false;
4083 : // end the stream
4084 0 : aLine.setLength( 0 );
4085 0 : aLine.append( "\nendstream\nendobj\n\n" );
4086 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
4087 :
4088 : // emit stream length object
4089 0 : if ( !updateObject( nStreamLengthObject ) ) return false;
4090 0 : aLine.setLength( 0 );
4091 0 : aLine.append( nStreamLengthObject );
4092 0 : aLine.append( " 0 obj\n" );
4093 0 : aLine.append( (sal_Int64)(nEndPos-nStartPos) );
4094 0 : aLine.append( "\nendobj\n\n" );
4095 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
4096 :
4097 : // write font descriptor
4098 0 : sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream );
4099 :
4100 0 : if( nToUnicodeStream )
4101 0 : nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs );
4102 :
4103 0 : sal_Int32 nFontObject = createObject();
4104 0 : if ( !updateObject( nFontObject ) ) return false;
4105 0 : aLine.setLength( 0 );
4106 0 : aLine.append( nFontObject );
4107 :
4108 0 : aLine.append( " 0 obj\n" );
4109 0 : aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ?
4110 : "<</Type/Font/Subtype/Type1/BaseFont/" :
4111 0 : "<</Type/Font/Subtype/TrueType/BaseFont/" );
4112 0 : appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine );
4113 : aLine.append( "\n"
4114 : "/FirstChar 0\n"
4115 0 : "/LastChar " );
4116 0 : aLine.append( (sal_Int32)(nGlyphs-1) );
4117 : aLine.append( "\n"
4118 0 : "/Widths[" );
4119 0 : for( int i = 0; i < nGlyphs; i++ )
4120 : {
4121 0 : aLine.append( pWidths[ i ] );
4122 0 : aLine.append( ((i & 15) == 15) ? "\n" : " " );
4123 : }
4124 : aLine.append( "]\n"
4125 0 : "/FontDescriptor " );
4126 0 : aLine.append( nFontDescriptor );
4127 0 : aLine.append( " 0 R\n" );
4128 0 : if( nToUnicodeStream )
4129 : {
4130 0 : aLine.append( "/ToUnicode " );
4131 0 : aLine.append( nToUnicodeStream );
4132 0 : aLine.append( " 0 R\n" );
4133 : }
4134 : aLine.append( ">>\n"
4135 0 : "endobj\n\n" );
4136 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
4137 :
4138 0 : aFontIDToObject[ lit->m_nFontID ] = nFontObject;
4139 : }
4140 : else
4141 : {
4142 0 : const PhysicalFontFace* pFont = it->first;
4143 0 : OStringBuffer aErrorComment( 256 );
4144 0 : aErrorComment.append( "CreateFontSubset failed for font \"" );
4145 0 : aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
4146 0 : aErrorComment.append( '\"' );
4147 0 : if( pFont->GetSlant() == ITALIC_NORMAL )
4148 0 : aErrorComment.append( " italic" );
4149 0 : else if( pFont->GetSlant() == ITALIC_OBLIQUE )
4150 0 : aErrorComment.append( " oblique" );
4151 0 : aErrorComment.append( " weight=" );
4152 0 : aErrorComment.append( sal_Int32(pFont->GetWeight()) );
4153 0 : emitComment( aErrorComment.getStr() );
4154 : }
4155 0 : }
4156 : }
4157 0 : osl_removeFile( aTmpName.pData );
4158 :
4159 : // emit embedded fonts
4160 0 : for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit )
4161 : {
4162 0 : std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second );
4163 0 : for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4164 : {
4165 0 : if ( !fit->second ) return false;
4166 0 : aFontIDToObject[ fit->first ] = fit->second;
4167 : }
4168 0 : }
4169 :
4170 : // emit system fonts
4171 0 : for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit )
4172 : {
4173 0 : std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second );
4174 0 : for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4175 : {
4176 0 : if ( !fit->second ) return false;
4177 0 : aFontIDToObject[ fit->first ] = fit->second;
4178 : }
4179 0 : }
4180 :
4181 0 : OStringBuffer aFontDict( 1024 );
4182 0 : aFontDict.append( getFontDictObject() );
4183 : aFontDict.append( " 0 obj\n"
4184 0 : "<<" );
4185 0 : int ni = 0;
4186 0 : for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit )
4187 : {
4188 0 : aFontDict.append( "/F" );
4189 0 : aFontDict.append( mit->first );
4190 0 : aFontDict.append( ' ' );
4191 0 : aFontDict.append( mit->second );
4192 0 : aFontDict.append( " 0 R" );
4193 0 : if( ((++ni) & 7) == 0 )
4194 0 : aFontDict.append( '\n' );
4195 : }
4196 : // emit builtin font for widget appearances / variable text
4197 0 : for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin();
4198 0 : it != m_aBuiltinFontToObjectMap.end(); ++it )
4199 : {
4200 0 : ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]);
4201 0 : it->second = emitBuiltinFont( &aData, it->second );
4202 0 : }
4203 0 : appendBuiltinFontsToDict( aFontDict );
4204 0 : aFontDict.append( "\n>>\nendobj\n\n" );
4205 :
4206 0 : if ( !updateObject( getFontDictObject() ) ) return false;
4207 0 : if ( !writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) ) return false;
4208 0 : return true;
4209 : }
4210 :
4211 0 : sal_Int32 PDFWriterImpl::emitResources()
4212 : {
4213 : // emit shadings
4214 0 : if( ! m_aGradients.empty() )
4215 0 : CHECK_RETURN( emitGradients() );
4216 : // emit tilings
4217 0 : if( ! m_aTilings.empty() )
4218 0 : CHECK_RETURN( emitTilings() );
4219 :
4220 : // emit font dict
4221 0 : CHECK_RETURN( emitFonts() );
4222 :
4223 : // emit Resource dict
4224 0 : OStringBuffer aLine( 512 );
4225 0 : sal_Int32 nResourceDict = getResourceDictObj();
4226 0 : CHECK_RETURN( updateObject( nResourceDict ) );
4227 0 : aLine.setLength( 0 );
4228 0 : aLine.append( nResourceDict );
4229 0 : aLine.append( " 0 obj\n" );
4230 0 : m_aGlobalResourceDict.append( aLine, getFontDictObject() );
4231 0 : aLine.append( "endobj\n\n" );
4232 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4233 0 : return nResourceDict;
4234 : }
4235 :
4236 0 : sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
4237 : sal_Int32 nItemLevel,
4238 : sal_Int32 nCurrentItemId )
4239 : {
4240 : /* The /Count number of an item is
4241 : positive: the number of visible subitems
4242 : negative: the negative number of subitems that will become visible if
4243 : the item gets opened
4244 : see PDF ref 1.4 p 478
4245 : */
4246 :
4247 0 : sal_Int32 nCount = 0;
4248 :
4249 0 : if( m_aContext.OpenBookmarkLevels < 0 || // all levels arevisible
4250 0 : m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible
4251 : )
4252 : {
4253 0 : PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4254 0 : sal_Int32 nChildren = rItem.m_aChildren.size();
4255 0 : for( sal_Int32 i = 0; i < nChildren; i++ )
4256 0 : nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4257 0 : rCounts[nCurrentItemId] = nCount;
4258 : // return 1 (this item) + visible sub items
4259 0 : if( nCount < 0 )
4260 0 : nCount = 0;
4261 0 : nCount++;
4262 : }
4263 : else
4264 : {
4265 : // this bookmark level is invisible
4266 0 : PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4267 0 : sal_Int32 nChildren = rItem.m_aChildren.size();
4268 0 : rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
4269 0 : for( sal_Int32 i = 0; i < nChildren; i++ )
4270 0 : updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4271 0 : nCount = -1;
4272 : }
4273 :
4274 0 : return nCount;
4275 : }
4276 :
4277 0 : sal_Int32 PDFWriterImpl::emitOutline()
4278 : {
4279 0 : int i, nItems = m_aOutline.size();
4280 :
4281 : // do we have an outline at all ?
4282 0 : if( nItems < 2 )
4283 0 : return 0;
4284 :
4285 : // reserve object numbers for all outline items
4286 0 : for( i = 0; i < nItems; ++i )
4287 0 : m_aOutline[i].m_nObject = createObject();
4288 :
4289 : // update all parent, next and prev object ids
4290 0 : for( i = 0; i < nItems; ++i )
4291 : {
4292 0 : PDFOutlineEntry& rItem = m_aOutline[i];
4293 0 : int nChildren = rItem.m_aChildren.size();
4294 :
4295 0 : if( nChildren )
4296 : {
4297 0 : for( int n = 0; n < nChildren; ++n )
4298 : {
4299 0 : PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
4300 :
4301 0 : rChild.m_nParentObject = rItem.m_nObject;
4302 0 : rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
4303 0 : rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
4304 : }
4305 :
4306 : }
4307 : }
4308 :
4309 : // calculate Count entries for all items
4310 0 : std::vector< sal_Int32 > aCounts( nItems );
4311 0 : updateOutlineItemCount( aCounts, 0, 0 );
4312 :
4313 : // emit hierarchy
4314 0 : for( i = 0; i < nItems; ++i )
4315 : {
4316 0 : PDFOutlineEntry& rItem = m_aOutline[i];
4317 0 : OStringBuffer aLine( 1024 );
4318 :
4319 0 : CHECK_RETURN( updateObject( rItem.m_nObject ) );
4320 0 : aLine.append( rItem.m_nObject );
4321 0 : aLine.append( " 0 obj\n" );
4322 0 : aLine.append( "<<" );
4323 : // number of visible children (all levels)
4324 0 : if( i > 0 || aCounts[0] > 0 )
4325 : {
4326 0 : aLine.append( "/Count " );
4327 0 : aLine.append( aCounts[i] );
4328 : }
4329 0 : if( ! rItem.m_aChildren.empty() )
4330 : {
4331 : // children list: First, Last
4332 0 : aLine.append( "/First " );
4333 0 : aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
4334 0 : aLine.append( " 0 R/Last " );
4335 0 : aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
4336 0 : aLine.append( " 0 R\n" );
4337 : }
4338 0 : if( i > 0 )
4339 : {
4340 : // Title, Dest, Parent, Prev, Next
4341 0 : aLine.append( "/Title" );
4342 0 : appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
4343 0 : aLine.append( "\n" );
4344 : // Dest is not required
4345 0 : if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() )
4346 : {
4347 0 : aLine.append( "/Dest" );
4348 0 : appendDest( rItem.m_nDestID, aLine );
4349 : }
4350 0 : aLine.append( "/Parent " );
4351 0 : aLine.append( rItem.m_nParentObject );
4352 0 : aLine.append( " 0 R" );
4353 0 : if( rItem.m_nPrevObject )
4354 : {
4355 0 : aLine.append( "/Prev " );
4356 0 : aLine.append( rItem.m_nPrevObject );
4357 0 : aLine.append( " 0 R" );
4358 : }
4359 0 : if( rItem.m_nNextObject )
4360 : {
4361 0 : aLine.append( "/Next " );
4362 0 : aLine.append( rItem.m_nNextObject );
4363 0 : aLine.append( " 0 R" );
4364 : }
4365 : }
4366 0 : aLine.append( ">>\nendobj\n\n" );
4367 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4368 0 : }
4369 :
4370 0 : return m_aOutline[0].m_nObject;
4371 : }
4372 :
4373 : #undef CHECK_RETURN
4374 : #define CHECK_RETURN( x ) if( !x ) return false
4375 :
4376 0 : bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
4377 : {
4378 0 : if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() )
4379 : {
4380 : #if OSL_DEBUG_LEVEL > 1
4381 : fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID );
4382 : #endif
4383 0 : return false;
4384 : }
4385 :
4386 0 : const PDFDest& rDest = m_aDests[ nDestID ];
4387 0 : const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
4388 :
4389 0 : rBuffer.append( '[' );
4390 0 : rBuffer.append( rDestPage.m_nPageObject );
4391 0 : rBuffer.append( " 0 R" );
4392 :
4393 0 : switch( rDest.m_eType )
4394 : {
4395 : case PDFWriter::XYZ:
4396 : default:
4397 0 : rBuffer.append( "/XYZ " );
4398 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4399 0 : rBuffer.append( ' ' );
4400 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4401 0 : rBuffer.append( " 0" );
4402 0 : break;
4403 : case PDFWriter::Fit:
4404 0 : rBuffer.append( "/Fit" );
4405 0 : break;
4406 : case PDFWriter::FitRectangle:
4407 0 : rBuffer.append( "/FitR " );
4408 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4409 0 : rBuffer.append( ' ' );
4410 0 : appendFixedInt( rDest.m_aRect.Top(), rBuffer );
4411 0 : rBuffer.append( ' ' );
4412 0 : appendFixedInt( rDest.m_aRect.Right(), rBuffer );
4413 0 : rBuffer.append( ' ' );
4414 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4415 0 : break;
4416 : case PDFWriter::FitHorizontal:
4417 0 : rBuffer.append( "/FitH " );
4418 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4419 0 : break;
4420 : case PDFWriter::FitVertical:
4421 0 : rBuffer.append( "/FitV " );
4422 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4423 0 : break;
4424 : case PDFWriter::FitPageBoundingBox:
4425 0 : rBuffer.append( "/FitB" );
4426 0 : break;
4427 : case PDFWriter::FitPageBoundingBoxHorizontal:
4428 0 : rBuffer.append( "/FitBH " );
4429 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4430 0 : break;
4431 : case PDFWriter::FitPageBoundingBoxVertical:
4432 0 : rBuffer.append( "/FitBV " );
4433 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4434 0 : break;
4435 : }
4436 0 : rBuffer.append( ']' );
4437 :
4438 0 : return true;
4439 : }
4440 :
4441 0 : bool PDFWriterImpl::emitLinkAnnotations()
4442 : {
4443 0 : int nAnnots = m_aLinks.size();
4444 0 : for( int i = 0; i < nAnnots; i++ )
4445 : {
4446 0 : const PDFLink& rLink = m_aLinks[i];
4447 0 : if( ! updateObject( rLink.m_nObject ) )
4448 0 : continue;
4449 :
4450 0 : OStringBuffer aLine( 1024 );
4451 0 : aLine.append( rLink.m_nObject );
4452 0 : aLine.append( " 0 obj\n" );
4453 : //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4454 : // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4455 0 : aLine.append( "<</Type/Annot" );
4456 0 : if( m_bIsPDF_A1 )
4457 0 : aLine.append( "/F 4" );
4458 0 : aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
4459 :
4460 0 : appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
4461 0 : aLine.append( ' ' );
4462 0 : appendFixedInt( rLink.m_aRect.Top(), aLine );
4463 0 : aLine.append( ' ' );
4464 0 : appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
4465 0 : aLine.append( ' ' );
4466 0 : appendFixedInt( rLink.m_aRect.Bottom(), aLine );
4467 0 : aLine.append( "]" );
4468 0 : if( rLink.m_nDest >= 0 )
4469 : {
4470 0 : aLine.append( "/Dest" );
4471 0 : appendDest( rLink.m_nDest, aLine );
4472 : }
4473 : else
4474 : {
4475 : /*--->i56629
4476 : destination is external to the document, so
4477 : we check in the following sequence:
4478 :
4479 : if target type is neither .pdf, nor .od[tpgs], then
4480 : check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
4481 : end processing
4482 : else if target is .od[tpgs]: then
4483 : if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file
4484 : processing continue
4485 :
4486 : if (new)target is .pdf : then
4487 : if GotToR is requested, then
4488 : convert the target in GoToR where the fragment of the URI is
4489 : considered the named destination in the target file, set relative or absolute as requested
4490 : else strip the fragment from URL and then set URI or 'launch application' as requested
4491 : */
4492 :
4493 : // FIXME: check if the decode mechanisms for URL processing throughout this implementation
4494 : // are the correct one!!
4495 :
4496 : // extract target file type
4497 0 : INetURLObject aDocumentURL( m_aContext.BaseURL );
4498 0 : INetURLObject aTargetURL( rLink.m_aURL );
4499 0 : sal_Int32 nSetGoToRMode = 0;
4500 0 : bool bTargetHasPDFExtension = false;
4501 0 : INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
4502 0 : bool bIsUNCPath = false;
4503 :
4504 : // check if the protocol is a known one, or if there is no protocol at all (on target only)
4505 : // if there is no protocol, make the target relative to the current document directory
4506 : // getting the needed URL information from the current document path
4507 0 : if( eTargetProtocol == INetProtocol::NotValid )
4508 : {
4509 0 : if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.startsWith("\\\\\\\\"))
4510 : {
4511 0 : bIsUNCPath = true;
4512 : }
4513 : else
4514 : {
4515 0 : INetURLObject aNewBase( aDocumentURL );//duplicate document URL
4516 0 : aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
4517 : //target document
4518 0 : aNewBase.insertName( rLink.m_aURL );
4519 0 : aTargetURL = aNewBase;//reassign the new target URL
4520 : //recompute the target protocol, with the new URL
4521 : //normal URL processing resumes
4522 0 : eTargetProtocol = aTargetURL.GetProtocol();
4523 : }
4524 : }
4525 :
4526 0 : OUString aFileExtension = aTargetURL.GetFileExtension();
4527 :
4528 : // Check if the URL ends in '/': if yes it's a directory,
4529 : // it will be forced to a URI link.
4530 : // possibly a malformed URI, leave it as it is, force as URI
4531 0 : if( aTargetURL.hasFinalSlash() )
4532 0 : m_aContext.DefaultLinkAction = PDFWriter::URIAction;
4533 :
4534 0 : if( !aFileExtension.isEmpty() )
4535 : {
4536 0 : if( m_aContext.ConvertOOoTargetToPDFTarget )
4537 : {
4538 0 : bool bChangeFileExtensionToPDF = false;
4539 : //examine the file type (.odm .odt. .odp, odg, ods)
4540 0 : if( aFileExtension.equalsIgnoreAsciiCase( "odm" ) )
4541 0 : bChangeFileExtensionToPDF = true;
4542 0 : if( aFileExtension.equalsIgnoreAsciiCase( "odt" ) )
4543 0 : bChangeFileExtensionToPDF = true;
4544 0 : else if( aFileExtension.equalsIgnoreAsciiCase( "odp" ) )
4545 0 : bChangeFileExtensionToPDF = true;
4546 0 : else if( aFileExtension.equalsIgnoreAsciiCase( "odg" ) )
4547 0 : bChangeFileExtensionToPDF = true;
4548 0 : else if( aFileExtension.equalsIgnoreAsciiCase( "ods" ) )
4549 0 : bChangeFileExtensionToPDF = true;
4550 0 : if( bChangeFileExtensionToPDF )
4551 0 : aTargetURL.setExtension(OUString( "pdf" ) );
4552 : }
4553 : //check if extension is pdf, see if GoToR should be forced
4554 0 : bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase( "pdf" );
4555 0 : if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
4556 0 : nSetGoToRMode++;
4557 : }
4558 : //prepare the URL, if relative or not
4559 0 : INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
4560 : //queue the string common to all types of actions
4561 0 : aLine.append( "/A<</Type/Action/S");
4562 0 : if( bIsUNCPath ) // handle Win UNC paths
4563 : {
4564 0 : aLine.append( "/Launch/Win<</F" );
4565 : // INetURLObject is not good with UNC paths, use original path
4566 0 : appendLiteralStringEncrypt( rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4567 0 : aLine.append( ">>" );
4568 : }
4569 : else
4570 : {
4571 0 : bool bSetRelative = false;
4572 0 : bool bFileSpec = false;
4573 : //check if relative file link is requested and if the protocol is 'file://'
4574 0 : if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INetProtocol::File )
4575 0 : bSetRelative = true;
4576 :
4577 0 : OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is,
4578 0 : if( nSetGoToRMode == 0 )
4579 : {
4580 0 : switch( m_aContext.DefaultLinkAction )
4581 : {
4582 : default:
4583 : case PDFWriter::URIAction :
4584 : case PDFWriter::URIActionDestination :
4585 0 : aLine.append( "/URI/URI" );
4586 0 : break;
4587 : case PDFWriter::LaunchAction:
4588 : // now:
4589 : // if a launch action is requested and the hyperlink target has a fragment
4590 : // and the target file does not have a pdf extension, or it's not a 'file:://'
4591 : // protocol then force the uri action on it
4592 : // This code will permit the correct opening of application on web pages,
4593 : // the one that normally have fragments (but I may be wrong...)
4594 : // and will force the use of URI when the protocol is not file:
4595 0 : if( (!aFragment.isEmpty() && !bTargetHasPDFExtension) ||
4596 : eTargetProtocol != INetProtocol::File )
4597 : {
4598 0 : aLine.append( "/URI/URI" );
4599 : }
4600 : else
4601 : {
4602 0 : aLine.append( "/Launch/F" );
4603 0 : bFileSpec = true;
4604 : }
4605 0 : break;
4606 : }
4607 : }
4608 :
4609 : //fragment are encoded in the same way as in the named destination processing
4610 0 : if( nSetGoToRMode )
4611 : {
4612 : //add the fragment
4613 0 : OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET );
4614 0 : aLine.append("/GoToR");
4615 0 : aLine.append("/F");
4616 0 : bFileSpec = true;
4617 : appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark,
4618 : INetURLObject::WAS_ENCODED,
4619 : INetURLObject::DECODE_WITH_CHARSET ) :
4620 0 : aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4621 0 : if( !aFragment.isEmpty() )
4622 : {
4623 0 : aLine.append("/D/");
4624 0 : appendDestinationName( aFragment , aLine );
4625 0 : }
4626 : }
4627 : else
4628 : {
4629 : // change the fragment to accommodate the bookmark (only if the file extension
4630 : // is PDF and the requested action is of the correct type)
4631 0 : if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination &&
4632 0 : bTargetHasPDFExtension && !aFragment.isEmpty() )
4633 : {
4634 0 : OStringBuffer aLineLoc( 1024 );
4635 0 : appendDestinationName( aFragment , aLineLoc );
4636 : //substitute the fragment
4637 0 : aTargetURL.SetMark( OStringToOUString(aLineLoc.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US) );
4638 : }
4639 0 : OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE );
4640 : appendLiteralStringEncrypt(bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL,
4641 : INetURLObject::WAS_ENCODED,
4642 : bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE
4643 : ) :
4644 0 : aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4645 0 : }
4646 : //<--- i56629
4647 : }
4648 0 : aLine.append( ">>\n" );
4649 : }
4650 0 : if( rLink.m_nStructParent > 0 )
4651 : {
4652 0 : aLine.append( "/StructParent " );
4653 0 : aLine.append( rLink.m_nStructParent );
4654 : }
4655 0 : aLine.append( ">>\nendobj\n\n" );
4656 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4657 0 : }
4658 :
4659 0 : return true;
4660 : }
4661 :
4662 0 : bool PDFWriterImpl::emitNoteAnnotations()
4663 : {
4664 : // emit note annotations
4665 0 : int nAnnots = m_aNotes.size();
4666 0 : for( int i = 0; i < nAnnots; i++ )
4667 : {
4668 0 : const PDFNoteEntry& rNote = m_aNotes[i];
4669 0 : if( ! updateObject( rNote.m_nObject ) )
4670 0 : return false;
4671 :
4672 0 : OStringBuffer aLine( 1024 );
4673 0 : aLine.append( rNote.m_nObject );
4674 0 : aLine.append( " 0 obj\n" );
4675 : //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4676 : // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4677 0 : aLine.append( "<</Type/Annot" );
4678 0 : if( m_bIsPDF_A1 )
4679 0 : aLine.append( "/F 4" );
4680 0 : aLine.append( "/Subtype/Text/Rect[" );
4681 :
4682 0 : appendFixedInt( rNote.m_aRect.Left(), aLine );
4683 0 : aLine.append( ' ' );
4684 0 : appendFixedInt( rNote.m_aRect.Top(), aLine );
4685 0 : aLine.append( ' ' );
4686 0 : appendFixedInt( rNote.m_aRect.Right(), aLine );
4687 0 : aLine.append( ' ' );
4688 0 : appendFixedInt( rNote.m_aRect.Bottom(), aLine );
4689 0 : aLine.append( "]" );
4690 :
4691 : // contents of the note (type text string)
4692 0 : aLine.append( "/Contents\n" );
4693 0 : appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
4694 0 : aLine.append( "\n" );
4695 :
4696 : // optional title
4697 0 : if( !rNote.m_aContents.Title.isEmpty() )
4698 : {
4699 0 : aLine.append( "/T" );
4700 0 : appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
4701 0 : aLine.append( "\n" );
4702 : }
4703 :
4704 0 : aLine.append( ">>\nendobj\n\n" );
4705 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4706 0 : }
4707 0 : return true;
4708 : }
4709 :
4710 0 : Font PDFWriterImpl::replaceFont( const vcl::Font& rControlFont, const vcl::Font& rAppSetFont )
4711 : {
4712 0 : bool bAdjustSize = false;
4713 :
4714 0 : Font aFont( rControlFont );
4715 0 : if( aFont.GetName().isEmpty() )
4716 : {
4717 0 : aFont = rAppSetFont;
4718 0 : if( rControlFont.GetHeight() )
4719 0 : aFont.SetSize( Size( 0, rControlFont.GetHeight() ) );
4720 : else
4721 0 : bAdjustSize = true;
4722 0 : if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
4723 0 : aFont.SetItalic( rControlFont.GetItalic() );
4724 0 : if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
4725 0 : aFont.SetWeight( rControlFont.GetWeight() );
4726 : }
4727 0 : else if( ! aFont.GetHeight() )
4728 : {
4729 0 : aFont.SetSize( rAppSetFont.GetSize() );
4730 0 : bAdjustSize = true;
4731 : }
4732 0 : if( bAdjustSize )
4733 : {
4734 0 : Size aFontSize = aFont.GetSize();
4735 0 : OutputDevice* pDefDev = Application::GetDefaultDevice();
4736 0 : aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
4737 0 : aFont.SetSize( aFontSize );
4738 : }
4739 0 : return aFont;
4740 : }
4741 :
4742 0 : sal_Int32 PDFWriterImpl::getBestBuiltinFont( const vcl::Font& rFont )
4743 : {
4744 0 : sal_Int32 nBest = 4; // default to Helvetica
4745 0 : OUString aFontName( rFont.GetName() );
4746 0 : aFontName = aFontName.toAsciiLowerCase();
4747 :
4748 0 : if( aFontName.indexOf( "times" ) != -1 )
4749 0 : nBest = 8;
4750 0 : else if( aFontName.indexOf( "courier" ) != -1 )
4751 0 : nBest = 0;
4752 0 : else if( aFontName.indexOf( "dingbats" ) != -1 )
4753 0 : nBest = 13;
4754 0 : else if( aFontName.indexOf( "symbol" ) != -1 )
4755 0 : nBest = 12;
4756 0 : if( nBest < 12 )
4757 : {
4758 0 : if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
4759 0 : nBest += 1;
4760 0 : if( rFont.GetWeight() > WEIGHT_MEDIUM )
4761 0 : nBest += 2;
4762 : }
4763 :
4764 0 : if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() )
4765 0 : m_aBuiltinFontToObjectMap[ nBest ] = createObject();
4766 :
4767 0 : return nBest;
4768 : }
4769 :
4770 0 : static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
4771 : {
4772 0 : return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1;
4773 : }
4774 :
4775 0 : void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget )
4776 : {
4777 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4778 :
4779 : // save graphics state
4780 0 : push( PushFlags::ALL );
4781 :
4782 : // transform relative to control's coordinates since an
4783 : // appearance stream is a form XObject
4784 : // this relies on the m_aRect member of rButton NOT already being transformed
4785 : // to default user space
4786 0 : if( rWidget.Background || rWidget.Border )
4787 : {
4788 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) );
4789 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) );
4790 0 : drawRectangle( rWidget.Location );
4791 : }
4792 : // prepare font to use
4793 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
4794 0 : setFont( aFont );
4795 0 : setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
4796 :
4797 0 : drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
4798 :
4799 : // create DA string while local mapmode is still in place
4800 : // (that is before endRedirect())
4801 0 : OStringBuffer aDA( 256 );
4802 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
4803 0 : Font aDummyFont( OUString( "Helvetica" ), aFont.GetSize() );
4804 0 : sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont );
4805 0 : aDA.append( ' ' );
4806 0 : aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() );
4807 0 : aDA.append( ' ' );
4808 0 : m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4809 0 : aDA.append( " Tf" );
4810 0 : rButton.m_aDAString = aDA.makeStringAndClear();
4811 :
4812 0 : pop();
4813 :
4814 0 : rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
4815 :
4816 : /* seems like a bad hack but at least works in both AR5 and 6:
4817 : we draw the button ourselves and tell AR
4818 : the button would be totally transparent with no text
4819 :
4820 : One would expect that simply setting a normal appearance
4821 : should suffice, but no, as soon as the user actually presses
4822 : the button and an action is tied to it (gasp! a button that
4823 : does something) the appearance gets replaced by some crap that AR
4824 : creates on the fly even if no DA or MK is given. On AR6 at least
4825 : the DA and MK work as expected, but on AR5 this creates a region
4826 : filled with the background color but nor text. Urgh.
4827 : */
4828 0 : rButton.m_aMKDict = "/BC [] /BG [] /CA";
4829 0 : rButton.m_aMKDictCAString = "";
4830 0 : }
4831 :
4832 0 : Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern,
4833 : const PDFWriter::AnyWidget& rWidget,
4834 : const StyleSettings& rSettings )
4835 : {
4836 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
4837 :
4838 0 : if( rWidget.Background || rWidget.Border )
4839 : {
4840 0 : if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) )
4841 : {
4842 0 : sal_Int32 nDelta = getReferenceDevice()->GetDPIX() / 500;
4843 0 : if( nDelta < 1 )
4844 0 : nDelta = 1;
4845 0 : setLineColor( Color( COL_TRANSPARENT ) );
4846 0 : Rectangle aRect = rIntern.m_aRect;
4847 0 : setFillColor( rSettings.GetLightBorderColor() );
4848 0 : drawRectangle( aRect );
4849 0 : aRect.Left() += nDelta; aRect.Top() += nDelta;
4850 0 : aRect.Right() -= nDelta; aRect.Bottom() -= nDelta;
4851 0 : setFillColor( rSettings.GetFieldColor() );
4852 0 : drawRectangle( aRect );
4853 0 : setFillColor( rSettings.GetLightColor() );
4854 0 : drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
4855 0 : drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
4856 0 : setFillColor( rSettings.GetDarkShadowColor() );
4857 0 : drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
4858 0 : drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
4859 : }
4860 : else
4861 : {
4862 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) );
4863 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4864 0 : drawRectangle( rIntern.m_aRect );
4865 : }
4866 :
4867 0 : if( rWidget.Border )
4868 : {
4869 : // adjust edit area accounting for border
4870 0 : sal_Int32 nDelta = aFont.GetHeight()/4;
4871 0 : if( nDelta < 1 )
4872 0 : nDelta = 1;
4873 0 : rIntern.m_aRect.Left() += nDelta;
4874 0 : rIntern.m_aRect.Top() += nDelta;
4875 0 : rIntern.m_aRect.Right() -= nDelta;
4876 0 : rIntern.m_aRect.Bottom()-= nDelta;
4877 : }
4878 : }
4879 0 : return aFont;
4880 : }
4881 :
4882 0 : void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget )
4883 : {
4884 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4885 0 : SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
4886 :
4887 0 : push( PushFlags::ALL );
4888 :
4889 : // prepare font to use, draw field border
4890 0 : Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
4891 0 : sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
4892 :
4893 : // prepare DA string
4894 0 : OStringBuffer aDA( 32 );
4895 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4896 0 : aDA.append( ' ' );
4897 0 : if( m_aContext.FieldsUseSystemFonts )
4898 : {
4899 0 : aDA.append( "/F" );
4900 0 : aDA.append( nBest );
4901 :
4902 0 : OStringBuffer aDR( 32 );
4903 0 : aDR.append( "/Font " );
4904 0 : aDR.append( getFontDictObject() );
4905 0 : aDR.append( " 0 R" );
4906 0 : rEdit.m_aDRDict = aDR.makeStringAndClear();
4907 : }
4908 : else
4909 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4910 0 : aDA.append( ' ' );
4911 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4912 0 : aDA.append( " Tf" );
4913 :
4914 : /* create an empty appearance stream, let the viewer create
4915 : the appearance at runtime. This is because AR5 seems to
4916 : paint the widget appearance always, and a dynamically created
4917 : appearance on top of it. AR6 is well behaved in that regard, so
4918 : that behaviour seems to be a bug. Anyway this empty appearance
4919 : relies on /NeedAppearances in the AcroForm dictionary set to "true"
4920 : */
4921 0 : beginRedirect( pEditStream, rEdit.m_aRect );
4922 0 : OStringBuffer aAppearance( 32 );
4923 0 : aAppearance.append( "/Tx BMC\nEMC\n" );
4924 0 : writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4925 :
4926 0 : endRedirect();
4927 0 : pop();
4928 :
4929 0 : rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
4930 :
4931 0 : rEdit.m_aDAString = aDA.makeStringAndClear();
4932 0 : }
4933 :
4934 0 : void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget )
4935 : {
4936 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4937 0 : SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
4938 :
4939 0 : push( PushFlags::ALL );
4940 :
4941 : // prepare font to use, draw field border
4942 0 : Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
4943 0 : sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
4944 :
4945 0 : beginRedirect( pListBoxStream, rBox.m_aRect );
4946 0 : OStringBuffer aAppearance( 64 );
4947 :
4948 0 : setLineColor( Color( COL_TRANSPARENT ) );
4949 0 : setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
4950 0 : drawRectangle( rBox.m_aRect );
4951 :
4952 : // empty appearance, see createDefaultEditAppearance for reference
4953 0 : aAppearance.append( "/Tx BMC\nEMC\n" );
4954 0 : writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4955 :
4956 0 : endRedirect();
4957 0 : pop();
4958 :
4959 0 : rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
4960 :
4961 : // prepare DA string
4962 0 : OStringBuffer aDA( 256 );
4963 : // prepare DA string
4964 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4965 0 : aDA.append( ' ' );
4966 0 : if( m_aContext.FieldsUseSystemFonts )
4967 : {
4968 0 : aDA.append( "/F" );
4969 0 : aDA.append( nBest );
4970 :
4971 0 : OStringBuffer aDR( 32 );
4972 0 : aDR.append( "/Font " );
4973 0 : aDR.append( getFontDictObject() );
4974 0 : aDR.append( " 0 R" );
4975 0 : rBox.m_aDRDict = aDR.makeStringAndClear();
4976 : }
4977 : else
4978 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4979 0 : aDA.append( ' ' );
4980 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4981 0 : aDA.append( " Tf" );
4982 0 : rBox.m_aDAString = aDA.makeStringAndClear();
4983 0 : }
4984 :
4985 0 : void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget )
4986 : {
4987 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4988 :
4989 : // save graphics state
4990 0 : push( PushFlags::ALL );
4991 :
4992 0 : if( rWidget.Background || rWidget.Border )
4993 : {
4994 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
4995 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4996 0 : drawRectangle( rBox.m_aRect );
4997 : }
4998 :
4999 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5000 0 : setFont( aFont );
5001 0 : Size aFontSize = aFont.GetSize();
5002 0 : if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5003 0 : aFontSize.Height() = rBox.m_aRect.GetHeight();
5004 0 : sal_Int32 nDelta = aFontSize.Height()/10;
5005 0 : if( nDelta < 1 )
5006 0 : nDelta = 1;
5007 :
5008 0 : Rectangle aCheckRect, aTextRect;
5009 0 : if( rWidget.ButtonIsLeft )
5010 : {
5011 0 : aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
5012 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5013 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5014 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5015 :
5016 : // #i74206# handle small controls without text area
5017 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5018 : {
5019 0 : aCheckRect.Right() -= nDelta;
5020 0 : aCheckRect.Top() += nDelta/2;
5021 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
5022 : }
5023 :
5024 0 : aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5025 0 : aTextRect.Top() = rBox.m_aRect.Top();
5026 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5027 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
5028 : }
5029 : else
5030 : {
5031 0 : aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5032 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5033 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5034 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5035 :
5036 : // #i74206# handle small controls without text area
5037 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5038 : {
5039 0 : aCheckRect.Left() += nDelta;
5040 0 : aCheckRect.Top() += nDelta/2;
5041 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
5042 : }
5043 :
5044 0 : aTextRect.Left() = rBox.m_aRect.Left();
5045 0 : aTextRect.Top() = rBox.m_aRect.Top();
5046 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5047 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
5048 : }
5049 0 : setLineColor( Color( COL_BLACK ) );
5050 0 : setFillColor( Color( COL_TRANSPARENT ) );
5051 0 : OStringBuffer aLW( 32 );
5052 0 : aLW.append( "q " );
5053 0 : m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
5054 0 : aLW.append( " w " );
5055 0 : writeBuffer( aLW.getStr(), aLW.getLength() );
5056 0 : drawRectangle( aCheckRect );
5057 0 : writeBuffer( " Q\n", 3 );
5058 0 : setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5059 0 : drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5060 :
5061 0 : pop();
5062 :
5063 0 : OStringBuffer aDA( 256 );
5064 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5065 0 : sal_Int32 nBest = getBestBuiltinFont( Font( OUString( "ZapfDingbats" ), aFont.GetSize() ) );
5066 0 : aDA.append( ' ' );
5067 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5068 0 : aDA.append( " 0 Tf" );
5069 0 : rBox.m_aDAString = aDA.makeStringAndClear();
5070 0 : rBox.m_aMKDict = "/CA";
5071 0 : rBox.m_aMKDictCAString = "8";
5072 0 : rBox.m_aRect = aCheckRect;
5073 :
5074 : // create appearance streams
5075 0 : sal_Char cMark = '8';
5076 0 : sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)];
5077 0 : nCharXOffset *= aCheckRect.GetHeight();
5078 0 : nCharXOffset /= 2000;
5079 : sal_Int32 nCharYOffset = 1000-
5080 0 : (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative
5081 0 : nCharYOffset *= aCheckRect.GetHeight();
5082 0 : nCharYOffset /= 2000;
5083 :
5084 0 : SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5085 0 : beginRedirect( pCheckStream, aCheckRect );
5086 0 : aDA.append( "/Tx BMC\nq BT\n" );
5087 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5088 0 : aDA.append( ' ' );
5089 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5090 0 : aDA.append( ' ' );
5091 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5092 0 : aDA.append( " Tf\n" );
5093 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
5094 0 : aDA.append( " " );
5095 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
5096 0 : aDA.append( " Td (" );
5097 0 : aDA.append( cMark );
5098 0 : aDA.append( ") Tj\nET\nQ\nEMC\n" );
5099 0 : writeBuffer( aDA.getStr(), aDA.getLength() );
5100 0 : endRedirect();
5101 0 : rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5102 :
5103 0 : SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5104 0 : beginRedirect( pUncheckStream, aCheckRect );
5105 0 : writeBuffer( "/Tx BMC\nEMC\n", 12 );
5106 0 : endRedirect();
5107 0 : rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5108 0 : }
5109 :
5110 0 : void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget )
5111 : {
5112 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5113 :
5114 : // save graphics state
5115 0 : push( PushFlags::ALL );
5116 :
5117 0 : if( rWidget.Background || rWidget.Border )
5118 : {
5119 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5120 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5121 0 : drawRectangle( rBox.m_aRect );
5122 : }
5123 :
5124 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5125 0 : setFont( aFont );
5126 0 : Size aFontSize = aFont.GetSize();
5127 0 : if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5128 0 : aFontSize.Height() = rBox.m_aRect.GetHeight();
5129 0 : sal_Int32 nDelta = aFontSize.Height()/10;
5130 0 : if( nDelta < 1 )
5131 0 : nDelta = 1;
5132 :
5133 0 : Rectangle aCheckRect, aTextRect;
5134 0 : if( rWidget.ButtonIsLeft )
5135 : {
5136 0 : aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
5137 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5138 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5139 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5140 :
5141 : // #i74206# handle small controls without text area
5142 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5143 : {
5144 0 : aCheckRect.Right() -= nDelta;
5145 0 : aCheckRect.Top() += nDelta/2;
5146 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
5147 : }
5148 :
5149 0 : aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5150 0 : aTextRect.Top() = rBox.m_aRect.Top();
5151 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5152 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
5153 : }
5154 : else
5155 : {
5156 0 : aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5157 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5158 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5159 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5160 :
5161 : // #i74206# handle small controls without text area
5162 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5163 : {
5164 0 : aCheckRect.Left() += nDelta;
5165 0 : aCheckRect.Top() += nDelta/2;
5166 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
5167 : }
5168 :
5169 0 : aTextRect.Left() = rBox.m_aRect.Left();
5170 0 : aTextRect.Top() = rBox.m_aRect.Top();
5171 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5172 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
5173 : }
5174 0 : setLineColor( Color( COL_BLACK ) );
5175 0 : setFillColor( Color( COL_TRANSPARENT ) );
5176 0 : OStringBuffer aLW( 32 );
5177 0 : aLW.append( "q " );
5178 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
5179 0 : aLW.append( " w " );
5180 0 : writeBuffer( aLW.getStr(), aLW.getLength() );
5181 0 : drawEllipse( aCheckRect );
5182 0 : writeBuffer( " Q\n", 3 );
5183 0 : setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5184 0 : drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5185 :
5186 0 : pop();
5187 :
5188 0 : OStringBuffer aDA( 256 );
5189 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5190 0 : sal_Int32 nBest = getBestBuiltinFont( Font( OUString( "ZapfDingbats" ), aFont.GetSize() ) );
5191 0 : aDA.append( ' ' );
5192 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5193 0 : aDA.append( " 0 Tf" );
5194 0 : rBox.m_aDAString = aDA.makeStringAndClear();
5195 : //to encrypt this (el)
5196 0 : rBox.m_aMKDict = "/CA";
5197 : //after this assignement, to m_aMKDic cannot be added anything
5198 0 : rBox.m_aMKDictCAString = "l";
5199 :
5200 0 : rBox.m_aRect = aCheckRect;
5201 :
5202 : // create appearance streams
5203 0 : push( PushFlags::ALL);
5204 0 : SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5205 :
5206 0 : beginRedirect( pCheckStream, aCheckRect );
5207 0 : aDA.append( "/Tx BMC\nq BT\n" );
5208 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5209 0 : aDA.append( ' ' );
5210 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5211 0 : aDA.append( ' ' );
5212 0 : m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5213 0 : aDA.append( " Tf\n0 0 Td\nET\nQ\n" );
5214 0 : writeBuffer( aDA.getStr(), aDA.getLength() );
5215 0 : setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5216 0 : setLineColor( Color( COL_TRANSPARENT ) );
5217 0 : aCheckRect.Left() += 3*nDelta;
5218 0 : aCheckRect.Top() += 3*nDelta;
5219 0 : aCheckRect.Bottom() -= 3*nDelta;
5220 0 : aCheckRect.Right() -= 3*nDelta;
5221 0 : drawEllipse( aCheckRect );
5222 0 : writeBuffer( "\nEMC\n", 5 );
5223 0 : endRedirect();
5224 :
5225 0 : pop();
5226 0 : rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5227 :
5228 0 : SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5229 0 : beginRedirect( pUncheckStream, aCheckRect );
5230 0 : writeBuffer( "/Tx BMC\nEMC\n", 12 );
5231 0 : endRedirect();
5232 0 : rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5233 0 : }
5234 :
5235 0 : bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
5236 : {
5237 : // TODO: check and insert default streams
5238 0 : OString aStandardAppearance;
5239 0 : switch( rWidget.m_eType )
5240 : {
5241 : case PDFWriter::CheckBox:
5242 0 : aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
5243 0 : break;
5244 : default:
5245 0 : break;
5246 : }
5247 :
5248 0 : if( !rWidget.m_aAppearances.empty() )
5249 : {
5250 0 : rAnnotDict.append( "/AP<<\n" );
5251 0 : for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it )
5252 : {
5253 0 : rAnnotDict.append( "/" );
5254 0 : rAnnotDict.append( dict_it->first );
5255 0 : bool bUseSubDict = (dict_it->second.size() > 1);
5256 0 : rAnnotDict.append( bUseSubDict ? "<<" : " " );
5257 :
5258 0 : for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin();
5259 0 : stream_it != dict_it->second.end(); ++stream_it )
5260 : {
5261 0 : SvMemoryStream* pApppearanceStream = stream_it->second;
5262 0 : dict_it->second[ stream_it->first ] = NULL;
5263 :
5264 0 : bool bDeflate = compressStream( pApppearanceStream );
5265 :
5266 0 : pApppearanceStream->Seek( STREAM_SEEK_TO_END );
5267 0 : sal_Int64 nStreamLen = pApppearanceStream->Tell();
5268 0 : pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
5269 0 : sal_Int32 nObject = createObject();
5270 0 : CHECK_RETURN( updateObject( nObject ) );
5271 : #if OSL_DEBUG_LEVEL > 1
5272 : emitComment( "PDFWriterImpl::emitAppearances" );
5273 : #endif
5274 0 : OStringBuffer aLine;
5275 0 : aLine.append( nObject );
5276 :
5277 : aLine.append( " 0 obj\n"
5278 : "<</Type/XObject\n"
5279 : "/Subtype/Form\n"
5280 0 : "/BBox[0 0 " );
5281 0 : appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
5282 0 : aLine.append( " " );
5283 0 : appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
5284 : aLine.append( "]\n"
5285 0 : "/Resources " );
5286 0 : aLine.append( getResourceDictObj() );
5287 : aLine.append( " 0 R\n"
5288 0 : "/Length " );
5289 0 : aLine.append( nStreamLen );
5290 0 : aLine.append( "\n" );
5291 0 : if( bDeflate )
5292 0 : aLine.append( "/Filter/FlateDecode\n" );
5293 0 : aLine.append( ">>\nstream\n" );
5294 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5295 0 : checkAndEnableStreamEncryption( nObject );
5296 0 : CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
5297 0 : disableStreamEncryption();
5298 0 : CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
5299 :
5300 0 : if( bUseSubDict )
5301 : {
5302 0 : rAnnotDict.append( " /" );
5303 0 : rAnnotDict.append( stream_it->first );
5304 0 : rAnnotDict.append( " " );
5305 : }
5306 0 : rAnnotDict.append( nObject );
5307 0 : rAnnotDict.append( " 0 R" );
5308 :
5309 0 : delete pApppearanceStream;
5310 0 : }
5311 :
5312 0 : rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
5313 : }
5314 0 : rAnnotDict.append( ">>\n" );
5315 0 : if( !aStandardAppearance.isEmpty() )
5316 : {
5317 0 : rAnnotDict.append( "/AS /" );
5318 0 : rAnnotDict.append( aStandardAppearance );
5319 0 : rAnnotDict.append( "\n" );
5320 : }
5321 : }
5322 :
5323 0 : return true;
5324 : }
5325 :
5326 0 : bool PDFWriterImpl::emitWidgetAnnotations()
5327 : {
5328 0 : ensureUniqueRadioOnValues();
5329 :
5330 0 : int nAnnots = m_aWidgets.size();
5331 0 : for( int a = 0; a < nAnnots; a++ )
5332 : {
5333 0 : PDFWidget& rWidget = m_aWidgets[a];
5334 :
5335 0 : OStringBuffer aLine( 1024 );
5336 0 : OStringBuffer aValue( 256 );
5337 0 : aLine.append( rWidget.m_nObject );
5338 : aLine.append( " 0 obj\n"
5339 0 : "<<" );
5340 0 : if( rWidget.m_eType != PDFWriter::Hierarchy )
5341 : {
5342 : // emit widget annotation only for terminal fields
5343 0 : if( rWidget.m_aKids.empty() )
5344 : {
5345 : int iRectMargin;
5346 :
5347 0 : aLine.append( "/Type/Annot/Subtype/Widget/F " );
5348 :
5349 0 : if (rWidget.m_eType == PDFWriter::Signature)
5350 : {
5351 0 : aLine.append( "132\n" ); // Print & Locked
5352 0 : iRectMargin = 0;
5353 : }
5354 : else
5355 : {
5356 0 : aLine.append( "4\n" );
5357 0 : iRectMargin = 1;
5358 : }
5359 :
5360 0 : aLine.append("/Rect[" );
5361 0 : appendFixedInt( rWidget.m_aRect.Left()-iRectMargin, aLine );
5362 0 : aLine.append( ' ' );
5363 0 : appendFixedInt( rWidget.m_aRect.Top()+iRectMargin, aLine );
5364 0 : aLine.append( ' ' );
5365 0 : appendFixedInt( rWidget.m_aRect.Right()+iRectMargin, aLine );
5366 0 : aLine.append( ' ' );
5367 0 : appendFixedInt( rWidget.m_aRect.Bottom()-iRectMargin, aLine );
5368 0 : aLine.append( "]\n" );
5369 : }
5370 0 : aLine.append( "/FT/" );
5371 0 : switch( rWidget.m_eType )
5372 : {
5373 : case PDFWriter::RadioButton:
5374 : case PDFWriter::CheckBox:
5375 : // for radio buttons only the RadioButton field, not the
5376 : // CheckBox children should have a value, else acrobat reader
5377 : // does not always check the right button
5378 : // of course real check boxes (not belonging to a radio group)
5379 : // need their values, too
5380 0 : if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
5381 : {
5382 0 : aValue.append( "/" );
5383 : // check for radio group with all buttons unpressed
5384 0 : if( rWidget.m_aValue.isEmpty() )
5385 0 : aValue.append( "Off" );
5386 : else
5387 0 : appendName( rWidget.m_aValue, aValue );
5388 : }
5389 : // fall-through
5390 : case PDFWriter::PushButton:
5391 0 : aLine.append( "Btn" );
5392 0 : break;
5393 : case PDFWriter::ListBox:
5394 0 : if( rWidget.m_nFlags & 0x200000 ) // multiselect
5395 : {
5396 0 : aValue.append( "[" );
5397 0 : for( size_t i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
5398 : {
5399 0 : sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
5400 0 : if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
5401 0 : appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
5402 : }
5403 0 : aValue.append( "]" );
5404 : }
5405 0 : else if( rWidget.m_aSelectedEntries.size() > 0 &&
5406 0 : rWidget.m_aSelectedEntries[0] >= 0 &&
5407 0 : rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
5408 : {
5409 0 : appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
5410 : }
5411 : else
5412 0 : appendUnicodeTextStringEncrypt( OUString(), rWidget.m_nObject, aValue );
5413 0 : aLine.append( "Ch" );
5414 0 : break;
5415 : case PDFWriter::ComboBox:
5416 0 : appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5417 0 : aLine.append( "Ch" );
5418 0 : break;
5419 : case PDFWriter::Edit:
5420 0 : aLine.append( "Tx" );
5421 0 : appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5422 0 : break;
5423 : case PDFWriter::Signature:
5424 0 : aLine.append( "Sig" );
5425 0 : aValue.append(OUStringToOString(rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US));
5426 0 : break;
5427 : case PDFWriter::Hierarchy: // make the compiler happy
5428 0 : break;
5429 : }
5430 0 : aLine.append( "\n" );
5431 0 : aLine.append( "/P " );
5432 0 : aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
5433 0 : aLine.append( " 0 R\n" );
5434 : }
5435 0 : if( rWidget.m_nParent )
5436 : {
5437 0 : aLine.append( "/Parent " );
5438 0 : aLine.append( rWidget.m_nParent );
5439 0 : aLine.append( " 0 R\n" );
5440 : }
5441 0 : if( rWidget.m_aKids.size() )
5442 : {
5443 0 : aLine.append( "/Kids[" );
5444 0 : for( size_t i = 0; i < rWidget.m_aKids.size(); i++ )
5445 : {
5446 0 : aLine.append( rWidget.m_aKids[i] );
5447 0 : aLine.append( " 0 R" );
5448 0 : aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5449 : }
5450 0 : aLine.append( "]\n" );
5451 : }
5452 0 : if( !rWidget.m_aName.isEmpty() )
5453 : {
5454 0 : aLine.append( "/T" );
5455 0 : appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
5456 0 : aLine.append( "\n" );
5457 : }
5458 0 : if( m_aContext.Version > PDFWriter::PDF_1_2 && !rWidget.m_aDescription.isEmpty() )
5459 : {
5460 : // the alternate field name should be unicode able since it is
5461 : // supposed to be used in UI
5462 0 : aLine.append( "/TU" );
5463 0 : appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
5464 0 : aLine.append( "\n" );
5465 : }
5466 :
5467 0 : if( rWidget.m_nFlags )
5468 : {
5469 0 : aLine.append( "/Ff " );
5470 0 : aLine.append( rWidget.m_nFlags );
5471 0 : aLine.append( "\n" );
5472 : }
5473 0 : if( !aValue.isEmpty() )
5474 : {
5475 0 : OString aVal = aValue.makeStringAndClear();
5476 0 : aLine.append( "/V " );
5477 0 : aLine.append( aVal );
5478 : aLine.append( "\n"
5479 0 : "/DV " );
5480 0 : aLine.append( aVal );
5481 0 : aLine.append( "\n" );
5482 : }
5483 0 : if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
5484 : {
5485 0 : sal_Int32 nTI = -1;
5486 0 : aLine.append( "/Opt[\n" );
5487 0 : sal_Int32 i = 0;
5488 0 : for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i )
5489 : {
5490 0 : appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine );
5491 0 : aLine.append( "\n" );
5492 0 : if( *it == rWidget.m_aValue )
5493 0 : nTI = i;
5494 : }
5495 0 : aLine.append( "]\n" );
5496 0 : if( nTI > 0 )
5497 : {
5498 0 : aLine.append( "/TI " );
5499 0 : aLine.append( nTI );
5500 0 : aLine.append( "\n" );
5501 0 : if( rWidget.m_nFlags & 0x200000 ) // Multiselect
5502 : {
5503 0 : aLine.append( "/I [" );
5504 0 : aLine.append( nTI );
5505 0 : aLine.append( "]\n" );
5506 : }
5507 : }
5508 : }
5509 0 : if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
5510 : {
5511 0 : aLine.append( "/MaxLen " );
5512 0 : aLine.append( rWidget.m_nMaxLen );
5513 0 : aLine.append( "\n" );
5514 : }
5515 0 : if( rWidget.m_eType == PDFWriter::PushButton )
5516 : {
5517 0 : if(!m_bIsPDF_A1)
5518 : {
5519 0 : OStringBuffer aDest;
5520 0 : if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) )
5521 : {
5522 0 : aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
5523 0 : aLine.append( aDest.makeStringAndClear() );
5524 0 : aLine.append( ">>>>\n" );
5525 : }
5526 0 : else if( rWidget.m_aListEntries.empty() )
5527 : {
5528 : // create a reset form action
5529 0 : aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
5530 : }
5531 0 : else if( rWidget.m_bSubmit )
5532 : {
5533 : // create a submit form action
5534 0 : aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
5535 0 : appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() );
5536 0 : aLine.append( "/Flags " );
5537 :
5538 0 : sal_Int32 nFlags = 0;
5539 0 : switch( m_aContext.SubmitFormat )
5540 : {
5541 : case PDFWriter::HTML:
5542 0 : nFlags |= 4;
5543 0 : break;
5544 : case PDFWriter::XML:
5545 0 : if( m_aContext.Version > PDFWriter::PDF_1_3 )
5546 0 : nFlags |= 32;
5547 0 : break;
5548 : case PDFWriter::PDF:
5549 0 : if( m_aContext.Version > PDFWriter::PDF_1_3 )
5550 0 : nFlags |= 256;
5551 0 : break;
5552 : case PDFWriter::FDF:
5553 : default:
5554 0 : break;
5555 : }
5556 0 : if( rWidget.m_bSubmitGet )
5557 0 : nFlags |= 8;
5558 0 : aLine.append( nFlags );
5559 0 : aLine.append( ">>>>\n" );
5560 : }
5561 : else
5562 : {
5563 : // create a URI action
5564 0 : aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
5565 0 : aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
5566 0 : aLine.append( ")>>>>\n" );
5567 0 : }
5568 : }
5569 : else
5570 0 : m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA );
5571 : }
5572 0 : if( !rWidget.m_aDAString.isEmpty() )
5573 : {
5574 0 : if( !rWidget.m_aDRDict.isEmpty() )
5575 : {
5576 0 : aLine.append( "/DR<<" );
5577 0 : aLine.append( rWidget.m_aDRDict );
5578 0 : aLine.append( ">>\n" );
5579 : }
5580 : else
5581 : {
5582 0 : aLine.append( "/DR<</Font<<" );
5583 0 : appendBuiltinFontsToDict( aLine );
5584 0 : aLine.append( ">>>>\n" );
5585 : }
5586 0 : aLine.append( "/DA" );
5587 0 : appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
5588 0 : aLine.append( "\n" );
5589 0 : if( rWidget.m_nTextStyle & DrawTextFlags::Center )
5590 0 : aLine.append( "/Q 1\n" );
5591 0 : else if( rWidget.m_nTextStyle & DrawTextFlags::Right )
5592 0 : aLine.append( "/Q 2\n" );
5593 : }
5594 : // appearance charactristics for terminal fields
5595 : // which are supposed to have an appearance constructed
5596 : // by the viewer application
5597 0 : if( !rWidget.m_aMKDict.isEmpty() )
5598 : {
5599 0 : aLine.append( "/MK<<" );
5600 0 : aLine.append( rWidget.m_aMKDict );
5601 : //add the CA string, encrypting it
5602 0 : appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
5603 0 : aLine.append( ">>\n" );
5604 : }
5605 :
5606 0 : CHECK_RETURN( emitAppearances( rWidget, aLine ) );
5607 :
5608 : aLine.append( ">>\n"
5609 0 : "endobj\n\n" );
5610 0 : CHECK_RETURN( updateObject( rWidget.m_nObject ) );
5611 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5612 0 : }
5613 0 : return true;
5614 : }
5615 :
5616 0 : bool PDFWriterImpl::emitAnnotations()
5617 : {
5618 0 : if( m_aPages.size() < 1 )
5619 0 : return false;
5620 :
5621 0 : CHECK_RETURN( emitLinkAnnotations() );
5622 0 : CHECK_RETURN( emitNoteAnnotations() );
5623 0 : CHECK_RETURN( emitWidgetAnnotations() );
5624 :
5625 0 : return true;
5626 : }
5627 :
5628 : #undef CHECK_RETURN
5629 : #define CHECK_RETURN( x ) if( !x ) return false
5630 :
5631 0 : bool PDFWriterImpl::emitCatalog()
5632 : {
5633 : // build page tree
5634 : // currently there is only one node that contains all leaves
5635 :
5636 : // first create a page tree node id
5637 0 : sal_Int32 nTreeNode = createObject();
5638 :
5639 : // emit global resource dictionary (page emit needs it)
5640 0 : CHECK_RETURN( emitResources() );
5641 :
5642 : // emit all pages
5643 0 : for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it )
5644 0 : if( ! it->emit( nTreeNode ) )
5645 0 : return false;
5646 :
5647 0 : sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
5648 :
5649 0 : sal_Int32 nOutlineDict = emitOutline();
5650 :
5651 : // emit Output intent i59651
5652 0 : sal_Int32 nOutputIntentObject = emitOutputIntent();
5653 :
5654 : // emit metadata
5655 0 : sal_Int32 nMetadataObject = emitDocumentMetadata();
5656 :
5657 0 : sal_Int32 nStructureDict = 0;
5658 0 : if(m_aStructure.size() > 1)
5659 : {
5660 : // check if dummy structure containers are needed
5661 0 : addInternalStructureContainer(m_aStructure[0]);
5662 0 : nStructureDict = m_aStructure[0].m_nObject = createObject();
5663 0 : emitStructure( m_aStructure[ 0 ] );
5664 : }
5665 :
5666 : // adjust tree node file offset
5667 0 : if( ! updateObject( nTreeNode ) )
5668 0 : return false;
5669 :
5670 : // emit tree node
5671 0 : OStringBuffer aLine( 2048 );
5672 0 : aLine.append( nTreeNode );
5673 0 : aLine.append( " 0 obj\n" );
5674 0 : aLine.append( "<</Type/Pages\n" );
5675 0 : aLine.append( "/Resources " );
5676 0 : aLine.append( getResourceDictObj() );
5677 0 : aLine.append( " 0 R\n" );
5678 :
5679 0 : switch( m_eInheritedOrientation )
5680 : {
5681 0 : case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
5682 0 : case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
5683 :
5684 : case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant
5685 : case PDFWriter::Portrait:
5686 : default:
5687 0 : break;
5688 : }
5689 0 : sal_Int32 nMediaBoxWidth = 0;
5690 0 : sal_Int32 nMediaBoxHeight = 0;
5691 0 : if( m_aPages.empty() ) // sanity check, this should not happen
5692 : {
5693 0 : nMediaBoxWidth = m_nInheritedPageWidth;
5694 0 : nMediaBoxHeight = m_nInheritedPageHeight;
5695 : }
5696 : else
5697 : {
5698 0 : for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter )
5699 : {
5700 0 : if( iter->m_nPageWidth > nMediaBoxWidth )
5701 0 : nMediaBoxWidth = iter->m_nPageWidth;
5702 0 : if( iter->m_nPageHeight > nMediaBoxHeight )
5703 0 : nMediaBoxHeight = iter->m_nPageHeight;
5704 : }
5705 : }
5706 0 : aLine.append( "/MediaBox[ 0 0 " );
5707 0 : aLine.append( nMediaBoxWidth );
5708 0 : aLine.append( ' ' );
5709 0 : aLine.append( nMediaBoxHeight );
5710 : aLine.append( " ]\n"
5711 0 : "/Kids[ " );
5712 0 : unsigned int i = 0;
5713 0 : for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ )
5714 : {
5715 0 : aLine.append( iter->m_nPageObject );
5716 0 : aLine.append( " 0 R" );
5717 0 : aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5718 : }
5719 : aLine.append( "]\n"
5720 0 : "/Count " );
5721 0 : aLine.append( (sal_Int32)m_aPages.size() );
5722 : aLine.append( ">>\n"
5723 0 : "endobj\n\n" );
5724 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5725 :
5726 : // emit annotation objects
5727 0 : CHECK_RETURN( emitAnnotations() );
5728 :
5729 : // emit Catalog
5730 0 : m_nCatalogObject = createObject();
5731 0 : if( ! updateObject( m_nCatalogObject ) )
5732 0 : return false;
5733 0 : aLine.setLength( 0 );
5734 0 : aLine.append( m_nCatalogObject );
5735 : aLine.append( " 0 obj\n"
5736 0 : "<</Type/Catalog/Pages " );
5737 0 : aLine.append( nTreeNode );
5738 0 : aLine.append( " 0 R\n" );
5739 : //--->i56629
5740 : // check if there are named destinations to emit (root must be inside the catalog)
5741 0 : if( nNamedDestinationsDictionary )
5742 : {
5743 0 : aLine.append("/Dests ");
5744 0 : aLine.append( nNamedDestinationsDictionary );
5745 0 : aLine.append( " 0 R\n" );
5746 : }
5747 : //<----
5748 0 : if( m_aContext.PageLayout != PDFWriter::DefaultLayout )
5749 0 : switch( m_aContext.PageLayout )
5750 : {
5751 : default :
5752 : case PDFWriter::SinglePage :
5753 0 : aLine.append( "/PageLayout/SinglePage\n" );
5754 0 : break;
5755 : case PDFWriter::Continuous :
5756 0 : aLine.append( "/PageLayout/OneColumn\n" );
5757 0 : break;
5758 : case PDFWriter::ContinuousFacing :
5759 : // the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5760 0 : aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5761 0 : break;
5762 : }
5763 0 : if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode )
5764 0 : switch( m_aContext.PDFDocumentMode )
5765 : {
5766 : default :
5767 0 : aLine.append( "/PageMode/UseNone\n" );
5768 0 : break;
5769 : case PDFWriter::UseOutlines :
5770 0 : aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5771 0 : break;
5772 : case PDFWriter::UseThumbs :
5773 0 : aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5774 0 : break;
5775 : }
5776 0 : else if( m_aContext.OpenInFullScreenMode )
5777 0 : aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5778 :
5779 0 : OStringBuffer aInitPageRef;
5780 0 : if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() )
5781 : {
5782 0 : aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5783 0 : aInitPageRef.append( " 0 R" );
5784 : }
5785 : else
5786 0 : aInitPageRef.append( "0" );
5787 :
5788 0 : switch( m_aContext.PDFDocumentAction )
5789 : {
5790 : case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default
5791 : default:
5792 0 : if( aInitPageRef.getLength() > 1 )
5793 : {
5794 0 : aLine.append( "/OpenAction[" );
5795 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5796 0 : aLine.append( " /XYZ null null 0]\n" );
5797 : }
5798 0 : break;
5799 : case PDFWriter::FitInWindow :
5800 0 : aLine.append( "/OpenAction[" );
5801 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5802 0 : aLine.append( " /Fit]\n" ); //Open fit page
5803 0 : break;
5804 : case PDFWriter::FitWidth :
5805 0 : aLine.append( "/OpenAction[" );
5806 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5807 0 : aLine.append( " /FitH " );
5808 0 : aLine.append( m_nInheritedPageHeight );//Open fit width
5809 0 : aLine.append( "]\n" );
5810 0 : break;
5811 : case PDFWriter::FitVisible :
5812 0 : aLine.append( "/OpenAction[" );
5813 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5814 0 : aLine.append( " /FitBH " );
5815 0 : aLine.append( m_nInheritedPageHeight );//Open fit visible
5816 0 : aLine.append( "]\n" );
5817 0 : break;
5818 : case PDFWriter::ActionZoom :
5819 0 : aLine.append( "/OpenAction[" );
5820 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5821 0 : aLine.append( " /XYZ null null " );
5822 0 : if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5823 0 : aLine.append( (double)m_aContext.Zoom/100.0 );
5824 : else
5825 0 : aLine.append( "0" );
5826 0 : aLine.append( "]\n" );
5827 0 : break;
5828 : }
5829 :
5830 : // viewer preferences, if we had some, then emit
5831 0 : if( m_aContext.HideViewerToolbar ||
5832 0 : ( m_aContext.Version > PDFWriter::PDF_1_3 && !m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle ) ||
5833 0 : m_aContext.HideViewerMenubar ||
5834 0 : m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
5835 0 : m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
5836 : m_aContext.OpenInFullScreenMode )
5837 : {
5838 0 : aLine.append( "/ViewerPreferences<<" );
5839 0 : if( m_aContext.HideViewerToolbar )
5840 0 : aLine.append( "/HideToolbar true\n" );
5841 0 : if( m_aContext.HideViewerMenubar )
5842 0 : aLine.append( "/HideMenubar true\n" );
5843 0 : if( m_aContext.HideViewerWindowControls )
5844 0 : aLine.append( "/HideWindowUI true\n" );
5845 0 : if( m_aContext.FitWindow )
5846 0 : aLine.append( "/FitWindow true\n" );
5847 0 : if( m_aContext.CenterWindow )
5848 0 : aLine.append( "/CenterWindow true\n" );
5849 0 : if( m_aContext.Version > PDFWriter::PDF_1_3 && !m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle )
5850 0 : aLine.append( "/DisplayDocTitle true\n" );
5851 0 : if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing )
5852 0 : aLine.append( "/Direction/R2L\n" );
5853 0 : if( m_aContext.OpenInFullScreenMode )
5854 0 : switch( m_aContext.PDFDocumentMode )
5855 : {
5856 : default :
5857 : case PDFWriter::ModeDefault :
5858 0 : aLine.append( "/NonFullScreenPageMode/UseNone\n" );
5859 0 : break;
5860 : case PDFWriter::UseOutlines :
5861 0 : aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
5862 0 : break;
5863 : case PDFWriter::UseThumbs :
5864 0 : aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
5865 0 : break;
5866 : }
5867 0 : aLine.append( ">>\n" );
5868 : }
5869 :
5870 0 : if( nOutlineDict )
5871 : {
5872 0 : aLine.append( "/Outlines " );
5873 0 : aLine.append( nOutlineDict );
5874 0 : aLine.append( " 0 R\n" );
5875 : }
5876 0 : if( nStructureDict )
5877 : {
5878 0 : aLine.append( "/StructTreeRoot " );
5879 0 : aLine.append( nStructureDict );
5880 0 : aLine.append( " 0 R\n" );
5881 : }
5882 0 : if( !m_aContext.DocumentLocale.Language.isEmpty() )
5883 : {
5884 : /* PDF allows only RFC 3066, see above in emitStructure(). */
5885 0 : LanguageTag aLanguageTag( m_aContext.DocumentLocale);
5886 0 : OUString aLanguage, aScript, aCountry;
5887 0 : aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
5888 0 : if (!aLanguage.isEmpty())
5889 : {
5890 0 : OUStringBuffer aLocBuf( 16 );
5891 0 : aLocBuf.append( aLanguage );
5892 0 : if( !aCountry.isEmpty() )
5893 : {
5894 0 : aLocBuf.append( '-' );
5895 0 : aLocBuf.append( aCountry );
5896 : }
5897 0 : aLine.append( "/Lang" );
5898 0 : appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
5899 0 : aLine.append( "\n" );
5900 0 : }
5901 : }
5902 0 : if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 )
5903 : {
5904 0 : aLine.append( "/MarkInfo<</Marked true>>\n" );
5905 : }
5906 0 : if( m_aWidgets.size() > 0 )
5907 : {
5908 0 : aLine.append( "/AcroForm<</Fields[\n" );
5909 0 : int nWidgets = m_aWidgets.size();
5910 0 : int nOut = 0;
5911 0 : for( int j = 0; j < nWidgets; j++ )
5912 : {
5913 : // output only root fields
5914 0 : if( m_aWidgets[j].m_nParent < 1 )
5915 : {
5916 0 : aLine.append( m_aWidgets[j].m_nObject );
5917 0 : aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
5918 : }
5919 : }
5920 0 : aLine.append( "\n]" );
5921 :
5922 : #if !defined(ANDROID) && !defined(IOS)
5923 0 : if (m_nSignatureObject != -1)
5924 0 : aLine.append( "/SigFlags 3");
5925 : #endif
5926 :
5927 0 : aLine.append( "/DR " );
5928 0 : aLine.append( getResourceDictObj() );
5929 0 : aLine.append( " 0 R" );
5930 : // /NeedAppearances must not be used if PDF is signed
5931 0 : if( m_bIsPDF_A1
5932 : #if !defined(ANDROID) && !defined(IOS)
5933 0 : || ( m_nSignatureObject != -1 )
5934 : #endif
5935 : )
5936 0 : aLine.append( ">>\n" );
5937 : else
5938 0 : aLine.append( "/NeedAppearances true>>\n" );
5939 : }
5940 :
5941 : //--->i59651
5942 : //check if there is a Metadata object
5943 0 : if( nOutputIntentObject )
5944 : {
5945 0 : aLine.append("/OutputIntents[");
5946 0 : aLine.append( nOutputIntentObject );
5947 0 : aLine.append( " 0 R]" );
5948 : }
5949 :
5950 0 : if( nMetadataObject )
5951 : {
5952 0 : aLine.append("/Metadata ");
5953 0 : aLine.append( nMetadataObject );
5954 0 : aLine.append( " 0 R" );
5955 : }
5956 : //<----
5957 : aLine.append( ">>\n"
5958 0 : "endobj\n\n" );
5959 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5960 :
5961 0 : return true;
5962 : }
5963 :
5964 : #if !defined(ANDROID) && !defined(IOS)
5965 :
5966 0 : bool PDFWriterImpl::emitSignature()
5967 : {
5968 0 : if( !updateObject( m_nSignatureObject ) )
5969 0 : return false;
5970 :
5971 0 : OStringBuffer aLine( 0x5000 );
5972 0 : aLine.append( m_nSignatureObject );
5973 0 : aLine.append( " 0 obj\n" );
5974 0 : aLine.append("<</Contents <" );
5975 :
5976 0 : sal_uInt64 nOffset = ~0U;
5977 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) );
5978 :
5979 0 : m_nSignatureContentOffset = nOffset + aLine.getLength();
5980 :
5981 : // reserve some space for the PKCS#7 object
5982 0 : OStringBuffer aContentFiller( MAX_SIGNATURE_CONTENT_LENGTH );
5983 0 : comphelper::string::padToLength(aContentFiller, MAX_SIGNATURE_CONTENT_LENGTH, '0');
5984 0 : aLine.append( aContentFiller.makeStringAndClear() );
5985 0 : aLine.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached");
5986 :
5987 0 : if( !m_aContext.DocumentInfo.Author.isEmpty() )
5988 : {
5989 0 : aLine.append( "/Name" );
5990 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, m_nSignatureObject, aLine );
5991 : }
5992 :
5993 0 : aLine.append( " /M ");
5994 0 : appendLiteralStringEncrypt( m_aCreationDateString, m_nSignatureObject, aLine );
5995 :
5996 0 : aLine.append( " /ByteRange [ 0 ");
5997 0 : aLine.append( m_nSignatureContentOffset - 1, 10 );
5998 0 : aLine.append( " " );
5999 0 : aLine.append( m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1, 10 );
6000 0 : aLine.append( " " );
6001 :
6002 0 : m_nSignatureLastByteRangeNoOffset = nOffset + aLine.getLength();
6003 :
6004 : // mark the last ByteRange no and add some space. Now, we don't know
6005 : // how many bytes we need for this ByteRange value
6006 : // The real value will be overwritten in the finalizeSignature method
6007 0 : OStringBuffer aByteRangeFiller( 100 );
6008 0 : comphelper::string::padToLength(aByteRangeFiller, 100, ' ');
6009 0 : aLine.append( aByteRangeFiller.makeStringAndClear() );
6010 0 : aLine.append(" /Filter/Adobe.PPKMS");
6011 :
6012 : //emit reason, location and contactinfo
6013 0 : if ( !m_aContext.SignReason.isEmpty() )
6014 : {
6015 0 : aLine.append("/Reason");
6016 0 : appendUnicodeTextStringEncrypt( m_aContext.SignReason, m_nSignatureObject, aLine );
6017 : }
6018 :
6019 0 : if ( !m_aContext.SignLocation.isEmpty() )
6020 : {
6021 0 : aLine.append("/Location");
6022 0 : appendUnicodeTextStringEncrypt( m_aContext.SignLocation, m_nSignatureObject, aLine );
6023 : }
6024 :
6025 0 : if ( !m_aContext.SignContact.isEmpty() )
6026 : {
6027 0 : aLine.append("/ContactInfo");
6028 0 : appendUnicodeTextStringEncrypt( m_aContext.SignContact, m_nSignatureObject, aLine );
6029 : }
6030 :
6031 0 : aLine.append(" >>\nendobj\n\n" );
6032 :
6033 0 : if (!writeBuffer( aLine.getStr(), aLine.getLength() ))
6034 0 : return false;
6035 :
6036 0 : return true;
6037 : }
6038 :
6039 : #if !defined(ANDROID) && !defined(IOS) && !defined(_WIN32)
6040 :
6041 : namespace {
6042 : #if 0
6043 : }
6044 : #endif
6045 :
6046 0 : char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo * /*slot*/, PRBool /*retry*/, void *arg)
6047 : {
6048 0 : return PL_strdup(static_cast<char *>(arg));
6049 : }
6050 :
6051 : class HashContextScope {
6052 : HASHContext *mpPtr;
6053 : public:
6054 0 : explicit HashContextScope(HASHContext *pPtr) : mpPtr(pPtr) {}
6055 0 : ~HashContextScope() { clear(); }
6056 0 : void clear() { if (mpPtr) { HASH_Destroy(mpPtr); } mpPtr = NULL; }
6057 0 : HASHContext *get() { return mpPtr; }
6058 : };
6059 :
6060 : // ASN.1 used in the (much simpler) time stamp request. From RFC3161
6061 : // and other sources.
6062 :
6063 : /*
6064 : AlgorithmIdentifier ::= SEQUENCE {
6065 : algorithm OBJECT IDENTIFIER,
6066 : parameters ANY DEFINED BY algorithm OPTIONAL }
6067 : -- contains a value of the type
6068 : -- registered for use with the
6069 : -- algorithm object identifier value
6070 :
6071 : MessageImprint ::= SEQUENCE {
6072 : hashAlgorithm AlgorithmIdentifier,
6073 : hashedMessage OCTET STRING }
6074 : */
6075 :
6076 : typedef struct {
6077 : SECAlgorithmID hashAlgorithm;
6078 : SECItem hashedMessage;
6079 : } MessageImprint;
6080 :
6081 : /*
6082 : Extension ::= SEQUENCE {
6083 : extnID OBJECT IDENTIFIER,
6084 : critical BOOLEAN DEFAULT FALSE,
6085 : extnValue OCTET STRING }
6086 : */
6087 :
6088 : typedef struct {
6089 : SECItem extnID;
6090 : SECItem critical;
6091 : SECItem extnValue;
6092 : } Extension;
6093 :
6094 : /*
6095 : Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
6096 : */
6097 :
6098 : /*
6099 : TSAPolicyId ::= OBJECT IDENTIFIER
6100 :
6101 : TimeStampReq ::= SEQUENCE {
6102 : version INTEGER { v1(1) },
6103 : messageImprint MessageImprint,
6104 : --a hash algorithm OID and the hash value of the data to be
6105 : --time-stamped
6106 : reqPolicy TSAPolicyId OPTIONAL,
6107 : nonce INTEGER OPTIONAL,
6108 : certReq BOOLEAN DEFAULT FALSE,
6109 : extensions [0] IMPLICIT Extensions OPTIONAL }
6110 : */
6111 :
6112 : typedef struct {
6113 : SECItem version;
6114 : MessageImprint messageImprint;
6115 : SECItem reqPolicy;
6116 : SECItem nonce;
6117 : SECItem certReq;
6118 : Extension *extensions;
6119 : } TimeStampReq;
6120 :
6121 : // (Partial) ASN.1 for the time stamp responce. Very complicated. Pulled
6122 : // together from varuous RFCs.
6123 :
6124 : /*
6125 : Accuracy ::= SEQUENCE {
6126 : seconds INTEGER OPTIONAL,
6127 : millis [0] INTEGER (1..999) OPTIONAL,
6128 : micros [1] INTEGER (1..999) OPTIONAL }
6129 :
6130 : PKIStatus ::= INTEGER {
6131 : granted (0),
6132 : -- when the PKIStatus contains the value zero a TimeStampToken, as requested, is present.
6133 : grantedWithMods (1),
6134 : -- when the PKIStatus contains the value one a TimeStampToken, with modifications, is present.
6135 : rejection (2),
6136 : waiting (3),
6137 : revocationWarning (4),
6138 : -- this message contains a warning that a revocation is
6139 : -- imminent
6140 : revocationNotification (5)
6141 : -- notification that a revocation has occurred
6142 : }
6143 :
6144 : PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String
6145 : -- text encoded as UTF-8 String [RFC3629] (note: each
6146 : -- UTF8String MAY include an [RFC3066] language tag
6147 : -- to indicate the language of the contained text
6148 : -- see [RFC2482] for details)
6149 :
6150 : PKIFailureInfo ::= BIT STRING {
6151 : badAlg (0),
6152 : -- unrecognized or unsupported Algorithm Identifier
6153 : badRequest (2),
6154 : -- transaction not permitted or supported
6155 : badDataFormat (5),
6156 : -- the data submitted has the wrong format
6157 : timeNotAvailable (14),
6158 : -- the TSA's time source is not available
6159 : unacceptedPolicy (15),
6160 : -- the requested TSA policy is not supported by the TSA.
6161 : unacceptedExtension (16),
6162 : -- the requested extension is not supported by the TSA.
6163 : addInfoNotAvailable (17),
6164 : -- the additional information requested could not be understood
6165 : -- or is not available
6166 : systemFailure (25)
6167 : -- the request cannot be handled due to system failure
6168 : }
6169 :
6170 : PKIStatusInfo ::= SEQUENCE {
6171 : status PKIStatus,
6172 : statusString PKIFreeText OPTIONAL,
6173 : failInfo PKIFailureInfo OPTIONAL }
6174 :
6175 : ContentType ::= OBJECT IDENTIFIER
6176 :
6177 : ContentInfo ::= SEQUENCE {
6178 : contentType ContentType,
6179 : content [0] EXPLICIT ANY DEFINED BY contentType }
6180 :
6181 : CMSVersion ::= INTEGER { v0(0), v1(1), v2(2), v3(3), v4(4), v5(5) }
6182 :
6183 : DigestAlgorithmIdentifier ::= AlgorithmIdentifier
6184 :
6185 : DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
6186 :
6187 : ContentType ::= OBJECT IDENTIFIER
6188 :
6189 : EncapsulatedContentInfo ::= SEQUENCE {
6190 : eContentType ContentType,
6191 : eContent [0] EXPLICIT OCTET STRING OPTIONAL }
6192 :
6193 : OtherCertificateFormat ::= SEQUENCE {
6194 : otherCertFormat OBJECT IDENTIFIER,
6195 : otherCert ANY DEFINED BY otherCertFormat }
6196 :
6197 : CertificateChoices ::= CHOICE {
6198 : certificate Certificate,
6199 : extendedCertificate [0] IMPLICIT ExtendedCertificate, -- Obsolete
6200 : v1AttrCert [1] IMPLICIT AttributeCertificateV1, -- Obsolete
6201 : v2AttrCert [2] IMPLICIT AttributeCertificateV2,
6202 : other [3] IMPLICIT OtherCertificateFormat }
6203 :
6204 : CertificateSet ::= SET OF CertificateChoices
6205 :
6206 : CertificateList ::= SEQUENCE {
6207 : tbsCertList TBSCertList,
6208 : signatureAlgorithm AlgorithmIdentifier,
6209 : signatureValue BIT STRING }
6210 :
6211 : TBSCertList ::= SEQUENCE {
6212 : version Version OPTIONAL,
6213 : -- if present, MUST be v2
6214 : signature AlgorithmIdentifier,
6215 : issuer Name,
6216 : thisUpdate Time,
6217 : nextUpdate Time OPTIONAL,
6218 : revokedCertificates SEQUENCE OF SEQUENCE {
6219 : userCertificate CertificateSerialNumber,
6220 : revocationDate Time,
6221 : crlEntryExtensions Extensions OPTIONAL
6222 : -- if present, version MUST be v2
6223 : } OPTIONAL,
6224 : crlExtensions [0] EXPLICIT Extensions OPTIONAL
6225 : -- if present, version MUST be v2
6226 : }
6227 :
6228 : OtherRevocationInfoFormat ::= SEQUENCE {
6229 : otherRevInfoFormat OBJECT IDENTIFIER,
6230 : otherRevInfo ANY DEFINED BY otherRevInfoFormat }
6231 :
6232 : RevocationInfoChoice ::= CHOICE {
6233 : crl CertificateList,
6234 : other [1] IMPLICIT OtherRevocationInfoFormat }
6235 :
6236 : RevocationInfoChoices ::= SET OF RevocationInfoChoice
6237 :
6238 : SignerIdentifier ::= CHOICE {
6239 : issuerAndSerialNumber IssuerAndSerialNumber,
6240 : subjectKeyIdentifier [0] SubjectKeyIdentifier }
6241 :
6242 : AttributeValue ::= ANY
6243 :
6244 : Attribute ::= SEQUENCE {
6245 : attrType OBJECT IDENTIFIER,
6246 : attrValues SET OF AttributeValue }
6247 :
6248 : SignedAttributes ::= SET SIZE (1..MAX) OF Attribute
6249 :
6250 : SignatureValue ::= OCTET STRING
6251 :
6252 : UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute
6253 :
6254 : SignerInfo ::= SEQUENCE {
6255 : version CMSVersion,
6256 : sid SignerIdentifier,
6257 : digestAlgorithm DigestAlgorithmIdentifier,
6258 : signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
6259 : signatureAlgorithm SignatureAlgorithmIdentifier,
6260 : signature SignatureValue,
6261 : unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
6262 :
6263 : SignerInfos ::= SET OF SignerInfo
6264 :
6265 : SignedData ::= SEQUENCE {
6266 : version CMSVersion,
6267 : digestAlgorithms DigestAlgorithmIdentifiers,
6268 : encapContentInfo EncapsulatedContentInfo,
6269 : certificates [0] IMPLICIT CertificateSet OPTIONAL,
6270 : crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
6271 : signerInfos SignerInfos }
6272 :
6273 : TimeStampToken ::= ContentInfo
6274 : -- contentType is id-signedData as defined in [CMS]
6275 : -- content is SignedData as defined in([CMS])
6276 : -- eContentType within SignedData is id-ct-TSTInfo
6277 : -- eContent within SignedData is TSTInfo
6278 :
6279 : TSTInfo ::= SEQUENCE {
6280 : version INTEGER { v1(1) },
6281 : policy TSAPolicyId,
6282 : messageImprint MessageImprint,
6283 : -- MUST have the same value as the similar field in
6284 : -- TimeStampReq
6285 : serialNumber INTEGER,
6286 : -- Time-Stamping users MUST be ready to accommodate integers
6287 : -- up to 160 bits.
6288 : genTime GeneralizedTime,
6289 : accuracy Accuracy OPTIONAL,
6290 : ordering BOOLEAN DEFAULT FALSE,
6291 : nonce INTEGER OPTIONAL,
6292 : -- MUST be present if the similar field was present
6293 : -- in TimeStampReq. In that case it MUST have the same value.
6294 : tsa [0] GeneralName OPTIONAL,
6295 : extensions [1] IMPLICIT Extensions OPTIONAL }
6296 :
6297 : TimeStampResp ::= SEQUENCE {
6298 : status PKIStatusInfo,
6299 : timeStampToken TimeStampToken OPTIONAL }
6300 : */
6301 :
6302 : const SEC_ASN1Template MessageImprint_Template[] =
6303 : {
6304 : { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(MessageImprint) },
6305 : { SEC_ASN1_INLINE, offsetof(MessageImprint, hashAlgorithm), SECOID_AlgorithmIDTemplate, 0 },
6306 : { SEC_ASN1_OCTET_STRING, offsetof(MessageImprint, hashedMessage), 0, 0 },
6307 : { 0, 0, 0, 0 }
6308 : };
6309 :
6310 : const SEC_ASN1Template Extension_Template[] =
6311 : {
6312 : { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(Extension) },
6313 : { SEC_ASN1_OBJECT_ID, offsetof(Extension, extnID), 0, 0 },
6314 : { SEC_ASN1_BOOLEAN, offsetof(Extension, critical), 0, 0 },
6315 : { SEC_ASN1_OCTET_STRING, offsetof(Extension, extnValue), 0, 0 },
6316 : { 0, 0, 0, 0 }
6317 : };
6318 :
6319 : const SEC_ASN1Template Extensions_Template[] =
6320 : {
6321 : { SEC_ASN1_SEQUENCE_OF, 0, Extension_Template, 0 }
6322 : };
6323 :
6324 : const SEC_ASN1Template TimeStampReq_Template[] =
6325 : {
6326 : { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(TimeStampReq) },
6327 : { SEC_ASN1_INTEGER, offsetof(TimeStampReq, version), 0, 0 },
6328 : { SEC_ASN1_INLINE, offsetof(TimeStampReq, messageImprint), MessageImprint_Template, 0 },
6329 : { SEC_ASN1_OBJECT_ID | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, reqPolicy), 0, 0 },
6330 : { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, nonce), 0, 0 },
6331 : { SEC_ASN1_BOOLEAN | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, certReq), 0, 0 },
6332 : { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(TimeStampReq, extensions), Extensions_Template, 0 },
6333 : { 0, 0, 0, 0 }
6334 : };
6335 :
6336 : typedef struct {
6337 : SECItem status;
6338 : SECItem statusString;
6339 : SECItem failInfo;
6340 : } PKIStatusInfo;
6341 :
6342 : const SEC_ASN1Template PKIStatusInfo_Template[] =
6343 : {
6344 : { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(PKIStatusInfo) },
6345 : { SEC_ASN1_INTEGER, offsetof(PKIStatusInfo, status), 0, 0 },
6346 : { SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, statusString), 0, 0 },
6347 : { SEC_ASN1_BIT_STRING | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, failInfo), 0, 0 },
6348 : { 0, 0, 0, 0 }
6349 : };
6350 :
6351 : const SEC_ASN1Template Any_Template[] =
6352 : {
6353 : { SEC_ASN1_ANY, 0, NULL, sizeof(SECItem) }
6354 : };
6355 :
6356 : typedef struct {
6357 : PKIStatusInfo status;
6358 : SECItem timeStampToken;
6359 : } TimeStampResp;
6360 :
6361 : const SEC_ASN1Template TimeStampResp_Template[] =
6362 : {
6363 : { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(TimeStampResp) },
6364 : { SEC_ASN1_INLINE, offsetof(TimeStampResp, status), PKIStatusInfo_Template, 0 },
6365 : { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, offsetof(TimeStampResp, timeStampToken), Any_Template, 0 },
6366 : { 0, 0, 0, 0 }
6367 : };
6368 :
6369 : /* Will see if these are needed or not
6370 : typedef struct {
6371 : SECItem seconds;
6372 : SECItem millis;
6373 : SECItem micros;
6374 : } Accuracy;
6375 :
6376 : const SEC_ASN1Template Integer_Template[] =
6377 : {
6378 : { SEC_ASN1_INTEGER, 0, NULL, sizeof(SECItem) }
6379 : };
6380 :
6381 : const SEC_ASN1Template Accuracy_Template[] =
6382 : {
6383 : { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(Accuracy) },
6384 : { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(Accuracy, seconds), 0, 0 },
6385 : { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(Accuracy, millis), Integer_Template, 0 },
6386 : { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 1, offsetof(Accuracy, micros), Integer_Template, 0 },
6387 : { 0, 0, 0, 0 }
6388 : };
6389 : */
6390 :
6391 0 : size_t AppendToBuffer(char *ptr, size_t size, size_t nmemb, void *userdata)
6392 : {
6393 0 : OStringBuffer *pBuffer = static_cast<OStringBuffer*>(userdata);
6394 0 : pBuffer->append(ptr, size*nmemb);
6395 :
6396 0 : return size*nmemb;
6397 : }
6398 :
6399 0 : OUString PKIStatusToString(int n)
6400 : {
6401 0 : switch (n)
6402 : {
6403 0 : case 0: return OUString("granted");
6404 0 : case 1: return OUString("grantedWithMods");
6405 0 : case 2: return OUString("rejection");
6406 0 : case 3: return OUString("waiting");
6407 0 : case 4: return OUString("revocationWarning");
6408 0 : case 5: return OUString("revocationNotification");
6409 0 : default: return "unknown (" + OUString::number(n) + ")";
6410 : }
6411 : }
6412 :
6413 0 : OUString PKIStatusInfoToString(const PKIStatusInfo& rStatusInfo)
6414 : {
6415 0 : OUString result;
6416 :
6417 0 : result += "{status=";
6418 0 : if (rStatusInfo.status.len == 1)
6419 0 : result += PKIStatusToString(rStatusInfo.status.data[0]);
6420 : else
6421 0 : result += "unknown (len=" + OUString::number(rStatusInfo.status.len);
6422 :
6423 : // FIXME: Perhaps look at rStatusInfo.statusString.data but note
6424 : // that we of course can't assume it contains proper UTF-8. After
6425 : // all, it is data from an external source. Also, RFC3161 claims
6426 : // it should be a SEQUENCE (1..MAX) OF UTF8String, but another
6427 : // source claimed it would be a single UTF8String, hmm?
6428 :
6429 : // FIXME: Worth it to decode failInfo to cleartext, probably not at least as long as this is only for a SAL_INFO
6430 :
6431 0 : result += "}";
6432 :
6433 0 : return result;
6434 : }
6435 :
6436 : // SEC_StringToOID() and NSS_CMSSignerInfo_AddUnauthAttr() are
6437 : // not exported from libsmime, so copy them here. Sigh.
6438 :
6439 : SECStatus
6440 0 : my_SEC_StringToOID(PLArenaPool *pool, SECItem *to, const char *from, PRUint32 len)
6441 : {
6442 0 : PRUint32 decimal_numbers = 0;
6443 0 : PRUint32 result_bytes = 0;
6444 : SECStatus rv;
6445 : PRUint8 result[1024];
6446 :
6447 : static const PRUint32 max_decimal = (0xffffffff / 10);
6448 : static const char OIDstring[] = {"OID."};
6449 :
6450 0 : if (!from || !to) {
6451 0 : PORT_SetError(SEC_ERROR_INVALID_ARGS);
6452 0 : return SECFailure;
6453 : }
6454 0 : if (!len) {
6455 0 : len = PL_strlen(from);
6456 : }
6457 0 : if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4)) {
6458 0 : from += 4; /* skip leading "OID." if present */
6459 0 : len -= 4;
6460 : }
6461 0 : if (!len) {
6462 : bad_data:
6463 0 : PORT_SetError(SEC_ERROR_BAD_DATA);
6464 0 : return SECFailure;
6465 : }
6466 0 : do {
6467 0 : PRUint32 decimal = 0;
6468 0 : while (len > 0 && isdigit(*from)) {
6469 0 : PRUint32 addend = (*from++ - '0');
6470 0 : --len;
6471 0 : if (decimal > max_decimal) /* overflow */
6472 0 : goto bad_data;
6473 0 : decimal = (decimal * 10) + addend;
6474 0 : if (decimal < addend) /* overflow */
6475 0 : goto bad_data;
6476 : }
6477 0 : if (len != 0 && *from != '.') {
6478 0 : goto bad_data;
6479 : }
6480 0 : if (decimal_numbers == 0) {
6481 0 : if (decimal > 2)
6482 0 : goto bad_data;
6483 0 : result[0] = decimal * 40;
6484 0 : result_bytes = 1;
6485 0 : } else if (decimal_numbers == 1) {
6486 0 : if (decimal > 40)
6487 0 : goto bad_data;
6488 0 : result[0] += decimal;
6489 : } else {
6490 : /* encode the decimal number, */
6491 : PRUint8 * rp;
6492 0 : PRUint32 num_bytes = 0;
6493 0 : PRUint32 tmp = decimal;
6494 0 : while (tmp) {
6495 0 : num_bytes++;
6496 0 : tmp >>= 7;
6497 : }
6498 0 : if (!num_bytes )
6499 0 : ++num_bytes; /* use one byte for a zero value */
6500 0 : if (num_bytes + result_bytes > sizeof result)
6501 0 : goto bad_data;
6502 0 : tmp = num_bytes;
6503 0 : rp = result + result_bytes - 1;
6504 0 : rp[tmp] = (PRUint8)(decimal & 0x7f);
6505 0 : decimal >>= 7;
6506 0 : while (--tmp > 0) {
6507 0 : rp[tmp] = (PRUint8)(decimal | 0x80);
6508 0 : decimal >>= 7;
6509 : }
6510 0 : result_bytes += num_bytes;
6511 : }
6512 0 : ++decimal_numbers;
6513 0 : if (len > 0) { /* skip trailing '.' */
6514 0 : ++from;
6515 0 : --len;
6516 : }
6517 : } while (len > 0);
6518 : /* now result contains result_bytes of data */
6519 0 : if (to->data && to->len >= result_bytes) {
6520 0 : PORT_Memcpy(to->data, result, to->len = result_bytes);
6521 0 : rv = SECSuccess;
6522 : } else {
6523 0 : SECItem result_item = {siBuffer, NULL, 0 };
6524 0 : result_item.data = result;
6525 0 : result_item.len = result_bytes;
6526 0 : rv = SECITEM_CopyItem(pool, to, &result_item);
6527 : }
6528 0 : return rv;
6529 : }
6530 :
6531 : NSSCMSAttribute *
6532 0 : my_NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only)
6533 : {
6534 : SECOidData *oid;
6535 : NSSCMSAttribute *attr1, *attr2;
6536 :
6537 0 : if (attrs == NULL)
6538 0 : return NULL;
6539 :
6540 0 : oid = SECOID_FindOIDByTag(oidtag);
6541 0 : if (oid == NULL)
6542 0 : return NULL;
6543 :
6544 0 : while ((attr1 = *attrs++) != NULL) {
6545 0 : if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data,
6546 : oid->oid.data,
6547 0 : oid->oid.len) == 0)
6548 0 : break;
6549 : }
6550 :
6551 0 : if (attr1 == NULL)
6552 0 : return NULL;
6553 :
6554 0 : if (!only)
6555 0 : return attr1;
6556 :
6557 0 : while ((attr2 = *attrs++) != NULL) {
6558 0 : if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data,
6559 : oid->oid.data,
6560 0 : oid->oid.len) == 0)
6561 0 : break;
6562 : }
6563 :
6564 0 : if (attr2 != NULL)
6565 0 : return NULL;
6566 :
6567 0 : return attr1;
6568 : }
6569 :
6570 : SECStatus
6571 0 : my_NSS_CMSArray_Add(PLArenaPool *poolp, void ***array, void *obj)
6572 : {
6573 : void **p;
6574 : int n;
6575 : void **dest;
6576 :
6577 : PORT_Assert(array != NULL);
6578 0 : if (array == NULL)
6579 0 : return SECFailure;
6580 :
6581 0 : if (*array == NULL) {
6582 0 : dest = static_cast<void **>(PORT_ArenaAlloc(poolp, 2 * sizeof(void *)));
6583 0 : n = 0;
6584 : } else {
6585 0 : n = 0; p = *array;
6586 0 : while (*p++)
6587 0 : n++;
6588 : dest = static_cast<void **>(PORT_ArenaGrow (poolp,
6589 : *array,
6590 0 : (n + 1) * sizeof(void *),
6591 0 : (n + 2) * sizeof(void *)));
6592 : }
6593 :
6594 0 : if (dest == NULL)
6595 0 : return SECFailure;
6596 :
6597 0 : dest[n] = obj;
6598 0 : dest[n+1] = NULL;
6599 0 : *array = dest;
6600 0 : return SECSuccess;
6601 : }
6602 :
6603 : SECOidTag
6604 0 : my_NSS_CMSAttribute_GetType(NSSCMSAttribute *attr)
6605 : {
6606 : SECOidData *typetag;
6607 :
6608 0 : typetag = SECOID_FindOID(&(attr->type));
6609 0 : if (typetag == NULL)
6610 0 : return SEC_OID_UNKNOWN;
6611 :
6612 0 : return typetag->offset;
6613 : }
6614 :
6615 : SECStatus
6616 0 : my_NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr)
6617 : {
6618 : NSSCMSAttribute *oattr;
6619 : void *mark;
6620 : SECOidTag type;
6621 :
6622 0 : mark = PORT_ArenaMark(poolp);
6623 :
6624 : /* find oidtag of attr */
6625 0 : type = my_NSS_CMSAttribute_GetType(attr);
6626 :
6627 : /* see if we have one already */
6628 0 : oattr = my_NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
6629 : PORT_Assert (oattr == NULL);
6630 0 : if (oattr != NULL)
6631 0 : goto loser; /* XXX or would it be better to replace it? */
6632 :
6633 : /* no, shove it in */
6634 0 : if (my_NSS_CMSArray_Add(poolp, reinterpret_cast<void ***>(attrs), static_cast<void *>(attr)) != SECSuccess)
6635 0 : goto loser;
6636 :
6637 0 : PORT_ArenaUnmark(poolp, mark);
6638 0 : return SECSuccess;
6639 :
6640 : loser:
6641 0 : PORT_ArenaRelease(poolp, mark);
6642 0 : return SECFailure;
6643 : }
6644 :
6645 : SECStatus
6646 0 : my_NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
6647 : {
6648 0 : return my_NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
6649 : }
6650 :
6651 0 : NSSCMSMessage *CreateCMSMessage(PRTime time,
6652 : NSSCMSSignedData **cms_sd,
6653 : NSSCMSSignerInfo **cms_signer,
6654 : CERTCertificate *cert,
6655 : SECItem *digest)
6656 : {
6657 0 : NSSCMSMessage *result = NSS_CMSMessage_Create(NULL);
6658 0 : if (!result)
6659 : {
6660 : SAL_WARN("vcl.pdfwriter", "NSS_CMSMessage_Create failed");
6661 0 : return NULL;
6662 : }
6663 :
6664 0 : *cms_sd = NSS_CMSSignedData_Create(result);
6665 0 : if (!*cms_sd)
6666 : {
6667 : SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_Create failed");
6668 0 : NSS_CMSMessage_Destroy(result);
6669 0 : return NULL;
6670 : }
6671 :
6672 0 : NSSCMSContentInfo *cms_cinfo = NSS_CMSMessage_GetContentInfo(result);
6673 0 : if (NSS_CMSContentInfo_SetContent_SignedData(result, cms_cinfo, *cms_sd) != SECSuccess)
6674 : {
6675 : SAL_WARN("vcl.pdfwriter", "NSS_CMSContentInfo_SetContent_SignedData failed");
6676 0 : NSS_CMSSignedData_Destroy(*cms_sd);
6677 0 : NSS_CMSMessage_Destroy(result);
6678 0 : return NULL;
6679 : }
6680 :
6681 0 : cms_cinfo = NSS_CMSSignedData_GetContentInfo(*cms_sd);
6682 :
6683 : // Attach NULL data as detached data
6684 0 : if (NSS_CMSContentInfo_SetContent_Data(result, cms_cinfo, NULL, PR_TRUE) != SECSuccess)
6685 : {
6686 : SAL_WARN("vcl.pdfwriter", "NSS_CMSContentInfo_SetContent_Data failed");
6687 0 : NSS_CMSSignedData_Destroy(*cms_sd);
6688 0 : NSS_CMSMessage_Destroy(result);
6689 0 : return NULL;
6690 : }
6691 :
6692 0 : *cms_signer = NSS_CMSSignerInfo_Create(result, cert, SEC_OID_SHA1);
6693 0 : if (!*cms_signer)
6694 : {
6695 : SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_Create failed");
6696 0 : NSS_CMSSignedData_Destroy(*cms_sd);
6697 0 : NSS_CMSMessage_Destroy(result);
6698 0 : return NULL;
6699 : }
6700 :
6701 0 : if (NSS_CMSSignerInfo_AddSigningTime(*cms_signer, time) != SECSuccess)
6702 : {
6703 : SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_AddSigningTime failed");
6704 0 : NSS_CMSSignedData_Destroy(*cms_sd);
6705 0 : NSS_CMSMessage_Destroy(result);
6706 0 : return NULL;
6707 : }
6708 :
6709 0 : if (NSS_CMSSignerInfo_IncludeCerts(*cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess)
6710 : {
6711 : SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_IncludeCerts failed");
6712 0 : NSS_CMSSignedData_Destroy(*cms_sd);
6713 0 : NSS_CMSMessage_Destroy(result);
6714 0 : return NULL;
6715 : }
6716 :
6717 0 : if (NSS_CMSSignedData_AddCertificate(*cms_sd, cert) != SECSuccess)
6718 : {
6719 : SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_AddCertificate failed");
6720 0 : NSS_CMSSignedData_Destroy(*cms_sd);
6721 0 : NSS_CMSMessage_Destroy(result);
6722 0 : return NULL;
6723 : }
6724 :
6725 0 : if (NSS_CMSSignedData_AddSignerInfo(*cms_sd, *cms_signer) != SECSuccess)
6726 : {
6727 : SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_AddSignerInfo failed");
6728 0 : NSS_CMSSignedData_Destroy(*cms_sd);
6729 0 : NSS_CMSMessage_Destroy(result);
6730 0 : return NULL;
6731 : }
6732 :
6733 0 : if (NSS_CMSSignedData_SetDigestValue(*cms_sd, SEC_OID_SHA1, digest) != SECSuccess)
6734 : {
6735 : SAL_WARN("vcl.pdfwriter", "NSS_CMSSignedData_SetDigestValue failed");
6736 0 : NSS_CMSSignedData_Destroy(*cms_sd);
6737 0 : NSS_CMSMessage_Destroy(result);
6738 0 : return NULL;
6739 : }
6740 :
6741 0 : return result;
6742 : }
6743 :
6744 : #if 0
6745 : {
6746 : #endif
6747 : } // anonymous namespace
6748 :
6749 : #endif // !defined(ANDROID) && !defined(IOS) && !defined(_WIN32)
6750 :
6751 : #ifdef _WIN32
6752 :
6753 : typedef BOOL (WINAPI *PointerTo_CryptRetrieveTimeStamp)(LPCWSTR wszUrl,
6754 : DWORD dwRetrievalFlags,
6755 : DWORD dwTimeout,
6756 : LPCSTR pszHashId,
6757 : const CRYPT_TIMESTAMP_PARA *pPara,
6758 : const BYTE *pbData,
6759 : DWORD cbData,
6760 : PCRYPT_TIMESTAMP_CONTEXT *ppTsContext,
6761 : PCCERT_CONTEXT *ppTsSigner,
6762 : HCERTSTORE phStore);
6763 :
6764 : namespace {
6765 :
6766 : OUString WindowsError(DWORD nErrorCode)
6767 : {
6768 : LPWSTR pMsgBuf;
6769 :
6770 : if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
6771 : NULL,
6772 : nErrorCode,
6773 : MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
6774 : (LPWSTR)&pMsgBuf,
6775 : 0,
6776 : NULL) == 0)
6777 : return OUString::number(nErrorCode, 16);
6778 :
6779 : if (pMsgBuf[wcslen(pMsgBuf)-1] == '\n')
6780 : pMsgBuf[wcslen(pMsgBuf)-1] = '\0';
6781 :
6782 : OUString result(pMsgBuf);
6783 :
6784 : LocalFree(pMsgBuf);
6785 :
6786 : return result;
6787 : }
6788 :
6789 : }
6790 :
6791 : #endif
6792 :
6793 0 : bool PDFWriterImpl::finalizeSignature()
6794 : {
6795 :
6796 0 : if (!m_aContext.SignCertificate.is())
6797 0 : return false;
6798 :
6799 : // 1- calculate last ByteRange value
6800 0 : sal_uInt64 nOffset = ~0U;
6801 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) );
6802 :
6803 0 : sal_Int64 nLastByteRangeNo = nOffset - (m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1);
6804 :
6805 : // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position
6806 0 : sal_uInt64 nWritten = 0;
6807 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureLastByteRangeNoOffset) ) );
6808 0 : OStringBuffer aByteRangeNo( 256 );
6809 0 : aByteRangeNo.append( nLastByteRangeNo, 10);
6810 0 : aByteRangeNo.append( " ]" );
6811 :
6812 0 : if (m_aFile.write(aByteRangeNo.getStr(), aByteRangeNo.getLength(), nWritten) != osl::File::E_None)
6813 : {
6814 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
6815 0 : return false;
6816 : }
6817 :
6818 : // 3- create the PKCS#7 object using NSS
6819 0 : com::sun::star::uno::Sequence< sal_Int8 > derEncoded = m_aContext.SignCertificate->getEncoded();
6820 :
6821 0 : if (!derEncoded.hasElements())
6822 0 : return false;
6823 :
6824 0 : sal_Int8* n_derArray = derEncoded.getArray();
6825 0 : sal_Int32 n_derLength = derEncoded.getLength();
6826 :
6827 : #ifndef _WIN32
6828 :
6829 0 : CERTCertificate *cert = CERT_DecodeCertFromPackage(reinterpret_cast<char *>(n_derArray), n_derLength);
6830 :
6831 0 : if (!cert)
6832 : {
6833 : SAL_WARN("vcl.pdfwriter", "CERT_DecodeCertFromPackage failed");
6834 0 : return false;
6835 : }
6836 :
6837 : // Prepare buffer and calculate PDF file digest
6838 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, 0)) );
6839 :
6840 0 : HashContextScope hc(HASH_Create(HASH_AlgSHA1));
6841 0 : if (!hc.get())
6842 : {
6843 : SAL_WARN("vcl.pdfwriter", "HASH_Create failed");
6844 0 : return false;
6845 : }
6846 :
6847 0 : HASH_Begin(hc.get());
6848 :
6849 0 : boost::scoped_array<char> buffer(new char[m_nSignatureContentOffset + 1]);
6850 : sal_uInt64 bytesRead;
6851 :
6852 : //FIXME: Check if SHA1 is calculated from the correct byterange
6853 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.read(buffer.get(), m_nSignatureContentOffset - 1 , bytesRead)) );
6854 0 : if (bytesRead != (sal_uInt64)m_nSignatureContentOffset - 1)
6855 : SAL_WARN("vcl.pdfwriter", "First buffer read failed");
6856 :
6857 0 : HASH_Update(hc.get(), reinterpret_cast<const unsigned char*>(buffer.get()), bytesRead);
6858 :
6859 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1)) );
6860 0 : buffer.reset(new char[nLastByteRangeNo + 1]);
6861 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.read(buffer.get(), nLastByteRangeNo, bytesRead)) );
6862 0 : if (bytesRead != (sal_uInt64) nLastByteRangeNo)
6863 : SAL_WARN("vcl.pdfwriter", "Second buffer read failed");
6864 :
6865 0 : HASH_Update(hc.get(), reinterpret_cast<const unsigned char*>(buffer.get()), bytesRead);
6866 :
6867 : SECItem digest;
6868 : unsigned char hash[SHA1_LENGTH];
6869 0 : digest.data = hash;
6870 0 : HASH_End(hc.get(), digest.data, &digest.len, SHA1_LENGTH);
6871 0 : hc.clear();
6872 :
6873 : #ifdef DBG_UTIL
6874 : {
6875 : FILE *out = fopen("PDFWRITER.hash.data", "wb");
6876 : fwrite(hash, SHA1_LENGTH, 1, out);
6877 : fclose(out);
6878 : }
6879 : #endif
6880 :
6881 0 : PRTime now = PR_Now();
6882 : NSSCMSSignedData *cms_sd;
6883 : NSSCMSSignerInfo *cms_signer;
6884 0 : NSSCMSMessage *cms_msg = CreateCMSMessage(now, &cms_sd, &cms_signer, cert, &digest);
6885 0 : if (!cms_msg)
6886 0 : return false;
6887 :
6888 0 : char *pass(strdup(OUStringToOString( m_aContext.SignPassword, RTL_TEXTENCODING_UTF8 ).getStr()));
6889 :
6890 : TimeStampReq src;
6891 0 : OStringBuffer response_buffer;
6892 : TimeStampResp response;
6893 : SECItem response_item;
6894 : NSSCMSAttribute timestamp;
6895 : SECItem values[2];
6896 : SECItem *valuesp[2];
6897 0 : valuesp[0] = values;
6898 0 : valuesp[1] = NULL;
6899 : SECOidData typetag;
6900 :
6901 0 : if( !m_aContext.SignTSA.isEmpty() )
6902 : {
6903 : // Create another CMS message with the same contents as cms_msg, because it doesn't seem
6904 : // possible to encode a message twice (once to get something to timestamp, and then after
6905 : // adding the timestamp attribute).
6906 :
6907 : NSSCMSSignedData *ts_cms_sd;
6908 : NSSCMSSignerInfo *ts_cms_signer;
6909 0 : NSSCMSMessage *ts_cms_msg = CreateCMSMessage(now, &ts_cms_sd, &ts_cms_signer, cert, &digest);
6910 0 : if (!ts_cms_msg)
6911 : {
6912 0 : free(pass);
6913 0 : return false;
6914 : }
6915 :
6916 : SECItem ts_cms_output;
6917 0 : ts_cms_output.data = 0;
6918 0 : ts_cms_output.len = 0;
6919 0 : PLArenaPool *ts_arena = PORT_NewArena(10000);
6920 : NSSCMSEncoderContext *ts_cms_ecx;
6921 0 : ts_cms_ecx = NSS_CMSEncoder_Start(ts_cms_msg, NULL, NULL, &ts_cms_output, ts_arena, PDFSigningPKCS7PasswordCallback, pass, NULL, NULL, NULL, NULL);
6922 :
6923 0 : if (NSS_CMSEncoder_Finish(ts_cms_ecx) != SECSuccess)
6924 : {
6925 : SAL_WARN("vcl.pdfwriter", "NSS_CMSEncoder_Finish failed");
6926 0 : free(pass);
6927 0 : return false;
6928 : }
6929 :
6930 : // I have compared the ts_cms_output produced here with the cms_output produced below, with
6931 : // the DONTCALLADDUNAUTHATTR env var set (i.e. without actually calling
6932 : // my_NSS_CMSSignerInfo_AddUnauthAttr()), and they are identical.
6933 :
6934 : #ifdef DBG_UTIL
6935 : {
6936 : FILE *out = fopen("PDFWRITER.ts_cms.data", "wb");
6937 : fwrite(ts_cms_output.data, ts_cms_output.len, 1, out);
6938 : fclose(out);
6939 : }
6940 : #endif
6941 :
6942 0 : HashContextScope ts_hc(HASH_Create(HASH_AlgSHA1));
6943 0 : if (!ts_hc.get())
6944 : {
6945 : SAL_WARN("vcl.pdfwriter", "HASH_Create failed");
6946 0 : free(pass);
6947 0 : return false;
6948 : }
6949 :
6950 0 : HASH_Begin(ts_hc.get());
6951 0 : HASH_Update(ts_hc.get(), ts_cms_signer->encDigest.data, ts_cms_signer->encDigest.len);
6952 : SECItem ts_digest;
6953 : unsigned char ts_hash[SHA1_LENGTH];
6954 0 : ts_digest.type = siBuffer;
6955 0 : ts_digest.data = ts_hash;
6956 0 : HASH_End(ts_hc.get(), ts_digest.data, &ts_digest.len, SHA1_LENGTH);
6957 0 : ts_hc.clear();
6958 :
6959 : #ifdef DBG_UTIL
6960 : {
6961 : FILE *out = fopen("PDFWRITER.ts_hash.data", "wb");
6962 : fwrite(ts_hash, SHA1_LENGTH, 1, out);
6963 : fclose(out);
6964 : }
6965 : #endif
6966 :
6967 0 : unsigned char cOne = 1;
6968 0 : src.version.type = siUnsignedInteger;
6969 0 : src.version.data = &cOne;
6970 0 : src.version.len = sizeof(cOne);
6971 :
6972 0 : src.messageImprint.hashAlgorithm.algorithm.data = NULL;
6973 0 : src.messageImprint.hashAlgorithm.parameters.data = NULL;
6974 0 : SECOID_SetAlgorithmID(NULL, &src.messageImprint.hashAlgorithm, SEC_OID_SHA1, NULL);
6975 0 : src.messageImprint.hashedMessage = ts_digest;
6976 :
6977 0 : src.reqPolicy.type = siBuffer;
6978 0 : src.reqPolicy.data = NULL;
6979 0 : src.reqPolicy.len = 0;
6980 :
6981 0 : unsigned int nNonce = comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32);
6982 0 : src.nonce.type = siUnsignedInteger;
6983 0 : src.nonce.data = reinterpret_cast<unsigned char*>(&nNonce);
6984 0 : src.nonce.len = sizeof(nNonce);
6985 :
6986 0 : src.certReq.type = siUnsignedInteger;
6987 0 : src.certReq.data = &cOne;
6988 0 : src.certReq.len = sizeof(cOne);
6989 :
6990 0 : src.extensions = NULL;
6991 :
6992 0 : SECItem* timestamp_request = SEC_ASN1EncodeItem(NULL, NULL, &src, TimeStampReq_Template);
6993 0 : if (timestamp_request == NULL)
6994 : {
6995 : SAL_WARN("vcl.pdfwriter", "SEC_ASN1EncodeItem failed");
6996 0 : free(pass);
6997 0 : return false;
6998 : }
6999 :
7000 0 : if (timestamp_request->data == NULL)
7001 : {
7002 : SAL_WARN("vcl.pdfwriter", "SEC_ASN1EncodeItem succeeded but got NULL data");
7003 0 : free(pass);
7004 0 : SECITEM_FreeItem(timestamp_request, PR_TRUE);
7005 0 : return false;
7006 : }
7007 :
7008 : SAL_INFO("vcl.pdfwriter", "request length=" << timestamp_request->len);
7009 :
7010 : #ifdef DBG_UTIL
7011 : {
7012 : FILE *out = fopen("PDFWRITER.timestampreq.data", "wb");
7013 : fwrite(timestamp_request->data, timestamp_request->len, 1, out);
7014 : fclose(out);
7015 : }
7016 : #endif
7017 :
7018 : // Send time stamp request to TSA server, receive response
7019 :
7020 0 : CURL* curl = curl_easy_init();
7021 : CURLcode rc;
7022 0 : struct curl_slist* slist = NULL;
7023 :
7024 0 : if (!curl)
7025 : {
7026 : SAL_WARN("vcl.pdfwriter", "curl_easy_init failed");
7027 0 : free(pass);
7028 0 : SECITEM_FreeItem(timestamp_request, PR_TRUE);
7029 0 : return false;
7030 : }
7031 :
7032 : SAL_INFO("vcl.pdfwriter", "Setting curl to verbose: " << (curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L) == CURLE_OK ? "OK" : "FAIL"));
7033 :
7034 0 : if ((rc = curl_easy_setopt(curl, CURLOPT_URL, OUStringToOString(m_aContext.SignTSA, RTL_TEXTENCODING_UTF8).getStr())) != CURLE_OK)
7035 : {
7036 : SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_URL) failed: " << curl_easy_strerror(rc));
7037 0 : free(pass);
7038 0 : curl_easy_cleanup(curl);
7039 0 : SECITEM_FreeItem(timestamp_request, PR_TRUE);
7040 0 : return false;
7041 : }
7042 :
7043 0 : slist = curl_slist_append(slist, "Content-Type: application/timestamp-query");
7044 0 : slist = curl_slist_append(slist, "Accept: application/timestamp-reply");
7045 :
7046 0 : if ((rc = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist)) != CURLE_OK)
7047 : {
7048 : SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_HTTPHEADER) failed: " << curl_easy_strerror(rc));
7049 0 : free(pass);
7050 0 : curl_slist_free_all(slist);
7051 0 : curl_easy_cleanup(curl);
7052 0 : SECITEM_FreeItem(timestamp_request, PR_TRUE);
7053 0 : return false;
7054 : }
7055 :
7056 0 : if ((rc = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<long>(timestamp_request->len))) != CURLE_OK ||
7057 0 : (rc = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, timestamp_request->data)) != CURLE_OK)
7058 : {
7059 : SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_POSTFIELDSIZE or CURLOPT_POSTFIELDS) failed: " << curl_easy_strerror(rc));
7060 0 : free(pass);
7061 0 : curl_easy_cleanup(curl);
7062 0 : SECITEM_FreeItem(timestamp_request, PR_TRUE);
7063 0 : return false;
7064 : }
7065 :
7066 0 : if ((rc = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buffer)) != CURLE_OK ||
7067 : (rc = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, AppendToBuffer)) != CURLE_OK)
7068 : {
7069 : SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_WRITEDATA or CURLOPT_WRITEFUNCTION) failed: " << curl_easy_strerror(rc));
7070 0 : free(pass);
7071 0 : curl_easy_cleanup(curl);
7072 0 : SECITEM_FreeItem(timestamp_request, PR_TRUE);
7073 0 : return false;
7074 : }
7075 :
7076 0 : if ((rc = curl_easy_setopt(curl, CURLOPT_POST, 1l)) != CURLE_OK)
7077 : {
7078 : SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_POST) failed: " << curl_easy_strerror(rc));
7079 0 : free(pass);
7080 0 : curl_easy_cleanup(curl);
7081 0 : SECITEM_FreeItem(timestamp_request, PR_TRUE);
7082 0 : return false;
7083 : }
7084 :
7085 : char error_buffer[CURL_ERROR_SIZE];
7086 0 : if ((rc = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer)) != CURLE_OK)
7087 : {
7088 : SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_ERRORBUFFER) failed: " << curl_easy_strerror(rc));
7089 0 : free(pass);
7090 0 : curl_easy_cleanup(curl);
7091 0 : SECITEM_FreeItem(timestamp_request, PR_TRUE);
7092 0 : return false;
7093 : }
7094 :
7095 : // Use a ten second timeout
7096 0 : if ((rc = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10l)) != CURLE_OK ||
7097 : (rc = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10l)) != CURLE_OK)
7098 : {
7099 : SAL_WARN("vcl.pdfwriter", "curl_easy_setopt(CURLOPT_TIMEOUT or CURLOPT_CONNECTTIMEOUT) failed: " << curl_easy_strerror(rc));
7100 0 : free(pass);
7101 0 : curl_easy_cleanup(curl);
7102 0 : SECITEM_FreeItem(timestamp_request, PR_TRUE);
7103 0 : return false;
7104 : }
7105 :
7106 0 : if (curl_easy_perform(curl) != CURLE_OK)
7107 : {
7108 : SAL_WARN("vcl.pdfwriter", "curl_easy_perform failed: " << error_buffer);
7109 0 : free(pass);
7110 0 : curl_easy_cleanup(curl);
7111 0 : SECITEM_FreeItem(timestamp_request, PR_TRUE);
7112 0 : return false;
7113 : }
7114 :
7115 : SAL_INFO("vcl.pdfwriter", "PDF signing: got response, length=" << response_buffer.getLength());
7116 :
7117 : #ifdef DBG_UTIL
7118 : {
7119 : FILE *out = fopen("PDFWRITER.reply.data", "wb");
7120 : fwrite(response_buffer.getStr(), response_buffer.getLength(), 1, out);
7121 : fclose(out);
7122 : }
7123 : #endif
7124 :
7125 0 : curl_slist_free_all(slist);
7126 0 : curl_easy_cleanup(curl);
7127 0 : SECITEM_FreeItem(timestamp_request, PR_TRUE);
7128 :
7129 0 : memset(&response, 0, sizeof(response));
7130 :
7131 0 : response_item.type = siBuffer;
7132 0 : response_item.data = reinterpret_cast<unsigned char*>(const_cast<char*>(response_buffer.getStr()));
7133 0 : response_item.len = response_buffer.getLength();
7134 :
7135 0 : if (SEC_ASN1DecodeItem(NULL, &response, TimeStampResp_Template, &response_item) != SECSuccess)
7136 : {
7137 : SAL_WARN("vcl.pdfwriter", "SEC_ASN1DecodeItem failed");
7138 0 : free(pass);
7139 0 : return false;
7140 : }
7141 :
7142 : SAL_INFO("vcl.pdfwriter", "TimeStampResp received and decoded, status=" << PKIStatusInfoToString(response.status));
7143 :
7144 0 : if (response.status.status.len != 1 ||
7145 0 : (response.status.status.data[0] != 0 && response.status.status.data[0] != 1))
7146 : {
7147 : SAL_WARN("vcl.pdfwriter", "Timestamp request was not granted");
7148 0 : free(pass);
7149 0 : return false;
7150 : }
7151 :
7152 : // timestamp.type filled in below
7153 :
7154 : // Not sure if we actually need two entries in the values array, now when valuesp is an
7155 : // array too, the pointer to the values array followed by a null pointer. But I don't feel
7156 : // like experimenting.
7157 0 : values[0] = response.timeStampToken;
7158 0 : values[1].type = siBuffer;
7159 0 : values[1].data = NULL;
7160 0 : values[1].len = 0;
7161 :
7162 0 : timestamp.values = valuesp;
7163 :
7164 0 : typetag.oid.data = NULL;
7165 : // id-aa-timeStampToken OBJECT IDENTIFIER ::= { iso(1)
7166 : // member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)
7167 : // smime(16) aa(2) 14 }
7168 0 : if (my_SEC_StringToOID(NULL, &typetag.oid, "1.2.840.113549.1.9.16.2.14", 0) != SECSuccess)
7169 : {
7170 : SAL_WARN("vcl.pdfwriter", "SEC_StringToOID failed");
7171 0 : free(pass);
7172 0 : return false;
7173 : }
7174 0 : typetag.offset = SEC_OID_UNKNOWN; // ???
7175 0 : typetag.desc = "id-aa-timeStampToken";
7176 0 : typetag.mechanism = CKM_SHA_1; // ???
7177 0 : typetag.supportedExtension = UNSUPPORTED_CERT_EXTENSION; // ???
7178 0 : timestamp.typeTag = &typetag;
7179 :
7180 0 : timestamp.type = typetag.oid; // ???
7181 :
7182 0 : timestamp.encoded = PR_TRUE; // ???
7183 :
7184 : #ifdef DBG_UTIL
7185 : if (getenv("DONTCALLADDUNAUTHATTR"))
7186 : ;
7187 : else
7188 : #endif
7189 0 : if (my_NSS_CMSSignerInfo_AddUnauthAttr(cms_signer, ×tamp) != SECSuccess)
7190 : {
7191 : SAL_WARN("vcl.pdfwriter", "NSS_CMSSignerInfo_AddUnauthAttr failed");
7192 0 : free(pass);
7193 0 : return false;
7194 0 : }
7195 : }
7196 :
7197 : SECItem cms_output;
7198 0 : cms_output.data = 0;
7199 0 : cms_output.len = 0;
7200 0 : PLArenaPool *arena = PORT_NewArena(10000);
7201 : NSSCMSEncoderContext *cms_ecx;
7202 :
7203 : // Possibly it would work to even just pass NULL for the password callback function and its
7204 : // argument here. After all, at least with the hardware token and associated software I tested
7205 : // with, the software itself pops up a dialog asking for the PIN (password). But I am not going
7206 : // to test it and risk locking up my token...
7207 :
7208 0 : cms_ecx = NSS_CMSEncoder_Start(cms_msg, NULL, NULL, &cms_output, arena, PDFSigningPKCS7PasswordCallback, pass, NULL, NULL, NULL, NULL);
7209 :
7210 0 : if (!cms_ecx)
7211 : {
7212 : SAL_WARN("vcl.pdfwriter", "NSS_CMSEncoder_Start failed");
7213 0 : free(pass);
7214 0 : return false;
7215 : }
7216 :
7217 0 : if (NSS_CMSEncoder_Finish(cms_ecx) != SECSuccess)
7218 : {
7219 : SAL_WARN("vcl.pdfwriter", "NSS_CMSEncoder_Finish failed");
7220 0 : free(pass);
7221 0 : return false;
7222 : }
7223 :
7224 0 : free(pass);
7225 :
7226 : #ifdef DBG_UTIL
7227 : {
7228 : FILE *out = fopen("PDFWRITER.cms.data", "wb");
7229 : fwrite(cms_output.data, cms_output.len, 1, out);
7230 : fclose(out);
7231 : }
7232 : #endif
7233 :
7234 0 : if (cms_output.len*2 > MAX_SIGNATURE_CONTENT_LENGTH)
7235 : {
7236 : SAL_WARN("vcl.pdfwriter", "Signature requires more space (" << cms_output.len*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH << ")");
7237 0 : NSS_CMSMessage_Destroy(cms_msg);
7238 0 : return false;
7239 : }
7240 :
7241 0 : OStringBuffer cms_hexbuffer;
7242 :
7243 0 : for (unsigned int i = 0; i < cms_output.len ; i++)
7244 0 : appendHex(cms_output.data[i], cms_hexbuffer);
7245 :
7246 : assert(cms_hexbuffer.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH);
7247 :
7248 : // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
7249 0 : nWritten = 0;
7250 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset)) );
7251 0 : m_aFile.write(cms_hexbuffer.getStr(), cms_hexbuffer.getLength(), nWritten);
7252 :
7253 0 : NSS_CMSMessage_Destroy(cms_msg);
7254 :
7255 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
7256 0 : return true;
7257 :
7258 : #else
7259 :
7260 : // Prepare buffer and calculate PDF file digest
7261 : CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, 0)) );
7262 :
7263 : boost::scoped_array<char> buffer1(new char[m_nSignatureContentOffset - 1]);
7264 : sal_uInt64 bytesRead1;
7265 :
7266 : if (osl::File::E_None != m_aFile.read(buffer1.get(), m_nSignatureContentOffset - 1 , bytesRead1) ||
7267 : bytesRead1 != (sal_uInt64)m_nSignatureContentOffset - 1)
7268 : {
7269 : SAL_WARN("vcl.pdfwriter", "First buffer read failed");
7270 : return false;
7271 : }
7272 :
7273 : boost::scoped_array<char> buffer2(new char[nLastByteRangeNo]);
7274 : sal_uInt64 bytesRead2;
7275 :
7276 : if (osl::File::E_None != m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1) ||
7277 : osl::File::E_None != m_aFile.read(buffer2.get(), nLastByteRangeNo, bytesRead2) ||
7278 : bytesRead2 != (sal_uInt64) nLastByteRangeNo)
7279 : {
7280 : SAL_WARN("vcl.pdfwriter", "Second buffer read failed");
7281 : return false;
7282 : }
7283 :
7284 : OString pass = OUStringToOString( m_aContext.SignPassword, RTL_TEXTENCODING_UTF8 );
7285 :
7286 : PCCERT_CONTEXT pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, reinterpret_cast<const BYTE*>(n_derArray), n_derLength);
7287 : if (pCertContext == NULL)
7288 : {
7289 : SAL_WARN("vcl.pdfwriter", "CertCreateCertificateContext failed: " << WindowsError(GetLastError()));
7290 : return false;
7291 : }
7292 :
7293 : CRYPT_SIGN_MESSAGE_PARA aPara;
7294 :
7295 : memset(&aPara, 0, sizeof(aPara));
7296 : aPara.cbSize = sizeof(aPara);
7297 : aPara.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
7298 : aPara.pSigningCert = pCertContext;
7299 : aPara.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA;
7300 : aPara.HashAlgorithm.Parameters.cbData = 0;
7301 : aPara.cMsgCert = 1;
7302 : aPara.rgpMsgCert = &pCertContext;
7303 :
7304 : HCRYPTPROV hCryptProv;
7305 : DWORD nKeySpec;
7306 : BOOL bFreeNeeded;
7307 :
7308 : if (!CryptAcquireCertificatePrivateKey(pCertContext,
7309 : CRYPT_ACQUIRE_CACHE_FLAG,
7310 : NULL,
7311 : &hCryptProv,
7312 : &nKeySpec,
7313 : &bFreeNeeded))
7314 : {
7315 : SAL_WARN("vcl.pdfwriter", "CryptAcquireCertificatePrivateKey failed: " << WindowsError(GetLastError()));
7316 : CertFreeCertificateContext(pCertContext);
7317 : return false;
7318 : }
7319 : assert(!bFreeNeeded);
7320 :
7321 : CMSG_SIGNER_ENCODE_INFO aSignerInfo;
7322 :
7323 : memset(&aSignerInfo, 0, sizeof(aSignerInfo));
7324 : aSignerInfo.cbSize = sizeof(aSignerInfo);
7325 : aSignerInfo.pCertInfo = pCertContext->pCertInfo;
7326 : aSignerInfo.hCryptProv = hCryptProv;
7327 : aSignerInfo.dwKeySpec = nKeySpec;
7328 : aSignerInfo.HashAlgorithm.pszObjId = szOID_RSA_SHA1RSA;
7329 : aSignerInfo.HashAlgorithm.Parameters.cbData = 0;
7330 :
7331 : CMSG_SIGNED_ENCODE_INFO aSignedInfo;
7332 : memset(&aSignedInfo, 0, sizeof(aSignedInfo));
7333 : aSignedInfo.cbSize = sizeof(aSignedInfo);
7334 : aSignedInfo.cSigners = 1;
7335 : aSignedInfo.rgSigners = &aSignerInfo;
7336 :
7337 : CERT_BLOB aCertBlob;
7338 :
7339 : aCertBlob.cbData = pCertContext->cbCertEncoded;
7340 : aCertBlob.pbData = pCertContext->pbCertEncoded;
7341 :
7342 : aSignedInfo.cCertEncoded = 1;
7343 : aSignedInfo.rgCertEncoded = &aCertBlob;
7344 :
7345 : HCRYPTMSG hMsg;
7346 : if (!(hMsg = CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
7347 : CMSG_DETACHED_FLAG,
7348 : CMSG_SIGNED,
7349 : &aSignedInfo,
7350 : NULL,
7351 : NULL)))
7352 : {
7353 : SAL_WARN("vcl.pdfwriter", "CryptMsgOpenToEncode failed: " << WindowsError(GetLastError()));
7354 : CertFreeCertificateContext(pCertContext);
7355 : return false;
7356 : }
7357 :
7358 : if (!CryptMsgUpdate(hMsg, (const BYTE *)buffer1.get(), bytesRead1, FALSE) ||
7359 : !CryptMsgUpdate(hMsg, (const BYTE *)buffer2.get(), bytesRead2, TRUE))
7360 : {
7361 : SAL_WARN("vcl.pdfwriter", "CryptMsgUpdate failed: " << WindowsError(GetLastError()));
7362 : CryptMsgClose(hMsg);
7363 : CertFreeCertificateContext(pCertContext);
7364 : return false;
7365 : }
7366 :
7367 : PCRYPT_TIMESTAMP_CONTEXT pTsContext = NULL;
7368 :
7369 : if( !m_aContext.SignTSA.isEmpty() )
7370 : {
7371 : PointerTo_CryptRetrieveTimeStamp crts = (PointerTo_CryptRetrieveTimeStamp) GetProcAddress(LoadLibrary("crypt32.dll"), "CryptRetrieveTimeStamp");
7372 : if (!crts)
7373 : {
7374 : SAL_WARN("vcl.pdfwriter", "Could not find the CryptRetrieveTimeStamp function in crypt32.dll: " << WindowsError(GetLastError()));
7375 : CryptMsgClose(hMsg);
7376 : CertFreeCertificateContext(pCertContext);
7377 : return false;
7378 : }
7379 :
7380 : HCRYPTMSG hDecodedMsg;
7381 : if (!(hDecodedMsg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
7382 : CMSG_DETACHED_FLAG,
7383 : CMSG_SIGNED,
7384 : NULL,
7385 : NULL,
7386 : NULL)))
7387 : {
7388 : SAL_WARN("vcl.pdfwriter", "CryptMsgOpenToDecode failed: " << WindowsError(GetLastError()));
7389 : CryptMsgClose(hMsg);
7390 : CertFreeCertificateContext(pCertContext);
7391 : return false;
7392 : }
7393 :
7394 : DWORD nTsSigLen = 0;
7395 :
7396 : if (!CryptMsgGetParam(hMsg, CMSG_BARE_CONTENT_PARAM, 0, NULL, &nTsSigLen))
7397 : {
7398 : SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsError(GetLastError()));
7399 : CryptMsgClose(hDecodedMsg);
7400 : CryptMsgClose(hMsg);
7401 : CertFreeCertificateContext(pCertContext);
7402 : return false;
7403 : }
7404 :
7405 : SAL_INFO("vcl.pdfwriter", "nTsSigLen=" << nTsSigLen);
7406 :
7407 : boost::scoped_array<BYTE> pTsSig(new BYTE[nTsSigLen]);
7408 :
7409 : if (!CryptMsgGetParam(hMsg, CMSG_BARE_CONTENT_PARAM, 0, pTsSig.get(), &nTsSigLen))
7410 : {
7411 : SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_BARE_CONTENT_PARAM) failed: " << WindowsError(GetLastError()));
7412 : CryptMsgClose(hDecodedMsg);
7413 : CryptMsgClose(hMsg);
7414 : CertFreeCertificateContext(pCertContext);
7415 : return false;
7416 : }
7417 :
7418 : if (!CryptMsgUpdate(hDecodedMsg, pTsSig.get(), nTsSigLen, TRUE))
7419 : {
7420 : SAL_WARN("vcl.pdfwriter", "CryptMsgUpdate failed: " << WindowsError(GetLastError()));
7421 : CryptMsgClose(hDecodedMsg);
7422 : CryptMsgClose(hMsg);
7423 : CertFreeCertificateContext(pCertContext);
7424 : return false;
7425 : }
7426 :
7427 : DWORD nDecodedSignerInfoLen = 0;
7428 : if (!CryptMsgGetParam(hDecodedMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &nDecodedSignerInfoLen))
7429 : {
7430 : SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsError(GetLastError()));
7431 : CryptMsgClose(hDecodedMsg);
7432 : CryptMsgClose(hMsg);
7433 : CertFreeCertificateContext(pCertContext);
7434 : return false;
7435 : }
7436 :
7437 : boost::scoped_array<BYTE> pDecodedSignerInfoBuf(new BYTE[nDecodedSignerInfoLen]);
7438 :
7439 : if (!CryptMsgGetParam(hDecodedMsg, CMSG_SIGNER_INFO_PARAM, 0, pDecodedSignerInfoBuf.get(), &nDecodedSignerInfoLen))
7440 : {
7441 : SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_SIGNER_INFO_PARAM) failed: " << WindowsError(GetLastError()));
7442 : CryptMsgClose(hDecodedMsg);
7443 : CryptMsgClose(hMsg);
7444 : CertFreeCertificateContext(pCertContext);
7445 : return false;
7446 : }
7447 :
7448 : CMSG_SIGNER_INFO *pDecodedSignerInfo = (CMSG_SIGNER_INFO *) pDecodedSignerInfoBuf.get();
7449 :
7450 : CRYPT_TIMESTAMP_PARA aTsPara;
7451 : unsigned int nNonce = comphelper::rng::uniform_uint_distribution(0, SAL_MAX_UINT32);
7452 :
7453 : aTsPara.pszTSAPolicyId = NULL;
7454 : aTsPara.fRequestCerts = TRUE;
7455 : aTsPara.Nonce.cbData = sizeof(nNonce);
7456 : aTsPara.Nonce.pbData = (BYTE *)&nNonce;
7457 : aTsPara.cExtension = 0;
7458 : aTsPara.rgExtension = NULL;
7459 :
7460 : if (!(*crts)(m_aContext.SignTSA.getStr(),
7461 : 0,
7462 : 10000,
7463 : szOID_NIST_sha256,
7464 : &aTsPara,
7465 : pDecodedSignerInfo->EncryptedHash.pbData,
7466 : pDecodedSignerInfo->EncryptedHash.cbData,
7467 : &pTsContext,
7468 : NULL,
7469 : NULL))
7470 : {
7471 : SAL_WARN("vcl.pdfwriter", "CryptRetrieveTimeStamp failed: " << WindowsError(GetLastError()));
7472 : CryptMsgClose(hDecodedMsg);
7473 : CryptMsgClose(hMsg);
7474 : CertFreeCertificateContext(pCertContext);
7475 : return false;
7476 : }
7477 :
7478 : SAL_INFO("vcl.pdfwriter", "Time stamp size is " << pTsContext->cbEncoded << " bytes");
7479 :
7480 : #ifdef DBG_UTIL
7481 : {
7482 : FILE *out = fopen("PDFWRITER.tstoken.data", "wb");
7483 : fwrite(pTsContext->pbEncoded, pTsContext->cbEncoded, 1, out);
7484 : fclose(out);
7485 : }
7486 : #endif
7487 :
7488 : // I tried to use CryptMsgControl() with CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR to add the
7489 : // timestamp, but that failed with "The parameter is incorrect". Probably it is too late to
7490 : // modify the message once its data has already been encoded as part of the
7491 : // CryptMsgGetParam() with CMSG_BARE_CONTENT_PARAM above. So close the message and re-do its
7492 : // creation steps, but now with an amended aSignerInfo.
7493 :
7494 : CRYPT_INTEGER_BLOB aTimestampBlob;
7495 : aTimestampBlob.cbData = pTsContext->cbEncoded;
7496 : aTimestampBlob.pbData = pTsContext->pbEncoded;
7497 :
7498 : CRYPT_ATTRIBUTE aTimestampAttribute;
7499 : aTimestampAttribute.pszObjId = "1.2.840.113549.1.9.16.2.14";
7500 : aTimestampAttribute.cValue = 1;
7501 : aTimestampAttribute.rgValue = &aTimestampBlob;
7502 :
7503 : aSignerInfo.cUnauthAttr = 1;
7504 : aSignerInfo.rgUnauthAttr = &aTimestampAttribute;
7505 :
7506 : CryptMsgClose(hMsg);
7507 :
7508 : if (!(hMsg = CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
7509 : CMSG_DETACHED_FLAG,
7510 : CMSG_SIGNED,
7511 : &aSignedInfo,
7512 : NULL,
7513 : NULL)) ||
7514 : !CryptMsgUpdate(hMsg, (const BYTE *)buffer1.get(), bytesRead1, FALSE) ||
7515 : !CryptMsgUpdate(hMsg, (const BYTE *)buffer2.get(), bytesRead2, TRUE))
7516 : {
7517 : SAL_WARN("vcl.pdfwriter", "Re-creating the message failed: " << WindowsError(GetLastError()));
7518 : CryptMemFree(pTsContext);
7519 : CryptMsgClose(hDecodedMsg);
7520 : CryptMsgClose(hMsg);
7521 : CertFreeCertificateContext(pCertContext);
7522 : return false;
7523 : }
7524 :
7525 : CryptMsgClose(hDecodedMsg);
7526 : }
7527 :
7528 : DWORD nSigLen = 0;
7529 :
7530 : if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, NULL, &nSigLen))
7531 : {
7532 : SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsError(GetLastError()));
7533 : if (pTsContext)
7534 : CryptMemFree(pTsContext);
7535 : CryptMsgClose(hMsg);
7536 : CertFreeCertificateContext(pCertContext);
7537 : return false;
7538 : }
7539 :
7540 : if (nSigLen*2 > MAX_SIGNATURE_CONTENT_LENGTH)
7541 : {
7542 : SAL_WARN("vcl.pdfwriter", "Signature requires more space (" << nSigLen*2 << ") than we reserved (" << MAX_SIGNATURE_CONTENT_LENGTH << ")");
7543 : if (pTsContext)
7544 : CryptMemFree(pTsContext);
7545 : CryptMsgClose(hMsg);
7546 : CertFreeCertificateContext(pCertContext);
7547 : return false;
7548 : }
7549 :
7550 : SAL_INFO("vcl.pdfwriter", "Signature size is " << nSigLen << " bytes");
7551 : boost::scoped_array<BYTE> pSig(new BYTE[nSigLen]);
7552 :
7553 : if (!CryptMsgGetParam(hMsg, CMSG_CONTENT_PARAM, 0, pSig.get(), &nSigLen))
7554 : {
7555 : SAL_WARN("vcl.pdfwriter", "CryptMsgGetParam(CMSG_CONTENT_PARAM) failed: " << WindowsError(GetLastError()));
7556 : if (pTsContext)
7557 : CryptMemFree(pTsContext);
7558 : CryptMsgClose(hMsg);
7559 : CertFreeCertificateContext(pCertContext);
7560 : return false;
7561 : }
7562 :
7563 : #ifdef DBG_UTIL
7564 : {
7565 : FILE *out = fopen("PDFWRITER.signature.data", "wb");
7566 : fwrite(pSig.get(), nSigLen, 1, out);
7567 : fclose(out);
7568 : }
7569 : #endif
7570 :
7571 : // Release resources
7572 : if (pTsContext)
7573 : CryptMemFree(pTsContext);
7574 : CryptMsgClose(hMsg);
7575 : CertFreeCertificateContext(pCertContext);
7576 :
7577 : OStringBuffer cms_hexbuffer;
7578 :
7579 : for (unsigned int i = 0; i < nSigLen ; i++)
7580 : appendHex(pSig[i], cms_hexbuffer);
7581 :
7582 : // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
7583 : nWritten = 0;
7584 : CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset)) );
7585 : m_aFile.write(cms_hexbuffer.getStr(), cms_hexbuffer.getLength(), nWritten);
7586 :
7587 : CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) );
7588 :
7589 : return true;
7590 : #endif
7591 : }
7592 :
7593 : #endif
7594 :
7595 0 : sal_Int32 PDFWriterImpl::emitInfoDict( )
7596 : {
7597 0 : sal_Int32 nObject = createObject();
7598 :
7599 0 : if( updateObject( nObject ) )
7600 : {
7601 0 : OStringBuffer aLine( 1024 );
7602 0 : aLine.append( nObject );
7603 : aLine.append( " 0 obj\n"
7604 0 : "<<" );
7605 0 : if( !m_aContext.DocumentInfo.Title.isEmpty() )
7606 : {
7607 0 : aLine.append( "/Title" );
7608 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine );
7609 0 : aLine.append( "\n" );
7610 : }
7611 0 : if( !m_aContext.DocumentInfo.Author.isEmpty() )
7612 : {
7613 0 : aLine.append( "/Author" );
7614 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine );
7615 0 : aLine.append( "\n" );
7616 : }
7617 0 : if( !m_aContext.DocumentInfo.Subject.isEmpty() )
7618 : {
7619 0 : aLine.append( "/Subject" );
7620 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine );
7621 0 : aLine.append( "\n" );
7622 : }
7623 0 : if( !m_aContext.DocumentInfo.Keywords.isEmpty() )
7624 : {
7625 0 : aLine.append( "/Keywords" );
7626 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine );
7627 0 : aLine.append( "\n" );
7628 : }
7629 0 : if( !m_aContext.DocumentInfo.Creator.isEmpty() )
7630 : {
7631 0 : aLine.append( "/Creator" );
7632 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine );
7633 0 : aLine.append( "\n" );
7634 : }
7635 0 : if( !m_aContext.DocumentInfo.Producer.isEmpty() )
7636 : {
7637 0 : aLine.append( "/Producer" );
7638 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine );
7639 0 : aLine.append( "\n" );
7640 : }
7641 :
7642 0 : aLine.append( "/CreationDate" );
7643 0 : appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine );
7644 0 : aLine.append( ">>\nendobj\n\n" );
7645 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
7646 0 : nObject = 0;
7647 : }
7648 : else
7649 0 : nObject = 0;
7650 :
7651 0 : return nObject;
7652 : }
7653 :
7654 : //--->i56629
7655 : // Part of this function may be shared with method appendDest.
7656 0 : sal_Int32 PDFWriterImpl::emitNamedDestinations()
7657 : {
7658 0 : sal_Int32 nCount = m_aNamedDests.size();
7659 0 : if( nCount <= 0 )
7660 0 : return 0;//define internal error
7661 :
7662 : //get the object number for all the destinations
7663 0 : sal_Int32 nObject = createObject();
7664 :
7665 0 : if( updateObject( nObject ) )
7666 : {
7667 : //emit the dictionary
7668 0 : OStringBuffer aLine( 1024 );
7669 0 : aLine.append( nObject );
7670 : aLine.append( " 0 obj\n"
7671 0 : "<<" );
7672 :
7673 : sal_Int32 nDestID;
7674 0 : for( nDestID = 0; nDestID < nCount; nDestID++ )
7675 : {
7676 0 : const PDFNamedDest& rDest = m_aNamedDests[ nDestID ];
7677 : // In order to correctly function both under an Internet browser and
7678 : // directly with a reader (provided the reader has the feature) we
7679 : // need to set the name of the destination the same way it will be encoded
7680 : // in an Internet link
7681 : INetURLObject aLocalURL(
7682 0 : OUString( "http://ahost.ax" ) ); //dummy location, won't be used
7683 0 : aLocalURL.SetMark( rDest.m_aDestName );
7684 :
7685 0 : const OUString aName = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as
7686 : // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
7687 0 : const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
7688 :
7689 0 : aLine.append( '/' );
7690 0 : appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
7691 0 : aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
7692 : //maps the preceding character properly
7693 0 : aLine.append( rDestPage.m_nPageObject );
7694 0 : aLine.append( " 0 R" );
7695 :
7696 0 : switch( rDest.m_eType )
7697 : {
7698 : case PDFWriter::XYZ:
7699 : default:
7700 0 : aLine.append( "/XYZ " );
7701 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
7702 0 : aLine.append( ' ' );
7703 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
7704 0 : aLine.append( " 0" );
7705 0 : break;
7706 : case PDFWriter::Fit:
7707 0 : aLine.append( "/Fit" );
7708 0 : break;
7709 : case PDFWriter::FitRectangle:
7710 0 : aLine.append( "/FitR " );
7711 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
7712 0 : aLine.append( ' ' );
7713 0 : appendFixedInt( rDest.m_aRect.Top(), aLine );
7714 0 : aLine.append( ' ' );
7715 0 : appendFixedInt( rDest.m_aRect.Right(), aLine );
7716 0 : aLine.append( ' ' );
7717 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
7718 0 : break;
7719 : case PDFWriter::FitHorizontal:
7720 0 : aLine.append( "/FitH " );
7721 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
7722 0 : break;
7723 : case PDFWriter::FitVertical:
7724 0 : aLine.append( "/FitV " );
7725 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
7726 0 : break;
7727 : case PDFWriter::FitPageBoundingBox:
7728 0 : aLine.append( "/FitB" );
7729 0 : break;
7730 : case PDFWriter::FitPageBoundingBoxHorizontal:
7731 0 : aLine.append( "/FitBH " );
7732 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
7733 0 : break;
7734 : case PDFWriter::FitPageBoundingBoxVertical:
7735 0 : aLine.append( "/FitBV " );
7736 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
7737 0 : break;
7738 : }
7739 0 : aLine.append( "]\n" );
7740 0 : }
7741 :
7742 : //close
7743 0 : aLine.append( ">>\nendobj\n\n" );
7744 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
7745 0 : nObject = 0;
7746 : }
7747 : else
7748 0 : nObject = 0;
7749 :
7750 0 : return nObject;
7751 : }
7752 : //<--- i56629
7753 :
7754 : //--->i59651
7755 : // emits the output intent dictionary
7756 0 : sal_Int32 PDFWriterImpl::emitOutputIntent()
7757 : {
7758 0 : if( !m_bIsPDF_A1 )
7759 0 : return 0;
7760 :
7761 : //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
7762 :
7763 0 : OStringBuffer aLine( 1024 );
7764 0 : sal_Int32 nICCObject = createObject();
7765 0 : sal_Int32 nStreamLengthObject = createObject();
7766 :
7767 0 : aLine.append( nICCObject );
7768 : // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
7769 0 : aLine.append( " 0 obj\n<</N 3/Length " );
7770 0 : aLine.append( nStreamLengthObject );
7771 0 : aLine.append( " 0 R" );
7772 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
7773 0 : aLine.append( "/Filter/FlateDecode" );
7774 : #endif
7775 0 : aLine.append( ">>\nstream\n" );
7776 0 : if ( !updateObject( nICCObject ) ) return 0;
7777 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
7778 : //get file position
7779 0 : sal_uInt64 nBeginStreamPos = 0;
7780 0 : m_aFile.getPos(nBeginStreamPos);
7781 0 : beginCompression();
7782 0 : checkAndEnableStreamEncryption( nICCObject );
7783 0 : cmsHPROFILE hProfile = cmsCreate_sRGBProfile();
7784 : //force ICC profile version 2.1
7785 0 : cmsSetProfileVersion(hProfile, 2.1);
7786 0 : cmsUInt32Number nBytesNeeded = 0;
7787 0 : cmsSaveProfileToMem(hProfile, NULL, &nBytesNeeded);
7788 0 : if (!nBytesNeeded)
7789 0 : return 0;
7790 0 : std::vector<unsigned char> xBuffer(nBytesNeeded);
7791 0 : cmsSaveProfileToMem(hProfile, &xBuffer[0], &nBytesNeeded);
7792 0 : cmsCloseProfile(hProfile);
7793 0 : bool written = writeBuffer( &xBuffer[0], (sal_Int32) xBuffer.size() );
7794 0 : disableStreamEncryption();
7795 0 : endCompression();
7796 0 : sal_uInt64 nEndStreamPos = 0;
7797 0 : m_aFile.getPos(nEndStreamPos);
7798 :
7799 0 : if( !written )
7800 0 : return 0;
7801 0 : if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
7802 0 : return 0 ;
7803 0 : aLine.setLength( 0 );
7804 :
7805 : //emit the stream length object
7806 0 : if ( !updateObject( nStreamLengthObject ) ) return 0;
7807 0 : aLine.setLength( 0 );
7808 0 : aLine.append( nStreamLengthObject );
7809 0 : aLine.append( " 0 obj\n" );
7810 0 : aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
7811 0 : aLine.append( "\nendobj\n\n" );
7812 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
7813 0 : aLine.setLength( 0 );
7814 :
7815 : //emit the OutputIntent dictionary
7816 0 : sal_Int32 nOIObject = createObject();
7817 0 : if ( !updateObject( nOIObject ) ) return 0;
7818 0 : aLine.append( nOIObject );
7819 : aLine.append( " 0 obj\n"
7820 0 : "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
7821 :
7822 0 : OUString aComment( "sRGB IEC61966-2.1" );
7823 0 : appendLiteralStringEncrypt( aComment ,nOIObject, aLine );
7824 0 : aLine.append("/DestOutputProfile ");
7825 0 : aLine.append( nICCObject );
7826 0 : aLine.append( " 0 R>>\nendobj\n\n" );;
7827 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
7828 :
7829 0 : return nOIObject;
7830 : }
7831 :
7832 : // formats the string for the XML stream
7833 0 : static void escapeStringXML( const OUString& rStr, OUString &rValue)
7834 : {
7835 0 : const sal_Unicode* pUni = rStr.getStr();
7836 0 : int nLen = rStr.getLength();
7837 0 : for( ; nLen; nLen--, pUni++ )
7838 : {
7839 0 : switch( *pUni )
7840 : {
7841 : case sal_Unicode('&'):
7842 0 : rValue += "&";
7843 0 : break;
7844 : case sal_Unicode('<'):
7845 0 : rValue += "<";
7846 0 : break;
7847 : case sal_Unicode('>'):
7848 0 : rValue += ">";
7849 0 : break;
7850 : case sal_Unicode('\''):
7851 0 : rValue += "'";
7852 0 : break;
7853 : case sal_Unicode('"'):
7854 0 : rValue += """;
7855 0 : break;
7856 : default:
7857 0 : rValue += OUString( *pUni );
7858 0 : break;
7859 : }
7860 : }
7861 0 : }
7862 :
7863 : // emits the document metadata
7864 0 : sal_Int32 PDFWriterImpl::emitDocumentMetadata()
7865 : {
7866 0 : if( !m_bIsPDF_A1 )
7867 0 : return 0;
7868 :
7869 : //get the object number for all the destinations
7870 0 : sal_Int32 nObject = createObject();
7871 :
7872 0 : if( updateObject( nObject ) )
7873 : {
7874 : // the following string are written in UTF-8 unicode
7875 0 : OStringBuffer aMetadataStream( 8192 );
7876 :
7877 0 : aMetadataStream.append( "<?xpacket begin=\"" );
7878 : // these lines write Unicode "zero width non-breaking space character" (U+FEFF)
7879 : // (aka byte-order mark ) used as a byte-order marker.
7880 0 : aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) );
7881 0 : aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
7882 0 : aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
7883 0 : aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
7884 : //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
7885 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
7886 0 : aMetadataStream.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
7887 0 : aMetadataStream.append( " <pdfaid:part>1</pdfaid:part>\n" );
7888 0 : aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" );
7889 0 : aMetadataStream.append( " </rdf:Description>\n" );
7890 : //... Dublin Core properties go here
7891 0 : if( !m_aContext.DocumentInfo.Title.isEmpty() ||
7892 0 : !m_aContext.DocumentInfo.Author.isEmpty() ||
7893 0 : !m_aContext.DocumentInfo.Subject.isEmpty() )
7894 : {
7895 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
7896 0 : aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
7897 0 : if( !m_aContext.DocumentInfo.Title.isEmpty() )
7898 : {
7899 : // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
7900 0 : aMetadataStream.append( " <dc:title>\n" );
7901 0 : aMetadataStream.append( " <rdf:Alt>\n" );
7902 0 : aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
7903 0 : OUString aTitle;
7904 0 : escapeStringXML( m_aContext.DocumentInfo.Title, aTitle );
7905 0 : aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) );
7906 0 : aMetadataStream.append( "</rdf:li>\n" );
7907 0 : aMetadataStream.append( " </rdf:Alt>\n" );
7908 0 : aMetadataStream.append( " </dc:title>\n" );
7909 : }
7910 0 : if( !m_aContext.DocumentInfo.Author.isEmpty() )
7911 : {
7912 0 : aMetadataStream.append( " <dc:creator>\n" );
7913 0 : aMetadataStream.append( " <rdf:Seq>\n" );
7914 0 : aMetadataStream.append( " <rdf:li>" );
7915 0 : OUString aAuthor;
7916 0 : escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor );
7917 0 : aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) );
7918 0 : aMetadataStream.append( "</rdf:li>\n" );
7919 0 : aMetadataStream.append( " </rdf:Seq>\n" );
7920 0 : aMetadataStream.append( " </dc:creator>\n" );
7921 : }
7922 0 : if( !m_aContext.DocumentInfo.Subject.isEmpty() )
7923 : {
7924 : // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
7925 0 : aMetadataStream.append( " <dc:description>\n" );
7926 0 : aMetadataStream.append( " <rdf:Alt>\n" );
7927 0 : aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
7928 0 : OUString aSubject;
7929 0 : escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject );
7930 0 : aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) );
7931 0 : aMetadataStream.append( "</rdf:li>\n" );
7932 0 : aMetadataStream.append( " </rdf:Alt>\n" );
7933 0 : aMetadataStream.append( " </dc:description>\n" );
7934 : }
7935 0 : aMetadataStream.append( " </rdf:Description>\n" );
7936 : }
7937 :
7938 : //... PDF properties go here
7939 0 : if( !m_aContext.DocumentInfo.Producer.isEmpty() ||
7940 0 : !m_aContext.DocumentInfo.Keywords.isEmpty() )
7941 : {
7942 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
7943 0 : aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
7944 0 : if( !m_aContext.DocumentInfo.Producer.isEmpty() )
7945 : {
7946 0 : aMetadataStream.append( " <pdf:Producer>" );
7947 0 : OUString aProducer;
7948 0 : escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer );
7949 0 : aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) );
7950 0 : aMetadataStream.append( "</pdf:Producer>\n" );
7951 : }
7952 0 : if( !m_aContext.DocumentInfo.Keywords.isEmpty() )
7953 : {
7954 0 : aMetadataStream.append( " <pdf:Keywords>" );
7955 0 : OUString aKeywords;
7956 0 : escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords );
7957 0 : aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) );
7958 0 : aMetadataStream.append( "</pdf:Keywords>\n" );
7959 : }
7960 0 : aMetadataStream.append( " </rdf:Description>\n" );
7961 : }
7962 :
7963 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
7964 0 : aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
7965 0 : if( !m_aContext.DocumentInfo.Creator.isEmpty() )
7966 : {
7967 0 : aMetadataStream.append( " <xmp:CreatorTool>" );
7968 0 : OUString aCreator;
7969 0 : escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator );
7970 0 : aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) );
7971 0 : aMetadataStream.append( "</xmp:CreatorTool>\n" );
7972 : }
7973 : //creation date
7974 0 : aMetadataStream.append( " <xmp:CreateDate>" );
7975 0 : aMetadataStream.append( m_aCreationMetaDateString );
7976 0 : aMetadataStream.append( "</xmp:CreateDate>\n" );
7977 :
7978 0 : aMetadataStream.append( " </rdf:Description>\n" );
7979 0 : aMetadataStream.append( " </rdf:RDF>\n" );
7980 0 : aMetadataStream.append( "</x:xmpmeta>\n" );
7981 :
7982 : //add the padding
7983 0 : for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ )
7984 : {
7985 0 : aMetadataStream.append( " " );
7986 0 : if( nSpaces % 100 == 0 )
7987 0 : aMetadataStream.append( "\n" );
7988 : }
7989 :
7990 0 : aMetadataStream.append( "<?xpacket end=\"w\"?>\n" );
7991 :
7992 0 : OStringBuffer aMetadataObj( 1024 );
7993 :
7994 0 : aMetadataObj.append( nObject );
7995 0 : aMetadataObj.append( " 0 obj\n" );
7996 :
7997 0 : aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
7998 :
7999 0 : aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() );
8000 0 : aMetadataObj.append( ">>\nstream\n" );
8001 0 : if ( !writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
8002 0 : return 0;
8003 : //emit the stream
8004 0 : if ( !writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) )
8005 0 : return 0;
8006 :
8007 0 : aMetadataObj.setLength( 0 );
8008 0 : aMetadataObj.append( "\nendstream\nendobj\n\n" );
8009 0 : if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
8010 0 : nObject = 0;
8011 : }
8012 : else
8013 0 : nObject = 0;
8014 :
8015 0 : return nObject;
8016 : }
8017 : //<---i59651
8018 :
8019 0 : bool PDFWriterImpl::emitTrailer()
8020 : {
8021 : // emit doc info
8022 0 : sal_Int32 nDocInfoObject = emitInfoDict( );
8023 :
8024 0 : sal_Int32 nSecObject = 0;
8025 :
8026 0 : if( m_aContext.Encryption.Encrypt() )
8027 : {
8028 : //emit the security information
8029 : //must be emitted as indirect dictionary object, since
8030 : //Acrobat Reader 5 works only with this kind of implementation
8031 0 : nSecObject = createObject();
8032 :
8033 0 : if( updateObject( nSecObject ) )
8034 : {
8035 0 : OStringBuffer aLineS( 1024 );
8036 0 : aLineS.append( nSecObject );
8037 : aLineS.append( " 0 obj\n"
8038 0 : "<</Filter/Standard/V " );
8039 : // check the version
8040 0 : if( m_aContext.Encryption.Security128bit )
8041 0 : aLineS.append( "2/Length 128/R 3" );
8042 : else
8043 0 : aLineS.append( "1/R 2" );
8044 :
8045 : // emit the owner password, must not be encrypted
8046 0 : aLineS.append( "/O(" );
8047 0 : appendLiteralString( reinterpret_cast<char*>(&m_aContext.Encryption.OValue[0]), sal_Int32(m_aContext.Encryption.OValue.size()), aLineS );
8048 0 : aLineS.append( ")/U(" );
8049 0 : appendLiteralString( reinterpret_cast<char*>(&m_aContext.Encryption.UValue[0]), sal_Int32(m_aContext.Encryption.UValue.size()), aLineS );
8050 0 : aLineS.append( ")/P " );// the permission set
8051 0 : aLineS.append( m_nAccessPermissions );
8052 0 : aLineS.append( ">>\nendobj\n\n" );
8053 0 : if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) )
8054 0 : nSecObject = 0;
8055 : }
8056 : else
8057 0 : nSecObject = 0;
8058 : }
8059 : // emit xref table
8060 : // remember start
8061 0 : sal_uInt64 nXRefOffset = 0;
8062 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nXRefOffset )) );
8063 0 : CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
8064 :
8065 0 : sal_Int32 nObjects = m_aObjects.size();
8066 0 : OStringBuffer aLine;
8067 0 : aLine.append( "0 " );
8068 0 : aLine.append( (sal_Int32)(nObjects+1) );
8069 0 : aLine.append( "\n" );
8070 0 : aLine.append( "0000000000 65535 f \n" );
8071 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8072 :
8073 0 : for( sal_Int32 i = 0; i < nObjects; i++ )
8074 : {
8075 0 : aLine.setLength( 0 );
8076 0 : OString aOffset = OString::number( m_aObjects[i] );
8077 0 : for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
8078 0 : aLine.append( '0' );
8079 0 : aLine.append( aOffset );
8080 0 : aLine.append( " 00000 n \n" );
8081 : DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" );
8082 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8083 0 : }
8084 :
8085 : // prepare document checksum
8086 0 : OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
8087 0 : if( m_aDocDigest )
8088 : {
8089 : sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
8090 0 : rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) );
8091 0 : for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ )
8092 0 : appendHex( nMD5Sum[i], aDocChecksum );
8093 : }
8094 : // document id set in setDocInfo method
8095 : // emit trailer
8096 0 : aLine.setLength( 0 );
8097 : aLine.append( "trailer\n"
8098 0 : "<</Size " );
8099 0 : aLine.append( (sal_Int32)(nObjects+1) );
8100 0 : aLine.append( "/Root " );
8101 0 : aLine.append( m_nCatalogObject );
8102 0 : aLine.append( " 0 R\n" );
8103 0 : if( nSecObject )
8104 : {
8105 0 : aLine.append( "/Encrypt ");
8106 0 : aLine.append( nSecObject );
8107 0 : aLine.append( " 0 R\n" );
8108 : }
8109 0 : if( nDocInfoObject )
8110 : {
8111 0 : aLine.append( "/Info " );
8112 0 : aLine.append( nDocInfoObject );
8113 0 : aLine.append( " 0 R\n" );
8114 : }
8115 0 : if( ! m_aContext.Encryption.DocumentIdentifier.empty() )
8116 : {
8117 0 : aLine.append( "/ID [ <" );
8118 0 : for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
8119 0 : it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
8120 : {
8121 0 : appendHex( sal_Int8(*it), aLine );
8122 : }
8123 : aLine.append( ">\n"
8124 0 : "<" );
8125 0 : for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
8126 0 : it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
8127 : {
8128 0 : appendHex( sal_Int8(*it), aLine );
8129 : }
8130 0 : aLine.append( "> ]\n" );
8131 : }
8132 0 : if( !aDocChecksum.isEmpty() )
8133 : {
8134 0 : aLine.append( "/DocChecksum /" );
8135 0 : aLine.append( aDocChecksum.makeStringAndClear() );
8136 0 : aLine.append( "\n" );
8137 : }
8138 0 : if( m_aAdditionalStreams.size() > 0 )
8139 : {
8140 0 : aLine.append( "/AdditionalStreams [" );
8141 0 : for( size_t i = 0; i < m_aAdditionalStreams.size(); i++ )
8142 : {
8143 0 : aLine.append( "/" );
8144 0 : appendName( m_aAdditionalStreams[i].m_aMimeType, aLine );
8145 0 : aLine.append( " " );
8146 0 : aLine.append( m_aAdditionalStreams[i].m_nStreamObject );
8147 0 : aLine.append( " 0 R\n" );
8148 : }
8149 0 : aLine.append( "]\n" );
8150 : }
8151 : aLine.append( ">>\n"
8152 0 : "startxref\n" );
8153 0 : aLine.append( (sal_Int64)nXRefOffset );
8154 : aLine.append( "\n"
8155 0 : "%%EOF\n" );
8156 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
8157 :
8158 0 : return true;
8159 : }
8160 :
8161 : struct AnnotationSortEntry
8162 : {
8163 : sal_Int32 nTabOrder;
8164 : sal_Int32 nObject;
8165 : sal_Int32 nWidgetIndex;
8166 :
8167 0 : AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
8168 : nTabOrder( nTab ),
8169 : nObject( nObj ),
8170 0 : nWidgetIndex( nI )
8171 0 : {}
8172 : };
8173 :
8174 0 : struct AnnotSortContainer
8175 : {
8176 : std::set< sal_Int32 > aObjects;
8177 : std::vector< AnnotationSortEntry > aSortedAnnots;
8178 : };
8179 :
8180 : struct AnnotSorterLess
8181 : {
8182 : std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets;
8183 :
8184 0 : explicit AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {}
8185 :
8186 0 : bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
8187 : {
8188 0 : if( rLeft.nTabOrder < rRight.nTabOrder )
8189 0 : return true;
8190 0 : if( rRight.nTabOrder < rLeft.nTabOrder )
8191 0 : return false;
8192 0 : if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
8193 0 : return false;
8194 0 : if( rRight.nWidgetIndex < 0 )
8195 0 : return true;
8196 0 : if( rLeft.nWidgetIndex < 0 )
8197 0 : return false;
8198 : // remember: widget rects are in PDF coordinates, so they are ordered down up
8199 0 : if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
8200 0 : m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
8201 0 : return true;
8202 0 : if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
8203 0 : m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
8204 0 : return false;
8205 0 : if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
8206 0 : m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
8207 0 : return true;
8208 0 : return false;
8209 : }
8210 : };
8211 :
8212 0 : void PDFWriterImpl::sortWidgets()
8213 : {
8214 : // sort widget annotations on each page as per their
8215 : // TabOrder attribute
8216 0 : std::unordered_map< sal_Int32, AnnotSortContainer > sorted;
8217 0 : int nWidgets = m_aWidgets.size();
8218 0 : for( int nW = 0; nW < nWidgets; nW++ )
8219 : {
8220 0 : const PDFWidget& rWidget = m_aWidgets[nW];
8221 0 : if( rWidget.m_nPage >= 0 )
8222 : {
8223 0 : AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
8224 : // optimize vector allocation
8225 0 : if( rCont.aSortedAnnots.empty() )
8226 0 : rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
8227 : // insert widget to tab sorter
8228 : // RadioButtons are not page annotations, only their individual check boxes are
8229 0 : if( rWidget.m_eType != PDFWriter::RadioButton )
8230 : {
8231 0 : rCont.aObjects.insert( rWidget.m_nObject );
8232 0 : rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) );
8233 : }
8234 : }
8235 : }
8236 0 : for( std::unordered_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it )
8237 : {
8238 : // append entries for non widget annotations
8239 0 : PDFPage& rPage = m_aPages[ it->first ];
8240 0 : unsigned int nAnnots = rPage.m_aAnnotations.size();
8241 0 : for( unsigned int nA = 0; nA < nAnnots; nA++ )
8242 0 : if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end())
8243 0 : it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) );
8244 :
8245 0 : AnnotSorterLess aLess( m_aWidgets );
8246 0 : std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess );
8247 : // sanity check
8248 0 : if( it->second.aSortedAnnots.size() == nAnnots)
8249 : {
8250 0 : for( unsigned int nA = 0; nA < nAnnots; nA++ )
8251 0 : rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject;
8252 : }
8253 : else
8254 : {
8255 : DBG_ASSERT( false, "wrong number of sorted annotations" );
8256 : #if OSL_DEBUG_LEVEL > 0
8257 : fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n"
8258 : " %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots );
8259 : #endif
8260 : }
8261 0 : }
8262 :
8263 : // FIXME: implement tab order in structure tree for PDF 1.5
8264 0 : }
8265 :
8266 : namespace vcl {
8267 : class PDFStreamIf :
8268 : public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream >
8269 : {
8270 : PDFWriterImpl* m_pWriter;
8271 : bool m_bWrite;
8272 : public:
8273 0 : explicit PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
8274 : virtual ~PDFStreamIf();
8275 :
8276 : virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw(std::exception) SAL_OVERRIDE;
8277 : virtual void SAL_CALL flush() throw(std::exception) SAL_OVERRIDE;
8278 : virtual void SAL_CALL closeOutput() throw(std::exception) SAL_OVERRIDE;
8279 : };
8280 : }
8281 :
8282 0 : PDFStreamIf::~PDFStreamIf()
8283 : {
8284 0 : }
8285 :
8286 0 : void SAL_CALL PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw(std::exception)
8287 : {
8288 0 : if( m_bWrite && aData.getLength() )
8289 : {
8290 0 : sal_Int32 nBytes = aData.getLength();
8291 0 : m_pWriter->writeBuffer( aData.getConstArray(), nBytes );
8292 : }
8293 0 : }
8294 :
8295 0 : void SAL_CALL PDFStreamIf::flush() throw(std::exception)
8296 : {
8297 0 : }
8298 :
8299 0 : void SAL_CALL PDFStreamIf::closeOutput() throw(std::exception)
8300 : {
8301 0 : m_bWrite = false;
8302 0 : }
8303 :
8304 0 : bool PDFWriterImpl::emitAdditionalStreams()
8305 : {
8306 0 : unsigned int nStreams = m_aAdditionalStreams.size();
8307 0 : for( unsigned int i = 0; i < nStreams; i++ )
8308 : {
8309 0 : PDFAddStream& rStream = m_aAdditionalStreams[i];
8310 0 : rStream.m_nStreamObject = createObject();
8311 0 : sal_Int32 nSizeObject = createObject();
8312 :
8313 0 : if( ! updateObject( rStream.m_nStreamObject ) )
8314 0 : return false;
8315 :
8316 0 : OStringBuffer aLine;
8317 0 : aLine.append( rStream.m_nStreamObject );
8318 0 : aLine.append( " 0 obj\n<</Length " );
8319 0 : aLine.append( nSizeObject );
8320 0 : aLine.append( " 0 R" );
8321 0 : if( rStream.m_bCompress )
8322 0 : aLine.append( "/Filter/FlateDecode" );
8323 0 : aLine.append( ">>\nstream\n" );
8324 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
8325 0 : return false;
8326 0 : sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0;
8327 0 : if( osl::File::E_None != m_aFile.getPos(nBeginStreamPos) )
8328 : {
8329 0 : m_aFile.close();
8330 0 : m_bOpen = false;
8331 : }
8332 0 : if( rStream.m_bCompress )
8333 0 : beginCompression();
8334 :
8335 0 : checkAndEnableStreamEncryption( rStream.m_nStreamObject );
8336 0 : com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) );
8337 : assert(rStream.m_pStream);
8338 0 : if (!rStream.m_pStream)
8339 0 : return false;
8340 0 : rStream.m_pStream->write( xStream );
8341 0 : xStream.clear();
8342 0 : delete rStream.m_pStream;
8343 0 : rStream.m_pStream = NULL;
8344 0 : disableStreamEncryption();
8345 :
8346 0 : if( rStream.m_bCompress )
8347 0 : endCompression();
8348 :
8349 0 : if (osl::File::E_None != m_aFile.getPos(nEndStreamPos))
8350 : {
8351 0 : m_aFile.close();
8352 0 : m_bOpen = false;
8353 0 : return false;
8354 : }
8355 0 : if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
8356 0 : return false ;
8357 : // emit stream length object
8358 0 : if( ! updateObject( nSizeObject ) )
8359 0 : return false;
8360 0 : aLine.setLength( 0 );
8361 0 : aLine.append( nSizeObject );
8362 0 : aLine.append( " 0 obj\n" );
8363 0 : aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
8364 0 : aLine.append( "\nendobj\n\n" );
8365 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
8366 0 : return false;
8367 0 : }
8368 0 : return true;
8369 : }
8370 :
8371 0 : bool PDFWriterImpl::emit()
8372 : {
8373 0 : endPage();
8374 :
8375 : // resort structure tree and annotations if necessary
8376 : // needed for widget tab order
8377 0 : sortWidgets();
8378 :
8379 : #if !defined(ANDROID) && !defined(IOS)
8380 0 : if( m_aContext.SignPDF )
8381 : {
8382 : // sign the document
8383 0 : PDFWriter::SignatureWidget aSignature;
8384 0 : aSignature.Name = "Signature1";
8385 0 : createControl( aSignature, 0 );
8386 : }
8387 : #endif
8388 :
8389 : // emit additional streams
8390 0 : CHECK_RETURN( emitAdditionalStreams() );
8391 :
8392 : // emit catalog
8393 0 : CHECK_RETURN( emitCatalog() );
8394 :
8395 : #if !defined(ANDROID) && !defined(IOS)
8396 0 : if (m_nSignatureObject != -1) // if document is signed, emit sigdict
8397 : {
8398 0 : if( !emitSignature() )
8399 : {
8400 0 : m_aErrors.insert( PDFWriter::Error_Signature_Failed );
8401 0 : return false;
8402 : }
8403 : }
8404 : #endif
8405 :
8406 : // emit trailer
8407 0 : CHECK_RETURN( emitTrailer() );
8408 :
8409 : #if !defined(ANDROID) && !defined(IOS)
8410 0 : if (m_nSignatureObject != -1) // finalize the signature
8411 : {
8412 0 : if( !finalizeSignature() )
8413 : {
8414 0 : m_aErrors.insert( PDFWriter::Error_Signature_Failed );
8415 0 : return false;
8416 : }
8417 : }
8418 : #endif
8419 :
8420 0 : m_aFile.close();
8421 0 : m_bOpen = false;
8422 :
8423 0 : return true;
8424 : }
8425 :
8426 :
8427 0 : sal_Int32 PDFWriterImpl::getSystemFont( const vcl::Font& i_rFont )
8428 : {
8429 0 : getReferenceDevice()->Push();
8430 0 : getReferenceDevice()->SetFont( i_rFont );
8431 0 : getReferenceDevice()->ImplNewFont();
8432 :
8433 0 : const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
8434 0 : sal_Int32 nFontID = 0;
8435 0 : FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont );
8436 0 : if( it != m_aSystemFonts.end() )
8437 0 : nFontID = it->second.m_nNormalFontID;
8438 : else
8439 : {
8440 0 : nFontID = m_nNextFID++;
8441 0 : m_aSystemFonts[ pDevFont ] = EmbedFont();
8442 0 : m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID;
8443 : }
8444 :
8445 0 : getReferenceDevice()->Pop();
8446 0 : getReferenceDevice()->ImplNewFont();
8447 :
8448 0 : return nFontID;
8449 : }
8450 :
8451 0 : bool PDFWriterImpl::registerGlyphs( int nGlyphs,
8452 : sal_GlyphId* pGlyphs,
8453 : sal_Int32* pGlyphWidths,
8454 : sal_Ucs* pUnicodes,
8455 : sal_Int32* pUnicodesPerGlyph,
8456 : sal_uInt8* pMappedGlyphs,
8457 : sal_Int32* pMappedFontObjects,
8458 : const PhysicalFontFace* pFallbackFonts[] )
8459 : {
8460 0 : SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics();
8461 :
8462 0 : if (!pGraphics)
8463 0 : return false;
8464 :
8465 0 : const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
8466 0 : sal_Ucs* pCurUnicode = pUnicodes;
8467 0 : for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ )
8468 : {
8469 0 : const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB);
8470 0 : const PhysicalFontFace* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont;
8471 :
8472 0 : if( pCurrentFont->mbSubsettable )
8473 : {
8474 0 : FontSubset& rSubset = m_aSubsets[ pCurrentFont ];
8475 : // search for font specific glyphID
8476 0 : FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId );
8477 0 : if( it != rSubset.m_aMapping.end() )
8478 : {
8479 0 : pMappedFontObjects[i] = it->second.m_nFontID;
8480 0 : pMappedGlyphs[i] = it->second.m_nSubsetGlyphID;
8481 : }
8482 : else
8483 : {
8484 : // create new subset if necessary
8485 0 : if( rSubset.m_aSubsets.empty()
8486 0 : || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
8487 : {
8488 0 : rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) );
8489 : }
8490 :
8491 : // copy font id
8492 0 : pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID;
8493 : // create new glyph in subset
8494 0 : sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
8495 0 : pMappedGlyphs[i] = nNewId;
8496 :
8497 : // add new glyph to emitted font subset
8498 0 : GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
8499 0 : rNewGlyphEmit.setGlyphId( nNewId );
8500 0 : for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ )
8501 0 : rNewGlyphEmit.addCode( pCurUnicode[n] );
8502 :
8503 : // add new glyph to font mapping
8504 0 : Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
8505 0 : rNewGlyph.m_nFontID = pMappedFontObjects[i];
8506 0 : rNewGlyph.m_nSubsetGlyphID = nNewId;
8507 : }
8508 0 : if (!getReferenceDevice()->AcquireGraphics())
8509 0 : return false;
8510 0 : const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0);
8511 0 : pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont,
8512 : nFontGlyphId,
8513 : bVertical,
8514 0 : pGraphics );
8515 : }
8516 0 : else if( pCurrentFont->IsEmbeddable() )
8517 : {
8518 0 : sal_Int32 nFontID = 0;
8519 0 : FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
8520 0 : if( it != m_aEmbeddedFonts.end() )
8521 0 : nFontID = it->second.m_nNormalFontID;
8522 : else
8523 : {
8524 0 : nFontID = m_nNextFID++;
8525 0 : m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
8526 0 : m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
8527 : }
8528 0 : EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont];
8529 :
8530 0 : const Ucs2SIntMap* pEncoding = NULL;
8531 0 : const Ucs2OStrMap* pNonEncoded = NULL;
8532 0 : if (!getReferenceDevice()->AcquireGraphics())
8533 0 : return false;
8534 0 : pEncoding = pGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded, 0);
8535 :
8536 0 : Ucs2SIntMap::const_iterator enc_it;
8537 0 : Ucs2OStrMap::const_iterator nonenc_it;
8538 :
8539 0 : sal_Int32 nCurFontID = nFontID;
8540 0 : sal_Ucs cChar = *pCurUnicode;
8541 0 : if( pEncoding )
8542 : {
8543 0 : enc_it = pEncoding->find( cChar );
8544 0 : if( enc_it != pEncoding->end() && enc_it->second > 0 )
8545 : {
8546 : DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" );
8547 0 : cChar = (sal_Ucs)enc_it->second;
8548 : }
8549 0 : else if( (enc_it == pEncoding->end() || enc_it->second == -1) &&
8550 0 : pNonEncoded &&
8551 0 : (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() )
8552 : {
8553 0 : nCurFontID = 0;
8554 : // find non encoded glyph
8555 0 : for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it )
8556 : {
8557 0 : if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() )
8558 : {
8559 0 : nCurFontID = nec_it->m_nFontID;
8560 0 : cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ];
8561 0 : break;
8562 : }
8563 : }
8564 0 : if( nCurFontID == 0 ) // new nonencoded glyph
8565 : {
8566 0 : if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 )
8567 : {
8568 0 : rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() );
8569 0 : rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++;
8570 : }
8571 0 : EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back();
8572 0 : rEncoding.m_aEncVector.push_back( EmbedCode() );
8573 0 : rEncoding.m_aEncVector.back().m_aUnicode = cChar;
8574 0 : rEncoding.m_aEncVector.back().m_aName = nonenc_it->second;
8575 0 : rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1);
8576 0 : nCurFontID = rEncoding.m_nFontID;
8577 0 : cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ];
8578 : }
8579 : }
8580 : else
8581 0 : pEncoding = NULL;
8582 : }
8583 0 : if( ! pEncoding )
8584 : {
8585 0 : if( cChar & 0xff00 )
8586 : {
8587 : // some characters can be used by conversion
8588 0 : if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area
8589 0 : cChar -= 0xf000;
8590 : else
8591 : {
8592 0 : OString aChar(&cChar, 1, RTL_TEXTENCODING_MS_1252);
8593 0 : cChar = ((sal_Ucs)aChar[0]) & 0x00ff;
8594 : }
8595 : }
8596 : }
8597 :
8598 0 : pMappedGlyphs[ i ] = (sal_Int8)cChar;
8599 0 : pMappedFontObjects[ i ] = nCurFontID;
8600 0 : pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont,
8601 : (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR,
8602 : false,
8603 0 : pGraphics );
8604 : }
8605 : }
8606 0 : return true;
8607 : }
8608 :
8609 0 : void PDFWriterImpl::drawRelief( SalLayout& rLayout, const OUString& rText, bool bTextLines )
8610 : {
8611 0 : push( PushFlags::ALL );
8612 :
8613 0 : FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief();
8614 :
8615 0 : Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor();
8616 0 : Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
8617 0 : Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
8618 0 : Color aReliefColor( COL_LIGHTGRAY );
8619 0 : if( aTextColor == COL_BLACK )
8620 0 : aTextColor = Color( COL_WHITE );
8621 0 : if( aTextLineColor == COL_BLACK )
8622 0 : aTextLineColor = Color( COL_WHITE );
8623 0 : if( aOverlineColor == COL_BLACK )
8624 0 : aOverlineColor = Color( COL_WHITE );
8625 0 : if( aTextColor == COL_WHITE )
8626 0 : aReliefColor = Color( COL_BLACK );
8627 :
8628 0 : Font aSetFont = m_aCurrentPDFState.m_aFont;
8629 0 : aSetFont.SetRelief( RELIEF_NONE );
8630 0 : aSetFont.SetShadow( false );
8631 :
8632 0 : aSetFont.SetColor( aReliefColor );
8633 0 : setTextLineColor( aReliefColor );
8634 0 : setOverlineColor( aReliefColor );
8635 0 : setFont( aSetFont );
8636 0 : long nOff = 1 + getReferenceDevice()->mnDPIX/300;
8637 0 : if( eRelief == RELIEF_ENGRAVED )
8638 0 : nOff = -nOff;
8639 :
8640 0 : rLayout.DrawOffset() += Point( nOff, nOff );
8641 0 : updateGraphicsState();
8642 0 : drawLayout( rLayout, rText, bTextLines );
8643 :
8644 0 : rLayout.DrawOffset() -= Point( nOff, nOff );
8645 0 : setTextLineColor( aTextLineColor );
8646 0 : setOverlineColor( aOverlineColor );
8647 0 : aSetFont.SetColor( aTextColor );
8648 0 : setFont( aSetFont );
8649 0 : updateGraphicsState();
8650 0 : drawLayout( rLayout, rText, bTextLines );
8651 :
8652 : // clean up the mess
8653 0 : pop();
8654 0 : }
8655 :
8656 0 : void PDFWriterImpl::drawShadow( SalLayout& rLayout, const OUString& rText, bool bTextLines )
8657 : {
8658 0 : Font aSaveFont = m_aCurrentPDFState.m_aFont;
8659 0 : Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
8660 0 : Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
8661 :
8662 0 : Font& rFont = m_aCurrentPDFState.m_aFont;
8663 0 : if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 )
8664 0 : rFont.SetColor( Color( COL_LIGHTGRAY ) );
8665 : else
8666 0 : rFont.SetColor( Color( COL_BLACK ) );
8667 0 : rFont.SetShadow( false );
8668 0 : rFont.SetOutline( false );
8669 0 : setFont( rFont );
8670 0 : setTextLineColor( rFont.GetColor() );
8671 0 : setOverlineColor( rFont.GetColor() );
8672 0 : updateGraphicsState();
8673 :
8674 0 : long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24);
8675 0 : if( rFont.IsOutline() )
8676 0 : nOff++;
8677 0 : rLayout.DrawBase() += Point( nOff, nOff );
8678 0 : drawLayout( rLayout, rText, bTextLines );
8679 0 : rLayout.DrawBase() -= Point( nOff, nOff );
8680 :
8681 0 : setFont( aSaveFont );
8682 0 : setTextLineColor( aSaveTextLineColor );
8683 0 : setOverlineColor( aSaveOverlineColor );
8684 0 : updateGraphicsState();
8685 0 : }
8686 :
8687 0 : void PDFWriterImpl::drawVerticalGlyphs(
8688 : const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
8689 : OStringBuffer& rLine,
8690 : const Point& rAlignOffset,
8691 : const Matrix3& rRotScale,
8692 : double fAngle,
8693 : double fXScale,
8694 : double fSkew,
8695 : sal_Int32 nFontHeight )
8696 : {
8697 0 : long nXOffset = 0;
8698 0 : Point aCurPos( rGlyphs[0].m_aPos );
8699 0 : aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
8700 0 : aCurPos += rAlignOffset;
8701 0 : for( size_t i = 0; i < rGlyphs.size(); i++ )
8702 : {
8703 : // have to emit each glyph on its own
8704 0 : double fDeltaAngle = 0.0;
8705 0 : double fYScale = 1.0;
8706 0 : double fTempXScale = fXScale;
8707 0 : double fSkewB = fSkew;
8708 0 : double fSkewA = 0.0;
8709 :
8710 0 : Point aDeltaPos;
8711 0 : if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL )
8712 : {
8713 0 : fDeltaAngle = M_PI/2.0;
8714 0 : aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent();
8715 0 : aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale);
8716 0 : fYScale = fXScale;
8717 0 : fTempXScale = 1.0;
8718 0 : fSkewA = -fSkewB;
8719 0 : fSkewB = 0.0;
8720 : }
8721 0 : else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR )
8722 : {
8723 0 : fDeltaAngle = -M_PI/2.0;
8724 0 : aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale);
8725 0 : aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent();
8726 0 : fYScale = fXScale;
8727 0 : fTempXScale = 1.0;
8728 0 : fSkewA = fSkewB;
8729 0 : fSkewB = 0.0;
8730 : }
8731 0 : aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) );
8732 0 : if( i < rGlyphs.size()-1 )
8733 : // #i120627# the text on the Y axis is reversed when export ppt file to PDF format
8734 : {
8735 0 : long nOffsetX = rGlyphs[i+1].m_aPos.X() - rGlyphs[i].m_aPos.X();
8736 0 : long nOffsetY = rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
8737 0 : nXOffset += (int)sqrt(double(nOffsetX*nOffsetX + nOffsetY*nOffsetY));
8738 : }
8739 0 : if( ! rGlyphs[i].m_nGlyphId )
8740 0 : continue;
8741 :
8742 0 : aDeltaPos = rRotScale.transform( aDeltaPos );
8743 :
8744 0 : Matrix3 aMat;
8745 0 : if( fSkewB != 0.0 || fSkewA != 0.0 )
8746 0 : aMat.skew( fSkewA, fSkewB );
8747 0 : aMat.scale( fTempXScale, fYScale );
8748 0 : aMat.rotate( fAngle+fDeltaAngle );
8749 0 : aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
8750 0 : aMat.append( m_aPages.back(), rLine );
8751 0 : rLine.append( " Tm" );
8752 0 : if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
8753 : {
8754 0 : rLine.append( " /F" );
8755 0 : rLine.append( rGlyphs[i].m_nMappedFontId );
8756 0 : rLine.append( ' ' );
8757 0 : m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
8758 0 : rLine.append( " Tf" );
8759 : }
8760 0 : rLine.append( "<" );
8761 0 : appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
8762 0 : rLine.append( ">Tj\n" );
8763 0 : }
8764 0 : }
8765 :
8766 0 : void PDFWriterImpl::drawHorizontalGlyphs(
8767 : const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
8768 : OStringBuffer& rLine,
8769 : const Point& rAlignOffset,
8770 : double fAngle,
8771 : double fXScale,
8772 : double fSkew,
8773 : sal_Int32 nFontHeight,
8774 : sal_Int32 nPixelFontHeight
8775 : )
8776 : {
8777 : // horizontal (= normal) case
8778 :
8779 : // fill in run end indices
8780 : // end is marked by index of the first glyph of the next run
8781 : // a run is marked by same mapped font id and same Y position
8782 0 : std::vector< sal_uInt32 > aRunEnds;
8783 0 : aRunEnds.reserve( rGlyphs.size() );
8784 0 : for( size_t i = 1; i < rGlyphs.size(); i++ )
8785 : {
8786 0 : if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
8787 0 : rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
8788 : {
8789 0 : aRunEnds.push_back(i);
8790 : }
8791 : }
8792 : // last run ends at last glyph
8793 0 : aRunEnds.push_back( rGlyphs.size() );
8794 :
8795 : // loop over runs of the same font
8796 0 : sal_uInt32 nBeginRun = 0;
8797 0 : for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
8798 : {
8799 : // setup text matrix
8800 0 : Point aCurPos = rGlyphs[nBeginRun].m_aPos;
8801 : // back transformation to current coordinate system
8802 0 : aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
8803 0 : aCurPos += rAlignOffset;
8804 : // the first run can be set with "Td" operator
8805 : // subsequent use of that operator would move
8806 : // the texline matrix relative to what was set before
8807 : // making use of that would drive us into rounding issues
8808 0 : Matrix3 aMat;
8809 0 : if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
8810 : {
8811 0 : m_aPages.back().appendPoint( aCurPos, rLine, false );
8812 0 : rLine.append( " Td " );
8813 : }
8814 : else
8815 : {
8816 0 : if( fSkew != 0.0 )
8817 0 : aMat.skew( 0.0, fSkew );
8818 0 : aMat.scale( fXScale, 1.0 );
8819 0 : aMat.rotate( fAngle );
8820 0 : aMat.translate( aCurPos.X(), aCurPos.Y() );
8821 0 : aMat.append( m_aPages.back(), rLine );
8822 0 : rLine.append( " Tm\n" );
8823 : }
8824 : // set up correct font
8825 0 : rLine.append( "/F" );
8826 0 : rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
8827 0 : rLine.append( ' ' );
8828 0 : m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
8829 0 : rLine.append( " Tf" );
8830 :
8831 : // output glyphs using Tj or TJ
8832 0 : OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
8833 0 : aKernedLine.append( "[<" );
8834 0 : aUnkernedLine.append( '<' );
8835 0 : appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
8836 0 : appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
8837 :
8838 0 : aMat.invert();
8839 0 : bool bNeedKern = false;
8840 0 : for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
8841 : {
8842 0 : appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
8843 : // check if default glyph positioning is sufficient
8844 0 : const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
8845 0 : const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
8846 0 : double fAdvance = aThisPos.X() - aPrevPos.X();
8847 0 : fAdvance *= 1000.0 / nPixelFontHeight;
8848 0 : const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5);
8849 0 : if( nAdjustment != 0 )
8850 : {
8851 : // apply individual glyph positioning
8852 0 : bNeedKern = true;
8853 0 : aKernedLine.append( ">" );
8854 0 : aKernedLine.append( nAdjustment );
8855 0 : aKernedLine.append( "<" );
8856 : }
8857 0 : appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
8858 : }
8859 0 : aKernedLine.append( ">]TJ\n" );
8860 0 : aUnkernedLine.append( ">Tj\n" );
8861 : rLine.append(
8862 0 : (bNeedKern ? aKernedLine : aUnkernedLine).makeStringAndClear() );
8863 :
8864 : // set beginning of next run
8865 0 : nBeginRun = aRunEnds[nRun];
8866 0 : }
8867 0 : }
8868 :
8869 0 : void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool bTextLines )
8870 : {
8871 : // relief takes precedence over shadow (see outdev3.cxx)
8872 0 : if( m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE )
8873 : {
8874 0 : drawRelief( rLayout, rText, bTextLines );
8875 0 : return;
8876 : }
8877 0 : else if( m_aCurrentPDFState.m_aFont.IsShadow() )
8878 0 : drawShadow( rLayout, rText, bTextLines );
8879 :
8880 0 : OStringBuffer aLine( 512 );
8881 :
8882 0 : const int nMaxGlyphs = 256;
8883 :
8884 : sal_GlyphId pGlyphs[nMaxGlyphs];
8885 : sal_Int32 pGlyphWidths[nMaxGlyphs];
8886 : sal_uInt8 pMappedGlyphs[nMaxGlyphs];
8887 : sal_Int32 pMappedFontObjects[nMaxGlyphs];
8888 0 : std::vector<sal_Ucs> aUnicodes;
8889 0 : aUnicodes.reserve( nMaxGlyphs );
8890 : sal_Int32 pUnicodesPerGlyph[nMaxGlyphs];
8891 : int pCharPosAry[nMaxGlyphs];
8892 : DeviceCoordinate nAdvanceWidths[nMaxGlyphs];
8893 0 : const PhysicalFontFace* pFallbackFonts[nMaxGlyphs] = { NULL };
8894 0 : bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
8895 : int nGlyphs;
8896 0 : int nIndex = 0;
8897 0 : int nMinCharPos = 0, nMaxCharPos = rText.getLength()-1;
8898 0 : double fXScale = 1.0;
8899 0 : double fSkew = 0.0;
8900 0 : sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight;
8901 0 : TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
8902 :
8903 : // transform font height back to current units
8904 : // note: the layout calculates in outdevs device pixel !!
8905 0 : sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
8906 0 : if( m_aCurrentPDFState.m_aFont.GetWidth() )
8907 : {
8908 0 : Font aFont( m_aCurrentPDFState.m_aFont );
8909 0 : aFont.SetWidth( 0 );
8910 0 : FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
8911 0 : if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() )
8912 : {
8913 : fXScale =
8914 0 : (double)m_aCurrentPDFState.m_aFont.GetWidth() /
8915 0 : (double)aMetric.GetWidth();
8916 : }
8917 : // force state before GetFontMetric
8918 0 : m_pReferenceDevice->ImplNewFont();
8919 : }
8920 :
8921 : // perform artificial italics if necessary
8922 0 : if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
8923 0 : m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
8924 0 : !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL ||
8925 0 : m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE )
8926 : )
8927 : {
8928 0 : fSkew = M_PI/12.0;
8929 : }
8930 :
8931 : // if the mapmode is distorted we need to adjust for that also
8932 0 : if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
8933 : {
8934 0 : fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
8935 : }
8936 :
8937 0 : int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
8938 : // normalize angles
8939 0 : while( nAngle < 0 )
8940 0 : nAngle += 3600;
8941 0 : nAngle = nAngle % 3600;
8942 0 : double fAngle = (double)nAngle * M_PI / 1800.0;
8943 :
8944 0 : Matrix3 aRotScale;
8945 0 : aRotScale.scale( fXScale, 1.0 );
8946 0 : if( fAngle != 0.0 )
8947 0 : aRotScale.rotate( -fAngle );
8948 :
8949 0 : bool bPop = false;
8950 0 : bool bABold = false;
8951 : // artificial bold necessary ?
8952 0 : if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
8953 0 : m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
8954 : {
8955 0 : if( ! bPop )
8956 0 : aLine.append( "q " );
8957 0 : bPop = true;
8958 0 : bABold = true;
8959 : }
8960 : // setup text colors (if necessary)
8961 0 : Color aStrokeColor( COL_TRANSPARENT );
8962 0 : Color aNonStrokeColor( COL_TRANSPARENT );
8963 :
8964 0 : if( m_aCurrentPDFState.m_aFont.IsOutline() )
8965 : {
8966 0 : aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
8967 0 : aNonStrokeColor = Color( COL_WHITE );
8968 : }
8969 : else
8970 0 : aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
8971 0 : if( bABold )
8972 0 : aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
8973 :
8974 0 : if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
8975 : {
8976 0 : if( ! bPop )
8977 0 : aLine.append( "q " );
8978 0 : bPop = true;
8979 0 : appendStrokingColor( aStrokeColor, aLine );
8980 0 : aLine.append( "\n" );
8981 : }
8982 0 : if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
8983 : {
8984 0 : if( ! bPop )
8985 0 : aLine.append( "q " );
8986 0 : bPop = true;
8987 0 : appendNonStrokingColor( aNonStrokeColor, aLine );
8988 0 : aLine.append( "\n" );
8989 : }
8990 :
8991 : // begin text object
8992 0 : aLine.append( "BT\n" );
8993 : // outline attribute ?
8994 0 : if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
8995 : {
8996 : // set correct text mode, set stroke width
8997 0 : aLine.append( "2 Tr " ); // fill, then stroke
8998 :
8999 0 : if( m_aCurrentPDFState.m_aFont.IsOutline() )
9000 : {
9001 : // unclear what to do in case of outline and artificial bold
9002 : // for the time being outline wins
9003 0 : aLine.append( "0.25 w \n" );
9004 : }
9005 : else
9006 : {
9007 0 : double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0;
9008 0 : m_aPages.back().appendMappedLength( fW, aLine );
9009 0 : aLine.append ( " w\n" );
9010 : }
9011 : }
9012 :
9013 0 : FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
9014 :
9015 : // collect the glyphs into a single array
9016 0 : const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
9017 0 : std::vector< PDFGlyph > aGlyphs;
9018 0 : aGlyphs.reserve( nTmpMaxGlyphs );
9019 : // first get all the glyphs and register them; coordinates still in Pixel
9020 0 : Point aGNGlyphPos;
9021 0 : while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry, pFallbackFonts )) != 0 )
9022 : {
9023 0 : aUnicodes.clear();
9024 0 : for( int i = 0; i < nGlyphs; i++ )
9025 : {
9026 : // default case: 1 glyph is one unicode
9027 0 : pUnicodesPerGlyph[i] = 1;
9028 0 : if( (pGlyphs[i] & GF_ISCHAR) )
9029 : {
9030 0 : aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) );
9031 : }
9032 0 : else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
9033 : {
9034 0 : int nChars = 1;
9035 0 : aUnicodes.push_back( rText[ pCharPosAry[i] ] );
9036 0 : pUnicodesPerGlyph[i] = 1;
9037 : // try to handle ligatures and such
9038 0 : if( i < nGlyphs-1 )
9039 : {
9040 0 : nChars = pCharPosAry[i+1] - pCharPosAry[i];
9041 : // #i115618# fix for simple RTL+CTL cases
9042 : // TODO: sanitize for RTL ligatures, more complex CTL, etc.
9043 0 : if( nChars < 0 )
9044 0 : nChars = -nChars;
9045 0 : else if( nChars == 0 )
9046 0 : nChars = 1;
9047 0 : pUnicodesPerGlyph[i] = nChars;
9048 0 : for( int n = 1; n < nChars; n++ )
9049 0 : aUnicodes.push_back( rText[ pCharPosAry[i] + n ] );
9050 : }
9051 : // #i36691# hack that is needed because currently the pGlyphs[]
9052 : // argument is ignored for embeddable fonts and so the layout
9053 : // engine's glyph work is ignored (i.e. char mirroring)
9054 : // TODO: a real solution would be to map the layout engine's
9055 : // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
9056 : // back to unicode and then to embeddable font's encoding
9057 0 : if( (getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL) != TEXT_LAYOUT_DEFAULT )
9058 : {
9059 0 : size_t nI = aUnicodes.size()-1;
9060 0 : for( int n = 0; n < nChars; n++, nI-- )
9061 0 : aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI]));
9062 0 : }
9063 : }
9064 : else
9065 0 : aUnicodes.push_back( 0 );
9066 : // note: in case of ctl one character may result
9067 : // in multiple glyphs. The current SalLayout
9068 : // implementations set -1 then to indicate that no direct
9069 : // mapping is possible
9070 : }
9071 :
9072 0 : registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
9073 :
9074 0 : for( int i = 0; i < nGlyphs; i++ )
9075 : {
9076 : aGlyphs.push_back( PDFGlyph( aGNGlyphPos,
9077 : pGlyphWidths[i],
9078 0 : pGlyphs[i],
9079 : pMappedFontObjects[i],
9080 0 : pMappedGlyphs[i] ) );
9081 0 : if( bVertical )
9082 0 : aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
9083 : else
9084 0 : aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
9085 : }
9086 : }
9087 :
9088 0 : Point aAlignOffset;
9089 0 : if ( eAlign == ALIGN_BOTTOM )
9090 0 : aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
9091 0 : else if ( eAlign == ALIGN_TOP )
9092 0 : aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
9093 0 : if( aAlignOffset.X() || aAlignOffset.Y() )
9094 0 : aAlignOffset = aRotScale.transform( aAlignOffset );
9095 :
9096 : /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
9097 : string contained only on of the UTF16 BOMs
9098 : */
9099 0 : if( ! aGlyphs.empty() )
9100 : {
9101 0 : if( bVertical )
9102 0 : drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
9103 : else
9104 0 : drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
9105 : }
9106 :
9107 : // end textobject
9108 0 : aLine.append( "ET\n" );
9109 0 : if( bPop )
9110 0 : aLine.append( "Q\n" );
9111 :
9112 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9113 :
9114 : // draw eventual textlines
9115 0 : FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
9116 0 : FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
9117 0 : FontUnderline eOverline = m_aCurrentPDFState.m_aFont.GetOverline();
9118 0 : if( bTextLines &&
9119 : (
9120 0 : ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) ||
9121 0 : ( eOverline != UNDERLINE_NONE && eOverline != UNDERLINE_DONTKNOW ) ||
9122 0 : ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
9123 : )
9124 : )
9125 : {
9126 0 : bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
9127 0 : if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
9128 : {
9129 0 : Point aPos, aStartPt;
9130 0 : sal_Int32 nWidth = 0;
9131 0 : DeviceCoordinate nAdvance = 0;
9132 0 : for( int nStart = 0;;)
9133 : {
9134 : sal_GlyphId aGlyphId;
9135 0 : if( !rLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) )
9136 0 : break;
9137 :
9138 0 : if( !SalLayout::IsSpacingGlyph( aGlyphId ) )
9139 : {
9140 0 : if( !nWidth )
9141 0 : aStartPt = aPos;
9142 :
9143 0 : nWidth += nAdvance;
9144 : }
9145 0 : else if( nWidth > 0 )
9146 : {
9147 0 : drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
9148 : m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
9149 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
9150 0 : nWidth = 0;
9151 : }
9152 0 : }
9153 :
9154 0 : if( nWidth > 0 )
9155 : {
9156 0 : drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
9157 : m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
9158 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
9159 : }
9160 : }
9161 : else
9162 : {
9163 0 : Point aStartPt = rLayout.GetDrawPosition();
9164 0 : int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
9165 0 : drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
9166 : m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
9167 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
9168 : }
9169 : }
9170 :
9171 : // write eventual emphasis marks
9172 0 : if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
9173 : {
9174 0 : tools::PolyPolygon aEmphPoly;
9175 0 : Rectangle aEmphRect1;
9176 0 : Rectangle aEmphRect2;
9177 : long nEmphYOff;
9178 : long nEmphWidth;
9179 : long nEmphHeight;
9180 : bool bEmphPolyLine;
9181 : FontEmphasisMark nEmphMark;
9182 :
9183 0 : push( PushFlags::ALL );
9184 :
9185 0 : aLine.setLength( 0 );
9186 0 : aLine.append( "q\n" );
9187 :
9188 0 : nEmphMark = OutputDevice::ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
9189 0 : if ( nEmphMark & EMPHASISMARK_POS_BELOW )
9190 0 : nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
9191 : else
9192 0 : nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
9193 : m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
9194 : bEmphPolyLine,
9195 : aEmphRect1,
9196 : aEmphRect2,
9197 : nEmphYOff,
9198 : nEmphWidth,
9199 : nEmphMark,
9200 : m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight),
9201 0 : m_pReferenceDevice->mpFontEntry->mnOrientation );
9202 0 : if ( bEmphPolyLine )
9203 : {
9204 0 : setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
9205 0 : setFillColor( Color( COL_TRANSPARENT ) );
9206 : }
9207 : else
9208 : {
9209 0 : setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
9210 0 : setLineColor( Color( COL_TRANSPARENT ) );
9211 : }
9212 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9213 :
9214 0 : Point aOffset = Point(0,0);
9215 :
9216 0 : if ( nEmphMark & EMPHASISMARK_POS_BELOW )
9217 0 : aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff;
9218 : else
9219 0 : aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff;
9220 :
9221 0 : long nEmphWidth2 = nEmphWidth / 2;
9222 0 : long nEmphHeight2 = nEmphHeight / 2;
9223 0 : aOffset += Point( nEmphWidth2, nEmphHeight2 );
9224 :
9225 0 : if ( eAlign == ALIGN_BOTTOM )
9226 0 : aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent;
9227 0 : else if ( eAlign == ALIGN_TOP )
9228 0 : aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent;
9229 :
9230 0 : for( int nStart = 0;;)
9231 : {
9232 0 : Point aPos;
9233 : sal_GlyphId aGlyphId;
9234 : DeviceCoordinate nAdvance;
9235 0 : if( !rLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) )
9236 0 : break;
9237 :
9238 0 : if( !SalLayout::IsSpacingGlyph( aGlyphId ) )
9239 : {
9240 0 : Point aAdjOffset = aOffset;
9241 0 : aAdjOffset.X() += (nAdvance - nEmphWidth) / 2;
9242 0 : aAdjOffset = aRotScale.transform( aAdjOffset );
9243 :
9244 0 : aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
9245 :
9246 0 : aPos += aAdjOffset;
9247 0 : aPos = m_pReferenceDevice->PixelToLogic( aPos );
9248 0 : drawEmphasisMark( aPos.X(), aPos.Y(),
9249 : aEmphPoly, bEmphPolyLine,
9250 0 : aEmphRect1, aEmphRect2 );
9251 : }
9252 0 : }
9253 :
9254 0 : writeBuffer( "Q\n", 2 );
9255 0 : pop();
9256 0 : }
9257 : }
9258 :
9259 0 : void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
9260 : const tools::PolyPolygon& rPolyPoly, bool bPolyLine,
9261 : const Rectangle& rRect1, const Rectangle& rRect2 )
9262 : {
9263 : // TODO: pass nWidth as width of this mark
9264 : // long nWidth = 0;
9265 :
9266 0 : if ( rPolyPoly.Count() )
9267 : {
9268 0 : if ( bPolyLine )
9269 : {
9270 0 : Polygon aPoly = rPolyPoly.GetObject( 0 );
9271 0 : aPoly.Move( nX, nY );
9272 0 : drawPolyLine( aPoly );
9273 : }
9274 : else
9275 : {
9276 0 : tools::PolyPolygon aPolyPoly = rPolyPoly;
9277 0 : aPolyPoly.Move( nX, nY );
9278 0 : drawPolyPolygon( aPolyPoly );
9279 : }
9280 : }
9281 :
9282 0 : if ( !rRect1.IsEmpty() )
9283 : {
9284 0 : Rectangle aRect( Point( nX+rRect1.Left(),
9285 0 : nY+rRect1.Top() ), rRect1.GetSize() );
9286 0 : drawRectangle( aRect );
9287 : }
9288 :
9289 0 : if ( !rRect2.IsEmpty() )
9290 : {
9291 0 : Rectangle aRect( Point( nX+rRect2.Left(),
9292 0 : nY+rRect2.Top() ), rRect2.GetSize() );
9293 :
9294 0 : drawRectangle( aRect );
9295 : }
9296 0 : }
9297 :
9298 0 : void PDFWriterImpl::drawText( const Point& rPos, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines )
9299 : {
9300 0 : MARK( "drawText" );
9301 :
9302 0 : updateGraphicsState();
9303 :
9304 : // get a layout from the OuputDevice's SalGraphics
9305 : // this also enforces font substitution and sets the font on SalGraphics
9306 0 : SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
9307 0 : if( pLayout )
9308 : {
9309 0 : drawLayout( *pLayout, rText, bTextLines );
9310 0 : pLayout->Release();
9311 : }
9312 0 : }
9313 :
9314 0 : void PDFWriterImpl::drawTextArray( const Point& rPos, const OUString& rText, const long* pDXArray, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines )
9315 : {
9316 0 : MARK( "drawText with array" );
9317 :
9318 0 : updateGraphicsState();
9319 :
9320 : // get a layout from the OuputDevice's SalGraphics
9321 : // this also enforces font substitution and sets the font on SalGraphics
9322 0 : SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
9323 0 : if( pLayout )
9324 : {
9325 0 : drawLayout( *pLayout, rText, bTextLines );
9326 0 : pLayout->Release();
9327 : }
9328 0 : }
9329 :
9330 0 : void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines )
9331 : {
9332 0 : MARK( "drawStretchText" );
9333 :
9334 0 : updateGraphicsState();
9335 :
9336 : // get a layout from the OuputDevice's SalGraphics
9337 : // this also enforces font substitution and sets the font on SalGraphics
9338 0 : SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
9339 0 : if( pLayout )
9340 : {
9341 0 : drawLayout( *pLayout, rText, bTextLines );
9342 0 : pLayout->Release();
9343 : }
9344 0 : }
9345 :
9346 0 : void PDFWriterImpl::drawText( const Rectangle& rRect, const OUString& rOrigStr, DrawTextFlags nStyle, bool bTextLines )
9347 : {
9348 0 : long nWidth = rRect.GetWidth();
9349 0 : long nHeight = rRect.GetHeight();
9350 :
9351 0 : if ( nWidth <= 0 || nHeight <= 0 )
9352 0 : return;
9353 :
9354 0 : MARK( "drawText with rectangle" );
9355 :
9356 0 : updateGraphicsState();
9357 :
9358 : // clip with rectangle
9359 0 : OStringBuffer aLine;
9360 0 : aLine.append( "q " );
9361 0 : m_aPages.back().appendRect( rRect, aLine );
9362 0 : aLine.append( " W* n\n" );
9363 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9364 :
9365 : // if disabled text is needed, put in here
9366 :
9367 0 : Point aPos = rRect.TopLeft();
9368 :
9369 0 : long nTextHeight = m_pReferenceDevice->GetTextHeight();
9370 0 : sal_Int32 nMnemonicPos = -1;
9371 :
9372 0 : OUString aStr = rOrigStr;
9373 0 : if ( nStyle & DrawTextFlags::Mnemonic )
9374 0 : aStr = OutputDevice::GetNonMnemonicString( aStr, nMnemonicPos );
9375 :
9376 : // multiline text
9377 0 : if ( nStyle & DrawTextFlags::MultiLine )
9378 : {
9379 0 : OUString aLastLine;
9380 0 : ImplMultiTextLineInfo aMultiLineInfo;
9381 : ImplTextLineInfo* pLineInfo;
9382 : sal_Int32 i;
9383 : sal_Int32 nLines;
9384 : sal_Int32 nFormatLines;
9385 :
9386 0 : if ( nTextHeight )
9387 : {
9388 0 : vcl::DefaultTextLayout aLayout( *m_pReferenceDevice );
9389 0 : OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout );
9390 0 : nLines = nHeight/nTextHeight;
9391 0 : nFormatLines = aMultiLineInfo.Count();
9392 0 : if ( !nLines )
9393 0 : nLines = 1;
9394 0 : if ( nFormatLines > nLines )
9395 : {
9396 0 : if ( nStyle & DrawTextFlags::EndEllipsis )
9397 : {
9398 : // handle last line
9399 0 : nFormatLines = nLines-1;
9400 :
9401 0 : pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
9402 0 : aLastLine = convertLineEnd(aStr.copy(pLineInfo->GetIndex()), LINEEND_LF);
9403 : // replace line feed by space
9404 0 : aLastLine = aLastLine.replace('\n', ' ');
9405 0 : aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
9406 0 : nStyle &= ~DrawTextFlags(DrawTextFlags::VCenter | DrawTextFlags::Bottom);
9407 0 : nStyle |= DrawTextFlags::Top;
9408 : }
9409 : }
9410 :
9411 : // vertical alignment
9412 0 : if ( nStyle & DrawTextFlags::Bottom )
9413 0 : aPos.Y() += nHeight-(nFormatLines*nTextHeight);
9414 0 : else if ( nStyle & DrawTextFlags::VCenter )
9415 0 : aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
9416 :
9417 : // draw all lines excluding the last
9418 0 : for ( i = 0; i < nFormatLines; i++ )
9419 : {
9420 0 : pLineInfo = aMultiLineInfo.GetLine( i );
9421 0 : if ( nStyle & DrawTextFlags::Right )
9422 0 : aPos.X() += nWidth-pLineInfo->GetWidth();
9423 0 : else if ( nStyle & DrawTextFlags::Center )
9424 0 : aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
9425 0 : sal_Int32 nIndex = pLineInfo->GetIndex();
9426 0 : sal_Int32 nLineLen = pLineInfo->GetLen();
9427 0 : drawText( aPos, aStr, nIndex, nLineLen, bTextLines );
9428 : // mnemonics should not appear in documents,
9429 : // if the need arises, put them in here
9430 0 : aPos.Y() += nTextHeight;
9431 0 : aPos.X() = rRect.Left();
9432 : }
9433 :
9434 : // output last line left adjusted since it was shortened
9435 0 : if (!aLastLine.isEmpty())
9436 0 : drawText( aPos, aLastLine, 0, aLastLine.getLength(), bTextLines );
9437 0 : }
9438 : }
9439 : else
9440 : {
9441 0 : long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
9442 :
9443 : // Evt. Text kuerzen
9444 0 : if ( nTextWidth > nWidth )
9445 : {
9446 0 : if ( nStyle & (DrawTextFlags::EndEllipsis | DrawTextFlags::PathEllipsis | DrawTextFlags::NewsEllipsis) )
9447 : {
9448 0 : aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
9449 0 : nStyle &= ~DrawTextFlags(DrawTextFlags::Center | DrawTextFlags::Right);
9450 0 : nStyle |= DrawTextFlags::Left;
9451 0 : nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
9452 : }
9453 : }
9454 :
9455 : // vertical alignment
9456 0 : if ( nStyle & DrawTextFlags::Right )
9457 0 : aPos.X() += nWidth-nTextWidth;
9458 0 : else if ( nStyle & DrawTextFlags::Center )
9459 0 : aPos.X() += (nWidth-nTextWidth)/2;
9460 :
9461 0 : if ( nStyle & DrawTextFlags::Bottom )
9462 0 : aPos.Y() += nHeight-nTextHeight;
9463 0 : else if ( nStyle & DrawTextFlags::VCenter )
9464 0 : aPos.Y() += (nHeight-nTextHeight)/2;
9465 :
9466 : // mnemonics should be inserted here if the need arises
9467 :
9468 : // draw the actual text
9469 0 : drawText( aPos, aStr, 0, aStr.getLength(), bTextLines );
9470 : }
9471 :
9472 : // reset clip region to original value
9473 0 : aLine.setLength( 0 );
9474 0 : aLine.append( "Q\n" );
9475 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9476 : }
9477 :
9478 0 : void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
9479 : {
9480 0 : MARK( "drawLine" );
9481 :
9482 0 : updateGraphicsState();
9483 :
9484 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9485 0 : return;
9486 :
9487 0 : OStringBuffer aLine;
9488 0 : m_aPages.back().appendPoint( rStart, aLine );
9489 0 : aLine.append( " m " );
9490 0 : m_aPages.back().appendPoint( rStop, aLine );
9491 0 : aLine.append( " l S\n" );
9492 :
9493 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9494 : }
9495 :
9496 0 : void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
9497 : {
9498 0 : MARK( "drawLine with LineInfo" );
9499 0 : updateGraphicsState();
9500 :
9501 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9502 0 : return;
9503 :
9504 0 : if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 )
9505 : {
9506 0 : drawLine( rStart, rStop );
9507 0 : return;
9508 : }
9509 :
9510 0 : OStringBuffer aLine;
9511 :
9512 0 : aLine.append( "q " );
9513 0 : if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
9514 : {
9515 0 : m_aPages.back().appendPoint( rStart, aLine );
9516 0 : aLine.append( " m " );
9517 0 : m_aPages.back().appendPoint( rStop, aLine );
9518 0 : aLine.append( " l S Q\n" );
9519 :
9520 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9521 : }
9522 : else
9523 : {
9524 0 : PDFWriter::ExtLineInfo aInfo;
9525 0 : convertLineInfoToExtLineInfo( rInfo, aInfo );
9526 0 : Point aPolyPoints[2] = { rStart, rStop };
9527 0 : Polygon aPoly( 2, aPolyPoints );
9528 0 : drawPolyLine( aPoly, aInfo );
9529 0 : }
9530 : }
9531 :
9532 : #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
9533 :
9534 0 : void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
9535 : {
9536 : // note: units in pFontEntry are ref device pixel
9537 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
9538 0 : long nLineHeight = 0;
9539 0 : long nLinePos = 0;
9540 :
9541 0 : appendStrokingColor( aColor, aLine );
9542 0 : aLine.append( "\n" );
9543 :
9544 0 : if ( bIsAbove )
9545 : {
9546 0 : if ( !pFontEntry->maMetric.mnAboveWUnderlineSize )
9547 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
9548 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize );
9549 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset );
9550 : }
9551 : else
9552 : {
9553 0 : if ( !pFontEntry->maMetric.mnWUnderlineSize )
9554 0 : m_pReferenceDevice->ImplInitTextLineSize();
9555 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize );
9556 0 : nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset );
9557 : }
9558 0 : if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
9559 0 : nLineHeight = 3;
9560 :
9561 0 : long nLineWidth = getReferenceDevice()->mnDPIX/450;
9562 0 : if ( ! nLineWidth )
9563 0 : nLineWidth = 1;
9564 :
9565 0 : if ( eTextLine == UNDERLINE_BOLDWAVE )
9566 0 : nLineWidth = 3*nLineWidth;
9567 :
9568 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
9569 0 : aLine.append( " w " );
9570 :
9571 0 : if ( eTextLine == UNDERLINE_DOUBLEWAVE )
9572 : {
9573 0 : long nOrgLineHeight = nLineHeight;
9574 0 : nLineHeight /= 3;
9575 0 : if ( nLineHeight < 2 )
9576 : {
9577 0 : if ( nOrgLineHeight > 1 )
9578 0 : nLineHeight = 2;
9579 : else
9580 0 : nLineHeight = 1;
9581 : }
9582 0 : long nLineDY = nOrgLineHeight-(nLineHeight*2);
9583 0 : if ( nLineDY < nLineWidth )
9584 0 : nLineDY = nLineWidth;
9585 0 : long nLineDY2 = nLineDY/2;
9586 0 : if ( !nLineDY2 )
9587 0 : nLineDY2 = 1;
9588 :
9589 0 : nLinePos -= nLineWidth-nLineDY2;
9590 :
9591 0 : m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
9592 :
9593 0 : nLinePos += nLineWidth+nLineDY;
9594 0 : m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
9595 : }
9596 : else
9597 : {
9598 0 : if ( eTextLine != UNDERLINE_BOLDWAVE )
9599 0 : nLinePos -= nLineWidth/2;
9600 0 : m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
9601 : }
9602 0 : }
9603 :
9604 0 : void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
9605 : {
9606 : // note: units in pFontEntry are ref device pixel
9607 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
9608 0 : long nLineHeight = 0;
9609 0 : long nLinePos = 0;
9610 0 : long nLinePos2 = 0;
9611 :
9612 0 : if ( eTextLine > UNDERLINE_BOLDWAVE )
9613 0 : eTextLine = UNDERLINE_SINGLE;
9614 :
9615 0 : switch ( eTextLine )
9616 : {
9617 : case UNDERLINE_SINGLE:
9618 : case UNDERLINE_DOTTED:
9619 : case UNDERLINE_DASH:
9620 : case UNDERLINE_LONGDASH:
9621 : case UNDERLINE_DASHDOT:
9622 : case UNDERLINE_DASHDOTDOT:
9623 0 : if ( bIsAbove )
9624 : {
9625 0 : if ( !pFontEntry->maMetric.mnAboveUnderlineSize )
9626 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
9627 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize );
9628 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset );
9629 : }
9630 : else
9631 : {
9632 0 : if ( !pFontEntry->maMetric.mnUnderlineSize )
9633 0 : m_pReferenceDevice->ImplInitTextLineSize();
9634 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize );
9635 0 : nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset );
9636 : }
9637 0 : break;
9638 : case UNDERLINE_BOLD:
9639 : case UNDERLINE_BOLDDOTTED:
9640 : case UNDERLINE_BOLDDASH:
9641 : case UNDERLINE_BOLDLONGDASH:
9642 : case UNDERLINE_BOLDDASHDOT:
9643 : case UNDERLINE_BOLDDASHDOTDOT:
9644 0 : if ( bIsAbove )
9645 : {
9646 0 : if ( !pFontEntry->maMetric.mnAboveBUnderlineSize )
9647 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
9648 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize );
9649 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset );
9650 : }
9651 : else
9652 : {
9653 0 : if ( !pFontEntry->maMetric.mnBUnderlineSize )
9654 0 : m_pReferenceDevice->ImplInitTextLineSize();
9655 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize );
9656 0 : nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset );
9657 0 : nLinePos += nLineHeight/2;
9658 : }
9659 0 : break;
9660 : case UNDERLINE_DOUBLE:
9661 0 : if ( bIsAbove )
9662 : {
9663 0 : if ( !pFontEntry->maMetric.mnAboveDUnderlineSize )
9664 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
9665 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize );
9666 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 );
9667 0 : nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 );
9668 : }
9669 : else
9670 : {
9671 0 : if ( !pFontEntry->maMetric.mnDUnderlineSize )
9672 0 : m_pReferenceDevice->ImplInitTextLineSize();
9673 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize );
9674 0 : nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 );
9675 0 : nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 );
9676 : }
9677 0 : break;
9678 : default:
9679 0 : break;
9680 : }
9681 :
9682 0 : if ( nLineHeight )
9683 : {
9684 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
9685 0 : aLine.append( " w " );
9686 0 : appendStrokingColor( aColor, aLine );
9687 0 : aLine.append( "\n" );
9688 :
9689 0 : switch ( eTextLine )
9690 : {
9691 : case UNDERLINE_DOTTED:
9692 : case UNDERLINE_BOLDDOTTED:
9693 0 : aLine.append( "[ " );
9694 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
9695 0 : aLine.append( " ] 0 d\n" );
9696 0 : break;
9697 : case UNDERLINE_DASH:
9698 : case UNDERLINE_LONGDASH:
9699 : case UNDERLINE_BOLDDASH:
9700 : case UNDERLINE_BOLDLONGDASH:
9701 : {
9702 0 : sal_Int32 nDashLength = 4*nLineHeight;
9703 0 : sal_Int32 nVoidLength = 2*nLineHeight;
9704 0 : if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) )
9705 0 : nDashLength = 8*nLineHeight;
9706 :
9707 0 : aLine.append( "[ " );
9708 0 : m_aPages.back().appendMappedLength( nDashLength, aLine, false );
9709 0 : aLine.append( ' ' );
9710 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9711 0 : aLine.append( " ] 0 d\n" );
9712 : }
9713 0 : break;
9714 : case UNDERLINE_DASHDOT:
9715 : case UNDERLINE_BOLDDASHDOT:
9716 : {
9717 0 : sal_Int32 nDashLength = 4*nLineHeight;
9718 0 : sal_Int32 nVoidLength = 2*nLineHeight;
9719 0 : aLine.append( "[ " );
9720 0 : m_aPages.back().appendMappedLength( nDashLength, aLine, false );
9721 0 : aLine.append( ' ' );
9722 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9723 0 : aLine.append( ' ' );
9724 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
9725 0 : aLine.append( ' ' );
9726 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9727 0 : aLine.append( " ] 0 d\n" );
9728 : }
9729 0 : break;
9730 : case UNDERLINE_DASHDOTDOT:
9731 : case UNDERLINE_BOLDDASHDOTDOT:
9732 : {
9733 0 : sal_Int32 nDashLength = 4*nLineHeight;
9734 0 : sal_Int32 nVoidLength = 2*nLineHeight;
9735 0 : aLine.append( "[ " );
9736 0 : m_aPages.back().appendMappedLength( nDashLength, aLine, false );
9737 0 : aLine.append( ' ' );
9738 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9739 0 : aLine.append( ' ' );
9740 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
9741 0 : aLine.append( ' ' );
9742 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9743 0 : aLine.append( ' ' );
9744 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
9745 0 : aLine.append( ' ' );
9746 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
9747 0 : aLine.append( " ] 0 d\n" );
9748 : }
9749 0 : break;
9750 : default:
9751 0 : break;
9752 : }
9753 :
9754 0 : aLine.append( "0 " );
9755 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
9756 0 : aLine.append( " m " );
9757 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
9758 0 : aLine.append( ' ' );
9759 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
9760 0 : aLine.append( " l S\n" );
9761 0 : if ( eTextLine == UNDERLINE_DOUBLE )
9762 : {
9763 0 : aLine.append( "0 " );
9764 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
9765 0 : aLine.append( " m " );
9766 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
9767 0 : aLine.append( ' ' );
9768 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
9769 0 : aLine.append( " l S\n" );
9770 : }
9771 : }
9772 0 : }
9773 :
9774 0 : void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
9775 : {
9776 : // note: units in pFontEntry are ref device pixel
9777 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
9778 0 : long nLineHeight = 0;
9779 0 : long nLinePos = 0;
9780 0 : long nLinePos2 = 0;
9781 :
9782 0 : if ( eStrikeout > STRIKEOUT_X )
9783 0 : eStrikeout = STRIKEOUT_SINGLE;
9784 :
9785 0 : switch ( eStrikeout )
9786 : {
9787 : case STRIKEOUT_SINGLE:
9788 0 : if ( !pFontEntry->maMetric.mnStrikeoutSize )
9789 0 : m_pReferenceDevice->ImplInitTextLineSize();
9790 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize );
9791 0 : nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset );
9792 0 : break;
9793 : case STRIKEOUT_BOLD:
9794 0 : if ( !pFontEntry->maMetric.mnBStrikeoutSize )
9795 0 : m_pReferenceDevice->ImplInitTextLineSize();
9796 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize );
9797 0 : nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset );
9798 0 : break;
9799 : case STRIKEOUT_DOUBLE:
9800 0 : if ( !pFontEntry->maMetric.mnDStrikeoutSize )
9801 0 : m_pReferenceDevice->ImplInitTextLineSize();
9802 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize );
9803 0 : nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 );
9804 0 : nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 );
9805 0 : break;
9806 : default:
9807 0 : break;
9808 : }
9809 :
9810 0 : if ( nLineHeight )
9811 : {
9812 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
9813 0 : aLine.append( " w " );
9814 0 : appendStrokingColor( aColor, aLine );
9815 0 : aLine.append( "\n" );
9816 :
9817 0 : aLine.append( "0 " );
9818 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
9819 0 : aLine.append( " m " );
9820 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
9821 0 : aLine.append( ' ' );
9822 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
9823 0 : aLine.append( " l S\n" );
9824 :
9825 0 : if ( eStrikeout == STRIKEOUT_DOUBLE )
9826 : {
9827 0 : aLine.append( "0 " );
9828 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
9829 0 : aLine.append( " m " );
9830 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
9831 0 : aLine.append( ' ' );
9832 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
9833 0 : aLine.append( " l S\n" );
9834 : }
9835 : }
9836 0 : }
9837 :
9838 0 : void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
9839 : {
9840 : //See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
9841 : //to tweak this
9842 :
9843 0 : OUString aStrikeoutChar = eStrikeout == STRIKEOUT_SLASH ? OUString( "/" ) : OUString( "X" );
9844 0 : OUString aStrikeout = aStrikeoutChar;
9845 0 : while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
9846 0 : aStrikeout += aStrikeout;
9847 :
9848 : // do not get broader than nWidth modulo 1 character
9849 0 : while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
9850 0 : aStrikeout = aStrikeout.replaceAt( 0, 1, "" );
9851 0 : aStrikeout += aStrikeoutChar;
9852 0 : bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
9853 0 : if ( bShadow )
9854 : {
9855 0 : Font aFont = m_aCurrentPDFState.m_aFont;
9856 0 : aFont.SetShadow( false );
9857 0 : setFont( aFont );
9858 0 : updateGraphicsState();
9859 : }
9860 :
9861 : // strikeout string is left aligned non-CTL text
9862 0 : ComplexTextLayoutMode nOrigTLM = m_pReferenceDevice->GetLayoutMode();
9863 0 : m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED );
9864 :
9865 0 : push( PushFlags::CLIPREGION );
9866 0 : FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
9867 0 : Rectangle aRect;
9868 0 : aRect.Left() = rPos.X();
9869 0 : aRect.Right() = aRect.Left()+nWidth;
9870 0 : aRect.Bottom() = rPos.Y()+aRefDevFontMetric.GetDescent();
9871 0 : aRect.Top() = rPos.Y()-aRefDevFontMetric.GetAscent();
9872 :
9873 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
9874 0 : if (pFontEntry->mnOrientation)
9875 : {
9876 0 : Polygon aPoly( aRect );
9877 0 : aPoly.Rotate( rPos, pFontEntry->mnOrientation);
9878 0 : aRect = aPoly.GetBoundRect();
9879 : }
9880 :
9881 0 : intersectClipRegion( aRect );
9882 0 : drawText( rPos, aStrikeout, 0, aStrikeout.getLength(), false );
9883 0 : pop();
9884 :
9885 0 : m_pReferenceDevice->SetLayoutMode( nOrigTLM );
9886 :
9887 0 : if ( bShadow )
9888 : {
9889 0 : Font aFont = m_aCurrentPDFState.m_aFont;
9890 0 : aFont.SetShadow( true );
9891 0 : setFont( aFont );
9892 0 : updateGraphicsState();
9893 0 : }
9894 0 : }
9895 :
9896 0 : void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove )
9897 : {
9898 0 : if ( !nWidth ||
9899 0 : ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
9900 0 : ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) &&
9901 0 : ((eOverline == UNDERLINE_NONE)||(eOverline == UNDERLINE_DONTKNOW)) ) )
9902 0 : return;
9903 :
9904 0 : MARK( "drawTextLine" );
9905 0 : updateGraphicsState();
9906 :
9907 : // note: units in pFontEntry are ref device pixel
9908 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
9909 0 : Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
9910 0 : Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
9911 0 : Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
9912 0 : bool bStrikeoutDone = false;
9913 0 : bool bUnderlineDone = false;
9914 0 : bool bOverlineDone = false;
9915 :
9916 0 : if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
9917 : {
9918 0 : drawStrikeoutChar( rPos, nWidth, eStrikeout );
9919 0 : bStrikeoutDone = true;
9920 : }
9921 :
9922 0 : Point aPos( rPos );
9923 0 : TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
9924 0 : if( eAlign == ALIGN_TOP )
9925 0 : aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent );
9926 0 : else if( eAlign == ALIGN_BOTTOM )
9927 0 : aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent );
9928 :
9929 0 : OStringBuffer aLine( 512 );
9930 : // save GS
9931 0 : aLine.append( "q " );
9932 :
9933 : // rotate and translate matrix
9934 0 : double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
9935 0 : Matrix3 aMat;
9936 0 : aMat.rotate( fAngle );
9937 0 : aMat.translate( aPos.X(), aPos.Y() );
9938 0 : aMat.append( m_aPages.back(), aLine );
9939 0 : aLine.append( " cm\n" );
9940 :
9941 0 : if ( aUnderlineColor.GetTransparency() != 0 )
9942 0 : aUnderlineColor = aStrikeoutColor;
9943 :
9944 0 : if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
9945 0 : (eUnderline == UNDERLINE_WAVE) ||
9946 0 : (eUnderline == UNDERLINE_DOUBLEWAVE) ||
9947 : (eUnderline == UNDERLINE_BOLDWAVE) )
9948 : {
9949 0 : drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
9950 0 : bUnderlineDone = true;
9951 : }
9952 :
9953 0 : if ( (eOverline == UNDERLINE_SMALLWAVE) ||
9954 0 : (eOverline == UNDERLINE_WAVE) ||
9955 0 : (eOverline == UNDERLINE_DOUBLEWAVE) ||
9956 : (eOverline == UNDERLINE_BOLDWAVE) )
9957 : {
9958 0 : drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
9959 0 : bOverlineDone = true;
9960 : }
9961 :
9962 0 : if ( !bUnderlineDone )
9963 : {
9964 0 : drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
9965 : }
9966 :
9967 0 : if ( !bOverlineDone )
9968 : {
9969 0 : drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
9970 : }
9971 :
9972 0 : if ( !bStrikeoutDone )
9973 : {
9974 0 : drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
9975 : }
9976 :
9977 0 : aLine.append( "Q\n" );
9978 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9979 : }
9980 :
9981 0 : void PDFWriterImpl::drawPolygon( const Polygon& rPoly )
9982 : {
9983 0 : MARK( "drawPolygon" );
9984 :
9985 0 : updateGraphicsState();
9986 :
9987 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
9988 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
9989 0 : return;
9990 :
9991 0 : int nPoints = rPoly.GetSize();
9992 0 : OStringBuffer aLine( 20 * nPoints );
9993 0 : m_aPages.back().appendPolygon( rPoly, aLine );
9994 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
9995 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
9996 0 : aLine.append( "B*\n" );
9997 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9998 0 : aLine.append( "S\n" );
9999 : else
10000 0 : aLine.append( "f*\n" );
10001 :
10002 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10003 : }
10004 :
10005 0 : void PDFWriterImpl::drawPolyPolygon( const tools::PolyPolygon& rPolyPoly )
10006 : {
10007 0 : MARK( "drawPolyPolygon" );
10008 :
10009 0 : updateGraphicsState();
10010 :
10011 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
10012 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
10013 0 : return;
10014 :
10015 0 : int nPolygons = rPolyPoly.Count();
10016 :
10017 0 : OStringBuffer aLine( 40 * nPolygons );
10018 0 : m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
10019 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
10020 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
10021 0 : aLine.append( "B*\n" );
10022 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10023 0 : aLine.append( "S\n" );
10024 : else
10025 0 : aLine.append( "f*\n" );
10026 :
10027 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10028 : }
10029 :
10030 0 : void PDFWriterImpl::drawTransparent( const tools::PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
10031 : {
10032 : DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
10033 0 : nTransparentPercent = nTransparentPercent % 100;
10034 :
10035 0 : MARK( "drawTransparent" );
10036 :
10037 0 : updateGraphicsState();
10038 :
10039 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
10040 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
10041 0 : return;
10042 :
10043 0 : if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 )
10044 : {
10045 : m_aErrors.insert( m_bIsPDF_A1 ?
10046 : PDFWriter::Warning_Transparency_Omitted_PDFA :
10047 0 : PDFWriter::Warning_Transparency_Omitted_PDF13 );
10048 :
10049 0 : drawPolyPolygon( rPolyPoly );
10050 0 : return;
10051 : }
10052 :
10053 : // create XObject
10054 0 : m_aTransparentObjects.push_back( TransparencyEmit() );
10055 : // FIXME: polygons with beziers may yield incorrect bound rect
10056 0 : m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect();
10057 : // convert rectangle to default user space
10058 0 : m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
10059 0 : m_aTransparentObjects.back().m_nObject = createObject();
10060 0 : m_aTransparentObjects.back().m_nExtGStateObject = createObject();
10061 0 : m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
10062 0 : m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 );
10063 : // create XObject's content stream
10064 0 : OStringBuffer aContent( 256 );
10065 0 : m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
10066 0 : if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
10067 0 : m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
10068 0 : aContent.append( " B*\n" );
10069 0 : else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
10070 0 : aContent.append( " S\n" );
10071 : else
10072 0 : aContent.append( " f*\n" );
10073 0 : m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() );
10074 :
10075 0 : OStringBuffer aObjName( 16 );
10076 0 : aObjName.append( "Tr" );
10077 0 : aObjName.append( m_aTransparentObjects.back().m_nObject );
10078 0 : OString aTrName( aObjName.makeStringAndClear() );
10079 0 : aObjName.append( "EGS" );
10080 0 : aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
10081 0 : OString aExtName( aObjName.makeStringAndClear() );
10082 :
10083 0 : OStringBuffer aLine( 80 );
10084 : // insert XObject
10085 0 : aLine.append( "q /" );
10086 0 : aLine.append( aExtName );
10087 0 : aLine.append( " gs /" );
10088 0 : aLine.append( aTrName );
10089 0 : aLine.append( " Do Q\n" );
10090 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10091 :
10092 0 : pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
10093 0 : pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
10094 : }
10095 :
10096 0 : void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
10097 : {
10098 0 : if( nObject >= 0 )
10099 : {
10100 0 : switch( eKind )
10101 : {
10102 : case ResXObject:
10103 0 : m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
10104 0 : if( ! m_aOutputStreams.empty() )
10105 0 : m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
10106 0 : break;
10107 : case ResExtGState:
10108 0 : m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
10109 0 : if( ! m_aOutputStreams.empty() )
10110 0 : m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
10111 0 : break;
10112 : case ResShading:
10113 0 : m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
10114 0 : if( ! m_aOutputStreams.empty() )
10115 0 : m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
10116 0 : break;
10117 : case ResPattern:
10118 0 : m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
10119 0 : if( ! m_aOutputStreams.empty() )
10120 0 : m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
10121 0 : break;
10122 : }
10123 : }
10124 0 : }
10125 :
10126 0 : void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect )
10127 : {
10128 0 : push( PushFlags::ALL );
10129 :
10130 : // force reemitting clip region inside the new stream, and
10131 : // prevent emitting an unbalanced "Q" at the start
10132 0 : clearClipRegion();
10133 : // this is needed to point m_aCurrentPDFState at the pushed state
10134 : // ... but it's pointless to actually write into the "outer" stream here!
10135 0 : updateGraphicsState(NOWRITE);
10136 :
10137 0 : m_aOutputStreams.push_front( StreamRedirect() );
10138 0 : m_aOutputStreams.front().m_pStream = pStream;
10139 0 : m_aOutputStreams.front().m_aMapMode = m_aMapMode;
10140 :
10141 0 : if( !rTargetRect.IsEmpty() )
10142 : {
10143 0 : m_aOutputStreams.front().m_aTargetRect =
10144 0 : lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10145 : m_aMapMode,
10146 : getReferenceDevice(),
10147 0 : rTargetRect );
10148 0 : Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
10149 0 : long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
10150 0 : aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
10151 0 : m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
10152 : }
10153 :
10154 : // setup graphics state for independent object stream
10155 :
10156 : // force reemitting colors
10157 0 : m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
10158 0 : m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
10159 0 : }
10160 :
10161 0 : SvStream* PDFWriterImpl::endRedirect()
10162 : {
10163 0 : SvStream* pStream = NULL;
10164 0 : if( ! m_aOutputStreams.empty() )
10165 : {
10166 0 : pStream = m_aOutputStreams.front().m_pStream;
10167 0 : m_aMapMode = m_aOutputStreams.front().m_aMapMode;
10168 0 : m_aOutputStreams.pop_front();
10169 : }
10170 :
10171 0 : pop();
10172 :
10173 0 : m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
10174 0 : m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
10175 :
10176 : // needed after pop() to set m_aCurrentPDFState
10177 0 : updateGraphicsState(NOWRITE);
10178 :
10179 0 : return pStream;
10180 : }
10181 :
10182 0 : void PDFWriterImpl::beginTransparencyGroup()
10183 : {
10184 0 : updateGraphicsState();
10185 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 )
10186 0 : beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
10187 0 : }
10188 :
10189 0 : void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
10190 : {
10191 : DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
10192 0 : nTransparentPercent = nTransparentPercent % 100;
10193 :
10194 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 )
10195 : {
10196 : // create XObject
10197 0 : m_aTransparentObjects.push_back( TransparencyEmit() );
10198 0 : m_aTransparentObjects.back().m_aBoundRect = rBoundingBox;
10199 : // convert rectangle to default user space
10200 0 : m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
10201 0 : m_aTransparentObjects.back().m_nObject = createObject();
10202 0 : m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
10203 : // get XObject's content stream
10204 0 : m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
10205 0 : m_aTransparentObjects.back().m_nExtGStateObject = createObject();
10206 :
10207 0 : OStringBuffer aObjName( 16 );
10208 0 : aObjName.append( "Tr" );
10209 0 : aObjName.append( m_aTransparentObjects.back().m_nObject );
10210 0 : OString aTrName( aObjName.makeStringAndClear() );
10211 0 : aObjName.append( "EGS" );
10212 0 : aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
10213 0 : OString aExtName( aObjName.makeStringAndClear() );
10214 :
10215 0 : OStringBuffer aLine( 80 );
10216 : // insert XObject
10217 0 : aLine.append( "q /" );
10218 0 : aLine.append( aExtName );
10219 0 : aLine.append( " gs /" );
10220 0 : aLine.append( aTrName );
10221 0 : aLine.append( " Do Q\n" );
10222 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10223 :
10224 0 : pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
10225 0 : pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
10226 : }
10227 0 : }
10228 :
10229 0 : void PDFWriterImpl::drawRectangle( const Rectangle& rRect )
10230 : {
10231 0 : MARK( "drawRectangle" );
10232 :
10233 0 : updateGraphicsState();
10234 :
10235 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
10236 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
10237 0 : return;
10238 :
10239 0 : OStringBuffer aLine( 40 );
10240 0 : m_aPages.back().appendRect( rRect, aLine );
10241 :
10242 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
10243 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
10244 0 : aLine.append( " B*\n" );
10245 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10246 0 : aLine.append( " S\n" );
10247 : else
10248 0 : aLine.append( " f*\n" );
10249 :
10250 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10251 : }
10252 :
10253 0 : void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
10254 : {
10255 0 : MARK( "drawRectangle with rounded edges" );
10256 :
10257 0 : if( !nHorzRound && !nVertRound )
10258 0 : drawRectangle( rRect );
10259 :
10260 0 : updateGraphicsState();
10261 :
10262 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
10263 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
10264 0 : return;
10265 :
10266 0 : if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
10267 0 : nHorzRound = rRect.GetWidth()/2;
10268 0 : if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
10269 0 : nVertRound = rRect.GetHeight()/2;
10270 :
10271 0 : Point aPoints[16];
10272 0 : const double kappa = 0.5522847498;
10273 0 : const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
10274 0 : const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
10275 :
10276 0 : aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
10277 0 : aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
10278 0 : aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
10279 0 : aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() );
10280 :
10281 0 : aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
10282 0 : aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky );
10283 0 : aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
10284 0 : aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky );
10285 :
10286 0 : aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
10287 0 : aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() );
10288 0 : aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
10289 0 : aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
10290 :
10291 0 : aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
10292 0 : aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
10293 0 : aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
10294 0 : aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
10295 :
10296 0 : OStringBuffer aLine( 80 );
10297 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
10298 0 : aLine.append( " m " );
10299 0 : m_aPages.back().appendPoint( aPoints[2], aLine );
10300 0 : aLine.append( " l " );
10301 0 : m_aPages.back().appendPoint( aPoints[3], aLine );
10302 0 : aLine.append( ' ' );
10303 0 : m_aPages.back().appendPoint( aPoints[4], aLine );
10304 0 : aLine.append( ' ' );
10305 0 : m_aPages.back().appendPoint( aPoints[5], aLine );
10306 0 : aLine.append( " c\n" );
10307 0 : m_aPages.back().appendPoint( aPoints[6], aLine );
10308 0 : aLine.append( " l " );
10309 0 : m_aPages.back().appendPoint( aPoints[7], aLine );
10310 0 : aLine.append( ' ' );
10311 0 : m_aPages.back().appendPoint( aPoints[8], aLine );
10312 0 : aLine.append( ' ' );
10313 0 : m_aPages.back().appendPoint( aPoints[9], aLine );
10314 0 : aLine.append( " c\n" );
10315 0 : m_aPages.back().appendPoint( aPoints[10], aLine );
10316 0 : aLine.append( " l " );
10317 0 : m_aPages.back().appendPoint( aPoints[11], aLine );
10318 0 : aLine.append( ' ' );
10319 0 : m_aPages.back().appendPoint( aPoints[12], aLine );
10320 0 : aLine.append( ' ' );
10321 0 : m_aPages.back().appendPoint( aPoints[13], aLine );
10322 0 : aLine.append( " c\n" );
10323 0 : m_aPages.back().appendPoint( aPoints[14], aLine );
10324 0 : aLine.append( " l " );
10325 0 : m_aPages.back().appendPoint( aPoints[15], aLine );
10326 0 : aLine.append( ' ' );
10327 0 : m_aPages.back().appendPoint( aPoints[0], aLine );
10328 0 : aLine.append( ' ' );
10329 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
10330 0 : aLine.append( " c " );
10331 :
10332 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
10333 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
10334 0 : aLine.append( "b*\n" );
10335 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10336 0 : aLine.append( "s\n" );
10337 : else
10338 0 : aLine.append( "f*\n" );
10339 :
10340 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10341 : }
10342 :
10343 0 : void PDFWriterImpl::drawEllipse( const Rectangle& rRect )
10344 : {
10345 0 : MARK( "drawEllipse" );
10346 :
10347 0 : updateGraphicsState();
10348 :
10349 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
10350 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
10351 0 : return;
10352 :
10353 0 : Point aPoints[12];
10354 0 : const double kappa = 0.5522847498;
10355 0 : const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
10356 0 : const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
10357 :
10358 0 : aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
10359 0 : aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
10360 0 : aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() );
10361 :
10362 0 : aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
10363 0 : aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky );
10364 0 : aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky );
10365 :
10366 0 : aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
10367 0 : aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() );
10368 0 : aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() );
10369 :
10370 0 : aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
10371 0 : aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky );
10372 0 : aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
10373 :
10374 0 : OStringBuffer aLine( 80 );
10375 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
10376 0 : aLine.append( " m " );
10377 0 : m_aPages.back().appendPoint( aPoints[2], aLine );
10378 0 : aLine.append( ' ' );
10379 0 : m_aPages.back().appendPoint( aPoints[3], aLine );
10380 0 : aLine.append( ' ' );
10381 0 : m_aPages.back().appendPoint( aPoints[4], aLine );
10382 0 : aLine.append( " c\n" );
10383 0 : m_aPages.back().appendPoint( aPoints[5], aLine );
10384 0 : aLine.append( ' ' );
10385 0 : m_aPages.back().appendPoint( aPoints[6], aLine );
10386 0 : aLine.append( ' ' );
10387 0 : m_aPages.back().appendPoint( aPoints[7], aLine );
10388 0 : aLine.append( " c\n" );
10389 0 : m_aPages.back().appendPoint( aPoints[8], aLine );
10390 0 : aLine.append( ' ' );
10391 0 : m_aPages.back().appendPoint( aPoints[9], aLine );
10392 0 : aLine.append( ' ' );
10393 0 : m_aPages.back().appendPoint( aPoints[10], aLine );
10394 0 : aLine.append( " c\n" );
10395 0 : m_aPages.back().appendPoint( aPoints[11], aLine );
10396 0 : aLine.append( ' ' );
10397 0 : m_aPages.back().appendPoint( aPoints[0], aLine );
10398 0 : aLine.append( ' ' );
10399 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
10400 0 : aLine.append( " c " );
10401 :
10402 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
10403 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
10404 0 : aLine.append( "b*\n" );
10405 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10406 0 : aLine.append( "s\n" );
10407 : else
10408 0 : aLine.append( "f*\n" );
10409 :
10410 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10411 : }
10412 :
10413 0 : static double calcAngle( const Rectangle& rRect, const Point& rPoint )
10414 : {
10415 0 : Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
10416 0 : (rRect.Top()+rRect.Bottom()+1)/2);
10417 0 : Point aPoint = rPoint - aOrigin;
10418 :
10419 0 : double fX = (double)aPoint.X();
10420 0 : double fY = (double)-aPoint.Y();
10421 :
10422 0 : if ((rRect.GetHeight() == 0) || (rRect.GetWidth() == 0))
10423 0 : throw o3tl::divide_by_zero();
10424 :
10425 0 : if( rRect.GetWidth() > rRect.GetHeight() )
10426 0 : fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
10427 0 : else if( rRect.GetHeight() > rRect.GetWidth() )
10428 0 : fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
10429 0 : return atan2( fY, fX );
10430 : }
10431 :
10432 0 : void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
10433 : {
10434 0 : MARK( "drawArc" );
10435 :
10436 0 : updateGraphicsState();
10437 :
10438 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
10439 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
10440 0 : return;
10441 :
10442 : // calculate start and stop angles
10443 0 : const double fStartAngle = calcAngle( rRect, rStart );
10444 0 : double fStopAngle = calcAngle( rRect, rStop );
10445 0 : while( fStopAngle < fStartAngle )
10446 0 : fStopAngle += 2.0*M_PI;
10447 0 : const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
10448 0 : const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
10449 0 : const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
10450 0 : const double halfWidth = (double)rRect.GetWidth()/2.0;
10451 0 : const double halfHeight = (double)rRect.GetHeight()/2.0;
10452 :
10453 0 : const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
10454 0 : (rRect.Top()+rRect.Bottom()+1)/2 );
10455 :
10456 0 : OStringBuffer aLine( 30*nFragments );
10457 0 : Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
10458 0 : -(int)(halfHeight * sin(fStartAngle) ) );
10459 0 : aPoint += aCenter;
10460 0 : m_aPages.back().appendPoint( aPoint, aLine );
10461 0 : aLine.append( " m " );
10462 0 : if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
10463 : {
10464 0 : for( int i = 0; i < nFragments; i++ )
10465 : {
10466 0 : const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
10467 0 : const double fStopFragment = fStartFragment + fFragmentDelta;
10468 0 : aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
10469 0 : -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
10470 0 : aPoint += aCenter;
10471 0 : m_aPages.back().appendPoint( aPoint, aLine );
10472 0 : aLine.append( ' ' );
10473 :
10474 0 : aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
10475 0 : -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
10476 0 : aPoint += aCenter;
10477 0 : m_aPages.back().appendPoint( aPoint, aLine );
10478 0 : aLine.append( ' ' );
10479 :
10480 0 : aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
10481 0 : -(int)(halfHeight * sin(fStopFragment) ) );
10482 0 : aPoint += aCenter;
10483 0 : m_aPages.back().appendPoint( aPoint, aLine );
10484 0 : aLine.append( " c\n" );
10485 : }
10486 : }
10487 0 : if( bWithChord || bWithPie )
10488 : {
10489 0 : if( bWithPie )
10490 : {
10491 0 : m_aPages.back().appendPoint( aCenter, aLine );
10492 0 : aLine.append( " l " );
10493 : }
10494 0 : aLine.append( "h " );
10495 : }
10496 0 : if( ! bWithChord && ! bWithPie )
10497 0 : aLine.append( "S\n" );
10498 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
10499 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
10500 0 : aLine.append( "B*\n" );
10501 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10502 0 : aLine.append( "S\n" );
10503 : else
10504 0 : aLine.append( "f*\n" );
10505 :
10506 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10507 : }
10508 :
10509 0 : void PDFWriterImpl::drawPolyLine( const Polygon& rPoly )
10510 : {
10511 0 : MARK( "drawPolyLine" );
10512 :
10513 0 : sal_uInt16 nPoints = rPoly.GetSize();
10514 0 : if( nPoints < 2 )
10515 0 : return;
10516 :
10517 0 : updateGraphicsState();
10518 :
10519 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
10520 0 : return;
10521 :
10522 0 : OStringBuffer aLine( 20 * nPoints );
10523 0 : m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
10524 0 : aLine.append( "S\n" );
10525 :
10526 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10527 : }
10528 :
10529 0 : void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo )
10530 : {
10531 0 : MARK( "drawPolyLine with LineInfo" );
10532 :
10533 0 : updateGraphicsState();
10534 :
10535 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
10536 0 : return;
10537 :
10538 0 : OStringBuffer aLine;
10539 0 : aLine.append( "q " );
10540 0 : if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
10541 : {
10542 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10543 0 : drawPolyLine( rPoly );
10544 0 : writeBuffer( "Q\n", 2 );
10545 : }
10546 : else
10547 : {
10548 0 : PDFWriter::ExtLineInfo aInfo;
10549 0 : convertLineInfoToExtLineInfo( rInfo, aInfo );
10550 0 : drawPolyLine( rPoly, aInfo );
10551 0 : }
10552 : }
10553 :
10554 0 : void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
10555 : {
10556 : DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" );
10557 0 : rOut.m_fLineWidth = rIn.GetWidth();
10558 0 : rOut.m_fTransparency = 0.0;
10559 0 : rOut.m_eCap = PDFWriter::capButt;
10560 0 : rOut.m_eJoin = PDFWriter::joinMiter;
10561 0 : rOut.m_fMiterLimit = 10;
10562 0 : rOut.m_aDashArray.clear();
10563 :
10564 : // add DashDot to DashArray
10565 0 : const int nDashes = rIn.GetDashCount();
10566 0 : const int nDashLen = rIn.GetDashLen();
10567 0 : const int nDistance = rIn.GetDistance();
10568 :
10569 0 : for( int n = 0; n < nDashes; n++ )
10570 : {
10571 0 : rOut.m_aDashArray.push_back( nDashLen );
10572 0 : rOut.m_aDashArray.push_back( nDistance );
10573 : }
10574 0 : const int nDots = rIn.GetDotCount();
10575 0 : const int nDotLen = rIn.GetDotLen();
10576 :
10577 0 : for( int n = 0; n < nDots; n++ )
10578 : {
10579 0 : rOut.m_aDashArray.push_back( nDotLen );
10580 0 : rOut.m_aDashArray.push_back( nDistance );
10581 : }
10582 :
10583 : // add LineJoin
10584 0 : switch(rIn.GetLineJoin())
10585 : {
10586 : case basegfx::B2DLineJoin::Bevel :
10587 : {
10588 0 : rOut.m_eJoin = PDFWriter::joinBevel;
10589 0 : break;
10590 : }
10591 : default : // basegfx::B2DLineJoin::NONE :
10592 : // Pdf has no 'none' lineJoin, default is miter
10593 : case basegfx::B2DLineJoin::Middle :
10594 : case basegfx::B2DLineJoin::Miter :
10595 : {
10596 0 : rOut.m_eJoin = PDFWriter::joinMiter;
10597 0 : break;
10598 : }
10599 : case basegfx::B2DLineJoin::Round :
10600 : {
10601 0 : rOut.m_eJoin = PDFWriter::joinRound;
10602 0 : break;
10603 : }
10604 : }
10605 :
10606 : // add LineCap
10607 0 : switch(rIn.GetLineCap())
10608 : {
10609 : default: /* com::sun::star::drawing::LineCap_BUTT */
10610 : {
10611 0 : rOut.m_eCap = PDFWriter::capButt;
10612 0 : break;
10613 : }
10614 : case com::sun::star::drawing::LineCap_ROUND:
10615 : {
10616 0 : rOut.m_eCap = PDFWriter::capRound;
10617 0 : break;
10618 : }
10619 : case com::sun::star::drawing::LineCap_SQUARE:
10620 : {
10621 0 : rOut.m_eCap = PDFWriter::capSquare;
10622 0 : break;
10623 : }
10624 : }
10625 0 : }
10626 :
10627 0 : void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
10628 : {
10629 0 : MARK( "drawPolyLine with ExtLineInfo" );
10630 :
10631 0 : updateGraphicsState();
10632 :
10633 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
10634 0 : return;
10635 :
10636 0 : if( rInfo.m_fTransparency >= 1.0 )
10637 0 : return;
10638 :
10639 0 : if( rInfo.m_fTransparency != 0.0 )
10640 0 : beginTransparencyGroup();
10641 :
10642 0 : OStringBuffer aLine;
10643 0 : aLine.append( "q " );
10644 0 : m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
10645 0 : aLine.append( " w" );
10646 0 : if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader
10647 : {
10648 0 : switch( rInfo.m_eCap )
10649 : {
10650 : default:
10651 0 : case PDFWriter::capButt: aLine.append( " 0 J" );break;
10652 0 : case PDFWriter::capRound: aLine.append( " 1 J" );break;
10653 0 : case PDFWriter::capSquare: aLine.append( " 2 J" );break;
10654 : }
10655 0 : switch( rInfo.m_eJoin )
10656 : {
10657 : default:
10658 : case PDFWriter::joinMiter:
10659 : {
10660 0 : double fLimit = rInfo.m_fMiterLimit;
10661 0 : if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
10662 0 : fLimit = fLimit / rInfo.m_fLineWidth;
10663 0 : if( fLimit < 1.0 )
10664 0 : fLimit = 1.0;
10665 0 : aLine.append( " 0 j " );
10666 0 : appendDouble( fLimit, aLine );
10667 0 : aLine.append( " M" );
10668 : }
10669 0 : break;
10670 0 : case PDFWriter::joinRound: aLine.append( " 1 j" );break;
10671 0 : case PDFWriter::joinBevel: aLine.append( " 2 j" );break;
10672 : }
10673 0 : if( rInfo.m_aDashArray.size() > 0 )
10674 : {
10675 0 : aLine.append( " [ " );
10676 0 : for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
10677 0 : it != rInfo.m_aDashArray.end(); ++it )
10678 : {
10679 0 : m_aPages.back().appendMappedLength( *it, aLine );
10680 0 : aLine.append( ' ' );
10681 : }
10682 0 : aLine.append( "] 0 d" );
10683 : }
10684 0 : aLine.append( "\n" );
10685 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10686 0 : drawPolyLine( rPoly );
10687 : }
10688 : else
10689 : {
10690 0 : basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
10691 0 : basegfx::B2DPolyPolygon aPolyPoly;
10692 :
10693 0 : basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
10694 :
10695 : // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
10696 : // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
10697 : // this line needs to be removed and the loop below adapted accordingly
10698 0 : aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
10699 :
10700 0 : const sal_uInt32 nPolygonCount(aPolyPoly.count());
10701 :
10702 0 : for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
10703 : {
10704 0 : aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
10705 0 : aPoly = aPolyPoly.getB2DPolygon( nPoly );
10706 0 : const sal_uInt32 nPointCount(aPoly.count());
10707 :
10708 0 : if(nPointCount)
10709 : {
10710 0 : const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
10711 0 : basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
10712 :
10713 0 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
10714 : {
10715 0 : if( a > 0 )
10716 0 : aLine.append( " " );
10717 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
10718 0 : const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
10719 :
10720 0 : m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
10721 : FRound(aCurrent.getY()) ),
10722 0 : aLine );
10723 0 : aLine.append( " m " );
10724 0 : m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
10725 : FRound(aNext.getY()) ),
10726 0 : aLine );
10727 0 : aLine.append( " l" );
10728 :
10729 : // prepare next edge
10730 0 : aCurrent = aNext;
10731 0 : }
10732 : }
10733 : }
10734 0 : aLine.append( " S " );
10735 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10736 : }
10737 0 : writeBuffer( "Q\n", 2 );
10738 :
10739 0 : if( rInfo.m_fTransparency != 0.0 )
10740 : {
10741 : // FIXME: actually this may be incorrect with bezier polygons
10742 0 : Rectangle aBoundRect( rPoly.GetBoundRect() );
10743 : // avoid clipping with thick lines
10744 0 : if( rInfo.m_fLineWidth > 0.0 )
10745 : {
10746 0 : sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
10747 0 : aBoundRect.Top() -= nLW;
10748 0 : aBoundRect.Left() -= nLW;
10749 0 : aBoundRect.Right() += nLW;
10750 0 : aBoundRect.Bottom() += nLW;
10751 : }
10752 0 : endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) );
10753 0 : }
10754 : }
10755 :
10756 0 : void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
10757 : {
10758 0 : MARK( "drawPixel" );
10759 :
10760 0 : Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
10761 :
10762 0 : if( aColor == Color( COL_TRANSPARENT ) )
10763 0 : return;
10764 :
10765 : // pixels are drawn in line color, so have to set
10766 : // the nonstroking color to line color
10767 0 : Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
10768 0 : setFillColor( aColor );
10769 :
10770 0 : updateGraphicsState();
10771 :
10772 0 : OStringBuffer aLine( 20 );
10773 0 : m_aPages.back().appendPoint( rPoint, aLine );
10774 0 : aLine.append( ' ' );
10775 0 : appendDouble( 1.0/double(getReferenceDevice()->GetDPIX()), aLine );
10776 0 : aLine.append( ' ' );
10777 0 : appendDouble( 1.0/double(getReferenceDevice()->GetDPIY()), aLine );
10778 0 : aLine.append( " re f\n" );
10779 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10780 :
10781 0 : setFillColor( aOldFillColor );
10782 : }
10783 :
10784 : class AccessReleaser
10785 : {
10786 : BitmapReadAccess* m_pAccess;
10787 : public:
10788 0 : explicit AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){}
10789 0 : ~AccessReleaser() { delete m_pAccess; }
10790 : };
10791 :
10792 0 : bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
10793 : {
10794 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
10795 :
10796 0 : bool bFlateFilter = compressStream( rObject.m_pContentStream );
10797 0 : rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
10798 0 : sal_uLong nSize = rObject.m_pContentStream->Tell();
10799 0 : rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
10800 : #if OSL_DEBUG_LEVEL > 1
10801 : emitComment( "PDFWriterImpl::writeTransparentObject" );
10802 : #endif
10803 0 : OStringBuffer aLine( 512 );
10804 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
10805 0 : aLine.append( rObject.m_nObject );
10806 : aLine.append( " 0 obj\n"
10807 : "<</Type/XObject\n"
10808 : "/Subtype/Form\n"
10809 0 : "/BBox[ " );
10810 0 : appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
10811 0 : aLine.append( ' ' );
10812 0 : appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
10813 0 : aLine.append( ' ' );
10814 0 : appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
10815 0 : aLine.append( ' ' );
10816 0 : appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
10817 0 : aLine.append( " ]\n" );
10818 0 : if( ! rObject.m_pSoftMaskStream )
10819 : {
10820 0 : if( ! m_bIsPDF_A1 )
10821 : {
10822 0 : aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
10823 : }
10824 : }
10825 : /* #i42884# the PDF reference recommends that each Form XObject
10826 : * should have a resource dict; alas if that is the same object
10827 : * as the one of the page it triggers an endless recursion in
10828 : * acroread 5 (6 and up have that fixed). Since we have only one
10829 : * resource dict anyway, let's use the one from the page by NOT
10830 : * emitting a Resources entry.
10831 : */
10832 :
10833 0 : aLine.append( "/Length " );
10834 0 : aLine.append( (sal_Int32)(nSize) );
10835 0 : aLine.append( "\n" );
10836 0 : if( bFlateFilter )
10837 0 : aLine.append( "/Filter/FlateDecode\n" );
10838 : aLine.append( ">>\n"
10839 0 : "stream\n" );
10840 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10841 0 : checkAndEnableStreamEncryption( rObject.m_nObject );
10842 0 : CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
10843 0 : disableStreamEncryption();
10844 0 : aLine.setLength( 0 );
10845 : aLine.append( "\n"
10846 : "endstream\n"
10847 0 : "endobj\n\n" );
10848 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10849 :
10850 : // write ExtGState dict for this XObject
10851 0 : aLine.setLength( 0 );
10852 0 : aLine.append( rObject.m_nExtGStateObject );
10853 : aLine.append( " 0 obj\n"
10854 0 : "<<" );
10855 0 : if( ! rObject.m_pSoftMaskStream )
10856 : {
10857 : //i59651
10858 0 : if( m_bIsPDF_A1 )
10859 : {
10860 0 : aLine.append( "/CA 1.0/ca 1.0" );
10861 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
10862 : }
10863 : else
10864 : {
10865 0 : aLine.append( "/CA " );
10866 0 : appendDouble( rObject.m_fAlpha, aLine );
10867 : aLine.append( "\n"
10868 0 : " /ca " );
10869 0 : appendDouble( rObject.m_fAlpha, aLine );
10870 : }
10871 0 : aLine.append( "\n" );
10872 : }
10873 : else
10874 : {
10875 0 : if( m_bIsPDF_A1 )
10876 : {
10877 0 : aLine.append( "/SMask/None" );
10878 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
10879 : }
10880 : else
10881 : {
10882 0 : rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
10883 0 : sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
10884 0 : rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
10885 0 : sal_Int32 nMaskObject = createObject();
10886 0 : aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
10887 0 : aLine.append( nMaskObject );
10888 0 : aLine.append( " 0 R>>\n" );
10889 :
10890 0 : OStringBuffer aMask;
10891 0 : aMask.append( nMaskObject );
10892 : aMask.append( " 0 obj\n"
10893 : "<</Type/XObject\n"
10894 : "/Subtype/Form\n"
10895 0 : "/BBox[" );
10896 0 : appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
10897 0 : aMask.append( ' ' );
10898 0 : appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
10899 0 : aMask.append( ' ' );
10900 0 : appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
10901 0 : aMask.append( ' ' );
10902 0 : appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
10903 0 : aMask.append( "]\n" );
10904 :
10905 : /* #i42884# see above */
10906 0 : aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
10907 0 : aMask.append( "/Length " );
10908 0 : aMask.append( nMaskSize );
10909 : aMask.append( ">>\n"
10910 0 : "stream\n" );
10911 0 : CHECK_RETURN( updateObject( nMaskObject ) );
10912 0 : checkAndEnableStreamEncryption( nMaskObject );
10913 0 : CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
10914 0 : CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
10915 0 : disableStreamEncryption();
10916 0 : aMask.setLength( 0 );
10917 : aMask.append( "\nendstream\n"
10918 0 : "endobj\n\n" );
10919 0 : CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
10920 : }
10921 : }
10922 : aLine.append( ">>\n"
10923 0 : "endobj\n\n" );
10924 0 : CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) );
10925 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10926 :
10927 0 : return true;
10928 : }
10929 :
10930 0 : bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
10931 : {
10932 : // LO internal gradient -> PDF shading type:
10933 : // * GradientStyle_LINEAR: axial shading, using sampled-function with 2 samples
10934 : // [t=0:colorStart, t=1:colorEnd]
10935 : // * GradientStyle_AXIAL: axial shading, using sampled-function with 3 samples
10936 : // [t=0:colorEnd, t=0.5:colorStart, t=1:colorEnd]
10937 : // * other styles: function shading with aSize.Width() * aSize.Height() samples
10938 0 : sal_Int32 nFunctionObject = createObject();
10939 0 : CHECK_RETURN( updateObject( nFunctionObject ) );
10940 :
10941 0 : ScopedVclPtrInstance< VirtualDevice > aDev;
10942 0 : aDev->SetOutputSizePixel( rObject.m_aSize );
10943 0 : aDev->SetMapMode( MapMode( MAP_PIXEL ) );
10944 0 : if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
10945 0 : aDev->SetDrawMode( aDev->GetDrawMode() |
10946 : ( DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText |
10947 0 : DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient ) );
10948 0 : aDev->DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
10949 :
10950 0 : Bitmap aSample = aDev->GetBitmap( Point( 0, 0 ), rObject.m_aSize );
10951 0 : BitmapReadAccess* pAccess = aSample.AcquireReadAccess();
10952 0 : AccessReleaser aReleaser( pAccess );
10953 :
10954 0 : Size aSize = aSample.GetSizePixel();
10955 :
10956 0 : sal_Int32 nStreamLengthObject = createObject();
10957 : #if OSL_DEBUG_LEVEL > 1
10958 : emitComment( "PDFWriterImpl::writeGradientFunction" );
10959 : #endif
10960 0 : OStringBuffer aLine( 120 );
10961 0 : aLine.append( nFunctionObject );
10962 : aLine.append( " 0 obj\n"
10963 0 : "<</FunctionType 0\n");
10964 0 : switch (rObject.m_aGradient.GetStyle())
10965 : {
10966 : case GradientStyle_LINEAR:
10967 : case GradientStyle_AXIAL:
10968 0 : aLine.append("/Domain[ 0 1]\n");
10969 0 : break;
10970 : default:
10971 0 : aLine.append("/Domain[ 0 1 0 1]\n");
10972 : }
10973 0 : aLine.append("/Size[ " );
10974 0 : switch (rObject.m_aGradient.GetStyle())
10975 : {
10976 : case GradientStyle_LINEAR:
10977 0 : aLine.append('2');
10978 0 : break;
10979 : case GradientStyle_AXIAL:
10980 0 : aLine.append('3');
10981 0 : break;
10982 : default:
10983 0 : aLine.append( (sal_Int32)aSize.Width() );
10984 0 : aLine.append( ' ' );
10985 0 : aLine.append( (sal_Int32)aSize.Height() );
10986 : }
10987 : aLine.append( " ]\n"
10988 : "/BitsPerSample 8\n"
10989 : "/Range[ 0 1 0 1 0 1 ]\n"
10990 : "/Order 3\n"
10991 0 : "/Length " );
10992 0 : aLine.append( nStreamLengthObject );
10993 : aLine.append( " 0 R\n"
10994 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
10995 : "/Filter/FlateDecode"
10996 : #endif
10997 : ">>\n"
10998 0 : "stream\n" );
10999 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11000 :
11001 0 : sal_uInt64 nStartStreamPos = 0;
11002 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nStartStreamPos)) );
11003 :
11004 0 : checkAndEnableStreamEncryption( nFunctionObject );
11005 0 : beginCompression();
11006 : sal_uInt8 aCol[3];
11007 0 : switch (rObject.m_aGradient.GetStyle())
11008 : {
11009 : case GradientStyle_AXIAL:
11010 0 : aCol[0] = rObject.m_aGradient.GetEndColor().GetRed();
11011 0 : aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen();
11012 0 : aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue();
11013 0 : CHECK_RETURN( writeBuffer( aCol, 3 ) );
11014 : // fall-through
11015 : case GradientStyle_LINEAR:
11016 : {
11017 0 : aCol[0] = rObject.m_aGradient.GetStartColor().GetRed();
11018 0 : aCol[1] = rObject.m_aGradient.GetStartColor().GetGreen();
11019 0 : aCol[2] = rObject.m_aGradient.GetStartColor().GetBlue();
11020 0 : CHECK_RETURN( writeBuffer( aCol, 3 ) );
11021 :
11022 0 : aCol[0] = rObject.m_aGradient.GetEndColor().GetRed();
11023 0 : aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen();
11024 0 : aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue();
11025 0 : CHECK_RETURN( writeBuffer( aCol, 3 ) );
11026 0 : break;
11027 : }
11028 : default:
11029 0 : for( int y = aSize.Height()-1; y >= 0; y-- )
11030 : {
11031 0 : for( long x = 0; x < aSize.Width(); x++ )
11032 : {
11033 0 : BitmapColor aColor = pAccess->GetColor( y, x );
11034 0 : aCol[0] = aColor.GetRed();
11035 0 : aCol[1] = aColor.GetGreen();
11036 0 : aCol[2] = aColor.GetBlue();
11037 0 : CHECK_RETURN( writeBuffer( aCol, 3 ) );
11038 0 : }
11039 : }
11040 : }
11041 0 : endCompression();
11042 0 : disableStreamEncryption();
11043 :
11044 0 : sal_uInt64 nEndStreamPos = 0;
11045 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nEndStreamPos)) );
11046 :
11047 0 : aLine.setLength( 0 );
11048 0 : aLine.append( "\nendstream\nendobj\n\n" );
11049 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11050 :
11051 : // write stream length
11052 0 : CHECK_RETURN( updateObject( nStreamLengthObject ) );
11053 0 : aLine.setLength( 0 );
11054 0 : aLine.append( nStreamLengthObject );
11055 0 : aLine.append( " 0 obj\n" );
11056 0 : aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
11057 0 : aLine.append( "\nendobj\n\n" );
11058 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11059 :
11060 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
11061 0 : aLine.setLength( 0 );
11062 0 : aLine.append( rObject.m_nObject );
11063 0 : aLine.append( " 0 obj\n");
11064 0 : switch (rObject.m_aGradient.GetStyle())
11065 : {
11066 : case GradientStyle_LINEAR:
11067 : case GradientStyle_AXIAL:
11068 0 : aLine.append("<</ShadingType 2\n");
11069 0 : break;
11070 : default:
11071 0 : aLine.append("<</ShadingType 1\n");
11072 : }
11073 : aLine.append("/ColorSpace/DeviceRGB\n"
11074 0 : "/AntiAlias true\n");
11075 :
11076 : // Determination of shading axis
11077 : // See: OutputDevice::ImplDrawLinearGradient for reference
11078 0 : Rectangle aRect;
11079 0 : aRect.Left() = aRect.Top() = 0;
11080 0 : aRect.Right() = aSize.Width();
11081 0 : aRect.Bottom() = aSize.Height();
11082 :
11083 0 : Rectangle aBoundRect;
11084 0 : Point aCenter;
11085 0 : sal_uInt16 nAngle = rObject.m_aGradient.GetAngle() % 3600;
11086 0 : rObject.m_aGradient.GetBoundRect( aRect, aBoundRect, aCenter );
11087 :
11088 0 : const bool bLinear = (rObject.m_aGradient.GetStyle() == GradientStyle_LINEAR);
11089 0 : double fBorder = aBoundRect.GetHeight() * rObject.m_aGradient.GetBorder() / 100.0;
11090 0 : if ( !bLinear )
11091 : {
11092 0 : fBorder /= 2.0;
11093 : }
11094 :
11095 0 : aBoundRect.Bottom() -= fBorder;
11096 0 : if (!bLinear)
11097 : {
11098 0 : aBoundRect.Top() += fBorder;
11099 : }
11100 :
11101 0 : switch (rObject.m_aGradient.GetStyle())
11102 : {
11103 : case GradientStyle_LINEAR:
11104 : case GradientStyle_AXIAL:
11105 : {
11106 : aLine.append("/Domain[ 0 1 ]\n"
11107 0 : "/Coords[ " );
11108 0 : Polygon aPoly( 2 );
11109 0 : aPoly[0] = aBoundRect.BottomCenter();
11110 0 : aPoly[1] = aBoundRect.TopCenter();
11111 0 : aPoly.Rotate( aCenter, 3600 - nAngle );
11112 :
11113 0 : aLine.append( (sal_Int32) aPoly[0].X() );
11114 0 : aLine.append( " " );
11115 0 : aLine.append( (sal_Int32) aPoly[0].Y() );
11116 0 : aLine.append( " " );
11117 0 : aLine.append( (sal_Int32) aPoly[1].X());
11118 0 : aLine.append( " ");
11119 0 : aLine.append( (sal_Int32) aPoly[1].Y());
11120 0 : aLine.append( " ]\n");
11121 0 : aLine.append("/Extend [true true]\n");
11122 0 : break;
11123 : }
11124 : default:
11125 : aLine.append("/Domain[ 0 1 0 1 ]\n"
11126 0 : "/Matrix[ " );
11127 0 : aLine.append( (sal_Int32)aSize.Width() );
11128 0 : aLine.append( " 0 0 " );
11129 0 : aLine.append( (sal_Int32)aSize.Height() );
11130 0 : aLine.append( " 0 0 ]\n");
11131 : }
11132 0 : aLine.append("/Function " );
11133 0 : aLine.append( nFunctionObject );
11134 : aLine.append( " 0 R\n"
11135 : ">>\n"
11136 0 : "endobj\n\n" );
11137 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11138 :
11139 0 : return true;
11140 : }
11141 :
11142 0 : bool PDFWriterImpl::writeJPG( JPGEmit& rObject )
11143 : {
11144 0 : CHECK_RETURN( rObject.m_pStream );
11145 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
11146 :
11147 0 : sal_Int32 nLength = 0;
11148 0 : rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
11149 0 : nLength = rObject.m_pStream->Tell();
11150 0 : rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
11151 :
11152 0 : sal_Int32 nMaskObject = 0;
11153 0 : if( !!rObject.m_aMask )
11154 : {
11155 0 : if( rObject.m_aMask.GetBitCount() == 1 ||
11156 0 : ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651
11157 : )
11158 : {
11159 0 : nMaskObject = createObject();
11160 : }
11161 0 : else if( m_bIsPDF_A1 )
11162 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
11163 0 : else if( m_aContext.Version < PDFWriter::PDF_1_4 )
11164 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
11165 :
11166 : }
11167 : #if OSL_DEBUG_LEVEL > 1
11168 : emitComment( "PDFWriterImpl::writeJPG" );
11169 : #endif
11170 :
11171 0 : OStringBuffer aLine(200);
11172 0 : aLine.append( rObject.m_nObject );
11173 : aLine.append( " 0 obj\n"
11174 0 : "<</Type/XObject/Subtype/Image/Width " );
11175 0 : aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
11176 0 : aLine.append( " /Height " );
11177 0 : aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
11178 0 : aLine.append( " /BitsPerComponent 8 " );
11179 0 : if( rObject.m_bTrueColor )
11180 0 : aLine.append( "/ColorSpace/DeviceRGB" );
11181 : else
11182 0 : aLine.append( "/ColorSpace/DeviceGray" );
11183 0 : aLine.append( "/Filter/DCTDecode/Length " );
11184 0 : aLine.append( nLength );
11185 0 : if( nMaskObject )
11186 : {
11187 0 : aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
11188 0 : aLine.append( nMaskObject );
11189 0 : aLine.append( " 0 R " );
11190 : }
11191 0 : aLine.append( ">>\nstream\n" );
11192 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11193 :
11194 0 : checkAndEnableStreamEncryption( rObject.m_nObject );
11195 0 : CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
11196 0 : disableStreamEncryption();
11197 :
11198 0 : aLine.setLength( 0 );
11199 0 : aLine.append( "\nendstream\nendobj\n\n" );
11200 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11201 :
11202 0 : if( nMaskObject )
11203 : {
11204 0 : BitmapEmit aEmit;
11205 0 : aEmit.m_nObject = nMaskObject;
11206 0 : if( rObject.m_aMask.GetBitCount() == 1 )
11207 0 : aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
11208 0 : else if( rObject.m_aMask.GetBitCount() == 8 )
11209 0 : aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
11210 0 : writeBitmapObject( aEmit, true );
11211 : }
11212 :
11213 0 : return true;
11214 : }
11215 :
11216 0 : bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
11217 : {
11218 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
11219 :
11220 0 : Bitmap aBitmap;
11221 0 : Color aTransparentColor( COL_TRANSPARENT );
11222 0 : bool bWriteMask = false;
11223 0 : if( ! bMask )
11224 : {
11225 0 : aBitmap = rObject.m_aBitmap.GetBitmap();
11226 0 : if( rObject.m_aBitmap.IsAlpha() )
11227 : {
11228 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 )
11229 0 : bWriteMask = true;
11230 : // else draw without alpha channel
11231 : }
11232 : else
11233 : {
11234 0 : switch( rObject.m_aBitmap.GetTransparentType() )
11235 : {
11236 : case TRANSPARENT_NONE:
11237 : // comes from drawMask function
11238 0 : if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask )
11239 0 : bMask = true;
11240 0 : break;
11241 : case TRANSPARENT_COLOR:
11242 0 : aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
11243 0 : break;
11244 : case TRANSPARENT_BITMAP:
11245 0 : bWriteMask = true;
11246 0 : break;
11247 : }
11248 : }
11249 : }
11250 : else
11251 : {
11252 0 : if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
11253 : {
11254 0 : aBitmap = rObject.m_aBitmap.GetMask();
11255 0 : aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
11256 : DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
11257 : }
11258 0 : else if( aBitmap.GetBitCount() != 8 )
11259 : {
11260 0 : aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap();
11261 0 : aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
11262 : DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" );
11263 : }
11264 : }
11265 :
11266 0 : BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();
11267 0 : AccessReleaser aReleaser( pAccess );
11268 :
11269 : bool bTrueColor;
11270 : sal_Int32 nBitsPerComponent;
11271 0 : switch( aBitmap.GetBitCount() )
11272 : {
11273 : case 1:
11274 : case 2:
11275 : case 4:
11276 : case 8:
11277 0 : bTrueColor = false;
11278 0 : nBitsPerComponent = aBitmap.GetBitCount();
11279 0 : break;
11280 : default:
11281 0 : bTrueColor = true;
11282 0 : nBitsPerComponent = 8;
11283 0 : break;
11284 : }
11285 :
11286 0 : sal_Int32 nStreamLengthObject = createObject();
11287 0 : sal_Int32 nMaskObject = 0;
11288 :
11289 : #if OSL_DEBUG_LEVEL > 1
11290 : emitComment( "PDFWriterImpl::writeBitmapObject" );
11291 : #endif
11292 0 : OStringBuffer aLine(1024);
11293 0 : aLine.append( rObject.m_nObject );
11294 : aLine.append( " 0 obj\n"
11295 0 : "<</Type/XObject/Subtype/Image/Width " );
11296 0 : aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
11297 0 : aLine.append( "/Height " );
11298 0 : aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
11299 0 : aLine.append( "/BitsPerComponent " );
11300 0 : aLine.append( nBitsPerComponent );
11301 0 : aLine.append( "/Length " );
11302 0 : aLine.append( nStreamLengthObject );
11303 0 : aLine.append( " 0 R\n" );
11304 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
11305 0 : if( nBitsPerComponent != 1 )
11306 : {
11307 0 : aLine.append( "/Filter/FlateDecode" );
11308 : }
11309 : else
11310 : {
11311 0 : aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
11312 0 : aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
11313 0 : aLine.append( ">>\n" );
11314 : }
11315 : #endif
11316 0 : if( ! bMask )
11317 : {
11318 0 : aLine.append( "/ColorSpace" );
11319 0 : if( bTrueColor )
11320 0 : aLine.append( "/DeviceRGB\n" );
11321 0 : else if( aBitmap.HasGreyPalette() )
11322 : {
11323 0 : aLine.append( "/DeviceGray\n" );
11324 0 : if( aBitmap.GetBitCount() == 1 )
11325 : {
11326 : // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
11327 0 : sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
11328 : DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
11329 0 : if( nBlackIndex == 1 )
11330 0 : aLine.append( "/Decode[1 0]\n" );
11331 : }
11332 : }
11333 : else
11334 : {
11335 0 : aLine.append( "[ /Indexed/DeviceRGB " );
11336 0 : aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
11337 0 : aLine.append( "\n<" );
11338 0 : if( m_aContext.Encryption.Encrypt() )
11339 : {
11340 0 : enableStringEncryption( rObject.m_nObject );
11341 : //check encryption buffer size
11342 0 : if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
11343 : {
11344 0 : int nChar = 0;
11345 : //fill the encryption buffer
11346 0 : for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
11347 : {
11348 0 : const BitmapColor& rColor = pAccess->GetPaletteColor( i );
11349 0 : m_pEncryptionBuffer[nChar++] = rColor.GetRed();
11350 0 : m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
11351 0 : m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
11352 : }
11353 : //encrypt the colorspace lookup table
11354 0 : rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
11355 : //now queue the data for output
11356 0 : nChar = 0;
11357 0 : for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
11358 : {
11359 0 : appendHex(m_pEncryptionBuffer[nChar++], aLine );
11360 0 : appendHex(m_pEncryptionBuffer[nChar++], aLine );
11361 0 : appendHex(m_pEncryptionBuffer[nChar++], aLine );
11362 : }
11363 : }
11364 : }
11365 : else //no encryption requested (PDF/A-1a program flow drops here)
11366 : {
11367 0 : for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
11368 : {
11369 0 : const BitmapColor& rColor = pAccess->GetPaletteColor( i );
11370 0 : appendHex( rColor.GetRed(), aLine );
11371 0 : appendHex( rColor.GetGreen(), aLine );
11372 0 : appendHex( rColor.GetBlue(), aLine );
11373 : }
11374 : }
11375 0 : aLine.append( ">\n]\n" );
11376 : }
11377 : }
11378 : else
11379 : {
11380 0 : if( aBitmap.GetBitCount() == 1 )
11381 : {
11382 0 : aLine.append( "/ImageMask true\n" );
11383 0 : sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
11384 : DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
11385 0 : if( nBlackIndex )
11386 0 : aLine.append( "/Decode[ 1 0 ]\n" );
11387 : else
11388 0 : aLine.append( "/Decode[ 0 1 ]\n" );
11389 : }
11390 0 : else if( aBitmap.GetBitCount() == 8 )
11391 : {
11392 : aLine.append( "/ColorSpace/DeviceGray\n"
11393 0 : "/Decode [ 1 0 ]\n" );
11394 : }
11395 : }
11396 :
11397 0 : if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651
11398 : {
11399 0 : if( bWriteMask )
11400 : {
11401 0 : nMaskObject = createObject();
11402 0 : if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 )
11403 0 : aLine.append( "/SMask " );
11404 : else
11405 0 : aLine.append( "/Mask " );
11406 0 : aLine.append( nMaskObject );
11407 0 : aLine.append( " 0 R\n" );
11408 : }
11409 0 : else if( aTransparentColor != Color( COL_TRANSPARENT ) )
11410 : {
11411 0 : aLine.append( "/Mask[ " );
11412 0 : if( bTrueColor )
11413 : {
11414 0 : aLine.append( (sal_Int32)aTransparentColor.GetRed() );
11415 0 : aLine.append( ' ' );
11416 0 : aLine.append( (sal_Int32)aTransparentColor.GetRed() );
11417 0 : aLine.append( ' ' );
11418 0 : aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
11419 0 : aLine.append( ' ' );
11420 0 : aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
11421 0 : aLine.append( ' ' );
11422 0 : aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
11423 0 : aLine.append( ' ' );
11424 0 : aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
11425 : }
11426 : else
11427 : {
11428 0 : sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
11429 0 : aLine.append( nIndex );
11430 : }
11431 0 : aLine.append( " ]\n" );
11432 0 : }
11433 : }
11434 0 : else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) )
11435 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
11436 :
11437 : aLine.append( ">>\n"
11438 0 : "stream\n" );
11439 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11440 0 : sal_uInt64 nStartPos = 0;
11441 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nStartPos)) );
11442 :
11443 0 : checkAndEnableStreamEncryption( rObject.m_nObject );
11444 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
11445 0 : if( nBitsPerComponent == 1 )
11446 : {
11447 0 : writeG4Stream( pAccess );
11448 : }
11449 : else
11450 : #endif
11451 : {
11452 0 : beginCompression();
11453 0 : if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
11454 : {
11455 0 : const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U );
11456 :
11457 0 : for( long i = 0; i < pAccess->Height(); i++ )
11458 : {
11459 0 : CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
11460 : }
11461 : }
11462 : else
11463 : {
11464 0 : const int nScanLineBytes = pAccess->Width()*3;
11465 0 : boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] );
11466 0 : for( long y = 0; y < pAccess->Height(); y++ )
11467 : {
11468 0 : for( long x = 0; x < pAccess->Width(); x++ )
11469 : {
11470 0 : BitmapColor aColor = pAccess->GetColor( y, x );
11471 0 : pCol[3*x+0] = aColor.GetRed();
11472 0 : pCol[3*x+1] = aColor.GetGreen();
11473 0 : pCol[3*x+2] = aColor.GetBlue();
11474 0 : }
11475 0 : CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) );
11476 0 : }
11477 : }
11478 0 : endCompression();
11479 : }
11480 0 : disableStreamEncryption();
11481 :
11482 0 : sal_uInt64 nEndPos = 0;
11483 0 : CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nEndPos)) );
11484 0 : aLine.setLength( 0 );
11485 0 : aLine.append( "\nendstream\nendobj\n\n" );
11486 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11487 0 : CHECK_RETURN( updateObject( nStreamLengthObject ) );
11488 0 : aLine.setLength( 0 );
11489 0 : aLine.append( nStreamLengthObject );
11490 0 : aLine.append( " 0 obj\n" );
11491 0 : aLine.append( (sal_Int64)(nEndPos-nStartPos) );
11492 0 : aLine.append( "\nendobj\n\n" );
11493 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
11494 :
11495 0 : if( nMaskObject )
11496 : {
11497 0 : BitmapEmit aEmit;
11498 0 : aEmit.m_nObject = nMaskObject;
11499 0 : aEmit.m_aBitmap = rObject.m_aBitmap;
11500 0 : return writeBitmapObject( aEmit, true );
11501 : }
11502 :
11503 0 : return true;
11504 : }
11505 :
11506 0 : void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask )
11507 : {
11508 0 : MARK( "drawJPGBitmap" );
11509 :
11510 0 : OStringBuffer aLine( 80 );
11511 0 : updateGraphicsState();
11512 :
11513 : // #i40055# sanity check
11514 0 : if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
11515 0 : return;
11516 0 : if( ! (rSizePixel.Width() && rSizePixel.Height()) )
11517 0 : return;
11518 :
11519 0 : rDCTData.Seek( 0 );
11520 0 : if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale )
11521 : {
11522 : // need to convert to grayscale;
11523 : // load stream to bitmap and draw the bitmap instead
11524 0 : Graphic aGraphic;
11525 0 : GraphicConverter::Import( rDCTData, aGraphic, ConvertDataFormat::JPG );
11526 0 : Bitmap aBmp( aGraphic.GetBitmap() );
11527 0 : if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() )
11528 : {
11529 0 : BitmapEx aBmpEx( aBmp, rMask );
11530 0 : drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx );
11531 : }
11532 : else
11533 0 : drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp );
11534 0 : return;
11535 : }
11536 :
11537 0 : SvMemoryStream* pStream = new SvMemoryStream;
11538 0 : pStream->WriteStream( rDCTData );
11539 0 : pStream->Seek( STREAM_SEEK_TO_END );
11540 :
11541 0 : BitmapID aID;
11542 0 : aID.m_aPixelSize = rSizePixel;
11543 0 : aID.m_nSize = pStream->Tell();
11544 0 : pStream->Seek( STREAM_SEEK_TO_BEGIN );
11545 0 : aID.m_nChecksum = rtl_crc32( 0, pStream->GetData(), aID.m_nSize );
11546 0 : if( ! rMask.IsEmpty() )
11547 0 : aID.m_nMaskChecksum = rMask.GetChecksum();
11548 :
11549 0 : std::list< JPGEmit >::const_iterator it;
11550 0 : for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
11551 : ;
11552 0 : if( it == m_aJPGs.end() )
11553 : {
11554 0 : m_aJPGs.push_front( JPGEmit() );
11555 0 : JPGEmit& rEmit = m_aJPGs.front();
11556 0 : rEmit.m_nObject = createObject();
11557 0 : rEmit.m_aID = aID;
11558 0 : rEmit.m_pStream = pStream;
11559 0 : rEmit.m_bTrueColor = bIsTrueColor;
11560 0 : if( !! rMask && rMask.GetSizePixel() == rSizePixel )
11561 0 : rEmit.m_aMask = rMask;
11562 :
11563 0 : it = m_aJPGs.begin();
11564 : }
11565 : else
11566 0 : delete pStream;
11567 :
11568 0 : aLine.append( "q " );
11569 0 : sal_Int32 nCheckWidth = 0;
11570 0 : m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth );
11571 0 : aLine.append( " 0 0 " );
11572 0 : sal_Int32 nCheckHeight = 0;
11573 0 : m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight );
11574 0 : aLine.append( ' ' );
11575 0 : m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
11576 0 : aLine.append( " cm\n/Im" );
11577 0 : aLine.append( it->m_nObject );
11578 0 : aLine.append( " Do Q\n" );
11579 0 : if( nCheckWidth == 0 || nCheckHeight == 0 )
11580 : {
11581 : // #i97512# avoid invalid current matrix
11582 0 : aLine.setLength( 0 );
11583 0 : aLine.append( "\n%jpeg image /Im" );
11584 0 : aLine.append( it->m_nObject );
11585 0 : aLine.append( " scaled to zero size, omitted\n" );
11586 : }
11587 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
11588 :
11589 0 : OStringBuffer aObjName( 16 );
11590 0 : aObjName.append( "Im" );
11591 0 : aObjName.append( it->m_nObject );
11592 0 : pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
11593 :
11594 : }
11595 :
11596 0 : void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
11597 : {
11598 0 : OStringBuffer aLine( 80 );
11599 0 : updateGraphicsState();
11600 :
11601 0 : aLine.append( "q " );
11602 0 : if( rFillColor != Color( COL_TRANSPARENT ) )
11603 : {
11604 0 : appendNonStrokingColor( rFillColor, aLine );
11605 0 : aLine.append( ' ' );
11606 : }
11607 0 : sal_Int32 nCheckWidth = 0;
11608 0 : m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth );
11609 0 : aLine.append( " 0 0 " );
11610 0 : sal_Int32 nCheckHeight = 0;
11611 0 : m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight );
11612 0 : aLine.append( ' ' );
11613 0 : m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
11614 0 : aLine.append( " cm\n/Im" );
11615 0 : aLine.append( rBitmap.m_nObject );
11616 0 : aLine.append( " Do Q\n" );
11617 0 : if( nCheckWidth == 0 || nCheckHeight == 0 )
11618 : {
11619 : // #i97512# avoid invalid current matrix
11620 0 : aLine.setLength( 0 );
11621 0 : aLine.append( "\n%bitmap image /Im" );
11622 0 : aLine.append( rBitmap.m_nObject );
11623 0 : aLine.append( " scaled to zero size, omitted\n" );
11624 : }
11625 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
11626 0 : }
11627 :
11628 0 : const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask )
11629 : {
11630 0 : BitmapEx aBitmap( i_rBitmap );
11631 0 : if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
11632 : {
11633 0 : BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS;
11634 0 : int nDepth = aBitmap.GetBitmap().GetBitCount();
11635 0 : if( nDepth <= 4 )
11636 0 : eConv = BMP_CONVERSION_4BIT_GREYS;
11637 0 : if( nDepth > 1 )
11638 0 : aBitmap.Convert( eConv );
11639 : }
11640 0 : BitmapID aID;
11641 0 : aID.m_aPixelSize = aBitmap.GetSizePixel();
11642 0 : aID.m_nSize = aBitmap.GetBitCount();
11643 0 : aID.m_nChecksum = aBitmap.GetBitmap().GetChecksum();
11644 0 : aID.m_nMaskChecksum = 0;
11645 0 : if( aBitmap.IsAlpha() )
11646 0 : aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum();
11647 : else
11648 : {
11649 0 : Bitmap aMask = aBitmap.GetMask();
11650 0 : if( ! aMask.IsEmpty() )
11651 0 : aID.m_nMaskChecksum = aMask.GetChecksum();
11652 : }
11653 0 : std::list< BitmapEmit >::const_iterator it;
11654 0 : for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
11655 : {
11656 0 : if( aID == it->m_aID )
11657 0 : break;
11658 : }
11659 0 : if( it == m_aBitmaps.end() )
11660 : {
11661 0 : m_aBitmaps.push_front( BitmapEmit() );
11662 0 : m_aBitmaps.front().m_aID = aID;
11663 0 : m_aBitmaps.front().m_aBitmap = aBitmap;
11664 0 : m_aBitmaps.front().m_nObject = createObject();
11665 0 : m_aBitmaps.front().m_bDrawMask = bDrawMask;
11666 0 : it = m_aBitmaps.begin();
11667 : }
11668 :
11669 0 : OStringBuffer aObjName( 16 );
11670 0 : aObjName.append( "Im" );
11671 0 : aObjName.append( it->m_nObject );
11672 0 : pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
11673 :
11674 0 : return *it;
11675 : }
11676 :
11677 0 : void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap )
11678 : {
11679 0 : MARK( "drawBitmap (Bitmap)" );
11680 :
11681 : // #i40055# sanity check
11682 0 : if( ! (rDestSize.Width() && rDestSize.Height()) )
11683 0 : return;
11684 :
11685 0 : const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) );
11686 0 : drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
11687 : }
11688 :
11689 0 : void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
11690 : {
11691 0 : MARK( "drawBitmap (BitmapEx)" );
11692 :
11693 : // #i40055# sanity check
11694 0 : if( ! (rDestSize.Width() && rDestSize.Height()) )
11695 0 : return;
11696 :
11697 0 : const BitmapEmit& rEmit = createBitmapEmit( rBitmap );
11698 0 : drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
11699 : }
11700 :
11701 0 : sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
11702 : {
11703 0 : Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
11704 : MapMode( MAP_POINT ),
11705 : getReferenceDevice(),
11706 0 : rSize ) );
11707 : // check if we already have this gradient
11708 0 : std::list<GradientEmit>::iterator it;
11709 : // rounding to point will generally lose some pixels
11710 : // round up to point boundary
11711 0 : aPtSize.Width()++;
11712 0 : aPtSize.Height()++;
11713 0 : for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
11714 : {
11715 0 : if( it->m_aGradient == rGradient )
11716 : {
11717 0 : if( it->m_aSize == aPtSize )
11718 0 : break;
11719 : }
11720 : }
11721 0 : if( it == m_aGradients.end() )
11722 : {
11723 0 : m_aGradients.push_front( GradientEmit() );
11724 0 : m_aGradients.front().m_aGradient = rGradient;
11725 0 : m_aGradients.front().m_nObject = createObject();
11726 0 : m_aGradients.front().m_aSize = aPtSize;
11727 0 : it = m_aGradients.begin();
11728 : }
11729 :
11730 0 : OStringBuffer aObjName( 16 );
11731 0 : aObjName.append( 'P' );
11732 0 : aObjName.append( it->m_nObject );
11733 0 : pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
11734 :
11735 0 : return it->m_nObject;
11736 : }
11737 :
11738 0 : void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient )
11739 : {
11740 0 : MARK( "drawGradient (Rectangle)" );
11741 :
11742 0 : if( m_aContext.Version == PDFWriter::PDF_1_2 )
11743 : {
11744 0 : drawRectangle( rRect );
11745 0 : return;
11746 : }
11747 :
11748 0 : sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
11749 :
11750 0 : Point aTranslate( rRect.BottomLeft() );
11751 0 : aTranslate += Point( 0, 1 );
11752 :
11753 0 : updateGraphicsState();
11754 :
11755 0 : OStringBuffer aLine( 80 );
11756 0 : aLine.append( "q 1 0 0 1 " );
11757 0 : m_aPages.back().appendPoint( aTranslate, aLine );
11758 0 : aLine.append( " cm " );
11759 : // if a stroke is appended reset the clip region before stroke
11760 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
11761 0 : aLine.append( "q " );
11762 0 : aLine.append( "0 0 " );
11763 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
11764 0 : aLine.append( ' ' );
11765 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
11766 0 : aLine.append( " re W n\n" );
11767 :
11768 0 : aLine.append( "/P" );
11769 0 : aLine.append( nGradient );
11770 0 : aLine.append( " sh " );
11771 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
11772 : {
11773 0 : aLine.append( "Q 0 0 " );
11774 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
11775 0 : aLine.append( ' ' );
11776 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
11777 0 : aLine.append( " re S " );
11778 : }
11779 0 : aLine.append( "Q\n" );
11780 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
11781 : }
11782 :
11783 0 : void PDFWriterImpl::drawHatch( const tools::PolyPolygon& rPolyPoly, const Hatch& rHatch )
11784 : {
11785 0 : MARK( "drawHatch" );
11786 :
11787 0 : updateGraphicsState();
11788 :
11789 0 : if( rPolyPoly.Count() )
11790 : {
11791 0 : tools::PolyPolygon aPolyPoly( rPolyPoly );
11792 :
11793 0 : aPolyPoly.Optimize( PolyOptimizeFlags::NO_SAME );
11794 0 : push( PushFlags::LINECOLOR );
11795 0 : setLineColor( rHatch.GetColor() );
11796 0 : getReferenceDevice()->DrawHatch( aPolyPoly, rHatch, false );
11797 0 : pop();
11798 : }
11799 0 : }
11800 :
11801 0 : void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall )
11802 : {
11803 0 : MARK( "drawWallpaper" );
11804 :
11805 0 : bool bDrawColor = false;
11806 0 : bool bDrawGradient = false;
11807 0 : bool bDrawBitmap = false;
11808 :
11809 0 : BitmapEx aBitmap;
11810 0 : Point aBmpPos = rRect.TopLeft();
11811 0 : Size aBmpSize;
11812 0 : if( rWall.IsBitmap() )
11813 : {
11814 0 : aBitmap = rWall.GetBitmap();
11815 0 : aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
11816 0 : getMapMode(),
11817 : getReferenceDevice(),
11818 0 : aBitmap.GetPrefSize() );
11819 0 : Rectangle aRect( rRect );
11820 0 : if( rWall.IsRect() )
11821 : {
11822 0 : aRect = rWall.GetRect();
11823 0 : aBmpPos = aRect.TopLeft();
11824 0 : aBmpSize = aRect.GetSize();
11825 : }
11826 0 : if( rWall.GetStyle() != WALLPAPER_SCALE )
11827 : {
11828 0 : if( rWall.GetStyle() != WALLPAPER_TILE )
11829 : {
11830 0 : bDrawBitmap = true;
11831 0 : if( rWall.IsGradient() )
11832 0 : bDrawGradient = true;
11833 : else
11834 0 : bDrawColor = true;
11835 0 : switch( rWall.GetStyle() )
11836 : {
11837 : case WALLPAPER_TOPLEFT:
11838 0 : break;
11839 : case WALLPAPER_TOP:
11840 0 : aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
11841 0 : break;
11842 : case WALLPAPER_LEFT:
11843 0 : aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
11844 0 : break;
11845 : case WALLPAPER_TOPRIGHT:
11846 0 : aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
11847 0 : break;
11848 : case WALLPAPER_CENTER:
11849 0 : aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
11850 0 : aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
11851 0 : break;
11852 : case WALLPAPER_RIGHT:
11853 0 : aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
11854 0 : aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
11855 0 : break;
11856 : case WALLPAPER_BOTTOMLEFT:
11857 0 : aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
11858 0 : break;
11859 : case WALLPAPER_BOTTOM:
11860 0 : aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
11861 0 : aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
11862 0 : break;
11863 : case WALLPAPER_BOTTOMRIGHT:
11864 0 : aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
11865 0 : aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
11866 0 : break;
11867 : default: ;
11868 : }
11869 : }
11870 : else
11871 : {
11872 : // push the bitmap
11873 0 : const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) );
11874 :
11875 : // convert to page coordinates; this needs to be done here
11876 : // since the emit does not know the page anymore
11877 0 : Rectangle aConvertRect( aBmpPos, aBmpSize );
11878 0 : m_aPages.back().convertRect( aConvertRect );
11879 :
11880 0 : OStringBuffer aNameBuf(16);
11881 0 : aNameBuf.append( "Im" );
11882 0 : aNameBuf.append( rEmit.m_nObject );
11883 0 : OString aImageName( aNameBuf.makeStringAndClear() );
11884 :
11885 : // push the pattern
11886 0 : OStringBuffer aTilingStream( 32 );
11887 0 : appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
11888 0 : aTilingStream.append( " 0 0 " );
11889 0 : appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
11890 0 : aTilingStream.append( " 0 0 cm\n/" );
11891 0 : aTilingStream.append( aImageName );
11892 0 : aTilingStream.append( " Do\n" );
11893 :
11894 0 : m_aTilings.push_back( TilingEmit() );
11895 0 : m_aTilings.back().m_nObject = createObject();
11896 0 : m_aTilings.back().m_aRectangle = Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
11897 0 : m_aTilings.back().m_pTilingStream = new SvMemoryStream();
11898 0 : m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() );
11899 : // phase the tiling so wallpaper begins on upper left
11900 0 : if ((aConvertRect.GetWidth() == 0) || (aConvertRect.GetHeight() == 0))
11901 0 : throw o3tl::divide_by_zero();
11902 0 : m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
11903 0 : m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
11904 0 : m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
11905 :
11906 0 : updateGraphicsState();
11907 :
11908 0 : OStringBuffer aObjName( 16 );
11909 0 : aObjName.append( 'P' );
11910 0 : aObjName.append( m_aTilings.back().m_nObject );
11911 0 : OString aPatternName( aObjName.makeStringAndClear() );
11912 0 : pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
11913 :
11914 : // fill a rRect with the pattern
11915 0 : OStringBuffer aLine( 100 );
11916 0 : aLine.append( "q /Pattern cs /" );
11917 0 : aLine.append( aPatternName );
11918 0 : aLine.append( " scn " );
11919 0 : m_aPages.back().appendRect( rRect, aLine );
11920 0 : aLine.append( " f Q\n" );
11921 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
11922 : }
11923 : }
11924 : else
11925 : {
11926 0 : aBmpPos = aRect.TopLeft();
11927 0 : aBmpSize = aRect.GetSize();
11928 0 : bDrawBitmap = true;
11929 : }
11930 :
11931 0 : if( aBitmap.IsTransparent() )
11932 : {
11933 0 : if( rWall.IsGradient() )
11934 0 : bDrawGradient = true;
11935 : else
11936 0 : bDrawColor = true;
11937 : }
11938 : }
11939 0 : else if( rWall.IsGradient() )
11940 0 : bDrawGradient = true;
11941 : else
11942 0 : bDrawColor = true;
11943 :
11944 0 : if( bDrawGradient )
11945 : {
11946 0 : drawGradient( rRect, rWall.GetGradient() );
11947 : }
11948 0 : if( bDrawColor )
11949 : {
11950 0 : Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
11951 0 : Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
11952 0 : setLineColor( Color( COL_TRANSPARENT ) );
11953 0 : setFillColor( rWall.GetColor() );
11954 0 : drawRectangle( rRect );
11955 0 : setLineColor( aOldLineColor );
11956 0 : setFillColor( aOldFillColor );
11957 : }
11958 0 : if( bDrawBitmap )
11959 : {
11960 : // set temporary clip region since aBmpPos and aBmpSize
11961 : // may be outside rRect
11962 0 : OStringBuffer aLine( 20 );
11963 0 : aLine.append( "q " );
11964 0 : m_aPages.back().appendRect( rRect, aLine );
11965 0 : aLine.append( " W n\n" );
11966 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
11967 0 : drawBitmap( aBmpPos, aBmpSize, aBitmap );
11968 0 : writeBuffer( "Q\n", 2 );
11969 0 : }
11970 0 : }
11971 :
11972 0 : void PDFWriterImpl::updateGraphicsState(Mode const mode)
11973 : {
11974 0 : OStringBuffer aLine( 256 );
11975 0 : GraphicsState& rNewState = m_aGraphicsStack.front();
11976 : // first set clip region since it might invalidate everything else
11977 :
11978 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) )
11979 : {
11980 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion;
11981 :
11982 0 : if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion ||
11983 0 : ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) )
11984 : {
11985 0 : if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() )
11986 : {
11987 0 : aLine.append( "Q " );
11988 : // invalidate everything but the clip region
11989 0 : m_aCurrentPDFState = GraphicsState();
11990 0 : rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion);
11991 : }
11992 0 : if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() )
11993 : {
11994 : // clip region is always stored in private PDF mapmode
11995 0 : MapMode aNewMapMode = rNewState.m_aMapMode;
11996 0 : rNewState.m_aMapMode = m_aMapMode;
11997 0 : getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
11998 0 : m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
11999 :
12000 0 : aLine.append( "q " );
12001 0 : m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine );
12002 0 : aLine.append( "W* n\n" );
12003 0 : rNewState.m_aMapMode = aNewMapMode;
12004 0 : getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
12005 0 : m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
12006 : }
12007 : }
12008 : }
12009 :
12010 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) )
12011 : {
12012 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode;
12013 0 : getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
12014 : }
12015 :
12016 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) )
12017 : {
12018 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont;
12019 0 : getReferenceDevice()->SetFont( rNewState.m_aFont );
12020 0 : getReferenceDevice()->ImplNewFont();
12021 : }
12022 :
12023 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) )
12024 : {
12025 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode;
12026 0 : getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
12027 : }
12028 :
12029 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) )
12030 : {
12031 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage;
12032 0 : getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
12033 : }
12034 :
12035 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) )
12036 : {
12037 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor;
12038 0 : if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
12039 0 : rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
12040 : {
12041 0 : appendStrokingColor( rNewState.m_aLineColor, aLine );
12042 0 : aLine.append( "\n" );
12043 : }
12044 : }
12045 :
12046 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) )
12047 : {
12048 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor;
12049 0 : if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
12050 0 : rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
12051 : {
12052 0 : appendNonStrokingColor( rNewState.m_aFillColor, aLine );
12053 0 : aLine.append( "\n" );
12054 : }
12055 : }
12056 :
12057 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) )
12058 : {
12059 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent;
12060 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent )
12061 : {
12062 : // TODO: switch extended graphicsstate
12063 : }
12064 : }
12065 :
12066 : // everything is up to date now
12067 0 : m_aCurrentPDFState = m_aGraphicsStack.front();
12068 0 : if ((mode != NOWRITE) && !aLine.isEmpty())
12069 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
12070 0 : }
12071 :
12072 : /* #i47544# imitate OutputDevice behaviour:
12073 : * if a font with a nontransparent color is set, it overwrites the current
12074 : * text color. OTOH setting the text color will overwrite the color of the font.
12075 : */
12076 0 : void PDFWriterImpl::setFont( const vcl::Font& rFont )
12077 : {
12078 0 : Color aColor = rFont.GetColor();
12079 0 : if( aColor == Color( COL_TRANSPARENT ) )
12080 0 : aColor = m_aGraphicsStack.front().m_aFont.GetColor();
12081 0 : m_aGraphicsStack.front().m_aFont = rFont;
12082 0 : m_aGraphicsStack.front().m_aFont.SetColor( aColor );
12083 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont;
12084 0 : }
12085 :
12086 0 : void PDFWriterImpl::push( PushFlags nFlags )
12087 : {
12088 : OSL_ENSURE( !m_aGraphicsStack.empty(), "invalid graphics stack" );
12089 0 : m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
12090 0 : m_aGraphicsStack.front().m_nFlags = nFlags;
12091 0 : }
12092 :
12093 0 : void PDFWriterImpl::pop()
12094 : {
12095 : OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" );
12096 0 : if( m_aGraphicsStack.size() < 2 )
12097 0 : return;
12098 :
12099 0 : GraphicsState aState = m_aGraphicsStack.front();
12100 0 : m_aGraphicsStack.pop_front();
12101 0 : GraphicsState& rOld = m_aGraphicsStack.front();
12102 :
12103 : // move those parameters back that were not pushed
12104 : // in the first place
12105 0 : if( ! (aState.m_nFlags & PushFlags::LINECOLOR) )
12106 0 : setLineColor( aState.m_aLineColor );
12107 0 : if( ! (aState.m_nFlags & PushFlags::FILLCOLOR) )
12108 0 : setFillColor( aState.m_aFillColor );
12109 0 : if( ! (aState.m_nFlags & PushFlags::FONT) )
12110 0 : setFont( aState.m_aFont );
12111 0 : if( ! (aState.m_nFlags & PushFlags::TEXTCOLOR) )
12112 0 : setTextColor( aState.m_aFont.GetColor() );
12113 0 : if( ! (aState.m_nFlags & PushFlags::MAPMODE) )
12114 0 : setMapMode( aState.m_aMapMode );
12115 0 : if( ! (aState.m_nFlags & PushFlags::CLIPREGION) )
12116 : {
12117 : // do not use setClipRegion here
12118 : // it would convert again assuming the current mapmode
12119 0 : rOld.m_aClipRegion = aState.m_aClipRegion;
12120 0 : rOld.m_bClipRegion = aState.m_bClipRegion;
12121 : }
12122 0 : if( ! (aState.m_nFlags & PushFlags::TEXTLINECOLOR ) )
12123 0 : setTextLineColor( aState.m_aTextLineColor );
12124 0 : if( ! (aState.m_nFlags & PushFlags::OVERLINECOLOR ) )
12125 0 : setOverlineColor( aState.m_aOverlineColor );
12126 0 : if( ! (aState.m_nFlags & PushFlags::TEXTALIGN ) )
12127 0 : setTextAlign( aState.m_aFont.GetAlign() );
12128 0 : if( ! (aState.m_nFlags & PushFlags::TEXTFILLCOLOR) )
12129 0 : setTextFillColor( aState.m_aFont.GetFillColor() );
12130 0 : if( ! (aState.m_nFlags & PushFlags::REFPOINT) )
12131 : {
12132 : // what ?
12133 : }
12134 : // invalidate graphics state
12135 0 : m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U);
12136 : }
12137 :
12138 0 : void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
12139 : {
12140 0 : m_aGraphicsStack.front().m_aMapMode = rMapMode;
12141 0 : getReferenceDevice()->SetMapMode( rMapMode );
12142 0 : m_aCurrentPDFState.m_aMapMode = rMapMode;
12143 0 : }
12144 :
12145 0 : void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion )
12146 : {
12147 0 : basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
12148 0 : aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
12149 0 : m_aGraphicsStack.front().m_aClipRegion = aRegion;
12150 0 : m_aGraphicsStack.front().m_bClipRegion = true;
12151 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
12152 0 : }
12153 :
12154 0 : void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
12155 : {
12156 0 : if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() )
12157 : {
12158 0 : Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
12159 : m_aMapMode,
12160 : getReferenceDevice(),
12161 0 : Point( nX, nY ) ) );
12162 0 : aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
12163 : m_aMapMode,
12164 : getReferenceDevice(),
12165 0 : Point() );
12166 0 : basegfx::B2DHomMatrix aMat;
12167 0 : aMat.translate( aPoint.X(), aPoint.Y() );
12168 0 : m_aGraphicsStack.front().m_aClipRegion.transform( aMat );
12169 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
12170 : }
12171 0 : }
12172 :
12173 0 : bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect )
12174 : {
12175 : basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect(
12176 0 : basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) );
12177 0 : return intersectClipRegion( aRect );
12178 : }
12179 :
12180 0 : bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion )
12181 : {
12182 0 : basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) );
12183 0 : aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
12184 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
12185 0 : if( m_aGraphicsStack.front().m_bClipRegion )
12186 : {
12187 0 : basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) );
12188 0 : aRegion = basegfx::tools::prepareForPolygonOperation( aRegion );
12189 0 : m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion );
12190 : }
12191 : else
12192 : {
12193 0 : m_aGraphicsStack.front().m_aClipRegion = aRegion;
12194 0 : m_aGraphicsStack.front().m_bClipRegion = true;
12195 : }
12196 0 : return true;
12197 : }
12198 :
12199 0 : void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
12200 : {
12201 0 : if( nPageNr < 0 )
12202 0 : nPageNr = m_nCurrentPage;
12203 :
12204 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
12205 0 : return;
12206 :
12207 0 : m_aNotes.push_back( PDFNoteEntry() );
12208 0 : m_aNotes.back().m_nObject = createObject();
12209 0 : m_aNotes.back().m_aContents = rNote;
12210 0 : m_aNotes.back().m_aRect = rRect;
12211 : // convert to default user space now, since the mapmode may change
12212 0 : m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
12213 :
12214 : // insert note to page's annotation list
12215 0 : m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
12216 : }
12217 :
12218 0 : sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr )
12219 : {
12220 0 : if( nPageNr < 0 )
12221 0 : nPageNr = m_nCurrentPage;
12222 :
12223 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
12224 0 : return -1;
12225 :
12226 0 : sal_Int32 nRet = m_aLinks.size();
12227 :
12228 0 : m_aLinks.push_back( PDFLink() );
12229 0 : m_aLinks.back().m_nObject = createObject();
12230 0 : m_aLinks.back().m_nPage = nPageNr;
12231 0 : m_aLinks.back().m_aRect = rRect;
12232 : // convert to default user space now, since the mapmode may change
12233 0 : m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
12234 :
12235 : // insert link to page's annotation list
12236 0 : m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
12237 :
12238 0 : return nRet;
12239 : }
12240 :
12241 : //--->i56629
12242 0 : sal_Int32 PDFWriterImpl::createNamedDest( const OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
12243 : {
12244 0 : if( nPageNr < 0 )
12245 0 : nPageNr = m_nCurrentPage;
12246 :
12247 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
12248 0 : return -1;
12249 :
12250 0 : sal_Int32 nRet = m_aNamedDests.size();
12251 :
12252 0 : m_aNamedDests.push_back( PDFNamedDest() );
12253 0 : m_aNamedDests.back().m_aDestName = sDestName;
12254 0 : m_aNamedDests.back().m_nPage = nPageNr;
12255 0 : m_aNamedDests.back().m_eType = eType;
12256 0 : m_aNamedDests.back().m_aRect = rRect;
12257 : // convert to default user space now, since the mapmode may change
12258 0 : m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
12259 :
12260 0 : return nRet;
12261 : }
12262 : //<---i56629
12263 :
12264 0 : sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
12265 : {
12266 0 : if( nPageNr < 0 )
12267 0 : nPageNr = m_nCurrentPage;
12268 :
12269 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
12270 0 : return -1;
12271 :
12272 0 : sal_Int32 nRet = m_aDests.size();
12273 :
12274 0 : m_aDests.push_back( PDFDest() );
12275 0 : m_aDests.back().m_nPage = nPageNr;
12276 0 : m_aDests.back().m_eType = eType;
12277 0 : m_aDests.back().m_aRect = rRect;
12278 : // convert to default user space now, since the mapmode may change
12279 0 : m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
12280 :
12281 0 : return nRet;
12282 : }
12283 :
12284 0 : sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
12285 : {
12286 0 : return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType );
12287 : }
12288 :
12289 0 : sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
12290 : {
12291 0 : if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
12292 0 : return -1;
12293 0 : if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
12294 0 : return -2;
12295 :
12296 0 : m_aLinks[ nLinkId ].m_nDest = nDestId;
12297 :
12298 0 : return 0;
12299 : }
12300 :
12301 0 : sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
12302 : {
12303 0 : if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
12304 0 : return -1;
12305 :
12306 0 : m_aLinks[ nLinkId ].m_nDest = -1;
12307 :
12308 : using namespace ::com::sun::star;
12309 :
12310 0 : if (!m_xTrans.is())
12311 : {
12312 0 : uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
12313 0 : m_xTrans = util::URLTransformer::create(xContext);;
12314 : }
12315 :
12316 0 : util::URL aURL;
12317 0 : aURL.Complete = rURL;
12318 :
12319 0 : m_xTrans->parseStrict( aURL );
12320 :
12321 0 : m_aLinks[ nLinkId ].m_aURL = aURL.Complete;
12322 :
12323 0 : return 0;
12324 : }
12325 :
12326 0 : void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
12327 : {
12328 0 : m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
12329 0 : }
12330 :
12331 0 : sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
12332 : {
12333 : // create new item
12334 0 : sal_Int32 nNewItem = m_aOutline.size();
12335 0 : m_aOutline.push_back( PDFOutlineEntry() );
12336 :
12337 : // set item attributes
12338 0 : setOutlineItemParent( nNewItem, nParent );
12339 0 : setOutlineItemText( nNewItem, rText );
12340 0 : setOutlineItemDest( nNewItem, nDestID );
12341 :
12342 0 : return nNewItem;
12343 : }
12344 :
12345 0 : sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
12346 : {
12347 0 : if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
12348 0 : return -1;
12349 :
12350 0 : int nRet = 0;
12351 :
12352 0 : if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
12353 : {
12354 0 : nNewParent = 0;
12355 0 : nRet = -2;
12356 : }
12357 : // remove item from previous parent
12358 0 : sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID;
12359 0 : if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() )
12360 : {
12361 0 : PDFOutlineEntry& rParent = m_aOutline[ nParentID ];
12362 :
12363 0 : for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin();
12364 0 : it != rParent.m_aChildren.end(); ++it )
12365 : {
12366 0 : if( *it == nItem )
12367 : {
12368 0 : rParent.m_aChildren.erase( it );
12369 0 : break;
12370 : }
12371 : }
12372 : }
12373 :
12374 : // insert item to new parent's list of children
12375 0 : m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
12376 :
12377 0 : return nRet;
12378 : }
12379 :
12380 0 : sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
12381 : {
12382 0 : if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
12383 0 : return -1;
12384 :
12385 0 : m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText );
12386 0 : return 0;
12387 : }
12388 :
12389 0 : sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
12390 : {
12391 0 : if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
12392 0 : return -1;
12393 0 : if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
12394 0 : return -2;
12395 0 : m_aOutline[nItem].m_nDestID = nDestID;
12396 0 : return 0;
12397 : }
12398 :
12399 0 : const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
12400 : {
12401 0 : static std::map< PDFWriter::StructElement, const char* > aTagStrings;
12402 0 : if( aTagStrings.empty() )
12403 : {
12404 0 : aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
12405 0 : aTagStrings[ PDFWriter::Document ] = "Document";
12406 0 : aTagStrings[ PDFWriter::Part ] = "Part";
12407 0 : aTagStrings[ PDFWriter::Article ] = "Art";
12408 0 : aTagStrings[ PDFWriter::Section ] = "Sect";
12409 0 : aTagStrings[ PDFWriter::Division ] = "Div";
12410 0 : aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote";
12411 0 : aTagStrings[ PDFWriter::Caption ] = "Caption";
12412 0 : aTagStrings[ PDFWriter::TOC ] = "TOC";
12413 0 : aTagStrings[ PDFWriter::TOCI ] = "TOCI";
12414 0 : aTagStrings[ PDFWriter::Index ] = "Index";
12415 0 : aTagStrings[ PDFWriter::Paragraph ] = "P";
12416 0 : aTagStrings[ PDFWriter::Heading ] = "H";
12417 0 : aTagStrings[ PDFWriter::H1 ] = "H1";
12418 0 : aTagStrings[ PDFWriter::H2 ] = "H2";
12419 0 : aTagStrings[ PDFWriter::H3 ] = "H3";
12420 0 : aTagStrings[ PDFWriter::H4 ] = "H4";
12421 0 : aTagStrings[ PDFWriter::H5 ] = "H5";
12422 0 : aTagStrings[ PDFWriter::H6 ] = "H6";
12423 0 : aTagStrings[ PDFWriter::List ] = "L";
12424 0 : aTagStrings[ PDFWriter::ListItem ] = "LI";
12425 0 : aTagStrings[ PDFWriter::LILabel ] = "Lbl";
12426 0 : aTagStrings[ PDFWriter::LIBody ] = "LBody";
12427 0 : aTagStrings[ PDFWriter::Table ] = "Table";
12428 0 : aTagStrings[ PDFWriter::TableRow ] = "TR";
12429 0 : aTagStrings[ PDFWriter::TableHeader ] = "TH";
12430 0 : aTagStrings[ PDFWriter::TableData ] = "TD";
12431 0 : aTagStrings[ PDFWriter::Span ] = "Span";
12432 0 : aTagStrings[ PDFWriter::Quote ] = "Quote";
12433 0 : aTagStrings[ PDFWriter::Note ] = "Note";
12434 0 : aTagStrings[ PDFWriter::Reference ] = "Reference";
12435 0 : aTagStrings[ PDFWriter::BibEntry ] = "BibEntry";
12436 0 : aTagStrings[ PDFWriter::Code ] = "Code";
12437 0 : aTagStrings[ PDFWriter::Link ] = "Link";
12438 0 : aTagStrings[ PDFWriter::Figure ] = "Figure";
12439 0 : aTagStrings[ PDFWriter::Formula ] = "Formula";
12440 0 : aTagStrings[ PDFWriter::Form ] = "Form";
12441 : }
12442 :
12443 0 : std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
12444 :
12445 0 : return it != aTagStrings.end() ? it->second : "Div";
12446 : }
12447 :
12448 0 : void PDFWriterImpl::beginStructureElementMCSeq()
12449 : {
12450 0 : if( m_bEmitStructure &&
12451 0 : m_nCurrentStructElement > 0 && // StructTreeRoot
12452 0 : ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
12453 : )
12454 : {
12455 0 : PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
12456 0 : OStringBuffer aLine( 128 );
12457 0 : sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
12458 0 : aLine.append( "/" );
12459 0 : if( !rEle.m_aAlias.isEmpty() )
12460 0 : aLine.append( rEle.m_aAlias );
12461 : else
12462 0 : aLine.append( getStructureTag( rEle.m_eType ) );
12463 0 : aLine.append( "<</MCID " );
12464 0 : aLine.append( nMCID );
12465 0 : aLine.append( ">>BDC\n" );
12466 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
12467 :
12468 : // update the element's content list
12469 : #if OSL_DEBUG_LEVEL > 1
12470 : fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n",
12471 : nMCID,
12472 : m_aPages[ m_nCurrentPage ].m_nPageObject,
12473 : rEle.m_nFirstPageObject );
12474 : #endif
12475 0 : rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) );
12476 : // update the page's mcid parent list
12477 0 : m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
12478 : // mark element MC sequence as open
12479 0 : rEle.m_bOpenMCSeq = true;
12480 : }
12481 : // handle artifacts
12482 0 : else if( ! m_bEmitStructure && m_aContext.Tagged &&
12483 0 : m_nCurrentStructElement > 0 &&
12484 0 : m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
12485 0 : ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
12486 : )
12487 : {
12488 0 : OStringBuffer aLine( 128 );
12489 0 : aLine.append( "/Artifact BMC\n" );
12490 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
12491 : // mark element MC sequence as open
12492 0 : m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
12493 : }
12494 0 : }
12495 :
12496 0 : void PDFWriterImpl::endStructureElementMCSeq()
12497 : {
12498 0 : if( m_nCurrentStructElement > 0 && // StructTreeRoot
12499 0 : ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
12500 0 : m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
12501 : )
12502 : {
12503 0 : writeBuffer( "EMC\n", 4 );
12504 0 : m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
12505 : }
12506 0 : }
12507 :
12508 0 : bool PDFWriterImpl::checkEmitStructure()
12509 : {
12510 0 : bool bEmit = false;
12511 0 : if( m_aContext.Tagged )
12512 : {
12513 0 : bEmit = true;
12514 0 : sal_Int32 nEle = m_nCurrentStructElement;
12515 0 : while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
12516 : {
12517 0 : if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
12518 : {
12519 0 : bEmit = false;
12520 0 : break;
12521 : }
12522 0 : nEle = m_aStructure[ nEle ].m_nParentElement;
12523 : }
12524 : }
12525 0 : return bEmit;
12526 : }
12527 :
12528 0 : sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const OUString& rAlias )
12529 : {
12530 0 : if( m_nCurrentPage < 0 )
12531 0 : return -1;
12532 :
12533 0 : if( ! m_aContext.Tagged )
12534 0 : return -1;
12535 :
12536 : // close eventual current MC sequence
12537 0 : endStructureElementMCSeq();
12538 :
12539 0 : if( m_nCurrentStructElement == 0 &&
12540 0 : eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
12541 : {
12542 : // struct tree root hit, but not beginning document
12543 : // this might happen with setCurrentStructureElement
12544 : // silently insert structure into document again if one properly exists
12545 0 : if( ! m_aStructure[ 0 ].m_aChildren.empty() )
12546 : {
12547 0 : PDFWriter::StructElement childType = PDFWriter::NonStructElement;
12548 0 : sal_Int32 nNewCurElement = 0;
12549 0 : const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
12550 0 : for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
12551 0 : childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
12552 : {
12553 0 : nNewCurElement = *it;
12554 0 : childType = m_aStructure[ nNewCurElement ].m_eType;
12555 : }
12556 0 : if( childType == PDFWriter::Document )
12557 : {
12558 0 : m_nCurrentStructElement = nNewCurElement;
12559 : DBG_ASSERT( false, "Structure element inserted to StructTreeRoot that is not a document" );
12560 : }
12561 : else {
12562 : OSL_FAIL( "document structure in disorder !" );
12563 : }
12564 : }
12565 : else {
12566 : OSL_FAIL( "PDF document structure MUST be contained in a Document element" );
12567 : }
12568 : }
12569 :
12570 0 : sal_Int32 nNewId = sal_Int32(m_aStructure.size());
12571 0 : m_aStructure.push_back( PDFStructureElement() );
12572 0 : PDFStructureElement& rEle = m_aStructure.back();
12573 0 : rEle.m_eType = eType;
12574 0 : rEle.m_nOwnElement = nNewId;
12575 0 : rEle.m_nParentElement = m_nCurrentStructElement;
12576 0 : rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject;
12577 0 : m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
12578 0 : m_nCurrentStructElement = nNewId;
12579 :
12580 : // handle alias names
12581 0 : if( !rAlias.isEmpty() && eType != PDFWriter::NonStructElement )
12582 : {
12583 0 : OStringBuffer aNameBuf( rAlias.getLength() );
12584 0 : appendName( rAlias, aNameBuf );
12585 0 : OString aAliasName( aNameBuf.makeStringAndClear() );
12586 0 : rEle.m_aAlias = aAliasName;
12587 0 : m_aRoleMap[ aAliasName ] = getStructureTag( eType );
12588 : }
12589 :
12590 : #if OSL_DEBUG_LEVEL > 1
12591 : OStringBuffer aLine( "beginStructureElement " );
12592 : aLine.append( m_nCurrentStructElement );
12593 : aLine.append( ": " );
12594 : aLine.append( getStructureTag( eType ) );
12595 : if( !rEle.m_aAlias.isEmpty() )
12596 : {
12597 : aLine.append( " aliased as \"" );
12598 : aLine.append( rEle.m_aAlias );
12599 : aLine.append( '\"' );
12600 : }
12601 : emitComment( aLine.getStr() );
12602 : #endif
12603 :
12604 : // check whether to emit structure henceforth
12605 0 : m_bEmitStructure = checkEmitStructure();
12606 :
12607 0 : if( m_bEmitStructure ) // don't create nonexistant objects
12608 : {
12609 0 : rEle.m_nObject = createObject();
12610 : // update parent's kids list
12611 0 : m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back(PDFStructureElementKid(rEle.m_nObject));
12612 : }
12613 0 : return nNewId;
12614 : }
12615 :
12616 0 : void PDFWriterImpl::endStructureElement()
12617 : {
12618 0 : if( m_nCurrentPage < 0 )
12619 0 : return;
12620 :
12621 0 : if( ! m_aContext.Tagged )
12622 0 : return;
12623 :
12624 0 : if( m_nCurrentStructElement == 0 )
12625 : {
12626 : // hit the struct tree root, that means there is an endStructureElement
12627 : // without corresponding beginStructureElement
12628 0 : return;
12629 : }
12630 :
12631 : // end the marked content sequence
12632 0 : endStructureElementMCSeq();
12633 :
12634 : #if OSL_DEBUG_LEVEL > 1
12635 : OStringBuffer aLine( "endStructureElement " );
12636 : aLine.append( m_nCurrentStructElement );
12637 : aLine.append( ": " );
12638 : aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
12639 : if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
12640 : {
12641 : aLine.append( " aliased as \"" );
12642 : aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
12643 : aLine.append( '\"' );
12644 : }
12645 : #endif
12646 :
12647 : // "end" the structure element, the parent becomes current element
12648 0 : m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
12649 :
12650 : // check whether to emit structure henceforth
12651 0 : m_bEmitStructure = checkEmitStructure();
12652 :
12653 : #if OSL_DEBUG_LEVEL > 1
12654 : if( m_bEmitStructure )
12655 : emitComment( aLine.getStr() );
12656 : #endif
12657 : }
12658 :
12659 : //---> i94258
12660 : /*
12661 : * This function adds an internal structure list container to overcome the 8191 elements array limitation
12662 : * in kids element emission.
12663 : * Recursive function
12664 : *
12665 : */
12666 0 : void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
12667 : {
12668 0 : if( rEle.m_eType == PDFWriter::NonStructElement &&
12669 0 : rEle.m_nOwnElement != rEle.m_nParentElement )
12670 0 : return;
12671 :
12672 0 : for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
12673 : {
12674 0 : if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
12675 : {
12676 0 : PDFStructureElement& rChild = m_aStructure[ *it ];
12677 0 : if( rChild.m_eType != PDFWriter::NonStructElement )
12678 : {
12679 : //triggered when a child of the rEle element is found
12680 0 : if( rChild.m_nParentElement == rEle.m_nOwnElement )
12681 0 : addInternalStructureContainer( rChild );//examine the child
12682 : else
12683 : {
12684 : OSL_FAIL( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
12685 : #if OSL_DEBUG_LEVEL > 1
12686 : fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
12687 : #endif
12688 : }
12689 : }
12690 : }
12691 : else
12692 : {
12693 : OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
12694 : #if OSL_DEBUG_LEVEL > 1
12695 : fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
12696 : #endif
12697 : }
12698 : }
12699 :
12700 0 : if( rEle.m_nOwnElement != rEle.m_nParentElement )
12701 : {
12702 0 : if( !rEle.m_aKids.empty() )
12703 : {
12704 0 : if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
12705 : //then we need to add the containers for the kids elements
12706 : // a list to be used for the new kid element
12707 0 : std::list< PDFStructureElementKid > aNewKids;
12708 0 : std::list< sal_Int32 > aNewChildren;
12709 :
12710 : // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
12711 0 : OStringBuffer aNameBuf( "Div" );
12712 0 : OString aAliasName( aNameBuf.makeStringAndClear() );
12713 0 : m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
12714 :
12715 0 : while( rEle.m_aKids.size() > ncMaxPDFArraySize )
12716 : {
12717 0 : sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
12718 0 : sal_Int32 nNewId = sal_Int32(m_aStructure.size());
12719 0 : m_aStructure.push_back( PDFStructureElement() );
12720 0 : PDFStructureElement& rEleNew = m_aStructure.back();
12721 0 : rEleNew.m_aAlias = aAliasName;
12722 0 : rEleNew.m_eType = PDFWriter::Division; // a new Div type container
12723 0 : rEleNew.m_nOwnElement = nNewId;
12724 0 : rEleNew.m_nParentElement = nCurrentStructElement;
12725 : //inherit the same page as the first child to be reparented
12726 0 : rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
12727 0 : rEleNew.m_nObject = createObject();//assign a PDF object number
12728 : //add the object to the kid list of the parent
12729 0 : aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) );
12730 0 : aNewChildren.push_back( nNewId );
12731 :
12732 0 : std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
12733 0 : std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
12734 0 : advance( aChildEndIt, ncMaxPDFArraySize );
12735 0 : advance( aKidEndIt, ncMaxPDFArraySize );
12736 :
12737 : rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
12738 : rEle.m_aKids,
12739 : rEle.m_aKids.begin(),
12740 0 : aKidEndIt );
12741 : rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
12742 : rEle.m_aChildren,
12743 : rEle.m_aChildren.begin(),
12744 0 : aChildEndIt );
12745 : // set the kid's new parent
12746 0 : for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
12747 0 : it != rEleNew.m_aChildren.end(); ++it )
12748 : {
12749 0 : m_aStructure[ *it ].m_nParentElement = nNewId;
12750 : }
12751 : }
12752 : //finally add the new kids resulting from the container added
12753 0 : rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
12754 0 : rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
12755 : }
12756 : }
12757 : }
12758 : }
12759 : //<--- i94258
12760 :
12761 0 : bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
12762 : {
12763 0 : bool bSuccess = false;
12764 :
12765 0 : if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
12766 : {
12767 : // end eventual previous marked content sequence
12768 0 : endStructureElementMCSeq();
12769 :
12770 0 : m_nCurrentStructElement = nEle;
12771 0 : m_bEmitStructure = checkEmitStructure();
12772 : #if OSL_DEBUG_LEVEL > 1
12773 : OStringBuffer aLine( "setCurrentStructureElement " );
12774 : aLine.append( m_nCurrentStructElement );
12775 : aLine.append( ": " );
12776 : aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
12777 : if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
12778 : {
12779 : aLine.append( " aliased as \"" );
12780 : aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
12781 : aLine.append( '\"' );
12782 : }
12783 : if( ! m_bEmitStructure )
12784 : aLine.append( " (inside NonStruct)" );
12785 : emitComment( aLine.getStr() );
12786 : #endif
12787 0 : bSuccess = true;
12788 : }
12789 :
12790 0 : return bSuccess;
12791 : }
12792 :
12793 0 : bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
12794 : {
12795 0 : if( !m_aContext.Tagged )
12796 0 : return false;
12797 :
12798 0 : bool bInsert = false;
12799 0 : if( m_nCurrentStructElement > 0 && m_bEmitStructure )
12800 : {
12801 0 : PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
12802 0 : switch( eAttr )
12803 : {
12804 : case PDFWriter::Placement:
12805 0 : if( eVal == PDFWriter::Block ||
12806 0 : eVal == PDFWriter::Inline ||
12807 0 : eVal == PDFWriter::Before ||
12808 0 : eVal == PDFWriter::Start ||
12809 : eVal == PDFWriter::End )
12810 0 : bInsert = true;
12811 0 : break;
12812 : case PDFWriter::WritingMode:
12813 0 : if( eVal == PDFWriter::LrTb ||
12814 0 : eVal == PDFWriter::RlTb ||
12815 : eVal == PDFWriter::TbRl )
12816 : {
12817 0 : bInsert = true;
12818 : }
12819 0 : break;
12820 : case PDFWriter::TextAlign:
12821 0 : if( eVal == PDFWriter::Start ||
12822 0 : eVal == PDFWriter::Center ||
12823 0 : eVal == PDFWriter::End ||
12824 : eVal == PDFWriter::Justify )
12825 : {
12826 0 : if( eType == PDFWriter::Paragraph ||
12827 0 : eType == PDFWriter::Heading ||
12828 0 : eType == PDFWriter::H1 ||
12829 0 : eType == PDFWriter::H2 ||
12830 0 : eType == PDFWriter::H3 ||
12831 0 : eType == PDFWriter::H4 ||
12832 0 : eType == PDFWriter::H5 ||
12833 0 : eType == PDFWriter::H6 ||
12834 0 : eType == PDFWriter::List ||
12835 0 : eType == PDFWriter::ListItem ||
12836 0 : eType == PDFWriter::LILabel ||
12837 0 : eType == PDFWriter::LIBody ||
12838 0 : eType == PDFWriter::Table ||
12839 0 : eType == PDFWriter::TableRow ||
12840 0 : eType == PDFWriter::TableHeader ||
12841 : eType == PDFWriter::TableData )
12842 : {
12843 0 : bInsert = true;
12844 : }
12845 : }
12846 0 : break;
12847 : case PDFWriter::Width:
12848 : case PDFWriter::Height:
12849 0 : if( eVal == PDFWriter::Auto )
12850 : {
12851 0 : if( eType == PDFWriter::Figure ||
12852 0 : eType == PDFWriter::Formula ||
12853 0 : eType == PDFWriter::Form ||
12854 0 : eType == PDFWriter::Table ||
12855 0 : eType == PDFWriter::TableHeader ||
12856 : eType == PDFWriter::TableData )
12857 : {
12858 0 : bInsert = true;
12859 : }
12860 : }
12861 0 : break;
12862 : case PDFWriter::BlockAlign:
12863 0 : if( eVal == PDFWriter::Before ||
12864 0 : eVal == PDFWriter::Middle ||
12865 0 : eVal == PDFWriter::After ||
12866 : eVal == PDFWriter::Justify )
12867 : {
12868 0 : if( eType == PDFWriter::TableHeader ||
12869 : eType == PDFWriter::TableData )
12870 : {
12871 0 : bInsert = true;
12872 : }
12873 : }
12874 0 : break;
12875 : case PDFWriter::InlineAlign:
12876 0 : if( eVal == PDFWriter::Start ||
12877 0 : eVal == PDFWriter::Center ||
12878 : eVal == PDFWriter::End )
12879 : {
12880 0 : if( eType == PDFWriter::TableHeader ||
12881 : eType == PDFWriter::TableData )
12882 : {
12883 0 : bInsert = true;
12884 : }
12885 : }
12886 0 : break;
12887 : case PDFWriter::LineHeight:
12888 0 : if( eVal == PDFWriter::Normal ||
12889 : eVal == PDFWriter::Auto )
12890 : {
12891 : // only for ILSE and BLSE
12892 0 : if( eType == PDFWriter::Paragraph ||
12893 0 : eType == PDFWriter::Heading ||
12894 0 : eType == PDFWriter::H1 ||
12895 0 : eType == PDFWriter::H2 ||
12896 0 : eType == PDFWriter::H3 ||
12897 0 : eType == PDFWriter::H4 ||
12898 0 : eType == PDFWriter::H5 ||
12899 0 : eType == PDFWriter::H6 ||
12900 0 : eType == PDFWriter::List ||
12901 0 : eType == PDFWriter::ListItem ||
12902 0 : eType == PDFWriter::LILabel ||
12903 0 : eType == PDFWriter::LIBody ||
12904 0 : eType == PDFWriter::Table ||
12905 0 : eType == PDFWriter::TableRow ||
12906 0 : eType == PDFWriter::TableHeader ||
12907 0 : eType == PDFWriter::TableData ||
12908 0 : eType == PDFWriter::Span ||
12909 0 : eType == PDFWriter::Quote ||
12910 0 : eType == PDFWriter::Note ||
12911 0 : eType == PDFWriter::Reference ||
12912 0 : eType == PDFWriter::BibEntry ||
12913 0 : eType == PDFWriter::Code ||
12914 : eType == PDFWriter::Link )
12915 : {
12916 0 : bInsert = true;
12917 : }
12918 : }
12919 0 : break;
12920 : case PDFWriter::TextDecorationType:
12921 0 : if( eVal == PDFWriter::NONE ||
12922 0 : eVal == PDFWriter::Underline ||
12923 0 : eVal == PDFWriter::Overline ||
12924 : eVal == PDFWriter::LineThrough )
12925 : {
12926 : // only for ILSE and BLSE
12927 0 : if( eType == PDFWriter::Paragraph ||
12928 0 : eType == PDFWriter::Heading ||
12929 0 : eType == PDFWriter::H1 ||
12930 0 : eType == PDFWriter::H2 ||
12931 0 : eType == PDFWriter::H3 ||
12932 0 : eType == PDFWriter::H4 ||
12933 0 : eType == PDFWriter::H5 ||
12934 0 : eType == PDFWriter::H6 ||
12935 0 : eType == PDFWriter::List ||
12936 0 : eType == PDFWriter::ListItem ||
12937 0 : eType == PDFWriter::LILabel ||
12938 0 : eType == PDFWriter::LIBody ||
12939 0 : eType == PDFWriter::Table ||
12940 0 : eType == PDFWriter::TableRow ||
12941 0 : eType == PDFWriter::TableHeader ||
12942 0 : eType == PDFWriter::TableData ||
12943 0 : eType == PDFWriter::Span ||
12944 0 : eType == PDFWriter::Quote ||
12945 0 : eType == PDFWriter::Note ||
12946 0 : eType == PDFWriter::Reference ||
12947 0 : eType == PDFWriter::BibEntry ||
12948 0 : eType == PDFWriter::Code ||
12949 : eType == PDFWriter::Link )
12950 : {
12951 0 : bInsert = true;
12952 : }
12953 : }
12954 0 : break;
12955 : case PDFWriter::ListNumbering:
12956 0 : if( eVal == PDFWriter::NONE ||
12957 0 : eVal == PDFWriter::Disc ||
12958 0 : eVal == PDFWriter::Circle ||
12959 0 : eVal == PDFWriter::Square ||
12960 0 : eVal == PDFWriter::Decimal ||
12961 0 : eVal == PDFWriter::UpperRoman ||
12962 0 : eVal == PDFWriter::LowerRoman ||
12963 0 : eVal == PDFWriter::UpperAlpha ||
12964 : eVal == PDFWriter::LowerAlpha )
12965 : {
12966 0 : if( eType == PDFWriter::List )
12967 0 : bInsert = true;
12968 : }
12969 0 : break;
12970 0 : default: break;
12971 : }
12972 : }
12973 :
12974 0 : if( bInsert )
12975 0 : m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
12976 : #if OSL_DEBUG_LEVEL > 1
12977 : else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
12978 : fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
12979 : getAttributeTag( eAttr ),
12980 : getAttributeValueTag( eVal ),
12981 : getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
12982 : m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
12983 : );
12984 : #endif
12985 :
12986 0 : return bInsert;
12987 : }
12988 :
12989 0 : bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
12990 : {
12991 0 : if( ! m_aContext.Tagged )
12992 0 : return false;
12993 :
12994 0 : bool bInsert = false;
12995 0 : if( m_nCurrentStructElement > 0 && m_bEmitStructure )
12996 : {
12997 0 : if( eAttr == PDFWriter::Language )
12998 : {
12999 0 : m_aStructure[ m_nCurrentStructElement ].m_aLocale = LanguageTag( (LanguageType)nValue ).getLocale();
13000 0 : return true;
13001 : }
13002 :
13003 0 : PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
13004 0 : switch( eAttr )
13005 : {
13006 : case PDFWriter::SpaceBefore:
13007 : case PDFWriter::SpaceAfter:
13008 : case PDFWriter::StartIndent:
13009 : case PDFWriter::EndIndent:
13010 : // just for BLSE
13011 0 : if( eType == PDFWriter::Paragraph ||
13012 0 : eType == PDFWriter::Heading ||
13013 0 : eType == PDFWriter::H1 ||
13014 0 : eType == PDFWriter::H2 ||
13015 0 : eType == PDFWriter::H3 ||
13016 0 : eType == PDFWriter::H4 ||
13017 0 : eType == PDFWriter::H5 ||
13018 0 : eType == PDFWriter::H6 ||
13019 0 : eType == PDFWriter::List ||
13020 0 : eType == PDFWriter::ListItem ||
13021 0 : eType == PDFWriter::LILabel ||
13022 0 : eType == PDFWriter::LIBody ||
13023 0 : eType == PDFWriter::Table ||
13024 0 : eType == PDFWriter::TableRow ||
13025 0 : eType == PDFWriter::TableHeader ||
13026 : eType == PDFWriter::TableData )
13027 : {
13028 0 : bInsert = true;
13029 : }
13030 0 : break;
13031 : case PDFWriter::TextIndent:
13032 : // paragraph like BLSE and additional elements
13033 0 : if( eType == PDFWriter::Paragraph ||
13034 0 : eType == PDFWriter::Heading ||
13035 0 : eType == PDFWriter::H1 ||
13036 0 : eType == PDFWriter::H2 ||
13037 0 : eType == PDFWriter::H3 ||
13038 0 : eType == PDFWriter::H4 ||
13039 0 : eType == PDFWriter::H5 ||
13040 0 : eType == PDFWriter::H6 ||
13041 0 : eType == PDFWriter::LILabel ||
13042 0 : eType == PDFWriter::LIBody ||
13043 0 : eType == PDFWriter::TableHeader ||
13044 : eType == PDFWriter::TableData )
13045 : {
13046 0 : bInsert = true;
13047 : }
13048 0 : break;
13049 : case PDFWriter::Width:
13050 : case PDFWriter::Height:
13051 0 : if( eType == PDFWriter::Figure ||
13052 0 : eType == PDFWriter::Formula ||
13053 0 : eType == PDFWriter::Form ||
13054 0 : eType == PDFWriter::Table ||
13055 0 : eType == PDFWriter::TableHeader ||
13056 : eType == PDFWriter::TableData )
13057 : {
13058 0 : bInsert = true;
13059 : }
13060 0 : break;
13061 : case PDFWriter::LineHeight:
13062 : case PDFWriter::BaselineShift:
13063 : // only for ILSE and BLSE
13064 0 : if( eType == PDFWriter::Paragraph ||
13065 0 : eType == PDFWriter::Heading ||
13066 0 : eType == PDFWriter::H1 ||
13067 0 : eType == PDFWriter::H2 ||
13068 0 : eType == PDFWriter::H3 ||
13069 0 : eType == PDFWriter::H4 ||
13070 0 : eType == PDFWriter::H5 ||
13071 0 : eType == PDFWriter::H6 ||
13072 0 : eType == PDFWriter::List ||
13073 0 : eType == PDFWriter::ListItem ||
13074 0 : eType == PDFWriter::LILabel ||
13075 0 : eType == PDFWriter::LIBody ||
13076 0 : eType == PDFWriter::Table ||
13077 0 : eType == PDFWriter::TableRow ||
13078 0 : eType == PDFWriter::TableHeader ||
13079 0 : eType == PDFWriter::TableData ||
13080 0 : eType == PDFWriter::Span ||
13081 0 : eType == PDFWriter::Quote ||
13082 0 : eType == PDFWriter::Note ||
13083 0 : eType == PDFWriter::Reference ||
13084 0 : eType == PDFWriter::BibEntry ||
13085 0 : eType == PDFWriter::Code ||
13086 : eType == PDFWriter::Link )
13087 : {
13088 0 : bInsert = true;
13089 : }
13090 0 : break;
13091 : case PDFWriter::RowSpan:
13092 : case PDFWriter::ColSpan:
13093 : // only for table cells
13094 0 : if( eType == PDFWriter::TableHeader ||
13095 : eType == PDFWriter::TableData )
13096 : {
13097 0 : bInsert = true;
13098 : }
13099 0 : break;
13100 : case PDFWriter::LinkAnnotation:
13101 0 : if( eType == PDFWriter::Link )
13102 0 : bInsert = true;
13103 0 : break;
13104 0 : default: break;
13105 : }
13106 : }
13107 :
13108 0 : if( bInsert )
13109 0 : m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
13110 : #if OSL_DEBUG_LEVEL > 1
13111 : else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
13112 : fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
13113 : getAttributeTag( eAttr ),
13114 : (int)nValue,
13115 : getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
13116 : m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() );
13117 : #endif
13118 :
13119 0 : return bInsert;
13120 : }
13121 :
13122 0 : void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect )
13123 : {
13124 0 : sal_Int32 nPageNr = m_nCurrentPage;
13125 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
13126 0 : return;
13127 :
13128 0 : if( m_nCurrentStructElement > 0 && m_bEmitStructure )
13129 : {
13130 0 : PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
13131 0 : if( eType == PDFWriter::Figure ||
13132 0 : eType == PDFWriter::Formula ||
13133 0 : eType == PDFWriter::Form ||
13134 : eType == PDFWriter::Table )
13135 : {
13136 0 : m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
13137 : // convert to default user space now, since the mapmode may change
13138 0 : m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
13139 : }
13140 : }
13141 : }
13142 :
13143 0 : void PDFWriterImpl::setActualText( const OUString& rText )
13144 : {
13145 0 : if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
13146 : {
13147 0 : m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
13148 : }
13149 0 : }
13150 :
13151 0 : void PDFWriterImpl::setAlternateText( const OUString& rText )
13152 : {
13153 0 : if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
13154 : {
13155 0 : m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
13156 : }
13157 0 : }
13158 :
13159 0 : void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
13160 : {
13161 0 : if( nPageNr < 0 )
13162 0 : nPageNr = m_nCurrentPage;
13163 :
13164 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
13165 0 : return;
13166 :
13167 0 : m_aPages[ nPageNr ].m_nDuration = nSeconds;
13168 : }
13169 :
13170 0 : void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
13171 : {
13172 0 : if( nPageNr < 0 )
13173 0 : nPageNr = m_nCurrentPage;
13174 :
13175 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
13176 0 : return;
13177 :
13178 0 : m_aPages[ nPageNr ].m_eTransition = eType;
13179 0 : m_aPages[ nPageNr ].m_nTransTime = nMilliSec;
13180 : }
13181 :
13182 0 : void PDFWriterImpl::ensureUniqueRadioOnValues()
13183 : {
13184 : // loop over radio groups
13185 0 : for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
13186 0 : group != m_aRadioGroupWidgets.end(); ++group )
13187 : {
13188 0 : PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
13189 : // check whether all kids have a unique OnValue
13190 0 : std::unordered_map< OUString, sal_Int32, OUStringHash > aOnValues;
13191 0 : int nChildren = rGroupWidget.m_aKidsIndex.size();
13192 0 : bool bIsUnique = true;
13193 0 : for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
13194 : {
13195 0 : int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
13196 0 : const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
13197 : #if OSL_DEBUG_LEVEL > 1
13198 : fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() );
13199 : #endif
13200 0 : if( aOnValues.find( rVal ) == aOnValues.end() )
13201 : {
13202 0 : aOnValues[ rVal ] = 1;
13203 : }
13204 : else
13205 : {
13206 0 : bIsUnique = false;
13207 : }
13208 : }
13209 0 : if( ! bIsUnique )
13210 : {
13211 : #if OSL_DEBUG_LEVEL > 1
13212 : fprintf( stderr, "enforcing unique OnValues\n" );
13213 : #endif
13214 : // make unique by using ascending OnValues
13215 0 : for( int nKid = 0; nKid < nChildren; nKid++ )
13216 : {
13217 0 : int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
13218 0 : PDFWidget& rKid = m_aWidgets[nKidIndex];
13219 0 : rKid.m_aOnValue = OUString::number( nKid+1 );
13220 0 : if( rKid.m_aValue != "Off" )
13221 0 : rKid.m_aValue = rKid.m_aOnValue;
13222 : }
13223 : }
13224 : // finally move the "Yes" appearance to the OnValue appearance
13225 0 : for( int nKid = 0; nKid < nChildren; nKid++ )
13226 : {
13227 0 : int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
13228 0 : PDFWidget& rKid = m_aWidgets[nKidIndex];
13229 0 : PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
13230 0 : if( app_it != rKid.m_aAppearances.end() )
13231 : {
13232 0 : PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
13233 0 : if( stream_it != app_it->second.end() )
13234 : {
13235 0 : SvMemoryStream* pStream = stream_it->second;
13236 0 : app_it->second.erase( stream_it );
13237 0 : OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
13238 0 : appendName( rKid.m_aOnValue, aBuf );
13239 0 : (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
13240 : }
13241 : #if OSL_DEBUG_LEVEL > 1
13242 : else
13243 : fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" );
13244 : #endif
13245 : }
13246 : // update selected radio button
13247 0 : if( rKid.m_aValue != "Off" )
13248 : {
13249 0 : rGroupWidget.m_aValue = rKid.m_aValue;
13250 : }
13251 : }
13252 0 : }
13253 0 : }
13254 :
13255 0 : sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
13256 : {
13257 0 : sal_Int32 nRadioGroupWidget = -1;
13258 :
13259 0 : std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
13260 :
13261 0 : if( it == m_aRadioGroupWidgets.end() )
13262 : {
13263 0 : m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
13264 0 : sal_Int32(m_aWidgets.size());
13265 :
13266 : // new group, insert the radiobutton
13267 0 : m_aWidgets.push_back( PDFWidget() );
13268 0 : m_aWidgets.back().m_nObject = createObject();
13269 0 : m_aWidgets.back().m_nPage = m_nCurrentPage;
13270 0 : m_aWidgets.back().m_eType = PDFWriter::RadioButton;
13271 0 : m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
13272 0 : m_aWidgets.back().m_nFlags |= 0x0000C000; // NoToggleToOff and Radio bits
13273 :
13274 0 : createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn );
13275 : }
13276 : else
13277 0 : nRadioGroupWidget = it->second;
13278 :
13279 0 : return nRadioGroupWidget;
13280 : }
13281 :
13282 0 : sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
13283 : {
13284 0 : if( nPageNr < 0 )
13285 0 : nPageNr = m_nCurrentPage;
13286 :
13287 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
13288 0 : return -1;
13289 :
13290 0 : bool sigHidden(true);
13291 0 : sal_Int32 nNewWidget = m_aWidgets.size();
13292 0 : m_aWidgets.push_back( PDFWidget() );
13293 :
13294 0 : m_aWidgets.back().m_nObject = createObject();
13295 0 : m_aWidgets.back().m_aRect = rControl.Location;
13296 0 : m_aWidgets.back().m_nPage = nPageNr;
13297 0 : m_aWidgets.back().m_eType = rControl.getType();
13298 :
13299 0 : sal_Int32 nRadioGroupWidget = -1;
13300 : // for unknown reasons the radio buttons of a radio group must not have a
13301 : // field name, else the buttons are in fact check boxes -
13302 : // that is multiple buttons of the radio group can be selected
13303 0 : if( rControl.getType() == PDFWriter::RadioButton )
13304 0 : nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
13305 : else
13306 : {
13307 0 : createWidgetFieldName( nNewWidget, rControl );
13308 : }
13309 :
13310 : // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
13311 0 : PDFWidget& rNewWidget = m_aWidgets[nNewWidget];
13312 0 : rNewWidget.m_aDescription = rControl.Description;
13313 0 : rNewWidget.m_aText = rControl.Text;
13314 0 : rNewWidget.m_nTextStyle = rControl.TextStyle &
13315 : ( DrawTextFlags::Left | DrawTextFlags::Center | DrawTextFlags::Right | DrawTextFlags::Top |
13316 : DrawTextFlags::VCenter | DrawTextFlags::Bottom |
13317 0 : DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
13318 0 : rNewWidget.m_nTabOrder = rControl.TabOrder;
13319 :
13320 : // various properties are set via the flags (/Ff) property of the field dict
13321 0 : if( rControl.ReadOnly )
13322 0 : rNewWidget.m_nFlags |= 1;
13323 0 : if( rControl.getType() == PDFWriter::PushButton )
13324 : {
13325 0 : const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
13326 0 : if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13327 0 : rNewWidget.m_nTextStyle =
13328 : DrawTextFlags::Center | DrawTextFlags::VCenter |
13329 0 : DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
13330 :
13331 0 : rNewWidget.m_nFlags |= 0x00010000;
13332 0 : if( !rBtn.URL.isEmpty() )
13333 0 : rNewWidget.m_aListEntries.push_back( rBtn.URL );
13334 0 : rNewWidget.m_bSubmit = rBtn.Submit;
13335 0 : rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
13336 0 : rNewWidget.m_nDest = rBtn.Dest;
13337 0 : createDefaultPushButtonAppearance( rNewWidget, rBtn );
13338 : }
13339 0 : else if( rControl.getType() == PDFWriter::RadioButton )
13340 : {
13341 0 : const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
13342 0 : if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13343 0 : rNewWidget.m_nTextStyle =
13344 0 : DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
13345 : /* PDF sees a RadioButton group as one radio button with
13346 : * children which are in turn check boxes
13347 : *
13348 : * so we need to create a radio button on demand for a new group
13349 : * and insert a checkbox for each RadioButtonWidget as its child
13350 : */
13351 0 : rNewWidget.m_eType = PDFWriter::CheckBox;
13352 0 : rNewWidget.m_nRadioGroup = rBtn.RadioGroup;
13353 :
13354 : DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" );
13355 :
13356 0 : PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
13357 0 : rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
13358 0 : rRadioButton.m_aKidsIndex.push_back( nNewWidget );
13359 0 : rNewWidget.m_nParent = rRadioButton.m_nObject;
13360 :
13361 0 : rNewWidget.m_aValue = "Off";
13362 0 : rNewWidget.m_aOnValue = rBtn.OnValue;
13363 0 : if( rRadioButton.m_aValue.isEmpty() && rBtn.Selected )
13364 : {
13365 0 : rNewWidget.m_aValue = rNewWidget.m_aOnValue;
13366 0 : rRadioButton.m_aValue = rNewWidget.m_aOnValue;
13367 : }
13368 0 : createDefaultRadioButtonAppearance( rNewWidget, rBtn );
13369 :
13370 : // union rect of radio group
13371 0 : Rectangle aRect = rNewWidget.m_aRect;
13372 0 : m_aPages[ nPageNr ].convertRect( aRect );
13373 0 : rRadioButton.m_aRect.Union( aRect );
13374 : }
13375 0 : else if( rControl.getType() == PDFWriter::CheckBox )
13376 : {
13377 0 : const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
13378 0 : if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13379 0 : rNewWidget.m_nTextStyle =
13380 0 : DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
13381 :
13382 0 : rNewWidget.m_aValue = rBox.Checked ? OUString("Yes") : OUString("Off" );
13383 : // create default appearance before m_aRect gets transformed
13384 0 : createDefaultCheckBoxAppearance( rNewWidget, rBox );
13385 : }
13386 0 : else if( rControl.getType() == PDFWriter::ListBox )
13387 : {
13388 0 : if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13389 0 : rNewWidget.m_nTextStyle = DrawTextFlags::VCenter;
13390 :
13391 0 : const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
13392 0 : rNewWidget.m_aListEntries = rLstBox.Entries;
13393 0 : rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
13394 0 : rNewWidget.m_aValue = rLstBox.Text;
13395 0 : if( rLstBox.DropDown )
13396 0 : rNewWidget.m_nFlags |= 0x00020000;
13397 0 : if( rLstBox.Sort )
13398 0 : rNewWidget.m_nFlags |= 0x00080000;
13399 0 : if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 )
13400 0 : rNewWidget.m_nFlags |= 0x00200000;
13401 :
13402 0 : createDefaultListBoxAppearance( rNewWidget, rLstBox );
13403 : }
13404 0 : else if( rControl.getType() == PDFWriter::ComboBox )
13405 : {
13406 0 : if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13407 0 : rNewWidget.m_nTextStyle = DrawTextFlags::VCenter;
13408 :
13409 0 : const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
13410 0 : rNewWidget.m_aValue = rBox.Text;
13411 0 : rNewWidget.m_aListEntries = rBox.Entries;
13412 0 : rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
13413 0 : if( rBox.Sort )
13414 0 : rNewWidget.m_nFlags |= 0x00080000;
13415 :
13416 0 : PDFWriter::ListBoxWidget aLBox;
13417 0 : aLBox.Name = rBox.Name;
13418 0 : aLBox.Description = rBox.Description;
13419 0 : aLBox.Text = rBox.Text;
13420 0 : aLBox.TextStyle = rBox.TextStyle;
13421 0 : aLBox.ReadOnly = rBox.ReadOnly;
13422 0 : aLBox.Border = rBox.Border;
13423 0 : aLBox.BorderColor = rBox.BorderColor;
13424 0 : aLBox.Background = rBox.Background;
13425 0 : aLBox.BackgroundColor = rBox.BackgroundColor;
13426 0 : aLBox.TextFont = rBox.TextFont;
13427 0 : aLBox.TextColor = rBox.TextColor;
13428 0 : aLBox.DropDown = true;
13429 0 : aLBox.Sort = rBox.Sort;
13430 0 : aLBox.MultiSelect = false;
13431 0 : aLBox.Entries = rBox.Entries;
13432 :
13433 0 : createDefaultListBoxAppearance( rNewWidget, aLBox );
13434 : }
13435 0 : else if( rControl.getType() == PDFWriter::Edit )
13436 : {
13437 0 : if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE )
13438 0 : rNewWidget.m_nTextStyle = DrawTextFlags::Left | DrawTextFlags::VCenter;
13439 :
13440 0 : const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl);
13441 0 : if( rEdit.MultiLine )
13442 : {
13443 0 : rNewWidget.m_nFlags |= 0x00001000;
13444 0 : rNewWidget.m_nTextStyle |= DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
13445 : }
13446 0 : if( rEdit.Password )
13447 0 : rNewWidget.m_nFlags |= 0x00002000;
13448 0 : if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 )
13449 0 : rNewWidget.m_nFlags |= 0x00100000;
13450 0 : rNewWidget.m_nMaxLen = rEdit.MaxLen;
13451 0 : rNewWidget.m_aValue = rEdit.Text;
13452 :
13453 0 : createDefaultEditAppearance( rNewWidget, rEdit );
13454 : }
13455 : #if !defined(ANDROID) && !defined(IOS)
13456 0 : else if( rControl.getType() == PDFWriter::Signature)
13457 : {
13458 0 : const PDFWriter::SignatureWidget& rSig = static_cast<const PDFWriter::SignatureWidget&>(rControl);
13459 0 : sigHidden = rSig.SigHidden;
13460 :
13461 0 : if ( sigHidden )
13462 0 : rNewWidget.m_aRect = Rectangle(0, 0, 0, 0);
13463 :
13464 0 : m_nSignatureObject = createObject();
13465 0 : rNewWidget.m_aValue = OUString::number( m_nSignatureObject );
13466 0 : rNewWidget.m_aValue += " 0 R";
13467 : // let's add a fake appearance
13468 0 : rNewWidget.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
13469 : }
13470 : #endif
13471 :
13472 : // if control is a hidden signature, do not convert coordinates since we
13473 : // need /Rect [ 0 0 0 0 ]
13474 0 : if ( ! ( ( rControl.getType() == PDFWriter::Signature ) && ( sigHidden ) ) )
13475 : {
13476 : // convert to default user space now, since the mapmode may change
13477 : // note: create default appearances before m_aRect gets transformed
13478 0 : m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
13479 : }
13480 :
13481 : // insert widget to page's annotation list
13482 0 : m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
13483 :
13484 : // mark page as having widgets
13485 0 : m_aPages[ nPageNr ].m_bHasWidgets = true;
13486 :
13487 0 : return nNewWidget;
13488 : }
13489 :
13490 0 : void PDFWriterImpl::addStream( const OUString& rMimeType, PDFOutputStream* pStream, bool bCompress )
13491 : {
13492 0 : if( pStream )
13493 : {
13494 0 : m_aAdditionalStreams.push_back( PDFAddStream() );
13495 0 : PDFAddStream& rStream = m_aAdditionalStreams.back();
13496 0 : rStream.m_aMimeType = !rMimeType.isEmpty()
13497 : ? OUString( rMimeType )
13498 0 : : OUString( "application/octet-stream" );
13499 0 : rStream.m_pStream = pStream;
13500 0 : rStream.m_bCompress = bCompress;
13501 : }
13502 801 : }
13503 :
13504 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|