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/string.hxx>
38 : #include <cppuhelper/implbase1.hxx>
39 : #include <i18nlangtag/languagetag.hxx>
40 : #include <osl/file.h>
41 : #include <osl/thread.h>
42 : #include <rtl/crc.h>
43 : #include <rtl/digest.h>
44 : #include <rtl/ustrbuf.hxx>
45 : #include <tools/debug.hxx>
46 : #include <tools/stream.hxx>
47 : #include <tools/urlobj.hxx>
48 : #include <tools/zcodec.hxx>
49 : #include <vcl/bitmapex.hxx>
50 : #include <vcl/bmpacc.hxx>
51 : #include <vcl/cvtgrf.hxx>
52 : #include <vcl/image.hxx>
53 : #include <vcl/lineinfo.hxx>
54 : #include <vcl/metric.hxx>
55 : #include <vcl/settings.hxx>
56 : #include <vcl/strhelper.hxx>
57 : #include <vcl/svapp.hxx>
58 : #include <vcl/virdev.hxx>
59 :
60 : #include "fontsubset.hxx"
61 : #include "outdev.h"
62 : #include "PhysicalFontFace.hxx"
63 : #include "salgdi.hxx"
64 : #include "sallayout.hxx"
65 : #include "textlayout.hxx"
66 :
67 : #include "pdfwriter_impl.hxx"
68 :
69 : #if !defined(ANDROID) && !defined(IOS)
70 : // NSS header files for PDF signing support
71 : #include "nss.h"
72 : #include "cert.h"
73 : #include "hasht.h"
74 : #include "sechash.h"
75 : #include "cms.h"
76 : #include "cmst.h"
77 : #endif
78 :
79 : #include <config_eot.h>
80 :
81 : #if ENABLE_EOT
82 : #include <libeot/libeot.h>
83 : #endif
84 :
85 : using namespace vcl;
86 :
87 : #if (OSL_DEBUG_LEVEL < 3)
88 : #define COMPRESS_PAGES
89 : #else
90 : #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams
91 : #endif
92 :
93 : #if !defined(ANDROID) && !defined(IOS)
94 : #define MAX_SIGNATURE_CONTENT_LENGTH 0x4000
95 : #endif
96 :
97 : #ifdef DO_TEST_PDF
98 : class PDFTestOutputStream : public PDFOutputStream
99 : {
100 : public:
101 : virtual ~PDFTestOutputStream();
102 : virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream );
103 : };
104 :
105 : PDFTestOutputStream::~PDFTestOutputStream()
106 : {
107 : }
108 :
109 : void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream )
110 : {
111 : OString aStr( "lalala\ntest\ntest\ntest" );
112 : com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() );
113 : memcpy( aData.getArray(), aStr.getStr(), aStr.getLength() );
114 : xStream->writeBytes( aData );
115 : }
116 :
117 : // this test code cannot be used to test PDF/A-1 because it forces
118 : // control item (widgets) to bypass the structure controlling
119 : // the embedding of such elements in actual run
120 : void doTestCode()
121 : {
122 : static const char* pHome = getenv( "HOME" );
123 : OUString aTestFile( "file://" );
124 : aTestFile += OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 );
125 : aTestFile += "/pdf_export_test.pdf";
126 :
127 : PDFWriter::PDFWriterContext aContext;
128 : aContext.URL = aTestFile;
129 : aContext.Version = PDFWriter::PDF_1_4;
130 : aContext.Tagged = true;
131 : aContext.InitialPage = 2;
132 : aContext.DocumentInfo.Title = "PDF export test document";
133 : aContext.DocumentInfo.Producer = "VCL";
134 :
135 : aContext.SignPDF = true;
136 : aContext.SignLocation = "Burdur";
137 : aContext.SignReason = "Some valid reason to sign";
138 : aContext.SignContact = "signer@example.com";
139 :
140 : com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder > xEnc;
141 : PDFWriter aWriter( aContext, xEnc );
142 : aWriter.NewPage( 595, 842 );
143 : aWriter.BeginStructureElement( PDFWriter::Document );
144 : // set duration of 3 sec for first page
145 : aWriter.SetAutoAdvanceTime( 3 );
146 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
147 :
148 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
149 : aWriter.SetLineColor( Color( COL_LIGHTGREEN ) );
150 : aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 );
151 :
152 : aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
153 : aWriter.SetTextColor( Color( COL_BLACK ) );
154 : aWriter.SetLineColor( Color( COL_BLACK ) );
155 : aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
156 :
157 : Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) );
158 : aWriter.DrawRect( aRect );
159 : aWriter.DrawText( aRect, OUString( "Link annot 1" ) );
160 : sal_Int32 nFirstLink = aWriter.CreateLink( aRect );
161 : PDFNote aNote;
162 : aNote.Title = "A small test note";
163 : aNote.Contents = "There is no business like show business like no business i know. Everything about it is appealing.";
164 : aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote );
165 :
166 : Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) );
167 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
168 : aWriter.DrawRect( aTargetRect );
169 : aWriter.DrawText( aTargetRect, "Dest second link" );
170 : sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect );
171 :
172 : aWriter.BeginStructureElement( PDFWriter::Section );
173 : aWriter.BeginStructureElement( PDFWriter::Heading );
174 : aWriter.DrawText( Point(4500, 9000), "A small structure test" );
175 : aWriter.EndStructureElement();
176 : aWriter.BeginStructureElement( PDFWriter::Paragraph );
177 : aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
178 : aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline );
179 : aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ),
180 : "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.",
181 : TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
182 : );
183 : 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." );
184 : aWriter.SetAlternateText( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." );
185 : aWriter.EndStructureElement();
186 : aWriter.BeginStructureElement( PDFWriter::Paragraph );
187 : aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb );
188 : aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ),
189 : "This paragraph is nothing special either but ends on the next page structurewise",
190 : TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
191 : );
192 :
193 : aWriter.NewPage( 595, 842 );
194 : // test AddStream interface
195 : aWriter.AddStream( "text/plain", new PDFTestOutputStream(), true );
196 : // set transitional mode
197 : aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 );
198 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
199 : aWriter.SetTextColor( Color( COL_BLACK ) );
200 : aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
201 : aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ),
202 : "Here's where all things come to an end ... well at least the paragaph from the last page.",
203 : TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK
204 : );
205 : aWriter.EndStructureElement();
206 :
207 : aWriter.SetFillColor( Color( COL_LIGHTBLUE ) );
208 : // disable structure
209 : aWriter.BeginStructureElement( PDFWriter::NonStructElement );
210 : aWriter.DrawRect( aRect );
211 : aWriter.BeginStructureElement( PDFWriter::Paragraph );
212 : aWriter.DrawText( aRect, "Link annot 2" );
213 : sal_Int32 nSecondLink = aWriter.CreateLink( aRect );
214 :
215 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
216 : aWriter.BeginStructureElement( PDFWriter::ListItem );
217 : aWriter.DrawRect( aTargetRect );
218 : aWriter.DrawText( aTargetRect, "Dest first link" );
219 : sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect );
220 : // enable structure
221 : aWriter.EndStructureElement();
222 :
223 : aWriter.EndStructureElement();
224 : aWriter.EndStructureElement();
225 : aWriter.BeginStructureElement( PDFWriter::Figure );
226 : aWriter.BeginStructureElement( PDFWriter::Caption );
227 : aWriter.DrawText( Point( 4500, 9000 ), "Some drawing stuff inside the structure" );
228 : aWriter.EndStructureElement();
229 :
230 : // test clipping
231 : basegfx::B2DPolyPolygon aClip;
232 : basegfx::B2DPolygon aClipPoly;
233 : aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) );
234 : aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) );
235 : aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) );
236 : aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) );
237 : aClipPoly.setClosed( true );
238 : aClip.append( aClipPoly );
239 :
240 : aWriter.Push( PUSH_CLIPREGION | PUSH_FILLCOLOR );
241 : aWriter.SetClipRegion( aClip );
242 : aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
243 : aWriter.MoveClipRegion( 1000, 500 );
244 : aWriter.SetFillColor( Color( COL_RED ) );
245 : aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) );
246 : aWriter.Pop();
247 : // test transparency
248 : // draw background
249 : Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) );
250 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
251 : aWriter.DrawRect( aTranspRect );
252 : aWriter.BeginTransparencyGroup();
253 :
254 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
255 : aWriter.DrawEllipse( aTranspRect );
256 : aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
257 : aWriter.DrawText( aTranspRect,
258 : "Some transparent text",
259 : TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
260 :
261 : aWriter.EndTransparencyGroup( aTranspRect, 50 );
262 :
263 : // prepare an alpha mask
264 : Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) );
265 : BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess();
266 : for( int nX = 0; nX < 256; nX++ )
267 : for( int nY = 0; nY < 256; nY++ )
268 : pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) );
269 : aTransMask.ReleaseAccess( pAcc );
270 : aTransMask.SetPrefMapMode( MAP_MM );
271 : aTransMask.SetPrefSize( Size( 10, 10 ) );
272 :
273 : aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask );
274 :
275 : aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) );
276 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
277 : aWriter.DrawRect( aTranspRect );
278 : aWriter.SetFillColor( Color( COL_LIGHTGREEN ) );
279 : aWriter.DrawEllipse( aTranspRect );
280 : aWriter.SetTextColor( Color( COL_LIGHTBLUE ) );
281 : aWriter.DrawText( aTranspRect,
282 : "Some transparent text",
283 : TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
284 : aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) );
285 : aWriter.SetFillColor( Color( COL_LIGHTRED ) );
286 : aWriter.DrawRect( aTranspRect );
287 :
288 : Bitmap aImageBmp( Size( 256, 256 ), 24 );
289 : pAcc = aImageBmp.AcquireWriteAccess();
290 : pAcc->SetFillColor( Color( 0xff, 0, 0xff ) );
291 : pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) );
292 : aImageBmp.ReleaseAccess( pAcc );
293 : BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) );
294 : aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx );
295 :
296 : aWriter.EndStructureElement();
297 : aWriter.EndStructureElement();
298 :
299 : LineInfo aLI( LINE_DASH, 3 );
300 : aLI.SetDashCount( 2 );
301 : aLI.SetDashLen( 50 );
302 : aLI.SetDotCount( 2 );
303 : aLI.SetDotLen( 25 );
304 : aLI.SetDistance( 15 );
305 : Point aLIPoints[] = { Point( 4000, 10000 ),
306 : Point( 8000, 12000 ),
307 : Point( 3000, 19000 ) };
308 : Polygon aLIPoly( 3, aLIPoints );
309 : aWriter.SetLineColor( Color( COL_BLUE ) );
310 : aWriter.SetFillColor();
311 : aWriter.DrawPolyLine( aLIPoly, aLI );
312 :
313 : aLI.SetDashCount( 4 );
314 : aLIPoly.Move( 1000, 1000 );
315 : aWriter.DrawPolyLine( aLIPoly, aLI );
316 :
317 : aWriter.NewPage( 595, 842 );
318 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
319 : Wallpaper aWall( aTransMask );
320 : aWall.SetStyle( WALLPAPER_TILE );
321 : aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall );
322 :
323 : aWriter.NewPage( 595, 842 );
324 : aWriter.SetMapMode( MapMode( MAP_100TH_MM ) );
325 : aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) );
326 : aWriter.SetTextColor( Color( COL_BLACK ) );
327 : aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) );
328 : aWriter.DrawRect( aRect );
329 : aWriter.DrawText( aRect, "www.heise.de" );
330 : sal_Int32 nURILink = aWriter.CreateLink( aRect );
331 : aWriter.SetLinkURL( nURILink, OUString( "http://www.heise.de" ) );
332 :
333 : aWriter.SetLinkDest( nFirstLink, nFirstDest );
334 : aWriter.SetLinkDest( nSecondLink, nSecondDest );
335 :
336 : // include a button
337 : PDFWriter::PushButtonWidget aBtn;
338 : aBtn.Name = "testButton";
339 : aBtn.Description = "A test button";
340 : aBtn.Text = "hit me";
341 : aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) );
342 : aBtn.Border = aBtn.Background = true;
343 : aWriter.CreateControl( aBtn );
344 :
345 : // include a uri button
346 : PDFWriter::PushButtonWidget aUriBtn;
347 : aUriBtn.Name = "wwwButton";
348 : aUriBtn.Description = "A URI button";
349 : aUriBtn.Text = "to www";
350 : aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) );
351 : aUriBtn.Border = aUriBtn.Background = true;
352 : aUriBtn.URL = "http://www.heise.de";
353 : aWriter.CreateControl( aUriBtn );
354 :
355 : // include a dest button
356 : PDFWriter::PushButtonWidget aDstBtn;
357 : aDstBtn.Name = "destButton";
358 : aDstBtn.Description = "A Dest button";
359 : aDstBtn.Text = "to paragraph";
360 : aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) );
361 : aDstBtn.Border = aDstBtn.Background = true;
362 : aDstBtn.Dest = nFirstDest;
363 : aWriter.CreateControl( aDstBtn );
364 :
365 : PDFWriter::CheckBoxWidget aCBox;
366 : aCBox.Name = "textCheckBox";
367 : aCBox.Description = "A test check box";
368 : aCBox.Text = "check me";
369 : aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) );
370 : aCBox.Checked = true;
371 : aCBox.Border = aCBox.Background = false;
372 : aWriter.CreateControl( aCBox );
373 :
374 : PDFWriter::CheckBoxWidget aCBox2;
375 : aCBox2.Name = "textCheckBox2";
376 : aCBox2.Description = "Another test check box";
377 : aCBox2.Text = "check me right";
378 : aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) );
379 : aCBox2.Checked = true;
380 : aCBox2.Border = aCBox2.Background = false;
381 : aCBox2.ButtonIsLeft = false;
382 : aWriter.CreateControl( aCBox2 );
383 :
384 : PDFWriter::RadioButtonWidget aRB1;
385 : aRB1.Name = "rb1_1";
386 : aRB1.Description = "radio 1 button 1";
387 : aRB1.Text = "Despair";
388 : aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) );
389 : aRB1.Selected = true;
390 : aRB1.RadioGroup = 1;
391 : aRB1.Border = aRB1.Background = true;
392 : aRB1.ButtonIsLeft = false;
393 : aRB1.BorderColor = Color( COL_LIGHTGREEN );
394 : aRB1.BackgroundColor = Color( COL_LIGHTBLUE );
395 : aRB1.TextColor = Color( COL_LIGHTRED );
396 : aRB1.TextFont = Font( OUString( "Courier" ), Size( 0, 800 ) );
397 : aWriter.CreateControl( aRB1 );
398 :
399 : PDFWriter::RadioButtonWidget aRB2;
400 : aRB2.Name = "rb2_1";
401 : aRB2.Description = "radio 2 button 1";
402 : aRB2.Text = "Joy";
403 : aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) );
404 : aRB2.Selected = true;
405 : aRB2.RadioGroup = 2;
406 : aWriter.CreateControl( aRB2 );
407 :
408 : PDFWriter::RadioButtonWidget aRB3;
409 : aRB3.Name = "rb1_2";
410 : aRB3.Description = "radio 1 button 2";
411 : aRB3.Text = "Desperation";
412 : aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) );
413 : aRB3.Selected = true;
414 : aRB3.RadioGroup = 1;
415 : aWriter.CreateControl( aRB3 );
416 :
417 : PDFWriter::EditWidget aEditBox;
418 : aEditBox.Name = "testEdit";
419 : aEditBox.Description = "A test edit field";
420 : aEditBox.Text = "A little test text";
421 : aEditBox.TextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
422 : aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) );
423 : aEditBox.MaxLen = 100;
424 : aEditBox.Border = aEditBox.Background = true;
425 : aEditBox.BorderColor = Color( COL_BLACK );
426 : aWriter.CreateControl( aEditBox );
427 :
428 : // normal list box
429 : PDFWriter::ListBoxWidget aLstBox;
430 : aLstBox.Name = "testListBox";
431 : aLstBox.Text = "One";
432 : aLstBox.Description = "select me";
433 : aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) );
434 : aLstBox.Sort = true;
435 : aLstBox.MultiSelect = true;
436 : aLstBox.Border = aLstBox.Background = true;
437 : aLstBox.BorderColor = Color( COL_BLACK );
438 : aLstBox.Entries.push_back( OUString( "One" ) );
439 : aLstBox.Entries.push_back( OUString( "Two" ) );
440 : aLstBox.Entries.push_back( OUString( "Three" ) );
441 : aLstBox.Entries.push_back( OUString( "Four" ) );
442 : aLstBox.SelectedEntries.push_back( 1 );
443 : aLstBox.SelectedEntries.push_back( 2 );
444 : aWriter.CreateControl( aLstBox );
445 :
446 : // dropdown list box
447 : aLstBox.Name = "testDropDownListBox";
448 : aLstBox.DropDown = true;
449 : aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) );
450 : aWriter.CreateControl( aLstBox );
451 :
452 : // combo box
453 : PDFWriter::ComboBoxWidget aComboBox;
454 : aComboBox.Name = "testComboBox";
455 : aComboBox.Text = "test a combobox";
456 : aComboBox.Entries.push_back( OUString( "Larry" ) );
457 : aComboBox.Entries.push_back( OUString( "Curly" ) );
458 : aComboBox.Entries.push_back( OUString( "Moe" ) );
459 : aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) );
460 : aWriter.CreateControl( aComboBox );
461 :
462 : // test outlines
463 : sal_Int32 nPage1OL = aWriter.CreateOutlineItem();
464 : aWriter.SetOutlineItemText( nPage1OL, OUString( "Page 1" ) );
465 : aWriter.SetOutlineItemDest( nPage1OL, nSecondDest );
466 : aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2" ), nSecondDest );
467 : aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 revisited" ), nSecondDest );
468 : aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 again" ), nSecondDest );
469 : sal_Int32 nPage2OL = aWriter.CreateOutlineItem();
470 : aWriter.SetOutlineItemText( nPage2OL, OUString( "Page 2" ) );
471 : aWriter.CreateOutlineItem( nPage2OL, OUString( "Dest 1" ), nFirstDest );
472 :
473 : aWriter.EndStructureElement(); // close document
474 :
475 : aWriter.Emit();
476 : }
477 : #endif
478 :
479 : static const sal_Int32 nLog10Divisor = 1;
480 : static const double fDivisor = 10.0;
481 :
482 0 : static inline double pixelToPoint( double px ) { return px/fDivisor; }
483 0 : static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); }
484 :
485 : const sal_uInt8 PDFWriterImpl::s_nPadString[32] =
486 : {
487 : 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08,
488 : 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A
489 : };
490 :
491 0 : static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer )
492 : {
493 : static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
494 : '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
495 0 : rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] );
496 0 : rBuffer.append( pHexDigits[ nInt & 15 ] );
497 0 : }
498 :
499 0 : static void appendName( const OUString& rStr, OStringBuffer& rBuffer )
500 : {
501 : // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1
502 : // I guess than when reading the #xx sequence it will count for a single character.
503 0 : OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) );
504 0 : const sal_Char* pStr = aStr.getStr();
505 0 : int nLen = aStr.getLength();
506 0 : for( int i = 0; i < nLen; i++ )
507 : {
508 : /* #i16920# PDF recommendation: output UTF8, any byte
509 : * outside the interval [33(=ASCII'!');126(=ASCII'~')]
510 : * should be escaped hexadecimal
511 : * for the sake of ghostscript which also reads PDF
512 : * but has a narrower acceptance rate we only pass
513 : * alphanumerics and '-' literally.
514 : */
515 0 : if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) ||
516 0 : (pStr[i] >= 'a' && pStr[i] <= 'z' ) ||
517 0 : (pStr[i] >= '0' && pStr[i] <= '9' ) ||
518 0 : pStr[i] == '-' )
519 : {
520 0 : rBuffer.append( pStr[i] );
521 : }
522 : else
523 : {
524 0 : rBuffer.append( '#' );
525 0 : appendHex( (sal_Int8)pStr[i], rBuffer );
526 : }
527 0 : }
528 0 : }
529 :
530 0 : static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer )
531 : {
532 : //FIXME i59651 see above
533 0 : while( pStr && *pStr )
534 : {
535 0 : if( (*pStr >= 'A' && *pStr <= 'Z' ) ||
536 0 : (*pStr >= 'a' && *pStr <= 'z' ) ||
537 0 : (*pStr >= '0' && *pStr <= '9' ) ||
538 0 : *pStr == '-' )
539 : {
540 0 : rBuffer.append( *pStr );
541 : }
542 : else
543 : {
544 0 : rBuffer.append( '#' );
545 0 : appendHex( (sal_Int8)*pStr, rBuffer );
546 : }
547 0 : pStr++;
548 : }
549 0 : }
550 :
551 : //used only to emit encoded passwords
552 0 : static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer )
553 : {
554 0 : while( nLength )
555 : {
556 0 : switch( *pStr )
557 : {
558 : case '\n' :
559 0 : rBuffer.append( "\\n" );
560 0 : break;
561 : case '\r' :
562 0 : rBuffer.append( "\\r" );
563 0 : break;
564 : case '\t' :
565 0 : rBuffer.append( "\\t" );
566 0 : break;
567 : case '\b' :
568 0 : rBuffer.append( "\\b" );
569 0 : break;
570 : case '\f' :
571 0 : rBuffer.append( "\\f" );
572 0 : break;
573 : case '(' :
574 : case ')' :
575 : case '\\' :
576 0 : rBuffer.append( "\\" );
577 0 : rBuffer.append( (sal_Char) *pStr );
578 0 : break;
579 : default:
580 0 : rBuffer.append( (sal_Char) *pStr );
581 0 : break;
582 : }
583 0 : pStr++;
584 0 : nLength--;
585 : }
586 0 : }
587 :
588 : /**--->i56629
589 : * Convert a string before using it.
590 : *
591 : * This string conversion function is needed because the destination name
592 : * in a PDF file seen through an Internet browser should be
593 : * specially crafted, in order to be used directly by the browser.
594 : * In this way the fragment part of a hyperlink to a PDF file (e.g. something
595 : * as 'test1/test2/a-file.pdf\#thefragment) will be (hopefully) interpreted by the
596 : * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called
597 : * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf
598 : * and go to named destination thefragment using default zoom'.
599 : * The conversion is needed because in case of a fragment in the form: Slide%201
600 : * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201
601 : * using this conversion, in both the generated named destinations, fragment and GoToR
602 : * destination.
603 : *
604 : * The names for destinations are name objects and so they don't need to be encrypted
605 : * even though they expose the content of PDF file (e.g. guessing the PDF content from the
606 : * destination name).
607 : *
608 : * Fhurter limitation: it is advisable to use standard ASCII characters for
609 : * OOo bookmarks.
610 : */
611 0 : static void appendDestinationName( const OUString& rString, OStringBuffer& rBuffer )
612 : {
613 0 : const sal_Unicode* pStr = rString.getStr();
614 0 : sal_Int32 nLen = rString.getLength();
615 0 : for( int i = 0; i < nLen; i++ )
616 : {
617 0 : sal_Unicode aChar = pStr[i];
618 0 : if( (aChar >= '0' && aChar <= '9' ) ||
619 0 : (aChar >= 'a' && aChar <= 'z' ) ||
620 0 : (aChar >= 'A' && aChar <= 'Z' ) ||
621 : aChar == '-' )
622 : {
623 0 : rBuffer.append((sal_Char)aChar);
624 : }
625 : else
626 : {
627 0 : sal_Int8 aValueHigh = sal_Int8(aChar >> 8);
628 0 : if(aValueHigh > 0)
629 0 : appendHex( aValueHigh, rBuffer );
630 0 : appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
631 : }
632 : }
633 0 : }
634 : //<--- i56629
635 :
636 0 : static void appendUnicodeTextString( const OUString& rString, OStringBuffer& rBuffer )
637 : {
638 0 : rBuffer.append( "FEFF" );
639 0 : const sal_Unicode* pStr = rString.getStr();
640 0 : sal_Int32 nLen = rString.getLength();
641 0 : for( int i = 0; i < nLen; i++ )
642 : {
643 0 : sal_Unicode aChar = pStr[i];
644 0 : appendHex( (sal_Int8)(aChar >> 8), rBuffer );
645 0 : appendHex( (sal_Int8)(aChar & 255 ), rBuffer );
646 : }
647 0 : }
648 :
649 0 : void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl )
650 : {
651 : /* #i80258# previously we use appendName here
652 : however we need a slightly different coding scheme than the normal
653 : name encoding for field names
654 : */
655 0 : const OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ? i_rControl.Name : i_rControl.Text;
656 0 : OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) );
657 0 : const sal_Char* pStr = aStr.getStr();
658 0 : int nLen = aStr.getLength();
659 :
660 0 : OStringBuffer aBuffer( rName.getLength()+64 );
661 0 : for( int i = 0; i < nLen; i++ )
662 : {
663 : /* #i16920# PDF recommendation: output UTF8, any byte
664 : * outside the interval [32(=ASCII' ');126(=ASCII'~')]
665 : * should be escaped hexadecimal
666 : */
667 0 : if( (pStr[i] >= 32 && pStr[i] <= 126 ) )
668 0 : aBuffer.append( pStr[i] );
669 : else
670 : {
671 0 : aBuffer.append( '#' );
672 0 : appendHex( (sal_Int8)pStr[i], aBuffer );
673 : }
674 : }
675 :
676 0 : OString aFullName( aBuffer.makeStringAndClear() );
677 :
678 : /* #i82785# create hierarchical fields down to the for each dot in i_rName */
679 0 : sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0;
680 0 : OString aPartialName;
681 0 : OString aDomain;
682 0 : do
683 : {
684 0 : nLastTokenIndex = nTokenIndex;
685 0 : aPartialName = aFullName.getToken( 0, '.', nTokenIndex );
686 0 : if( nTokenIndex != -1 )
687 : {
688 : // find or create a hierarchical field
689 : // first find the fully qualified name up to this field
690 0 : aDomain = aFullName.copy( 0, nTokenIndex-1 );
691 0 : boost::unordered_map< OString, sal_Int32, OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
692 0 : if( it == m_aFieldNameMap.end() )
693 : {
694 : // create new hierarchy field
695 0 : sal_Int32 nNewWidget = m_aWidgets.size();
696 0 : m_aWidgets.push_back( PDFWidget() );
697 0 : m_aWidgets[nNewWidget].m_nObject = createObject();
698 0 : m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy;
699 0 : m_aWidgets[nNewWidget].m_aName = aPartialName;
700 0 : m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
701 0 : m_aFieldNameMap[aDomain] = nNewWidget;
702 0 : m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject;
703 0 : if( nLastTokenIndex > 0 )
704 : {
705 : // this field is not a root field and
706 : // needs to be inserted to its parent
707 0 : OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) );
708 0 : it = m_aFieldNameMap.find( aParentDomain );
709 : OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" );
710 0 : if( it != m_aFieldNameMap.end() )
711 : {
712 : OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" );
713 0 : if( it->second < sal_Int32(m_aWidgets.size()) )
714 : {
715 0 : PDFWidget& rParentField( m_aWidgets[it->second] );
716 0 : rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject );
717 0 : rParentField.m_aKidsIndex.push_back( nNewWidget );
718 0 : m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject;
719 : }
720 0 : }
721 : }
722 : }
723 0 : else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy )
724 : {
725 : // this is invalid, someone tries to have a terminal field as parent
726 : // example: a button with the name foo.bar exists and
727 : // another button is named foo.bar.no
728 : // workaround: put the second terminal field as much up in the hierarchy as
729 : // necessary to have a non-terminal field as parent (or none at all)
730 : // since it->second already is terminal, we just need to use its parent
731 0 : aDomain = OString();
732 0 : aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
733 0 : if( nLastTokenIndex > 0 )
734 : {
735 0 : aDomain = aFullName.copy( 0, nLastTokenIndex-1 );
736 0 : OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() );
737 0 : aBuf.append( aDomain );
738 0 : aBuf.append( '.' );
739 0 : aBuf.append( aPartialName );
740 0 : aFullName = aBuf.makeStringAndClear();
741 : }
742 : else
743 0 : aFullName = aPartialName;
744 0 : break;
745 : }
746 : }
747 0 : } while( nTokenIndex != -1 );
748 :
749 : // insert widget into its hierarchy field
750 0 : if( !aDomain.isEmpty() )
751 : {
752 0 : boost::unordered_map< OString, sal_Int32, OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain );
753 0 : if( it != m_aFieldNameMap.end() )
754 : {
755 : OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" );
756 0 : if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) )
757 : {
758 0 : m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject;
759 0 : m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject);
760 0 : m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex );
761 : }
762 : }
763 : }
764 :
765 0 : if( aPartialName.isEmpty() )
766 : {
767 : // how funny, an empty field name
768 0 : if( i_rControl.getType() == PDFWriter::RadioButton )
769 : {
770 0 : aPartialName = "RadioGroup";
771 0 : aPartialName += OString::number( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup );
772 : }
773 : else
774 0 : aPartialName = OString( "Widget" );
775 : }
776 :
777 0 : if( ! m_aContext.AllowDuplicateFieldNames )
778 : {
779 0 : boost::unordered_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aFullName );
780 :
781 0 : if( it != m_aFieldNameMap.end() ) // not unique
782 : {
783 0 : boost::unordered_map< OString, sal_Int32, OStringHash >::const_iterator check_it;
784 0 : OString aTry;
785 0 : sal_Int32 nTry = 2;
786 0 : do
787 : {
788 0 : OStringBuffer aUnique( aFullName.getLength() + 16 );
789 0 : aUnique.append( aFullName );
790 0 : aUnique.append( '_' );
791 0 : aUnique.append( nTry++ );
792 0 : aTry = aUnique.makeStringAndClear();
793 0 : check_it = m_aFieldNameMap.find( aTry );
794 0 : } while( check_it != m_aFieldNameMap.end() );
795 0 : aFullName = aTry;
796 0 : m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
797 0 : aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 );
798 : }
799 : else
800 0 : m_aFieldNameMap[ aFullName ] = i_nWidgetIndex;
801 : }
802 :
803 : // finally
804 0 : m_aWidgets[i_nWidgetIndex].m_aName = aPartialName;
805 0 : }
806 :
807 0 : static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor )
808 : {
809 0 : if( nValue < 0 )
810 : {
811 0 : rBuffer.append( '-' );
812 0 : nValue = -nValue;
813 : }
814 0 : sal_Int32 nFactor = 1, nDiv = nPrecision;
815 0 : while( nDiv-- )
816 0 : nFactor *= 10;
817 :
818 0 : sal_Int32 nInt = nValue / nFactor;
819 0 : rBuffer.append( nInt );
820 0 : if( nFactor > 1 )
821 : {
822 0 : sal_Int32 nDecimal = nValue % nFactor;
823 0 : if( nDecimal )
824 : {
825 0 : rBuffer.append( '.' );
826 : // omit trailing zeros
827 0 : while( (nDecimal % 10) == 0 )
828 0 : nDecimal /= 10;
829 0 : rBuffer.append( nDecimal );
830 : }
831 : }
832 0 : }
833 :
834 : // appends a double. PDF does not accept exponential format, only fixed point
835 0 : static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 )
836 : {
837 0 : bool bNeg = false;
838 0 : if( fValue < 0.0 )
839 : {
840 0 : bNeg = true;
841 0 : fValue=-fValue;
842 : }
843 :
844 0 : sal_Int64 nInt = (sal_Int64)fValue;
845 0 : fValue -= (double)nInt;
846 : // optimizing hardware may lead to a value of 1.0 after the subtraction
847 0 : if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision )
848 : {
849 0 : nInt++;
850 0 : fValue = 0.0;
851 : }
852 0 : sal_Int64 nFrac = 0;
853 0 : if( fValue )
854 : {
855 0 : fValue *= pow( 10.0, (double)nPrecision );
856 0 : nFrac = (sal_Int64)fValue;
857 : }
858 0 : if( bNeg && ( nInt || nFrac ) )
859 0 : rBuffer.append( '-' );
860 0 : rBuffer.append( nInt );
861 0 : if( nFrac )
862 : {
863 : int i;
864 0 : rBuffer.append( '.' );
865 0 : sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5);
866 0 : for ( i = 0; ( i < nPrecision ) && nFrac; i++ )
867 : {
868 0 : sal_Int64 nNumb = nFrac / nBound;
869 0 : nFrac -= nNumb * nBound;
870 0 : rBuffer.append( nNumb );
871 0 : nBound /= 10;
872 : }
873 : }
874 0 : }
875 :
876 0 : static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey = false )
877 : {
878 :
879 0 : if( rColor != Color( COL_TRANSPARENT ) )
880 : {
881 0 : if( bConvertToGrey )
882 : {
883 0 : sal_uInt8 cByte = rColor.GetLuminance();
884 0 : appendDouble( (double)cByte / 255.0, rBuffer );
885 : }
886 : else
887 : {
888 0 : appendDouble( (double)rColor.GetRed() / 255.0, rBuffer );
889 0 : rBuffer.append( ' ' );
890 0 : appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer );
891 0 : rBuffer.append( ' ' );
892 0 : appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer );
893 : }
894 : }
895 0 : }
896 :
897 0 : void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
898 : {
899 0 : if( rColor != Color( COL_TRANSPARENT ) )
900 : {
901 0 : bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
902 0 : appendColor( rColor, rBuffer, bGrey );
903 0 : rBuffer.append( bGrey ? " G" : " RG" );
904 : }
905 0 : }
906 :
907 0 : void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer )
908 : {
909 0 : if( rColor != Color( COL_TRANSPARENT ) )
910 : {
911 0 : bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale;
912 0 : appendColor( rColor, rBuffer, bGrey );
913 0 : rBuffer.append( bGrey ? " g" : " rg" );
914 : }
915 0 : }
916 :
917 : // matrix helper class
918 : // TODO: use basegfx matrix class instead or derive from it
919 : namespace vcl // TODO: use anonymous namespace to keep this class local
920 : {
921 : /* for sparse matrices of the form (2D linear transformations)
922 : * f[0] f[1] 0
923 : * f[2] f[3] 0
924 : * f[4] f[5] 1
925 : */
926 : class Matrix3
927 : {
928 : double f[6];
929 :
930 0 : void set( double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; }
931 : public:
932 : Matrix3();
933 0 : ~Matrix3() {}
934 :
935 : void skew( double alpha, double beta );
936 : void scale( double sx, double sy );
937 : void rotate( double angle );
938 : void translate( double tx, double ty );
939 : bool invert();
940 :
941 : void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL );
942 :
943 : Point transform( const Point& rPoint ) const;
944 : };
945 : }
946 :
947 0 : Matrix3::Matrix3()
948 : {
949 : // initialize to unity
950 0 : f[0] = 1.0;
951 0 : f[1] = 0.0;
952 0 : f[2] = 0.0;
953 0 : f[3] = 1.0;
954 0 : f[4] = 0.0;
955 0 : f[5] = 0.0;
956 0 : }
957 :
958 0 : Point Matrix3::transform( const Point& rOrig ) const
959 : {
960 0 : double x = (double)rOrig.X(), y = (double)rOrig.Y();
961 0 : return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) );
962 : }
963 :
964 0 : void Matrix3::skew( double alpha, double beta )
965 : {
966 : double fn[6];
967 0 : double tb = tan( beta );
968 0 : fn[0] = f[0] + f[2]*tb;
969 0 : fn[1] = f[1];
970 0 : fn[2] = f[2] + f[3]*tb;
971 0 : fn[3] = f[3];
972 0 : fn[4] = f[4] + f[5]*tb;
973 0 : fn[5] = f[5];
974 0 : if( alpha != 0.0 )
975 : {
976 0 : double ta = tan( alpha );
977 0 : fn[1] += f[0]*ta;
978 0 : fn[3] += f[2]*ta;
979 0 : fn[5] += f[4]*ta;
980 : }
981 0 : set( fn );
982 0 : }
983 :
984 0 : void Matrix3::scale( double sx, double sy )
985 : {
986 : double fn[6];
987 0 : fn[0] = sx*f[0];
988 0 : fn[1] = sy*f[1];
989 0 : fn[2] = sx*f[2];
990 0 : fn[3] = sy*f[3];
991 0 : fn[4] = sx*f[4];
992 0 : fn[5] = sy*f[5];
993 0 : set( fn );
994 0 : }
995 :
996 0 : void Matrix3::rotate( double angle )
997 : {
998 : double fn[6];
999 0 : double fSin = sin(angle);
1000 0 : double fCos = cos(angle);
1001 0 : fn[0] = f[0]*fCos - f[1]*fSin;
1002 0 : fn[1] = f[0]*fSin + f[1]*fCos;
1003 0 : fn[2] = f[2]*fCos - f[3]*fSin;
1004 0 : fn[3] = f[2]*fSin + f[3]*fCos;
1005 0 : fn[4] = f[4]*fCos - f[5]*fSin;
1006 0 : fn[5] = f[4]*fSin + f[5]*fCos;
1007 0 : set( fn );
1008 0 : }
1009 :
1010 0 : void Matrix3::translate( double tx, double ty )
1011 : {
1012 0 : f[4] += tx;
1013 0 : f[5] += ty;
1014 0 : }
1015 :
1016 0 : bool Matrix3::invert()
1017 : {
1018 : // short circuit trivial cases
1019 0 : if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 )
1020 : {
1021 0 : f[4] = -f[4];
1022 0 : f[5] = -f[5];
1023 0 : return true;
1024 : }
1025 :
1026 : // check determinant
1027 0 : const double fDet = f[0]*f[3]-f[1]*f[2];
1028 0 : if( fDet == 0.0 )
1029 0 : return false;
1030 :
1031 : // invert the matrix
1032 : double fn[6];
1033 0 : fn[0] = +f[3] / fDet;
1034 0 : fn[1] = -f[1] / fDet;
1035 0 : fn[2] = -f[2] / fDet;
1036 0 : fn[3] = +f[0] / fDet;
1037 :
1038 : // apply inversion to translation
1039 0 : fn[4] = -(f[4]*fn[0] + f[5]*fn[2]);
1040 0 : fn[5] = -(f[4]*fn[1] + f[5]*fn[3]);
1041 :
1042 0 : set( fn );
1043 0 : return true;
1044 : }
1045 :
1046 0 : void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack )
1047 : {
1048 0 : appendDouble( f[0], rBuffer );
1049 0 : rBuffer.append( ' ' );
1050 0 : appendDouble( f[1], rBuffer );
1051 0 : rBuffer.append( ' ' );
1052 0 : appendDouble( f[2], rBuffer );
1053 0 : rBuffer.append( ' ' );
1054 0 : appendDouble( f[3], rBuffer );
1055 0 : rBuffer.append( ' ' );
1056 0 : rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack );
1057 0 : }
1058 :
1059 0 : static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList )
1060 : {
1061 0 : if( rList.empty() )
1062 0 : return;
1063 0 : rBuf.append( '/' );
1064 0 : rBuf.append( pPrefix );
1065 0 : rBuf.append( "<<" );
1066 0 : int ni = 0;
1067 0 : for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it )
1068 : {
1069 0 : if( !it->first.isEmpty() && it->second > 0 )
1070 : {
1071 0 : rBuf.append( '/' );
1072 0 : rBuf.append( it->first );
1073 0 : rBuf.append( ' ' );
1074 0 : rBuf.append( it->second );
1075 0 : rBuf.append( " 0 R" );
1076 0 : if( ((++ni) & 7) == 0 )
1077 0 : rBuf.append( '\n' );
1078 : }
1079 : }
1080 0 : rBuf.append( ">>\n" );
1081 : }
1082 :
1083 0 : void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject )
1084 : {
1085 0 : rBuf.append( "<</Font " );
1086 0 : rBuf.append( nFontDictObject );
1087 0 : rBuf.append( " 0 R\n" );
1088 0 : appendResourceMap( rBuf, "XObject", m_aXObjects );
1089 0 : appendResourceMap( rBuf, "ExtGState", m_aExtGStates );
1090 0 : appendResourceMap( rBuf, "Shading", m_aShadings );
1091 0 : appendResourceMap( rBuf, "Pattern", m_aPatterns );
1092 0 : rBuf.append( "/ProcSet[/PDF/Text" );
1093 0 : if( !m_aXObjects.empty() )
1094 0 : rBuf.append( "/ImageC/ImageI/ImageB" );
1095 0 : rBuf.append( "]\n>>\n" );
1096 0 : };
1097 :
1098 0 : PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
1099 : :
1100 : m_pWriter( pWriter ),
1101 : m_nPageWidth( nPageWidth ),
1102 : m_nPageHeight( nPageHeight ),
1103 : m_eOrientation( eOrientation ),
1104 : m_nPageObject( 0 ), // invalid object number
1105 : m_nPageIndex( -1 ), // invalid index
1106 : m_nStreamLengthObject( 0 ),
1107 : m_nBeginStreamPos( 0 ),
1108 : m_eTransition( PDFWriter::Regular ),
1109 : m_nTransTime( 0 ),
1110 : m_nDuration( 0 ),
1111 0 : m_bHasWidgets( false )
1112 : {
1113 : // object ref must be only ever updated in emit()
1114 0 : m_nPageObject = m_pWriter->createObject();
1115 0 : }
1116 :
1117 0 : PDFWriterImpl::PDFPage::~PDFPage()
1118 : {
1119 0 : }
1120 :
1121 0 : void PDFWriterImpl::PDFPage::beginStream()
1122 : {
1123 : #if OSL_DEBUG_LEVEL > 1
1124 : {
1125 : OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" );
1126 : m_pWriter->emitComment( aLine.getStr() );
1127 : }
1128 : #endif
1129 0 : m_aStreamObjects.push_back(m_pWriter->createObject());
1130 0 : if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) )
1131 0 : return;
1132 :
1133 0 : m_nStreamLengthObject = m_pWriter->createObject();
1134 : // write content stream header
1135 0 : OStringBuffer aLine;
1136 0 : aLine.append( m_aStreamObjects.back() );
1137 0 : aLine.append( " 0 obj\n<</Length " );
1138 0 : aLine.append( m_nStreamLengthObject );
1139 0 : aLine.append( " 0 R" );
1140 : #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1141 0 : aLine.append( "/Filter/FlateDecode" );
1142 : #endif
1143 0 : aLine.append( ">>\nstream\n" );
1144 0 : if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) )
1145 0 : return;
1146 0 : if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &m_nBeginStreamPos ) )
1147 : {
1148 0 : osl_closeFile( m_pWriter->m_aFile );
1149 0 : m_pWriter->m_bOpen = false;
1150 : }
1151 : #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1152 0 : m_pWriter->beginCompression();
1153 : #endif
1154 0 : m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() );
1155 : }
1156 :
1157 0 : void PDFWriterImpl::PDFPage::endStream()
1158 : {
1159 : #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION )
1160 0 : m_pWriter->endCompression();
1161 : #endif
1162 : sal_uInt64 nEndStreamPos;
1163 0 : if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &nEndStreamPos ) )
1164 : {
1165 0 : osl_closeFile( m_pWriter->m_aFile );
1166 0 : m_pWriter->m_bOpen = false;
1167 0 : return;
1168 : }
1169 0 : m_pWriter->disableStreamEncryption();
1170 0 : if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
1171 0 : return;
1172 : // emit stream length object
1173 0 : if( ! m_pWriter->updateObject( m_nStreamLengthObject ) )
1174 0 : return;
1175 0 : OStringBuffer aLine;
1176 0 : aLine.append( m_nStreamLengthObject );
1177 0 : aLine.append( " 0 obj\n" );
1178 0 : aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) );
1179 0 : aLine.append( "\nendobj\n\n" );
1180 0 : m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1181 : }
1182 :
1183 0 : bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject )
1184 : {
1185 : // emit page object
1186 0 : if( ! m_pWriter->updateObject( m_nPageObject ) )
1187 0 : return false;
1188 0 : OStringBuffer aLine;
1189 :
1190 0 : aLine.append( m_nPageObject );
1191 : aLine.append( " 0 obj\n"
1192 0 : "<</Type/Page/Parent " );
1193 0 : aLine.append( nParentObject );
1194 0 : aLine.append( " 0 R" );
1195 0 : aLine.append( "/Resources " );
1196 0 : aLine.append( m_pWriter->getResourceDictObj() );
1197 0 : aLine.append( " 0 R" );
1198 0 : if( m_nPageWidth && m_nPageHeight )
1199 : {
1200 0 : aLine.append( "/MediaBox[0 0 " );
1201 0 : aLine.append( m_nPageWidth );
1202 0 : aLine.append( ' ' );
1203 0 : aLine.append( m_nPageHeight );
1204 0 : aLine.append( "]" );
1205 : }
1206 0 : switch( m_eOrientation )
1207 : {
1208 0 : case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
1209 0 : case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
1210 0 : case PDFWriter::Portrait: aLine.append( "/Rotate 0\n" );break;
1211 :
1212 : case PDFWriter::Inherit:
1213 : default:
1214 0 : break;
1215 : }
1216 0 : int nAnnots = m_aAnnotations.size();
1217 0 : if( nAnnots > 0 )
1218 : {
1219 0 : aLine.append( "/Annots[\n" );
1220 0 : for( int i = 0; i < nAnnots; i++ )
1221 : {
1222 0 : aLine.append( m_aAnnotations[i] );
1223 0 : aLine.append( " 0 R" );
1224 0 : aLine.append( ((i+1)%15) ? " " : "\n" );
1225 : }
1226 0 : aLine.append( "]\n" );
1227 : }
1228 0 : if( m_aMCIDParents.size() > 0 )
1229 : {
1230 0 : OStringBuffer aStructParents( 1024 );
1231 0 : aStructParents.append( "[ " );
1232 0 : int nParents = m_aMCIDParents.size();
1233 0 : for( int i = 0; i < nParents; i++ )
1234 : {
1235 0 : aStructParents.append( m_aMCIDParents[i] );
1236 0 : aStructParents.append( " 0 R" );
1237 0 : aStructParents.append( ((i%10) == 9) ? "\n" : " " );
1238 : }
1239 0 : aStructParents.append( "]" );
1240 0 : m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() );
1241 :
1242 0 : aLine.append( "/StructParents " );
1243 0 : aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) );
1244 0 : aLine.append( "\n" );
1245 : }
1246 0 : if( m_nDuration > 0 )
1247 : {
1248 0 : aLine.append( "/Dur " );
1249 0 : aLine.append( (sal_Int32)m_nDuration );
1250 0 : aLine.append( "\n" );
1251 : }
1252 0 : if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 )
1253 : {
1254 : // transition duration
1255 0 : aLine.append( "/Trans<</D " );
1256 0 : appendDouble( (double)m_nTransTime/1000.0, aLine, 3 );
1257 0 : aLine.append( "\n" );
1258 0 : const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL;
1259 0 : switch( m_eTransition )
1260 : {
1261 : case PDFWriter::SplitHorizontalInward:
1262 0 : pStyle = "Split"; pDm = "H"; pM = "I"; break;
1263 : case PDFWriter::SplitHorizontalOutward:
1264 0 : pStyle = "Split"; pDm = "H"; pM = "O"; break;
1265 : case PDFWriter::SplitVerticalInward:
1266 0 : pStyle = "Split"; pDm = "V"; pM = "I"; break;
1267 : case PDFWriter::SplitVerticalOutward:
1268 0 : pStyle = "Split"; pDm = "V"; pM = "O"; break;
1269 : case PDFWriter::BlindsHorizontal:
1270 0 : pStyle = "Blinds"; pDm = "H"; break;
1271 : case PDFWriter::BlindsVertical:
1272 0 : pStyle = "Blinds"; pDm = "V"; break;
1273 : case PDFWriter::BoxInward:
1274 0 : pStyle = "Box"; pM = "I"; break;
1275 : case PDFWriter::BoxOutward:
1276 0 : pStyle = "Box"; pM = "O"; break;
1277 : case PDFWriter::WipeLeftToRight:
1278 0 : pStyle = "Wipe"; pDi = "0"; break;
1279 : case PDFWriter::WipeBottomToTop:
1280 0 : pStyle = "Wipe"; pDi = "90"; break;
1281 : case PDFWriter::WipeRightToLeft:
1282 0 : pStyle = "Wipe"; pDi = "180"; break;
1283 : case PDFWriter::WipeTopToBottom:
1284 0 : pStyle = "Wipe"; pDi = "270"; break;
1285 : case PDFWriter::Dissolve:
1286 0 : pStyle = "Dissolve"; break;
1287 : case PDFWriter::GlitterLeftToRight:
1288 0 : pStyle = "Glitter"; pDi = "0"; break;
1289 : case PDFWriter::GlitterTopToBottom:
1290 0 : pStyle = "Glitter"; pDi = "270"; break;
1291 : case PDFWriter::GlitterTopLeftToBottomRight:
1292 0 : pStyle = "Glitter"; pDi = "315"; break;
1293 : case PDFWriter::Regular:
1294 0 : break;
1295 : }
1296 : // transition style
1297 0 : if( pStyle )
1298 : {
1299 0 : aLine.append( "/S/" );
1300 0 : aLine.append( pStyle );
1301 0 : aLine.append( "\n" );
1302 : }
1303 0 : if( pDm )
1304 : {
1305 0 : aLine.append( "/Dm/" );
1306 0 : aLine.append( pDm );
1307 0 : aLine.append( "\n" );
1308 : }
1309 0 : if( pM )
1310 : {
1311 0 : aLine.append( "/M/" );
1312 0 : aLine.append( pM );
1313 0 : aLine.append( "\n" );
1314 : }
1315 0 : if( pDi )
1316 : {
1317 0 : aLine.append( "/Di " );
1318 0 : aLine.append( pDi );
1319 0 : aLine.append( "\n" );
1320 : }
1321 0 : aLine.append( ">>\n" );
1322 : }
1323 0 : if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 )
1324 : {
1325 0 : aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" );
1326 : }
1327 0 : aLine.append( "/Contents" );
1328 0 : unsigned int nStreamObjects = m_aStreamObjects.size();
1329 0 : if( nStreamObjects > 1 )
1330 0 : aLine.append( '[' );
1331 0 : for( unsigned int i = 0; i < m_aStreamObjects.size(); i++ )
1332 : {
1333 0 : aLine.append( ' ' );
1334 0 : aLine.append( m_aStreamObjects[i] );
1335 0 : aLine.append( " 0 R" );
1336 : }
1337 0 : if( nStreamObjects > 1 )
1338 0 : aLine.append( ']' );
1339 0 : aLine.append( ">>\nendobj\n\n" );
1340 0 : return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() );
1341 : }
1342 :
1343 : namespace vcl
1344 : {
1345 : template < class GEOMETRY >
1346 0 : GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject )
1347 : {
1348 0 : GEOMETRY aPoint;
1349 0 : if ( MAP_PIXEL == _rSource.GetMapUnit() )
1350 : {
1351 0 : aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest );
1352 : }
1353 : else
1354 : {
1355 0 : aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest );
1356 : }
1357 0 : return aPoint;
1358 : }
1359 : }
1360 :
1361 0 : void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const
1362 : {
1363 0 : if( pOutPoint )
1364 : {
1365 0 : Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1366 : m_pWriter->m_aMapMode,
1367 : m_pWriter->getReferenceDevice(),
1368 0 : rPoint ) );
1369 0 : *pOutPoint = aPoint;
1370 : }
1371 :
1372 0 : Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1373 : m_pWriter->m_aMapMode,
1374 : m_pWriter->getReferenceDevice(),
1375 0 : rPoint ) );
1376 :
1377 0 : sal_Int32 nValue = aPoint.X();
1378 0 : if( bNeg )
1379 0 : nValue = -nValue;
1380 :
1381 0 : appendFixedInt( nValue, rBuffer );
1382 :
1383 0 : rBuffer.append( ' ' );
1384 :
1385 0 : nValue = pointToPixel(getHeight()) - aPoint.Y();
1386 0 : if( bNeg )
1387 0 : nValue = -nValue;
1388 :
1389 0 : appendFixedInt( nValue, rBuffer );
1390 0 : }
1391 :
1392 0 : void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const
1393 : {
1394 0 : double fValue = pixelToPoint(rPoint.getX());
1395 :
1396 0 : appendDouble( fValue, rBuffer, nLog10Divisor );
1397 0 : rBuffer.append( ' ' );
1398 0 : fValue = double(getHeight()) - pixelToPoint(rPoint.getY());
1399 0 : appendDouble( fValue, rBuffer, nLog10Divisor );
1400 0 : }
1401 :
1402 0 : void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const
1403 : {
1404 0 : appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer );
1405 0 : rBuffer.append( ' ' );
1406 0 : appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false );
1407 0 : rBuffer.append( ' ' );
1408 0 : appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true );
1409 0 : rBuffer.append( " re" );
1410 0 : }
1411 :
1412 0 : void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const
1413 : {
1414 0 : Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1415 : m_pWriter->m_aMapMode,
1416 : m_pWriter->getReferenceDevice(),
1417 0 : rRect.BottomLeft() + Point( 0, 1 )
1418 0 : );
1419 0 : Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1420 : m_pWriter->m_aMapMode,
1421 : m_pWriter->getReferenceDevice(),
1422 0 : rRect.GetSize() );
1423 0 : rRect.Left() = aLL.X();
1424 0 : rRect.Right() = aLL.X() + aSize.Width();
1425 0 : rRect.Top() = pointToPixel(getHeight()) - aLL.Y();
1426 0 : rRect.Bottom() = rRect.Top() + aSize.Height();
1427 0 : }
1428 :
1429 0 : void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1430 : {
1431 0 : sal_uInt16 nPoints = rPoly.GetSize();
1432 : /*
1433 : * #108582# applications do weird things
1434 : */
1435 0 : sal_uInt32 nBufLen = rBuffer.getLength();
1436 0 : if( nPoints > 0 )
1437 : {
1438 0 : const sal_uInt8* pFlagArray = rPoly.GetConstFlagAry();
1439 0 : appendPoint( rPoly[0], rBuffer );
1440 0 : rBuffer.append( " m\n" );
1441 0 : for( sal_uInt16 i = 1; i < nPoints; i++ )
1442 : {
1443 0 : if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 )
1444 : {
1445 : // bezier
1446 : DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" );
1447 0 : appendPoint( rPoly[i], rBuffer );
1448 0 : rBuffer.append( " " );
1449 0 : appendPoint( rPoly[i+1], rBuffer );
1450 0 : rBuffer.append( " " );
1451 0 : appendPoint( rPoly[i+2], rBuffer );
1452 0 : rBuffer.append( " c" );
1453 0 : i += 2; // add additionally consumed points
1454 : }
1455 : else
1456 : {
1457 : // line
1458 0 : appendPoint( rPoly[i], rBuffer );
1459 0 : rBuffer.append( " l" );
1460 : }
1461 0 : if( (rBuffer.getLength() - nBufLen) > 65 )
1462 : {
1463 0 : rBuffer.append( "\n" );
1464 0 : nBufLen = rBuffer.getLength();
1465 : }
1466 : else
1467 0 : rBuffer.append( " " );
1468 : }
1469 0 : if( bClose )
1470 0 : rBuffer.append( "h\n" );
1471 : }
1472 0 : }
1473 :
1474 0 : void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const
1475 : {
1476 0 : basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1477 : m_pWriter->m_aMapMode,
1478 : m_pWriter->getReferenceDevice(),
1479 0 : rPoly ) );
1480 :
1481 0 : if( basegfx::tools::isRectangle( aPoly ) )
1482 : {
1483 0 : basegfx::B2DRange aRange( aPoly.getB2DRange() );
1484 0 : basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() );
1485 0 : appendPixelPoint( aBL, rBuffer );
1486 0 : rBuffer.append( ' ' );
1487 0 : appendMappedLength( aRange.getWidth(), rBuffer, false, NULL, nLog10Divisor );
1488 0 : rBuffer.append( ' ' );
1489 0 : appendMappedLength( aRange.getHeight(), rBuffer, true, NULL, nLog10Divisor );
1490 0 : rBuffer.append( " re\n" );
1491 0 : return;
1492 : }
1493 0 : sal_uInt32 nPoints = aPoly.count();
1494 0 : if( nPoints > 0 )
1495 : {
1496 0 : sal_uInt32 nBufLen = rBuffer.getLength();
1497 0 : basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) );
1498 0 : appendPixelPoint( aLastPoint, rBuffer );
1499 0 : rBuffer.append( " m\n" );
1500 0 : for( sal_uInt32 i = 1; i <= nPoints; i++ )
1501 : {
1502 0 : if( i != nPoints || aPoly.isClosed() )
1503 : {
1504 0 : sal_uInt32 nCurPoint = i % nPoints;
1505 0 : sal_uInt32 nLastPoint = i-1;
1506 0 : basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) );
1507 0 : if( aPoly.isNextControlPointUsed( nLastPoint ) &&
1508 0 : aPoly.isPrevControlPointUsed( nCurPoint ) )
1509 : {
1510 0 : appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1511 0 : rBuffer.append( ' ' );
1512 0 : appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1513 0 : rBuffer.append( ' ' );
1514 0 : appendPixelPoint( aPoint, rBuffer );
1515 0 : rBuffer.append( " c" );
1516 : }
1517 0 : else if( aPoly.isNextControlPointUsed( nLastPoint ) )
1518 : {
1519 0 : appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer );
1520 0 : rBuffer.append( ' ' );
1521 0 : appendPixelPoint( aPoint, rBuffer );
1522 0 : rBuffer.append( " y" );
1523 : }
1524 0 : else if( aPoly.isPrevControlPointUsed( nCurPoint ) )
1525 : {
1526 0 : appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer );
1527 0 : rBuffer.append( ' ' );
1528 0 : appendPixelPoint( aPoint, rBuffer );
1529 0 : rBuffer.append( " v" );
1530 : }
1531 : else
1532 : {
1533 0 : appendPixelPoint( aPoint, rBuffer );
1534 0 : rBuffer.append( " l" );
1535 : }
1536 0 : if( (rBuffer.getLength() - nBufLen) > 65 )
1537 : {
1538 0 : rBuffer.append( "\n" );
1539 0 : nBufLen = rBuffer.getLength();
1540 : }
1541 : else
1542 0 : rBuffer.append( " " );
1543 : }
1544 : }
1545 0 : if( bClose )
1546 0 : rBuffer.append( "h\n" );
1547 0 : }
1548 : }
1549 :
1550 0 : void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1551 : {
1552 0 : sal_uInt16 nPolygons = rPolyPoly.Count();
1553 0 : for( sal_uInt16 n = 0; n < nPolygons; n++ )
1554 0 : appendPolygon( rPolyPoly[n], rBuffer, bClose );
1555 0 : }
1556 :
1557 0 : void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const
1558 : {
1559 0 : sal_uInt32 nPolygons = rPolyPoly.count();
1560 0 : for( sal_uInt32 n = 0; n < nPolygons; n++ )
1561 0 : appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer, bClose );
1562 0 : }
1563 :
1564 0 : void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const
1565 : {
1566 0 : sal_Int32 nValue = nLength;
1567 0 : if ( nLength < 0 )
1568 : {
1569 0 : rBuffer.append( '-' );
1570 0 : nValue = -nLength;
1571 : }
1572 0 : Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1573 : m_pWriter->m_aMapMode,
1574 : m_pWriter->getReferenceDevice(),
1575 0 : Size( nValue, nValue ) ) );
1576 0 : nValue = bVertical ? aSize.Height() : aSize.Width();
1577 0 : if( pOutLength )
1578 0 : *pOutLength = ((nLength < 0 ) ? -nValue : nValue);
1579 :
1580 0 : appendFixedInt( nValue, rBuffer, 1 );
1581 0 : }
1582 :
1583 0 : void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength, sal_Int32 nPrecision ) const
1584 : {
1585 0 : Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode,
1586 : m_pWriter->m_aMapMode,
1587 : m_pWriter->getReferenceDevice(),
1588 0 : Size( 1000, 1000 ) ) );
1589 0 : if( pOutLength )
1590 0 : *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0);
1591 0 : fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0);
1592 0 : appendDouble( fLength, rBuffer, nPrecision );
1593 0 : }
1594 :
1595 0 : bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const
1596 : {
1597 0 : if(LINE_DASH == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen())
1598 : {
1599 : // dashed and non-degraded case, check for implementation limits of dash array
1600 : // in PDF reader apps (e.g. acroread)
1601 0 : if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10)
1602 : {
1603 0 : return false;
1604 : }
1605 : }
1606 :
1607 0 : if(basegfx::B2DLINEJOIN_NONE != rInfo.GetLineJoin())
1608 : {
1609 : // LineJoin used, ExtLineInfo required
1610 0 : return false;
1611 : }
1612 :
1613 0 : if(com::sun::star::drawing::LineCap_BUTT != rInfo.GetLineCap())
1614 : {
1615 : // LineCap used, ExtLineInfo required
1616 0 : return false;
1617 : }
1618 :
1619 0 : if( rInfo.GetStyle() == LINE_DASH )
1620 : {
1621 0 : rBuffer.append( "[ " );
1622 0 : if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case
1623 : {
1624 0 : appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1625 0 : rBuffer.append( ' ' );
1626 0 : appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1627 0 : rBuffer.append( ' ' );
1628 : }
1629 : else
1630 : {
1631 0 : for( int n = 0; n < rInfo.GetDashCount(); n++ )
1632 : {
1633 0 : appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer );
1634 0 : rBuffer.append( ' ' );
1635 0 : appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1636 0 : rBuffer.append( ' ' );
1637 : }
1638 0 : for( int m = 0; m < rInfo.GetDotCount(); m++ )
1639 : {
1640 0 : appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer );
1641 0 : rBuffer.append( ' ' );
1642 0 : appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer );
1643 0 : rBuffer.append( ' ' );
1644 : }
1645 : }
1646 0 : rBuffer.append( "] 0 d\n" );
1647 : }
1648 :
1649 0 : if( rInfo.GetWidth() > 1 )
1650 : {
1651 0 : appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer );
1652 0 : rBuffer.append( " w\n" );
1653 : }
1654 0 : else if( rInfo.GetWidth() == 0 )
1655 : {
1656 : // "pixel" line
1657 0 : appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->ImplGetDPIX()), rBuffer );
1658 0 : rBuffer.append( " w\n" );
1659 : }
1660 :
1661 0 : return true;
1662 : }
1663 :
1664 0 : void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const
1665 : {
1666 0 : if( nWidth <= 0 )
1667 0 : return;
1668 0 : if( nDelta < 1 )
1669 0 : nDelta = 1;
1670 :
1671 0 : rBuffer.append( "0 " );
1672 0 : appendMappedLength( nY, rBuffer, true );
1673 0 : rBuffer.append( " m\n" );
1674 0 : for( sal_Int32 n = 0; n < nWidth; )
1675 : {
1676 0 : n += nDelta;
1677 0 : appendMappedLength( n, rBuffer, false );
1678 0 : rBuffer.append( ' ' );
1679 0 : appendMappedLength( nDelta+nY, rBuffer, true );
1680 0 : rBuffer.append( ' ' );
1681 0 : n += nDelta;
1682 0 : appendMappedLength( n, rBuffer, false );
1683 0 : rBuffer.append( ' ' );
1684 0 : appendMappedLength( nY, rBuffer, true );
1685 0 : rBuffer.append( " v " );
1686 0 : if( n < nWidth )
1687 : {
1688 0 : n += nDelta;
1689 0 : appendMappedLength( n, rBuffer, false );
1690 0 : rBuffer.append( ' ' );
1691 0 : appendMappedLength( nY-nDelta, rBuffer, true );
1692 0 : rBuffer.append( ' ' );
1693 0 : n += nDelta;
1694 0 : appendMappedLength( n, rBuffer, false );
1695 0 : rBuffer.append( ' ' );
1696 0 : appendMappedLength( nY, rBuffer, true );
1697 0 : rBuffer.append( " v\n" );
1698 : }
1699 : }
1700 0 : rBuffer.append( "S\n" );
1701 : }
1702 :
1703 0 : PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext,
1704 : const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc,
1705 : PDFWriter& i_rOuterFace)
1706 : :
1707 : m_pReferenceDevice( NULL ),
1708 : m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ),
1709 : m_nCurrentStructElement( 0 ),
1710 : m_bEmitStructure( true ),
1711 : m_bNewMCID( false ),
1712 : m_nNextFID( 1 ),
1713 : m_nInheritedPageWidth( 595 ), // default A4
1714 : m_nInheritedPageHeight( 842 ), // default A4
1715 : m_eInheritedOrientation( PDFWriter::Portrait ),
1716 : m_nCurrentPage( -1 ),
1717 : m_nSignatureObject( -1 ),
1718 : m_nSignatureContentOffset( 0 ),
1719 : m_nSignatureLastByteRangeNoOffset( 0 ),
1720 : m_nResourceDict( -1 ),
1721 : m_nFontDictObject( -1 ),
1722 : m_pCodec( NULL ),
1723 0 : m_aDocDigest( rtl_digest_createMD5() ),
1724 : m_aCipher( (rtlCipher)NULL ),
1725 : m_aDigest( NULL ),
1726 : m_bEncryptThisStream( false ),
1727 : m_pEncryptionBuffer( NULL ),
1728 : m_nEncryptionBufferSize( 0 ),
1729 : m_bIsPDF_A1( false ),
1730 0 : m_rOuterFace( i_rOuterFace )
1731 : {
1732 : #ifdef DO_TEST_PDF
1733 : static bool bOnce = true;
1734 : if( bOnce )
1735 : {
1736 : bOnce = false;
1737 : doTestCode();
1738 : }
1739 : #endif
1740 0 : m_aContext = rContext;
1741 0 : m_aStructure.push_back( PDFStructureElement() );
1742 0 : m_aStructure[0].m_nOwnElement = 0;
1743 0 : m_aStructure[0].m_nParentElement = 0;
1744 :
1745 0 : Font aFont;
1746 0 : aFont.SetName( OUString( "Times" ) );
1747 0 : aFont.SetSize( Size( 0, 12 ) );
1748 :
1749 0 : GraphicsState aState;
1750 0 : aState.m_aMapMode = m_aMapMode;
1751 0 : aState.m_aFont = aFont;
1752 0 : m_aGraphicsStack.push_front( aState );
1753 :
1754 0 : oslFileError aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
1755 0 : if( aError != osl_File_E_None )
1756 : {
1757 0 : if( aError == osl_File_E_EXIST )
1758 : {
1759 0 : aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write );
1760 0 : if( aError == osl_File_E_None )
1761 0 : aError = osl_setFileSize( m_aFile, 0 );
1762 : }
1763 : }
1764 0 : if( aError != osl_File_E_None )
1765 0 : return;
1766 :
1767 0 : m_bOpen = true;
1768 :
1769 : // setup DocInfo
1770 0 : setupDocInfo();
1771 :
1772 : /* prepare the cypher engine, can be done in CTOR, free in DTOR */
1773 0 : m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream );
1774 0 : m_aDigest = rtl_digest_createMD5();
1775 :
1776 : /* the size of the Codec default maximum */
1777 0 : checkEncryptionBufferSize( 0x4000 );
1778 :
1779 0 : if( xEnc.is() )
1780 0 : prepareEncryption( xEnc );
1781 :
1782 0 : if( m_aContext.Encryption.Encrypt() )
1783 : {
1784 : // sanity check
1785 0 : if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE ||
1786 0 : m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE ||
1787 0 : m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH
1788 : )
1789 : {
1790 : // the field lengths are invalid ? This was not setup by initEncryption.
1791 : // do not encrypt after all
1792 0 : m_aContext.Encryption.OValue.clear();
1793 0 : m_aContext.Encryption.UValue.clear();
1794 : OSL_ENSURE( false, "encryption data failed sanity check, encryption disabled" );
1795 : }
1796 : else // setup key lengths
1797 0 : m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength );
1798 : }
1799 :
1800 : // write header
1801 0 : OStringBuffer aBuffer( 20 );
1802 0 : aBuffer.append( "%PDF-" );
1803 0 : switch( m_aContext.Version )
1804 : {
1805 0 : case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break;
1806 0 : case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break;
1807 : case PDFWriter::PDF_A_1:
1808 : default:
1809 0 : case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break;
1810 0 : case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break;
1811 : }
1812 : // append something binary as comment (suggested in PDF Reference)
1813 0 : aBuffer.append( "\n%\303\244\303\274\303\266\303\237\n" );
1814 0 : if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) )
1815 : {
1816 0 : osl_closeFile( m_aFile );
1817 0 : m_bOpen = false;
1818 0 : return;
1819 : }
1820 :
1821 : // insert outline root
1822 0 : m_aOutline.push_back( PDFOutlineEntry() );
1823 :
1824 0 : m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1);
1825 0 : if( m_bIsPDF_A1 )
1826 0 : m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour
1827 : }
1828 :
1829 0 : PDFWriterImpl::~PDFWriterImpl()
1830 : {
1831 0 : if( m_aDocDigest )
1832 0 : rtl_digest_destroyMD5( m_aDocDigest );
1833 0 : delete static_cast<VirtualDevice*>(m_pReferenceDevice);
1834 :
1835 0 : if( m_aCipher )
1836 0 : rtl_cipher_destroyARCFOUR( m_aCipher );
1837 0 : if( m_aDigest )
1838 0 : rtl_digest_destroyMD5( m_aDigest );
1839 :
1840 0 : rtl_freeMemory( m_pEncryptionBuffer );
1841 0 : }
1842 :
1843 0 : void PDFWriterImpl::setupDocInfo()
1844 : {
1845 0 : std::vector< sal_uInt8 > aId;
1846 0 : computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString );
1847 0 : if( m_aContext.Encryption.DocumentIdentifier.empty() )
1848 0 : m_aContext.Encryption.DocumentIdentifier = aId;
1849 0 : }
1850 :
1851 0 : void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier,
1852 : const vcl::PDFWriter::PDFDocInfo& i_rDocInfo,
1853 : OString& o_rCString1,
1854 : OString& o_rCString2
1855 : )
1856 : {
1857 0 : o_rIdentifier.clear();
1858 :
1859 : //build the document id
1860 0 : OString aInfoValuesOut;
1861 0 : OStringBuffer aID( 1024 );
1862 0 : if( !i_rDocInfo.Title.isEmpty() )
1863 0 : appendUnicodeTextString( i_rDocInfo.Title, aID );
1864 0 : if( !i_rDocInfo.Author.isEmpty() )
1865 0 : appendUnicodeTextString( i_rDocInfo.Author, aID );
1866 0 : if( !i_rDocInfo.Subject.isEmpty() )
1867 0 : appendUnicodeTextString( i_rDocInfo.Subject, aID );
1868 0 : if( !i_rDocInfo.Keywords.isEmpty() )
1869 0 : appendUnicodeTextString( i_rDocInfo.Keywords, aID );
1870 0 : if( !i_rDocInfo.Creator.isEmpty() )
1871 0 : appendUnicodeTextString( i_rDocInfo.Creator, aID );
1872 0 : if( !i_rDocInfo.Producer.isEmpty() )
1873 0 : appendUnicodeTextString( i_rDocInfo.Producer, aID );
1874 :
1875 : TimeValue aTVal, aGMT;
1876 : oslDateTime aDT;
1877 0 : osl_getSystemTime( &aGMT );
1878 0 : osl_getLocalTimeFromSystemTime( &aGMT, &aTVal );
1879 0 : osl_getDateTimeFromTimeValue( &aTVal, &aDT );
1880 0 : OStringBuffer aCreationDateString(64), aCreationMetaDateString(64);
1881 0 : aCreationDateString.append( "D:" );
1882 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1883 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1884 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1885 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1886 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1887 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1888 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1889 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1890 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1891 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1892 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1893 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1894 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1895 0 : aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1896 :
1897 : //--> i59651, we fill the Metadata date string as well, if PDF/A is requested
1898 : // according to ISO 19005-1:2005 6.7.3 the date is corrected for
1899 : // local time zone offset UTC only, whereas Acrobat 8 seems
1900 : // to use the localtime notation only
1901 : // according to a recommendation in XMP Specification (Jan 2004, page 75)
1902 : // the Acrobat way seems the right approach
1903 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) );
1904 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) );
1905 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) );
1906 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) );
1907 0 : aCreationMetaDateString.append( "-" );
1908 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) );
1909 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) );
1910 0 : aCreationMetaDateString.append( "-" );
1911 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) );
1912 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) );
1913 0 : aCreationMetaDateString.append( "T" );
1914 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) );
1915 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) );
1916 0 : aCreationMetaDateString.append( ":" );
1917 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) );
1918 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) );
1919 0 : aCreationMetaDateString.append( ":" );
1920 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) );
1921 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) );
1922 :
1923 0 : sal_uInt32 nDelta = 0;
1924 0 : if( aGMT.Seconds > aTVal.Seconds )
1925 : {
1926 0 : aCreationDateString.append( "-" );
1927 0 : nDelta = aGMT.Seconds-aTVal.Seconds;
1928 0 : aCreationMetaDateString.append( "-" );
1929 : }
1930 0 : else if( aGMT.Seconds < aTVal.Seconds )
1931 : {
1932 0 : aCreationDateString.append( "+" );
1933 0 : nDelta = aTVal.Seconds-aGMT.Seconds;
1934 0 : aCreationMetaDateString.append( "+" );
1935 : }
1936 : else
1937 : {
1938 0 : aCreationDateString.append( "Z" );
1939 0 : aCreationMetaDateString.append( "Z" );
1940 :
1941 : }
1942 0 : if( nDelta )
1943 : {
1944 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1945 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1946 0 : aCreationDateString.append( "'" );
1947 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1948 0 : aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1949 :
1950 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) );
1951 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) );
1952 0 : aCreationMetaDateString.append( ":" );
1953 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) );
1954 0 : aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) );
1955 : }
1956 0 : aCreationDateString.append( "'" );
1957 0 : aID.append( aCreationDateString.getStr(), aCreationDateString.getLength() );
1958 :
1959 0 : aInfoValuesOut = aID.makeStringAndClear();
1960 0 : o_rCString1 = aCreationDateString.makeStringAndClear();
1961 0 : o_rCString2 = aCreationMetaDateString.makeStringAndClear();
1962 :
1963 0 : rtlDigest aDigest = rtl_digest_createMD5();
1964 : OSL_ENSURE( aDigest != NULL, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" );
1965 0 : if( aDigest )
1966 : {
1967 0 : rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) );
1968 0 : if( nError == rtl_Digest_E_None )
1969 0 : nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() );
1970 0 : if( nError == rtl_Digest_E_None )
1971 : {
1972 0 : o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 );
1973 : //the binary form of the doc id is needed for encryption stuff
1974 0 : rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 );
1975 : }
1976 0 : rtl_digest_destroyMD5(aDigest);
1977 0 : }
1978 0 : }
1979 :
1980 : /* i12626 methods */
1981 : /*
1982 : check if the Unicode string must be encrypted or not, perform the requested task,
1983 : append the string as unicode hex, encrypted if needed
1984 : */
1985 0 : inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
1986 : {
1987 0 : rOutBuffer.append( "<" );
1988 0 : if( m_aContext.Encryption.Encrypt() )
1989 : {
1990 0 : const sal_Unicode* pStr = rInString.getStr();
1991 0 : sal_Int32 nLen = rInString.getLength();
1992 : //prepare a unicode string, encrypt it
1993 0 : if( checkEncryptionBufferSize( nLen*2 ) )
1994 : {
1995 0 : enableStringEncryption( nInObjectNumber );
1996 0 : sal_uInt8 *pCopy = m_pEncryptionBuffer;
1997 0 : sal_Int32 nChars = 2;
1998 0 : *pCopy++ = 0xFE;
1999 0 : *pCopy++ = 0xFF;
2000 : // we need to prepare a byte stream from the unicode string buffer
2001 0 : for( int i = 0; i < nLen; i++ )
2002 : {
2003 0 : sal_Unicode aUnChar = pStr[i];
2004 0 : *pCopy++ = (sal_uInt8)( aUnChar >> 8 );
2005 0 : *pCopy++ = (sal_uInt8)( aUnChar & 255 );
2006 0 : nChars += 2;
2007 : }
2008 : //encrypt in place
2009 0 : rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars );
2010 : //now append, hexadecimal (appendHex), the encrypted result
2011 0 : for(int i = 0; i < nChars; i++)
2012 0 : appendHex( m_pEncryptionBuffer[i], rOutBuffer );
2013 : }
2014 : }
2015 : else
2016 0 : appendUnicodeTextString( rInString, rOutBuffer );
2017 0 : rOutBuffer.append( ">" );
2018 0 : }
2019 :
2020 0 : inline void PDFWriterImpl::appendLiteralStringEncrypt( OStringBuffer& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2021 : {
2022 0 : rOutBuffer.append( "(" );
2023 0 : sal_Int32 nChars = rInString.getLength();
2024 : //check for encryption, if ok, encrypt the string, then convert with appndLiteralString
2025 0 : if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) )
2026 : {
2027 : //encrypt the string in a buffer, then append it
2028 0 : enableStringEncryption( nInObjectNumber );
2029 0 : rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars );
2030 0 : appendLiteralString( (const sal_Char*)m_pEncryptionBuffer, nChars, rOutBuffer );
2031 : }
2032 : else
2033 0 : appendLiteralString( rInString.getStr(), nChars , rOutBuffer );
2034 0 : rOutBuffer.append( ")" );
2035 0 : }
2036 :
2037 0 : inline void PDFWriterImpl::appendLiteralStringEncrypt( const OString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer )
2038 : {
2039 0 : OStringBuffer aBufferString( rInString );
2040 0 : appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2041 0 : }
2042 :
2043 0 : void PDFWriterImpl::appendLiteralStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc )
2044 : {
2045 0 : OString aBufferString( OUStringToOString( rInString, nEnc ) );
2046 0 : sal_Int32 nLen = aBufferString.getLength();
2047 0 : OStringBuffer aBuf( nLen );
2048 0 : const sal_Char* pT = aBufferString.getStr();
2049 :
2050 0 : for( sal_Int32 i = 0; i < nLen; i++, pT++ )
2051 : {
2052 0 : if( (*pT & 0x80) == 0 )
2053 0 : aBuf.append( *pT );
2054 : else
2055 : {
2056 0 : aBuf.append( '<' );
2057 0 : appendHex( *pT, aBuf );
2058 0 : aBuf.append( '>' );
2059 : }
2060 : }
2061 0 : aBufferString = aBuf.makeStringAndClear();
2062 0 : appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer);
2063 0 : }
2064 :
2065 : /* end i12626 methods */
2066 :
2067 0 : void PDFWriterImpl::emitComment( const char* pComment )
2068 : {
2069 0 : OStringBuffer aLine( 64 );
2070 0 : aLine.append( "% " );
2071 0 : aLine.append( (const sal_Char*)pComment );
2072 0 : aLine.append( "\n" );
2073 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
2074 0 : }
2075 :
2076 0 : bool PDFWriterImpl::compressStream( SvMemoryStream* pStream )
2077 : {
2078 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2079 0 : pStream->Seek( STREAM_SEEK_TO_END );
2080 0 : sal_uLong nEndPos = pStream->Tell();
2081 0 : pStream->Seek( STREAM_SEEK_TO_BEGIN );
2082 0 : ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
2083 0 : SvMemoryStream aStream;
2084 0 : pCodec->BeginCompression();
2085 0 : pCodec->Write( aStream, (const sal_uInt8*)pStream->GetData(), nEndPos );
2086 0 : pCodec->EndCompression();
2087 0 : delete pCodec;
2088 0 : nEndPos = aStream.Tell();
2089 0 : pStream->Seek( STREAM_SEEK_TO_BEGIN );
2090 0 : aStream.Seek( STREAM_SEEK_TO_BEGIN );
2091 0 : pStream->SetStreamSize( nEndPos );
2092 0 : pStream->Write( aStream.GetData(), nEndPos );
2093 0 : return true;
2094 : #else
2095 : (void)pStream;
2096 : return false;
2097 : #endif
2098 : }
2099 :
2100 0 : void PDFWriterImpl::beginCompression()
2101 : {
2102 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2103 0 : m_pCodec = new ZCodec( 0x4000, 0x4000 );
2104 0 : m_pMemStream = new SvMemoryStream();
2105 0 : m_pCodec->BeginCompression();
2106 : #endif
2107 0 : }
2108 :
2109 0 : void PDFWriterImpl::endCompression()
2110 : {
2111 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
2112 0 : if( m_pCodec )
2113 : {
2114 0 : m_pCodec->EndCompression();
2115 0 : delete m_pCodec;
2116 0 : m_pCodec = NULL;
2117 0 : sal_uInt64 nLen = m_pMemStream->Tell();
2118 0 : m_pMemStream->Seek( 0 );
2119 0 : writeBuffer( m_pMemStream->GetData(), nLen );
2120 0 : delete m_pMemStream;
2121 0 : m_pMemStream = NULL;
2122 : }
2123 : #endif
2124 0 : }
2125 :
2126 0 : bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes )
2127 : {
2128 0 : if( ! m_bOpen ) // we are already down the drain
2129 0 : return false;
2130 :
2131 0 : if( ! nBytes ) // huh ?
2132 0 : return true;
2133 :
2134 0 : if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2135 : {
2136 0 : m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END );
2137 0 : m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) );
2138 0 : return true;
2139 : }
2140 :
2141 : sal_uInt64 nWritten;
2142 0 : if( m_pCodec )
2143 : {
2144 0 : m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes );
2145 0 : nWritten = nBytes;
2146 : }
2147 : else
2148 : {
2149 0 : bool buffOK = true;
2150 0 : if( m_bEncryptThisStream )
2151 : {
2152 : /* implement the encryption part of the PDF spec encryption algorithm 3.1 */
2153 0 : if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) )
2154 : rtl_cipher_encodeARCFOUR( m_aCipher,
2155 : (sal_uInt8*)pBuffer, static_cast<sal_Size>(nBytes),
2156 0 : m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) );
2157 : }
2158 :
2159 0 : const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer : pBuffer;
2160 0 : if( m_aDocDigest )
2161 0 : rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) );
2162 :
2163 0 : if( osl_writeFile( m_aFile,
2164 : pWriteBuffer,
2165 0 : nBytes, &nWritten ) != osl_File_E_None )
2166 0 : nWritten = 0;
2167 :
2168 0 : if( nWritten != nBytes )
2169 : {
2170 0 : osl_closeFile( m_aFile );
2171 0 : m_bOpen = false;
2172 : }
2173 : }
2174 :
2175 0 : return nWritten == nBytes;
2176 : }
2177 :
2178 0 : OutputDevice* PDFWriterImpl::getReferenceDevice()
2179 : {
2180 0 : if( ! m_pReferenceDevice )
2181 : {
2182 0 : VirtualDevice* pVDev = new VirtualDevice( 0 );
2183 :
2184 0 : m_pReferenceDevice = pVDev;
2185 :
2186 0 : if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 )
2187 0 : pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 );
2188 : else
2189 0 : pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy );
2190 :
2191 0 : pVDev->SetOutputSizePixel( Size( 640, 480 ) );
2192 0 : pVDev->SetMapMode( MAP_MM );
2193 :
2194 0 : m_pReferenceDevice->mpPDFWriter = this;
2195 0 : m_pReferenceDevice->ImplUpdateFontData( true );
2196 : }
2197 0 : return m_pReferenceDevice;
2198 : }
2199 :
2200 0 : class ImplPdfBuiltinFontData : public PhysicalFontFace
2201 : {
2202 : private:
2203 : const PDFWriterImpl::BuiltinFont& mrBuiltin;
2204 :
2205 : public:
2206 : enum {PDF_FONT_MAGIC = 0xBDFF0A1C };
2207 : ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& );
2208 0 : const PDFWriterImpl::BuiltinFont* GetBuiltinFont() const { return &mrBuiltin; }
2209 :
2210 0 : virtual PhysicalFontFace* Clone() const SAL_OVERRIDE { return new ImplPdfBuiltinFontData(*this); }
2211 : virtual ImplFontEntry* CreateFontInstance( FontSelectPattern& ) const SAL_OVERRIDE;
2212 0 : virtual sal_IntPtr GetFontId() const SAL_OVERRIDE { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); }
2213 : };
2214 :
2215 0 : inline const ImplPdfBuiltinFontData* GetPdfFontData( const PhysicalFontFace* pFontData )
2216 : {
2217 0 : const ImplPdfBuiltinFontData* pFD = NULL;
2218 0 : if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) )
2219 0 : pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData );
2220 0 : return pFD;
2221 : }
2222 :
2223 0 : static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin )
2224 : {
2225 0 : ImplDevFontAttributes aDFA;
2226 0 : aDFA.SetFamilyName( OUString::createFromAscii( rBuiltin.m_pName ) );
2227 0 : aDFA.SetStyleName( OUString::createFromAscii( rBuiltin.m_pStyleName ) );
2228 0 : aDFA.SetFamilyType( rBuiltin.m_eFamily );
2229 0 : aDFA.SetSymbolFlag( rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 );
2230 0 : aDFA.SetPitch( rBuiltin.m_ePitch );
2231 0 : aDFA.SetWeight( rBuiltin.m_eWeight );
2232 0 : aDFA.SetItalic( rBuiltin.m_eItalic );
2233 0 : aDFA.SetWidthType( rBuiltin.m_eWidthType );
2234 :
2235 0 : aDFA.mbOrientation = true;
2236 0 : aDFA.mbDevice = true;
2237 0 : aDFA.mnQuality = 50000;
2238 0 : aDFA.mbSubsettable = false;
2239 0 : aDFA.mbEmbeddable = false;
2240 0 : return aDFA;
2241 : }
2242 :
2243 0 : ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin )
2244 : : PhysicalFontFace( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ),
2245 0 : mrBuiltin( rBuiltin )
2246 0 : {}
2247 :
2248 0 : ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( FontSelectPattern& rFSD ) const
2249 : {
2250 0 : ImplFontEntry* pEntry = new ImplFontEntry( rFSD );
2251 0 : return pEntry;
2252 : }
2253 :
2254 : // - PDFWriterImpl -
2255 :
2256 0 : sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation )
2257 : {
2258 0 : endPage();
2259 0 : m_nCurrentPage = m_aPages.size();
2260 0 : m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) );
2261 0 : m_aPages.back().m_nPageIndex = m_nCurrentPage;
2262 0 : m_aPages.back().beginStream();
2263 :
2264 : // setup global graphics state
2265 : // linewidth is "1 pixel" by default
2266 0 : OStringBuffer aBuf( 16 );
2267 0 : appendDouble( 72.0/double(getReferenceDevice()->ImplGetDPIX()), aBuf );
2268 0 : aBuf.append( " w\n" );
2269 0 : writeBuffer( aBuf.getStr(), aBuf.getLength() );
2270 :
2271 0 : return m_nCurrentPage;
2272 : }
2273 :
2274 0 : void PDFWriterImpl::endPage()
2275 : {
2276 0 : if( m_aPages.begin() != m_aPages.end() )
2277 : {
2278 : // close eventual MC sequence
2279 0 : endStructureElementMCSeq();
2280 :
2281 : // sanity check
2282 0 : if( m_aOutputStreams.begin() != m_aOutputStreams.end() )
2283 : {
2284 : OSL_FAIL( "redirection across pages !!!" );
2285 0 : m_aOutputStreams.clear(); // leak !
2286 0 : m_aMapMode.SetOrigin( Point() );
2287 : }
2288 :
2289 0 : m_aGraphicsStack.clear();
2290 0 : m_aGraphicsStack.push_back( GraphicsState() );
2291 :
2292 : // this should pop the PDF graphics stack if necessary
2293 0 : updateGraphicsState();
2294 :
2295 0 : m_aPages.back().endStream();
2296 :
2297 : // reset the default font
2298 0 : Font aFont;
2299 0 : aFont.SetName( OUString( "Times" ) );
2300 0 : aFont.SetSize( Size( 0, 12 ) );
2301 :
2302 0 : m_aCurrentPDFState = m_aGraphicsStack.front();
2303 0 : m_aGraphicsStack.front().m_aFont = aFont;
2304 :
2305 0 : for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin();
2306 0 : it != m_aBitmaps.end(); ++it )
2307 : {
2308 0 : if( ! it->m_aBitmap.IsEmpty() )
2309 : {
2310 0 : writeBitmapObject( *it );
2311 0 : it->m_aBitmap = BitmapEx();
2312 : }
2313 : }
2314 0 : for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg )
2315 : {
2316 0 : if( jpeg->m_pStream )
2317 : {
2318 0 : writeJPG( *jpeg );
2319 0 : delete jpeg->m_pStream;
2320 0 : jpeg->m_pStream = NULL;
2321 0 : jpeg->m_aMask = Bitmap();
2322 : }
2323 : }
2324 0 : for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin();
2325 0 : t != m_aTransparentObjects.end(); ++t )
2326 : {
2327 0 : if( t->m_pContentStream )
2328 : {
2329 0 : writeTransparentObject( *t );
2330 0 : delete t->m_pContentStream;
2331 0 : t->m_pContentStream = NULL;
2332 : }
2333 0 : }
2334 : }
2335 0 : }
2336 :
2337 0 : sal_Int32 PDFWriterImpl::createObject()
2338 : {
2339 0 : m_aObjects.push_back( ~0U );
2340 0 : return m_aObjects.size();
2341 : }
2342 :
2343 0 : bool PDFWriterImpl::updateObject( sal_Int32 n )
2344 : {
2345 0 : if( ! m_bOpen )
2346 0 : return false;
2347 :
2348 0 : sal_uInt64 nOffset = ~0U;
2349 0 : oslFileError aError = osl_getFilePos( m_aFile, &nOffset );
2350 : DBG_ASSERT( aError == osl_File_E_None, "could not register object" );
2351 0 : if( aError != osl_File_E_None )
2352 : {
2353 0 : osl_closeFile( m_aFile );
2354 0 : m_bOpen = false;
2355 : }
2356 0 : m_aObjects[ n-1 ] = nOffset;
2357 0 : return aError == osl_File_E_None;
2358 : }
2359 :
2360 : #define CHECK_RETURN( x ) if( !(x) ) return 0
2361 :
2362 0 : sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject )
2363 : {
2364 0 : if( nObject > 0 )
2365 : {
2366 0 : OStringBuffer aLine( 1024 );
2367 :
2368 0 : aLine.append( nObject );
2369 : aLine.append( " 0 obj\n"
2370 0 : "<</Nums[\n" );
2371 0 : sal_Int32 nTreeItems = m_aStructParentTree.size();
2372 0 : for( sal_Int32 n = 0; n < nTreeItems; n++ )
2373 : {
2374 0 : aLine.append( n );
2375 0 : aLine.append( ' ' );
2376 0 : aLine.append( m_aStructParentTree[n] );
2377 0 : aLine.append( "\n" );
2378 : }
2379 0 : aLine.append( "]>>\nendobj\n\n" );
2380 0 : CHECK_RETURN( updateObject( nObject ) );
2381 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2382 : }
2383 0 : return nObject;
2384 : }
2385 :
2386 0 : const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr )
2387 : {
2388 0 : static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings;
2389 : // fill maps once
2390 0 : if( aAttributeStrings.empty() )
2391 : {
2392 0 : aAttributeStrings[ PDFWriter::Placement ] = "Placement";
2393 0 : aAttributeStrings[ PDFWriter::WritingMode ] = "WritingMode";
2394 0 : aAttributeStrings[ PDFWriter::SpaceBefore ] = "SpaceBefore";
2395 0 : aAttributeStrings[ PDFWriter::SpaceAfter ] = "SpaceAfter";
2396 0 : aAttributeStrings[ PDFWriter::StartIndent ] = "StartIndent";
2397 0 : aAttributeStrings[ PDFWriter::EndIndent ] = "EndIndent";
2398 0 : aAttributeStrings[ PDFWriter::TextIndent ] = "TextIndent";
2399 0 : aAttributeStrings[ PDFWriter::TextAlign ] = "TextAlign";
2400 0 : aAttributeStrings[ PDFWriter::Width ] = "Width";
2401 0 : aAttributeStrings[ PDFWriter::Height ] = "Height";
2402 0 : aAttributeStrings[ PDFWriter::BlockAlign ] = "BlockAlign";
2403 0 : aAttributeStrings[ PDFWriter::InlineAlign ] = "InlineAlign";
2404 0 : aAttributeStrings[ PDFWriter::LineHeight ] = "LineHeight";
2405 0 : aAttributeStrings[ PDFWriter::BaselineShift ] = "BaselineShift";
2406 0 : aAttributeStrings[ PDFWriter::TextDecorationType ] = "TextDecorationType";
2407 0 : aAttributeStrings[ PDFWriter::ListNumbering ] = "ListNumbering";
2408 0 : aAttributeStrings[ PDFWriter::RowSpan ] = "RowSpan";
2409 0 : aAttributeStrings[ PDFWriter::ColSpan ] = "ColSpan";
2410 0 : aAttributeStrings[ PDFWriter::LinkAnnotation ] = "LinkAnnotation";
2411 : }
2412 :
2413 : std::map< PDFWriter::StructAttribute, const char* >::const_iterator it =
2414 0 : aAttributeStrings.find( eAttr );
2415 :
2416 : #if OSL_DEBUG_LEVEL > 1
2417 : if( it == aAttributeStrings.end() )
2418 : fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr );
2419 : #endif
2420 :
2421 0 : return it != aAttributeStrings.end() ? it->second : "";
2422 : }
2423 :
2424 0 : const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal )
2425 : {
2426 0 : static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings;
2427 :
2428 0 : if( aValueStrings.empty() )
2429 : {
2430 0 : aValueStrings[ PDFWriter::NONE ] = "None";
2431 0 : aValueStrings[ PDFWriter::Block ] = "Block";
2432 0 : aValueStrings[ PDFWriter::Inline ] = "Inline";
2433 0 : aValueStrings[ PDFWriter::Before ] = "Before";
2434 0 : aValueStrings[ PDFWriter::After ] = "After";
2435 0 : aValueStrings[ PDFWriter::Start ] = "Start";
2436 0 : aValueStrings[ PDFWriter::End ] = "End";
2437 0 : aValueStrings[ PDFWriter::LrTb ] = "LrTb";
2438 0 : aValueStrings[ PDFWriter::RlTb ] = "RlTb";
2439 0 : aValueStrings[ PDFWriter::TbRl ] = "TbRl";
2440 0 : aValueStrings[ PDFWriter::Center ] = "Center";
2441 0 : aValueStrings[ PDFWriter::Justify ] = "Justify";
2442 0 : aValueStrings[ PDFWriter::Auto ] = "Auto";
2443 0 : aValueStrings[ PDFWriter::Middle ] = "Middle";
2444 0 : aValueStrings[ PDFWriter::Normal ] = "Normal";
2445 0 : aValueStrings[ PDFWriter::Underline ] = "Underline";
2446 0 : aValueStrings[ PDFWriter::Overline ] = "Overline";
2447 0 : aValueStrings[ PDFWriter::LineThrough ] = "LineThrough";
2448 0 : aValueStrings[ PDFWriter::Disc ] = "Disc";
2449 0 : aValueStrings[ PDFWriter::Circle ] = "Circle";
2450 0 : aValueStrings[ PDFWriter::Square ] = "Square";
2451 0 : aValueStrings[ PDFWriter::Decimal ] = "Decimal";
2452 0 : aValueStrings[ PDFWriter::UpperRoman ] = "UpperRoman";
2453 0 : aValueStrings[ PDFWriter::LowerRoman ] = "LowerRoman";
2454 0 : aValueStrings[ PDFWriter::UpperAlpha ] = "UpperAlpha";
2455 0 : aValueStrings[ PDFWriter::LowerAlpha ] = "LowerAlpha";
2456 : }
2457 :
2458 : std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it =
2459 0 : aValueStrings.find( eVal );
2460 :
2461 : #if OSL_DEBUG_LEVEL > 1
2462 : if( it == aValueStrings.end() )
2463 : fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal );
2464 : #endif
2465 :
2466 0 : return it != aValueStrings.end() ? it->second : "";
2467 : }
2468 :
2469 0 : static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt )
2470 : {
2471 0 : o_rLine.append( "/" );
2472 0 : o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) );
2473 :
2474 0 : if( i_rVal.eValue != PDFWriter::Invalid )
2475 : {
2476 0 : o_rLine.append( "/" );
2477 0 : o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) );
2478 : }
2479 : else
2480 : {
2481 : // numerical value
2482 0 : o_rLine.append( " " );
2483 0 : if( i_bIsFixedInt )
2484 0 : appendFixedInt( i_rVal.nValue, o_rLine );
2485 : else
2486 0 : o_rLine.append( i_rVal.nValue );
2487 : }
2488 0 : o_rLine.append( "\n" );
2489 0 : }
2490 :
2491 0 : OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle )
2492 : {
2493 : // create layout, list and table attribute sets
2494 0 : OStringBuffer aLayout(256), aList(64), aTable(64);
2495 0 : for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin();
2496 0 : it != i_rEle.m_aAttributes.end(); ++it )
2497 : {
2498 0 : if( it->first == PDFWriter::ListNumbering )
2499 0 : appendStructureAttributeLine( it->first, it->second, aList, true );
2500 0 : else if( it->first == PDFWriter::RowSpan ||
2501 0 : it->first == PDFWriter::ColSpan )
2502 0 : appendStructureAttributeLine( it->first, it->second, aTable, false );
2503 0 : else if( it->first == PDFWriter::LinkAnnotation )
2504 : {
2505 0 : sal_Int32 nLink = it->second.nValue;
2506 : std::map< sal_Int32, sal_Int32 >::const_iterator link_it =
2507 0 : m_aLinkPropertyMap.find( nLink );
2508 0 : if( link_it != m_aLinkPropertyMap.end() )
2509 0 : nLink = link_it->second;
2510 0 : if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() )
2511 : {
2512 : // update struct parent of link
2513 0 : OStringBuffer aStructParentEntry( 32 );
2514 0 : aStructParentEntry.append( i_rEle.m_nObject );
2515 0 : aStructParentEntry.append( " 0 R" );
2516 0 : m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() );
2517 0 : m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1;
2518 :
2519 0 : sal_Int32 nRefObject = createObject();
2520 0 : OStringBuffer aRef( 256 );
2521 0 : aRef.append( nRefObject );
2522 : aRef.append( " 0 obj\n"
2523 0 : "<</Type/OBJR/Obj " );
2524 0 : aRef.append( m_aLinks[ nLink ].m_nObject );
2525 : aRef.append( " 0 R>>\n"
2526 : "endobj\n\n"
2527 0 : );
2528 0 : updateObject( nRefObject );
2529 0 : writeBuffer( aRef.getStr(), aRef.getLength() );
2530 :
2531 0 : i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) );
2532 : }
2533 : else
2534 : {
2535 : OSL_FAIL( "unresolved link id for Link structure" );
2536 : #if OSL_DEBUG_LEVEL > 1
2537 : fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink );
2538 : {
2539 : OStringBuffer aLine( "unresolved link id " );
2540 : aLine.append( nLink );
2541 : aLine.append( " for Link structure" );
2542 : emitComment( aLine.getStr() );
2543 : }
2544 : #endif
2545 : }
2546 : }
2547 : else
2548 0 : appendStructureAttributeLine( it->first, it->second, aLayout, true );
2549 : }
2550 0 : if( ! i_rEle.m_aBBox.IsEmpty() )
2551 : {
2552 0 : aLayout.append( "/BBox[" );
2553 0 : appendFixedInt( i_rEle.m_aBBox.Left(), aLayout );
2554 0 : aLayout.append( " " );
2555 0 : appendFixedInt( i_rEle.m_aBBox.Top(), aLayout );
2556 0 : aLayout.append( " " );
2557 0 : appendFixedInt( i_rEle.m_aBBox.Right(), aLayout );
2558 0 : aLayout.append( " " );
2559 0 : appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout );
2560 0 : aLayout.append( "]\n" );
2561 : }
2562 :
2563 0 : std::vector< sal_Int32 > aAttribObjects;
2564 0 : if( !aLayout.isEmpty() )
2565 : {
2566 0 : aAttribObjects.push_back( createObject() );
2567 0 : updateObject( aAttribObjects.back() );
2568 0 : OStringBuffer aObj( 64 );
2569 0 : aObj.append( aAttribObjects.back() );
2570 : aObj.append( " 0 obj\n"
2571 0 : "<</O/Layout\n" );
2572 0 : aLayout.append( ">>\nendobj\n\n" );
2573 0 : writeBuffer( aObj.getStr(), aObj.getLength() );
2574 0 : writeBuffer( aLayout.getStr(), aLayout.getLength() );
2575 : }
2576 0 : if( !aList.isEmpty() )
2577 : {
2578 0 : aAttribObjects.push_back( createObject() );
2579 0 : updateObject( aAttribObjects.back() );
2580 0 : OStringBuffer aObj( 64 );
2581 0 : aObj.append( aAttribObjects.back() );
2582 : aObj.append( " 0 obj\n"
2583 0 : "<</O/List\n" );
2584 0 : aList.append( ">>\nendobj\n\n" );
2585 0 : writeBuffer( aObj.getStr(), aObj.getLength() );
2586 0 : writeBuffer( aList.getStr(), aList.getLength() );
2587 : }
2588 0 : if( !aTable.isEmpty() )
2589 : {
2590 0 : aAttribObjects.push_back( createObject() );
2591 0 : updateObject( aAttribObjects.back() );
2592 0 : OStringBuffer aObj( 64 );
2593 0 : aObj.append( aAttribObjects.back() );
2594 : aObj.append( " 0 obj\n"
2595 0 : "<</O/Table\n" );
2596 0 : aTable.append( ">>\nendobj\n\n" );
2597 0 : writeBuffer( aObj.getStr(), aObj.getLength() );
2598 0 : writeBuffer( aTable.getStr(), aTable.getLength() );
2599 : }
2600 :
2601 0 : OStringBuffer aRet( 64 );
2602 0 : if( aAttribObjects.size() > 1 )
2603 0 : aRet.append( " [" );
2604 0 : for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin();
2605 0 : at_it != aAttribObjects.end(); ++at_it )
2606 : {
2607 0 : aRet.append( " " );
2608 0 : aRet.append( *at_it );
2609 0 : aRet.append( " 0 R" );
2610 : }
2611 0 : if( aAttribObjects.size() > 1 )
2612 0 : aRet.append( " ]" );
2613 0 : return aRet.makeStringAndClear();
2614 : }
2615 :
2616 0 : sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle )
2617 : {
2618 0 : if(
2619 : // do not emit NonStruct and its children
2620 0 : rEle.m_eType == PDFWriter::NonStructElement &&
2621 0 : rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root
2622 : )
2623 0 : return 0;
2624 :
2625 0 : for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
2626 : {
2627 0 : if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
2628 : {
2629 0 : PDFStructureElement& rChild = m_aStructure[ *it ];
2630 0 : if( rChild.m_eType != PDFWriter::NonStructElement )
2631 : {
2632 0 : if( rChild.m_nParentElement == rEle.m_nOwnElement )
2633 0 : emitStructure( rChild );
2634 : else
2635 : {
2636 : OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure element" );
2637 : #if OSL_DEBUG_LEVEL > 1
2638 : fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
2639 : #endif
2640 : }
2641 : }
2642 : }
2643 : else
2644 : {
2645 : OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
2646 : #if OSL_DEBUG_LEVEL > 1
2647 : fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
2648 : #endif
2649 : }
2650 : }
2651 :
2652 0 : OStringBuffer aLine( 512 );
2653 0 : aLine.append( rEle.m_nObject );
2654 : aLine.append( " 0 obj\n"
2655 0 : "<</Type" );
2656 0 : sal_Int32 nParentTree = -1;
2657 0 : if( rEle.m_nOwnElement == rEle.m_nParentElement )
2658 : {
2659 0 : nParentTree = createObject();
2660 0 : CHECK_RETURN( nParentTree );
2661 0 : aLine.append( "/StructTreeRoot\n" );
2662 0 : aLine.append( "/ParentTree " );
2663 0 : aLine.append( nParentTree );
2664 0 : aLine.append( " 0 R\n" );
2665 0 : if( ! m_aRoleMap.empty() )
2666 : {
2667 0 : aLine.append( "/RoleMap<<" );
2668 0 : for( boost::unordered_map<OString,OString,OStringHash>::const_iterator
2669 0 : it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it )
2670 : {
2671 0 : aLine.append( '/' );
2672 0 : aLine.append(it->first);
2673 0 : aLine.append( '/' );
2674 0 : aLine.append( it->second );
2675 0 : aLine.append( '\n' );
2676 : }
2677 0 : aLine.append( ">>\n" );
2678 : }
2679 : }
2680 : else
2681 : {
2682 : aLine.append( "/StructElem\n"
2683 0 : "/S/" );
2684 0 : if( !rEle.m_aAlias.isEmpty() )
2685 0 : aLine.append( rEle.m_aAlias );
2686 : else
2687 0 : aLine.append( getStructureTag( rEle.m_eType ) );
2688 : aLine.append( "\n"
2689 0 : "/P " );
2690 0 : aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject );
2691 : aLine.append( " 0 R\n"
2692 0 : "/Pg " );
2693 0 : aLine.append( rEle.m_nFirstPageObject );
2694 0 : aLine.append( " 0 R\n" );
2695 0 : if( !rEle.m_aActualText.isEmpty() )
2696 : {
2697 0 : aLine.append( "/ActualText" );
2698 0 : appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine );
2699 0 : aLine.append( "\n" );
2700 : }
2701 0 : if( !rEle.m_aAltText.isEmpty() )
2702 : {
2703 0 : aLine.append( "/Alt" );
2704 0 : appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine );
2705 0 : aLine.append( "\n" );
2706 : }
2707 : }
2708 0 : if( ! rEle.m_aBBox.IsEmpty() || rEle.m_aAttributes.size() )
2709 : {
2710 0 : OString aAttribs = emitStructureAttributes( rEle );
2711 0 : if( !aAttribs.isEmpty() )
2712 : {
2713 0 : aLine.append( "/A" );
2714 0 : aLine.append( aAttribs );
2715 0 : aLine.append( "\n" );
2716 0 : }
2717 : }
2718 0 : if( !rEle.m_aLocale.Language.isEmpty() )
2719 : {
2720 : /* PDF allows only RFC 3066, which is only partly BCP 47 and does not
2721 : * include script tags and others.
2722 : * http://pdf.editme.com/pdfua-naturalLanguageSpecification
2723 : * http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf#page=886
2724 : * https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf#M13.9.19332.1Heading.97.Natural.Language.Specification
2725 : * */
2726 0 : LanguageTag aLanguageTag( rEle.m_aLocale);
2727 0 : OUString aLanguage, aScript, aCountry;
2728 0 : aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
2729 0 : if (!aLanguage.isEmpty())
2730 : {
2731 0 : OUStringBuffer aLocBuf( 16 );
2732 0 : aLocBuf.append( aLanguage );
2733 0 : if( !aCountry.isEmpty() )
2734 : {
2735 0 : aLocBuf.append( '-' );
2736 0 : aLocBuf.append( aCountry );
2737 : }
2738 0 : aLine.append( "/Lang" );
2739 0 : appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine );
2740 0 : aLine.append( "\n" );
2741 0 : }
2742 : }
2743 0 : if( ! rEle.m_aKids.empty() )
2744 : {
2745 0 : unsigned int i = 0;
2746 0 : aLine.append( "/K[" );
2747 0 : for( std::list< PDFStructureElementKid >::const_iterator it =
2748 0 : rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ )
2749 : {
2750 0 : if( it->nMCID == -1 )
2751 : {
2752 0 : aLine.append( it->nObject );
2753 0 : aLine.append( " 0 R" );
2754 0 : aLine.append( ( (i & 15) == 15 ) ? "\n" : " " );
2755 : }
2756 : else
2757 : {
2758 0 : if( it->nObject == rEle.m_nFirstPageObject )
2759 : {
2760 0 : aLine.append( it->nMCID );
2761 0 : aLine.append( " " );
2762 : }
2763 : else
2764 : {
2765 0 : aLine.append( "<</Type/MCR/Pg " );
2766 0 : aLine.append( it->nObject );
2767 0 : aLine.append( " 0 R /MCID " );
2768 0 : aLine.append( it->nMCID );
2769 0 : aLine.append( ">>\n" );
2770 : }
2771 : }
2772 : }
2773 0 : aLine.append( "]\n" );
2774 : }
2775 0 : aLine.append( ">>\nendobj\n\n" );
2776 :
2777 0 : CHECK_RETURN( updateObject( rEle.m_nObject ) );
2778 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2779 :
2780 0 : CHECK_RETURN( emitStructParentTree( nParentTree ) );
2781 :
2782 0 : return rEle.m_nObject;
2783 : }
2784 :
2785 0 : bool PDFWriterImpl::emitGradients()
2786 : {
2787 0 : for( std::list<GradientEmit>::iterator it = m_aGradients.begin();
2788 0 : it != m_aGradients.end(); ++it )
2789 : {
2790 0 : if ( !writeGradientFunction( *it ) ) return false;
2791 : }
2792 0 : return true;
2793 : }
2794 :
2795 0 : bool PDFWriterImpl::emitTilings()
2796 : {
2797 0 : OStringBuffer aTilingObj( 1024 );
2798 :
2799 0 : for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it )
2800 : {
2801 : DBG_ASSERT( it->m_pTilingStream, "tiling without stream" );
2802 0 : if( ! it->m_pTilingStream )
2803 0 : continue;
2804 :
2805 0 : aTilingObj.setLength( 0 );
2806 :
2807 : #if OSL_DEBUG_LEVEL > 1
2808 : emitComment( "PDFWriterImpl::emitTilings" );
2809 : #endif
2810 :
2811 0 : sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left();
2812 0 : sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top();
2813 0 : sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth();
2814 0 : sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight();
2815 0 : if( it->m_aCellSize.Width() == 0 )
2816 0 : it->m_aCellSize.Width() = nW;
2817 0 : if( it->m_aCellSize.Height() == 0 )
2818 0 : it->m_aCellSize.Height() = nH;
2819 :
2820 0 : bool bDeflate = compressStream( it->m_pTilingStream );
2821 0 : it->m_pTilingStream->Seek( STREAM_SEEK_TO_END );
2822 0 : sal_Size nTilingStreamSize = it->m_pTilingStream->Tell();
2823 0 : it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN );
2824 :
2825 : // write pattern object
2826 0 : aTilingObj.append( it->m_nObject );
2827 0 : aTilingObj.append( " 0 obj\n" );
2828 : aTilingObj.append( "<</Type/Pattern/PatternType 1\n"
2829 : "/PaintType 1\n"
2830 : "/TilingType 2\n"
2831 0 : "/BBox[" );
2832 0 : appendFixedInt( nX, aTilingObj );
2833 0 : aTilingObj.append( ' ' );
2834 0 : appendFixedInt( nY, aTilingObj );
2835 0 : aTilingObj.append( ' ' );
2836 0 : appendFixedInt( nX+nW, aTilingObj );
2837 0 : aTilingObj.append( ' ' );
2838 0 : appendFixedInt( nY+nH, aTilingObj );
2839 : aTilingObj.append( "]\n"
2840 0 : "/XStep " );
2841 0 : appendFixedInt( it->m_aCellSize.Width(), aTilingObj );
2842 : aTilingObj.append( "\n"
2843 0 : "/YStep " );
2844 0 : appendFixedInt( it->m_aCellSize.Height(), aTilingObj );
2845 0 : aTilingObj.append( "\n" );
2846 0 : if( it->m_aTransform.matrix[0] != 1.0 ||
2847 0 : it->m_aTransform.matrix[1] != 0.0 ||
2848 0 : it->m_aTransform.matrix[3] != 0.0 ||
2849 0 : it->m_aTransform.matrix[4] != 1.0 ||
2850 0 : it->m_aTransform.matrix[2] != 0.0 ||
2851 0 : it->m_aTransform.matrix[5] != 0.0 )
2852 : {
2853 0 : aTilingObj.append( "/Matrix [" );
2854 : // TODO: scaling, mirroring on y, etc
2855 0 : appendDouble( it->m_aTransform.matrix[0], aTilingObj );
2856 0 : aTilingObj.append( ' ' );
2857 0 : appendDouble( it->m_aTransform.matrix[1], aTilingObj );
2858 0 : aTilingObj.append( ' ' );
2859 0 : appendDouble( it->m_aTransform.matrix[3], aTilingObj );
2860 0 : aTilingObj.append( ' ' );
2861 0 : appendDouble( it->m_aTransform.matrix[4], aTilingObj );
2862 0 : aTilingObj.append( ' ' );
2863 0 : appendDouble( it->m_aTransform.matrix[2], aTilingObj );
2864 0 : aTilingObj.append( ' ' );
2865 0 : appendDouble( it->m_aTransform.matrix[5], aTilingObj );
2866 0 : aTilingObj.append( "]\n" );
2867 : }
2868 0 : aTilingObj.append( "/Resources" );
2869 0 : it->m_aResources.append( aTilingObj, getFontDictObject() );
2870 0 : if( bDeflate )
2871 0 : aTilingObj.append( "/Filter/FlateDecode" );
2872 0 : aTilingObj.append( "/Length " );
2873 0 : aTilingObj.append( (sal_Int32)nTilingStreamSize );
2874 0 : aTilingObj.append( ">>\nstream\n" );
2875 0 : if ( !updateObject( it->m_nObject ) ) return false;
2876 0 : if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false;
2877 0 : checkAndEnableStreamEncryption( it->m_nObject );
2878 0 : bool written = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize );
2879 0 : delete it->m_pTilingStream;
2880 0 : it->m_pTilingStream = NULL;
2881 0 : if( !written )
2882 0 : return false;
2883 0 : disableStreamEncryption();
2884 0 : aTilingObj.setLength( 0 );
2885 0 : aTilingObj.append( "\nendstream\nendobj\n\n" );
2886 0 : if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false;
2887 : }
2888 0 : return true;
2889 : }
2890 :
2891 0 : sal_Int32 PDFWriterImpl::emitBuiltinFont( const PhysicalFontFace* pFont, sal_Int32 nFontObject )
2892 : {
2893 0 : const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont );
2894 0 : if( !pFD )
2895 0 : return 0;
2896 0 : const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont();
2897 :
2898 0 : OStringBuffer aLine( 1024 );
2899 :
2900 0 : if( nFontObject <= 0 )
2901 0 : nFontObject = createObject();
2902 0 : CHECK_RETURN( updateObject( nFontObject ) );
2903 0 : aLine.append( nFontObject );
2904 : aLine.append( " 0 obj\n"
2905 0 : "<</Type/Font/Subtype/Type1/BaseFont/" );
2906 0 : appendName( pBuiltinFont->m_pPSName, aLine );
2907 0 : aLine.append( "\n" );
2908 0 : if( pBuiltinFont->m_eCharSet == RTL_TEXTENCODING_MS_1252 )
2909 0 : aLine.append( "/Encoding/WinAnsiEncoding\n" );
2910 0 : aLine.append( ">>\nendobj\n\n" );
2911 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
2912 0 : return nFontObject;
2913 : }
2914 :
2915 0 : std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const PhysicalFontFace* pFont, EmbedFont& rEmbed )
2916 : {
2917 0 : std::map< sal_Int32, sal_Int32 > aRet;
2918 :
2919 0 : sal_Int32 nFontDescriptor = 0;
2920 0 : OString aSubType( "/Type1" );
2921 0 : FontSubsetInfo aInfo;
2922 : // fill in dummy values
2923 0 : aInfo.m_nAscent = 1000;
2924 0 : aInfo.m_nDescent = 200;
2925 0 : aInfo.m_nCapHeight = 1000;
2926 0 : aInfo.m_aFontBBox = Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) );
2927 0 : aInfo.m_aPSName = pFont->GetFamilyName();
2928 : sal_Int32 pWidths[256];
2929 0 : memset( pWidths, 0, sizeof(pWidths) );
2930 0 : if( pFont->IsEmbeddable() )
2931 : {
2932 0 : const unsigned char* pFontData = NULL;
2933 0 : long nFontLen = 0;
2934 : sal_Ucs nEncodedCodes[256];
2935 : sal_Int32 pEncWidths[256];
2936 0 : if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pEncWidths, aInfo, &nFontLen )) != NULL )
2937 : {
2938 0 : m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
2939 0 : for( int i = 0; i < 256; i++ )
2940 : {
2941 0 : if( nEncodedCodes[i] >= 32 && nEncodedCodes[i] < 256 )
2942 : {
2943 0 : pWidths[i] = pEncWidths[ i ];
2944 : }
2945 : }
2946 : }
2947 : }
2948 0 : else if( pFont->mbSubsettable )
2949 : {
2950 0 : aSubType = OString( "/TrueType" );
2951 0 : Int32Vector aGlyphWidths;
2952 0 : Ucs2UIntMap aUnicodeMap;
2953 0 : m_pReferenceDevice->mpGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap );
2954 :
2955 0 : OUString aTmpName;
2956 0 : osl_createTempFile( NULL, NULL, &aTmpName.pData );
2957 : sal_GlyphId aGlyphIds[ 256 ];
2958 : sal_uInt8 pEncoding[ 256 ];
2959 : sal_Int32 pDuWidths[ 256 ];
2960 :
2961 0 : memset( aGlyphIds, 0, sizeof( aGlyphIds ) );
2962 0 : memset( pEncoding, 0, sizeof( pEncoding ) );
2963 0 : memset( pDuWidths, 0, sizeof( pDuWidths ) );
2964 :
2965 0 : for( sal_Ucs c = 32; c < 256; c++ )
2966 : {
2967 0 : pEncoding[c] = c;
2968 0 : aGlyphIds[c] = 0;
2969 0 : if( aUnicodeMap.find( c ) != aUnicodeMap.end() )
2970 0 : pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ];
2971 : }
2972 :
2973 0 : m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, pFont, aGlyphIds, pEncoding, pDuWidths, 256, aInfo );
2974 0 : osl_removeFile( aTmpName.pData );
2975 : }
2976 : else
2977 : {
2978 : OSL_FAIL( "system font neither embeddable nor subsettable" );
2979 : }
2980 :
2981 : // write font descriptor
2982 0 : nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 );
2983 0 : if( nFontDescriptor )
2984 : {
2985 : // write font object
2986 0 : sal_Int32 nObject = createObject();
2987 0 : if( updateObject( nObject ) )
2988 : {
2989 0 : OStringBuffer aLine( 1024 );
2990 0 : aLine.append( nObject );
2991 : aLine.append( " 0 obj\n"
2992 0 : "<</Type/Font/Subtype" );
2993 0 : aLine.append( aSubType );
2994 0 : aLine.append( "/BaseFont/" );
2995 0 : appendName( aInfo.m_aPSName, aLine );
2996 0 : aLine.append( "\n" );
2997 0 : if( !pFont->IsSymbolFont() )
2998 0 : aLine.append( "/Encoding/WinAnsiEncoding\n" );
2999 : aLine.append( "/FirstChar 32 /LastChar 255\n"
3000 0 : "/Widths[" );
3001 0 : for( int i = 32; i < 256; i++ )
3002 : {
3003 0 : aLine.append( pWidths[i] );
3004 0 : aLine.append( ((i&15) == 15) ? "\n" : " " );
3005 : }
3006 : aLine.append( "]\n"
3007 0 : "/FontDescriptor " );
3008 0 : aLine.append( nFontDescriptor );
3009 : aLine.append( " 0 R>>\n"
3010 0 : "endobj\n\n" );
3011 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
3012 :
3013 0 : aRet[ rEmbed.m_nNormalFontID ] = nObject;
3014 : }
3015 : }
3016 :
3017 0 : return aRet;
3018 : }
3019 :
3020 : typedef int ThreeInts[3];
3021 0 : static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen,
3022 : ThreeInts& rSegmentLengths )
3023 : {
3024 0 : if( !pFontBytes || (nByteLen < 0) )
3025 0 : return false;
3026 0 : const unsigned char* pPtr = pFontBytes;
3027 0 : const unsigned char* pEnd = pFontBytes + nByteLen;
3028 :
3029 0 : for( int i = 0; i < 3; ++i) {
3030 : // read segment1 header
3031 0 : if( pPtr+6 >= pEnd )
3032 0 : return false;
3033 0 : if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) )
3034 0 : return false;
3035 0 : const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2];
3036 0 : if( nLen <= 0)
3037 0 : return false;
3038 0 : rSegmentLengths[i] = nLen;
3039 0 : pPtr += nLen + 6;
3040 : }
3041 :
3042 : // read segment-end header
3043 0 : if( pPtr+2 >= pEnd )
3044 0 : return false;
3045 0 : if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) )
3046 0 : return false;
3047 :
3048 0 : return true;
3049 : }
3050 :
3051 0 : struct FontException : public std::exception
3052 : {
3053 : };
3054 :
3055 : // TODO: always subset instead of embedding the full font => this method becomes obsolete then
3056 0 : std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const PhysicalFontFace* pFont, EmbedFont& rEmbed )
3057 : {
3058 0 : std::map< sal_Int32, sal_Int32 > aRet;
3059 :
3060 0 : sal_Int32 nStreamObject = 0;
3061 0 : sal_Int32 nFontDescriptor = 0;
3062 :
3063 : // prepare font encoding
3064 0 : const Ucs2SIntMap* pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pFont, NULL );
3065 0 : sal_Int32 nToUnicodeStream = 0;
3066 : sal_uInt8 nEncoding[256];
3067 : sal_Ucs nEncodedCodes[256];
3068 0 : std::vector<sal_Ucs> aUnicodes;
3069 0 : aUnicodes.reserve( 256 );
3070 : sal_Int32 pUnicodesPerGlyph[256];
3071 : sal_Int32 pEncToUnicodeIndex[256];
3072 0 : if( pEncoding )
3073 : {
3074 0 : memset( nEncoding, 0, sizeof(nEncoding) );
3075 0 : memset( nEncodedCodes, 0, sizeof(nEncodedCodes) );
3076 0 : memset( pUnicodesPerGlyph, 0, sizeof(pUnicodesPerGlyph) );
3077 0 : memset( pEncToUnicodeIndex, 0, sizeof(pEncToUnicodeIndex) );
3078 0 : for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it )
3079 : {
3080 0 : if( it->second != -1 )
3081 : {
3082 0 : sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff);
3083 0 : nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode );
3084 0 : nEncodedCodes[ nCode ] = it->first;
3085 0 : pEncToUnicodeIndex[ nCode ] = static_cast<sal_Int32>(aUnicodes.size());
3086 0 : aUnicodes.push_back( it->first );
3087 0 : pUnicodesPerGlyph[ nCode ] = 1;
3088 : }
3089 : }
3090 : }
3091 :
3092 0 : FontSubsetInfo aInfo;
3093 : sal_Int32 pWidths[256];
3094 0 : const unsigned char* pFontData = NULL;
3095 0 : long nFontLen = 0;
3096 : sal_Int32 nLength1, nLength2;
3097 : try
3098 : {
3099 0 : if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL )
3100 : {
3101 0 : if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 )
3102 0 : throw FontException();
3103 : // see whether it is pfb or pfa; if it is a pfb, fill ranges
3104 : // of 6 bytes that are not part of the font program
3105 0 : std::list< int > aSections;
3106 0 : std::list< int >::const_iterator it;
3107 0 : int nIndex = 0;
3108 0 : while( (nIndex < nFontLen-1) && pFontData[nIndex] == 0x80 )
3109 : {
3110 0 : aSections.push_back( nIndex );
3111 0 : if( pFontData[nIndex+1] == 0x03 )
3112 0 : break;
3113 : sal_Int32 nBytes =
3114 0 : ((sal_Int32)pFontData[nIndex+2]) |
3115 0 : ((sal_Int32)pFontData[nIndex+3]) << 8 |
3116 0 : ((sal_Int32)pFontData[nIndex+4]) << 16 |
3117 0 : ((sal_Int32)pFontData[nIndex+5]) << 24;
3118 0 : nIndex += nBytes+6;
3119 : }
3120 :
3121 : // search for eexec
3122 : // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below
3123 0 : nIndex = 0;
3124 : int nEndAsciiIndex;
3125 : int nBeginBinaryIndex;
3126 : int nEndBinaryIndex;
3127 0 : do
3128 : {
3129 0 : while( nIndex < nFontLen-4 &&
3130 0 : ( pFontData[nIndex] != 'e' ||
3131 0 : pFontData[nIndex+1] != 'e' ||
3132 0 : pFontData[nIndex+2] != 'x' ||
3133 0 : pFontData[nIndex+3] != 'e' ||
3134 0 : pFontData[nIndex+4] != 'c'
3135 : )
3136 : )
3137 : {
3138 0 : ++nIndex;
3139 : }
3140 : // check whether we are in a excluded section
3141 0 : for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3142 : ;
3143 0 : } while( it != aSections.end() && nIndex < nFontLen-4 );
3144 : // this should end the ascii part
3145 0 : if( nIndex > nFontLen-5 )
3146 0 : throw FontException();
3147 :
3148 0 : nEndAsciiIndex = nIndex+4;
3149 : // now count backwards until we can account for 512 '0'
3150 : // which is the endmarker of the (hopefully) binary data
3151 : // do not count the pfb header sections
3152 0 : int nFound = 0;
3153 0 : nIndex = nFontLen-1;
3154 0 : while( nIndex > 0 && nFound < 512 )
3155 : {
3156 0 : for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3157 : ;
3158 0 : if( it == aSections.end() )
3159 : {
3160 : // inside the 512 '0' block there may only be whitespace
3161 : // according to T1 spec; probably it would be to simple
3162 : // if all fonts complied
3163 0 : if( pFontData[nIndex] == '0' )
3164 0 : nFound++;
3165 0 : else if( nFound > 0 &&
3166 0 : pFontData[nIndex] != '\r' &&
3167 0 : pFontData[nIndex] != '\t' &&
3168 0 : pFontData[nIndex] != '\n' &&
3169 0 : pFontData[nIndex] != ' ' )
3170 0 : break;
3171 : }
3172 0 : nIndex--;
3173 : }
3174 :
3175 0 : if( nIndex < 1 || nIndex <= nEndAsciiIndex )
3176 0 : throw FontException();
3177 :
3178 : // nLength3 is the rest of the file - excluding any section headers
3179 : // nIndex now points before the first of the 512 '0' characters marking the
3180 : // fixed content portion
3181 0 : sal_Int32 nLength3 = nFontLen - nIndex - 1;
3182 0 : for( it = aSections.begin(); it != aSections.end(); ++it )
3183 : {
3184 : // special case: nIndex inside a section marker
3185 0 : if( nIndex >= (*it) && (*it)+6 > nIndex )
3186 0 : nLength3 -= (*it)+6 - nIndex;
3187 0 : else if( *it >= nIndex )
3188 : {
3189 0 : if( *it < nFontLen - 6 )
3190 0 : nLength3 -= 6;
3191 : else // the last section 0x8003 is only 2 bytes after all
3192 0 : nLength3 -= (nFontLen - *it);
3193 : }
3194 : }
3195 :
3196 : // there may be whitespace to ignore before the 512 '0'
3197 0 : while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' )
3198 : {
3199 0 : nIndex--;
3200 0 : for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it )
3201 : ;
3202 0 : if( it != aSections.end() )
3203 : {
3204 0 : nIndex = (*it)-1;
3205 0 : break; // this is surely a binary boundary, in ascii case it wouldn't matter
3206 : }
3207 : }
3208 0 : nEndBinaryIndex = nIndex;
3209 :
3210 : // search for beginning of binary section
3211 0 : nBeginBinaryIndex = nEndAsciiIndex;
3212 0 : do
3213 : {
3214 0 : nBeginBinaryIndex++;
3215 0 : for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it )
3216 : ;
3217 0 : } while( nBeginBinaryIndex < nEndBinaryIndex &&
3218 0 : ( pFontData[nBeginBinaryIndex] == '\r' ||
3219 0 : pFontData[nBeginBinaryIndex] == '\n' ||
3220 0 : it != aSections.end() ) );
3221 :
3222 : // it seems to be vital to copy the exact whitespace between binary data
3223 : // and eexec, else a invalid font results. so make nEndAsciiIndex
3224 : // always immediate in front of nBeginBinaryIndex
3225 0 : nEndAsciiIndex = nBeginBinaryIndex-1;
3226 0 : for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it )
3227 : ;
3228 0 : if( it != aSections.end() )
3229 0 : nEndAsciiIndex = (*it)-1;
3230 :
3231 0 : nLength1 = nEndAsciiIndex+1; // including the last character
3232 0 : for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it )
3233 0 : nLength1 -= 6; // decrease by pfb section size
3234 :
3235 : // if the first four bytes are all ascii hex characters, then binary data
3236 : // has to be converted to real binary data
3237 0 : for( nIndex = 0; nIndex < 4 &&
3238 0 : ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) ||
3239 0 : ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) ||
3240 0 : ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' )
3241 : ); ++nIndex )
3242 : ;
3243 0 : bool bConvertHexData = true;
3244 0 : if( nIndex < 4 )
3245 : {
3246 0 : bConvertHexData = false;
3247 0 : nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte
3248 0 : for( it = aSections.begin(); it != aSections.end(); ++it )
3249 0 : if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex )
3250 0 : nLength2 -= 6;
3251 : }
3252 : else
3253 : {
3254 : // count the hex ascii characters to get nLength2
3255 0 : nLength2 = 0;
3256 0 : int nNextSectionIndex = 0;
3257 0 : for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3258 : ;
3259 0 : if( it != aSections.end() )
3260 0 : nNextSectionIndex = *it;
3261 0 : for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3262 : {
3263 0 : if( nIndex == nNextSectionIndex )
3264 : {
3265 0 : nIndex += 6;
3266 0 : ++it;
3267 0 : nNextSectionIndex = (it == aSections.end() ? 0 : *it );
3268 : }
3269 0 : if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) ||
3270 0 : ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) ||
3271 0 : ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) )
3272 0 : nLength2++;
3273 : }
3274 : DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" );
3275 0 : nLength2 /= 2;
3276 : }
3277 :
3278 : // now we can actually write the font stream !
3279 : #if OSL_DEBUG_LEVEL > 1
3280 : emitComment( " PDFWriterImpl::emitEmbeddedFont" );
3281 : #endif
3282 0 : OStringBuffer aLine( 512 );
3283 0 : nStreamObject = createObject();
3284 0 : if( !updateObject(nStreamObject))
3285 0 : throw FontException();
3286 0 : sal_Int32 nStreamLengthObject = createObject();
3287 0 : aLine.append( nStreamObject );
3288 : aLine.append( " 0 obj\n"
3289 0 : "<</Length " );
3290 0 : aLine.append( nStreamLengthObject );
3291 : aLine.append( " 0 R"
3292 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3293 : "/Filter/FlateDecode"
3294 : #endif
3295 0 : "/Length1 " );
3296 0 : aLine.append( nLength1 );
3297 0 : aLine.append( " /Length2 " );
3298 0 : aLine.append( nLength2 );
3299 0 : aLine.append( " /Length3 ");
3300 0 : aLine.append( nLength3 );
3301 : aLine.append( ">>\n"
3302 0 : "stream\n" );
3303 0 : if( !writeBuffer( aLine.getStr(), aLine.getLength() ) )
3304 0 : throw FontException();
3305 :
3306 0 : sal_uInt64 nBeginStreamPos = 0;
3307 0 : osl_getFilePos( m_aFile, &nBeginStreamPos );
3308 :
3309 0 : beginCompression();
3310 0 : checkAndEnableStreamEncryption( nStreamObject );
3311 :
3312 : // write ascii section
3313 0 : if( aSections.begin() == aSections.end() )
3314 : {
3315 0 : if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) )
3316 0 : throw FontException();
3317 : }
3318 : else
3319 : {
3320 : // first section always starts at 0
3321 0 : it = aSections.begin();
3322 0 : nIndex = (*it)+6;
3323 0 : ++it;
3324 0 : while( *it < nEndAsciiIndex )
3325 : {
3326 0 : if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) )
3327 0 : throw FontException();
3328 0 : nIndex = (*it)+6;
3329 0 : ++it;
3330 : }
3331 : // write partial last section
3332 0 : if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) )
3333 0 : throw FontException();
3334 : }
3335 :
3336 : // write binary section
3337 0 : if( ! bConvertHexData )
3338 : {
3339 0 : if( aSections.begin() == aSections.end() )
3340 : {
3341 0 : if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) )
3342 0 : throw FontException();
3343 : }
3344 : else
3345 : {
3346 0 : for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it )
3347 : ;
3348 : // write first partial section
3349 0 : if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) )
3350 0 : throw FontException();
3351 : // write following sections
3352 0 : while( it != aSections.end() )
3353 : {
3354 0 : nIndex = (*it)+6;
3355 0 : ++it;
3356 0 : if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3357 : {
3358 0 : sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3359 0 : if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3360 0 : throw FontException();
3361 : }
3362 : }
3363 : }
3364 : }
3365 : else
3366 : {
3367 0 : boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] );
3368 0 : memset( pWriteBuffer.get(), 0, nLength2 );
3369 0 : int nWriteIndex = 0;
3370 :
3371 0 : int nNextSectionIndex = 0;
3372 0 : for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it )
3373 : ;
3374 0 : if( it != aSections.end() )
3375 0 : nNextSectionIndex = *it;
3376 0 : for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ )
3377 : {
3378 0 : if( nIndex == nNextSectionIndex )
3379 : {
3380 0 : nIndex += 6;
3381 0 : ++it;
3382 0 : nNextSectionIndex = (it == aSections.end() ? nFontLen : *it );
3383 : }
3384 0 : unsigned char cNibble = 0x80;
3385 0 : if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' )
3386 0 : cNibble = pFontData[nIndex] - '0';
3387 0 : else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' )
3388 0 : cNibble = pFontData[nIndex] - 'a' + 10;
3389 0 : else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' )
3390 0 : cNibble = pFontData[nIndex] - 'A' + 10;
3391 0 : if( cNibble != 0x80 )
3392 : {
3393 0 : if( !(nWriteIndex & 1 ) )
3394 0 : cNibble <<= 4;
3395 0 : pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble;
3396 0 : nWriteIndex++;
3397 : }
3398 : }
3399 0 : if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) )
3400 0 : throw FontException();
3401 0 : if( aSections.empty() )
3402 : {
3403 0 : if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) )
3404 0 : throw FontException();
3405 : }
3406 : else
3407 : {
3408 : // write rest of this section
3409 0 : if( nIndex < nNextSectionIndex )
3410 : {
3411 0 : if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) )
3412 0 : throw FontException();
3413 : }
3414 : // write following sections
3415 0 : while( it != aSections.end() )
3416 : {
3417 0 : nIndex = (*it)+6;
3418 0 : ++it;
3419 0 : if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes
3420 : {
3421 0 : sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex;
3422 0 : if( ! writeBuffer( pFontData+nIndex, nSectionLen ) )
3423 0 : throw FontException();
3424 : }
3425 : }
3426 0 : }
3427 : }
3428 0 : endCompression();
3429 0 : disableStreamEncryption();
3430 :
3431 0 : sal_uInt64 nEndStreamPos = 0;
3432 0 : osl_getFilePos( m_aFile, &nEndStreamPos );
3433 :
3434 : // and finally close the stream
3435 0 : aLine.setLength( 0 );
3436 0 : aLine.append( "\nendstream\nendobj\n\n" );
3437 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3438 0 : throw FontException();
3439 :
3440 : // write stream length object
3441 0 : aLine.setLength( 0 );
3442 0 : if( ! updateObject( nStreamLengthObject ) )
3443 0 : throw FontException();
3444 0 : aLine.append( nStreamLengthObject );
3445 0 : aLine.append( " 0 obj\n" );
3446 0 : aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) );
3447 0 : aLine.append( "\nendobj\n\n" );
3448 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3449 0 : throw FontException();
3450 : }
3451 : else
3452 : {
3453 0 : OStringBuffer aErrorComment( 256 );
3454 0 : aErrorComment.append( "GetEmbedFontData failed for font \"" );
3455 0 : aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
3456 0 : aErrorComment.append( '\"' );
3457 0 : if( pFont->GetSlant() == ITALIC_NORMAL )
3458 0 : aErrorComment.append( " italic" );
3459 0 : else if( pFont->GetSlant() == ITALIC_OBLIQUE )
3460 0 : aErrorComment.append( " oblique" );
3461 0 : aErrorComment.append( " weight=" );
3462 0 : aErrorComment.append( sal_Int32(pFont->GetWeight()) );
3463 0 : emitComment( aErrorComment.getStr() );
3464 : }
3465 :
3466 0 : if( nStreamObject )
3467 : {
3468 : // write font descriptor
3469 0 : nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject );
3470 : }
3471 :
3472 0 : if( nFontDescriptor )
3473 : {
3474 0 : if( pEncoding )
3475 0 : nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, SAL_N_ELEMENTS(nEncoding) );
3476 :
3477 : // write font object
3478 0 : sal_Int32 nObject = createObject();
3479 0 : if( ! updateObject( nObject ) )
3480 0 : throw FontException();
3481 :
3482 0 : OStringBuffer aLine( 1024 );
3483 0 : aLine.append( nObject );
3484 : aLine.append( " 0 obj\n"
3485 0 : "<</Type/Font/Subtype/Type1/BaseFont/" );
3486 0 : appendName( aInfo.m_aPSName, aLine );
3487 0 : aLine.append( "\n" );
3488 0 : if( !pFont->IsSymbolFont() && pEncoding == 0 )
3489 0 : aLine.append( "/Encoding/WinAnsiEncoding\n" );
3490 0 : if( nToUnicodeStream )
3491 : {
3492 0 : aLine.append( "/ToUnicode " );
3493 0 : aLine.append( nToUnicodeStream );
3494 0 : aLine.append( " 0 R\n" );
3495 : }
3496 : aLine.append( "/FirstChar 0 /LastChar 255\n"
3497 0 : "/Widths[" );
3498 0 : for( int i = 0; i < 256; i++ )
3499 : {
3500 0 : aLine.append( pWidths[i] );
3501 0 : aLine.append( ((i&15) == 15) ? "\n" : " " );
3502 : }
3503 : aLine.append( "]\n"
3504 0 : "/FontDescriptor " );
3505 0 : aLine.append( nFontDescriptor );
3506 : aLine.append( " 0 R>>\n"
3507 0 : "endobj\n\n" );
3508 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3509 0 : throw FontException();
3510 :
3511 0 : aRet[ rEmbed.m_nNormalFontID ] = nObject;
3512 :
3513 : // write additional encodings
3514 0 : for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it )
3515 : {
3516 : sal_Int32 aEncWidths[ 256 ];
3517 : // emit encoding dict
3518 0 : sal_Int32 nEncObject = createObject();
3519 0 : if( ! updateObject( nEncObject ) )
3520 0 : throw FontException();
3521 :
3522 0 : OutputDevice* pRef = getReferenceDevice();
3523 0 : pRef->Push( PUSH_FONT | PUSH_MAPMODE );
3524 0 : pRef->SetMapMode( MapMode( MAP_PIXEL ) );
3525 0 : Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) );
3526 0 : aFont.SetWeight( pFont->GetWeight() );
3527 0 : aFont.SetItalic( pFont->GetSlant() );
3528 0 : aFont.SetPitch( pFont->GetPitch() );
3529 0 : pRef->SetFont( aFont );
3530 0 : pRef->ImplNewFont();
3531 :
3532 0 : aLine.setLength( 0 );
3533 0 : aLine.append( nEncObject );
3534 : aLine.append( " 0 obj\n"
3535 0 : "<</Type/Encoding/Differences[ 0\n" );
3536 0 : int nEncoded = 0;
3537 0 : aUnicodes.clear();
3538 0 : for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it )
3539 : {
3540 0 : OUString aStr( str_it->m_aUnicode );
3541 0 : aEncWidths[nEncoded] = pRef->GetTextWidth( aStr );
3542 0 : nEncodedCodes[nEncoded] = str_it->m_aUnicode;
3543 0 : nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded);
3544 0 : pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size());
3545 0 : aUnicodes.push_back( nEncodedCodes[nEncoded] );
3546 0 : pUnicodesPerGlyph[nEncoded] = 1;
3547 :
3548 0 : aLine.append( " /" );
3549 0 : aLine.append( str_it->m_aName );
3550 0 : if( !((++nEncoded) & 15) )
3551 0 : aLine.append( "\n" );
3552 0 : }
3553 : aLine.append( "]>>\n"
3554 0 : "endobj\n\n" );
3555 :
3556 0 : pRef->Pop();
3557 :
3558 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3559 0 : throw FontException();
3560 :
3561 0 : nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded );
3562 :
3563 0 : nObject = createObject();
3564 0 : if( ! updateObject( nObject ) )
3565 0 : throw FontException();
3566 :
3567 0 : aLine.setLength( 0 );
3568 0 : aLine.append( nObject );
3569 : aLine.append( " 0 obj\n"
3570 0 : "<</Type/Font/Subtype/Type1/BaseFont/" );
3571 0 : appendName( aInfo.m_aPSName, aLine );
3572 0 : aLine.append( "\n" );
3573 0 : aLine.append( "/Encoding " );
3574 0 : aLine.append( nEncObject );
3575 0 : aLine.append( " 0 R\n" );
3576 0 : if( nToUnicodeStream )
3577 : {
3578 0 : aLine.append( "/ToUnicode " );
3579 0 : aLine.append( nToUnicodeStream );
3580 0 : aLine.append( " 0 R\n" );
3581 : }
3582 : aLine.append( "/FirstChar 0\n"
3583 0 : "/LastChar " );
3584 0 : aLine.append( (sal_Int32)(nEncoded-1) );
3585 : aLine.append( "\n"
3586 0 : "/Widths[" );
3587 0 : for( int i = 0; i < nEncoded; i++ )
3588 : {
3589 0 : aLine.append( aEncWidths[i] );
3590 0 : aLine.append( ((i&15) == 15) ? "\n" : " " );
3591 : }
3592 : aLine.append( " ]\n"
3593 0 : "/FontDescriptor " );
3594 0 : aLine.append( nFontDescriptor );
3595 : aLine.append( " 0 R>>\n"
3596 0 : "endobj\n\n" );
3597 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
3598 0 : throw FontException();
3599 :
3600 0 : aRet[ enc_it->m_nFontID ] = nObject;
3601 0 : }
3602 : }
3603 : }
3604 0 : catch( FontException& )
3605 : {
3606 : // these do nothing in case there was no compression or encryption ongoing
3607 0 : endCompression();
3608 0 : disableStreamEncryption();
3609 : }
3610 :
3611 0 : if( pFontData )
3612 0 : m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen );
3613 :
3614 0 : return aRet;
3615 : }
3616 :
3617 0 : static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer )
3618 : {
3619 0 : if( nSubsetID )
3620 : {
3621 0 : for( int i = 0; i < 6; i++ )
3622 : {
3623 0 : int nOffset = (nSubsetID % 26);
3624 0 : nSubsetID /= 26;
3625 0 : rBuffer.append( (sal_Char)('A'+nOffset) );
3626 : }
3627 0 : rBuffer.append( '+' );
3628 : }
3629 0 : appendName( rPSName, rBuffer );
3630 0 : }
3631 :
3632 0 : sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding,
3633 : sal_Ucs* pUnicodes,
3634 : sal_Int32* pUnicodesPerGlyph,
3635 : sal_Int32* pEncToUnicodeIndex,
3636 : int nGlyphs )
3637 : {
3638 0 : int nMapped = 0, n = 0;
3639 0 : for( n = 0; n < nGlyphs; n++ )
3640 0 : if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3641 0 : nMapped++;
3642 :
3643 0 : if( nMapped == 0 )
3644 0 : return 0;
3645 :
3646 0 : sal_Int32 nStream = createObject();
3647 0 : CHECK_RETURN( updateObject( nStream ) );
3648 :
3649 0 : OStringBuffer aContents( 1024 );
3650 : aContents.append(
3651 : "/CIDInit/ProcSet findresource begin\n"
3652 : "12 dict begin\n"
3653 : "begincmap\n"
3654 : "/CIDSystemInfo<<\n"
3655 : "/Registry (Adobe)\n"
3656 : "/Ordering (UCS)\n"
3657 : "/Supplement 0\n"
3658 : ">> def\n"
3659 : "/CMapName/Adobe-Identity-UCS def\n"
3660 : "/CMapType 2 def\n"
3661 : "1 begincodespacerange\n"
3662 : "<00> <FF>\n"
3663 : "endcodespacerange\n"
3664 0 : );
3665 0 : int nCount = 0;
3666 0 : for( n = 0; n < nGlyphs; n++ )
3667 : {
3668 0 : if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] )
3669 : {
3670 0 : if( (nCount % 100) == 0 )
3671 : {
3672 0 : if( nCount )
3673 0 : aContents.append( "endbfchar\n" );
3674 0 : aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) );
3675 0 : aContents.append( " beginbfchar\n" );
3676 : }
3677 0 : aContents.append( '<' );
3678 0 : appendHex( (sal_Int8)pEncoding[n], aContents );
3679 0 : aContents.append( "> <" );
3680 : // TODO: handle unicodes>U+FFFF
3681 0 : sal_Int32 nIndex = pEncToUnicodeIndex[n];
3682 0 : for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ )
3683 : {
3684 0 : appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents );
3685 0 : appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents );
3686 : }
3687 0 : aContents.append( ">\n" );
3688 0 : nCount++;
3689 : }
3690 : }
3691 : aContents.append( "endbfchar\n"
3692 : "endcmap\n"
3693 : "CMapName currentdict /CMap defineresource pop\n"
3694 : "end\n"
3695 0 : "end\n" );
3696 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3697 0 : ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 );
3698 0 : SvMemoryStream aStream;
3699 0 : pCodec->BeginCompression();
3700 0 : pCodec->Write( aStream, (const sal_uInt8*)aContents.getStr(), aContents.getLength() );
3701 0 : pCodec->EndCompression();
3702 0 : delete pCodec;
3703 : #endif
3704 :
3705 : #if OSL_DEBUG_LEVEL > 1
3706 : emitComment( "PDFWriterImpl::createToUnicodeCMap" );
3707 : #endif
3708 0 : OStringBuffer aLine( 40 );
3709 :
3710 0 : aLine.append( nStream );
3711 0 : aLine.append( " 0 obj\n<</Length " );
3712 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3713 0 : sal_Int32 nLen = (sal_Int32)aStream.Tell();
3714 0 : aStream.Seek( 0 );
3715 0 : aLine.append( nLen );
3716 0 : aLine.append( "/Filter/FlateDecode" );
3717 : #else
3718 : aLine.append( aContents.getLength() );
3719 : #endif
3720 0 : aLine.append( ">>\nstream\n" );
3721 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3722 0 : checkAndEnableStreamEncryption( nStream );
3723 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3724 0 : CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) );
3725 : #else
3726 : CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) );
3727 : #endif
3728 0 : disableStreamEncryption();
3729 0 : aLine.setLength( 0 );
3730 : aLine.append( "\nendstream\n"
3731 0 : "endobj\n\n" );
3732 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3733 0 : return nStream;
3734 : }
3735 :
3736 0 : sal_Int32 PDFWriterImpl::emitFontDescriptor( const PhysicalFontFace* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream )
3737 : {
3738 0 : OStringBuffer aLine( 1024 );
3739 : // get font flags, see PDF reference 1.4 p. 358
3740 : // possibly characters outside Adobe standard encoding
3741 : // so set Symbolic flag
3742 0 : sal_Int32 nFontFlags = (1<<2);
3743 0 : if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE )
3744 0 : nFontFlags |= (1 << 6);
3745 0 : if( pFont->GetPitch() == PITCH_FIXED )
3746 0 : nFontFlags |= 1;
3747 0 : if( pFont->GetFamilyType() == FAMILY_SCRIPT )
3748 0 : nFontFlags |= (1 << 3);
3749 0 : else if( pFont->GetFamilyType() == FAMILY_ROMAN )
3750 0 : nFontFlags |= (1 << 1);
3751 :
3752 0 : sal_Int32 nFontDescriptor = createObject();
3753 0 : CHECK_RETURN( updateObject( nFontDescriptor ) );
3754 0 : aLine.setLength( 0 );
3755 0 : aLine.append( nFontDescriptor );
3756 : aLine.append( " 0 obj\n"
3757 0 : "<</Type/FontDescriptor/FontName/" );
3758 0 : appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine );
3759 : aLine.append( "\n"
3760 0 : "/Flags " );
3761 0 : aLine.append( nFontFlags );
3762 : aLine.append( "\n"
3763 0 : "/FontBBox[" );
3764 : // note: Top and Bottom are reversed in VCL and PDF rectangles
3765 0 : aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() );
3766 0 : aLine.append( ' ' );
3767 0 : aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() );
3768 0 : aLine.append( ' ' );
3769 0 : aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() );
3770 0 : aLine.append( ' ' );
3771 0 : aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) );
3772 0 : aLine.append( "]/ItalicAngle " );
3773 0 : if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL )
3774 0 : aLine.append( "-30" );
3775 : else
3776 0 : aLine.append( "0" );
3777 : aLine.append( "\n"
3778 0 : "/Ascent " );
3779 0 : aLine.append( (sal_Int32)rInfo.m_nAscent );
3780 : aLine.append( "\n"
3781 0 : "/Descent " );
3782 0 : aLine.append( (sal_Int32)-rInfo.m_nDescent );
3783 : aLine.append( "\n"
3784 0 : "/CapHeight " );
3785 0 : aLine.append( (sal_Int32)rInfo.m_nCapHeight );
3786 : // According to PDF reference 1.4 StemV is required
3787 : // seems a tad strange to me, but well ...
3788 : aLine.append( "\n"
3789 0 : "/StemV 80\n" );
3790 0 : if( nFontStream )
3791 : {
3792 0 : aLine.append( "/FontFile" );
3793 0 : switch( rInfo.m_nFontType )
3794 : {
3795 : case FontSubsetInfo::SFNT_TTF:
3796 0 : aLine.append( '2' );
3797 0 : break;
3798 : case FontSubsetInfo::TYPE1_PFA:
3799 : case FontSubsetInfo::TYPE1_PFB:
3800 : case FontSubsetInfo::ANY_TYPE1:
3801 0 : break;
3802 : default:
3803 : OSL_FAIL( "unknown fonttype in PDF font descriptor" );
3804 0 : return 0;
3805 : }
3806 0 : aLine.append( ' ' );
3807 0 : aLine.append( nFontStream );
3808 0 : aLine.append( " 0 R\n" );
3809 : }
3810 : aLine.append( ">>\n"
3811 0 : "endobj\n\n" );
3812 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
3813 :
3814 0 : return nFontDescriptor;
3815 : }
3816 :
3817 0 : void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const
3818 : {
3819 0 : for( std::map< sal_Int32, sal_Int32 >::const_iterator it =
3820 0 : m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it )
3821 : {
3822 0 : rDict.append( m_aBuiltinFonts[it->first].getNameObject() );
3823 0 : rDict.append( ' ' );
3824 0 : rDict.append( it->second );
3825 0 : rDict.append( " 0 R" );
3826 : }
3827 0 : }
3828 :
3829 0 : bool PDFWriterImpl::emitFonts()
3830 : {
3831 0 : if( ! m_pReferenceDevice->ImplGetGraphics() )
3832 0 : return false;
3833 :
3834 0 : OStringBuffer aLine( 1024 );
3835 :
3836 0 : std::map< sal_Int32, sal_Int32 > aFontIDToObject;
3837 :
3838 0 : OUString aTmpName;
3839 0 : osl_createTempFile( NULL, NULL, &aTmpName.pData );
3840 0 : for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it )
3841 : {
3842 0 : for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit )
3843 : {
3844 : sal_GlyphId aGlyphIds[ 256 ];
3845 : sal_Int32 pWidths[ 256 ];
3846 : sal_uInt8 pEncoding[ 256 ];
3847 : sal_Int32 pEncToUnicodeIndex[ 256 ];
3848 : sal_Int32 pUnicodesPerGlyph[ 256 ];
3849 0 : std::vector<sal_Ucs> aUnicodes;
3850 0 : aUnicodes.reserve( 256 );
3851 0 : int nGlyphs = 1;
3852 : // fill arrays and prepare encoding index map
3853 0 : sal_Int32 nToUnicodeStream = 0;
3854 :
3855 0 : memset( aGlyphIds, 0, sizeof( aGlyphIds ) );
3856 0 : memset( pEncoding, 0, sizeof( pEncoding ) );
3857 0 : memset( pUnicodesPerGlyph, 0, sizeof( pUnicodesPerGlyph ) );
3858 0 : memset( pEncToUnicodeIndex, 0, sizeof( pEncToUnicodeIndex ) );
3859 0 : for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit )
3860 : {
3861 0 : sal_uInt8 nEnc = fit->second.getGlyphId();
3862 :
3863 : DBG_ASSERT( aGlyphIds[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" );
3864 : DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" );
3865 :
3866 0 : aGlyphIds[ nEnc ] = fit->first;
3867 0 : pEncoding[ nEnc ] = nEnc;
3868 0 : pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size());
3869 0 : pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes();
3870 0 : for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ )
3871 0 : aUnicodes.push_back( fit->second.getCode( n ) );
3872 0 : if( fit->second.getCode(0) )
3873 0 : nToUnicodeStream = 1;
3874 0 : if( nGlyphs < 256 )
3875 0 : nGlyphs++;
3876 : else
3877 : {
3878 : OSL_FAIL( "too many glyphs for subset" );
3879 : }
3880 : }
3881 0 : FontSubsetInfo aSubsetInfo;
3882 0 : if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, aGlyphIds, pEncoding, pWidths, nGlyphs, aSubsetInfo ) )
3883 : {
3884 : // create font stream
3885 : oslFileHandle aFontFile;
3886 0 : if ( osl_File_E_None != osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) return false;
3887 : // get file size
3888 : sal_uInt64 nLength1;
3889 0 : if ( osl_File_E_None != osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) return false;
3890 0 : if ( osl_File_E_None != osl_getFilePos( aFontFile, &nLength1 ) ) return false;
3891 0 : if ( osl_File_E_None != osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) return false;
3892 :
3893 : #if OSL_DEBUG_LEVEL > 1
3894 : emitComment( "PDFWriterImpl::emitFonts" );
3895 : #endif
3896 0 : sal_Int32 nFontStream = createObject();
3897 0 : sal_Int32 nStreamLengthObject = createObject();
3898 0 : if ( !updateObject( nFontStream ) ) return false;
3899 0 : aLine.setLength( 0 );
3900 0 : aLine.append( nFontStream );
3901 : aLine.append( " 0 obj\n"
3902 0 : "<</Length " );
3903 0 : aLine.append( (sal_Int32)nStreamLengthObject );
3904 : aLine.append( " 0 R"
3905 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
3906 : "/Filter/FlateDecode"
3907 : #endif
3908 0 : "/Length1 " );
3909 :
3910 0 : sal_uInt64 nStartPos = 0;
3911 0 : if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF )
3912 : {
3913 0 : aLine.append( (sal_Int32)nLength1 );
3914 :
3915 : aLine.append( ">>\n"
3916 0 : "stream\n" );
3917 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
3918 0 : if ( osl_File_E_None != osl_getFilePos( m_aFile, &nStartPos ) ) return false;
3919 :
3920 : // copy font file
3921 0 : beginCompression();
3922 0 : checkAndEnableStreamEncryption( nFontStream );
3923 0 : sal_Bool bEOF = sal_False;
3924 0 : do
3925 : {
3926 : char buf[8192];
3927 : sal_uInt64 nRead;
3928 0 : if ( osl_File_E_None != osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) return false;
3929 0 : if ( !writeBuffer( buf, nRead ) ) return false;
3930 0 : if ( osl_File_E_None != osl_isEndOfFile( aFontFile, &bEOF ) ) return false;
3931 0 : } while( ! bEOF );
3932 : }
3933 0 : else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 )
3934 : {
3935 : // TODO: implement
3936 : OSL_FAIL( "PDFWriterImpl does not support CFF-font subsets yet!" );
3937 : }
3938 0 : else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA?
3939 : {
3940 0 : boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] );
3941 :
3942 0 : sal_uInt64 nBytesRead = 0;
3943 0 : if ( osl_File_E_None != osl_readFile( aFontFile, pBuffer.get(), nLength1, &nBytesRead ) ) return false;
3944 : DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" );
3945 0 : if ( osl_File_E_None != osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) return false;
3946 : // get the PFB-segment lengths
3947 0 : ThreeInts aSegmentLengths = {0,0,0};
3948 0 : getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths );
3949 : // the lengths below are mandatory for PDF-exported Type1 fonts
3950 : // because the PFB segment headers get stripped! WhyOhWhy.
3951 0 : aLine.append( (sal_Int32)aSegmentLengths[0] );
3952 0 : aLine.append( "/Length2 " );
3953 0 : aLine.append( (sal_Int32)aSegmentLengths[1] );
3954 0 : aLine.append( "/Length3 " );
3955 0 : aLine.append( (sal_Int32)aSegmentLengths[2] );
3956 :
3957 : aLine.append( ">>\n"
3958 0 : "stream\n" );
3959 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
3960 0 : if ( osl_File_E_None != osl_getFilePos( m_aFile, &nStartPos ) ) return false;
3961 :
3962 : // emit PFB-sections without section headers
3963 0 : beginCompression();
3964 0 : checkAndEnableStreamEncryption( nFontStream );
3965 0 : if ( !writeBuffer( &pBuffer[6], aSegmentLengths[0] ) ) return false;
3966 0 : if ( !writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) ) return false;
3967 0 : if ( !writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ) return false;
3968 : }
3969 : else
3970 : {
3971 0 : fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType);
3972 0 : aLine.append( "0 >>\nstream\n" );
3973 : }
3974 :
3975 0 : endCompression();
3976 0 : disableStreamEncryption();
3977 : // close the file
3978 0 : osl_closeFile( aFontFile );
3979 :
3980 0 : sal_uInt64 nEndPos = 0;
3981 0 : if ( osl_File_E_None != osl_getFilePos( m_aFile, &nEndPos ) ) return false;
3982 : // end the stream
3983 0 : aLine.setLength( 0 );
3984 0 : aLine.append( "\nendstream\nendobj\n\n" );
3985 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
3986 :
3987 : // emit stream length object
3988 0 : if ( !updateObject( nStreamLengthObject ) ) return false;
3989 0 : aLine.setLength( 0 );
3990 0 : aLine.append( nStreamLengthObject );
3991 0 : aLine.append( " 0 obj\n" );
3992 0 : aLine.append( (sal_Int64)(nEndPos-nStartPos) );
3993 0 : aLine.append( "\nendobj\n\n" );
3994 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
3995 :
3996 : // write font descriptor
3997 0 : sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream );
3998 :
3999 0 : if( nToUnicodeStream )
4000 0 : nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs );
4001 :
4002 0 : sal_Int32 nFontObject = createObject();
4003 0 : if ( !updateObject( nFontObject ) ) return false;
4004 0 : aLine.setLength( 0 );
4005 0 : aLine.append( nFontObject );
4006 :
4007 0 : aLine.append( " 0 obj\n" );
4008 0 : aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ?
4009 : "<</Type/Font/Subtype/Type1/BaseFont/" :
4010 0 : "<</Type/Font/Subtype/TrueType/BaseFont/" );
4011 0 : appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine );
4012 : aLine.append( "\n"
4013 : "/FirstChar 0\n"
4014 0 : "/LastChar " );
4015 0 : aLine.append( (sal_Int32)(nGlyphs-1) );
4016 : aLine.append( "\n"
4017 0 : "/Widths[" );
4018 0 : for( int i = 0; i < nGlyphs; i++ )
4019 : {
4020 0 : aLine.append( pWidths[ i ] );
4021 0 : aLine.append( ((i & 15) == 15) ? "\n" : " " );
4022 : }
4023 : aLine.append( "]\n"
4024 0 : "/FontDescriptor " );
4025 0 : aLine.append( nFontDescriptor );
4026 0 : aLine.append( " 0 R\n" );
4027 0 : if( nToUnicodeStream )
4028 : {
4029 0 : aLine.append( "/ToUnicode " );
4030 0 : aLine.append( nToUnicodeStream );
4031 0 : aLine.append( " 0 R\n" );
4032 : }
4033 : aLine.append( ">>\n"
4034 0 : "endobj\n\n" );
4035 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false;
4036 :
4037 0 : aFontIDToObject[ lit->m_nFontID ] = nFontObject;
4038 : }
4039 : else
4040 : {
4041 0 : const PhysicalFontFace* pFont = it->first;
4042 0 : OStringBuffer aErrorComment( 256 );
4043 0 : aErrorComment.append( "CreateFontSubset failed for font \"" );
4044 0 : aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
4045 0 : aErrorComment.append( '\"' );
4046 0 : if( pFont->GetSlant() == ITALIC_NORMAL )
4047 0 : aErrorComment.append( " italic" );
4048 0 : else if( pFont->GetSlant() == ITALIC_OBLIQUE )
4049 0 : aErrorComment.append( " oblique" );
4050 0 : aErrorComment.append( " weight=" );
4051 0 : aErrorComment.append( sal_Int32(pFont->GetWeight()) );
4052 0 : emitComment( aErrorComment.getStr() );
4053 : }
4054 0 : }
4055 : }
4056 0 : osl_removeFile( aTmpName.pData );
4057 :
4058 : // emit embedded fonts
4059 0 : for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit )
4060 : {
4061 0 : std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second );
4062 0 : for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4063 : {
4064 0 : if ( !fit->second ) return false;
4065 0 : aFontIDToObject[ fit->first ] = fit->second;
4066 : }
4067 0 : }
4068 :
4069 : // emit system fonts
4070 0 : for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit )
4071 : {
4072 0 : std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second );
4073 0 : for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit )
4074 : {
4075 0 : if ( !fit->second ) return false;
4076 0 : aFontIDToObject[ fit->first ] = fit->second;
4077 : }
4078 0 : }
4079 :
4080 0 : OStringBuffer aFontDict( 1024 );
4081 0 : aFontDict.append( getFontDictObject() );
4082 : aFontDict.append( " 0 obj\n"
4083 0 : "<<" );
4084 0 : int ni = 0;
4085 0 : for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit )
4086 : {
4087 0 : aFontDict.append( "/F" );
4088 0 : aFontDict.append( mit->first );
4089 0 : aFontDict.append( ' ' );
4090 0 : aFontDict.append( mit->second );
4091 0 : aFontDict.append( " 0 R" );
4092 0 : if( ((++ni) & 7) == 0 )
4093 0 : aFontDict.append( '\n' );
4094 : }
4095 : // emit builtin font for widget apperances / variable text
4096 0 : for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin();
4097 0 : it != m_aBuiltinFontToObjectMap.end(); ++it )
4098 : {
4099 0 : ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]);
4100 0 : it->second = emitBuiltinFont( &aData, it->second );
4101 0 : }
4102 0 : appendBuiltinFontsToDict( aFontDict );
4103 0 : aFontDict.append( "\n>>\nendobj\n\n" );
4104 :
4105 0 : if ( !updateObject( getFontDictObject() ) ) return false;
4106 0 : if ( !writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) ) return false;
4107 0 : return true;
4108 : }
4109 :
4110 0 : sal_Int32 PDFWriterImpl::emitResources()
4111 : {
4112 : // emit shadings
4113 0 : if( ! m_aGradients.empty() )
4114 0 : CHECK_RETURN( emitGradients() );
4115 : // emit tilings
4116 0 : if( ! m_aTilings.empty() )
4117 0 : CHECK_RETURN( emitTilings() );
4118 :
4119 : // emit font dict
4120 0 : CHECK_RETURN( emitFonts() );
4121 :
4122 : // emit Resource dict
4123 0 : OStringBuffer aLine( 512 );
4124 0 : sal_Int32 nResourceDict = getResourceDictObj();
4125 0 : CHECK_RETURN( updateObject( nResourceDict ) );
4126 0 : aLine.setLength( 0 );
4127 0 : aLine.append( nResourceDict );
4128 0 : aLine.append( " 0 obj\n" );
4129 0 : m_aGlobalResourceDict.append( aLine, getFontDictObject() );
4130 0 : aLine.append( "endobj\n\n" );
4131 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4132 0 : return nResourceDict;
4133 : }
4134 :
4135 0 : sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts,
4136 : sal_Int32 nItemLevel,
4137 : sal_Int32 nCurrentItemId )
4138 : {
4139 : /* The /Count number of an item is
4140 : positive: the number of visible subitems
4141 : negative: the negative number of subitems that will become visible if
4142 : the item gets opened
4143 : see PDF ref 1.4 p 478
4144 : */
4145 :
4146 0 : sal_Int32 nCount = 0;
4147 :
4148 0 : if( m_aContext.OpenBookmarkLevels < 0 || // all levels arevisible
4149 0 : m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible
4150 : )
4151 : {
4152 0 : PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4153 0 : sal_Int32 nChildren = rItem.m_aChildren.size();
4154 0 : for( sal_Int32 i = 0; i < nChildren; i++ )
4155 0 : nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4156 0 : rCounts[nCurrentItemId] = nCount;
4157 : // return 1 (this item) + visible sub items
4158 0 : if( nCount < 0 )
4159 0 : nCount = 0;
4160 0 : nCount++;
4161 : }
4162 : else
4163 : {
4164 : // this bookmark level is invisible
4165 0 : PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ];
4166 0 : sal_Int32 nChildren = rItem.m_aChildren.size();
4167 0 : rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size());
4168 0 : for( sal_Int32 i = 0; i < nChildren; i++ )
4169 0 : updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] );
4170 0 : nCount = -1;
4171 : }
4172 :
4173 0 : return nCount;
4174 : }
4175 :
4176 0 : sal_Int32 PDFWriterImpl::emitOutline()
4177 : {
4178 0 : int i, nItems = m_aOutline.size();
4179 :
4180 : // do we have an outline at all ?
4181 0 : if( nItems < 2 )
4182 0 : return 0;
4183 :
4184 : // reserve object numbers for all outline items
4185 0 : for( i = 0; i < nItems; ++i )
4186 0 : m_aOutline[i].m_nObject = createObject();
4187 :
4188 : // update all parent, next and prev object ids
4189 0 : for( i = 0; i < nItems; ++i )
4190 : {
4191 0 : PDFOutlineEntry& rItem = m_aOutline[i];
4192 0 : int nChildren = rItem.m_aChildren.size();
4193 :
4194 0 : if( nChildren )
4195 : {
4196 0 : for( int n = 0; n < nChildren; ++n )
4197 : {
4198 0 : PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ];
4199 :
4200 0 : rChild.m_nParentObject = rItem.m_nObject;
4201 0 : rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0;
4202 0 : rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0;
4203 : }
4204 :
4205 : }
4206 : }
4207 :
4208 : // calculate Count entries for all items
4209 0 : std::vector< sal_Int32 > aCounts( nItems );
4210 0 : updateOutlineItemCount( aCounts, 0, 0 );
4211 :
4212 : // emit hierarchy
4213 0 : for( i = 0; i < nItems; ++i )
4214 : {
4215 0 : PDFOutlineEntry& rItem = m_aOutline[i];
4216 0 : OStringBuffer aLine( 1024 );
4217 :
4218 0 : CHECK_RETURN( updateObject( rItem.m_nObject ) );
4219 0 : aLine.append( rItem.m_nObject );
4220 0 : aLine.append( " 0 obj\n" );
4221 0 : aLine.append( "<<" );
4222 : // number of visible children (all levels)
4223 0 : if( i > 0 || aCounts[0] > 0 )
4224 : {
4225 0 : aLine.append( "/Count " );
4226 0 : aLine.append( aCounts[i] );
4227 : }
4228 0 : if( ! rItem.m_aChildren.empty() )
4229 : {
4230 : // children list: First, Last
4231 0 : aLine.append( "/First " );
4232 0 : aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject );
4233 0 : aLine.append( " 0 R/Last " );
4234 0 : aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject );
4235 0 : aLine.append( " 0 R\n" );
4236 : }
4237 0 : if( i > 0 )
4238 : {
4239 : // Title, Dest, Parent, Prev, Next
4240 0 : aLine.append( "/Title" );
4241 0 : appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine );
4242 0 : aLine.append( "\n" );
4243 : // Dest is not required
4244 0 : if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() )
4245 : {
4246 0 : aLine.append( "/Dest" );
4247 0 : appendDest( rItem.m_nDestID, aLine );
4248 : }
4249 0 : aLine.append( "/Parent " );
4250 0 : aLine.append( rItem.m_nParentObject );
4251 0 : aLine.append( " 0 R" );
4252 0 : if( rItem.m_nPrevObject )
4253 : {
4254 0 : aLine.append( "/Prev " );
4255 0 : aLine.append( rItem.m_nPrevObject );
4256 0 : aLine.append( " 0 R" );
4257 : }
4258 0 : if( rItem.m_nNextObject )
4259 : {
4260 0 : aLine.append( "/Next " );
4261 0 : aLine.append( rItem.m_nNextObject );
4262 0 : aLine.append( " 0 R" );
4263 : }
4264 : }
4265 0 : aLine.append( ">>\nendobj\n\n" );
4266 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4267 0 : }
4268 :
4269 0 : return m_aOutline[0].m_nObject;
4270 : }
4271 :
4272 : #undef CHECK_RETURN
4273 : #define CHECK_RETURN( x ) if( !x ) return false
4274 :
4275 0 : bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer )
4276 : {
4277 0 : if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() )
4278 : {
4279 : #if OSL_DEBUG_LEVEL > 1
4280 : fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID );
4281 : #endif
4282 0 : return false;
4283 : }
4284 :
4285 0 : const PDFDest& rDest = m_aDests[ nDestID ];
4286 0 : const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
4287 :
4288 0 : rBuffer.append( '[' );
4289 0 : rBuffer.append( rDestPage.m_nPageObject );
4290 0 : rBuffer.append( " 0 R" );
4291 :
4292 0 : switch( rDest.m_eType )
4293 : {
4294 : case PDFWriter::XYZ:
4295 : default:
4296 0 : rBuffer.append( "/XYZ " );
4297 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4298 0 : rBuffer.append( ' ' );
4299 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4300 0 : rBuffer.append( " 0" );
4301 0 : break;
4302 : case PDFWriter::Fit:
4303 0 : rBuffer.append( "/Fit" );
4304 0 : break;
4305 : case PDFWriter::FitRectangle:
4306 0 : rBuffer.append( "/FitR " );
4307 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4308 0 : rBuffer.append( ' ' );
4309 0 : appendFixedInt( rDest.m_aRect.Top(), rBuffer );
4310 0 : rBuffer.append( ' ' );
4311 0 : appendFixedInt( rDest.m_aRect.Right(), rBuffer );
4312 0 : rBuffer.append( ' ' );
4313 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4314 0 : break;
4315 : case PDFWriter::FitHorizontal:
4316 0 : rBuffer.append( "/FitH " );
4317 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4318 0 : break;
4319 : case PDFWriter::FitVertical:
4320 0 : rBuffer.append( "/FitV " );
4321 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4322 0 : break;
4323 : case PDFWriter::FitPageBoundingBox:
4324 0 : rBuffer.append( "/FitB" );
4325 0 : break;
4326 : case PDFWriter::FitPageBoundingBoxHorizontal:
4327 0 : rBuffer.append( "/FitBH " );
4328 0 : appendFixedInt( rDest.m_aRect.Bottom(), rBuffer );
4329 0 : break;
4330 : case PDFWriter::FitPageBoundingBoxVertical:
4331 0 : rBuffer.append( "/FitBV " );
4332 0 : appendFixedInt( rDest.m_aRect.Left(), rBuffer );
4333 0 : break;
4334 : }
4335 0 : rBuffer.append( ']' );
4336 :
4337 0 : return true;
4338 : }
4339 :
4340 0 : bool PDFWriterImpl::emitLinkAnnotations()
4341 : {
4342 0 : int nAnnots = m_aLinks.size();
4343 0 : for( int i = 0; i < nAnnots; i++ )
4344 : {
4345 0 : const PDFLink& rLink = m_aLinks[i];
4346 0 : if( ! updateObject( rLink.m_nObject ) )
4347 0 : continue;
4348 :
4349 0 : OStringBuffer aLine( 1024 );
4350 0 : aLine.append( rLink.m_nObject );
4351 0 : aLine.append( " 0 obj\n" );
4352 : //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4353 : // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4354 0 : aLine.append( "<</Type/Annot" );
4355 0 : if( m_bIsPDF_A1 )
4356 0 : aLine.append( "/F 4" );
4357 0 : aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" );
4358 :
4359 0 : appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle
4360 0 : aLine.append( ' ' );
4361 0 : appendFixedInt( rLink.m_aRect.Top(), aLine );
4362 0 : aLine.append( ' ' );
4363 0 : appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle
4364 0 : aLine.append( ' ' );
4365 0 : appendFixedInt( rLink.m_aRect.Bottom(), aLine );
4366 0 : aLine.append( "]" );
4367 0 : if( rLink.m_nDest >= 0 )
4368 : {
4369 0 : aLine.append( "/Dest" );
4370 0 : appendDest( rLink.m_nDest, aLine );
4371 : }
4372 : else
4373 : {
4374 : /*--->i56629
4375 : destination is external to the document, so
4376 : we check in the following sequence:
4377 :
4378 : if target type is neither .pdf, nor .od[tpgs], then
4379 : check if relative or absolute and act accordingly (use URI or 'launch application' as requested)
4380 : end processing
4381 : else if target is .od[tpgs]: then
4382 : if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file
4383 : processing continue
4384 :
4385 : if (new)target is .pdf : then
4386 : if GotToR is requested, then
4387 : convert the target in GoToR where the fragment of the URI is
4388 : considered the named destination in the target file, set relative or absolute as requested
4389 : else strip the fragment from URL and then set URI or 'launch application' as requested
4390 : */
4391 :
4392 : // FIXME: check if the decode mechanisms for URL processing throughout this implementation
4393 : // are the correct one!!
4394 :
4395 : // extract target file type
4396 0 : INetURLObject aDocumentURL( m_aContext.BaseURL );
4397 0 : INetURLObject aTargetURL( rLink.m_aURL );
4398 0 : sal_Int32 nSetGoToRMode = 0;
4399 0 : bool bTargetHasPDFExtension = false;
4400 0 : INetProtocol eTargetProtocol = aTargetURL.GetProtocol();
4401 0 : bool bIsUNCPath = false;
4402 :
4403 : // check if the protocol is a known one, or if there is no protocol at all (on target only)
4404 : // if there is no protocol, make the target relative to the current document directory
4405 : // getting the needed URL information from the current document path
4406 0 : if( eTargetProtocol == INET_PROT_NOT_VALID )
4407 : {
4408 0 : if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.startsWith("\\\\\\\\"))
4409 : {
4410 0 : bIsUNCPath = true;
4411 : }
4412 : else
4413 : {
4414 0 : INetURLObject aNewBase( aDocumentURL );//duplicate document URL
4415 0 : aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the
4416 : //target document
4417 0 : aNewBase.insertName( rLink.m_aURL );
4418 0 : aTargetURL = aNewBase;//reassign the new target URL
4419 : //recompute the target protocol, with the new URL
4420 : //normal URL processing resumes
4421 0 : eTargetProtocol = aTargetURL.GetProtocol();
4422 : }
4423 : }
4424 :
4425 0 : OUString aFileExtension = aTargetURL.GetFileExtension();
4426 :
4427 : // Check if the URL ends in '/': if yes it's a directory,
4428 : // it will be forced to a URI link.
4429 : // possibly a malformed URI, leave it as it is, force as URI
4430 0 : if( aTargetURL.hasFinalSlash() )
4431 0 : m_aContext.DefaultLinkAction = PDFWriter::URIAction;
4432 :
4433 0 : if( !aFileExtension.isEmpty() )
4434 : {
4435 0 : if( m_aContext.ConvertOOoTargetToPDFTarget )
4436 : {
4437 0 : bool bChangeFileExtensionToPDF = false;
4438 : //examine the file type (.odm .odt. .odp, odg, ods)
4439 0 : if( aFileExtension.equalsIgnoreAsciiCase( "odm" ) )
4440 0 : bChangeFileExtensionToPDF = true;
4441 0 : if( aFileExtension.equalsIgnoreAsciiCase( "odt" ) )
4442 0 : bChangeFileExtensionToPDF = true;
4443 0 : else if( aFileExtension.equalsIgnoreAsciiCase( "odp" ) )
4444 0 : bChangeFileExtensionToPDF = true;
4445 0 : else if( aFileExtension.equalsIgnoreAsciiCase( "odg" ) )
4446 0 : bChangeFileExtensionToPDF = true;
4447 0 : else if( aFileExtension.equalsIgnoreAsciiCase( "ods" ) )
4448 0 : bChangeFileExtensionToPDF = true;
4449 0 : if( bChangeFileExtensionToPDF )
4450 0 : aTargetURL.setExtension(OUString( "pdf" ) );
4451 : }
4452 : //check if extension is pdf, see if GoToR should be forced
4453 0 : bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase( "pdf" );
4454 0 : if( m_aContext.ForcePDFAction && bTargetHasPDFExtension )
4455 0 : nSetGoToRMode++;
4456 : }
4457 : //prepare the URL, if relative or not
4458 0 : INetProtocol eBaseProtocol = aDocumentURL.GetProtocol();
4459 : //queue the string common to all types of actions
4460 0 : aLine.append( "/A<</Type/Action/S");
4461 0 : if( bIsUNCPath ) // handle Win UNC paths
4462 : {
4463 0 : aLine.append( "/Launch/Win<</F" );
4464 : // INetURLObject is not good with UNC paths, use original path
4465 0 : appendLiteralStringEncrypt( rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4466 0 : aLine.append( ">>" );
4467 : }
4468 : else
4469 : {
4470 0 : bool bSetRelative = false;
4471 0 : bool bFileSpec = false;
4472 : //check if relative file link is requested and if the protocol is 'file://'
4473 0 : if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE )
4474 0 : bSetRelative = true;
4475 :
4476 0 : OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is,
4477 0 : if( nSetGoToRMode == 0 )
4478 : {
4479 0 : switch( m_aContext.DefaultLinkAction )
4480 : {
4481 : default:
4482 : case PDFWriter::URIAction :
4483 : case PDFWriter::URIActionDestination :
4484 0 : aLine.append( "/URI/URI" );
4485 0 : break;
4486 : case PDFWriter::LaunchAction:
4487 : // now:
4488 : // if a launch action is requested and the hyperlink target has a fragment
4489 : // and the target file does not have a pdf extension, or it's not a 'file:://'
4490 : // protocol then force the uri action on it
4491 : // This code will permit the correct opening of application on web pages,
4492 : // the one that normally have fragments (but I may be wrong...)
4493 : // and will force the use of URI when the protocol is not file:
4494 0 : if( (!aFragment.isEmpty() && !bTargetHasPDFExtension) ||
4495 : eTargetProtocol != INET_PROT_FILE )
4496 : {
4497 0 : aLine.append( "/URI/URI" );
4498 : }
4499 : else
4500 : {
4501 0 : aLine.append( "/Launch/F" );
4502 0 : bFileSpec = true;
4503 : }
4504 0 : break;
4505 : }
4506 : }
4507 :
4508 : //fragment are encoded in the same way as in the named destination processing
4509 0 : if( nSetGoToRMode )
4510 : {
4511 : //add the fragment
4512 0 : OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET );
4513 0 : aLine.append("/GoToR");
4514 0 : aLine.append("/F");
4515 0 : bFileSpec = true;
4516 : appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark,
4517 : INetURLObject::WAS_ENCODED,
4518 : INetURLObject::DECODE_WITH_CHARSET ) :
4519 0 : aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4520 0 : if( !aFragment.isEmpty() )
4521 : {
4522 0 : aLine.append("/D/");
4523 0 : appendDestinationName( aFragment , aLine );
4524 0 : }
4525 : }
4526 : else
4527 : {
4528 : // change the fragment to accommodate the bookmark (only if the file extension
4529 : // is PDF and the requested action is of the correct type)
4530 0 : if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination &&
4531 0 : bTargetHasPDFExtension && !aFragment.isEmpty() )
4532 : {
4533 0 : OStringBuffer aLineLoc( 1024 );
4534 0 : appendDestinationName( aFragment , aLineLoc );
4535 : //substitute the fragment
4536 0 : aTargetURL.SetMark( OStringToOUString(aLineLoc.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US) );
4537 : }
4538 0 : OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE );
4539 : appendLiteralStringEncrypt(bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL,
4540 : INetURLObject::WAS_ENCODED,
4541 : bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE
4542 : ) :
4543 0 : aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() );
4544 0 : }
4545 : //<--- i56629
4546 : }
4547 0 : aLine.append( ">>\n" );
4548 : }
4549 0 : if( rLink.m_nStructParent > 0 )
4550 : {
4551 0 : aLine.append( "/StructParent " );
4552 0 : aLine.append( rLink.m_nStructParent );
4553 : }
4554 0 : aLine.append( ">>\nendobj\n\n" );
4555 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4556 0 : }
4557 :
4558 0 : return true;
4559 : }
4560 :
4561 0 : bool PDFWriterImpl::emitNoteAnnotations()
4562 : {
4563 : // emit note annotations
4564 0 : int nAnnots = m_aNotes.size();
4565 0 : for( int i = 0; i < nAnnots; i++ )
4566 : {
4567 0 : const PDFNoteEntry& rNote = m_aNotes[i];
4568 0 : if( ! updateObject( rNote.m_nObject ) )
4569 0 : return false;
4570 :
4571 0 : OStringBuffer aLine( 1024 );
4572 0 : aLine.append( rNote.m_nObject );
4573 0 : aLine.append( " 0 obj\n" );
4574 : //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should'
4575 : // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3
4576 0 : aLine.append( "<</Type/Annot" );
4577 0 : if( m_bIsPDF_A1 )
4578 0 : aLine.append( "/F 4" );
4579 0 : aLine.append( "/Subtype/Text/Rect[" );
4580 :
4581 0 : appendFixedInt( rNote.m_aRect.Left(), aLine );
4582 0 : aLine.append( ' ' );
4583 0 : appendFixedInt( rNote.m_aRect.Top(), aLine );
4584 0 : aLine.append( ' ' );
4585 0 : appendFixedInt( rNote.m_aRect.Right(), aLine );
4586 0 : aLine.append( ' ' );
4587 0 : appendFixedInt( rNote.m_aRect.Bottom(), aLine );
4588 0 : aLine.append( "]" );
4589 :
4590 : // contents of the note (type text string)
4591 0 : aLine.append( "/Contents\n" );
4592 0 : appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine );
4593 0 : aLine.append( "\n" );
4594 :
4595 : // optional title
4596 0 : if( !rNote.m_aContents.Title.isEmpty() )
4597 : {
4598 0 : aLine.append( "/T" );
4599 0 : appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine );
4600 0 : aLine.append( "\n" );
4601 : }
4602 :
4603 0 : aLine.append( ">>\nendobj\n\n" );
4604 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
4605 0 : }
4606 0 : return true;
4607 : }
4608 :
4609 0 : Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font& rAppSetFont )
4610 : {
4611 0 : bool bAdjustSize = false;
4612 :
4613 0 : Font aFont( rControlFont );
4614 0 : if( aFont.GetName().isEmpty() )
4615 : {
4616 0 : aFont = rAppSetFont;
4617 0 : if( rControlFont.GetHeight() )
4618 0 : aFont.SetSize( Size( 0, rControlFont.GetHeight() ) );
4619 : else
4620 0 : bAdjustSize = true;
4621 0 : if( rControlFont.GetItalic() != ITALIC_DONTKNOW )
4622 0 : aFont.SetItalic( rControlFont.GetItalic() );
4623 0 : if( rControlFont.GetWeight() != WEIGHT_DONTKNOW )
4624 0 : aFont.SetWeight( rControlFont.GetWeight() );
4625 : }
4626 0 : else if( ! aFont.GetHeight() )
4627 : {
4628 0 : aFont.SetSize( rAppSetFont.GetSize() );
4629 0 : bAdjustSize = true;
4630 : }
4631 0 : if( bAdjustSize )
4632 : {
4633 0 : Size aFontSize = aFont.GetSize();
4634 0 : OutputDevice* pDefDev = Application::GetDefaultDevice();
4635 0 : aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() );
4636 0 : aFont.SetSize( aFontSize );
4637 : }
4638 0 : return aFont;
4639 : }
4640 :
4641 0 : sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont )
4642 : {
4643 0 : sal_Int32 nBest = 4; // default to Helvetica
4644 0 : OUString aFontName( rFont.GetName() );
4645 0 : aFontName = aFontName.toAsciiLowerCase();
4646 :
4647 0 : if( aFontName.indexOf( "times" ) != -1 )
4648 0 : nBest = 8;
4649 0 : else if( aFontName.indexOf( "courier" ) != -1 )
4650 0 : nBest = 0;
4651 0 : else if( aFontName.indexOf( "dingbats" ) != -1 )
4652 0 : nBest = 13;
4653 0 : else if( aFontName.indexOf( "symbol" ) != -1 )
4654 0 : nBest = 12;
4655 0 : if( nBest < 12 )
4656 : {
4657 0 : if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL )
4658 0 : nBest += 1;
4659 0 : if( rFont.GetWeight() > WEIGHT_MEDIUM )
4660 0 : nBest += 2;
4661 : }
4662 :
4663 0 : if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() )
4664 0 : m_aBuiltinFontToObjectMap[ nBest ] = createObject();
4665 :
4666 0 : return nBest;
4667 : }
4668 :
4669 0 : static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 )
4670 : {
4671 0 : return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1;
4672 : }
4673 :
4674 0 : void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget )
4675 : {
4676 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4677 :
4678 : // save graphics state
4679 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
4680 :
4681 : // transform relative to control's coordinates since an
4682 : // appearance stream is a form XObject
4683 : // this relies on the m_aRect member of rButton NOT already being transformed
4684 : // to default user space
4685 0 : if( rWidget.Background || rWidget.Border )
4686 : {
4687 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) );
4688 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) );
4689 0 : drawRectangle( rWidget.Location );
4690 : }
4691 : // prepare font to use
4692 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() );
4693 0 : setFont( aFont );
4694 0 : setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) );
4695 :
4696 0 : drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle );
4697 :
4698 : // create DA string while local mapmode is still in place
4699 : // (that is before endRedirect())
4700 0 : OStringBuffer aDA( 256 );
4701 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA );
4702 0 : Font aDummyFont( OUString( "Helvetica" ), aFont.GetSize() );
4703 0 : sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont );
4704 0 : aDA.append( ' ' );
4705 0 : aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() );
4706 0 : aDA.append( ' ' );
4707 0 : m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4708 0 : aDA.append( " Tf" );
4709 0 : rButton.m_aDAString = aDA.makeStringAndClear();
4710 :
4711 0 : pop();
4712 :
4713 0 : rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
4714 :
4715 : /* seems like a bad hack but at least works in both AR5 and 6:
4716 : we draw the button ourselves and tell AR
4717 : the button would be totally transparent with no text
4718 :
4719 : One would expect that simply setting a normal appearance
4720 : should suffice, but no, as soon as the user actually presses
4721 : the button and an action is tied to it (gasp! a button that
4722 : does something) the appearance gets replaced by some crap that AR
4723 : creates on the fly even if no DA or MK is given. On AR6 at least
4724 : the DA and MK work as expected, but on AR5 this creates a region
4725 : filled with the background color but nor text. Urgh.
4726 : */
4727 0 : rButton.m_aMKDict = "/BC [] /BG [] /CA";
4728 0 : rButton.m_aMKDictCAString = "";
4729 0 : }
4730 :
4731 0 : Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern,
4732 : const PDFWriter::AnyWidget& rWidget,
4733 : const StyleSettings& rSettings )
4734 : {
4735 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() );
4736 :
4737 0 : if( rWidget.Background || rWidget.Border )
4738 : {
4739 0 : if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) )
4740 : {
4741 0 : sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500;
4742 0 : if( nDelta < 1 )
4743 0 : nDelta = 1;
4744 0 : setLineColor( Color( COL_TRANSPARENT ) );
4745 0 : Rectangle aRect = rIntern.m_aRect;
4746 0 : setFillColor( rSettings.GetLightBorderColor() );
4747 0 : drawRectangle( aRect );
4748 0 : aRect.Left() += nDelta; aRect.Top() += nDelta;
4749 0 : aRect.Right() -= nDelta; aRect.Bottom() -= nDelta;
4750 0 : setFillColor( rSettings.GetFieldColor() );
4751 0 : drawRectangle( aRect );
4752 0 : setFillColor( rSettings.GetLightColor() );
4753 0 : drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) );
4754 0 : drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) );
4755 0 : setFillColor( rSettings.GetDarkShadowColor() );
4756 0 : drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) );
4757 0 : drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) );
4758 : }
4759 : else
4760 : {
4761 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) );
4762 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4763 0 : drawRectangle( rIntern.m_aRect );
4764 : }
4765 :
4766 0 : if( rWidget.Border )
4767 : {
4768 : // adjust edit area accounting for border
4769 0 : sal_Int32 nDelta = aFont.GetHeight()/4;
4770 0 : if( nDelta < 1 )
4771 0 : nDelta = 1;
4772 0 : rIntern.m_aRect.Left() += nDelta;
4773 0 : rIntern.m_aRect.Top() += nDelta;
4774 0 : rIntern.m_aRect.Right() -= nDelta;
4775 0 : rIntern.m_aRect.Bottom()-= nDelta;
4776 : }
4777 : }
4778 0 : return aFont;
4779 : }
4780 :
4781 0 : void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget )
4782 : {
4783 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4784 0 : SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 );
4785 :
4786 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
4787 :
4788 : // prepare font to use, draw field border
4789 0 : Font aFont = drawFieldBorder( rEdit, rWidget, rSettings );
4790 0 : sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
4791 :
4792 : // prepare DA string
4793 0 : OStringBuffer aDA( 32 );
4794 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4795 0 : aDA.append( ' ' );
4796 0 : if( m_aContext.FieldsUseSystemFonts )
4797 : {
4798 0 : aDA.append( "/F" );
4799 0 : aDA.append( nBest );
4800 :
4801 0 : OStringBuffer aDR( 32 );
4802 0 : aDR.append( "/Font " );
4803 0 : aDR.append( getFontDictObject() );
4804 0 : aDR.append( " 0 R" );
4805 0 : rEdit.m_aDRDict = aDR.makeStringAndClear();
4806 : }
4807 : else
4808 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4809 0 : aDA.append( ' ' );
4810 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4811 0 : aDA.append( " Tf" );
4812 :
4813 : /* create an empty appearance stream, let the viewer create
4814 : the appearance at runtime. This is because AR5 seems to
4815 : paint the widget appearance always, and a dynamically created
4816 : appearance on top of it. AR6 is well behaved in that regard, so
4817 : that behaviour seems to be a bug. Anyway this empty appearance
4818 : relies on /NeedAppearances in the AcroForm dictionary set to "true"
4819 : */
4820 0 : beginRedirect( pEditStream, rEdit.m_aRect );
4821 0 : OStringBuffer aAppearance( 32 );
4822 0 : aAppearance.append( "/Tx BMC\nEMC\n" );
4823 0 : writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4824 :
4825 0 : endRedirect();
4826 0 : pop();
4827 :
4828 0 : rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream;
4829 :
4830 0 : rEdit.m_aDAString = aDA.makeStringAndClear();
4831 0 : }
4832 :
4833 0 : void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget )
4834 : {
4835 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4836 0 : SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 );
4837 :
4838 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
4839 :
4840 : // prepare font to use, draw field border
4841 0 : Font aFont = drawFieldBorder( rBox, rWidget, rSettings );
4842 0 : sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont );
4843 :
4844 0 : beginRedirect( pListBoxStream, rBox.m_aRect );
4845 0 : OStringBuffer aAppearance( 64 );
4846 :
4847 0 : setLineColor( Color( COL_TRANSPARENT ) );
4848 0 : setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) );
4849 0 : drawRectangle( rBox.m_aRect );
4850 :
4851 : // empty appearance, see createDefaultEditAppearance for reference
4852 0 : aAppearance.append( "/Tx BMC\nEMC\n" );
4853 0 : writeBuffer( aAppearance.getStr(), aAppearance.getLength() );
4854 :
4855 0 : endRedirect();
4856 0 : pop();
4857 :
4858 0 : rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream;
4859 :
4860 : // prepare DA string
4861 0 : OStringBuffer aDA( 256 );
4862 : // prepare DA string
4863 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA );
4864 0 : aDA.append( ' ' );
4865 0 : if( m_aContext.FieldsUseSystemFonts )
4866 : {
4867 0 : aDA.append( "/F" );
4868 0 : aDA.append( nBest );
4869 :
4870 0 : OStringBuffer aDR( 32 );
4871 0 : aDR.append( "/Font " );
4872 0 : aDR.append( getFontDictObject() );
4873 0 : aDR.append( " 0 R" );
4874 0 : rBox.m_aDRDict = aDR.makeStringAndClear();
4875 : }
4876 : else
4877 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4878 0 : aDA.append( ' ' );
4879 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA );
4880 0 : aDA.append( " Tf" );
4881 0 : rBox.m_aDAString = aDA.makeStringAndClear();
4882 0 : }
4883 :
4884 0 : void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget )
4885 : {
4886 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
4887 :
4888 : // save graphics state
4889 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
4890 :
4891 0 : if( rWidget.Background || rWidget.Border )
4892 : {
4893 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
4894 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
4895 0 : drawRectangle( rBox.m_aRect );
4896 : }
4897 :
4898 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
4899 0 : setFont( aFont );
4900 0 : Size aFontSize = aFont.GetSize();
4901 0 : if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
4902 0 : aFontSize.Height() = rBox.m_aRect.GetHeight();
4903 0 : sal_Int32 nDelta = aFontSize.Height()/10;
4904 0 : if( nDelta < 1 )
4905 0 : nDelta = 1;
4906 :
4907 0 : Rectangle aCheckRect, aTextRect;
4908 0 : if( rWidget.ButtonIsLeft )
4909 : {
4910 0 : aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
4911 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4912 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
4913 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4914 :
4915 : // #i74206# handle small controls without text area
4916 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4917 : {
4918 0 : aCheckRect.Right() -= nDelta;
4919 0 : aCheckRect.Top() += nDelta/2;
4920 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
4921 : }
4922 :
4923 0 : aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
4924 0 : aTextRect.Top() = rBox.m_aRect.Top();
4925 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4926 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
4927 : }
4928 : else
4929 : {
4930 0 : aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
4931 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
4932 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
4933 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
4934 :
4935 : // #i74206# handle small controls without text area
4936 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
4937 : {
4938 0 : aCheckRect.Left() += nDelta;
4939 0 : aCheckRect.Top() += nDelta/2;
4940 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
4941 : }
4942 :
4943 0 : aTextRect.Left() = rBox.m_aRect.Left();
4944 0 : aTextRect.Top() = rBox.m_aRect.Top();
4945 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
4946 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
4947 : }
4948 0 : setLineColor( Color( COL_BLACK ) );
4949 0 : setFillColor( Color( COL_TRANSPARENT ) );
4950 0 : OStringBuffer aLW( 32 );
4951 0 : aLW.append( "q " );
4952 0 : m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW );
4953 0 : aLW.append( " w " );
4954 0 : writeBuffer( aLW.getStr(), aLW.getLength() );
4955 0 : drawRectangle( aCheckRect );
4956 0 : writeBuffer( " Q\n", 3 );
4957 0 : setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
4958 0 : drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
4959 :
4960 0 : pop();
4961 :
4962 0 : OStringBuffer aDA( 256 );
4963 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4964 0 : sal_Int32 nBest = getBestBuiltinFont( Font( OUString( "ZapfDingbats" ), aFont.GetSize() ) );
4965 0 : aDA.append( ' ' );
4966 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4967 0 : aDA.append( " 0 Tf" );
4968 0 : rBox.m_aDAString = aDA.makeStringAndClear();
4969 0 : rBox.m_aMKDict = "/CA";
4970 0 : rBox.m_aMKDictCAString = "8";
4971 0 : rBox.m_aRect = aCheckRect;
4972 :
4973 : // create appearance streams
4974 0 : sal_Char cMark = '8';
4975 0 : sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)];
4976 0 : nCharXOffset *= aCheckRect.GetHeight();
4977 0 : nCharXOffset /= 2000;
4978 : sal_Int32 nCharYOffset = 1000-
4979 0 : (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative
4980 0 : nCharYOffset *= aCheckRect.GetHeight();
4981 0 : nCharYOffset /= 2000;
4982 :
4983 0 : SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
4984 0 : beginRedirect( pCheckStream, aCheckRect );
4985 0 : aDA.append( "/Tx BMC\nq BT\n" );
4986 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
4987 0 : aDA.append( ' ' );
4988 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
4989 0 : aDA.append( ' ' );
4990 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
4991 0 : aDA.append( " Tf\n" );
4992 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA );
4993 0 : aDA.append( " " );
4994 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA );
4995 0 : aDA.append( " Td (" );
4996 0 : aDA.append( cMark );
4997 0 : aDA.append( ") Tj\nET\nQ\nEMC\n" );
4998 0 : writeBuffer( aDA.getStr(), aDA.getLength() );
4999 0 : endRedirect();
5000 0 : rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5001 :
5002 0 : SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5003 0 : beginRedirect( pUncheckStream, aCheckRect );
5004 0 : writeBuffer( "/Tx BMC\nEMC\n", 12 );
5005 0 : endRedirect();
5006 0 : rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5007 0 : }
5008 :
5009 0 : void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget )
5010 : {
5011 0 : const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
5012 :
5013 : // save graphics state
5014 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
5015 :
5016 0 : if( rWidget.Background || rWidget.Border )
5017 : {
5018 0 : setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) );
5019 0 : setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) );
5020 0 : drawRectangle( rBox.m_aRect );
5021 : }
5022 :
5023 0 : Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() );
5024 0 : setFont( aFont );
5025 0 : Size aFontSize = aFont.GetSize();
5026 0 : if( aFontSize.Height() > rBox.m_aRect.GetHeight() )
5027 0 : aFontSize.Height() = rBox.m_aRect.GetHeight();
5028 0 : sal_Int32 nDelta = aFontSize.Height()/10;
5029 0 : if( nDelta < 1 )
5030 0 : nDelta = 1;
5031 :
5032 0 : Rectangle aCheckRect, aTextRect;
5033 0 : if( rWidget.ButtonIsLeft )
5034 : {
5035 0 : aCheckRect.Left() = rBox.m_aRect.Left() + nDelta;
5036 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5037 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5038 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5039 :
5040 : // #i74206# handle small controls without text area
5041 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5042 : {
5043 0 : aCheckRect.Right() -= nDelta;
5044 0 : aCheckRect.Top() += nDelta/2;
5045 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
5046 : }
5047 :
5048 0 : aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta;
5049 0 : aTextRect.Top() = rBox.m_aRect.Top();
5050 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5051 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
5052 : }
5053 : else
5054 : {
5055 0 : aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height();
5056 0 : aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2;
5057 0 : aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height();
5058 0 : aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height();
5059 :
5060 : // #i74206# handle small controls without text area
5061 0 : while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta )
5062 : {
5063 0 : aCheckRect.Left() += nDelta;
5064 0 : aCheckRect.Top() += nDelta/2;
5065 0 : aCheckRect.Bottom() -= nDelta - (nDelta/2);
5066 : }
5067 :
5068 0 : aTextRect.Left() = rBox.m_aRect.Left();
5069 0 : aTextRect.Top() = rBox.m_aRect.Top();
5070 0 : aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta;
5071 0 : aTextRect.Bottom() = rBox.m_aRect.Bottom();
5072 : }
5073 0 : setLineColor( Color( COL_BLACK ) );
5074 0 : setFillColor( Color( COL_TRANSPARENT ) );
5075 0 : OStringBuffer aLW( 32 );
5076 0 : aLW.append( "q " );
5077 0 : m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW );
5078 0 : aLW.append( " w " );
5079 0 : writeBuffer( aLW.getStr(), aLW.getLength() );
5080 0 : drawEllipse( aCheckRect );
5081 0 : writeBuffer( " Q\n", 3 );
5082 0 : setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5083 0 : drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle );
5084 :
5085 0 : pop();
5086 :
5087 0 : OStringBuffer aDA( 256 );
5088 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5089 0 : sal_Int32 nBest = getBestBuiltinFont( Font( OUString( "ZapfDingbats" ), aFont.GetSize() ) );
5090 0 : aDA.append( ' ' );
5091 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5092 0 : aDA.append( " 0 Tf" );
5093 0 : rBox.m_aDAString = aDA.makeStringAndClear();
5094 : //to encrypt this (el)
5095 0 : rBox.m_aMKDict = "/CA";
5096 : //after this assignement, to m_aMKDic cannot be added anything
5097 0 : rBox.m_aMKDictCAString = "l";
5098 :
5099 0 : rBox.m_aRect = aCheckRect;
5100 :
5101 : // create appearance streams
5102 0 : push( sal::static_int_cast<sal_uInt16>(~0U) );
5103 0 : SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 );
5104 :
5105 0 : beginRedirect( pCheckStream, aCheckRect );
5106 0 : aDA.append( "/Tx BMC\nq BT\n" );
5107 0 : appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
5108 0 : aDA.append( ' ' );
5109 0 : aDA.append( m_aBuiltinFonts[nBest].getNameObject() );
5110 0 : aDA.append( ' ' );
5111 0 : m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA );
5112 0 : aDA.append( " Tf\n0 0 Td\nET\nQ\n" );
5113 0 : writeBuffer( aDA.getStr(), aDA.getLength() );
5114 0 : setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) );
5115 0 : setLineColor( Color( COL_TRANSPARENT ) );
5116 0 : aCheckRect.Left() += 3*nDelta;
5117 0 : aCheckRect.Top() += 3*nDelta;
5118 0 : aCheckRect.Bottom() -= 3*nDelta;
5119 0 : aCheckRect.Right() -= 3*nDelta;
5120 0 : drawEllipse( aCheckRect );
5121 0 : writeBuffer( "\nEMC\n", 5 );
5122 0 : endRedirect();
5123 :
5124 0 : pop();
5125 0 : rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream;
5126 :
5127 0 : SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 );
5128 0 : beginRedirect( pUncheckStream, aCheckRect );
5129 0 : writeBuffer( "/Tx BMC\nEMC\n", 12 );
5130 0 : endRedirect();
5131 0 : rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream;
5132 0 : }
5133 :
5134 0 : bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict )
5135 : {
5136 : // TODO: check and insert default streams
5137 0 : OString aStandardAppearance;
5138 0 : switch( rWidget.m_eType )
5139 : {
5140 : case PDFWriter::CheckBox:
5141 0 : aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US );
5142 0 : break;
5143 : default:
5144 0 : break;
5145 : }
5146 :
5147 0 : if( rWidget.m_aAppearances.size() )
5148 : {
5149 0 : rAnnotDict.append( "/AP<<\n" );
5150 0 : for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it )
5151 : {
5152 0 : rAnnotDict.append( "/" );
5153 0 : rAnnotDict.append( dict_it->first );
5154 0 : bool bUseSubDict = (dict_it->second.size() > 1);
5155 0 : rAnnotDict.append( bUseSubDict ? "<<" : " " );
5156 :
5157 0 : for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin();
5158 0 : stream_it != dict_it->second.end(); ++stream_it )
5159 : {
5160 0 : SvMemoryStream* pApppearanceStream = stream_it->second;
5161 0 : dict_it->second[ stream_it->first ] = NULL;
5162 :
5163 0 : bool bDeflate = compressStream( pApppearanceStream );
5164 :
5165 0 : pApppearanceStream->Seek( STREAM_SEEK_TO_END );
5166 0 : sal_Int64 nStreamLen = pApppearanceStream->Tell();
5167 0 : pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN );
5168 0 : sal_Int32 nObject = createObject();
5169 0 : CHECK_RETURN( updateObject( nObject ) );
5170 : #if OSL_DEBUG_LEVEL > 1
5171 : emitComment( "PDFWriterImpl::emitAppearances" );
5172 : #endif
5173 0 : OStringBuffer aLine;
5174 0 : aLine.append( nObject );
5175 :
5176 : aLine.append( " 0 obj\n"
5177 : "<</Type/XObject\n"
5178 : "/Subtype/Form\n"
5179 0 : "/BBox[0 0 " );
5180 0 : appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine );
5181 0 : aLine.append( " " );
5182 0 : appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine );
5183 : aLine.append( "]\n"
5184 0 : "/Resources " );
5185 0 : aLine.append( getResourceDictObj() );
5186 : aLine.append( " 0 R\n"
5187 0 : "/Length " );
5188 0 : aLine.append( nStreamLen );
5189 0 : aLine.append( "\n" );
5190 0 : if( bDeflate )
5191 0 : aLine.append( "/Filter/FlateDecode\n" );
5192 0 : aLine.append( ">>\nstream\n" );
5193 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5194 0 : checkAndEnableStreamEncryption( nObject );
5195 0 : CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) );
5196 0 : disableStreamEncryption();
5197 0 : CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) );
5198 :
5199 0 : if( bUseSubDict )
5200 : {
5201 0 : rAnnotDict.append( " /" );
5202 0 : rAnnotDict.append( stream_it->first );
5203 0 : rAnnotDict.append( " " );
5204 : }
5205 0 : rAnnotDict.append( nObject );
5206 0 : rAnnotDict.append( " 0 R" );
5207 :
5208 0 : delete pApppearanceStream;
5209 0 : }
5210 :
5211 0 : rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" );
5212 : }
5213 0 : rAnnotDict.append( ">>\n" );
5214 0 : if( !aStandardAppearance.isEmpty() )
5215 : {
5216 0 : rAnnotDict.append( "/AS /" );
5217 0 : rAnnotDict.append( aStandardAppearance );
5218 0 : rAnnotDict.append( "\n" );
5219 : }
5220 : }
5221 :
5222 0 : return true;
5223 : }
5224 :
5225 0 : bool PDFWriterImpl::emitWidgetAnnotations()
5226 : {
5227 0 : ensureUniqueRadioOnValues();
5228 :
5229 0 : int nAnnots = m_aWidgets.size();
5230 0 : for( int a = 0; a < nAnnots; a++ )
5231 : {
5232 0 : PDFWidget& rWidget = m_aWidgets[a];
5233 :
5234 0 : OStringBuffer aLine( 1024 );
5235 0 : OStringBuffer aValue( 256 );
5236 0 : aLine.append( rWidget.m_nObject );
5237 : aLine.append( " 0 obj\n"
5238 0 : "<<" );
5239 0 : if( rWidget.m_eType != PDFWriter::Hierarchy )
5240 : {
5241 : // emit widget annotation only for terminal fields
5242 0 : if( rWidget.m_aKids.empty() )
5243 : {
5244 : int iRectMargin;
5245 :
5246 0 : aLine.append( "/Type/Annot/Subtype/Widget/F " );
5247 :
5248 0 : if (rWidget.m_eType == PDFWriter::Signature)
5249 : {
5250 0 : aLine.append( "132\n" ); // Print & Locked
5251 0 : iRectMargin = 0;
5252 : }
5253 : else
5254 : {
5255 0 : aLine.append( "4\n" );
5256 0 : iRectMargin = 1;
5257 : }
5258 :
5259 0 : aLine.append("/Rect[" );
5260 0 : appendFixedInt( rWidget.m_aRect.Left()-iRectMargin, aLine );
5261 0 : aLine.append( ' ' );
5262 0 : appendFixedInt( rWidget.m_aRect.Top()+iRectMargin, aLine );
5263 0 : aLine.append( ' ' );
5264 0 : appendFixedInt( rWidget.m_aRect.Right()+iRectMargin, aLine );
5265 0 : aLine.append( ' ' );
5266 0 : appendFixedInt( rWidget.m_aRect.Bottom()-iRectMargin, aLine );
5267 0 : aLine.append( "]\n" );
5268 : }
5269 0 : aLine.append( "/FT/" );
5270 0 : switch( rWidget.m_eType )
5271 : {
5272 : case PDFWriter::RadioButton:
5273 : case PDFWriter::CheckBox:
5274 : // for radio buttons only the RadioButton field, not the
5275 : // CheckBox children should have a value, else acrobat reader
5276 : // does not always check the right button
5277 : // of course real check boxes (not belonging to a radio group)
5278 : // need their values, too
5279 0 : if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 )
5280 : {
5281 0 : aValue.append( "/" );
5282 : // check for radio group with all buttons unpressed
5283 0 : if( rWidget.m_aValue.isEmpty() )
5284 0 : aValue.append( "Off" );
5285 : else
5286 0 : appendName( rWidget.m_aValue, aValue );
5287 : }
5288 : // fall-through
5289 : case PDFWriter::PushButton:
5290 0 : aLine.append( "Btn" );
5291 0 : break;
5292 : case PDFWriter::ListBox:
5293 0 : if( rWidget.m_nFlags & 0x200000 ) // multiselect
5294 : {
5295 0 : aValue.append( "[" );
5296 0 : for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ )
5297 : {
5298 0 : sal_Int32 nEntry = rWidget.m_aSelectedEntries[i];
5299 0 : if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) )
5300 0 : appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue );
5301 : }
5302 0 : aValue.append( "]" );
5303 : }
5304 0 : else if( rWidget.m_aSelectedEntries.size() > 0 &&
5305 0 : rWidget.m_aSelectedEntries[0] >= 0 &&
5306 0 : rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) )
5307 : {
5308 0 : appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue );
5309 : }
5310 : else
5311 0 : appendUnicodeTextStringEncrypt( OUString(), rWidget.m_nObject, aValue );
5312 0 : aLine.append( "Ch" );
5313 0 : break;
5314 : case PDFWriter::ComboBox:
5315 0 : appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5316 0 : aLine.append( "Ch" );
5317 0 : break;
5318 : case PDFWriter::Edit:
5319 0 : aLine.append( "Tx" );
5320 0 : appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue );
5321 0 : break;
5322 : case PDFWriter::Signature:
5323 0 : aLine.append( "Sig" );
5324 0 : aValue.append(OUStringToOString(rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US));
5325 0 : break;
5326 : case PDFWriter::Hierarchy: // make the compiler happy
5327 0 : break;
5328 : }
5329 0 : aLine.append( "\n" );
5330 0 : aLine.append( "/P " );
5331 0 : aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject );
5332 0 : aLine.append( " 0 R\n" );
5333 : }
5334 0 : if( rWidget.m_nParent )
5335 : {
5336 0 : aLine.append( "/Parent " );
5337 0 : aLine.append( rWidget.m_nParent );
5338 0 : aLine.append( " 0 R\n" );
5339 : }
5340 0 : if( rWidget.m_aKids.size() )
5341 : {
5342 0 : aLine.append( "/Kids[" );
5343 0 : for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ )
5344 : {
5345 0 : aLine.append( rWidget.m_aKids[i] );
5346 0 : aLine.append( " 0 R" );
5347 0 : aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5348 : }
5349 0 : aLine.append( "]\n" );
5350 : }
5351 0 : if( !rWidget.m_aName.isEmpty() )
5352 : {
5353 0 : aLine.append( "/T" );
5354 0 : appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine );
5355 0 : aLine.append( "\n" );
5356 : }
5357 0 : if( m_aContext.Version > PDFWriter::PDF_1_2 && !rWidget.m_aDescription.isEmpty() )
5358 : {
5359 : // the alternate field name should be unicode able since it is
5360 : // supposed to be used in UI
5361 0 : aLine.append( "/TU" );
5362 0 : appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine );
5363 0 : aLine.append( "\n" );
5364 : }
5365 :
5366 0 : if( rWidget.m_nFlags )
5367 : {
5368 0 : aLine.append( "/Ff " );
5369 0 : aLine.append( rWidget.m_nFlags );
5370 0 : aLine.append( "\n" );
5371 : }
5372 0 : if( !aValue.isEmpty() )
5373 : {
5374 0 : OString aVal = aValue.makeStringAndClear();
5375 0 : aLine.append( "/V " );
5376 0 : aLine.append( aVal );
5377 : aLine.append( "\n"
5378 0 : "/DV " );
5379 0 : aLine.append( aVal );
5380 0 : aLine.append( "\n" );
5381 : }
5382 0 : if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox )
5383 : {
5384 0 : sal_Int32 nTI = -1;
5385 0 : aLine.append( "/Opt[\n" );
5386 0 : sal_Int32 i = 0;
5387 0 : for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i )
5388 : {
5389 0 : appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine );
5390 0 : aLine.append( "\n" );
5391 0 : if( *it == rWidget.m_aValue )
5392 0 : nTI = i;
5393 : }
5394 0 : aLine.append( "]\n" );
5395 0 : if( nTI > 0 )
5396 : {
5397 0 : aLine.append( "/TI " );
5398 0 : aLine.append( nTI );
5399 0 : aLine.append( "\n" );
5400 0 : if( rWidget.m_nFlags & 0x200000 ) // Multiselect
5401 : {
5402 0 : aLine.append( "/I [" );
5403 0 : aLine.append( nTI );
5404 0 : aLine.append( "]\n" );
5405 : }
5406 : }
5407 : }
5408 0 : if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 )
5409 : {
5410 0 : aLine.append( "/MaxLen " );
5411 0 : aLine.append( rWidget.m_nMaxLen );
5412 0 : aLine.append( "\n" );
5413 : }
5414 0 : if( rWidget.m_eType == PDFWriter::PushButton )
5415 : {
5416 0 : if(!m_bIsPDF_A1)
5417 : {
5418 0 : OStringBuffer aDest;
5419 0 : if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) )
5420 : {
5421 0 : aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " );
5422 0 : aLine.append( aDest.makeStringAndClear() );
5423 0 : aLine.append( ">>>>\n" );
5424 : }
5425 0 : else if( rWidget.m_aListEntries.empty() )
5426 : {
5427 : // create a reset form action
5428 0 : aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" );
5429 : }
5430 0 : else if( rWidget.m_bSubmit )
5431 : {
5432 : // create a submit form action
5433 0 : aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" );
5434 0 : appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() );
5435 0 : aLine.append( "/Flags " );
5436 :
5437 0 : sal_Int32 nFlags = 0;
5438 0 : switch( m_aContext.SubmitFormat )
5439 : {
5440 : case PDFWriter::HTML:
5441 0 : nFlags |= 4;
5442 0 : break;
5443 : case PDFWriter::XML:
5444 0 : if( m_aContext.Version > PDFWriter::PDF_1_3 )
5445 0 : nFlags |= 32;
5446 0 : break;
5447 : case PDFWriter::PDF:
5448 0 : if( m_aContext.Version > PDFWriter::PDF_1_3 )
5449 0 : nFlags |= 256;
5450 0 : break;
5451 : case PDFWriter::FDF:
5452 : default:
5453 0 : break;
5454 : }
5455 0 : if( rWidget.m_bSubmitGet )
5456 0 : nFlags |= 8;
5457 0 : aLine.append( nFlags );
5458 0 : aLine.append( ">>>>\n" );
5459 : }
5460 : else
5461 : {
5462 : // create a URI action
5463 0 : aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" );
5464 0 : aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) );
5465 0 : aLine.append( ")>>>>\n" );
5466 0 : }
5467 : }
5468 : else
5469 0 : m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA );
5470 : }
5471 0 : if( !rWidget.m_aDAString.isEmpty() )
5472 : {
5473 0 : if( !rWidget.m_aDRDict.isEmpty() )
5474 : {
5475 0 : aLine.append( "/DR<<" );
5476 0 : aLine.append( rWidget.m_aDRDict );
5477 0 : aLine.append( ">>\n" );
5478 : }
5479 : else
5480 : {
5481 0 : aLine.append( "/DR<</Font<<" );
5482 0 : appendBuiltinFontsToDict( aLine );
5483 0 : aLine.append( ">>>>\n" );
5484 : }
5485 0 : aLine.append( "/DA" );
5486 0 : appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine );
5487 0 : aLine.append( "\n" );
5488 0 : if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER )
5489 0 : aLine.append( "/Q 1\n" );
5490 0 : else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT )
5491 0 : aLine.append( "/Q 2\n" );
5492 : }
5493 : // appearance charactristics for terminal fields
5494 : // which are supposed to have an appearance constructed
5495 : // by the viewer application
5496 0 : if( !rWidget.m_aMKDict.isEmpty() )
5497 : {
5498 0 : aLine.append( "/MK<<" );
5499 0 : aLine.append( rWidget.m_aMKDict );
5500 : //add the CA string, encrypting it
5501 0 : appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine);
5502 0 : aLine.append( ">>\n" );
5503 : }
5504 :
5505 0 : CHECK_RETURN( emitAppearances( rWidget, aLine ) );
5506 :
5507 : aLine.append( ">>\n"
5508 0 : "endobj\n\n" );
5509 0 : CHECK_RETURN( updateObject( rWidget.m_nObject ) );
5510 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5511 0 : }
5512 0 : return true;
5513 : }
5514 :
5515 0 : bool PDFWriterImpl::emitAnnotations()
5516 : {
5517 0 : if( m_aPages.size() < 1 )
5518 0 : return false;
5519 :
5520 0 : CHECK_RETURN( emitLinkAnnotations() );
5521 0 : CHECK_RETURN( emitNoteAnnotations() );
5522 0 : CHECK_RETURN( emitWidgetAnnotations() );
5523 :
5524 0 : return true;
5525 : }
5526 :
5527 : #undef CHECK_RETURN
5528 : #define CHECK_RETURN( x ) if( !x ) return false
5529 :
5530 0 : bool PDFWriterImpl::emitCatalog()
5531 : {
5532 : // build page tree
5533 : // currently there is only one node that contains all leaves
5534 :
5535 : // first create a page tree node id
5536 0 : sal_Int32 nTreeNode = createObject();
5537 :
5538 : // emit global resource dictionary (page emit needs it)
5539 0 : CHECK_RETURN( emitResources() );
5540 :
5541 : // emit all pages
5542 0 : for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it )
5543 0 : if( ! it->emit( nTreeNode ) )
5544 0 : return false;
5545 :
5546 0 : sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations();
5547 :
5548 0 : sal_Int32 nOutlineDict = emitOutline();
5549 :
5550 : // emit Output intent i59651
5551 0 : sal_Int32 nOutputIntentObject = emitOutputIntent();
5552 :
5553 : // emit metadata
5554 0 : sal_Int32 nMetadataObject = emitDocumentMetadata();
5555 :
5556 0 : sal_Int32 nStructureDict = 0;
5557 0 : if(m_aStructure.size() > 1)
5558 : {
5559 : // check if dummy structure containers are needed
5560 0 : addInternalStructureContainer(m_aStructure[0]);
5561 0 : nStructureDict = m_aStructure[0].m_nObject = createObject();
5562 0 : emitStructure( m_aStructure[ 0 ] );
5563 : }
5564 :
5565 : // adjust tree node file offset
5566 0 : if( ! updateObject( nTreeNode ) )
5567 0 : return false;
5568 :
5569 : // emit tree node
5570 0 : OStringBuffer aLine( 2048 );
5571 0 : aLine.append( nTreeNode );
5572 0 : aLine.append( " 0 obj\n" );
5573 0 : aLine.append( "<</Type/Pages\n" );
5574 0 : aLine.append( "/Resources " );
5575 0 : aLine.append( getResourceDictObj() );
5576 0 : aLine.append( " 0 R\n" );
5577 :
5578 0 : switch( m_eInheritedOrientation )
5579 : {
5580 0 : case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break;
5581 0 : case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break;
5582 :
5583 : case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant
5584 : case PDFWriter::Portrait:
5585 : default:
5586 0 : break;
5587 : }
5588 0 : sal_Int32 nMediaBoxWidth = 0;
5589 0 : sal_Int32 nMediaBoxHeight = 0;
5590 0 : if( m_aPages.empty() ) // sanity check, this should not happen
5591 : {
5592 0 : nMediaBoxWidth = m_nInheritedPageWidth;
5593 0 : nMediaBoxHeight = m_nInheritedPageHeight;
5594 : }
5595 : else
5596 : {
5597 0 : for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter )
5598 : {
5599 0 : if( iter->m_nPageWidth > nMediaBoxWidth )
5600 0 : nMediaBoxWidth = iter->m_nPageWidth;
5601 0 : if( iter->m_nPageHeight > nMediaBoxHeight )
5602 0 : nMediaBoxHeight = iter->m_nPageHeight;
5603 : }
5604 : }
5605 0 : aLine.append( "/MediaBox[ 0 0 " );
5606 0 : aLine.append( nMediaBoxWidth );
5607 0 : aLine.append( ' ' );
5608 0 : aLine.append( nMediaBoxHeight );
5609 : aLine.append( " ]\n"
5610 0 : "/Kids[ " );
5611 0 : unsigned int i = 0;
5612 0 : for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ )
5613 : {
5614 0 : aLine.append( iter->m_nPageObject );
5615 0 : aLine.append( " 0 R" );
5616 0 : aLine.append( ( (i&15) == 15 ) ? "\n" : " " );
5617 : }
5618 : aLine.append( "]\n"
5619 0 : "/Count " );
5620 0 : aLine.append( (sal_Int32)m_aPages.size() );
5621 : aLine.append( ">>\n"
5622 0 : "endobj\n\n" );
5623 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5624 :
5625 : // emit annotation objects
5626 0 : CHECK_RETURN( emitAnnotations() );
5627 :
5628 : // emit Catalog
5629 0 : m_nCatalogObject = createObject();
5630 0 : if( ! updateObject( m_nCatalogObject ) )
5631 0 : return false;
5632 0 : aLine.setLength( 0 );
5633 0 : aLine.append( m_nCatalogObject );
5634 : aLine.append( " 0 obj\n"
5635 0 : "<</Type/Catalog/Pages " );
5636 0 : aLine.append( nTreeNode );
5637 0 : aLine.append( " 0 R\n" );
5638 : //--->i56629
5639 : // check if there are named destinations to emit (root must be inside the catalog)
5640 0 : if( nNamedDestinationsDictionary )
5641 : {
5642 0 : aLine.append("/Dests ");
5643 0 : aLine.append( nNamedDestinationsDictionary );
5644 0 : aLine.append( " 0 R\n" );
5645 : }
5646 : //<----
5647 0 : if( m_aContext.PageLayout != PDFWriter::DefaultLayout )
5648 0 : switch( m_aContext.PageLayout )
5649 : {
5650 : default :
5651 : case PDFWriter::SinglePage :
5652 0 : aLine.append( "/PageLayout/SinglePage\n" );
5653 0 : break;
5654 : case PDFWriter::Continuous :
5655 0 : aLine.append( "/PageLayout/OneColumn\n" );
5656 0 : break;
5657 : case PDFWriter::ContinuousFacing :
5658 : // the flag m_aContext.FirstPageLeft below is used to set the page on the left side
5659 0 : aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side
5660 0 : break;
5661 : }
5662 0 : if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode )
5663 0 : switch( m_aContext.PDFDocumentMode )
5664 : {
5665 : default :
5666 0 : aLine.append( "/PageMode/UseNone\n" );
5667 0 : break;
5668 : case PDFWriter::UseOutlines :
5669 0 : aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open
5670 0 : break;
5671 : case PDFWriter::UseThumbs :
5672 0 : aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open
5673 0 : break;
5674 : }
5675 0 : else if( m_aContext.OpenInFullScreenMode )
5676 0 : aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen
5677 :
5678 0 : OStringBuffer aInitPageRef;
5679 0 : if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() )
5680 : {
5681 0 : aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject );
5682 0 : aInitPageRef.append( " 0 R" );
5683 : }
5684 : else
5685 0 : aInitPageRef.append( "0" );
5686 :
5687 0 : switch( m_aContext.PDFDocumentAction )
5688 : {
5689 : case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default
5690 : default:
5691 0 : if( aInitPageRef.getLength() > 1 )
5692 : {
5693 0 : aLine.append( "/OpenAction[" );
5694 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5695 0 : aLine.append( " /XYZ null null 0]\n" );
5696 : }
5697 0 : break;
5698 : case PDFWriter::FitInWindow :
5699 0 : aLine.append( "/OpenAction[" );
5700 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5701 0 : aLine.append( " /Fit]\n" ); //Open fit page
5702 0 : break;
5703 : case PDFWriter::FitWidth :
5704 0 : aLine.append( "/OpenAction[" );
5705 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5706 0 : aLine.append( " /FitH " );
5707 0 : aLine.append( m_nInheritedPageHeight );//Open fit width
5708 0 : aLine.append( "]\n" );
5709 0 : break;
5710 : case PDFWriter::FitVisible :
5711 0 : aLine.append( "/OpenAction[" );
5712 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5713 0 : aLine.append( " /FitBH " );
5714 0 : aLine.append( m_nInheritedPageHeight );//Open fit visible
5715 0 : aLine.append( "]\n" );
5716 0 : break;
5717 : case PDFWriter::ActionZoom :
5718 0 : aLine.append( "/OpenAction[" );
5719 0 : aLine.append( aInitPageRef.makeStringAndClear() );
5720 0 : aLine.append( " /XYZ null null " );
5721 0 : if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 )
5722 0 : aLine.append( (double)m_aContext.Zoom/100.0 );
5723 : else
5724 0 : aLine.append( "0" );
5725 0 : aLine.append( "]\n" );
5726 0 : break;
5727 : }
5728 :
5729 : // viewer preferences, if we had some, then emit
5730 0 : if( m_aContext.HideViewerToolbar ||
5731 0 : ( m_aContext.Version > PDFWriter::PDF_1_3 && !m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle ) ||
5732 0 : m_aContext.HideViewerMenubar ||
5733 0 : m_aContext.HideViewerWindowControls || m_aContext.FitWindow ||
5734 0 : m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) ||
5735 : m_aContext.OpenInFullScreenMode )
5736 : {
5737 0 : aLine.append( "/ViewerPreferences<<" );
5738 0 : if( m_aContext.HideViewerToolbar )
5739 0 : aLine.append( "/HideToolbar true\n" );
5740 0 : if( m_aContext.HideViewerMenubar )
5741 0 : aLine.append( "/HideMenubar true\n" );
5742 0 : if( m_aContext.HideViewerWindowControls )
5743 0 : aLine.append( "/HideWindowUI true\n" );
5744 0 : if( m_aContext.FitWindow )
5745 0 : aLine.append( "/FitWindow true\n" );
5746 0 : if( m_aContext.CenterWindow )
5747 0 : aLine.append( "/CenterWindow true\n" );
5748 0 : if( m_aContext.Version > PDFWriter::PDF_1_3 && !m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle )
5749 0 : aLine.append( "/DisplayDocTitle true\n" );
5750 0 : if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing )
5751 0 : aLine.append( "/Direction/R2L\n" );
5752 0 : if( m_aContext.OpenInFullScreenMode )
5753 0 : switch( m_aContext.PDFDocumentMode )
5754 : {
5755 : default :
5756 : case PDFWriter::ModeDefault :
5757 0 : aLine.append( "/NonFullScreenPageMode/UseNone\n" );
5758 0 : break;
5759 : case PDFWriter::UseOutlines :
5760 0 : aLine.append( "/NonFullScreenPageMode/UseOutlines\n" );
5761 0 : break;
5762 : case PDFWriter::UseThumbs :
5763 0 : aLine.append( "/NonFullScreenPageMode/UseThumbs\n" );
5764 0 : break;
5765 : }
5766 0 : aLine.append( ">>\n" );
5767 : }
5768 :
5769 0 : if( nOutlineDict )
5770 : {
5771 0 : aLine.append( "/Outlines " );
5772 0 : aLine.append( nOutlineDict );
5773 0 : aLine.append( " 0 R\n" );
5774 : }
5775 0 : if( nStructureDict )
5776 : {
5777 0 : aLine.append( "/StructTreeRoot " );
5778 0 : aLine.append( nStructureDict );
5779 0 : aLine.append( " 0 R\n" );
5780 : }
5781 0 : if( !m_aContext.DocumentLocale.Language.isEmpty() )
5782 : {
5783 : /* PDF allows only RFC 3066, see above in emitStructure(). */
5784 0 : LanguageTag aLanguageTag( m_aContext.DocumentLocale);
5785 0 : OUString aLanguage, aScript, aCountry;
5786 0 : aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry);
5787 0 : if (!aLanguage.isEmpty())
5788 : {
5789 0 : OUStringBuffer aLocBuf( 16 );
5790 0 : aLocBuf.append( aLanguage );
5791 0 : if( !aCountry.isEmpty() )
5792 : {
5793 0 : aLocBuf.append( '-' );
5794 0 : aLocBuf.append( aCountry );
5795 : }
5796 0 : aLine.append( "/Lang" );
5797 0 : appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine );
5798 0 : aLine.append( "\n" );
5799 0 : }
5800 : }
5801 0 : if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 )
5802 : {
5803 0 : aLine.append( "/MarkInfo<</Marked true>>\n" );
5804 : }
5805 0 : if( m_aWidgets.size() > 0 )
5806 : {
5807 0 : aLine.append( "/AcroForm<</Fields[\n" );
5808 0 : int nWidgets = m_aWidgets.size();
5809 0 : int nOut = 0;
5810 0 : for( int j = 0; j < nWidgets; j++ )
5811 : {
5812 : // output only root fields
5813 0 : if( m_aWidgets[j].m_nParent < 1 )
5814 : {
5815 0 : aLine.append( m_aWidgets[j].m_nObject );
5816 0 : aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " );
5817 : }
5818 : }
5819 0 : aLine.append( "\n]" );
5820 :
5821 : #if !defined(ANDROID) && !defined(IOS)
5822 0 : if (m_nSignatureObject != -1)
5823 0 : aLine.append( "/SigFlags 3");
5824 : #endif
5825 :
5826 0 : aLine.append( "/DR " );
5827 0 : aLine.append( getResourceDictObj() );
5828 0 : aLine.append( " 0 R" );
5829 : // /NeedAppearances must not be used if PDF is signed
5830 0 : if( m_bIsPDF_A1
5831 : #if !defined(ANDROID) && !defined(IOS)
5832 0 : || ( m_nSignatureObject != -1 )
5833 : #endif
5834 : )
5835 0 : aLine.append( ">>\n" );
5836 : else
5837 0 : aLine.append( "/NeedAppearances true>>\n" );
5838 : }
5839 :
5840 : //--->i59651
5841 : //check if there is a Metadata object
5842 0 : if( nOutputIntentObject )
5843 : {
5844 0 : aLine.append("/OutputIntents[");
5845 0 : aLine.append( nOutputIntentObject );
5846 0 : aLine.append( " 0 R]" );
5847 : }
5848 :
5849 0 : if( nMetadataObject )
5850 : {
5851 0 : aLine.append("/Metadata ");
5852 0 : aLine.append( nMetadataObject );
5853 0 : aLine.append( " 0 R" );
5854 : }
5855 : //<----
5856 : aLine.append( ">>\n"
5857 0 : "endobj\n\n" );
5858 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
5859 :
5860 0 : return true;
5861 : }
5862 :
5863 : #if !defined(ANDROID) && !defined(IOS)
5864 :
5865 0 : bool PDFWriterImpl::emitSignature()
5866 : {
5867 0 : if( !updateObject( m_nSignatureObject ) )
5868 0 : return false;
5869 :
5870 0 : OStringBuffer aLine( 0x5000 );
5871 0 : aLine.append( m_nSignatureObject );
5872 0 : aLine.append( " 0 obj\n" );
5873 0 : aLine.append("<</Contents <" );
5874 :
5875 0 : sal_uInt64 nOffset = ~0U;
5876 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nOffset ) ) );
5877 :
5878 0 : m_nSignatureContentOffset = nOffset + aLine.getLength();
5879 :
5880 : // reserve some space for the PKCS#7 object
5881 0 : OStringBuffer aContentFiller( MAX_SIGNATURE_CONTENT_LENGTH );
5882 0 : comphelper::string::padToLength(aContentFiller, MAX_SIGNATURE_CONTENT_LENGTH, '0');
5883 0 : aLine.append( aContentFiller.makeStringAndClear() );
5884 0 : aLine.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached");
5885 :
5886 0 : if( !m_aContext.DocumentInfo.Author.isEmpty() )
5887 : {
5888 0 : aLine.append( "/Name" );
5889 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, m_nSignatureObject, aLine );
5890 : }
5891 :
5892 0 : aLine.append( " /M ");
5893 0 : appendLiteralStringEncrypt( m_aCreationDateString, m_nSignatureObject, aLine );
5894 :
5895 0 : aLine.append( " /ByteRange [ 0 ");
5896 0 : aLine.append( m_nSignatureContentOffset - 1, 10 );
5897 0 : aLine.append( " " );
5898 0 : aLine.append( m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1, 10 );
5899 0 : aLine.append( " " );
5900 :
5901 0 : m_nSignatureLastByteRangeNoOffset = nOffset + aLine.getLength();
5902 :
5903 : // mark the last ByteRange no and add some space. Now, we don't know
5904 : // how many bytes we need for this ByteRange value
5905 : // The real value will be overwritten in the finalizeSignature method
5906 0 : OStringBuffer aByteRangeFiller( 100 );
5907 0 : comphelper::string::padToLength(aByteRangeFiller, 100, ' ');
5908 0 : aLine.append( aByteRangeFiller.makeStringAndClear() );
5909 0 : aLine.append(" /Filter/Adobe.PPKMS");
5910 :
5911 : //emit reason, location and contactinfo
5912 0 : if ( !m_aContext.SignReason.isEmpty() )
5913 : {
5914 0 : aLine.append("/Reason");
5915 0 : appendUnicodeTextStringEncrypt( m_aContext.SignReason, m_nSignatureObject, aLine );
5916 : }
5917 :
5918 0 : if ( !m_aContext.SignLocation.isEmpty() )
5919 : {
5920 0 : aLine.append("/Location");
5921 0 : appendUnicodeTextStringEncrypt( m_aContext.SignLocation, m_nSignatureObject, aLine );
5922 : }
5923 :
5924 0 : if ( !m_aContext.SignContact.isEmpty() )
5925 : {
5926 0 : aLine.append("/ContactInfo");
5927 0 : appendUnicodeTextStringEncrypt( m_aContext.SignContact, m_nSignatureObject, aLine );
5928 : }
5929 :
5930 0 : aLine.append(" >>\nendobj\n\n" );
5931 :
5932 0 : if (!writeBuffer( aLine.getStr(), aLine.getLength() ))
5933 0 : return false;
5934 :
5935 0 : return true;
5936 : }
5937 :
5938 0 : char *PDFSigningPKCS7PasswordCallback(PK11SlotInfo * /*slot*/, PRBool /*retry*/, void *arg)
5939 : {
5940 0 : return (char *)arg;
5941 : }
5942 :
5943 0 : bool PDFWriterImpl::finalizeSignature()
5944 : {
5945 :
5946 0 : if (!m_aContext.SignCertificate.is())
5947 0 : return false;
5948 :
5949 : // 1- calculate last ByteRange value
5950 0 : sal_uInt64 nOffset = ~0U;
5951 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nOffset ) ) );
5952 :
5953 0 : sal_Int64 nLastByteRangeNo = nOffset - (m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1);
5954 :
5955 : // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position
5956 0 : sal_uInt64 nWritten = 0;
5957 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureLastByteRangeNoOffset ) ) );
5958 0 : OStringBuffer aByteRangeNo( 256 );
5959 0 : aByteRangeNo.append( nLastByteRangeNo, 10);
5960 0 : aByteRangeNo.append( " ]" );
5961 :
5962 0 : if( osl_writeFile( m_aFile, aByteRangeNo.getStr(), aByteRangeNo.getLength(), &nWritten ) != osl_File_E_None )
5963 : {
5964 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, nOffset ) ) );
5965 0 : return false;
5966 : }
5967 :
5968 : // 3- create the PKCS#7 object using NSS
5969 0 : com::sun::star::uno::Sequence< sal_Int8 > derEncoded = m_aContext.SignCertificate->getEncoded();
5970 :
5971 0 : if (!derEncoded.hasElements())
5972 0 : return false;
5973 :
5974 0 : sal_Int8* n_derArray = derEncoded.getArray();
5975 0 : sal_Int32 n_derLength = derEncoded.getLength();
5976 :
5977 0 : NSS_NoDB_Init(".");
5978 :
5979 0 : CERTCertificate *cert = CERT_DecodeCertFromPackage(reinterpret_cast<char *>(n_derArray), n_derLength);
5980 :
5981 0 : if (!cert)
5982 : {
5983 : SAL_WARN("vcl.gdi", "PDF Signing: Error occured, certificate cannot be reconstructed.");
5984 0 : return false;
5985 : }
5986 :
5987 : SAL_WARN("vcl.gdi", "PDF Signing: Certificate Subject: " << cert->subjectName << "\n\tCertificate Issuer: " << cert->issuerName);
5988 :
5989 : // Prepare buffer and calculate PDF file digest
5990 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, 0) ) );
5991 :
5992 0 : boost::scoped_ptr<HASHContext> hc(HASH_Create(HASH_AlgSHA1));
5993 :
5994 0 : if (!hc)
5995 : {
5996 : SAL_WARN("vcl.gdi", "PDF Signing: SHA1 HASH_Create failed!");
5997 0 : return false;
5998 : }
5999 :
6000 0 : HASH_Begin(hc.get());
6001 :
6002 0 : boost::scoped_array<char> buffer(new char[m_nSignatureContentOffset + 1]);
6003 : sal_uInt64 bytesRead;
6004 :
6005 : //FIXME: Check if SHA1 is calculated from the correct byterange
6006 :
6007 0 : CHECK_RETURN( (osl_File_E_None == osl_readFile( m_aFile, buffer.get(), m_nSignatureContentOffset - 1 , &bytesRead ) ) );
6008 0 : if (bytesRead != (sal_uInt64)m_nSignatureContentOffset - 1)
6009 : SAL_WARN("vcl.gdi", "PDF Signing: First buffer read failed!");
6010 :
6011 0 : HASH_Update(hc.get(), reinterpret_cast<const unsigned char*>(buffer.get()), bytesRead);
6012 :
6013 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1) ) );
6014 0 : buffer.reset(new char[nLastByteRangeNo + 1]);
6015 0 : CHECK_RETURN( (osl_File_E_None == osl_readFile( m_aFile, buffer.get(), nLastByteRangeNo, &bytesRead ) ) );
6016 0 : if (bytesRead != (sal_uInt64) nLastByteRangeNo)
6017 : SAL_WARN("vcl.gdi", "PDF Signing: Second buffer read failed!");
6018 :
6019 0 : HASH_Update(hc.get(), reinterpret_cast<const unsigned char*>(buffer.get()), bytesRead);
6020 :
6021 : SECItem digest;
6022 : unsigned char hash[SHA1_LENGTH];
6023 0 : digest.data = hash;
6024 0 : HASH_End(hc.get(), digest.data, &digest.len, SHA1_LENGTH);
6025 0 : HASH_Destroy(hc.get());
6026 :
6027 0 : const char *pass = OUStringToOString( m_aContext.SignPassword, RTL_TEXTENCODING_UTF8 ).getStr();
6028 :
6029 0 : NSSCMSMessage *cms_msg = NSS_CMSMessage_Create(NULL);
6030 0 : if (!cms_msg)
6031 : {
6032 : SAL_WARN("vcl.gdi", "PDF signing: can't create new CMS message.");
6033 0 : return false;
6034 : }
6035 :
6036 0 : NSSCMSSignedData *cms_sd = NSS_CMSSignedData_Create(cms_msg);
6037 0 : if (!cms_sd)
6038 : {
6039 : SAL_WARN("vcl.gdi", "PDF signing: can't create CMS SignedData.");
6040 0 : return false;
6041 : }
6042 :
6043 0 : NSSCMSContentInfo *cms_cinfo = NSS_CMSMessage_GetContentInfo(cms_msg);
6044 0 : if (NSS_CMSContentInfo_SetContent_SignedData(cms_msg, cms_cinfo, cms_sd) != SECSuccess)
6045 : {
6046 : SAL_WARN("vcl.gdi", "PDF signing: Can't set CMS content signed data.");
6047 0 : return false;
6048 : }
6049 :
6050 0 : cms_cinfo = NSS_CMSSignedData_GetContentInfo(cms_sd);
6051 : //attach NULL data as detached data
6052 0 : if (NSS_CMSContentInfo_SetContent_Data(cms_msg, cms_cinfo, NULL, PR_TRUE) != SECSuccess)
6053 : {
6054 : SAL_WARN("vcl.gdi", "PDF signing: Can't set CMS content data.");
6055 0 : return false;
6056 : }
6057 :
6058 0 : NSSCMSSignerInfo *cms_signer = NSS_CMSSignerInfo_Create(cms_msg, cert, SEC_OID_SHA1);
6059 0 : if (!cms_signer)
6060 : {
6061 : SAL_WARN("vcl.gdi", "PDF signing: can't create CMS SignerInfo.");
6062 0 : return false;
6063 : }
6064 :
6065 0 : if (NSS_CMSSignerInfo_IncludeCerts(cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess)
6066 : {
6067 : SAL_WARN("vcl.gdi", "PDF signing: can't include cert chain.");
6068 0 : return false;
6069 : }
6070 :
6071 0 : if (NSS_CMSSignerInfo_AddSigningTime(cms_signer, PR_Now()) != SECSuccess)
6072 : {
6073 : SAL_WARN("vcl.gdi", "PDF signing: can't add signing time.");
6074 0 : return false;
6075 : }
6076 :
6077 0 : if (NSS_CMSSignedData_AddCertificate(cms_sd, cert) != SECSuccess)
6078 : {
6079 : SAL_WARN("vcl.gdi", "PDF signing: can't add signer certificate.");
6080 0 : return false;
6081 : }
6082 :
6083 0 : if (NSS_CMSSignedData_AddSignerInfo(cms_sd, cms_signer) != SECSuccess)
6084 : {
6085 : SAL_WARN("vcl.gdi", "PDF signing: can't add signer info.");
6086 0 : return false;
6087 : }
6088 :
6089 0 : if (NSS_CMSSignedData_SetDigestValue(cms_sd, SEC_OID_SHA1, &digest) != SECSuccess)
6090 : {
6091 : SAL_WARN("vcl.gdi", "PDF signing: can't set PDF digest value.");
6092 0 : return false;
6093 : }
6094 :
6095 : SAL_WARN("vcl.gdi","PKCS7 Object created successfully!");
6096 :
6097 : SECItem cms_output;
6098 0 : cms_output.data = 0;
6099 0 : cms_output.len = 0;
6100 0 : PLArenaPool *arena = PORT_NewArena(MAX_SIGNATURE_CONTENT_LENGTH);
6101 : NSSCMSEncoderContext *cms_ecx;
6102 :
6103 : //FIXME: Check if password is passed correctly to SEC_PKCS7CreateSignedData function
6104 0 : cms_ecx = NSS_CMSEncoder_Start(cms_msg, NULL, NULL, &cms_output, arena, (PK11PasswordFunc)::PDFSigningPKCS7PasswordCallback, (void *)pass, NULL, NULL, NULL, NULL);
6105 :
6106 0 : if (!cms_ecx)
6107 : {
6108 : SAL_WARN("vcl.gdi", "PDF Signing: can't start DER encoder.");
6109 0 : return false;
6110 : }
6111 : SAL_WARN("vcl.gdi", "PDF Signing: Started DER encoding.");
6112 :
6113 0 : if (NSS_CMSEncoder_Finish(cms_ecx) != SECSuccess)
6114 : {
6115 : SAL_WARN("vcl.gdi", "PDF Signing: can't finish DER encoder.");
6116 0 : return false;
6117 : }
6118 : SAL_WARN("vcl.gdi", "PDF Signing: Finished DER encoding.");
6119 :
6120 0 : OStringBuffer cms_hexbuffer;
6121 :
6122 0 : for (unsigned int i = 0; i < cms_output.len ; i++)
6123 0 : appendHex(cms_output.data[i], cms_hexbuffer);
6124 :
6125 : SAL_WARN("vcl.gdi","PKCS7 object encoded successfully!");
6126 :
6127 : // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object
6128 0 : nWritten = 0;
6129 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, m_nSignatureContentOffset) ) );
6130 0 : osl_writeFile(m_aFile, cms_hexbuffer.getStr(), cms_hexbuffer.getLength(), &nWritten);
6131 :
6132 0 : NSS_CMSMessage_Destroy(cms_msg);
6133 :
6134 0 : CHECK_RETURN( (osl_File_E_None == osl_setFilePos( m_aFile, osl_Pos_Absolut, nOffset ) ) );
6135 0 : return true;
6136 : }
6137 :
6138 : #endif
6139 :
6140 0 : sal_Int32 PDFWriterImpl::emitInfoDict( )
6141 : {
6142 0 : sal_Int32 nObject = createObject();
6143 :
6144 0 : if( updateObject( nObject ) )
6145 : {
6146 0 : OStringBuffer aLine( 1024 );
6147 0 : aLine.append( nObject );
6148 : aLine.append( " 0 obj\n"
6149 0 : "<<" );
6150 0 : if( !m_aContext.DocumentInfo.Title.isEmpty() )
6151 : {
6152 0 : aLine.append( "/Title" );
6153 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine );
6154 0 : aLine.append( "\n" );
6155 : }
6156 0 : if( !m_aContext.DocumentInfo.Author.isEmpty() )
6157 : {
6158 0 : aLine.append( "/Author" );
6159 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine );
6160 0 : aLine.append( "\n" );
6161 : }
6162 0 : if( !m_aContext.DocumentInfo.Subject.isEmpty() )
6163 : {
6164 0 : aLine.append( "/Subject" );
6165 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine );
6166 0 : aLine.append( "\n" );
6167 : }
6168 0 : if( !m_aContext.DocumentInfo.Keywords.isEmpty() )
6169 : {
6170 0 : aLine.append( "/Keywords" );
6171 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine );
6172 0 : aLine.append( "\n" );
6173 : }
6174 0 : if( !m_aContext.DocumentInfo.Creator.isEmpty() )
6175 : {
6176 0 : aLine.append( "/Creator" );
6177 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine );
6178 0 : aLine.append( "\n" );
6179 : }
6180 0 : if( !m_aContext.DocumentInfo.Producer.isEmpty() )
6181 : {
6182 0 : aLine.append( "/Producer" );
6183 0 : appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine );
6184 0 : aLine.append( "\n" );
6185 : }
6186 :
6187 0 : aLine.append( "/CreationDate" );
6188 0 : appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine );
6189 0 : aLine.append( ">>\nendobj\n\n" );
6190 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6191 0 : nObject = 0;
6192 : }
6193 : else
6194 0 : nObject = 0;
6195 :
6196 0 : return nObject;
6197 : }
6198 :
6199 : //--->i56629
6200 : // Part of this function may be shared with method appendDest.
6201 0 : sal_Int32 PDFWriterImpl::emitNamedDestinations()
6202 : {
6203 0 : sal_Int32 nCount = m_aNamedDests.size();
6204 0 : if( nCount <= 0 )
6205 0 : return 0;//define internal error
6206 :
6207 : //get the object number for all the destinations
6208 0 : sal_Int32 nObject = createObject();
6209 :
6210 0 : if( updateObject( nObject ) )
6211 : {
6212 : //emit the dictionary
6213 0 : OStringBuffer aLine( 1024 );
6214 0 : aLine.append( nObject );
6215 : aLine.append( " 0 obj\n"
6216 0 : "<<" );
6217 :
6218 : sal_Int32 nDestID;
6219 0 : for( nDestID = 0; nDestID < nCount; nDestID++ )
6220 : {
6221 0 : const PDFNamedDest& rDest = m_aNamedDests[ nDestID ];
6222 : // In order to correctly function both under an Internet browser and
6223 : // directly with a reader (provided the reader has the feature) we
6224 : // need to set the name of the destination the same way it will be encoded
6225 : // in an Internet link
6226 : INetURLObject aLocalURL(
6227 0 : OUString( "http://ahost.ax" ) ); //dummy location, won't be used
6228 0 : aLocalURL.SetMark( rDest.m_aDestName );
6229 :
6230 0 : const OUString aName = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as
6231 : // in link creation ( see PDFWriterImpl::emitLinkAnnotations )
6232 0 : const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ];
6233 :
6234 0 : aLine.append( '/' );
6235 0 : appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog )
6236 0 : aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function
6237 : //maps the preceeding character properly
6238 0 : aLine.append( rDestPage.m_nPageObject );
6239 0 : aLine.append( " 0 R" );
6240 :
6241 0 : switch( rDest.m_eType )
6242 : {
6243 : case PDFWriter::XYZ:
6244 : default:
6245 0 : aLine.append( "/XYZ " );
6246 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
6247 0 : aLine.append( ' ' );
6248 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6249 0 : aLine.append( " 0" );
6250 0 : break;
6251 : case PDFWriter::Fit:
6252 0 : aLine.append( "/Fit" );
6253 0 : break;
6254 : case PDFWriter::FitRectangle:
6255 0 : aLine.append( "/FitR " );
6256 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
6257 0 : aLine.append( ' ' );
6258 0 : appendFixedInt( rDest.m_aRect.Top(), aLine );
6259 0 : aLine.append( ' ' );
6260 0 : appendFixedInt( rDest.m_aRect.Right(), aLine );
6261 0 : aLine.append( ' ' );
6262 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6263 0 : break;
6264 : case PDFWriter::FitHorizontal:
6265 0 : aLine.append( "/FitH " );
6266 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6267 0 : break;
6268 : case PDFWriter::FitVertical:
6269 0 : aLine.append( "/FitV " );
6270 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
6271 0 : break;
6272 : case PDFWriter::FitPageBoundingBox:
6273 0 : aLine.append( "/FitB" );
6274 0 : break;
6275 : case PDFWriter::FitPageBoundingBoxHorizontal:
6276 0 : aLine.append( "/FitBH " );
6277 0 : appendFixedInt( rDest.m_aRect.Bottom(), aLine );
6278 0 : break;
6279 : case PDFWriter::FitPageBoundingBoxVertical:
6280 0 : aLine.append( "/FitBV " );
6281 0 : appendFixedInt( rDest.m_aRect.Left(), aLine );
6282 0 : break;
6283 : }
6284 0 : aLine.append( "]\n" );
6285 0 : }
6286 :
6287 : //close
6288 0 : aLine.append( ">>\nendobj\n\n" );
6289 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6290 0 : nObject = 0;
6291 : }
6292 : else
6293 0 : nObject = 0;
6294 :
6295 0 : return nObject;
6296 : }
6297 : //<--- i56629
6298 :
6299 : //--->i59651
6300 : // emits the output intent dictionary
6301 0 : sal_Int32 PDFWriterImpl::emitOutputIntent()
6302 : {
6303 0 : if( !m_bIsPDF_A1 )
6304 0 : return 0;
6305 :
6306 : //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1
6307 :
6308 0 : OStringBuffer aLine( 1024 );
6309 0 : sal_Int32 nICCObject = createObject();
6310 0 : sal_Int32 nStreamLengthObject = createObject();
6311 :
6312 0 : aLine.append( nICCObject );
6313 : // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16)
6314 0 : aLine.append( " 0 obj\n<</N 3/Length " );
6315 0 : aLine.append( nStreamLengthObject );
6316 0 : aLine.append( " 0 R" );
6317 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
6318 0 : aLine.append( "/Filter/FlateDecode" );
6319 : #endif
6320 0 : aLine.append( ">>\nstream\n" );
6321 0 : if ( !updateObject( nICCObject ) ) return 0;
6322 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
6323 : //get file position
6324 0 : sal_uInt64 nBeginStreamPos = 0;
6325 0 : osl_getFilePos( m_aFile, &nBeginStreamPos );
6326 0 : beginCompression();
6327 0 : checkAndEnableStreamEncryption( nICCObject );
6328 0 : cmsHPROFILE hProfile = cmsCreate_sRGBProfile();
6329 : //force ICC profile version 2.1
6330 0 : cmsSetProfileVersion(hProfile, 2.1);
6331 0 : cmsUInt32Number nBytesNeeded = 0;
6332 0 : cmsSaveProfileToMem(hProfile, NULL, &nBytesNeeded);
6333 0 : if (!nBytesNeeded)
6334 0 : return 0;
6335 0 : std::vector<unsigned char> xBuffer(nBytesNeeded);
6336 0 : cmsSaveProfileToMem(hProfile, &xBuffer[0], &nBytesNeeded);
6337 0 : cmsCloseProfile(hProfile);
6338 0 : bool written = writeBuffer( &xBuffer[0], (sal_Int32) xBuffer.size() );
6339 0 : disableStreamEncryption();
6340 0 : endCompression();
6341 0 : sal_uInt64 nEndStreamPos = 0;
6342 0 : osl_getFilePos( m_aFile, &nEndStreamPos );
6343 :
6344 0 : if( !written )
6345 0 : return 0;
6346 0 : if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6347 0 : return 0 ;
6348 0 : aLine.setLength( 0 );
6349 :
6350 : //emit the stream length object
6351 0 : if ( !updateObject( nStreamLengthObject ) ) return 0;
6352 0 : aLine.setLength( 0 );
6353 0 : aLine.append( nStreamLengthObject );
6354 0 : aLine.append( " 0 obj\n" );
6355 0 : aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6356 0 : aLine.append( "\nendobj\n\n" );
6357 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
6358 0 : aLine.setLength( 0 );
6359 :
6360 : //emit the OutputIntent dictionary
6361 0 : sal_Int32 nOIObject = createObject();
6362 0 : if ( !updateObject( nOIObject ) ) return 0;
6363 0 : aLine.append( nOIObject );
6364 : aLine.append( " 0 obj\n"
6365 0 : "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier");
6366 :
6367 0 : OUString aComment( "sRGB IEC61966-2.1" );
6368 0 : appendLiteralStringEncrypt( aComment ,nOIObject, aLine );
6369 0 : aLine.append("/DestOutputProfile ");
6370 0 : aLine.append( nICCObject );
6371 0 : aLine.append( " 0 R>>\nendobj\n\n" );;
6372 0 : if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0;
6373 :
6374 0 : return nOIObject;
6375 : }
6376 :
6377 : // formats the string for the XML stream
6378 0 : static void escapeStringXML( const OUString& rStr, OUString &rValue)
6379 : {
6380 0 : const sal_Unicode* pUni = rStr.getStr();
6381 0 : int nLen = rStr.getLength();
6382 0 : for( ; nLen; nLen--, pUni++ )
6383 : {
6384 0 : switch( *pUni )
6385 : {
6386 : case sal_Unicode('&'):
6387 0 : rValue += "&";
6388 0 : break;
6389 : case sal_Unicode('<'):
6390 0 : rValue += "<";
6391 0 : break;
6392 : case sal_Unicode('>'):
6393 0 : rValue += ">";
6394 0 : break;
6395 : case sal_Unicode('\''):
6396 0 : rValue += "'";
6397 0 : break;
6398 : case sal_Unicode('"'):
6399 0 : rValue += """;
6400 0 : break;
6401 : default:
6402 0 : rValue += OUString( *pUni );
6403 0 : break;
6404 : }
6405 : }
6406 0 : }
6407 :
6408 : // emits the document metadata
6409 0 : sal_Int32 PDFWriterImpl::emitDocumentMetadata()
6410 : {
6411 0 : if( !m_bIsPDF_A1 )
6412 0 : return 0;
6413 :
6414 : //get the object number for all the destinations
6415 0 : sal_Int32 nObject = createObject();
6416 :
6417 0 : if( updateObject( nObject ) )
6418 : {
6419 : // the following string are written in UTF-8 unicode
6420 0 : OStringBuffer aMetadataStream( 8192 );
6421 :
6422 0 : aMetadataStream.append( "<?xpacket begin=\"" );
6423 : // these lines write Unicode "zero width non-breaking space character" (U+FEFF)
6424 : // (aka byte-order mark ) used as a byte-order marker.
6425 0 : aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) );
6426 0 : aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" );
6427 0 : aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" );
6428 0 : aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" );
6429 : //PDF/A part ( ISO 19005-1:2005 - 6.7.11 )
6430 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
6431 0 : aMetadataStream.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" );
6432 0 : aMetadataStream.append( " <pdfaid:part>1</pdfaid:part>\n" );
6433 0 : aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" );
6434 0 : aMetadataStream.append( " </rdf:Description>\n" );
6435 : //... Dublin Core properties go here
6436 0 : if( !m_aContext.DocumentInfo.Title.isEmpty() ||
6437 0 : !m_aContext.DocumentInfo.Author.isEmpty() ||
6438 0 : !m_aContext.DocumentInfo.Subject.isEmpty() )
6439 : {
6440 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
6441 0 : aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" );
6442 0 : if( !m_aContext.DocumentInfo.Title.isEmpty() )
6443 : {
6444 : // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6445 0 : aMetadataStream.append( " <dc:title>\n" );
6446 0 : aMetadataStream.append( " <rdf:Alt>\n" );
6447 0 : aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
6448 0 : OUString aTitle;
6449 0 : escapeStringXML( m_aContext.DocumentInfo.Title, aTitle );
6450 0 : aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) );
6451 0 : aMetadataStream.append( "</rdf:li>\n" );
6452 0 : aMetadataStream.append( " </rdf:Alt>\n" );
6453 0 : aMetadataStream.append( " </dc:title>\n" );
6454 : }
6455 0 : if( !m_aContext.DocumentInfo.Author.isEmpty() )
6456 : {
6457 0 : aMetadataStream.append( " <dc:creator>\n" );
6458 0 : aMetadataStream.append( " <rdf:Seq>\n" );
6459 0 : aMetadataStream.append( " <rdf:li>" );
6460 0 : OUString aAuthor;
6461 0 : escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor );
6462 0 : aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) );
6463 0 : aMetadataStream.append( "</rdf:li>\n" );
6464 0 : aMetadataStream.append( " </rdf:Seq>\n" );
6465 0 : aMetadataStream.append( " </dc:creator>\n" );
6466 : }
6467 0 : if( !m_aContext.DocumentInfo.Subject.isEmpty() )
6468 : {
6469 : // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01)
6470 0 : aMetadataStream.append( " <dc:description>\n" );
6471 0 : aMetadataStream.append( " <rdf:Alt>\n" );
6472 0 : aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" );
6473 0 : OUString aSubject;
6474 0 : escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject );
6475 0 : aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) );
6476 0 : aMetadataStream.append( "</rdf:li>\n" );
6477 0 : aMetadataStream.append( " </rdf:Alt>\n" );
6478 0 : aMetadataStream.append( " </dc:description>\n" );
6479 : }
6480 0 : aMetadataStream.append( " </rdf:Description>\n" );
6481 : }
6482 :
6483 : //... PDF properties go here
6484 0 : if( !m_aContext.DocumentInfo.Producer.isEmpty() ||
6485 0 : !m_aContext.DocumentInfo.Keywords.isEmpty() )
6486 : {
6487 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
6488 0 : aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" );
6489 0 : if( !m_aContext.DocumentInfo.Producer.isEmpty() )
6490 : {
6491 0 : aMetadataStream.append( " <pdf:Producer>" );
6492 0 : OUString aProducer;
6493 0 : escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer );
6494 0 : aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) );
6495 0 : aMetadataStream.append( "</pdf:Producer>\n" );
6496 : }
6497 0 : if( !m_aContext.DocumentInfo.Keywords.isEmpty() )
6498 : {
6499 0 : aMetadataStream.append( " <pdf:Keywords>" );
6500 0 : OUString aKeywords;
6501 0 : escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords );
6502 0 : aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) );
6503 0 : aMetadataStream.append( "</pdf:Keywords>\n" );
6504 : }
6505 0 : aMetadataStream.append( " </rdf:Description>\n" );
6506 : }
6507 :
6508 0 : aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" );
6509 0 : aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" );
6510 0 : if( !m_aContext.DocumentInfo.Creator.isEmpty() )
6511 : {
6512 0 : aMetadataStream.append( " <xmp:CreatorTool>" );
6513 0 : OUString aCreator;
6514 0 : escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator );
6515 0 : aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) );
6516 0 : aMetadataStream.append( "</xmp:CreatorTool>\n" );
6517 : }
6518 : //creation date
6519 0 : aMetadataStream.append( " <xmp:CreateDate>" );
6520 0 : aMetadataStream.append( m_aCreationMetaDateString );
6521 0 : aMetadataStream.append( "</xmp:CreateDate>\n" );
6522 :
6523 0 : aMetadataStream.append( " </rdf:Description>\n" );
6524 0 : aMetadataStream.append( " </rdf:RDF>\n" );
6525 0 : aMetadataStream.append( "</x:xmpmeta>\n" );
6526 :
6527 : //add the padding
6528 0 : for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ )
6529 : {
6530 0 : aMetadataStream.append( " " );
6531 0 : if( nSpaces % 100 == 0 )
6532 0 : aMetadataStream.append( "\n" );
6533 : }
6534 :
6535 0 : aMetadataStream.append( "<?xpacket end=\"w\"?>\n" );
6536 :
6537 0 : OStringBuffer aMetadataObj( 1024 );
6538 :
6539 0 : aMetadataObj.append( nObject );
6540 0 : aMetadataObj.append( " 0 obj\n" );
6541 :
6542 0 : aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " );
6543 :
6544 0 : aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() );
6545 0 : aMetadataObj.append( ">>\nstream\n" );
6546 0 : if ( !writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
6547 0 : return 0;
6548 : //emit the stream
6549 0 : if ( !writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) )
6550 0 : return 0;
6551 :
6552 0 : aMetadataObj.setLength( 0 );
6553 0 : aMetadataObj.append( "\nendstream\nendobj\n\n" );
6554 0 : if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) )
6555 0 : nObject = 0;
6556 : }
6557 : else
6558 0 : nObject = 0;
6559 :
6560 0 : return nObject;
6561 : }
6562 : //<---i59651
6563 :
6564 0 : bool PDFWriterImpl::emitTrailer()
6565 : {
6566 : // emit doc info
6567 0 : sal_Int32 nDocInfoObject = emitInfoDict( );
6568 :
6569 0 : sal_Int32 nSecObject = 0;
6570 :
6571 0 : if( m_aContext.Encryption.Encrypt() )
6572 : {
6573 : //emit the security information
6574 : //must be emitted as indirect dictionary object, since
6575 : //Acrobat Reader 5 works only with this kind of implementation
6576 0 : nSecObject = createObject();
6577 :
6578 0 : if( updateObject( nSecObject ) )
6579 : {
6580 0 : OStringBuffer aLineS( 1024 );
6581 0 : aLineS.append( nSecObject );
6582 : aLineS.append( " 0 obj\n"
6583 0 : "<</Filter/Standard/V " );
6584 : // check the version
6585 0 : if( m_aContext.Encryption.Security128bit )
6586 0 : aLineS.append( "2/Length 128/R 3" );
6587 : else
6588 0 : aLineS.append( "1/R 2" );
6589 :
6590 : // emit the owner password, must not be encrypted
6591 0 : aLineS.append( "/O(" );
6592 0 : appendLiteralString( (const sal_Char*)&m_aContext.Encryption.OValue[0], sal_Int32(m_aContext.Encryption.OValue.size()), aLineS );
6593 0 : aLineS.append( ")/U(" );
6594 0 : appendLiteralString( (const sal_Char*)&m_aContext.Encryption.UValue[0], sal_Int32(m_aContext.Encryption.UValue.size()), aLineS );
6595 0 : aLineS.append( ")/P " );// the permission set
6596 0 : aLineS.append( m_nAccessPermissions );
6597 0 : aLineS.append( ">>\nendobj\n\n" );
6598 0 : if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) )
6599 0 : nSecObject = 0;
6600 : }
6601 : else
6602 0 : nSecObject = 0;
6603 : }
6604 : // emit xref table
6605 : // remember start
6606 0 : sal_uInt64 nXRefOffset = 0;
6607 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) );
6608 0 : CHECK_RETURN( writeBuffer( "xref\n", 5 ) );
6609 :
6610 0 : sal_Int32 nObjects = m_aObjects.size();
6611 0 : OStringBuffer aLine;
6612 0 : aLine.append( "0 " );
6613 0 : aLine.append( (sal_Int32)(nObjects+1) );
6614 0 : aLine.append( "\n" );
6615 0 : aLine.append( "0000000000 65535 f \n" );
6616 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6617 :
6618 0 : for( sal_Int32 i = 0; i < nObjects; i++ )
6619 : {
6620 0 : aLine.setLength( 0 );
6621 0 : OString aOffset = OString::number( m_aObjects[i] );
6622 0 : for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ )
6623 0 : aLine.append( '0' );
6624 0 : aLine.append( aOffset );
6625 0 : aLine.append( " 00000 n \n" );
6626 : DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" );
6627 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6628 0 : }
6629 :
6630 : // prepare document checksum
6631 0 : OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 );
6632 0 : if( m_aDocDigest )
6633 : {
6634 : sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ];
6635 0 : rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) );
6636 0 : for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ )
6637 0 : appendHex( nMD5Sum[i], aDocChecksum );
6638 : }
6639 : // document id set in setDocInfo method
6640 : // emit trailer
6641 0 : aLine.setLength( 0 );
6642 : aLine.append( "trailer\n"
6643 0 : "<</Size " );
6644 0 : aLine.append( (sal_Int32)(nObjects+1) );
6645 0 : aLine.append( "/Root " );
6646 0 : aLine.append( m_nCatalogObject );
6647 0 : aLine.append( " 0 R\n" );
6648 0 : if( nSecObject )
6649 : {
6650 0 : aLine.append( "/Encrypt ");
6651 0 : aLine.append( nSecObject );
6652 0 : aLine.append( " 0 R\n" );
6653 : }
6654 0 : if( nDocInfoObject )
6655 : {
6656 0 : aLine.append( "/Info " );
6657 0 : aLine.append( nDocInfoObject );
6658 0 : aLine.append( " 0 R\n" );
6659 : }
6660 0 : if( ! m_aContext.Encryption.DocumentIdentifier.empty() )
6661 : {
6662 0 : aLine.append( "/ID [ <" );
6663 0 : for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6664 0 : it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6665 : {
6666 0 : appendHex( sal_Int8(*it), aLine );
6667 : }
6668 : aLine.append( ">\n"
6669 0 : "<" );
6670 0 : for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin();
6671 0 : it != m_aContext.Encryption.DocumentIdentifier.end(); ++it )
6672 : {
6673 0 : appendHex( sal_Int8(*it), aLine );
6674 : }
6675 0 : aLine.append( "> ]\n" );
6676 : }
6677 0 : if( !aDocChecksum.isEmpty() )
6678 : {
6679 0 : aLine.append( "/DocChecksum /" );
6680 0 : aLine.append( aDocChecksum.makeStringAndClear() );
6681 0 : aLine.append( "\n" );
6682 : }
6683 0 : if( m_aAdditionalStreams.size() > 0 )
6684 : {
6685 0 : aLine.append( "/AdditionalStreams [" );
6686 0 : for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ )
6687 : {
6688 0 : aLine.append( "/" );
6689 0 : appendName( m_aAdditionalStreams[i].m_aMimeType, aLine );
6690 0 : aLine.append( " " );
6691 0 : aLine.append( m_aAdditionalStreams[i].m_nStreamObject );
6692 0 : aLine.append( " 0 R\n" );
6693 : }
6694 0 : aLine.append( "]\n" );
6695 : }
6696 : aLine.append( ">>\n"
6697 0 : "startxref\n" );
6698 0 : aLine.append( (sal_Int64)nXRefOffset );
6699 : aLine.append( "\n"
6700 0 : "%%EOF\n" );
6701 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
6702 :
6703 0 : return true;
6704 : }
6705 :
6706 : struct AnnotationSortEntry
6707 : {
6708 : sal_Int32 nTabOrder;
6709 : sal_Int32 nObject;
6710 : sal_Int32 nWidgetIndex;
6711 :
6712 0 : AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) :
6713 : nTabOrder( nTab ),
6714 : nObject( nObj ),
6715 0 : nWidgetIndex( nI )
6716 0 : {}
6717 : };
6718 :
6719 0 : struct AnnotSortContainer
6720 : {
6721 : std::set< sal_Int32 > aObjects;
6722 : std::vector< AnnotationSortEntry > aSortedAnnots;
6723 : };
6724 :
6725 : struct AnnotSorterLess
6726 : {
6727 : std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets;
6728 :
6729 0 : AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {}
6730 :
6731 0 : bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight )
6732 : {
6733 0 : if( rLeft.nTabOrder < rRight.nTabOrder )
6734 0 : return true;
6735 0 : if( rRight.nTabOrder < rLeft.nTabOrder )
6736 0 : return false;
6737 0 : if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 )
6738 0 : return false;
6739 0 : if( rRight.nWidgetIndex < 0 )
6740 0 : return true;
6741 0 : if( rLeft.nWidgetIndex < 0 )
6742 0 : return false;
6743 : // remember: widget rects are in PDF coordinates, so they are ordered down up
6744 0 : if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() >
6745 0 : m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() )
6746 0 : return true;
6747 0 : if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() >
6748 0 : m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() )
6749 0 : return false;
6750 0 : if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() <
6751 0 : m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() )
6752 0 : return true;
6753 0 : return false;
6754 : }
6755 : };
6756 :
6757 0 : void PDFWriterImpl::sortWidgets()
6758 : {
6759 : // sort widget annotations on each page as per their
6760 : // TabOrder attribute
6761 0 : boost::unordered_map< sal_Int32, AnnotSortContainer > sorted;
6762 0 : int nWidgets = m_aWidgets.size();
6763 0 : for( int nW = 0; nW < nWidgets; nW++ )
6764 : {
6765 0 : const PDFWidget& rWidget = m_aWidgets[nW];
6766 0 : if( rWidget.m_nPage >= 0 )
6767 : {
6768 0 : AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ];
6769 : // optimize vector allocation
6770 0 : if( rCont.aSortedAnnots.empty() )
6771 0 : rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() );
6772 : // insert widget to tab sorter
6773 : // RadioButtons are not page annotations, only their individual check boxes are
6774 0 : if( rWidget.m_eType != PDFWriter::RadioButton )
6775 : {
6776 0 : rCont.aObjects.insert( rWidget.m_nObject );
6777 0 : rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) );
6778 : }
6779 : }
6780 : }
6781 0 : for( boost::unordered_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it )
6782 : {
6783 : // append entries for non widget annotations
6784 0 : PDFPage& rPage = m_aPages[ it->first ];
6785 0 : unsigned int nAnnots = rPage.m_aAnnotations.size();
6786 0 : for( unsigned int nA = 0; nA < nAnnots; nA++ )
6787 0 : if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end())
6788 0 : it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) );
6789 :
6790 0 : AnnotSorterLess aLess( m_aWidgets );
6791 0 : std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess );
6792 : // sanity check
6793 0 : if( it->second.aSortedAnnots.size() == nAnnots)
6794 : {
6795 0 : for( unsigned int nA = 0; nA < nAnnots; nA++ )
6796 0 : rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject;
6797 : }
6798 : else
6799 : {
6800 : DBG_ASSERT( false, "wrong number of sorted annotations" );
6801 : #if OSL_DEBUG_LEVEL > 0
6802 : fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n"
6803 : " %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots );
6804 : #endif
6805 : }
6806 0 : }
6807 :
6808 : // FIXME: implement tab order in structure tree for PDF 1.5
6809 0 : }
6810 :
6811 : namespace vcl {
6812 : class PDFStreamIf :
6813 : public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream >
6814 : {
6815 : PDFWriterImpl* m_pWriter;
6816 : bool m_bWrite;
6817 : public:
6818 0 : PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {}
6819 : virtual ~PDFStreamIf();
6820 :
6821 : virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw(std::exception) SAL_OVERRIDE;
6822 : virtual void SAL_CALL flush() throw(std::exception) SAL_OVERRIDE;
6823 : virtual void SAL_CALL closeOutput() throw(std::exception) SAL_OVERRIDE;
6824 : };
6825 : }
6826 :
6827 0 : PDFStreamIf::~PDFStreamIf()
6828 : {
6829 0 : }
6830 :
6831 0 : void SAL_CALL PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw(std::exception)
6832 : {
6833 0 : if( m_bWrite && aData.getLength() )
6834 : {
6835 0 : sal_Int32 nBytes = aData.getLength();
6836 0 : m_pWriter->writeBuffer( aData.getConstArray(), nBytes );
6837 : }
6838 0 : }
6839 :
6840 0 : void SAL_CALL PDFStreamIf::flush() throw(std::exception)
6841 : {
6842 0 : }
6843 :
6844 0 : void SAL_CALL PDFStreamIf::closeOutput() throw(std::exception)
6845 : {
6846 0 : m_bWrite = false;
6847 0 : }
6848 :
6849 0 : bool PDFWriterImpl::emitAdditionalStreams()
6850 : {
6851 0 : unsigned int nStreams = m_aAdditionalStreams.size();
6852 0 : for( unsigned int i = 0; i < nStreams; i++ )
6853 : {
6854 0 : PDFAddStream& rStream = m_aAdditionalStreams[i];
6855 0 : rStream.m_nStreamObject = createObject();
6856 0 : sal_Int32 nSizeObject = createObject();
6857 :
6858 0 : if( ! updateObject( rStream.m_nStreamObject ) )
6859 0 : return false;
6860 :
6861 0 : OStringBuffer aLine;
6862 0 : aLine.append( rStream.m_nStreamObject );
6863 0 : aLine.append( " 0 obj\n<</Length " );
6864 0 : aLine.append( nSizeObject );
6865 0 : aLine.append( " 0 R" );
6866 0 : if( rStream.m_bCompress )
6867 0 : aLine.append( "/Filter/FlateDecode" );
6868 0 : aLine.append( ">>\nstream\n" );
6869 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6870 0 : return false;
6871 0 : sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0;
6872 0 : if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) )
6873 : {
6874 0 : osl_closeFile( m_aFile );
6875 0 : m_bOpen = false;
6876 : }
6877 0 : if( rStream.m_bCompress )
6878 0 : beginCompression();
6879 :
6880 0 : checkAndEnableStreamEncryption( rStream.m_nStreamObject );
6881 0 : com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) );
6882 0 : rStream.m_pStream->write( xStream );
6883 0 : xStream.clear();
6884 0 : delete rStream.m_pStream;
6885 0 : rStream.m_pStream = NULL;
6886 0 : disableStreamEncryption();
6887 :
6888 0 : if( rStream.m_bCompress )
6889 0 : endCompression();
6890 :
6891 0 : if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) )
6892 : {
6893 0 : osl_closeFile( m_aFile );
6894 0 : m_bOpen = false;
6895 0 : return false;
6896 : }
6897 0 : if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) )
6898 0 : return false ;
6899 : // emit stream length object
6900 0 : if( ! updateObject( nSizeObject ) )
6901 0 : return false;
6902 0 : aLine.setLength( 0 );
6903 0 : aLine.append( nSizeObject );
6904 0 : aLine.append( " 0 obj\n" );
6905 0 : aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) );
6906 0 : aLine.append( "\nendobj\n\n" );
6907 0 : if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) )
6908 0 : return false;
6909 0 : }
6910 0 : return true;
6911 : }
6912 :
6913 0 : bool PDFWriterImpl::emit()
6914 : {
6915 0 : endPage();
6916 :
6917 : // resort structure tree and annotations if necessary
6918 : // needed for widget tab order
6919 0 : sortWidgets();
6920 :
6921 : #if !defined(ANDROID) && !defined(IOS)
6922 0 : if( m_aContext.SignPDF )
6923 : {
6924 : // sign the document
6925 0 : PDFWriter::SignatureWidget aSignature;
6926 0 : aSignature.Name = "Signature1";
6927 0 : createControl( aSignature, 0 );
6928 : }
6929 : #endif
6930 :
6931 : // emit additional streams
6932 0 : CHECK_RETURN( emitAdditionalStreams() );
6933 :
6934 : // emit catalog
6935 0 : CHECK_RETURN( emitCatalog() );
6936 :
6937 : #if !defined(ANDROID) && !defined(IOS)
6938 0 : if (m_nSignatureObject != -1) // if document is signed, emit sigdict
6939 0 : CHECK_RETURN( emitSignature() );
6940 : #endif
6941 :
6942 : // emit trailer
6943 0 : CHECK_RETURN( emitTrailer() );
6944 :
6945 : #if !defined(ANDROID) && !defined(IOS)
6946 0 : if (m_nSignatureObject != -1) // finalize the signature
6947 0 : CHECK_RETURN( finalizeSignature() );
6948 : #endif
6949 :
6950 0 : osl_closeFile( m_aFile );
6951 0 : m_bOpen = false;
6952 :
6953 0 : return true;
6954 : }
6955 :
6956 0 : std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors()
6957 : {
6958 0 : return m_aErrors;
6959 : }
6960 :
6961 0 : sal_Int32 PDFWriterImpl::getSystemFont( const Font& i_rFont )
6962 : {
6963 0 : getReferenceDevice()->Push();
6964 0 : getReferenceDevice()->SetFont( i_rFont );
6965 0 : getReferenceDevice()->ImplNewFont();
6966 :
6967 0 : const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6968 0 : sal_Int32 nFontID = 0;
6969 0 : FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont );
6970 0 : if( it != m_aSystemFonts.end() )
6971 0 : nFontID = it->second.m_nNormalFontID;
6972 : else
6973 : {
6974 0 : nFontID = m_nNextFID++;
6975 0 : m_aSystemFonts[ pDevFont ] = EmbedFont();
6976 0 : m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID;
6977 : }
6978 :
6979 0 : getReferenceDevice()->Pop();
6980 0 : getReferenceDevice()->ImplNewFont();
6981 :
6982 0 : return nFontID;
6983 : }
6984 :
6985 0 : void PDFWriterImpl::registerGlyphs( int nGlyphs,
6986 : sal_GlyphId* pGlyphs,
6987 : sal_Int32* pGlyphWidths,
6988 : sal_Ucs* pUnicodes,
6989 : sal_Int32* pUnicodesPerGlyph,
6990 : sal_uInt8* pMappedGlyphs,
6991 : sal_Int32* pMappedFontObjects,
6992 : const PhysicalFontFace* pFallbackFonts[] )
6993 : {
6994 0 : const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData;
6995 0 : sal_Ucs* pCurUnicode = pUnicodes;
6996 0 : for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ )
6997 : {
6998 0 : const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB);
6999 0 : const PhysicalFontFace* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont;
7000 :
7001 0 : if( pCurrentFont->mbSubsettable )
7002 : {
7003 0 : FontSubset& rSubset = m_aSubsets[ pCurrentFont ];
7004 : // search for font specific glyphID
7005 0 : FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId );
7006 0 : if( it != rSubset.m_aMapping.end() )
7007 : {
7008 0 : pMappedFontObjects[i] = it->second.m_nFontID;
7009 0 : pMappedGlyphs[i] = it->second.m_nSubsetGlyphID;
7010 : }
7011 : else
7012 : {
7013 : // create new subset if necessary
7014 0 : if( rSubset.m_aSubsets.empty()
7015 0 : || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) )
7016 : {
7017 0 : rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) );
7018 : }
7019 :
7020 : // copy font id
7021 0 : pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID;
7022 : // create new glyph in subset
7023 0 : sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1);
7024 0 : pMappedGlyphs[i] = nNewId;
7025 :
7026 : // add new glyph to emitted font subset
7027 0 : GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ];
7028 0 : rNewGlyphEmit.setGlyphId( nNewId );
7029 0 : for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ )
7030 0 : rNewGlyphEmit.addCode( pCurUnicode[n] );
7031 :
7032 : // add new glyph to font mapping
7033 0 : Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ];
7034 0 : rNewGlyph.m_nFontID = pMappedFontObjects[i];
7035 0 : rNewGlyph.m_nSubsetGlyphID = nNewId;
7036 : }
7037 0 : getReferenceDevice()->ImplGetGraphics();
7038 0 : const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0);
7039 0 : pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont,
7040 : nFontGlyphId,
7041 : bVertical,
7042 0 : m_pReferenceDevice->mpGraphics );
7043 : }
7044 0 : else if( pCurrentFont->IsEmbeddable() )
7045 : {
7046 0 : sal_Int32 nFontID = 0;
7047 0 : FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont );
7048 0 : if( it != m_aEmbeddedFonts.end() )
7049 0 : nFontID = it->second.m_nNormalFontID;
7050 : else
7051 : {
7052 0 : nFontID = m_nNextFID++;
7053 0 : m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont();
7054 0 : m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID;
7055 : }
7056 0 : EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont];
7057 :
7058 0 : const Ucs2SIntMap* pEncoding = NULL;
7059 0 : const Ucs2OStrMap* pNonEncoded = NULL;
7060 0 : getReferenceDevice()->ImplGetGraphics();
7061 0 : pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded );
7062 :
7063 0 : Ucs2SIntMap::const_iterator enc_it;
7064 0 : Ucs2OStrMap::const_iterator nonenc_it;
7065 :
7066 0 : sal_Int32 nCurFontID = nFontID;
7067 0 : sal_Ucs cChar = *pCurUnicode;
7068 0 : if( pEncoding )
7069 : {
7070 0 : enc_it = pEncoding->find( cChar );
7071 0 : if( enc_it != pEncoding->end() && enc_it->second > 0 )
7072 : {
7073 : DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" );
7074 0 : cChar = (sal_Ucs)enc_it->second;
7075 : }
7076 0 : else if( (enc_it == pEncoding->end() || enc_it->second == -1) &&
7077 0 : pNonEncoded &&
7078 0 : (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() )
7079 : {
7080 0 : nCurFontID = 0;
7081 : // find non encoded glyph
7082 0 : for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it )
7083 : {
7084 0 : if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() )
7085 : {
7086 0 : nCurFontID = nec_it->m_nFontID;
7087 0 : cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ];
7088 0 : break;
7089 : }
7090 : }
7091 0 : if( nCurFontID == 0 ) // new nonencoded glyph
7092 : {
7093 0 : if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 )
7094 : {
7095 0 : rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() );
7096 0 : rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++;
7097 : }
7098 0 : EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back();
7099 0 : rEncoding.m_aEncVector.push_back( EmbedCode() );
7100 0 : rEncoding.m_aEncVector.back().m_aUnicode = cChar;
7101 0 : rEncoding.m_aEncVector.back().m_aName = nonenc_it->second;
7102 0 : rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1);
7103 0 : nCurFontID = rEncoding.m_nFontID;
7104 0 : cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ];
7105 : }
7106 : }
7107 : else
7108 0 : pEncoding = NULL;
7109 : }
7110 0 : if( ! pEncoding )
7111 : {
7112 0 : if( cChar & 0xff00 )
7113 : {
7114 : // some characters can be used by conversion
7115 0 : if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area
7116 0 : cChar -= 0xf000;
7117 : else
7118 : {
7119 0 : OString aChar(&cChar, 1, RTL_TEXTENCODING_MS_1252);
7120 0 : cChar = ((sal_Ucs)aChar[0]) & 0x00ff;
7121 : }
7122 : }
7123 : }
7124 :
7125 0 : pMappedGlyphs[ i ] = (sal_Int8)cChar;
7126 0 : pMappedFontObjects[ i ] = nCurFontID;
7127 0 : pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont,
7128 : (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR,
7129 : false,
7130 0 : m_pReferenceDevice->mpGraphics );
7131 : }
7132 : }
7133 0 : }
7134 :
7135 0 : void PDFWriterImpl::drawRelief( SalLayout& rLayout, const OUString& rText, bool bTextLines )
7136 : {
7137 0 : push( PUSH_ALL );
7138 :
7139 0 : FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief();
7140 :
7141 0 : Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor();
7142 0 : Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7143 0 : Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7144 0 : Color aReliefColor( COL_LIGHTGRAY );
7145 0 : if( aTextColor == COL_BLACK )
7146 0 : aTextColor = Color( COL_WHITE );
7147 0 : if( aTextLineColor == COL_BLACK )
7148 0 : aTextLineColor = Color( COL_WHITE );
7149 0 : if( aOverlineColor == COL_BLACK )
7150 0 : aOverlineColor = Color( COL_WHITE );
7151 0 : if( aTextColor == COL_WHITE )
7152 0 : aReliefColor = Color( COL_BLACK );
7153 :
7154 0 : Font aSetFont = m_aCurrentPDFState.m_aFont;
7155 0 : aSetFont.SetRelief( RELIEF_NONE );
7156 0 : aSetFont.SetShadow( false );
7157 :
7158 0 : aSetFont.SetColor( aReliefColor );
7159 0 : setTextLineColor( aReliefColor );
7160 0 : setOverlineColor( aReliefColor );
7161 0 : setFont( aSetFont );
7162 0 : long nOff = 1 + getReferenceDevice()->mnDPIX/300;
7163 0 : if( eRelief == RELIEF_ENGRAVED )
7164 0 : nOff = -nOff;
7165 :
7166 0 : rLayout.DrawOffset() += Point( nOff, nOff );
7167 0 : updateGraphicsState();
7168 0 : drawLayout( rLayout, rText, bTextLines );
7169 :
7170 0 : rLayout.DrawOffset() -= Point( nOff, nOff );
7171 0 : setTextLineColor( aTextLineColor );
7172 0 : setOverlineColor( aOverlineColor );
7173 0 : aSetFont.SetColor( aTextColor );
7174 0 : setFont( aSetFont );
7175 0 : updateGraphicsState();
7176 0 : drawLayout( rLayout, rText, bTextLines );
7177 :
7178 : // clean up the mess
7179 0 : pop();
7180 0 : }
7181 :
7182 0 : void PDFWriterImpl::drawShadow( SalLayout& rLayout, const OUString& rText, bool bTextLines )
7183 : {
7184 0 : Font aSaveFont = m_aCurrentPDFState.m_aFont;
7185 0 : Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor;
7186 0 : Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
7187 :
7188 0 : Font& rFont = m_aCurrentPDFState.m_aFont;
7189 0 : if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 )
7190 0 : rFont.SetColor( Color( COL_LIGHTGRAY ) );
7191 : else
7192 0 : rFont.SetColor( Color( COL_BLACK ) );
7193 0 : rFont.SetShadow( false );
7194 0 : rFont.SetOutline( false );
7195 0 : setFont( rFont );
7196 0 : setTextLineColor( rFont.GetColor() );
7197 0 : setOverlineColor( rFont.GetColor() );
7198 0 : updateGraphicsState();
7199 :
7200 0 : long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24);
7201 0 : if( rFont.IsOutline() )
7202 0 : nOff++;
7203 0 : rLayout.DrawBase() += Point( nOff, nOff );
7204 0 : drawLayout( rLayout, rText, bTextLines );
7205 0 : rLayout.DrawBase() -= Point( nOff, nOff );
7206 :
7207 0 : setFont( aSaveFont );
7208 0 : setTextLineColor( aSaveTextLineColor );
7209 0 : setOverlineColor( aSaveOverlineColor );
7210 0 : updateGraphicsState();
7211 0 : }
7212 :
7213 0 : void PDFWriterImpl::drawVerticalGlyphs(
7214 : const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7215 : OStringBuffer& rLine,
7216 : const Point& rAlignOffset,
7217 : const Matrix3& rRotScale,
7218 : double fAngle,
7219 : double fXScale,
7220 : double fSkew,
7221 : sal_Int32 nFontHeight )
7222 : {
7223 0 : long nXOffset = 0;
7224 0 : Point aCurPos( rGlyphs[0].m_aPos );
7225 0 : aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7226 0 : aCurPos += rAlignOffset;
7227 0 : for( size_t i = 0; i < rGlyphs.size(); i++ )
7228 : {
7229 : // have to emit each glyph on its own
7230 0 : double fDeltaAngle = 0.0;
7231 0 : double fYScale = 1.0;
7232 0 : double fTempXScale = fXScale;
7233 0 : double fSkewB = fSkew;
7234 0 : double fSkewA = 0.0;
7235 :
7236 0 : Point aDeltaPos;
7237 0 : if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL )
7238 : {
7239 0 : fDeltaAngle = M_PI/2.0;
7240 0 : aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent();
7241 0 : aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale);
7242 0 : fYScale = fXScale;
7243 0 : fTempXScale = 1.0;
7244 0 : fSkewA = -fSkewB;
7245 0 : fSkewB = 0.0;
7246 : }
7247 0 : else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR )
7248 : {
7249 0 : fDeltaAngle = -M_PI/2.0;
7250 0 : aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale);
7251 0 : aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent();
7252 0 : fYScale = fXScale;
7253 0 : fTempXScale = 1.0;
7254 0 : fSkewA = fSkewB;
7255 0 : fSkewB = 0.0;
7256 : }
7257 0 : aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) );
7258 0 : if( i < rGlyphs.size()-1 )
7259 : // #i120627# the text on the Y axis is reversed when export ppt file to PDF format
7260 : {
7261 0 : long nOffsetX = rGlyphs[i+1].m_aPos.X() - rGlyphs[i].m_aPos.X();
7262 0 : long nOffsetY = rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y();
7263 0 : nXOffset += (int)sqrt(double(nOffsetX*nOffsetX + nOffsetY*nOffsetY));
7264 : }
7265 0 : if( ! rGlyphs[i].m_nGlyphId )
7266 0 : continue;
7267 :
7268 0 : aDeltaPos = rRotScale.transform( aDeltaPos );
7269 :
7270 0 : Matrix3 aMat;
7271 0 : if( fSkewB != 0.0 || fSkewA != 0.0 )
7272 0 : aMat.skew( fSkewA, fSkewB );
7273 0 : aMat.scale( fTempXScale, fYScale );
7274 0 : aMat.rotate( fAngle+fDeltaAngle );
7275 0 : aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() );
7276 0 : aMat.append( m_aPages.back(), rLine );
7277 0 : rLine.append( " Tm" );
7278 0 : if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId )
7279 : {
7280 0 : rLine.append( " /F" );
7281 0 : rLine.append( rGlyphs[i].m_nMappedFontId );
7282 0 : rLine.append( ' ' );
7283 0 : m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7284 0 : rLine.append( " Tf" );
7285 : }
7286 0 : rLine.append( "<" );
7287 0 : appendHex( rGlyphs[i].m_nMappedGlyphId, rLine );
7288 0 : rLine.append( ">Tj\n" );
7289 0 : }
7290 0 : }
7291 :
7292 0 : void PDFWriterImpl::drawHorizontalGlyphs(
7293 : const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs,
7294 : OStringBuffer& rLine,
7295 : const Point& rAlignOffset,
7296 : double fAngle,
7297 : double fXScale,
7298 : double fSkew,
7299 : sal_Int32 nFontHeight,
7300 : sal_Int32 nPixelFontHeight
7301 : )
7302 : {
7303 : // horizontal (= normal) case
7304 :
7305 : // fill in run end indices
7306 : // end is marked by index of the first glyph of the next run
7307 : // a run is marked by same mapped font id and same Y position
7308 0 : std::vector< sal_uInt32 > aRunEnds;
7309 0 : aRunEnds.reserve( rGlyphs.size() );
7310 0 : for( size_t i = 1; i < rGlyphs.size(); i++ )
7311 : {
7312 0 : if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId ||
7313 0 : rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() )
7314 : {
7315 0 : aRunEnds.push_back(i);
7316 : }
7317 : }
7318 : // last run ends at last glyph
7319 0 : aRunEnds.push_back( rGlyphs.size() );
7320 :
7321 : // loop over runs of the same font
7322 0 : sal_uInt32 nBeginRun = 0;
7323 0 : for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ )
7324 : {
7325 : // setup text matrix
7326 0 : Point aCurPos = rGlyphs[nBeginRun].m_aPos;
7327 : // back transformation to current coordinate system
7328 0 : aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos );
7329 0 : aCurPos += rAlignOffset;
7330 : // the first run can be set with "Td" operator
7331 : // subsequent use of that operator would move
7332 : // the texline matrix relative to what was set before
7333 : // making use of that would drive us into rounding issues
7334 0 : Matrix3 aMat;
7335 0 : if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 )
7336 : {
7337 0 : m_aPages.back().appendPoint( aCurPos, rLine, false );
7338 0 : rLine.append( " Td " );
7339 : }
7340 : else
7341 : {
7342 0 : if( fSkew != 0.0 )
7343 0 : aMat.skew( 0.0, fSkew );
7344 0 : aMat.scale( fXScale, 1.0 );
7345 0 : aMat.rotate( fAngle );
7346 0 : aMat.translate( aCurPos.X(), aCurPos.Y() );
7347 0 : aMat.append( m_aPages.back(), rLine );
7348 0 : rLine.append( " Tm\n" );
7349 : }
7350 : // set up correct font
7351 0 : rLine.append( "/F" );
7352 0 : rLine.append( rGlyphs[nBeginRun].m_nMappedFontId );
7353 0 : rLine.append( ' ' );
7354 0 : m_aPages.back().appendMappedLength( nFontHeight, rLine, true );
7355 0 : rLine.append( " Tf" );
7356 :
7357 : // output glyphs using Tj or TJ
7358 0 : OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 );
7359 0 : aKernedLine.append( "[<" );
7360 0 : aUnkernedLine.append( '<' );
7361 0 : appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine );
7362 0 : appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine );
7363 :
7364 0 : aMat.invert();
7365 0 : bool bNeedKern = false;
7366 0 : for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ )
7367 : {
7368 0 : appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine );
7369 : // check if default glyph positioning is sufficient
7370 0 : const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos );
7371 0 : const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos );
7372 0 : double fAdvance = aThisPos.X() - aPrevPos.X();
7373 0 : fAdvance *= 1000.0 / nPixelFontHeight;
7374 0 : const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5);
7375 0 : if( nAdjustment != 0 )
7376 : {
7377 : // apply individual glyph positioning
7378 0 : bNeedKern = true;
7379 0 : aKernedLine.append( ">" );
7380 0 : aKernedLine.append( nAdjustment );
7381 0 : aKernedLine.append( "<" );
7382 : }
7383 0 : appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine );
7384 : }
7385 0 : aKernedLine.append( ">]TJ\n" );
7386 0 : aUnkernedLine.append( ">Tj\n" );
7387 : rLine.append(
7388 0 : (bNeedKern ? aKernedLine : aUnkernedLine).makeStringAndClear() );
7389 :
7390 : // set beginning of next run
7391 0 : nBeginRun = aRunEnds[nRun];
7392 0 : }
7393 0 : }
7394 :
7395 0 : void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool bTextLines )
7396 : {
7397 : // relief takes precedence over shadow (see outdev3.cxx)
7398 0 : if( m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE )
7399 : {
7400 0 : drawRelief( rLayout, rText, bTextLines );
7401 0 : return;
7402 : }
7403 0 : else if( m_aCurrentPDFState.m_aFont.IsShadow() )
7404 0 : drawShadow( rLayout, rText, bTextLines );
7405 :
7406 0 : OStringBuffer aLine( 512 );
7407 :
7408 0 : const int nMaxGlyphs = 256;
7409 :
7410 : sal_GlyphId pGlyphs[nMaxGlyphs];
7411 : sal_Int32 pGlyphWidths[nMaxGlyphs];
7412 : sal_uInt8 pMappedGlyphs[nMaxGlyphs];
7413 : sal_Int32 pMappedFontObjects[nMaxGlyphs];
7414 0 : std::vector<sal_Ucs> aUnicodes;
7415 0 : aUnicodes.reserve( nMaxGlyphs );
7416 : sal_Int32 pUnicodesPerGlyph[nMaxGlyphs];
7417 : int pCharPosAry[nMaxGlyphs];
7418 : sal_Int32 nAdvanceWidths[nMaxGlyphs];
7419 0 : const PhysicalFontFace* pFallbackFonts[nMaxGlyphs] = { NULL };
7420 0 : bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical();
7421 : int nGlyphs;
7422 0 : int nIndex = 0;
7423 0 : int nMinCharPos = 0, nMaxCharPos = rText.getLength()-1;
7424 0 : double fXScale = 1.0;
7425 0 : double fSkew = 0.0;
7426 0 : sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight;
7427 0 : TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
7428 :
7429 : // transform font height back to current units
7430 : // note: the layout calculates in outdevs device pixel !!
7431 0 : sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight );
7432 0 : if( m_aCurrentPDFState.m_aFont.GetWidth() )
7433 : {
7434 0 : Font aFont( m_aCurrentPDFState.m_aFont );
7435 0 : aFont.SetWidth( 0 );
7436 0 : FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont );
7437 0 : if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() )
7438 : {
7439 : fXScale =
7440 0 : (double)m_aCurrentPDFState.m_aFont.GetWidth() /
7441 0 : (double)aMetric.GetWidth();
7442 : }
7443 : // force state before GetFontMetric
7444 0 : m_pReferenceDevice->ImplNewFont();
7445 : }
7446 :
7447 : // perform artificial italics if necessary
7448 0 : if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL ||
7449 0 : m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) &&
7450 0 : !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL ||
7451 0 : m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE )
7452 : )
7453 : {
7454 0 : fSkew = M_PI/12.0;
7455 : }
7456 :
7457 : // if the mapmode is distorted we need to adjust for that also
7458 0 : if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() )
7459 : {
7460 0 : fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY());
7461 : }
7462 :
7463 0 : int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation();
7464 : // normalize angles
7465 0 : while( nAngle < 0 )
7466 0 : nAngle += 3600;
7467 0 : nAngle = nAngle % 3600;
7468 0 : double fAngle = (double)nAngle * M_PI / 1800.0;
7469 :
7470 0 : Matrix3 aRotScale;
7471 0 : aRotScale.scale( fXScale, 1.0 );
7472 0 : if( fAngle != 0.0 )
7473 0 : aRotScale.rotate( -fAngle );
7474 :
7475 0 : bool bPop = false;
7476 0 : bool bABold = false;
7477 : // artificial bold necessary ?
7478 0 : if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM &&
7479 0 : m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM )
7480 : {
7481 0 : if( ! bPop )
7482 0 : aLine.append( "q " );
7483 0 : bPop = true;
7484 0 : bABold = true;
7485 : }
7486 : // setup text colors (if necessary)
7487 0 : Color aStrokeColor( COL_TRANSPARENT );
7488 0 : Color aNonStrokeColor( COL_TRANSPARENT );
7489 :
7490 0 : if( m_aCurrentPDFState.m_aFont.IsOutline() )
7491 : {
7492 0 : aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7493 0 : aNonStrokeColor = Color( COL_WHITE );
7494 : }
7495 : else
7496 0 : aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7497 0 : if( bABold )
7498 0 : aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor();
7499 :
7500 0 : if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor )
7501 : {
7502 0 : if( ! bPop )
7503 0 : aLine.append( "q " );
7504 0 : bPop = true;
7505 0 : appendStrokingColor( aStrokeColor, aLine );
7506 0 : aLine.append( "\n" );
7507 : }
7508 0 : if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor )
7509 : {
7510 0 : if( ! bPop )
7511 0 : aLine.append( "q " );
7512 0 : bPop = true;
7513 0 : appendNonStrokingColor( aNonStrokeColor, aLine );
7514 0 : aLine.append( "\n" );
7515 : }
7516 :
7517 : // begin text object
7518 0 : aLine.append( "BT\n" );
7519 : // outline attribute ?
7520 0 : if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold )
7521 : {
7522 : // set correct text mode, set stroke width
7523 0 : aLine.append( "2 Tr " ); // fill, then stroke
7524 :
7525 0 : if( m_aCurrentPDFState.m_aFont.IsOutline() )
7526 : {
7527 : // unclear what to do in case of outline and artificial bold
7528 : // for the time being outline wins
7529 0 : aLine.append( "0.25 w \n" );
7530 : }
7531 : else
7532 : {
7533 0 : double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0;
7534 0 : m_aPages.back().appendMappedLength( fW, aLine );
7535 0 : aLine.append ( " w\n" );
7536 : }
7537 : }
7538 :
7539 0 : FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
7540 :
7541 : // collect the glyphs into a single array
7542 0 : const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686#
7543 0 : std::vector< PDFGlyph > aGlyphs;
7544 0 : aGlyphs.reserve( nTmpMaxGlyphs );
7545 : // first get all the glyphs and register them; coordinates still in Pixel
7546 0 : Point aGNGlyphPos;
7547 0 : while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry, pFallbackFonts )) != 0 )
7548 : {
7549 0 : aUnicodes.clear();
7550 0 : for( int i = 0; i < nGlyphs; i++ )
7551 : {
7552 : // default case: 1 glyph is one unicode
7553 0 : pUnicodesPerGlyph[i] = 1;
7554 0 : if( (pGlyphs[i] & GF_ISCHAR) )
7555 : {
7556 0 : aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) );
7557 : }
7558 0 : else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos )
7559 : {
7560 0 : int nChars = 1;
7561 0 : aUnicodes.push_back( rText[ pCharPosAry[i] ] );
7562 0 : pUnicodesPerGlyph[i] = 1;
7563 : // try to handle ligatures and such
7564 0 : if( i < nGlyphs-1 )
7565 : {
7566 0 : nChars = pCharPosAry[i+1] - pCharPosAry[i];
7567 : // #i115618# fix for simple RTL+CTL cases
7568 : // TODO: sanitize for RTL ligatures, more complex CTL, etc.
7569 0 : if( nChars < 0 )
7570 0 : nChars = -nChars;
7571 0 : else if( nChars == 0 )
7572 0 : nChars = 1;
7573 0 : pUnicodesPerGlyph[i] = nChars;
7574 0 : for( int n = 1; n < nChars; n++ )
7575 0 : aUnicodes.push_back( rText[ pCharPosAry[i] + n ] );
7576 : }
7577 : // #i36691# hack that is needed because currently the pGlyphs[]
7578 : // argument is ignored for embeddable fonts and so the layout
7579 : // engine's glyph work is ignored (i.e. char mirroring)
7580 : // TODO: a real solution would be to map the layout engine's
7581 : // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font)
7582 : // back to unicode and then to embeddable font's encoding
7583 0 : if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL )
7584 : {
7585 0 : size_t nI = aUnicodes.size()-1;
7586 0 : for( int n = 0; n < nChars; n++, nI-- )
7587 0 : aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI]));
7588 0 : }
7589 : }
7590 : else
7591 0 : aUnicodes.push_back( 0 );
7592 : // note: in case of ctl one character may result
7593 : // in multiple glyphs. The current SalLayout
7594 : // implementations set -1 then to indicate that no direct
7595 : // mapping is possible
7596 : }
7597 :
7598 0 : registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts );
7599 :
7600 0 : for( int i = 0; i < nGlyphs; i++ )
7601 : {
7602 : aGlyphs.push_back( PDFGlyph( aGNGlyphPos,
7603 : pGlyphWidths[i],
7604 0 : pGlyphs[i],
7605 : pMappedFontObjects[i],
7606 0 : pMappedGlyphs[i] ) );
7607 0 : if( bVertical )
7608 0 : aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7609 : else
7610 0 : aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel();
7611 : }
7612 : }
7613 :
7614 0 : Point aAlignOffset;
7615 0 : if ( eAlign == ALIGN_BOTTOM )
7616 0 : aAlignOffset.Y() -= aRefDevFontMetric.GetDescent();
7617 0 : else if ( eAlign == ALIGN_TOP )
7618 0 : aAlignOffset.Y() += aRefDevFontMetric.GetAscent();
7619 0 : if( aAlignOffset.X() || aAlignOffset.Y() )
7620 0 : aAlignOffset = aRotScale.transform( aAlignOffset );
7621 :
7622 : /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original
7623 : string contained only on of the UTF16 BOMs
7624 : */
7625 0 : if( ! aGlyphs.empty() )
7626 : {
7627 0 : if( bVertical )
7628 0 : drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight );
7629 : else
7630 0 : drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight );
7631 : }
7632 :
7633 : // end textobject
7634 0 : aLine.append( "ET\n" );
7635 0 : if( bPop )
7636 0 : aLine.append( "Q\n" );
7637 :
7638 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
7639 :
7640 : // draw eventual textlines
7641 0 : FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout();
7642 0 : FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline();
7643 0 : FontUnderline eOverline = m_aCurrentPDFState.m_aFont.GetOverline();
7644 0 : if( bTextLines &&
7645 : (
7646 0 : ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) ||
7647 0 : ( eOverline != UNDERLINE_NONE && eOverline != UNDERLINE_DONTKNOW ) ||
7648 0 : ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW )
7649 : )
7650 : )
7651 : {
7652 0 : bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont );
7653 0 : if( m_aCurrentPDFState.m_aFont.IsWordLineMode() )
7654 : {
7655 0 : Point aPos, aStartPt;
7656 0 : sal_Int32 nWidth = 0, nAdvance=0;
7657 0 : for( int nStart = 0;;)
7658 : {
7659 : sal_GlyphId aGlyphId;
7660 0 : if( !rLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) )
7661 0 : break;
7662 :
7663 0 : if( !rLayout.IsSpacingGlyph( aGlyphId ) )
7664 : {
7665 0 : if( !nWidth )
7666 0 : aStartPt = aPos;
7667 :
7668 0 : nWidth += nAdvance;
7669 : }
7670 0 : else if( nWidth > 0 )
7671 : {
7672 0 : drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7673 : m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7674 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7675 0 : nWidth = 0;
7676 : }
7677 0 : }
7678 :
7679 0 : if( nWidth > 0 )
7680 : {
7681 0 : drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7682 : m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7683 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7684 : }
7685 : }
7686 : else
7687 : {
7688 0 : Point aStartPt = rLayout.GetDrawPosition();
7689 0 : int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel();
7690 0 : drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ),
7691 : m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ),
7692 0 : eStrikeout, eUnderline, eOverline, bUnderlineAbove );
7693 : }
7694 : }
7695 :
7696 : // write eventual emphasis marks
7697 0 : if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
7698 : {
7699 0 : PolyPolygon aEmphPoly;
7700 0 : Rectangle aEmphRect1;
7701 0 : Rectangle aEmphRect2;
7702 : long nEmphYOff;
7703 : long nEmphWidth;
7704 : long nEmphHeight;
7705 : bool bEmphPolyLine;
7706 : FontEmphasisMark nEmphMark;
7707 :
7708 0 : push( PUSH_ALL );
7709 :
7710 0 : aLine.setLength( 0 );
7711 0 : aLine.append( "q\n" );
7712 :
7713 0 : nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont );
7714 0 : if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7715 0 : nEmphHeight = m_pReferenceDevice->mnEmphasisDescent;
7716 : else
7717 0 : nEmphHeight = m_pReferenceDevice->mnEmphasisAscent;
7718 : m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly,
7719 : bEmphPolyLine,
7720 : aEmphRect1,
7721 : aEmphRect2,
7722 : nEmphYOff,
7723 : nEmphWidth,
7724 : nEmphMark,
7725 : m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight),
7726 0 : m_pReferenceDevice->mpFontEntry->mnOrientation );
7727 0 : if ( bEmphPolyLine )
7728 : {
7729 0 : setLineColor( m_aCurrentPDFState.m_aFont.GetColor() );
7730 0 : setFillColor( Color( COL_TRANSPARENT ) );
7731 : }
7732 : else
7733 : {
7734 0 : setFillColor( m_aCurrentPDFState.m_aFont.GetColor() );
7735 0 : setLineColor( Color( COL_TRANSPARENT ) );
7736 : }
7737 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
7738 :
7739 0 : Point aOffset = Point(0,0);
7740 :
7741 0 : if ( nEmphMark & EMPHASISMARK_POS_BELOW )
7742 0 : aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff;
7743 : else
7744 0 : aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff;
7745 :
7746 0 : long nEmphWidth2 = nEmphWidth / 2;
7747 0 : long nEmphHeight2 = nEmphHeight / 2;
7748 0 : aOffset += Point( nEmphWidth2, nEmphHeight2 );
7749 :
7750 0 : if ( eAlign == ALIGN_BOTTOM )
7751 0 : aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent;
7752 0 : else if ( eAlign == ALIGN_TOP )
7753 0 : aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent;
7754 :
7755 0 : for( int nStart = 0;;)
7756 : {
7757 0 : Point aPos;
7758 : sal_GlyphId aGlyphId;
7759 : sal_Int32 nAdvance;
7760 0 : if( !rLayout.GetNextGlyphs( 1, &aGlyphId, aPos, nStart, &nAdvance ) )
7761 0 : break;
7762 :
7763 0 : if( !rLayout.IsSpacingGlyph( aGlyphId ) )
7764 : {
7765 0 : Point aAdjOffset = aOffset;
7766 0 : aAdjOffset.X() += (nAdvance - nEmphWidth) / 2;
7767 0 : aAdjOffset = aRotScale.transform( aAdjOffset );
7768 :
7769 0 : aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 );
7770 :
7771 0 : aPos += aAdjOffset;
7772 0 : aPos = m_pReferenceDevice->PixelToLogic( aPos );
7773 0 : drawEmphasisMark( aPos.X(), aPos.Y(),
7774 : aEmphPoly, bEmphPolyLine,
7775 0 : aEmphRect1, aEmphRect2 );
7776 : }
7777 0 : }
7778 :
7779 0 : writeBuffer( "Q\n", 2 );
7780 0 : pop();
7781 0 : }
7782 :
7783 : }
7784 :
7785 0 : void PDFWriterImpl::drawEmphasisMark( long nX, long nY,
7786 : const PolyPolygon& rPolyPoly, bool bPolyLine,
7787 : const Rectangle& rRect1, const Rectangle& rRect2 )
7788 : {
7789 : // TODO: pass nWidth as width of this mark
7790 : // long nWidth = 0;
7791 :
7792 0 : if ( rPolyPoly.Count() )
7793 : {
7794 0 : if ( bPolyLine )
7795 : {
7796 0 : Polygon aPoly = rPolyPoly.GetObject( 0 );
7797 0 : aPoly.Move( nX, nY );
7798 0 : drawPolyLine( aPoly );
7799 : }
7800 : else
7801 : {
7802 0 : PolyPolygon aPolyPoly = rPolyPoly;
7803 0 : aPolyPoly.Move( nX, nY );
7804 0 : drawPolyPolygon( aPolyPoly );
7805 : }
7806 : }
7807 :
7808 0 : if ( !rRect1.IsEmpty() )
7809 : {
7810 0 : Rectangle aRect( Point( nX+rRect1.Left(),
7811 0 : nY+rRect1.Top() ), rRect1.GetSize() );
7812 0 : drawRectangle( aRect );
7813 : }
7814 :
7815 0 : if ( !rRect2.IsEmpty() )
7816 : {
7817 0 : Rectangle aRect( Point( nX+rRect2.Left(),
7818 0 : nY+rRect2.Top() ), rRect2.GetSize() );
7819 :
7820 0 : drawRectangle( aRect );
7821 : }
7822 0 : }
7823 :
7824 0 : void PDFWriterImpl::drawText( const Point& rPos, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines )
7825 : {
7826 0 : MARK( "drawText" );
7827 :
7828 0 : updateGraphicsState();
7829 :
7830 : // get a layout from the OuputDevice's SalGraphics
7831 : // this also enforces font substitution and sets the font on SalGraphics
7832 0 : SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos );
7833 0 : if( pLayout )
7834 : {
7835 0 : drawLayout( *pLayout, rText, bTextLines );
7836 0 : pLayout->Release();
7837 : }
7838 0 : }
7839 :
7840 0 : void PDFWriterImpl::drawTextArray( const Point& rPos, const OUString& rText, const sal_Int32* pDXArray, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines )
7841 : {
7842 0 : MARK( "drawText with array" );
7843 :
7844 0 : updateGraphicsState();
7845 :
7846 : // get a layout from the OuputDevice's SalGraphics
7847 : // this also enforces font substitution and sets the font on SalGraphics
7848 0 : SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray );
7849 0 : if( pLayout )
7850 : {
7851 0 : drawLayout( *pLayout, rText, bTextLines );
7852 0 : pLayout->Release();
7853 : }
7854 0 : }
7855 :
7856 0 : void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines )
7857 : {
7858 0 : MARK( "drawStretchText" );
7859 :
7860 0 : updateGraphicsState();
7861 :
7862 : // get a layout from the OuputDevice's SalGraphics
7863 : // this also enforces font substitution and sets the font on SalGraphics
7864 0 : SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth );
7865 0 : if( pLayout )
7866 : {
7867 0 : drawLayout( *pLayout, rText, bTextLines );
7868 0 : pLayout->Release();
7869 : }
7870 0 : }
7871 :
7872 0 : void PDFWriterImpl::drawText( const Rectangle& rRect, const OUString& rOrigStr, sal_uInt16 nStyle, bool bTextLines )
7873 : {
7874 0 : long nWidth = rRect.GetWidth();
7875 0 : long nHeight = rRect.GetHeight();
7876 :
7877 0 : if ( nWidth <= 0 || nHeight <= 0 )
7878 0 : return;
7879 :
7880 0 : MARK( "drawText with rectangle" );
7881 :
7882 0 : updateGraphicsState();
7883 :
7884 : // clip with rectangle
7885 0 : OStringBuffer aLine;
7886 0 : aLine.append( "q " );
7887 0 : m_aPages.back().appendRect( rRect, aLine );
7888 0 : aLine.append( " W* n\n" );
7889 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
7890 :
7891 : // if disabled text is needed, put in here
7892 :
7893 0 : Point aPos = rRect.TopLeft();
7894 :
7895 0 : long nTextHeight = m_pReferenceDevice->GetTextHeight();
7896 0 : sal_Int32 nMnemonicPos = -1;
7897 :
7898 0 : OUString aStr = rOrigStr;
7899 0 : if ( nStyle & TEXT_DRAW_MNEMONIC )
7900 0 : aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos );
7901 :
7902 : // multiline text
7903 0 : if ( nStyle & TEXT_DRAW_MULTILINE )
7904 : {
7905 0 : OUString aLastLine;
7906 0 : ImplMultiTextLineInfo aMultiLineInfo;
7907 : ImplTextLineInfo* pLineInfo;
7908 : sal_Int32 i;
7909 : sal_Int32 nLines;
7910 : sal_Int32 nFormatLines;
7911 :
7912 0 : if ( nTextHeight )
7913 : {
7914 0 : ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice );
7915 0 : OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout );
7916 0 : nLines = nHeight/nTextHeight;
7917 0 : nFormatLines = aMultiLineInfo.Count();
7918 0 : if ( !nLines )
7919 0 : nLines = 1;
7920 0 : if ( nFormatLines > nLines )
7921 : {
7922 0 : if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
7923 : {
7924 : // handle last line
7925 0 : nFormatLines = nLines-1;
7926 :
7927 0 : pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
7928 0 : aLastLine = convertLineEnd(aStr.copy(pLineInfo->GetIndex()), LINEEND_LF);
7929 : // replace line feed by space
7930 0 : aLastLine = aLastLine.replace('\n', ' ');
7931 0 : aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle );
7932 0 : nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
7933 0 : nStyle |= TEXT_DRAW_TOP;
7934 : }
7935 : }
7936 :
7937 : // vertical alignment
7938 0 : if ( nStyle & TEXT_DRAW_BOTTOM )
7939 0 : aPos.Y() += nHeight-(nFormatLines*nTextHeight);
7940 0 : else if ( nStyle & TEXT_DRAW_VCENTER )
7941 0 : aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
7942 :
7943 : // draw all lines excluding the last
7944 0 : for ( i = 0; i < nFormatLines; i++ )
7945 : {
7946 0 : pLineInfo = aMultiLineInfo.GetLine( i );
7947 0 : if ( nStyle & TEXT_DRAW_RIGHT )
7948 0 : aPos.X() += nWidth-pLineInfo->GetWidth();
7949 0 : else if ( nStyle & TEXT_DRAW_CENTER )
7950 0 : aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
7951 0 : sal_Int32 nIndex = pLineInfo->GetIndex();
7952 0 : sal_Int32 nLineLen = pLineInfo->GetLen();
7953 0 : drawText( aPos, aStr, nIndex, nLineLen, bTextLines );
7954 : // mnemonics should not appear in documents,
7955 : // if the need arises, put them in here
7956 0 : aPos.Y() += nTextHeight;
7957 0 : aPos.X() = rRect.Left();
7958 : }
7959 :
7960 : // output last line left adjusted since it was shortened
7961 0 : if (!aLastLine.isEmpty())
7962 0 : drawText( aPos, aLastLine, 0, aLastLine.getLength(), bTextLines );
7963 0 : }
7964 : }
7965 : else
7966 : {
7967 0 : long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7968 :
7969 : // Evt. Text kuerzen
7970 0 : if ( nTextWidth > nWidth )
7971 : {
7972 0 : if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) )
7973 : {
7974 0 : aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle );
7975 0 : nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
7976 0 : nStyle |= TEXT_DRAW_LEFT;
7977 0 : nTextWidth = m_pReferenceDevice->GetTextWidth( aStr );
7978 : }
7979 : }
7980 :
7981 : // vertical alignment
7982 0 : if ( nStyle & TEXT_DRAW_RIGHT )
7983 0 : aPos.X() += nWidth-nTextWidth;
7984 0 : else if ( nStyle & TEXT_DRAW_CENTER )
7985 0 : aPos.X() += (nWidth-nTextWidth)/2;
7986 :
7987 0 : if ( nStyle & TEXT_DRAW_BOTTOM )
7988 0 : aPos.Y() += nHeight-nTextHeight;
7989 0 : else if ( nStyle & TEXT_DRAW_VCENTER )
7990 0 : aPos.Y() += (nHeight-nTextHeight)/2;
7991 :
7992 : // mnemonics should be inserted here if the need arises
7993 :
7994 : // draw the actual text
7995 0 : drawText( aPos, aStr, 0, aStr.getLength(), bTextLines );
7996 : }
7997 :
7998 : // reset clip region to original value
7999 0 : aLine.setLength( 0 );
8000 0 : aLine.append( "Q\n" );
8001 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8002 : }
8003 :
8004 0 : void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop )
8005 : {
8006 0 : MARK( "drawLine" );
8007 :
8008 0 : updateGraphicsState();
8009 :
8010 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8011 0 : return;
8012 :
8013 0 : OStringBuffer aLine;
8014 0 : m_aPages.back().appendPoint( rStart, aLine );
8015 0 : aLine.append( " m " );
8016 0 : m_aPages.back().appendPoint( rStop, aLine );
8017 0 : aLine.append( " l S\n" );
8018 :
8019 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8020 : }
8021 :
8022 0 : void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo )
8023 : {
8024 0 : MARK( "drawLine with LineInfo" );
8025 0 : updateGraphicsState();
8026 :
8027 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
8028 0 : return;
8029 :
8030 0 : if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 )
8031 : {
8032 0 : drawLine( rStart, rStop );
8033 0 : return;
8034 : }
8035 :
8036 0 : OStringBuffer aLine;
8037 :
8038 0 : aLine.append( "q " );
8039 0 : if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
8040 : {
8041 0 : m_aPages.back().appendPoint( rStart, aLine );
8042 0 : aLine.append( " m " );
8043 0 : m_aPages.back().appendPoint( rStop, aLine );
8044 0 : aLine.append( " l S Q\n" );
8045 :
8046 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8047 : }
8048 : else
8049 : {
8050 0 : PDFWriter::ExtLineInfo aInfo;
8051 0 : convertLineInfoToExtLineInfo( rInfo, aInfo );
8052 0 : Point aPolyPoints[2] = { rStart, rStop };
8053 0 : Polygon aPoly( 2, aPolyPoints );
8054 0 : drawPolyLine( aPoly, aInfo );
8055 0 : }
8056 : }
8057 :
8058 : #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x )
8059 :
8060 0 : void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8061 : {
8062 : // note: units in pFontEntry are ref device pixel
8063 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
8064 0 : long nLineHeight = 0;
8065 0 : long nLinePos = 0;
8066 :
8067 0 : appendStrokingColor( aColor, aLine );
8068 0 : aLine.append( "\n" );
8069 :
8070 0 : if ( bIsAbove )
8071 : {
8072 0 : if ( !pFontEntry->maMetric.mnAboveWUnderlineSize )
8073 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
8074 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize );
8075 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset );
8076 : }
8077 : else
8078 : {
8079 0 : if ( !pFontEntry->maMetric.mnWUnderlineSize )
8080 0 : m_pReferenceDevice->ImplInitTextLineSize();
8081 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize );
8082 0 : nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset );
8083 : }
8084 0 : if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
8085 0 : nLineHeight = 3;
8086 :
8087 0 : long nLineWidth = getReferenceDevice()->mnDPIX/450;
8088 0 : if ( ! nLineWidth )
8089 0 : nLineWidth = 1;
8090 :
8091 0 : if ( eTextLine == UNDERLINE_BOLDWAVE )
8092 0 : nLineWidth = 3*nLineWidth;
8093 :
8094 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine );
8095 0 : aLine.append( " w " );
8096 :
8097 0 : if ( eTextLine == UNDERLINE_DOUBLEWAVE )
8098 : {
8099 0 : long nOrgLineHeight = nLineHeight;
8100 0 : nLineHeight /= 3;
8101 0 : if ( nLineHeight < 2 )
8102 : {
8103 0 : if ( nOrgLineHeight > 1 )
8104 0 : nLineHeight = 2;
8105 : else
8106 0 : nLineHeight = 1;
8107 : }
8108 0 : long nLineDY = nOrgLineHeight-(nLineHeight*2);
8109 0 : if ( nLineDY < nLineWidth )
8110 0 : nLineDY = nLineWidth;
8111 0 : long nLineDY2 = nLineDY/2;
8112 0 : if ( !nLineDY2 )
8113 0 : nLineDY2 = 1;
8114 :
8115 0 : nLinePos -= nLineWidth-nLineDY2;
8116 :
8117 0 : m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8118 :
8119 0 : nLinePos += nLineWidth+nLineDY;
8120 0 : m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine );
8121 : }
8122 : else
8123 : {
8124 0 : if ( eTextLine != UNDERLINE_BOLDWAVE )
8125 0 : nLinePos -= nLineWidth/2;
8126 0 : m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine );
8127 : }
8128 0 : }
8129 :
8130 0 : void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove )
8131 : {
8132 : // note: units in pFontEntry are ref device pixel
8133 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
8134 0 : long nLineHeight = 0;
8135 0 : long nLinePos = 0;
8136 0 : long nLinePos2 = 0;
8137 :
8138 0 : if ( eTextLine > UNDERLINE_BOLDWAVE )
8139 0 : eTextLine = UNDERLINE_SINGLE;
8140 :
8141 0 : switch ( eTextLine )
8142 : {
8143 : case UNDERLINE_SINGLE:
8144 : case UNDERLINE_DOTTED:
8145 : case UNDERLINE_DASH:
8146 : case UNDERLINE_LONGDASH:
8147 : case UNDERLINE_DASHDOT:
8148 : case UNDERLINE_DASHDOTDOT:
8149 0 : if ( bIsAbove )
8150 : {
8151 0 : if ( !pFontEntry->maMetric.mnAboveUnderlineSize )
8152 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
8153 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize );
8154 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset );
8155 : }
8156 : else
8157 : {
8158 0 : if ( !pFontEntry->maMetric.mnUnderlineSize )
8159 0 : m_pReferenceDevice->ImplInitTextLineSize();
8160 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize );
8161 0 : nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset );
8162 : }
8163 0 : break;
8164 : case UNDERLINE_BOLD:
8165 : case UNDERLINE_BOLDDOTTED:
8166 : case UNDERLINE_BOLDDASH:
8167 : case UNDERLINE_BOLDLONGDASH:
8168 : case UNDERLINE_BOLDDASHDOT:
8169 : case UNDERLINE_BOLDDASHDOTDOT:
8170 0 : if ( bIsAbove )
8171 : {
8172 0 : if ( !pFontEntry->maMetric.mnAboveBUnderlineSize )
8173 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
8174 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize );
8175 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset );
8176 : }
8177 : else
8178 : {
8179 0 : if ( !pFontEntry->maMetric.mnBUnderlineSize )
8180 0 : m_pReferenceDevice->ImplInitTextLineSize();
8181 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize );
8182 0 : nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset );
8183 0 : nLinePos += nLineHeight/2;
8184 : }
8185 0 : break;
8186 : case UNDERLINE_DOUBLE:
8187 0 : if ( bIsAbove )
8188 : {
8189 0 : if ( !pFontEntry->maMetric.mnAboveDUnderlineSize )
8190 0 : m_pReferenceDevice->ImplInitAboveTextLineSize();
8191 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize );
8192 0 : nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 );
8193 0 : nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 );
8194 : }
8195 : else
8196 : {
8197 0 : if ( !pFontEntry->maMetric.mnDUnderlineSize )
8198 0 : m_pReferenceDevice->ImplInitTextLineSize();
8199 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize );
8200 0 : nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 );
8201 0 : nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 );
8202 : }
8203 : default:
8204 0 : break;
8205 : }
8206 :
8207 0 : if ( nLineHeight )
8208 : {
8209 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8210 0 : aLine.append( " w " );
8211 0 : appendStrokingColor( aColor, aLine );
8212 0 : aLine.append( "\n" );
8213 :
8214 0 : switch ( eTextLine )
8215 : {
8216 : case UNDERLINE_DOTTED:
8217 : case UNDERLINE_BOLDDOTTED:
8218 0 : aLine.append( "[ " );
8219 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8220 0 : aLine.append( " ] 0 d\n" );
8221 0 : break;
8222 : case UNDERLINE_DASH:
8223 : case UNDERLINE_LONGDASH:
8224 : case UNDERLINE_BOLDDASH:
8225 : case UNDERLINE_BOLDLONGDASH:
8226 : {
8227 0 : sal_Int32 nDashLength = 4*nLineHeight;
8228 0 : sal_Int32 nVoidLength = 2*nLineHeight;
8229 0 : if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) )
8230 0 : nDashLength = 8*nLineHeight;
8231 :
8232 0 : aLine.append( "[ " );
8233 0 : m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8234 0 : aLine.append( ' ' );
8235 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8236 0 : aLine.append( " ] 0 d\n" );
8237 : }
8238 0 : break;
8239 : case UNDERLINE_DASHDOT:
8240 : case UNDERLINE_BOLDDASHDOT:
8241 : {
8242 0 : sal_Int32 nDashLength = 4*nLineHeight;
8243 0 : sal_Int32 nVoidLength = 2*nLineHeight;
8244 0 : aLine.append( "[ " );
8245 0 : m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8246 0 : aLine.append( ' ' );
8247 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8248 0 : aLine.append( ' ' );
8249 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8250 0 : aLine.append( ' ' );
8251 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8252 0 : aLine.append( " ] 0 d\n" );
8253 : }
8254 0 : break;
8255 : case UNDERLINE_DASHDOTDOT:
8256 : case UNDERLINE_BOLDDASHDOTDOT:
8257 : {
8258 0 : sal_Int32 nDashLength = 4*nLineHeight;
8259 0 : sal_Int32 nVoidLength = 2*nLineHeight;
8260 0 : aLine.append( "[ " );
8261 0 : m_aPages.back().appendMappedLength( nDashLength, aLine, false );
8262 0 : aLine.append( ' ' );
8263 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8264 0 : aLine.append( ' ' );
8265 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8266 0 : aLine.append( ' ' );
8267 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8268 0 : aLine.append( ' ' );
8269 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false );
8270 0 : aLine.append( ' ' );
8271 0 : m_aPages.back().appendMappedLength( nVoidLength, aLine, false );
8272 0 : aLine.append( " ] 0 d\n" );
8273 : }
8274 0 : break;
8275 : default:
8276 0 : break;
8277 : }
8278 :
8279 0 : aLine.append( "0 " );
8280 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8281 0 : aLine.append( " m " );
8282 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8283 0 : aLine.append( ' ' );
8284 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8285 0 : aLine.append( " l S\n" );
8286 0 : if ( eTextLine == UNDERLINE_DOUBLE )
8287 : {
8288 0 : aLine.append( "0 " );
8289 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8290 0 : aLine.append( " m " );
8291 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false );
8292 0 : aLine.append( ' ' );
8293 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8294 0 : aLine.append( " l S\n" );
8295 : }
8296 : }
8297 0 : }
8298 :
8299 0 : void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor )
8300 : {
8301 : // note: units in pFontEntry are ref device pixel
8302 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
8303 0 : long nLineHeight = 0;
8304 0 : long nLinePos = 0;
8305 0 : long nLinePos2 = 0;
8306 :
8307 0 : if ( eStrikeout > STRIKEOUT_X )
8308 0 : eStrikeout = STRIKEOUT_SINGLE;
8309 :
8310 0 : switch ( eStrikeout )
8311 : {
8312 : case STRIKEOUT_SINGLE:
8313 0 : if ( !pFontEntry->maMetric.mnStrikeoutSize )
8314 0 : m_pReferenceDevice->ImplInitTextLineSize();
8315 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize );
8316 0 : nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset );
8317 0 : break;
8318 : case STRIKEOUT_BOLD:
8319 0 : if ( !pFontEntry->maMetric.mnBStrikeoutSize )
8320 0 : m_pReferenceDevice->ImplInitTextLineSize();
8321 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize );
8322 0 : nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset );
8323 0 : break;
8324 : case STRIKEOUT_DOUBLE:
8325 0 : if ( !pFontEntry->maMetric.mnDStrikeoutSize )
8326 0 : m_pReferenceDevice->ImplInitTextLineSize();
8327 0 : nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize );
8328 0 : nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 );
8329 0 : nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 );
8330 0 : break;
8331 : default:
8332 0 : break;
8333 : }
8334 :
8335 0 : if ( nLineHeight )
8336 : {
8337 0 : m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true );
8338 0 : aLine.append( " w " );
8339 0 : appendStrokingColor( aColor, aLine );
8340 0 : aLine.append( "\n" );
8341 :
8342 0 : aLine.append( "0 " );
8343 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8344 0 : aLine.append( " m " );
8345 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8346 0 : aLine.append( ' ' );
8347 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true );
8348 0 : aLine.append( " l S\n" );
8349 :
8350 0 : if ( eStrikeout == STRIKEOUT_DOUBLE )
8351 : {
8352 0 : aLine.append( "0 " );
8353 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8354 0 : aLine.append( " m " );
8355 0 : m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true );
8356 0 : aLine.append( ' ' );
8357 0 : m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true );
8358 0 : aLine.append( " l S\n" );
8359 : }
8360 : }
8361 0 : }
8362 :
8363 0 : void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout )
8364 : {
8365 : //See qadevOOo/testdocs/StrikeThrough.odt for examples if you need
8366 : //to tweak this
8367 :
8368 0 : OUString aStrikeoutChar = eStrikeout == STRIKEOUT_SLASH ? OUString( "/" ) : OUString( "X" );
8369 0 : OUString aStrikeout = aStrikeoutChar;
8370 0 : while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth )
8371 0 : aStrikeout += aStrikeout;
8372 :
8373 : // do not get broader than nWidth modulo 1 character
8374 0 : while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth )
8375 0 : aStrikeout = aStrikeout.replaceAt( 0, 1, "" );
8376 0 : aStrikeout += aStrikeoutChar;
8377 0 : bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow();
8378 0 : if ( bShadow )
8379 : {
8380 0 : Font aFont = m_aCurrentPDFState.m_aFont;
8381 0 : aFont.SetShadow( false );
8382 0 : setFont( aFont );
8383 0 : updateGraphicsState();
8384 : }
8385 :
8386 : // strikeout string is left aligned non-CTL text
8387 0 : sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode();
8388 0 : m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED );
8389 :
8390 0 : push( PUSH_CLIPREGION );
8391 0 : FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric();
8392 0 : Rectangle aRect;
8393 0 : aRect.Left() = rPos.X();
8394 0 : aRect.Right() = aRect.Left()+nWidth;
8395 0 : aRect.Bottom() = rPos.Y()+aRefDevFontMetric.GetDescent();
8396 0 : aRect.Top() = rPos.Y()-aRefDevFontMetric.GetAscent();
8397 :
8398 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
8399 0 : if (pFontEntry->mnOrientation)
8400 : {
8401 0 : Polygon aPoly( aRect );
8402 0 : aPoly.Rotate( rPos, pFontEntry->mnOrientation);
8403 0 : aRect = aPoly.GetBoundRect();
8404 : }
8405 :
8406 0 : intersectClipRegion( aRect );
8407 0 : drawText( rPos, aStrikeout, 0, aStrikeout.getLength(), false );
8408 0 : pop();
8409 :
8410 0 : m_pReferenceDevice->SetLayoutMode( nOrigTLM );
8411 :
8412 0 : if ( bShadow )
8413 : {
8414 0 : Font aFont = m_aCurrentPDFState.m_aFont;
8415 0 : aFont.SetShadow( true );
8416 0 : setFont( aFont );
8417 0 : updateGraphicsState();
8418 0 : }
8419 0 : }
8420 :
8421 0 : void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove )
8422 : {
8423 0 : if ( !nWidth ||
8424 0 : ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) &&
8425 0 : ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) &&
8426 0 : ((eOverline == UNDERLINE_NONE)||(eOverline == UNDERLINE_DONTKNOW)) ) )
8427 0 : return;
8428 :
8429 0 : MARK( "drawTextLine" );
8430 0 : updateGraphicsState();
8431 :
8432 : // note: units in pFontEntry are ref device pixel
8433 0 : ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry;
8434 0 : Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor;
8435 0 : Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor;
8436 0 : Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor();
8437 0 : bool bStrikeoutDone = false;
8438 0 : bool bUnderlineDone = false;
8439 0 : bool bOverlineDone = false;
8440 :
8441 0 : if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) )
8442 : {
8443 0 : drawStrikeoutChar( rPos, nWidth, eStrikeout );
8444 0 : bStrikeoutDone = true;
8445 : }
8446 :
8447 0 : Point aPos( rPos );
8448 0 : TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign();
8449 0 : if( eAlign == ALIGN_TOP )
8450 0 : aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent );
8451 0 : else if( eAlign == ALIGN_BOTTOM )
8452 0 : aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent );
8453 :
8454 0 : OStringBuffer aLine( 512 );
8455 : // save GS
8456 0 : aLine.append( "q " );
8457 :
8458 : // rotate and translate matrix
8459 0 : double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0;
8460 0 : Matrix3 aMat;
8461 0 : aMat.rotate( fAngle );
8462 0 : aMat.translate( aPos.X(), aPos.Y() );
8463 0 : aMat.append( m_aPages.back(), aLine );
8464 0 : aLine.append( " cm\n" );
8465 :
8466 0 : if ( aUnderlineColor.GetTransparency() != 0 )
8467 0 : aUnderlineColor = aStrikeoutColor;
8468 :
8469 0 : if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
8470 0 : (eUnderline == UNDERLINE_WAVE) ||
8471 0 : (eUnderline == UNDERLINE_DOUBLEWAVE) ||
8472 : (eUnderline == UNDERLINE_BOLDWAVE) )
8473 : {
8474 0 : drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8475 0 : bUnderlineDone = true;
8476 : }
8477 :
8478 0 : if ( (eOverline == UNDERLINE_SMALLWAVE) ||
8479 0 : (eOverline == UNDERLINE_WAVE) ||
8480 0 : (eOverline == UNDERLINE_DOUBLEWAVE) ||
8481 : (eOverline == UNDERLINE_BOLDWAVE) )
8482 : {
8483 0 : drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8484 0 : bOverlineDone = true;
8485 : }
8486 :
8487 0 : if ( !bUnderlineDone )
8488 : {
8489 0 : drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
8490 : }
8491 :
8492 0 : if ( !bOverlineDone )
8493 : {
8494 0 : drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true );
8495 : }
8496 :
8497 0 : if ( !bStrikeoutDone )
8498 : {
8499 0 : drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor );
8500 : }
8501 :
8502 0 : aLine.append( "Q\n" );
8503 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8504 : }
8505 :
8506 0 : void PDFWriterImpl::drawPolygon( const Polygon& rPoly )
8507 : {
8508 0 : MARK( "drawPolygon" );
8509 :
8510 0 : updateGraphicsState();
8511 :
8512 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8513 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8514 0 : return;
8515 :
8516 0 : int nPoints = rPoly.GetSize();
8517 0 : OStringBuffer aLine( 20 * nPoints );
8518 0 : m_aPages.back().appendPolygon( rPoly, aLine );
8519 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8520 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8521 0 : aLine.append( "B*\n" );
8522 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8523 0 : aLine.append( "S\n" );
8524 : else
8525 0 : aLine.append( "f*\n" );
8526 :
8527 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8528 : }
8529 :
8530 0 : void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly )
8531 : {
8532 0 : MARK( "drawPolyPolygon" );
8533 :
8534 0 : updateGraphicsState();
8535 :
8536 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8537 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8538 0 : return;
8539 :
8540 0 : int nPolygons = rPolyPoly.Count();
8541 :
8542 0 : OStringBuffer aLine( 40 * nPolygons );
8543 0 : m_aPages.back().appendPolyPolygon( rPolyPoly, aLine );
8544 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8545 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8546 0 : aLine.append( "B*\n" );
8547 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8548 0 : aLine.append( "S\n" );
8549 : else
8550 0 : aLine.append( "f*\n" );
8551 :
8552 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8553 : }
8554 :
8555 0 : void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent )
8556 : {
8557 : DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8558 0 : nTransparentPercent = nTransparentPercent % 100;
8559 :
8560 0 : MARK( "drawTransparent" );
8561 :
8562 0 : updateGraphicsState();
8563 :
8564 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8565 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8566 0 : return;
8567 :
8568 0 : if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 )
8569 : {
8570 : m_aErrors.insert( m_bIsPDF_A1 ?
8571 : PDFWriter::Warning_Transparency_Omitted_PDFA :
8572 0 : PDFWriter::Warning_Transparency_Omitted_PDF13 );
8573 :
8574 0 : drawPolyPolygon( rPolyPoly );
8575 0 : return;
8576 : }
8577 :
8578 : // create XObject
8579 0 : m_aTransparentObjects.push_back( TransparencyEmit() );
8580 : // FIXME: polygons with beziers may yield incorrect bound rect
8581 0 : m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect();
8582 : // convert rectangle to default user space
8583 0 : m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8584 0 : m_aTransparentObjects.back().m_nObject = createObject();
8585 0 : m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8586 0 : m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
8587 0 : m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 );
8588 : // create XObject's content stream
8589 0 : OStringBuffer aContent( 256 );
8590 0 : m_aPages.back().appendPolyPolygon( rPolyPoly, aContent );
8591 0 : if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) &&
8592 0 : m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) )
8593 0 : aContent.append( " B*\n" );
8594 0 : else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) )
8595 0 : aContent.append( " S\n" );
8596 : else
8597 0 : aContent.append( " f*\n" );
8598 0 : m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() );
8599 :
8600 0 : OStringBuffer aObjName( 16 );
8601 0 : aObjName.append( "Tr" );
8602 0 : aObjName.append( m_aTransparentObjects.back().m_nObject );
8603 0 : OString aTrName( aObjName.makeStringAndClear() );
8604 0 : aObjName.append( "EGS" );
8605 0 : aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8606 0 : OString aExtName( aObjName.makeStringAndClear() );
8607 :
8608 0 : OStringBuffer aLine( 80 );
8609 : // insert XObject
8610 0 : aLine.append( "q /" );
8611 0 : aLine.append( aExtName );
8612 0 : aLine.append( " gs /" );
8613 0 : aLine.append( aTrName );
8614 0 : aLine.append( " Do Q\n" );
8615 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8616 :
8617 0 : pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8618 0 : pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8619 : }
8620 :
8621 0 : void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject )
8622 : {
8623 0 : if( nObject >= 0 )
8624 : {
8625 0 : switch( eKind )
8626 : {
8627 : case ResXObject:
8628 0 : m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject;
8629 0 : if( ! m_aOutputStreams.empty() )
8630 0 : m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject;
8631 0 : break;
8632 : case ResExtGState:
8633 0 : m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject;
8634 0 : if( ! m_aOutputStreams.empty() )
8635 0 : m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject;
8636 0 : break;
8637 : case ResShading:
8638 0 : m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject;
8639 0 : if( ! m_aOutputStreams.empty() )
8640 0 : m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject;
8641 0 : break;
8642 : case ResPattern:
8643 0 : m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject;
8644 0 : if( ! m_aOutputStreams.empty() )
8645 0 : m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject;
8646 0 : break;
8647 : }
8648 : }
8649 0 : }
8650 :
8651 0 : void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect )
8652 : {
8653 0 : push( PUSH_ALL );
8654 :
8655 : // force reemitting clip region inside the new stream, and
8656 : // prevent emitting an unbalanced "Q" at the start
8657 0 : clearClipRegion();
8658 : // this is needed to point m_aCurrentPDFState at the pushed state
8659 : // ... but it's pointless to actually write into the "outer" stream here!
8660 0 : updateGraphicsState(NOWRITE);
8661 :
8662 0 : m_aOutputStreams.push_front( StreamRedirect() );
8663 0 : m_aOutputStreams.front().m_pStream = pStream;
8664 0 : m_aOutputStreams.front().m_aMapMode = m_aMapMode;
8665 :
8666 0 : if( !rTargetRect.IsEmpty() )
8667 : {
8668 0 : m_aOutputStreams.front().m_aTargetRect =
8669 0 : lcl_convert( m_aGraphicsStack.front().m_aMapMode,
8670 : m_aMapMode,
8671 : getReferenceDevice(),
8672 0 : rTargetRect );
8673 0 : Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft();
8674 0 : long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight());
8675 0 : aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom());
8676 0 : m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta );
8677 : }
8678 :
8679 : // setup graphics state for independent object stream
8680 :
8681 : // force reemitting colors
8682 0 : m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8683 0 : m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8684 0 : }
8685 :
8686 0 : SvStream* PDFWriterImpl::endRedirect()
8687 : {
8688 0 : SvStream* pStream = NULL;
8689 0 : if( ! m_aOutputStreams.empty() )
8690 : {
8691 0 : pStream = m_aOutputStreams.front().m_pStream;
8692 0 : m_aMapMode = m_aOutputStreams.front().m_aMapMode;
8693 0 : m_aOutputStreams.pop_front();
8694 : }
8695 :
8696 0 : pop();
8697 :
8698 0 : m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT );
8699 0 : m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT );
8700 :
8701 : // needed after pop() to set m_aCurrentPDFState
8702 0 : updateGraphicsState(NOWRITE);
8703 :
8704 0 : return pStream;
8705 : }
8706 :
8707 0 : void PDFWriterImpl::beginTransparencyGroup()
8708 : {
8709 0 : updateGraphicsState();
8710 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8711 0 : beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() );
8712 0 : }
8713 :
8714 0 : void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent )
8715 : {
8716 : DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" );
8717 0 : nTransparentPercent = nTransparentPercent % 100;
8718 :
8719 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 )
8720 : {
8721 : // create XObject
8722 0 : m_aTransparentObjects.push_back( TransparencyEmit() );
8723 0 : m_aTransparentObjects.back().m_aBoundRect = rBoundingBox;
8724 : // convert rectangle to default user space
8725 0 : m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect );
8726 0 : m_aTransparentObjects.back().m_nObject = createObject();
8727 0 : m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0;
8728 : // get XObject's content stream
8729 0 : m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect());
8730 0 : m_aTransparentObjects.back().m_nExtGStateObject = createObject();
8731 :
8732 0 : OStringBuffer aObjName( 16 );
8733 0 : aObjName.append( "Tr" );
8734 0 : aObjName.append( m_aTransparentObjects.back().m_nObject );
8735 0 : OString aTrName( aObjName.makeStringAndClear() );
8736 0 : aObjName.append( "EGS" );
8737 0 : aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject );
8738 0 : OString aExtName( aObjName.makeStringAndClear() );
8739 :
8740 0 : OStringBuffer aLine( 80 );
8741 : // insert XObject
8742 0 : aLine.append( "q /" );
8743 0 : aLine.append( aExtName );
8744 0 : aLine.append( " gs /" );
8745 0 : aLine.append( aTrName );
8746 0 : aLine.append( " Do Q\n" );
8747 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8748 :
8749 0 : pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject );
8750 0 : pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject );
8751 : }
8752 0 : }
8753 :
8754 0 : void PDFWriterImpl::drawRectangle( const Rectangle& rRect )
8755 : {
8756 0 : MARK( "drawRectangle" );
8757 :
8758 0 : updateGraphicsState();
8759 :
8760 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8761 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8762 0 : return;
8763 :
8764 0 : OStringBuffer aLine( 40 );
8765 0 : m_aPages.back().appendRect( rRect, aLine );
8766 :
8767 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8768 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8769 0 : aLine.append( " B*\n" );
8770 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8771 0 : aLine.append( " S\n" );
8772 : else
8773 0 : aLine.append( " f*\n" );
8774 :
8775 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8776 : }
8777 :
8778 0 : void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound )
8779 : {
8780 0 : MARK( "drawRectangle with rounded edges" );
8781 :
8782 0 : if( !nHorzRound && !nVertRound )
8783 0 : drawRectangle( rRect );
8784 :
8785 0 : updateGraphicsState();
8786 :
8787 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8788 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8789 0 : return;
8790 :
8791 0 : if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 )
8792 0 : nHorzRound = rRect.GetWidth()/2;
8793 0 : if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 )
8794 0 : nVertRound = rRect.GetHeight()/2;
8795 :
8796 0 : Point aPoints[16];
8797 0 : const double kappa = 0.5522847498;
8798 0 : const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5);
8799 0 : const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5);
8800 :
8801 0 : aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() );
8802 0 : aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8803 0 : aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() );
8804 0 : aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() );
8805 :
8806 0 : aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound );
8807 0 : aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky );
8808 0 : aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound );
8809 0 : aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky );
8810 :
8811 0 : aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 );
8812 0 : aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() );
8813 0 : aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() );
8814 0 : aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() );
8815 :
8816 0 : aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound );
8817 0 : aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky );
8818 0 : aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound );
8819 0 : aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky );
8820 :
8821 0 : OStringBuffer aLine( 80 );
8822 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
8823 0 : aLine.append( " m " );
8824 0 : m_aPages.back().appendPoint( aPoints[2], aLine );
8825 0 : aLine.append( " l " );
8826 0 : m_aPages.back().appendPoint( aPoints[3], aLine );
8827 0 : aLine.append( ' ' );
8828 0 : m_aPages.back().appendPoint( aPoints[4], aLine );
8829 0 : aLine.append( ' ' );
8830 0 : m_aPages.back().appendPoint( aPoints[5], aLine );
8831 0 : aLine.append( " c\n" );
8832 0 : m_aPages.back().appendPoint( aPoints[6], aLine );
8833 0 : aLine.append( " l " );
8834 0 : m_aPages.back().appendPoint( aPoints[7], aLine );
8835 0 : aLine.append( ' ' );
8836 0 : m_aPages.back().appendPoint( aPoints[8], aLine );
8837 0 : aLine.append( ' ' );
8838 0 : m_aPages.back().appendPoint( aPoints[9], aLine );
8839 0 : aLine.append( " c\n" );
8840 0 : m_aPages.back().appendPoint( aPoints[10], aLine );
8841 0 : aLine.append( " l " );
8842 0 : m_aPages.back().appendPoint( aPoints[11], aLine );
8843 0 : aLine.append( ' ' );
8844 0 : m_aPages.back().appendPoint( aPoints[12], aLine );
8845 0 : aLine.append( ' ' );
8846 0 : m_aPages.back().appendPoint( aPoints[13], aLine );
8847 0 : aLine.append( " c\n" );
8848 0 : m_aPages.back().appendPoint( aPoints[14], aLine );
8849 0 : aLine.append( " l " );
8850 0 : m_aPages.back().appendPoint( aPoints[15], aLine );
8851 0 : aLine.append( ' ' );
8852 0 : m_aPages.back().appendPoint( aPoints[0], aLine );
8853 0 : aLine.append( ' ' );
8854 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
8855 0 : aLine.append( " c " );
8856 :
8857 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8858 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8859 0 : aLine.append( "b*\n" );
8860 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8861 0 : aLine.append( "s\n" );
8862 : else
8863 0 : aLine.append( "f*\n" );
8864 :
8865 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8866 : }
8867 :
8868 0 : void PDFWriterImpl::drawEllipse( const Rectangle& rRect )
8869 : {
8870 0 : MARK( "drawEllipse" );
8871 :
8872 0 : updateGraphicsState();
8873 :
8874 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8875 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8876 0 : return;
8877 :
8878 0 : Point aPoints[12];
8879 0 : const double kappa = 0.5522847498;
8880 0 : const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5);
8881 0 : const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5);
8882 :
8883 0 : aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() );
8884 0 : aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() );
8885 0 : aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() );
8886 :
8887 0 : aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 );
8888 0 : aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky );
8889 0 : aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky );
8890 :
8891 0 : aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 );
8892 0 : aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() );
8893 0 : aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() );
8894 :
8895 0 : aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 );
8896 0 : aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky );
8897 0 : aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky );
8898 :
8899 0 : OStringBuffer aLine( 80 );
8900 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
8901 0 : aLine.append( " m " );
8902 0 : m_aPages.back().appendPoint( aPoints[2], aLine );
8903 0 : aLine.append( ' ' );
8904 0 : m_aPages.back().appendPoint( aPoints[3], aLine );
8905 0 : aLine.append( ' ' );
8906 0 : m_aPages.back().appendPoint( aPoints[4], aLine );
8907 0 : aLine.append( " c\n" );
8908 0 : m_aPages.back().appendPoint( aPoints[5], aLine );
8909 0 : aLine.append( ' ' );
8910 0 : m_aPages.back().appendPoint( aPoints[6], aLine );
8911 0 : aLine.append( ' ' );
8912 0 : m_aPages.back().appendPoint( aPoints[7], aLine );
8913 0 : aLine.append( " c\n" );
8914 0 : m_aPages.back().appendPoint( aPoints[8], aLine );
8915 0 : aLine.append( ' ' );
8916 0 : m_aPages.back().appendPoint( aPoints[9], aLine );
8917 0 : aLine.append( ' ' );
8918 0 : m_aPages.back().appendPoint( aPoints[10], aLine );
8919 0 : aLine.append( " c\n" );
8920 0 : m_aPages.back().appendPoint( aPoints[11], aLine );
8921 0 : aLine.append( ' ' );
8922 0 : m_aPages.back().appendPoint( aPoints[0], aLine );
8923 0 : aLine.append( ' ' );
8924 0 : m_aPages.back().appendPoint( aPoints[1], aLine );
8925 0 : aLine.append( " c " );
8926 :
8927 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
8928 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
8929 0 : aLine.append( "b*\n" );
8930 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
8931 0 : aLine.append( "s\n" );
8932 : else
8933 0 : aLine.append( "f*\n" );
8934 :
8935 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
8936 : }
8937 :
8938 0 : static double calcAngle( const Rectangle& rRect, const Point& rPoint )
8939 : {
8940 0 : Point aOrigin((rRect.Left()+rRect.Right()+1)/2,
8941 0 : (rRect.Top()+rRect.Bottom()+1)/2);
8942 0 : Point aPoint = rPoint - aOrigin;
8943 :
8944 0 : double fX = (double)aPoint.X();
8945 0 : double fY = (double)-aPoint.Y();
8946 :
8947 0 : if( rRect.GetWidth() > rRect.GetHeight() )
8948 0 : fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight());
8949 0 : else if( rRect.GetHeight() > rRect.GetWidth() )
8950 0 : fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth());
8951 0 : return atan2( fY, fX );
8952 : }
8953 :
8954 0 : void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord )
8955 : {
8956 0 : MARK( "drawArc" );
8957 :
8958 0 : updateGraphicsState();
8959 :
8960 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) &&
8961 0 : m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) )
8962 0 : return;
8963 :
8964 : // calculate start and stop angles
8965 0 : const double fStartAngle = calcAngle( rRect, rStart );
8966 0 : double fStopAngle = calcAngle( rRect, rStop );
8967 0 : while( fStopAngle < fStartAngle )
8968 0 : fStopAngle += 2.0*M_PI;
8969 0 : const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1;
8970 0 : const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments;
8971 0 : const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0);
8972 0 : const double halfWidth = (double)rRect.GetWidth()/2.0;
8973 0 : const double halfHeight = (double)rRect.GetHeight()/2.0;
8974 :
8975 0 : const Point aCenter( (rRect.Left()+rRect.Right()+1)/2,
8976 0 : (rRect.Top()+rRect.Bottom()+1)/2 );
8977 :
8978 0 : OStringBuffer aLine( 30*nFragments );
8979 0 : Point aPoint( (int)(halfWidth * cos(fStartAngle) ),
8980 0 : -(int)(halfHeight * sin(fStartAngle) ) );
8981 0 : aPoint += aCenter;
8982 0 : m_aPages.back().appendPoint( aPoint, aLine );
8983 0 : aLine.append( " m " );
8984 0 : if( !basegfx::fTools::equal(fStartAngle, fStopAngle) )
8985 : {
8986 0 : for( int i = 0; i < nFragments; i++ )
8987 : {
8988 0 : const double fStartFragment = fStartAngle + (double)i*fFragmentDelta;
8989 0 : const double fStopFragment = fStartFragment + fFragmentDelta;
8990 0 : aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ),
8991 0 : -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) );
8992 0 : aPoint += aCenter;
8993 0 : m_aPages.back().appendPoint( aPoint, aLine );
8994 0 : aLine.append( ' ' );
8995 :
8996 0 : aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ),
8997 0 : -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) );
8998 0 : aPoint += aCenter;
8999 0 : m_aPages.back().appendPoint( aPoint, aLine );
9000 0 : aLine.append( ' ' );
9001 :
9002 0 : aPoint = Point( (int)(halfWidth * cos(fStopFragment) ),
9003 0 : -(int)(halfHeight * sin(fStopFragment) ) );
9004 0 : aPoint += aCenter;
9005 0 : m_aPages.back().appendPoint( aPoint, aLine );
9006 0 : aLine.append( " c\n" );
9007 : }
9008 : }
9009 0 : if( bWithChord || bWithPie )
9010 : {
9011 0 : if( bWithPie )
9012 : {
9013 0 : m_aPages.back().appendPoint( aCenter, aLine );
9014 0 : aLine.append( " l " );
9015 : }
9016 0 : aLine.append( "h " );
9017 : }
9018 0 : if( ! bWithChord && ! bWithPie )
9019 0 : aLine.append( "S\n" );
9020 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) &&
9021 0 : m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) )
9022 0 : aLine.append( "B*\n" );
9023 0 : else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
9024 0 : aLine.append( "S\n" );
9025 : else
9026 0 : aLine.append( "f*\n" );
9027 :
9028 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9029 : }
9030 :
9031 0 : void PDFWriterImpl::drawPolyLine( const Polygon& rPoly )
9032 : {
9033 0 : MARK( "drawPolyLine" );
9034 :
9035 0 : sal_uInt16 nPoints = rPoly.GetSize();
9036 0 : if( nPoints < 2 )
9037 0 : return;
9038 :
9039 0 : updateGraphicsState();
9040 :
9041 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9042 0 : return;
9043 :
9044 0 : OStringBuffer aLine( 20 * nPoints );
9045 0 : m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] );
9046 0 : aLine.append( "S\n" );
9047 :
9048 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9049 : }
9050 :
9051 0 : void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo )
9052 : {
9053 0 : MARK( "drawPolyLine with LineInfo" );
9054 :
9055 0 : updateGraphicsState();
9056 :
9057 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9058 0 : return;
9059 :
9060 0 : OStringBuffer aLine;
9061 0 : aLine.append( "q " );
9062 0 : if( m_aPages.back().appendLineInfo( rInfo, aLine ) )
9063 : {
9064 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9065 0 : drawPolyLine( rPoly );
9066 0 : writeBuffer( "Q\n", 2 );
9067 : }
9068 : else
9069 : {
9070 0 : PDFWriter::ExtLineInfo aInfo;
9071 0 : convertLineInfoToExtLineInfo( rInfo, aInfo );
9072 0 : drawPolyLine( rPoly, aInfo );
9073 0 : }
9074 : }
9075 :
9076 0 : void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut )
9077 : {
9078 : DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" );
9079 0 : rOut.m_fLineWidth = rIn.GetWidth();
9080 0 : rOut.m_fTransparency = 0.0;
9081 0 : rOut.m_eCap = PDFWriter::capButt;
9082 0 : rOut.m_eJoin = PDFWriter::joinMiter;
9083 0 : rOut.m_fMiterLimit = 10;
9084 0 : rOut.m_aDashArray.clear();
9085 :
9086 : // add DashDot to DashArray
9087 0 : const int nDashes = rIn.GetDashCount();
9088 0 : const int nDashLen = rIn.GetDashLen();
9089 0 : const int nDistance = rIn.GetDistance();
9090 :
9091 0 : for( int n = 0; n < nDashes; n++ )
9092 : {
9093 0 : rOut.m_aDashArray.push_back( nDashLen );
9094 0 : rOut.m_aDashArray.push_back( nDistance );
9095 : }
9096 0 : const int nDots = rIn.GetDotCount();
9097 0 : const int nDotLen = rIn.GetDotLen();
9098 :
9099 0 : for( int n = 0; n < nDots; n++ )
9100 : {
9101 0 : rOut.m_aDashArray.push_back( nDotLen );
9102 0 : rOut.m_aDashArray.push_back( nDistance );
9103 : }
9104 :
9105 : // add LineJoin
9106 0 : switch(rIn.GetLineJoin())
9107 : {
9108 : case basegfx::B2DLINEJOIN_BEVEL :
9109 : {
9110 0 : rOut.m_eJoin = PDFWriter::joinBevel;
9111 0 : break;
9112 : }
9113 : default : // basegfx::B2DLINEJOIN_NONE :
9114 : // Pdf has no 'none' lineJoin, default is miter
9115 : case basegfx::B2DLINEJOIN_MIDDLE :
9116 : case basegfx::B2DLINEJOIN_MITER :
9117 : {
9118 0 : rOut.m_eJoin = PDFWriter::joinMiter;
9119 0 : break;
9120 : }
9121 : case basegfx::B2DLINEJOIN_ROUND :
9122 : {
9123 0 : rOut.m_eJoin = PDFWriter::joinRound;
9124 0 : break;
9125 : }
9126 : }
9127 :
9128 : // add LineCap
9129 0 : switch(rIn.GetLineCap())
9130 : {
9131 : default: /* com::sun::star::drawing::LineCap_BUTT */
9132 : {
9133 0 : rOut.m_eCap = PDFWriter::capButt;
9134 0 : break;
9135 : }
9136 : case com::sun::star::drawing::LineCap_ROUND:
9137 : {
9138 0 : rOut.m_eCap = PDFWriter::capRound;
9139 0 : break;
9140 : }
9141 : case com::sun::star::drawing::LineCap_SQUARE:
9142 : {
9143 0 : rOut.m_eCap = PDFWriter::capSquare;
9144 0 : break;
9145 : }
9146 : }
9147 0 : }
9148 :
9149 0 : void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo )
9150 : {
9151 0 : MARK( "drawPolyLine with ExtLineInfo" );
9152 :
9153 0 : updateGraphicsState();
9154 :
9155 0 : if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) )
9156 0 : return;
9157 :
9158 0 : if( rInfo.m_fTransparency >= 1.0 )
9159 0 : return;
9160 :
9161 0 : if( rInfo.m_fTransparency != 0.0 )
9162 0 : beginTransparencyGroup();
9163 :
9164 0 : OStringBuffer aLine;
9165 0 : aLine.append( "q " );
9166 0 : m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine );
9167 0 : aLine.append( " w" );
9168 0 : if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader
9169 : {
9170 0 : switch( rInfo.m_eCap )
9171 : {
9172 : default:
9173 0 : case PDFWriter::capButt: aLine.append( " 0 J" );break;
9174 0 : case PDFWriter::capRound: aLine.append( " 1 J" );break;
9175 0 : case PDFWriter::capSquare: aLine.append( " 2 J" );break;
9176 : }
9177 0 : switch( rInfo.m_eJoin )
9178 : {
9179 : default:
9180 : case PDFWriter::joinMiter:
9181 : {
9182 0 : double fLimit = rInfo.m_fMiterLimit;
9183 0 : if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit )
9184 0 : fLimit = fLimit / rInfo.m_fLineWidth;
9185 0 : if( fLimit < 1.0 )
9186 0 : fLimit = 1.0;
9187 0 : aLine.append( " 0 j " );
9188 0 : appendDouble( fLimit, aLine );
9189 0 : aLine.append( " M" );
9190 : }
9191 0 : break;
9192 0 : case PDFWriter::joinRound: aLine.append( " 1 j" );break;
9193 0 : case PDFWriter::joinBevel: aLine.append( " 2 j" );break;
9194 : }
9195 0 : if( rInfo.m_aDashArray.size() > 0 )
9196 : {
9197 0 : aLine.append( " [ " );
9198 0 : for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin();
9199 0 : it != rInfo.m_aDashArray.end(); ++it )
9200 : {
9201 0 : m_aPages.back().appendMappedLength( *it, aLine );
9202 0 : aLine.append( ' ' );
9203 : }
9204 0 : aLine.append( "] 0 d" );
9205 : }
9206 0 : aLine.append( "\n" );
9207 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9208 0 : drawPolyLine( rPoly );
9209 : }
9210 : else
9211 : {
9212 0 : basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon());
9213 0 : basegfx::B2DPolyPolygon aPolyPoly;
9214 :
9215 0 : basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly);
9216 :
9217 : // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments.
9218 : // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality)
9219 : // this line needs to be removed and the loop below adapted accordingly
9220 0 : aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly);
9221 :
9222 0 : const sal_uInt32 nPolygonCount(aPolyPoly.count());
9223 :
9224 0 : for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ )
9225 : {
9226 0 : aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " );
9227 0 : aPoly = aPolyPoly.getB2DPolygon( nPoly );
9228 0 : const sal_uInt32 nPointCount(aPoly.count());
9229 :
9230 0 : if(nPointCount)
9231 : {
9232 0 : const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1);
9233 0 : basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0));
9234 :
9235 0 : for(sal_uInt32 a(0); a < nEdgeCount; a++)
9236 : {
9237 0 : if( a > 0 )
9238 0 : aLine.append( " " );
9239 0 : const sal_uInt32 nNextIndex((a + 1) % nPointCount);
9240 0 : const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex));
9241 :
9242 0 : m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()),
9243 : FRound(aCurrent.getY()) ),
9244 0 : aLine );
9245 0 : aLine.append( " m " );
9246 0 : m_aPages.back().appendPoint( Point( FRound(aNext.getX()),
9247 : FRound(aNext.getY()) ),
9248 0 : aLine );
9249 0 : aLine.append( " l" );
9250 :
9251 : // prepare next edge
9252 0 : aCurrent = aNext;
9253 0 : }
9254 : }
9255 : }
9256 0 : aLine.append( " S " );
9257 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9258 : }
9259 0 : writeBuffer( "Q\n", 2 );
9260 :
9261 0 : if( rInfo.m_fTransparency != 0.0 )
9262 : {
9263 : // FIXME: actually this may be incorrect with bezier polygons
9264 0 : Rectangle aBoundRect( rPoly.GetBoundRect() );
9265 : // avoid clipping with thick lines
9266 0 : if( rInfo.m_fLineWidth > 0.0 )
9267 : {
9268 0 : sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth);
9269 0 : aBoundRect.Top() -= nLW;
9270 0 : aBoundRect.Left() -= nLW;
9271 0 : aBoundRect.Right() += nLW;
9272 0 : aBoundRect.Bottom() += nLW;
9273 : }
9274 0 : endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) );
9275 0 : }
9276 : }
9277 :
9278 0 : void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor )
9279 : {
9280 0 : MARK( "drawPixel" );
9281 :
9282 0 : Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor );
9283 :
9284 0 : if( aColor == Color( COL_TRANSPARENT ) )
9285 0 : return;
9286 :
9287 : // pixels are drawn in line color, so have to set
9288 : // the nonstroking color to line color
9289 0 : Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
9290 0 : setFillColor( aColor );
9291 :
9292 0 : updateGraphicsState();
9293 :
9294 0 : OStringBuffer aLine( 20 );
9295 0 : m_aPages.back().appendPoint( rPoint, aLine );
9296 0 : aLine.append( ' ' );
9297 0 : appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine );
9298 0 : aLine.append( ' ' );
9299 0 : appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine );
9300 0 : aLine.append( " re f\n" );
9301 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
9302 :
9303 0 : setFillColor( aOldFillColor );
9304 : }
9305 :
9306 : class AccessReleaser
9307 : {
9308 : BitmapReadAccess* m_pAccess;
9309 : public:
9310 0 : AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){}
9311 0 : ~AccessReleaser() { delete m_pAccess; }
9312 : };
9313 :
9314 0 : bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject )
9315 : {
9316 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
9317 :
9318 0 : bool bFlateFilter = compressStream( rObject.m_pContentStream );
9319 0 : rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END );
9320 0 : sal_uLong nSize = rObject.m_pContentStream->Tell();
9321 0 : rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN );
9322 : #if OSL_DEBUG_LEVEL > 1
9323 : emitComment( "PDFWriterImpl::writeTransparentObject" );
9324 : #endif
9325 0 : OStringBuffer aLine( 512 );
9326 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
9327 0 : aLine.append( rObject.m_nObject );
9328 : aLine.append( " 0 obj\n"
9329 : "<</Type/XObject\n"
9330 : "/Subtype/Form\n"
9331 0 : "/BBox[ " );
9332 0 : appendFixedInt( rObject.m_aBoundRect.Left(), aLine );
9333 0 : aLine.append( ' ' );
9334 0 : appendFixedInt( rObject.m_aBoundRect.Top(), aLine );
9335 0 : aLine.append( ' ' );
9336 0 : appendFixedInt( rObject.m_aBoundRect.Right(), aLine );
9337 0 : aLine.append( ' ' );
9338 0 : appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine );
9339 0 : aLine.append( " ]\n" );
9340 0 : if( ! rObject.m_pSoftMaskStream )
9341 : {
9342 0 : if( ! m_bIsPDF_A1 )
9343 : {
9344 0 : aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" );
9345 : }
9346 : }
9347 : /* #i42884# the PDF reference recommends that each Form XObject
9348 : * should have a resource dict; alas if that is the same object
9349 : * as the one of the page it triggers an endless recursion in
9350 : * acroread 5 (6 and up have that fixed). Since we have only one
9351 : * resource dict anyway, let's use the one from the page by NOT
9352 : * emitting a Resources entry.
9353 : */
9354 :
9355 0 : aLine.append( "/Length " );
9356 0 : aLine.append( (sal_Int32)(nSize) );
9357 0 : aLine.append( "\n" );
9358 0 : if( bFlateFilter )
9359 0 : aLine.append( "/Filter/FlateDecode\n" );
9360 : aLine.append( ">>\n"
9361 0 : "stream\n" );
9362 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9363 0 : checkAndEnableStreamEncryption( rObject.m_nObject );
9364 0 : CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) );
9365 0 : disableStreamEncryption();
9366 0 : aLine.setLength( 0 );
9367 : aLine.append( "\n"
9368 : "endstream\n"
9369 0 : "endobj\n\n" );
9370 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9371 :
9372 : // write ExtGState dict for this XObject
9373 0 : aLine.setLength( 0 );
9374 0 : aLine.append( rObject.m_nExtGStateObject );
9375 : aLine.append( " 0 obj\n"
9376 0 : "<<" );
9377 0 : if( ! rObject.m_pSoftMaskStream )
9378 : {
9379 : //i59651
9380 0 : if( m_bIsPDF_A1 )
9381 : {
9382 0 : aLine.append( "/CA 1.0/ca 1.0" );
9383 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9384 : }
9385 : else
9386 : {
9387 0 : aLine.append( "/CA " );
9388 0 : appendDouble( rObject.m_fAlpha, aLine );
9389 : aLine.append( "\n"
9390 0 : " /ca " );
9391 0 : appendDouble( rObject.m_fAlpha, aLine );
9392 : }
9393 0 : aLine.append( "\n" );
9394 : }
9395 : else
9396 : {
9397 0 : if( m_bIsPDF_A1 )
9398 : {
9399 0 : aLine.append( "/SMask/None" );
9400 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9401 : }
9402 : else
9403 : {
9404 0 : rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END );
9405 0 : sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell();
9406 0 : rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN );
9407 0 : sal_Int32 nMaskObject = createObject();
9408 0 : aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " );
9409 0 : aLine.append( nMaskObject );
9410 0 : aLine.append( " 0 R>>\n" );
9411 :
9412 0 : OStringBuffer aMask;
9413 0 : aMask.append( nMaskObject );
9414 : aMask.append( " 0 obj\n"
9415 : "<</Type/XObject\n"
9416 : "/Subtype/Form\n"
9417 0 : "/BBox[" );
9418 0 : appendFixedInt( rObject.m_aBoundRect.Left(), aMask );
9419 0 : aMask.append( ' ' );
9420 0 : appendFixedInt( rObject.m_aBoundRect.Top(), aMask );
9421 0 : aMask.append( ' ' );
9422 0 : appendFixedInt( rObject.m_aBoundRect.Right(), aMask );
9423 0 : aMask.append( ' ' );
9424 0 : appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask );
9425 0 : aMask.append( "]\n" );
9426 :
9427 : /* #i42884# see above */
9428 0 : aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" );
9429 0 : aMask.append( "/Length " );
9430 0 : aMask.append( nMaskSize );
9431 : aMask.append( ">>\n"
9432 0 : "stream\n" );
9433 0 : CHECK_RETURN( updateObject( nMaskObject ) );
9434 0 : checkAndEnableStreamEncryption( nMaskObject );
9435 0 : CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9436 0 : CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) );
9437 0 : disableStreamEncryption();
9438 0 : aMask.setLength( 0 );
9439 : aMask.append( "\nendstream\n"
9440 0 : "endobj\n\n" );
9441 0 : CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) );
9442 : }
9443 : }
9444 : aLine.append( ">>\n"
9445 0 : "endobj\n\n" );
9446 0 : CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) );
9447 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9448 :
9449 0 : return true;
9450 : }
9451 :
9452 0 : bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject )
9453 : {
9454 : // LO internal gradient -> PDF shading type:
9455 : // * GradientStyle_LINEAR: axial shading, using sampled-function with 2 samples
9456 : // [t=0:colorStart, t=1:colorEnd]
9457 : // * GradientStyle_AXIAL: axial shading, using sampled-function with 3 samples
9458 : // [t=0:colorEnd, t=0.5:colorStart, t=1:colorEnd]
9459 : // * other styles: function shading with aSize.Width() * aSize.Height() samples
9460 0 : sal_Int32 nFunctionObject = createObject();
9461 0 : CHECK_RETURN( updateObject( nFunctionObject ) );
9462 :
9463 0 : VirtualDevice aDev;
9464 0 : aDev.SetOutputSizePixel( rObject.m_aSize );
9465 0 : aDev.SetMapMode( MapMode( MAP_PIXEL ) );
9466 0 : if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
9467 0 : aDev.SetDrawMode( aDev.GetDrawMode() |
9468 : ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT |
9469 0 : DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) );
9470 0 : aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient );
9471 :
9472 0 : Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize );
9473 0 : BitmapReadAccess* pAccess = aSample.AcquireReadAccess();
9474 0 : AccessReleaser aReleaser( pAccess );
9475 :
9476 0 : Size aSize = aSample.GetSizePixel();
9477 :
9478 0 : sal_Int32 nStreamLengthObject = createObject();
9479 : #if OSL_DEBUG_LEVEL > 1
9480 : emitComment( "PDFWriterImpl::writeGradientFunction" );
9481 : #endif
9482 0 : OStringBuffer aLine( 120 );
9483 0 : aLine.append( nFunctionObject );
9484 : aLine.append( " 0 obj\n"
9485 0 : "<</FunctionType 0\n");
9486 0 : switch (rObject.m_aGradient.GetStyle())
9487 : {
9488 : case GradientStyle_LINEAR:
9489 : case GradientStyle_AXIAL:
9490 0 : aLine.append("/Domain[ 0 1]\n");
9491 0 : break;
9492 : default:
9493 0 : aLine.append("/Domain[ 0 1 0 1]\n");
9494 : }
9495 0 : aLine.append("/Size[ " );
9496 0 : switch (rObject.m_aGradient.GetStyle())
9497 : {
9498 : case GradientStyle_LINEAR:
9499 0 : aLine.append('2');
9500 0 : break;
9501 : case GradientStyle_AXIAL:
9502 0 : aLine.append('3');
9503 0 : break;
9504 : default:
9505 0 : aLine.append( (sal_Int32)aSize.Width() );
9506 0 : aLine.append( ' ' );
9507 0 : aLine.append( (sal_Int32)aSize.Height() );
9508 : }
9509 : aLine.append( " ]\n"
9510 : "/BitsPerSample 8\n"
9511 : "/Range[ 0 1 0 1 0 1 ]\n"
9512 : "/Order 3\n"
9513 0 : "/Length " );
9514 0 : aLine.append( nStreamLengthObject );
9515 : aLine.append( " 0 R\n"
9516 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9517 : "/Filter/FlateDecode"
9518 : #endif
9519 : ">>\n"
9520 0 : "stream\n" );
9521 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9522 :
9523 0 : sal_uInt64 nStartStreamPos = 0;
9524 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) );
9525 :
9526 0 : checkAndEnableStreamEncryption( nFunctionObject );
9527 0 : beginCompression();
9528 : sal_uInt8 aCol[3];
9529 0 : switch (rObject.m_aGradient.GetStyle())
9530 : {
9531 : case GradientStyle_AXIAL:
9532 0 : aCol[0] = rObject.m_aGradient.GetEndColor().GetRed();
9533 0 : aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen();
9534 0 : aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue();
9535 0 : CHECK_RETURN( writeBuffer( aCol, 3 ) );
9536 : case GradientStyle_LINEAR:
9537 : {
9538 0 : aCol[0] = rObject.m_aGradient.GetStartColor().GetRed();
9539 0 : aCol[1] = rObject.m_aGradient.GetStartColor().GetGreen();
9540 0 : aCol[2] = rObject.m_aGradient.GetStartColor().GetBlue();
9541 0 : CHECK_RETURN( writeBuffer( aCol, 3 ) );
9542 :
9543 0 : aCol[0] = rObject.m_aGradient.GetEndColor().GetRed();
9544 0 : aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen();
9545 0 : aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue();
9546 0 : CHECK_RETURN( writeBuffer( aCol, 3 ) );
9547 0 : break;
9548 : }
9549 : default:
9550 0 : for( int y = aSize.Height()-1; y >= 0; y-- )
9551 : {
9552 0 : for( int x = 0; x < aSize.Width(); x++ )
9553 : {
9554 0 : BitmapColor aColor = pAccess->GetColor( y, x );
9555 0 : aCol[0] = aColor.GetRed();
9556 0 : aCol[1] = aColor.GetGreen();
9557 0 : aCol[2] = aColor.GetBlue();
9558 0 : CHECK_RETURN( writeBuffer( aCol, 3 ) );
9559 0 : }
9560 : }
9561 : }
9562 0 : endCompression();
9563 0 : disableStreamEncryption();
9564 :
9565 0 : sal_uInt64 nEndStreamPos = 0;
9566 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) );
9567 :
9568 0 : aLine.setLength( 0 );
9569 0 : aLine.append( "\nendstream\nendobj\n\n" );
9570 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9571 :
9572 : // write stream length
9573 0 : CHECK_RETURN( updateObject( nStreamLengthObject ) );
9574 0 : aLine.setLength( 0 );
9575 0 : aLine.append( nStreamLengthObject );
9576 0 : aLine.append( " 0 obj\n" );
9577 0 : aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) );
9578 0 : aLine.append( "\nendobj\n\n" );
9579 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9580 :
9581 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
9582 0 : aLine.setLength( 0 );
9583 0 : aLine.append( rObject.m_nObject );
9584 0 : aLine.append( " 0 obj\n");
9585 0 : switch (rObject.m_aGradient.GetStyle())
9586 : {
9587 : case GradientStyle_LINEAR:
9588 : case GradientStyle_AXIAL:
9589 0 : aLine.append("<</ShadingType 2\n");
9590 0 : break;
9591 : default:
9592 0 : aLine.append("<</ShadingType 1\n");
9593 : }
9594 : aLine.append("/ColorSpace/DeviceRGB\n"
9595 0 : "/AntiAlias true\n");
9596 :
9597 : // Determination of shading axis
9598 : // See: OutputDevice::ImplDrawLinearGradient for reference
9599 0 : Rectangle aRect;
9600 0 : aRect.Left() = aRect.Top() = 0;
9601 0 : aRect.Right() = aSize.Width();
9602 0 : aRect.Bottom() = aSize.Height();
9603 :
9604 0 : Rectangle aBoundRect;
9605 0 : Point aCenter;
9606 0 : sal_uInt16 nAngle = rObject.m_aGradient.GetAngle() % 3600;
9607 0 : rObject.m_aGradient.GetBoundRect( aRect, aBoundRect, aCenter );
9608 :
9609 0 : const bool bLinear = (rObject.m_aGradient.GetStyle() == GradientStyle_LINEAR);
9610 0 : double fBorder = aBoundRect.GetHeight() * rObject.m_aGradient.GetBorder() / 100.0;
9611 0 : if ( !bLinear )
9612 : {
9613 0 : fBorder /= 2.0;
9614 : }
9615 :
9616 0 : aBoundRect.Bottom() -= fBorder;
9617 0 : if (!bLinear)
9618 : {
9619 0 : aBoundRect.Top() += fBorder;
9620 : }
9621 :
9622 0 : switch (rObject.m_aGradient.GetStyle())
9623 : {
9624 : case GradientStyle_LINEAR:
9625 : case GradientStyle_AXIAL:
9626 : {
9627 : aLine.append("/Domain[ 0 1 ]\n"
9628 0 : "/Coords[ " );
9629 0 : Polygon aPoly( 2 );
9630 0 : aPoly[0] = aBoundRect.BottomCenter();
9631 0 : aPoly[1] = aBoundRect.TopCenter();
9632 0 : aPoly.Rotate( aCenter, 3600 - nAngle );
9633 :
9634 0 : aLine.append( (sal_Int32) aPoly[0].X() );
9635 0 : aLine.append( " " );
9636 0 : aLine.append( (sal_Int32) aPoly[0].Y() );
9637 0 : aLine.append( " " );
9638 0 : aLine.append( (sal_Int32) aPoly[1].X());
9639 0 : aLine.append( " ");
9640 0 : aLine.append( (sal_Int32) aPoly[1].Y());
9641 0 : aLine.append( " ]\n");
9642 0 : aLine.append("/Extend [true true]\n");
9643 0 : break;
9644 : }
9645 : default:
9646 : aLine.append("/Domain[ 0 1 0 1 ]\n"
9647 0 : "/Matrix[ " );
9648 0 : aLine.append( (sal_Int32)aSize.Width() );
9649 0 : aLine.append( " 0 0 " );
9650 0 : aLine.append( (sal_Int32)aSize.Height() );
9651 0 : aLine.append( " 0 0 ]\n");
9652 : }
9653 0 : aLine.append("/Function " );
9654 0 : aLine.append( nFunctionObject );
9655 : aLine.append( " 0 R\n"
9656 : ">>\n"
9657 0 : "endobj\n\n" );
9658 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9659 :
9660 0 : return true;
9661 : }
9662 :
9663 0 : bool PDFWriterImpl::writeJPG( JPGEmit& rObject )
9664 : {
9665 0 : CHECK_RETURN( rObject.m_pStream );
9666 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
9667 :
9668 0 : sal_Int32 nLength = 0;
9669 0 : rObject.m_pStream->Seek( STREAM_SEEK_TO_END );
9670 0 : nLength = rObject.m_pStream->Tell();
9671 0 : rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN );
9672 :
9673 0 : sal_Int32 nMaskObject = 0;
9674 0 : if( !!rObject.m_aMask )
9675 : {
9676 0 : if( rObject.m_aMask.GetBitCount() == 1 ||
9677 0 : ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651
9678 : )
9679 : {
9680 0 : nMaskObject = createObject();
9681 : }
9682 0 : else if( m_bIsPDF_A1 )
9683 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9684 0 : else if( m_aContext.Version < PDFWriter::PDF_1_4 )
9685 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 );
9686 :
9687 : }
9688 : #if OSL_DEBUG_LEVEL > 1
9689 : emitComment( "PDFWriterImpl::writeJPG" );
9690 : #endif
9691 :
9692 0 : OStringBuffer aLine(200);
9693 0 : aLine.append( rObject.m_nObject );
9694 : aLine.append( " 0 obj\n"
9695 0 : "<</Type/XObject/Subtype/Image/Width " );
9696 0 : aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() );
9697 0 : aLine.append( " /Height " );
9698 0 : aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() );
9699 0 : aLine.append( " /BitsPerComponent 8 " );
9700 0 : if( rObject.m_bTrueColor )
9701 0 : aLine.append( "/ColorSpace/DeviceRGB" );
9702 : else
9703 0 : aLine.append( "/ColorSpace/DeviceGray" );
9704 0 : aLine.append( "/Filter/DCTDecode/Length " );
9705 0 : aLine.append( nLength );
9706 0 : if( nMaskObject )
9707 : {
9708 0 : aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " );
9709 0 : aLine.append( nMaskObject );
9710 0 : aLine.append( " 0 R " );
9711 : }
9712 0 : aLine.append( ">>\nstream\n" );
9713 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9714 :
9715 0 : checkAndEnableStreamEncryption( rObject.m_nObject );
9716 0 : CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) );
9717 0 : disableStreamEncryption();
9718 :
9719 0 : aLine.setLength( 0 );
9720 0 : aLine.append( "\nendstream\nendobj\n\n" );
9721 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9722 :
9723 0 : if( nMaskObject )
9724 : {
9725 0 : BitmapEmit aEmit;
9726 0 : aEmit.m_nObject = nMaskObject;
9727 0 : if( rObject.m_aMask.GetBitCount() == 1 )
9728 0 : aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask );
9729 0 : else if( rObject.m_aMask.GetBitCount() == 8 )
9730 0 : aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) );
9731 0 : writeBitmapObject( aEmit, true );
9732 : }
9733 :
9734 0 : return true;
9735 : }
9736 :
9737 0 : bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask )
9738 : {
9739 0 : CHECK_RETURN( updateObject( rObject.m_nObject ) );
9740 :
9741 0 : Bitmap aBitmap;
9742 0 : Color aTransparentColor( COL_TRANSPARENT );
9743 0 : bool bWriteMask = false;
9744 0 : if( ! bMask )
9745 : {
9746 0 : aBitmap = rObject.m_aBitmap.GetBitmap();
9747 0 : if( rObject.m_aBitmap.IsAlpha() )
9748 : {
9749 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 )
9750 0 : bWriteMask = true;
9751 : // else draw without alpha channel
9752 : }
9753 : else
9754 : {
9755 0 : switch( rObject.m_aBitmap.GetTransparentType() )
9756 : {
9757 : case TRANSPARENT_NONE:
9758 : // comes from drawMask function
9759 0 : if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask )
9760 0 : bMask = true;
9761 0 : break;
9762 : case TRANSPARENT_COLOR:
9763 0 : aTransparentColor = rObject.m_aBitmap.GetTransparentColor();
9764 0 : break;
9765 : case TRANSPARENT_BITMAP:
9766 0 : bWriteMask = true;
9767 0 : break;
9768 : }
9769 : }
9770 : }
9771 : else
9772 : {
9773 0 : if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() )
9774 : {
9775 0 : aBitmap = rObject.m_aBitmap.GetMask();
9776 0 : aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD );
9777 : DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" );
9778 : }
9779 0 : else if( aBitmap.GetBitCount() != 8 )
9780 : {
9781 0 : aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap();
9782 0 : aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS );
9783 : DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" );
9784 : }
9785 : }
9786 :
9787 0 : BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess();
9788 0 : AccessReleaser aReleaser( pAccess );
9789 :
9790 : bool bTrueColor;
9791 : sal_Int32 nBitsPerComponent;
9792 0 : switch( aBitmap.GetBitCount() )
9793 : {
9794 : case 1:
9795 : case 2:
9796 : case 4:
9797 : case 8:
9798 0 : bTrueColor = false;
9799 0 : nBitsPerComponent = aBitmap.GetBitCount();
9800 0 : break;
9801 : default:
9802 0 : bTrueColor = true;
9803 0 : nBitsPerComponent = 8;
9804 0 : break;
9805 : }
9806 :
9807 0 : sal_Int32 nStreamLengthObject = createObject();
9808 0 : sal_Int32 nMaskObject = 0;
9809 :
9810 : #if OSL_DEBUG_LEVEL > 1
9811 : emitComment( "PDFWriterImpl::writeBitmapObject" );
9812 : #endif
9813 0 : OStringBuffer aLine(1024);
9814 0 : aLine.append( rObject.m_nObject );
9815 : aLine.append( " 0 obj\n"
9816 0 : "<</Type/XObject/Subtype/Image/Width " );
9817 0 : aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9818 0 : aLine.append( "/Height " );
9819 0 : aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() );
9820 0 : aLine.append( "/BitsPerComponent " );
9821 0 : aLine.append( nBitsPerComponent );
9822 0 : aLine.append( "/Length " );
9823 0 : aLine.append( nStreamLengthObject );
9824 0 : aLine.append( " 0 R\n" );
9825 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9826 0 : if( nBitsPerComponent != 1 )
9827 : {
9828 0 : aLine.append( "/Filter/FlateDecode" );
9829 : }
9830 : else
9831 : {
9832 0 : aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " );
9833 0 : aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() );
9834 0 : aLine.append( ">>\n" );
9835 : }
9836 : #endif
9837 0 : if( ! bMask )
9838 : {
9839 0 : aLine.append( "/ColorSpace" );
9840 0 : if( bTrueColor )
9841 0 : aLine.append( "/DeviceRGB\n" );
9842 0 : else if( aBitmap.HasGreyPalette() )
9843 : {
9844 0 : aLine.append( "/DeviceGray\n" );
9845 0 : if( aBitmap.GetBitCount() == 1 )
9846 : {
9847 : // #i47395# 1 bit bitmaps occasionally have an inverted grey palette
9848 0 : sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9849 : DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9850 0 : if( nBlackIndex == 1 )
9851 0 : aLine.append( "/Decode[1 0]\n" );
9852 : }
9853 : }
9854 : else
9855 : {
9856 0 : aLine.append( "[ /Indexed/DeviceRGB " );
9857 0 : aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) );
9858 0 : aLine.append( "\n<" );
9859 0 : if( m_aContext.Encryption.Encrypt() )
9860 : {
9861 0 : enableStringEncryption( rObject.m_nObject );
9862 : //check encryption buffer size
9863 0 : if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) )
9864 : {
9865 0 : int nChar = 0;
9866 : //fill the encryption buffer
9867 0 : for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9868 : {
9869 0 : const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9870 0 : m_pEncryptionBuffer[nChar++] = rColor.GetRed();
9871 0 : m_pEncryptionBuffer[nChar++] = rColor.GetGreen();
9872 0 : m_pEncryptionBuffer[nChar++] = rColor.GetBlue();
9873 : }
9874 : //encrypt the colorspace lookup table
9875 0 : rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar );
9876 : //now queue the data for output
9877 0 : nChar = 0;
9878 0 : for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9879 : {
9880 0 : appendHex(m_pEncryptionBuffer[nChar++], aLine );
9881 0 : appendHex(m_pEncryptionBuffer[nChar++], aLine );
9882 0 : appendHex(m_pEncryptionBuffer[nChar++], aLine );
9883 : }
9884 : }
9885 : }
9886 : else //no encryption requested (PDF/A-1a program flow drops here)
9887 : {
9888 0 : for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ )
9889 : {
9890 0 : const BitmapColor& rColor = pAccess->GetPaletteColor( i );
9891 0 : appendHex( rColor.GetRed(), aLine );
9892 0 : appendHex( rColor.GetGreen(), aLine );
9893 0 : appendHex( rColor.GetBlue(), aLine );
9894 : }
9895 : }
9896 0 : aLine.append( ">\n]\n" );
9897 : }
9898 : }
9899 : else
9900 : {
9901 0 : if( aBitmap.GetBitCount() == 1 )
9902 : {
9903 0 : aLine.append( "/ImageMask true\n" );
9904 0 : sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) );
9905 : DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" );
9906 0 : if( nBlackIndex )
9907 0 : aLine.append( "/Decode[ 1 0 ]\n" );
9908 : else
9909 0 : aLine.append( "/Decode[ 0 1 ]\n" );
9910 : }
9911 0 : else if( aBitmap.GetBitCount() == 8 )
9912 : {
9913 : aLine.append( "/ColorSpace/DeviceGray\n"
9914 0 : "/Decode [ 1 0 ]\n" );
9915 : }
9916 : }
9917 :
9918 0 : if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651
9919 : {
9920 0 : if( bWriteMask )
9921 : {
9922 0 : nMaskObject = createObject();
9923 0 : if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 )
9924 0 : aLine.append( "/SMask " );
9925 : else
9926 0 : aLine.append( "/Mask " );
9927 0 : aLine.append( nMaskObject );
9928 0 : aLine.append( " 0 R\n" );
9929 : }
9930 0 : else if( aTransparentColor != Color( COL_TRANSPARENT ) )
9931 : {
9932 0 : aLine.append( "/Mask[ " );
9933 0 : if( bTrueColor )
9934 : {
9935 0 : aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9936 0 : aLine.append( ' ' );
9937 0 : aLine.append( (sal_Int32)aTransparentColor.GetRed() );
9938 0 : aLine.append( ' ' );
9939 0 : aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9940 0 : aLine.append( ' ' );
9941 0 : aLine.append( (sal_Int32)aTransparentColor.GetGreen() );
9942 0 : aLine.append( ' ' );
9943 0 : aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9944 0 : aLine.append( ' ' );
9945 0 : aLine.append( (sal_Int32)aTransparentColor.GetBlue() );
9946 : }
9947 : else
9948 : {
9949 0 : sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) );
9950 0 : aLine.append( nIndex );
9951 : }
9952 0 : aLine.append( " ]\n" );
9953 0 : }
9954 : }
9955 0 : else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) )
9956 0 : m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA );
9957 :
9958 : aLine.append( ">>\n"
9959 0 : "stream\n" );
9960 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
9961 0 : sal_uInt64 nStartPos = 0;
9962 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) );
9963 :
9964 0 : checkAndEnableStreamEncryption( rObject.m_nObject );
9965 : #ifndef DEBUG_DISABLE_PDFCOMPRESSION
9966 0 : if( nBitsPerComponent == 1 )
9967 : {
9968 0 : writeG4Stream( pAccess );
9969 : }
9970 : else
9971 : #endif
9972 : {
9973 0 : beginCompression();
9974 0 : if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB )
9975 : {
9976 0 : const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U );
9977 :
9978 0 : for( int i = 0; i < pAccess->Height(); i++ )
9979 : {
9980 0 : CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) );
9981 : }
9982 : }
9983 : else
9984 : {
9985 0 : const int nScanLineBytes = pAccess->Width()*3;
9986 0 : boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] );
9987 0 : for( int y = 0; y < pAccess->Height(); y++ )
9988 : {
9989 0 : for( int x = 0; x < pAccess->Width(); x++ )
9990 : {
9991 0 : BitmapColor aColor = pAccess->GetColor( y, x );
9992 0 : pCol[3*x+0] = aColor.GetRed();
9993 0 : pCol[3*x+1] = aColor.GetGreen();
9994 0 : pCol[3*x+2] = aColor.GetBlue();
9995 0 : }
9996 0 : CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) );
9997 0 : }
9998 : }
9999 0 : endCompression();
10000 : }
10001 0 : disableStreamEncryption();
10002 :
10003 0 : sal_uInt64 nEndPos = 0;
10004 0 : CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) );
10005 0 : aLine.setLength( 0 );
10006 0 : aLine.append( "\nendstream\nendobj\n\n" );
10007 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10008 0 : CHECK_RETURN( updateObject( nStreamLengthObject ) );
10009 0 : aLine.setLength( 0 );
10010 0 : aLine.append( nStreamLengthObject );
10011 0 : aLine.append( " 0 obj\n" );
10012 0 : aLine.append( (sal_Int64)(nEndPos-nStartPos) );
10013 0 : aLine.append( "\nendobj\n\n" );
10014 0 : CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) );
10015 :
10016 0 : if( nMaskObject )
10017 : {
10018 0 : BitmapEmit aEmit;
10019 0 : aEmit.m_nObject = nMaskObject;
10020 0 : aEmit.m_aBitmap = rObject.m_aBitmap;
10021 0 : return writeBitmapObject( aEmit, true );
10022 : }
10023 :
10024 0 : return true;
10025 : }
10026 :
10027 0 : void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask )
10028 : {
10029 0 : MARK( "drawJPGBitmap" );
10030 :
10031 0 : OStringBuffer aLine( 80 );
10032 0 : updateGraphicsState();
10033 :
10034 : // #i40055# sanity check
10035 0 : if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) )
10036 0 : return;
10037 0 : if( ! (rSizePixel.Width() && rSizePixel.Height()) )
10038 0 : return;
10039 :
10040 0 : rDCTData.Seek( 0 );
10041 0 : if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale )
10042 : {
10043 : // need to convert to grayscale;
10044 : // load stream to bitmap and draw the bitmap instead
10045 0 : Graphic aGraphic;
10046 0 : GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG );
10047 0 : Bitmap aBmp( aGraphic.GetBitmap() );
10048 0 : if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() )
10049 : {
10050 0 : BitmapEx aBmpEx( aBmp, rMask );
10051 0 : drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx );
10052 : }
10053 : else
10054 0 : drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp );
10055 0 : return;
10056 : }
10057 :
10058 0 : SvMemoryStream* pStream = new SvMemoryStream;
10059 0 : pStream->WriteStream( rDCTData );
10060 0 : pStream->Seek( STREAM_SEEK_TO_END );
10061 :
10062 0 : BitmapID aID;
10063 0 : aID.m_aPixelSize = rSizePixel;
10064 0 : aID.m_nSize = pStream->Tell();
10065 0 : pStream->Seek( STREAM_SEEK_TO_BEGIN );
10066 0 : aID.m_nChecksum = rtl_crc32( 0, pStream->GetData(), aID.m_nSize );
10067 0 : if( ! rMask.IsEmpty() )
10068 0 : aID.m_nMaskChecksum = rMask.GetChecksum();
10069 :
10070 0 : std::list< JPGEmit >::const_iterator it;
10071 0 : for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it )
10072 : ;
10073 0 : if( it == m_aJPGs.end() )
10074 : {
10075 0 : m_aJPGs.push_front( JPGEmit() );
10076 0 : JPGEmit& rEmit = m_aJPGs.front();
10077 0 : rEmit.m_nObject = createObject();
10078 0 : rEmit.m_aID = aID;
10079 0 : rEmit.m_pStream = pStream;
10080 0 : rEmit.m_bTrueColor = bIsTrueColor;
10081 0 : if( !! rMask && rMask.GetSizePixel() == rSizePixel )
10082 0 : rEmit.m_aMask = rMask;
10083 :
10084 0 : it = m_aJPGs.begin();
10085 : }
10086 : else
10087 0 : delete pStream;
10088 :
10089 0 : aLine.append( "q " );
10090 0 : sal_Int32 nCheckWidth = 0;
10091 0 : m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth );
10092 0 : aLine.append( " 0 0 " );
10093 0 : sal_Int32 nCheckHeight = 0;
10094 0 : m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight );
10095 0 : aLine.append( ' ' );
10096 0 : m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine );
10097 0 : aLine.append( " cm\n/Im" );
10098 0 : aLine.append( it->m_nObject );
10099 0 : aLine.append( " Do Q\n" );
10100 0 : if( nCheckWidth == 0 || nCheckHeight == 0 )
10101 : {
10102 : // #i97512# avoid invalid current matrix
10103 0 : aLine.setLength( 0 );
10104 0 : aLine.append( "\n%jpeg image /Im" );
10105 0 : aLine.append( it->m_nObject );
10106 0 : aLine.append( " scaled to zero size, omitted\n" );
10107 : }
10108 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10109 :
10110 0 : OStringBuffer aObjName( 16 );
10111 0 : aObjName.append( "Im" );
10112 0 : aObjName.append( it->m_nObject );
10113 0 : pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10114 :
10115 : }
10116 :
10117 0 : void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor )
10118 : {
10119 0 : OStringBuffer aLine( 80 );
10120 0 : updateGraphicsState();
10121 :
10122 0 : aLine.append( "q " );
10123 0 : if( rFillColor != Color( COL_TRANSPARENT ) )
10124 : {
10125 0 : appendNonStrokingColor( rFillColor, aLine );
10126 0 : aLine.append( ' ' );
10127 : }
10128 0 : sal_Int32 nCheckWidth = 0;
10129 0 : m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth );
10130 0 : aLine.append( " 0 0 " );
10131 0 : sal_Int32 nCheckHeight = 0;
10132 0 : m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight );
10133 0 : aLine.append( ' ' );
10134 0 : m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine );
10135 0 : aLine.append( " cm\n/Im" );
10136 0 : aLine.append( rBitmap.m_nObject );
10137 0 : aLine.append( " Do Q\n" );
10138 0 : if( nCheckWidth == 0 || nCheckHeight == 0 )
10139 : {
10140 : // #i97512# avoid invalid current matrix
10141 0 : aLine.setLength( 0 );
10142 0 : aLine.append( "\n%bitmap image /Im" );
10143 0 : aLine.append( rBitmap.m_nObject );
10144 0 : aLine.append( " scaled to zero size, omitted\n" );
10145 : }
10146 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10147 0 : }
10148 :
10149 0 : const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask )
10150 : {
10151 0 : BitmapEx aBitmap( i_rBitmap );
10152 0 : if( m_aContext.ColorMode == PDFWriter::DrawGreyscale )
10153 : {
10154 0 : BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS;
10155 0 : int nDepth = aBitmap.GetBitmap().GetBitCount();
10156 0 : if( nDepth <= 4 )
10157 0 : eConv = BMP_CONVERSION_4BIT_GREYS;
10158 0 : if( nDepth > 1 )
10159 0 : aBitmap.Convert( eConv );
10160 : }
10161 0 : BitmapID aID;
10162 0 : aID.m_aPixelSize = aBitmap.GetSizePixel();
10163 0 : aID.m_nSize = aBitmap.GetBitCount();
10164 0 : aID.m_nChecksum = aBitmap.GetBitmap().GetChecksum();
10165 0 : aID.m_nMaskChecksum = 0;
10166 0 : if( aBitmap.IsAlpha() )
10167 0 : aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum();
10168 : else
10169 : {
10170 0 : Bitmap aMask = aBitmap.GetMask();
10171 0 : if( ! aMask.IsEmpty() )
10172 0 : aID.m_nMaskChecksum = aMask.GetChecksum();
10173 : }
10174 0 : std::list< BitmapEmit >::const_iterator it;
10175 0 : for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it )
10176 : {
10177 0 : if( aID == it->m_aID )
10178 0 : break;
10179 : }
10180 0 : if( it == m_aBitmaps.end() )
10181 : {
10182 0 : m_aBitmaps.push_front( BitmapEmit() );
10183 0 : m_aBitmaps.front().m_aID = aID;
10184 0 : m_aBitmaps.front().m_aBitmap = aBitmap;
10185 0 : m_aBitmaps.front().m_nObject = createObject();
10186 0 : m_aBitmaps.front().m_bDrawMask = bDrawMask;
10187 0 : it = m_aBitmaps.begin();
10188 : }
10189 :
10190 0 : OStringBuffer aObjName( 16 );
10191 0 : aObjName.append( "Im" );
10192 0 : aObjName.append( it->m_nObject );
10193 0 : pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject );
10194 :
10195 0 : return *it;
10196 : }
10197 :
10198 0 : void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap )
10199 : {
10200 0 : MARK( "drawBitmap (Bitmap)" );
10201 :
10202 : // #i40055# sanity check
10203 0 : if( ! (rDestSize.Width() && rDestSize.Height()) )
10204 0 : return;
10205 :
10206 0 : const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) );
10207 0 : drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10208 : }
10209 :
10210 0 : void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap )
10211 : {
10212 0 : MARK( "drawBitmap (BitmapEx)" );
10213 :
10214 : // #i40055# sanity check
10215 0 : if( ! (rDestSize.Width() && rDestSize.Height()) )
10216 0 : return;
10217 :
10218 0 : const BitmapEmit& rEmit = createBitmapEmit( rBitmap );
10219 0 : drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) );
10220 : }
10221 :
10222 0 : sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize )
10223 : {
10224 0 : Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10225 : MapMode( MAP_POINT ),
10226 : getReferenceDevice(),
10227 0 : rSize ) );
10228 : // check if we already have this gradient
10229 0 : std::list<GradientEmit>::iterator it;
10230 : // rounding to point will generally lose some pixels
10231 : // round up to point boundary
10232 0 : aPtSize.Width()++;
10233 0 : aPtSize.Height()++;
10234 0 : for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it )
10235 : {
10236 0 : if( it->m_aGradient == rGradient )
10237 : {
10238 0 : if( it->m_aSize == aPtSize )
10239 0 : break;
10240 : }
10241 : }
10242 0 : if( it == m_aGradients.end() )
10243 : {
10244 0 : m_aGradients.push_front( GradientEmit() );
10245 0 : m_aGradients.front().m_aGradient = rGradient;
10246 0 : m_aGradients.front().m_nObject = createObject();
10247 0 : m_aGradients.front().m_aSize = aPtSize;
10248 0 : it = m_aGradients.begin();
10249 : }
10250 :
10251 0 : OStringBuffer aObjName( 16 );
10252 0 : aObjName.append( 'P' );
10253 0 : aObjName.append( it->m_nObject );
10254 0 : pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject );
10255 :
10256 0 : return it->m_nObject;
10257 : }
10258 :
10259 0 : void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient )
10260 : {
10261 0 : MARK( "drawGradient (Rectangle)" );
10262 :
10263 0 : if( m_aContext.Version == PDFWriter::PDF_1_2 )
10264 : {
10265 0 : drawRectangle( rRect );
10266 0 : return;
10267 : }
10268 :
10269 0 : sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() );
10270 :
10271 0 : Point aTranslate( rRect.BottomLeft() );
10272 0 : aTranslate += Point( 0, 1 );
10273 :
10274 0 : updateGraphicsState();
10275 :
10276 0 : OStringBuffer aLine( 80 );
10277 0 : aLine.append( "q 1 0 0 1 " );
10278 0 : m_aPages.back().appendPoint( aTranslate, aLine );
10279 0 : aLine.append( " cm " );
10280 : // if a stroke is appended reset the clip region before stroke
10281 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10282 0 : aLine.append( "q " );
10283 0 : aLine.append( "0 0 " );
10284 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10285 0 : aLine.append( ' ' );
10286 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10287 0 : aLine.append( " re W n\n" );
10288 :
10289 0 : aLine.append( "/P" );
10290 0 : aLine.append( nGradient );
10291 0 : aLine.append( " sh " );
10292 0 : if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) )
10293 : {
10294 0 : aLine.append( "Q 0 0 " );
10295 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false );
10296 0 : aLine.append( ' ' );
10297 0 : m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true );
10298 0 : aLine.append( " re S " );
10299 : }
10300 0 : aLine.append( "Q\n" );
10301 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10302 : }
10303 :
10304 0 : void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
10305 : {
10306 0 : MARK( "drawHatch" );
10307 :
10308 0 : updateGraphicsState();
10309 :
10310 0 : if( rPolyPoly.Count() )
10311 : {
10312 0 : PolyPolygon aPolyPoly( rPolyPoly );
10313 :
10314 0 : aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
10315 0 : push( PUSH_LINECOLOR );
10316 0 : setLineColor( rHatch.GetColor() );
10317 0 : getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, false );
10318 0 : pop();
10319 : }
10320 0 : }
10321 :
10322 0 : void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall )
10323 : {
10324 0 : MARK( "drawWallpaper" );
10325 :
10326 0 : bool bDrawColor = false;
10327 0 : bool bDrawGradient = false;
10328 0 : bool bDrawBitmap = false;
10329 :
10330 0 : BitmapEx aBitmap;
10331 0 : Point aBmpPos = rRect.TopLeft();
10332 0 : Size aBmpSize;
10333 0 : if( rWall.IsBitmap() )
10334 : {
10335 0 : aBitmap = rWall.GetBitmap();
10336 0 : aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(),
10337 0 : getMapMode(),
10338 : getReferenceDevice(),
10339 0 : aBitmap.GetPrefSize() );
10340 0 : Rectangle aRect( rRect );
10341 0 : if( rWall.IsRect() )
10342 : {
10343 0 : aRect = rWall.GetRect();
10344 0 : aBmpPos = aRect.TopLeft();
10345 0 : aBmpSize = aRect.GetSize();
10346 : }
10347 0 : if( rWall.GetStyle() != WALLPAPER_SCALE )
10348 : {
10349 0 : if( rWall.GetStyle() != WALLPAPER_TILE )
10350 : {
10351 0 : bDrawBitmap = true;
10352 0 : if( rWall.IsGradient() )
10353 0 : bDrawGradient = true;
10354 : else
10355 0 : bDrawColor = true;
10356 0 : switch( rWall.GetStyle() )
10357 : {
10358 : case WALLPAPER_TOPLEFT:
10359 0 : break;
10360 : case WALLPAPER_TOP:
10361 0 : aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10362 0 : break;
10363 : case WALLPAPER_LEFT:
10364 0 : aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10365 0 : break;
10366 : case WALLPAPER_TOPRIGHT:
10367 0 : aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10368 0 : break;
10369 : case WALLPAPER_CENTER:
10370 0 : aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10371 0 : aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10372 0 : break;
10373 : case WALLPAPER_RIGHT:
10374 0 : aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10375 0 : aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2;
10376 0 : break;
10377 : case WALLPAPER_BOTTOMLEFT:
10378 0 : aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10379 0 : break;
10380 : case WALLPAPER_BOTTOM:
10381 0 : aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2;
10382 0 : aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10383 0 : break;
10384 : case WALLPAPER_BOTTOMRIGHT:
10385 0 : aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width();
10386 0 : aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height();
10387 0 : break;
10388 : default: ;
10389 : }
10390 : }
10391 : else
10392 : {
10393 : // push the bitmap
10394 0 : const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) );
10395 :
10396 : // convert to page coordinates; this needs to be done here
10397 : // since the emit does not know the page anymore
10398 0 : Rectangle aConvertRect( aBmpPos, aBmpSize );
10399 0 : m_aPages.back().convertRect( aConvertRect );
10400 :
10401 0 : OStringBuffer aNameBuf(16);
10402 0 : aNameBuf.append( "Im" );
10403 0 : aNameBuf.append( rEmit.m_nObject );
10404 0 : OString aImageName( aNameBuf.makeStringAndClear() );
10405 :
10406 : // push the pattern
10407 0 : OStringBuffer aTilingStream( 32 );
10408 0 : appendFixedInt( aConvertRect.GetWidth(), aTilingStream );
10409 0 : aTilingStream.append( " 0 0 " );
10410 0 : appendFixedInt( aConvertRect.GetHeight(), aTilingStream );
10411 0 : aTilingStream.append( " 0 0 cm\n/" );
10412 0 : aTilingStream.append( aImageName );
10413 0 : aTilingStream.append( " Do\n" );
10414 :
10415 0 : m_aTilings.push_back( TilingEmit() );
10416 0 : m_aTilings.back().m_nObject = createObject();
10417 0 : m_aTilings.back().m_aRectangle = Rectangle( Point( 0, 0 ), aConvertRect.GetSize() );
10418 0 : m_aTilings.back().m_pTilingStream = new SvMemoryStream();
10419 0 : m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() );
10420 : // phase the tiling so wallpaper begins on upper left
10421 0 : m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor;
10422 0 : m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor;
10423 0 : m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject;
10424 :
10425 0 : updateGraphicsState();
10426 :
10427 0 : OStringBuffer aObjName( 16 );
10428 0 : aObjName.append( 'P' );
10429 0 : aObjName.append( m_aTilings.back().m_nObject );
10430 0 : OString aPatternName( aObjName.makeStringAndClear() );
10431 0 : pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject );
10432 :
10433 : // fill a rRect with the pattern
10434 0 : OStringBuffer aLine( 100 );
10435 0 : aLine.append( "q /Pattern cs /" );
10436 0 : aLine.append( aPatternName );
10437 0 : aLine.append( " scn " );
10438 0 : m_aPages.back().appendRect( rRect, aLine );
10439 0 : aLine.append( " f Q\n" );
10440 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10441 : }
10442 : }
10443 : else
10444 : {
10445 0 : aBmpPos = aRect.TopLeft();
10446 0 : aBmpSize = aRect.GetSize();
10447 0 : bDrawBitmap = true;
10448 : }
10449 :
10450 0 : if( aBitmap.IsTransparent() )
10451 : {
10452 0 : if( rWall.IsGradient() )
10453 0 : bDrawGradient = true;
10454 : else
10455 0 : bDrawColor = true;
10456 : }
10457 : }
10458 0 : else if( rWall.IsGradient() )
10459 0 : bDrawGradient = true;
10460 : else
10461 0 : bDrawColor = true;
10462 :
10463 0 : if( bDrawGradient )
10464 : {
10465 0 : drawGradient( rRect, rWall.GetGradient() );
10466 : }
10467 0 : if( bDrawColor )
10468 : {
10469 0 : Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor;
10470 0 : Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor;
10471 0 : setLineColor( Color( COL_TRANSPARENT ) );
10472 0 : setFillColor( rWall.GetColor() );
10473 0 : drawRectangle( rRect );
10474 0 : setLineColor( aOldLineColor );
10475 0 : setFillColor( aOldFillColor );
10476 : }
10477 0 : if( bDrawBitmap )
10478 : {
10479 : // set temporary clip region since aBmpPos and aBmpSize
10480 : // may be outside rRect
10481 0 : OStringBuffer aLine( 20 );
10482 0 : aLine.append( "q " );
10483 0 : m_aPages.back().appendRect( rRect, aLine );
10484 0 : aLine.append( " W n\n" );
10485 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10486 0 : drawBitmap( aBmpPos, aBmpSize, aBitmap );
10487 0 : writeBuffer( "Q\n", 2 );
10488 0 : }
10489 0 : }
10490 :
10491 0 : void PDFWriterImpl::updateGraphicsState(Mode const mode)
10492 : {
10493 0 : OStringBuffer aLine( 256 );
10494 0 : GraphicsState& rNewState = m_aGraphicsStack.front();
10495 : // first set clip region since it might invalidate everything else
10496 :
10497 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) )
10498 : {
10499 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion;
10500 :
10501 0 : if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion ||
10502 0 : ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) )
10503 : {
10504 0 : if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() )
10505 : {
10506 0 : aLine.append( "Q " );
10507 : // invalidate everything but the clip region
10508 0 : m_aCurrentPDFState = GraphicsState();
10509 0 : rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion);
10510 : }
10511 0 : if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() )
10512 : {
10513 : // clip region is always stored in private PDF mapmode
10514 0 : MapMode aNewMapMode = rNewState.m_aMapMode;
10515 0 : rNewState.m_aMapMode = m_aMapMode;
10516 0 : getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10517 0 : m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10518 :
10519 0 : aLine.append( "q " );
10520 0 : m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine );
10521 0 : aLine.append( "W* n\n" );
10522 0 : rNewState.m_aMapMode = aNewMapMode;
10523 0 : getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10524 0 : m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode;
10525 : }
10526 : }
10527 : }
10528 :
10529 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) )
10530 : {
10531 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode;
10532 0 : getReferenceDevice()->SetMapMode( rNewState.m_aMapMode );
10533 : }
10534 :
10535 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) )
10536 : {
10537 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont;
10538 0 : getReferenceDevice()->SetFont( rNewState.m_aFont );
10539 0 : getReferenceDevice()->ImplNewFont();
10540 : }
10541 :
10542 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) )
10543 : {
10544 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode;
10545 0 : getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode );
10546 : }
10547 :
10548 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) )
10549 : {
10550 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage;
10551 0 : getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage );
10552 : }
10553 :
10554 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) )
10555 : {
10556 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor;
10557 0 : if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor &&
10558 0 : rNewState.m_aLineColor != Color( COL_TRANSPARENT ) )
10559 : {
10560 0 : appendStrokingColor( rNewState.m_aLineColor, aLine );
10561 0 : aLine.append( "\n" );
10562 : }
10563 : }
10564 :
10565 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) )
10566 : {
10567 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor;
10568 0 : if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor &&
10569 0 : rNewState.m_aFillColor != Color( COL_TRANSPARENT ) )
10570 : {
10571 0 : appendNonStrokingColor( rNewState.m_aFillColor, aLine );
10572 0 : aLine.append( "\n" );
10573 : }
10574 : }
10575 :
10576 0 : if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) )
10577 : {
10578 0 : rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent;
10579 0 : if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent )
10580 : {
10581 : // TODO: switch extended graphicsstate
10582 : }
10583 : }
10584 :
10585 : // everything is up to date now
10586 0 : m_aCurrentPDFState = m_aGraphicsStack.front();
10587 0 : if ((mode != NOWRITE) && !aLine.isEmpty())
10588 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10589 0 : }
10590 :
10591 : /* #i47544# imitate OutputDevice behaviour:
10592 : * if a font with a nontransparent color is set, it overwrites the current
10593 : * text color. OTOH setting the text color will overwrite the color of the font.
10594 : */
10595 0 : void PDFWriterImpl::setFont( const Font& rFont )
10596 : {
10597 0 : Color aColor = rFont.GetColor();
10598 0 : if( aColor == Color( COL_TRANSPARENT ) )
10599 0 : aColor = m_aGraphicsStack.front().m_aFont.GetColor();
10600 0 : m_aGraphicsStack.front().m_aFont = rFont;
10601 0 : m_aGraphicsStack.front().m_aFont.SetColor( aColor );
10602 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont;
10603 0 : }
10604 :
10605 0 : void PDFWriterImpl::push( sal_uInt16 nFlags )
10606 : {
10607 : OSL_ENSURE( !m_aGraphicsStack.empty(), "invalid graphics stack" );
10608 0 : m_aGraphicsStack.push_front( m_aGraphicsStack.front() );
10609 0 : m_aGraphicsStack.front().m_nFlags = nFlags;
10610 0 : }
10611 :
10612 0 : void PDFWriterImpl::pop()
10613 : {
10614 : OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" );
10615 0 : if( m_aGraphicsStack.size() < 2 )
10616 0 : return;
10617 :
10618 0 : GraphicsState aState = m_aGraphicsStack.front();
10619 0 : m_aGraphicsStack.pop_front();
10620 0 : GraphicsState& rOld = m_aGraphicsStack.front();
10621 :
10622 : // move those parameters back that were not pushed
10623 : // in the first place
10624 0 : if( ! (aState.m_nFlags & PUSH_LINECOLOR) )
10625 0 : setLineColor( aState.m_aLineColor );
10626 0 : if( ! (aState.m_nFlags & PUSH_FILLCOLOR) )
10627 0 : setFillColor( aState.m_aFillColor );
10628 0 : if( ! (aState.m_nFlags & PUSH_FONT) )
10629 0 : setFont( aState.m_aFont );
10630 0 : if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) )
10631 0 : setTextColor( aState.m_aFont.GetColor() );
10632 0 : if( ! (aState.m_nFlags & PUSH_MAPMODE) )
10633 0 : setMapMode( aState.m_aMapMode );
10634 0 : if( ! (aState.m_nFlags & PUSH_CLIPREGION) )
10635 : {
10636 : // do not use setClipRegion here
10637 : // it would convert again assuming the current mapmode
10638 0 : rOld.m_aClipRegion = aState.m_aClipRegion;
10639 0 : rOld.m_bClipRegion = aState.m_bClipRegion;
10640 : }
10641 0 : if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) )
10642 0 : setTextLineColor( aState.m_aTextLineColor );
10643 0 : if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) )
10644 0 : setOverlineColor( aState.m_aOverlineColor );
10645 0 : if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) )
10646 0 : setTextAlign( aState.m_aFont.GetAlign() );
10647 0 : if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) )
10648 0 : setTextFillColor( aState.m_aFont.GetFillColor() );
10649 0 : if( ! (aState.m_nFlags & PUSH_REFPOINT) )
10650 : {
10651 : // what ?
10652 : }
10653 : // invalidate graphics state
10654 0 : m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U);
10655 : }
10656 :
10657 0 : void PDFWriterImpl::setMapMode( const MapMode& rMapMode )
10658 : {
10659 0 : m_aGraphicsStack.front().m_aMapMode = rMapMode;
10660 0 : getReferenceDevice()->SetMapMode( rMapMode );
10661 0 : m_aCurrentPDFState.m_aMapMode = rMapMode;
10662 0 : }
10663 :
10664 0 : void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10665 : {
10666 0 : basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode );
10667 0 : aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10668 0 : m_aGraphicsStack.front().m_aClipRegion = aRegion;
10669 0 : m_aGraphicsStack.front().m_bClipRegion = true;
10670 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10671 0 : }
10672 :
10673 0 : void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY )
10674 : {
10675 0 : if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() )
10676 : {
10677 0 : Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10678 : m_aMapMode,
10679 : getReferenceDevice(),
10680 0 : Point( nX, nY ) ) );
10681 0 : aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode,
10682 : m_aMapMode,
10683 : getReferenceDevice(),
10684 0 : Point() );
10685 0 : basegfx::B2DHomMatrix aMat;
10686 0 : aMat.translate( aPoint.X(), aPoint.Y() );
10687 0 : m_aGraphicsStack.front().m_aClipRegion.transform( aMat );
10688 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10689 : }
10690 0 : }
10691 :
10692 0 : bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect )
10693 : {
10694 : basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect(
10695 0 : basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) );
10696 0 : return intersectClipRegion( aRect );
10697 : }
10698 :
10699 0 : bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion )
10700 : {
10701 0 : basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) );
10702 0 : aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode );
10703 0 : m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion;
10704 0 : if( m_aGraphicsStack.front().m_bClipRegion )
10705 : {
10706 0 : basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) );
10707 0 : aRegion = basegfx::tools::prepareForPolygonOperation( aRegion );
10708 0 : m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion );
10709 : }
10710 : else
10711 : {
10712 0 : m_aGraphicsStack.front().m_aClipRegion = aRegion;
10713 0 : m_aGraphicsStack.front().m_bClipRegion = true;
10714 : }
10715 0 : return true;
10716 : }
10717 :
10718 0 : void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr )
10719 : {
10720 0 : if( nPageNr < 0 )
10721 0 : nPageNr = m_nCurrentPage;
10722 :
10723 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10724 0 : return;
10725 :
10726 0 : m_aNotes.push_back( PDFNoteEntry() );
10727 0 : m_aNotes.back().m_nObject = createObject();
10728 0 : m_aNotes.back().m_aContents = rNote;
10729 0 : m_aNotes.back().m_aRect = rRect;
10730 : // convert to default user space now, since the mapmode may change
10731 0 : m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect );
10732 :
10733 : // insert note to page's annotation list
10734 0 : m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject );
10735 : }
10736 :
10737 0 : sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr )
10738 : {
10739 0 : if( nPageNr < 0 )
10740 0 : nPageNr = m_nCurrentPage;
10741 :
10742 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10743 0 : return -1;
10744 :
10745 0 : sal_Int32 nRet = m_aLinks.size();
10746 :
10747 0 : m_aLinks.push_back( PDFLink() );
10748 0 : m_aLinks.back().m_nObject = createObject();
10749 0 : m_aLinks.back().m_nPage = nPageNr;
10750 0 : m_aLinks.back().m_aRect = rRect;
10751 : // convert to default user space now, since the mapmode may change
10752 0 : m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect );
10753 :
10754 : // insert link to page's annotation list
10755 0 : m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject );
10756 :
10757 0 : return nRet;
10758 : }
10759 :
10760 : //--->i56629
10761 0 : sal_Int32 PDFWriterImpl::createNamedDest( const OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10762 : {
10763 0 : if( nPageNr < 0 )
10764 0 : nPageNr = m_nCurrentPage;
10765 :
10766 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10767 0 : return -1;
10768 :
10769 0 : sal_Int32 nRet = m_aNamedDests.size();
10770 :
10771 0 : m_aNamedDests.push_back( PDFNamedDest() );
10772 0 : m_aNamedDests.back().m_aDestName = sDestName;
10773 0 : m_aNamedDests.back().m_nPage = nPageNr;
10774 0 : m_aNamedDests.back().m_eType = eType;
10775 0 : m_aNamedDests.back().m_aRect = rRect;
10776 : // convert to default user space now, since the mapmode may change
10777 0 : m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect );
10778 :
10779 0 : return nRet;
10780 : }
10781 : //<---i56629
10782 :
10783 0 : sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10784 : {
10785 0 : if( nPageNr < 0 )
10786 0 : nPageNr = m_nCurrentPage;
10787 :
10788 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
10789 0 : return -1;
10790 :
10791 0 : sal_Int32 nRet = m_aDests.size();
10792 :
10793 0 : m_aDests.push_back( PDFDest() );
10794 0 : m_aDests.back().m_nPage = nPageNr;
10795 0 : m_aDests.back().m_eType = eType;
10796 0 : m_aDests.back().m_aRect = rRect;
10797 : // convert to default user space now, since the mapmode may change
10798 0 : m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect );
10799 :
10800 0 : return nRet;
10801 : }
10802 :
10803 0 : sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType )
10804 : {
10805 0 : return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType );
10806 : }
10807 :
10808 0 : sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId )
10809 : {
10810 0 : if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10811 0 : return -1;
10812 0 : if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() )
10813 0 : return -2;
10814 :
10815 0 : m_aLinks[ nLinkId ].m_nDest = nDestId;
10816 :
10817 0 : return 0;
10818 : }
10819 :
10820 0 : sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL )
10821 : {
10822 0 : if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() )
10823 0 : return -1;
10824 :
10825 0 : m_aLinks[ nLinkId ].m_nDest = -1;
10826 :
10827 : using namespace ::com::sun::star;
10828 :
10829 0 : if (!m_xTrans.is())
10830 : {
10831 0 : uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
10832 0 : m_xTrans = util::URLTransformer::create(xContext);;
10833 : }
10834 :
10835 0 : util::URL aURL;
10836 0 : aURL.Complete = rURL;
10837 :
10838 0 : m_xTrans->parseStrict( aURL );
10839 :
10840 0 : m_aLinks[ nLinkId ].m_aURL = aURL.Complete;
10841 :
10842 0 : return 0;
10843 : }
10844 :
10845 0 : void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId )
10846 : {
10847 0 : m_aLinkPropertyMap[ nPropertyId ] = nLinkId;
10848 0 : }
10849 :
10850 0 : sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID )
10851 : {
10852 : // create new item
10853 0 : sal_Int32 nNewItem = m_aOutline.size();
10854 0 : m_aOutline.push_back( PDFOutlineEntry() );
10855 :
10856 : // set item attributes
10857 0 : setOutlineItemParent( nNewItem, nParent );
10858 0 : setOutlineItemText( nNewItem, rText );
10859 0 : setOutlineItemDest( nNewItem, nDestID );
10860 :
10861 0 : return nNewItem;
10862 : }
10863 :
10864 0 : sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent )
10865 : {
10866 0 : if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10867 0 : return -1;
10868 :
10869 0 : int nRet = 0;
10870 :
10871 0 : if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem )
10872 : {
10873 0 : nNewParent = 0;
10874 0 : nRet = -2;
10875 : }
10876 : // remove item from previous parent
10877 0 : sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID;
10878 0 : if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() )
10879 : {
10880 0 : PDFOutlineEntry& rParent = m_aOutline[ nParentID ];
10881 :
10882 0 : for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin();
10883 0 : it != rParent.m_aChildren.end(); ++it )
10884 : {
10885 0 : if( *it == nItem )
10886 : {
10887 0 : rParent.m_aChildren.erase( it );
10888 0 : break;
10889 : }
10890 : }
10891 : }
10892 :
10893 : // insert item to new parent's list of children
10894 0 : m_aOutline[ nNewParent ].m_aChildren.push_back( nItem );
10895 :
10896 0 : return nRet;
10897 : }
10898 :
10899 0 : sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText )
10900 : {
10901 0 : if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() )
10902 0 : return -1;
10903 :
10904 0 : m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText );
10905 0 : return 0;
10906 : }
10907 :
10908 0 : sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID )
10909 : {
10910 0 : if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist
10911 0 : return -1;
10912 0 : if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist
10913 0 : return -2;
10914 0 : m_aOutline[nItem].m_nDestID = nDestID;
10915 0 : return 0;
10916 : }
10917 :
10918 0 : const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType )
10919 : {
10920 0 : static std::map< PDFWriter::StructElement, const char* > aTagStrings;
10921 0 : if( aTagStrings.empty() )
10922 : {
10923 0 : aTagStrings[ PDFWriter::NonStructElement] = "NonStruct";
10924 0 : aTagStrings[ PDFWriter::Document ] = "Document";
10925 0 : aTagStrings[ PDFWriter::Part ] = "Part";
10926 0 : aTagStrings[ PDFWriter::Article ] = "Art";
10927 0 : aTagStrings[ PDFWriter::Section ] = "Sect";
10928 0 : aTagStrings[ PDFWriter::Division ] = "Div";
10929 0 : aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote";
10930 0 : aTagStrings[ PDFWriter::Caption ] = "Caption";
10931 0 : aTagStrings[ PDFWriter::TOC ] = "TOC";
10932 0 : aTagStrings[ PDFWriter::TOCI ] = "TOCI";
10933 0 : aTagStrings[ PDFWriter::Index ] = "Index";
10934 0 : aTagStrings[ PDFWriter::Paragraph ] = "P";
10935 0 : aTagStrings[ PDFWriter::Heading ] = "H";
10936 0 : aTagStrings[ PDFWriter::H1 ] = "H1";
10937 0 : aTagStrings[ PDFWriter::H2 ] = "H2";
10938 0 : aTagStrings[ PDFWriter::H3 ] = "H3";
10939 0 : aTagStrings[ PDFWriter::H4 ] = "H4";
10940 0 : aTagStrings[ PDFWriter::H5 ] = "H5";
10941 0 : aTagStrings[ PDFWriter::H6 ] = "H6";
10942 0 : aTagStrings[ PDFWriter::List ] = "L";
10943 0 : aTagStrings[ PDFWriter::ListItem ] = "LI";
10944 0 : aTagStrings[ PDFWriter::LILabel ] = "Lbl";
10945 0 : aTagStrings[ PDFWriter::LIBody ] = "LBody";
10946 0 : aTagStrings[ PDFWriter::Table ] = "Table";
10947 0 : aTagStrings[ PDFWriter::TableRow ] = "TR";
10948 0 : aTagStrings[ PDFWriter::TableHeader ] = "TH";
10949 0 : aTagStrings[ PDFWriter::TableData ] = "TD";
10950 0 : aTagStrings[ PDFWriter::Span ] = "Span";
10951 0 : aTagStrings[ PDFWriter::Quote ] = "Quote";
10952 0 : aTagStrings[ PDFWriter::Note ] = "Note";
10953 0 : aTagStrings[ PDFWriter::Reference ] = "Reference";
10954 0 : aTagStrings[ PDFWriter::BibEntry ] = "BibEntry";
10955 0 : aTagStrings[ PDFWriter::Code ] = "Code";
10956 0 : aTagStrings[ PDFWriter::Link ] = "Link";
10957 0 : aTagStrings[ PDFWriter::Figure ] = "Figure";
10958 0 : aTagStrings[ PDFWriter::Formula ] = "Formula";
10959 0 : aTagStrings[ PDFWriter::Form ] = "Form";
10960 : }
10961 :
10962 0 : std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType );
10963 :
10964 0 : return it != aTagStrings.end() ? it->second : "Div";
10965 : }
10966 :
10967 0 : void PDFWriterImpl::beginStructureElementMCSeq()
10968 : {
10969 0 : if( m_bEmitStructure &&
10970 0 : m_nCurrentStructElement > 0 && // StructTreeRoot
10971 0 : ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
10972 : )
10973 : {
10974 0 : PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ];
10975 0 : OStringBuffer aLine( 128 );
10976 0 : sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size();
10977 0 : aLine.append( "/" );
10978 0 : if( !rEle.m_aAlias.isEmpty() )
10979 0 : aLine.append( rEle.m_aAlias );
10980 : else
10981 0 : aLine.append( getStructureTag( rEle.m_eType ) );
10982 0 : aLine.append( "<</MCID " );
10983 0 : aLine.append( nMCID );
10984 0 : aLine.append( ">>BDC\n" );
10985 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
10986 :
10987 : // update the element's content list
10988 : #if OSL_DEBUG_LEVEL > 1
10989 : fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n",
10990 : nMCID,
10991 : m_aPages[ m_nCurrentPage ].m_nPageObject,
10992 : rEle.m_nFirstPageObject );
10993 : #endif
10994 0 : rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) );
10995 : // update the page's mcid parent list
10996 0 : m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject );
10997 : // mark element MC sequence as open
10998 0 : rEle.m_bOpenMCSeq = true;
10999 : }
11000 : // handle artifacts
11001 0 : else if( ! m_bEmitStructure && m_aContext.Tagged &&
11002 0 : m_nCurrentStructElement > 0 &&
11003 0 : m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement &&
11004 0 : ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence
11005 : )
11006 : {
11007 0 : OStringBuffer aLine( 128 );
11008 0 : aLine.append( "/Artifact BMC\n" );
11009 0 : writeBuffer( aLine.getStr(), aLine.getLength() );
11010 : // mark element MC sequence as open
11011 0 : m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true;
11012 : }
11013 0 : }
11014 :
11015 0 : void PDFWriterImpl::endStructureElementMCSeq()
11016 : {
11017 0 : if( m_nCurrentStructElement > 0 && // StructTreeRoot
11018 0 : ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) &&
11019 0 : m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence
11020 : )
11021 : {
11022 0 : writeBuffer( "EMC\n", 4 );
11023 0 : m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false;
11024 : }
11025 0 : }
11026 :
11027 0 : bool PDFWriterImpl::checkEmitStructure()
11028 : {
11029 0 : bool bEmit = false;
11030 0 : if( m_aContext.Tagged )
11031 : {
11032 0 : bEmit = true;
11033 0 : sal_Int32 nEle = m_nCurrentStructElement;
11034 0 : while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) )
11035 : {
11036 0 : if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement )
11037 : {
11038 0 : bEmit = false;
11039 0 : break;
11040 : }
11041 0 : nEle = m_aStructure[ nEle ].m_nParentElement;
11042 : }
11043 : }
11044 0 : return bEmit;
11045 : }
11046 :
11047 0 : sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const OUString& rAlias )
11048 : {
11049 0 : if( m_nCurrentPage < 0 )
11050 0 : return -1;
11051 :
11052 0 : if( ! m_aContext.Tagged )
11053 0 : return -1;
11054 :
11055 : // close eventual current MC sequence
11056 0 : endStructureElementMCSeq();
11057 :
11058 0 : if( m_nCurrentStructElement == 0 &&
11059 0 : eType != PDFWriter::Document && eType != PDFWriter::NonStructElement )
11060 : {
11061 : // struct tree root hit, but not beginning document
11062 : // this might happen with setCurrentStructureElement
11063 : // silently insert structure into document again if one properly exists
11064 0 : if( ! m_aStructure[ 0 ].m_aChildren.empty() )
11065 : {
11066 0 : PDFWriter::StructElement childType = PDFWriter::NonStructElement;
11067 0 : sal_Int32 nNewCurElement = 0;
11068 0 : const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren;
11069 0 : for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin();
11070 0 : childType != PDFWriter::Document && it != rRootChildren.end(); ++it )
11071 : {
11072 0 : nNewCurElement = *it;
11073 0 : childType = m_aStructure[ nNewCurElement ].m_eType;
11074 : }
11075 0 : if( childType == PDFWriter::Document )
11076 : {
11077 0 : m_nCurrentStructElement = nNewCurElement;
11078 : DBG_ASSERT( false, "Structure element inserted to StructTreeRoot that is not a document" );
11079 : }
11080 : else {
11081 : OSL_FAIL( "document structure in disorder !" );
11082 : }
11083 : }
11084 : else {
11085 : OSL_FAIL( "PDF document structure MUST be contained in a Document element" );
11086 : }
11087 : }
11088 :
11089 0 : sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11090 0 : m_aStructure.push_back( PDFStructureElement() );
11091 0 : PDFStructureElement& rEle = m_aStructure.back();
11092 0 : rEle.m_eType = eType;
11093 0 : rEle.m_nOwnElement = nNewId;
11094 0 : rEle.m_nParentElement = m_nCurrentStructElement;
11095 0 : rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject;
11096 0 : m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId );
11097 0 : m_nCurrentStructElement = nNewId;
11098 :
11099 : // handle alias names
11100 0 : if( !rAlias.isEmpty() && eType != PDFWriter::NonStructElement )
11101 : {
11102 0 : OStringBuffer aNameBuf( rAlias.getLength() );
11103 0 : appendName( rAlias, aNameBuf );
11104 0 : OString aAliasName( aNameBuf.makeStringAndClear() );
11105 0 : rEle.m_aAlias = aAliasName;
11106 0 : m_aRoleMap[ aAliasName ] = getStructureTag( eType );
11107 : }
11108 :
11109 : #if OSL_DEBUG_LEVEL > 1
11110 : OStringBuffer aLine( "beginStructureElement " );
11111 : aLine.append( m_nCurrentStructElement );
11112 : aLine.append( ": " );
11113 : aLine.append( getStructureTag( eType ) );
11114 : if( !rEle.m_aAlias.isEmpty() )
11115 : {
11116 : aLine.append( " aliased as \"" );
11117 : aLine.append( rEle.m_aAlias );
11118 : aLine.append( '\"' );
11119 : }
11120 : emitComment( aLine.getStr() );
11121 : #endif
11122 :
11123 : // check whether to emit structure henceforth
11124 0 : m_bEmitStructure = checkEmitStructure();
11125 :
11126 0 : if( m_bEmitStructure ) // don't create nonexistant objects
11127 : {
11128 0 : rEle.m_nObject = createObject();
11129 : // update parent's kids list
11130 0 : m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject );
11131 : }
11132 0 : return nNewId;
11133 : }
11134 :
11135 0 : void PDFWriterImpl::endStructureElement()
11136 : {
11137 0 : if( m_nCurrentPage < 0 )
11138 0 : return;
11139 :
11140 0 : if( ! m_aContext.Tagged )
11141 0 : return;
11142 :
11143 0 : if( m_nCurrentStructElement == 0 )
11144 : {
11145 : // hit the struct tree root, that means there is an endStructureElement
11146 : // without corresponding beginStructureElement
11147 0 : return;
11148 : }
11149 :
11150 : // end the marked content sequence
11151 0 : endStructureElementMCSeq();
11152 :
11153 : #if OSL_DEBUG_LEVEL > 1
11154 : OStringBuffer aLine( "endStructureElement " );
11155 : aLine.append( m_nCurrentStructElement );
11156 : aLine.append( ": " );
11157 : aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11158 : if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
11159 : {
11160 : aLine.append( " aliased as \"" );
11161 : aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11162 : aLine.append( '\"' );
11163 : }
11164 : #endif
11165 :
11166 : // "end" the structure element, the parent becomes current element
11167 0 : m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement;
11168 :
11169 : // check whether to emit structure henceforth
11170 0 : m_bEmitStructure = checkEmitStructure();
11171 :
11172 : #if OSL_DEBUG_LEVEL > 1
11173 : if( m_bEmitStructure )
11174 : emitComment( aLine.getStr() );
11175 : #endif
11176 : }
11177 :
11178 : //---> i94258
11179 : /*
11180 : * This function adds an internal structure list container to overcome the 8191 elements array limitation
11181 : * in kids element emission.
11182 : * Recursive function
11183 : *
11184 : */
11185 0 : void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle )
11186 : {
11187 0 : if( rEle.m_eType == PDFWriter::NonStructElement &&
11188 0 : rEle.m_nOwnElement != rEle.m_nParentElement )
11189 0 : return;
11190 :
11191 0 : for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it )
11192 : {
11193 0 : if( *it > 0 && *it < sal_Int32(m_aStructure.size()) )
11194 : {
11195 0 : PDFStructureElement& rChild = m_aStructure[ *it ];
11196 0 : if( rChild.m_eType != PDFWriter::NonStructElement )
11197 : {
11198 : //triggered when a child of the rEle element is found
11199 0 : if( rChild.m_nParentElement == rEle.m_nOwnElement )
11200 0 : addInternalStructureContainer( rChild );//examine the child
11201 : else
11202 : {
11203 : OSL_FAIL( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" );
11204 : #if OSL_DEBUG_LEVEL > 1
11205 : fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it );
11206 : #endif
11207 : }
11208 : }
11209 : }
11210 : else
11211 : {
11212 : OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" );
11213 : #if OSL_DEBUG_LEVEL > 1
11214 : fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it );
11215 : #endif
11216 : }
11217 : }
11218 :
11219 0 : if( rEle.m_nOwnElement != rEle.m_nParentElement )
11220 : {
11221 0 : if( !rEle.m_aKids.empty() )
11222 : {
11223 0 : if( rEle.m_aKids.size() > ncMaxPDFArraySize ) {
11224 : //then we need to add the containers for the kids elements
11225 : // a list to be used for the new kid element
11226 0 : std::list< PDFStructureElementKid > aNewKids;
11227 0 : std::list< sal_Int32 > aNewChildren;
11228 :
11229 : // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?)
11230 0 : OStringBuffer aNameBuf( "Div" );
11231 0 : OString aAliasName( aNameBuf.makeStringAndClear() );
11232 0 : m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division );
11233 :
11234 0 : while( rEle.m_aKids.size() > ncMaxPDFArraySize )
11235 : {
11236 0 : sal_Int32 nCurrentStructElement = rEle.m_nOwnElement;
11237 0 : sal_Int32 nNewId = sal_Int32(m_aStructure.size());
11238 0 : m_aStructure.push_back( PDFStructureElement() );
11239 0 : PDFStructureElement& rEleNew = m_aStructure.back();
11240 0 : rEleNew.m_aAlias = aAliasName;
11241 0 : rEleNew.m_eType = PDFWriter::Division; // a new Div type container
11242 0 : rEleNew.m_nOwnElement = nNewId;
11243 0 : rEleNew.m_nParentElement = nCurrentStructElement;
11244 : //inherit the same page as the first child to be reparented
11245 0 : rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject;
11246 0 : rEleNew.m_nObject = createObject();//assign a PDF object number
11247 : //add the object to the kid list of the parent
11248 0 : aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) );
11249 0 : aNewChildren.push_back( nNewId );
11250 :
11251 0 : std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() );
11252 0 : std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() );
11253 0 : advance( aChildEndIt, ncMaxPDFArraySize );
11254 0 : advance( aKidEndIt, ncMaxPDFArraySize );
11255 :
11256 : rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(),
11257 : rEle.m_aKids,
11258 : rEle.m_aKids.begin(),
11259 0 : aKidEndIt );
11260 : rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(),
11261 : rEle.m_aChildren,
11262 : rEle.m_aChildren.begin(),
11263 0 : aChildEndIt );
11264 : // set the kid's new parent
11265 0 : for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin();
11266 0 : it != rEleNew.m_aChildren.end(); ++it )
11267 : {
11268 0 : m_aStructure[ *it ].m_nParentElement = nNewId;
11269 : }
11270 : }
11271 : //finally add the new kids resulting from the container added
11272 0 : rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() );
11273 0 : rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() );
11274 : }
11275 : }
11276 : }
11277 : }
11278 : //<--- i94258
11279 :
11280 0 : bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle )
11281 : {
11282 0 : bool bSuccess = false;
11283 :
11284 0 : if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) )
11285 : {
11286 : // end eventual previous marked content sequence
11287 0 : endStructureElementMCSeq();
11288 :
11289 0 : m_nCurrentStructElement = nEle;
11290 0 : m_bEmitStructure = checkEmitStructure();
11291 : #if OSL_DEBUG_LEVEL > 1
11292 : OStringBuffer aLine( "setCurrentStructureElement " );
11293 : aLine.append( m_nCurrentStructElement );
11294 : aLine.append( ": " );
11295 : aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) );
11296 : if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() )
11297 : {
11298 : aLine.append( " aliased as \"" );
11299 : aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias );
11300 : aLine.append( '\"' );
11301 : }
11302 : if( ! m_bEmitStructure )
11303 : aLine.append( " (inside NonStruct)" );
11304 : emitComment( aLine.getStr() );
11305 : #endif
11306 0 : bSuccess = true;
11307 : }
11308 :
11309 0 : return bSuccess;
11310 : }
11311 :
11312 0 : bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal )
11313 : {
11314 0 : if( !m_aContext.Tagged )
11315 0 : return false;
11316 :
11317 0 : bool bInsert = false;
11318 0 : if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11319 : {
11320 0 : PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11321 0 : switch( eAttr )
11322 : {
11323 : case PDFWriter::Placement:
11324 0 : if( eVal == PDFWriter::Block ||
11325 0 : eVal == PDFWriter::Inline ||
11326 0 : eVal == PDFWriter::Before ||
11327 0 : eVal == PDFWriter::Start ||
11328 : eVal == PDFWriter::End )
11329 0 : bInsert = true;
11330 0 : break;
11331 : case PDFWriter::WritingMode:
11332 0 : if( eVal == PDFWriter::LrTb ||
11333 0 : eVal == PDFWriter::RlTb ||
11334 : eVal == PDFWriter::TbRl )
11335 : {
11336 0 : bInsert = true;
11337 : }
11338 0 : break;
11339 : case PDFWriter::TextAlign:
11340 0 : if( eVal == PDFWriter::Start ||
11341 0 : eVal == PDFWriter::Center ||
11342 0 : eVal == PDFWriter::End ||
11343 : eVal == PDFWriter::Justify )
11344 : {
11345 0 : if( eType == PDFWriter::Paragraph ||
11346 0 : eType == PDFWriter::Heading ||
11347 0 : eType == PDFWriter::H1 ||
11348 0 : eType == PDFWriter::H2 ||
11349 0 : eType == PDFWriter::H3 ||
11350 0 : eType == PDFWriter::H4 ||
11351 0 : eType == PDFWriter::H5 ||
11352 0 : eType == PDFWriter::H6 ||
11353 0 : eType == PDFWriter::List ||
11354 0 : eType == PDFWriter::ListItem ||
11355 0 : eType == PDFWriter::LILabel ||
11356 0 : eType == PDFWriter::LIBody ||
11357 0 : eType == PDFWriter::Table ||
11358 0 : eType == PDFWriter::TableRow ||
11359 0 : eType == PDFWriter::TableHeader ||
11360 : eType == PDFWriter::TableData )
11361 : {
11362 0 : bInsert = true;
11363 : }
11364 : }
11365 0 : break;
11366 : case PDFWriter::Width:
11367 : case PDFWriter::Height:
11368 0 : if( eVal == PDFWriter::Auto )
11369 : {
11370 0 : if( eType == PDFWriter::Figure ||
11371 0 : eType == PDFWriter::Formula ||
11372 0 : eType == PDFWriter::Form ||
11373 0 : eType == PDFWriter::Table ||
11374 0 : eType == PDFWriter::TableHeader ||
11375 : eType == PDFWriter::TableData )
11376 : {
11377 0 : bInsert = true;
11378 : }
11379 : }
11380 0 : break;
11381 : case PDFWriter::BlockAlign:
11382 0 : if( eVal == PDFWriter::Before ||
11383 0 : eVal == PDFWriter::Middle ||
11384 0 : eVal == PDFWriter::After ||
11385 : eVal == PDFWriter::Justify )
11386 : {
11387 0 : if( eType == PDFWriter::TableHeader ||
11388 : eType == PDFWriter::TableData )
11389 : {
11390 0 : bInsert = true;
11391 : }
11392 : }
11393 0 : break;
11394 : case PDFWriter::InlineAlign:
11395 0 : if( eVal == PDFWriter::Start ||
11396 0 : eVal == PDFWriter::Center ||
11397 : eVal == PDFWriter::End )
11398 : {
11399 0 : if( eType == PDFWriter::TableHeader ||
11400 : eType == PDFWriter::TableData )
11401 : {
11402 0 : bInsert = true;
11403 : }
11404 : }
11405 0 : break;
11406 : case PDFWriter::LineHeight:
11407 0 : if( eVal == PDFWriter::Normal ||
11408 : eVal == PDFWriter::Auto )
11409 : {
11410 : // only for ILSE and BLSE
11411 0 : if( eType == PDFWriter::Paragraph ||
11412 0 : eType == PDFWriter::Heading ||
11413 0 : eType == PDFWriter::H1 ||
11414 0 : eType == PDFWriter::H2 ||
11415 0 : eType == PDFWriter::H3 ||
11416 0 : eType == PDFWriter::H4 ||
11417 0 : eType == PDFWriter::H5 ||
11418 0 : eType == PDFWriter::H6 ||
11419 0 : eType == PDFWriter::List ||
11420 0 : eType == PDFWriter::ListItem ||
11421 0 : eType == PDFWriter::LILabel ||
11422 0 : eType == PDFWriter::LIBody ||
11423 0 : eType == PDFWriter::Table ||
11424 0 : eType == PDFWriter::TableRow ||
11425 0 : eType == PDFWriter::TableHeader ||
11426 0 : eType == PDFWriter::TableData ||
11427 0 : eType == PDFWriter::Span ||
11428 0 : eType == PDFWriter::Quote ||
11429 0 : eType == PDFWriter::Note ||
11430 0 : eType == PDFWriter::Reference ||
11431 0 : eType == PDFWriter::BibEntry ||
11432 0 : eType == PDFWriter::Code ||
11433 : eType == PDFWriter::Link )
11434 : {
11435 0 : bInsert = true;
11436 : }
11437 : }
11438 0 : break;
11439 : case PDFWriter::TextDecorationType:
11440 0 : if( eVal == PDFWriter::NONE ||
11441 0 : eVal == PDFWriter::Underline ||
11442 0 : eVal == PDFWriter::Overline ||
11443 : eVal == PDFWriter::LineThrough )
11444 : {
11445 : // only for ILSE and BLSE
11446 0 : if( eType == PDFWriter::Paragraph ||
11447 0 : eType == PDFWriter::Heading ||
11448 0 : eType == PDFWriter::H1 ||
11449 0 : eType == PDFWriter::H2 ||
11450 0 : eType == PDFWriter::H3 ||
11451 0 : eType == PDFWriter::H4 ||
11452 0 : eType == PDFWriter::H5 ||
11453 0 : eType == PDFWriter::H6 ||
11454 0 : eType == PDFWriter::List ||
11455 0 : eType == PDFWriter::ListItem ||
11456 0 : eType == PDFWriter::LILabel ||
11457 0 : eType == PDFWriter::LIBody ||
11458 0 : eType == PDFWriter::Table ||
11459 0 : eType == PDFWriter::TableRow ||
11460 0 : eType == PDFWriter::TableHeader ||
11461 0 : eType == PDFWriter::TableData ||
11462 0 : eType == PDFWriter::Span ||
11463 0 : eType == PDFWriter::Quote ||
11464 0 : eType == PDFWriter::Note ||
11465 0 : eType == PDFWriter::Reference ||
11466 0 : eType == PDFWriter::BibEntry ||
11467 0 : eType == PDFWriter::Code ||
11468 : eType == PDFWriter::Link )
11469 : {
11470 0 : bInsert = true;
11471 : }
11472 : }
11473 0 : break;
11474 : case PDFWriter::ListNumbering:
11475 0 : if( eVal == PDFWriter::NONE ||
11476 0 : eVal == PDFWriter::Disc ||
11477 0 : eVal == PDFWriter::Circle ||
11478 0 : eVal == PDFWriter::Square ||
11479 0 : eVal == PDFWriter::Decimal ||
11480 0 : eVal == PDFWriter::UpperRoman ||
11481 0 : eVal == PDFWriter::LowerRoman ||
11482 0 : eVal == PDFWriter::UpperAlpha ||
11483 : eVal == PDFWriter::LowerAlpha )
11484 : {
11485 0 : if( eType == PDFWriter::List )
11486 0 : bInsert = true;
11487 : }
11488 0 : break;
11489 0 : default: break;
11490 : }
11491 : }
11492 :
11493 0 : if( bInsert )
11494 0 : m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal );
11495 : #if OSL_DEBUG_LEVEL > 1
11496 : else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11497 : fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n",
11498 : getAttributeTag( eAttr ),
11499 : getAttributeValueTag( eVal ),
11500 : getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11501 : m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr()
11502 : );
11503 : #endif
11504 :
11505 0 : return bInsert;
11506 : }
11507 :
11508 0 : bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue )
11509 : {
11510 0 : if( ! m_aContext.Tagged )
11511 0 : return false;
11512 :
11513 0 : bool bInsert = false;
11514 0 : if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11515 : {
11516 0 : if( eAttr == PDFWriter::Language )
11517 : {
11518 0 : m_aStructure[ m_nCurrentStructElement ].m_aLocale = LanguageTag( (LanguageType)nValue ).getLocale();
11519 0 : return true;
11520 : }
11521 :
11522 0 : PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11523 0 : switch( eAttr )
11524 : {
11525 : case PDFWriter::SpaceBefore:
11526 : case PDFWriter::SpaceAfter:
11527 : case PDFWriter::StartIndent:
11528 : case PDFWriter::EndIndent:
11529 : // just for BLSE
11530 0 : if( eType == PDFWriter::Paragraph ||
11531 0 : eType == PDFWriter::Heading ||
11532 0 : eType == PDFWriter::H1 ||
11533 0 : eType == PDFWriter::H2 ||
11534 0 : eType == PDFWriter::H3 ||
11535 0 : eType == PDFWriter::H4 ||
11536 0 : eType == PDFWriter::H5 ||
11537 0 : eType == PDFWriter::H6 ||
11538 0 : eType == PDFWriter::List ||
11539 0 : eType == PDFWriter::ListItem ||
11540 0 : eType == PDFWriter::LILabel ||
11541 0 : eType == PDFWriter::LIBody ||
11542 0 : eType == PDFWriter::Table ||
11543 0 : eType == PDFWriter::TableRow ||
11544 0 : eType == PDFWriter::TableHeader ||
11545 : eType == PDFWriter::TableData )
11546 : {
11547 0 : bInsert = true;
11548 : }
11549 0 : break;
11550 : case PDFWriter::TextIndent:
11551 : // paragraph like BLSE and additional elements
11552 0 : if( eType == PDFWriter::Paragraph ||
11553 0 : eType == PDFWriter::Heading ||
11554 0 : eType == PDFWriter::H1 ||
11555 0 : eType == PDFWriter::H2 ||
11556 0 : eType == PDFWriter::H3 ||
11557 0 : eType == PDFWriter::H4 ||
11558 0 : eType == PDFWriter::H5 ||
11559 0 : eType == PDFWriter::H6 ||
11560 0 : eType == PDFWriter::LILabel ||
11561 0 : eType == PDFWriter::LIBody ||
11562 0 : eType == PDFWriter::TableHeader ||
11563 : eType == PDFWriter::TableData )
11564 : {
11565 0 : bInsert = true;
11566 : }
11567 0 : break;
11568 : case PDFWriter::Width:
11569 : case PDFWriter::Height:
11570 0 : if( eType == PDFWriter::Figure ||
11571 0 : eType == PDFWriter::Formula ||
11572 0 : eType == PDFWriter::Form ||
11573 0 : eType == PDFWriter::Table ||
11574 0 : eType == PDFWriter::TableHeader ||
11575 : eType == PDFWriter::TableData )
11576 : {
11577 0 : bInsert = true;
11578 : }
11579 0 : break;
11580 : case PDFWriter::LineHeight:
11581 : case PDFWriter::BaselineShift:
11582 : // only for ILSE and BLSE
11583 0 : if( eType == PDFWriter::Paragraph ||
11584 0 : eType == PDFWriter::Heading ||
11585 0 : eType == PDFWriter::H1 ||
11586 0 : eType == PDFWriter::H2 ||
11587 0 : eType == PDFWriter::H3 ||
11588 0 : eType == PDFWriter::H4 ||
11589 0 : eType == PDFWriter::H5 ||
11590 0 : eType == PDFWriter::H6 ||
11591 0 : eType == PDFWriter::List ||
11592 0 : eType == PDFWriter::ListItem ||
11593 0 : eType == PDFWriter::LILabel ||
11594 0 : eType == PDFWriter::LIBody ||
11595 0 : eType == PDFWriter::Table ||
11596 0 : eType == PDFWriter::TableRow ||
11597 0 : eType == PDFWriter::TableHeader ||
11598 0 : eType == PDFWriter::TableData ||
11599 0 : eType == PDFWriter::Span ||
11600 0 : eType == PDFWriter::Quote ||
11601 0 : eType == PDFWriter::Note ||
11602 0 : eType == PDFWriter::Reference ||
11603 0 : eType == PDFWriter::BibEntry ||
11604 0 : eType == PDFWriter::Code ||
11605 : eType == PDFWriter::Link )
11606 : {
11607 0 : bInsert = true;
11608 : }
11609 0 : break;
11610 : case PDFWriter::RowSpan:
11611 : case PDFWriter::ColSpan:
11612 : // only for table cells
11613 0 : if( eType == PDFWriter::TableHeader ||
11614 : eType == PDFWriter::TableData )
11615 : {
11616 0 : bInsert = true;
11617 : }
11618 0 : break;
11619 : case PDFWriter::LinkAnnotation:
11620 0 : if( eType == PDFWriter::Link )
11621 0 : bInsert = true;
11622 0 : break;
11623 0 : default: break;
11624 : }
11625 : }
11626 :
11627 0 : if( bInsert )
11628 0 : m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue );
11629 : #if OSL_DEBUG_LEVEL > 1
11630 : else if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11631 : fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n",
11632 : getAttributeTag( eAttr ),
11633 : (int)nValue,
11634 : getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ),
11635 : m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() );
11636 : #endif
11637 :
11638 0 : return bInsert;
11639 : }
11640 :
11641 0 : void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect )
11642 : {
11643 0 : sal_Int32 nPageNr = m_nCurrentPage;
11644 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged )
11645 0 : return;
11646 :
11647 0 : if( m_nCurrentStructElement > 0 && m_bEmitStructure )
11648 : {
11649 0 : PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType;
11650 0 : if( eType == PDFWriter::Figure ||
11651 0 : eType == PDFWriter::Formula ||
11652 0 : eType == PDFWriter::Form ||
11653 : eType == PDFWriter::Table )
11654 : {
11655 0 : m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect;
11656 : // convert to default user space now, since the mapmode may change
11657 0 : m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox );
11658 : }
11659 : }
11660 : }
11661 :
11662 0 : void PDFWriterImpl::setActualText( const OUString& rText )
11663 : {
11664 0 : if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11665 : {
11666 0 : m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText;
11667 : }
11668 0 : }
11669 :
11670 0 : void PDFWriterImpl::setAlternateText( const OUString& rText )
11671 : {
11672 0 : if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure )
11673 : {
11674 0 : m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText;
11675 : }
11676 0 : }
11677 :
11678 0 : void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr )
11679 : {
11680 0 : if( nPageNr < 0 )
11681 0 : nPageNr = m_nCurrentPage;
11682 :
11683 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11684 0 : return;
11685 :
11686 0 : m_aPages[ nPageNr ].m_nDuration = nSeconds;
11687 : }
11688 :
11689 0 : void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr )
11690 : {
11691 0 : if( nPageNr < 0 )
11692 0 : nPageNr = m_nCurrentPage;
11693 :
11694 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11695 0 : return;
11696 :
11697 0 : m_aPages[ nPageNr ].m_eTransition = eType;
11698 0 : m_aPages[ nPageNr ].m_nTransTime = nMilliSec;
11699 : }
11700 :
11701 0 : void PDFWriterImpl::ensureUniqueRadioOnValues()
11702 : {
11703 : // loop over radio groups
11704 0 : for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin();
11705 0 : group != m_aRadioGroupWidgets.end(); ++group )
11706 : {
11707 0 : PDFWidget& rGroupWidget = m_aWidgets[ group->second ];
11708 : // check whether all kids have a unique OnValue
11709 0 : boost::unordered_map< OUString, sal_Int32, OUStringHash > aOnValues;
11710 0 : int nChildren = rGroupWidget.m_aKidsIndex.size();
11711 0 : bool bIsUnique = true;
11712 0 : for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ )
11713 : {
11714 0 : int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11715 0 : const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue;
11716 : #if OSL_DEBUG_LEVEL > 1
11717 : fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() );
11718 : #endif
11719 0 : if( aOnValues.find( rVal ) == aOnValues.end() )
11720 : {
11721 0 : aOnValues[ rVal ] = 1;
11722 : }
11723 : else
11724 : {
11725 0 : bIsUnique = false;
11726 : }
11727 : }
11728 0 : if( ! bIsUnique )
11729 : {
11730 : #if OSL_DEBUG_LEVEL > 1
11731 : fprintf( stderr, "enforcing unique OnValues\n" );
11732 : #endif
11733 : // make unique by using ascending OnValues
11734 0 : for( int nKid = 0; nKid < nChildren; nKid++ )
11735 : {
11736 0 : int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11737 0 : PDFWidget& rKid = m_aWidgets[nKidIndex];
11738 0 : rKid.m_aOnValue = OUString::number( nKid+1 );
11739 0 : if( rKid.m_aValue != "Off" )
11740 0 : rKid.m_aValue = rKid.m_aOnValue;
11741 : }
11742 : }
11743 : // finally move the "Yes" appearance to the OnValue appearance
11744 0 : for( int nKid = 0; nKid < nChildren; nKid++ )
11745 : {
11746 0 : int nKidIndex = rGroupWidget.m_aKidsIndex[nKid];
11747 0 : PDFWidget& rKid = m_aWidgets[nKidIndex];
11748 0 : PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" );
11749 0 : if( app_it != rKid.m_aAppearances.end() )
11750 : {
11751 0 : PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" );
11752 0 : if( stream_it != app_it->second.end() )
11753 : {
11754 0 : SvMemoryStream* pStream = stream_it->second;
11755 0 : app_it->second.erase( stream_it );
11756 0 : OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 );
11757 0 : appendName( rKid.m_aOnValue, aBuf );
11758 0 : (app_it->second)[ aBuf.makeStringAndClear() ] = pStream;
11759 : }
11760 : #if OSL_DEBUG_LEVEL > 1
11761 : else
11762 : fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" );
11763 : #endif
11764 : }
11765 : // update selected radio button
11766 0 : if( rKid.m_aValue != "Off" )
11767 : {
11768 0 : rGroupWidget.m_aValue = rKid.m_aValue;
11769 : }
11770 : }
11771 0 : }
11772 0 : }
11773 :
11774 0 : sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn )
11775 : {
11776 0 : sal_Int32 nRadioGroupWidget = -1;
11777 :
11778 0 : std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup );
11779 :
11780 0 : if( it == m_aRadioGroupWidgets.end() )
11781 : {
11782 0 : m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget =
11783 0 : sal_Int32(m_aWidgets.size());
11784 :
11785 : // new group, insert the radiobutton
11786 0 : m_aWidgets.push_back( PDFWidget() );
11787 0 : m_aWidgets.back().m_nObject = createObject();
11788 0 : m_aWidgets.back().m_nPage = m_nCurrentPage;
11789 0 : m_aWidgets.back().m_eType = PDFWriter::RadioButton;
11790 0 : m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup;
11791 0 : m_aWidgets.back().m_nFlags |= 0x0000C000; // NoToggleToOff and Radio bits
11792 :
11793 0 : createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn );
11794 : }
11795 : else
11796 0 : nRadioGroupWidget = it->second;
11797 :
11798 0 : return nRadioGroupWidget;
11799 : }
11800 :
11801 0 : sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr )
11802 : {
11803 0 : if( nPageNr < 0 )
11804 0 : nPageNr = m_nCurrentPage;
11805 :
11806 0 : if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() )
11807 0 : return -1;
11808 :
11809 0 : bool sigHidden(true);
11810 0 : sal_Int32 nNewWidget = m_aWidgets.size();
11811 0 : m_aWidgets.push_back( PDFWidget() );
11812 :
11813 0 : m_aWidgets.back().m_nObject = createObject();
11814 0 : m_aWidgets.back().m_aRect = rControl.Location;
11815 0 : m_aWidgets.back().m_nPage = nPageNr;
11816 0 : m_aWidgets.back().m_eType = rControl.getType();
11817 :
11818 0 : sal_Int32 nRadioGroupWidget = -1;
11819 : // for unknown reasons the radio buttons of a radio group must not have a
11820 : // field name, else the buttons are in fact check boxes -
11821 : // that is multiple buttons of the radio group can be selected
11822 0 : if( rControl.getType() == PDFWriter::RadioButton )
11823 0 : nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) );
11824 : else
11825 : {
11826 0 : createWidgetFieldName( nNewWidget, rControl );
11827 : }
11828 :
11829 : // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid
11830 0 : PDFWidget& rNewWidget = m_aWidgets[nNewWidget];
11831 0 : rNewWidget.m_aDescription = rControl.Description;
11832 0 : rNewWidget.m_aText = rControl.Text;
11833 : rNewWidget.m_nTextStyle = rControl.TextStyle &
11834 : ( TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP |
11835 : TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM |
11836 0 : TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK );
11837 0 : rNewWidget.m_nTabOrder = rControl.TabOrder;
11838 :
11839 : // various properties are set via the flags (/Ff) property of the field dict
11840 0 : if( rControl.ReadOnly )
11841 0 : rNewWidget.m_nFlags |= 1;
11842 0 : if( rControl.getType() == PDFWriter::PushButton )
11843 : {
11844 0 : const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl);
11845 0 : if( rNewWidget.m_nTextStyle == 0 )
11846 : rNewWidget.m_nTextStyle =
11847 : TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER |
11848 0 : TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11849 :
11850 0 : rNewWidget.m_nFlags |= 0x00010000;
11851 0 : if( !rBtn.URL.isEmpty() )
11852 0 : rNewWidget.m_aListEntries.push_back( rBtn.URL );
11853 0 : rNewWidget.m_bSubmit = rBtn.Submit;
11854 0 : rNewWidget.m_bSubmitGet = rBtn.SubmitGet;
11855 0 : rNewWidget.m_nDest = rBtn.Dest;
11856 0 : createDefaultPushButtonAppearance( rNewWidget, rBtn );
11857 : }
11858 0 : else if( rControl.getType() == PDFWriter::RadioButton )
11859 : {
11860 0 : const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl);
11861 0 : if( rNewWidget.m_nTextStyle == 0 )
11862 : rNewWidget.m_nTextStyle =
11863 0 : TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11864 : /* PDF sees a RadioButton group as one radio button with
11865 : * children which are in turn check boxes
11866 : *
11867 : * so we need to create a radio button on demand for a new group
11868 : * and insert a checkbox for each RadioButtonWidget as its child
11869 : */
11870 0 : rNewWidget.m_eType = PDFWriter::CheckBox;
11871 0 : rNewWidget.m_nRadioGroup = rBtn.RadioGroup;
11872 :
11873 : DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" );
11874 :
11875 0 : PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget];
11876 0 : rRadioButton.m_aKids.push_back( rNewWidget.m_nObject );
11877 0 : rRadioButton.m_aKidsIndex.push_back( nNewWidget );
11878 0 : rNewWidget.m_nParent = rRadioButton.m_nObject;
11879 :
11880 0 : rNewWidget.m_aValue = "Off";
11881 0 : rNewWidget.m_aOnValue = rBtn.OnValue;
11882 0 : if( rRadioButton.m_aValue.isEmpty() && rBtn.Selected )
11883 : {
11884 0 : rNewWidget.m_aValue = rNewWidget.m_aOnValue;
11885 0 : rRadioButton.m_aValue = rNewWidget.m_aOnValue;
11886 : }
11887 0 : createDefaultRadioButtonAppearance( rNewWidget, rBtn );
11888 :
11889 : // union rect of radio group
11890 0 : Rectangle aRect = rNewWidget.m_aRect;
11891 0 : m_aPages[ nPageNr ].convertRect( aRect );
11892 0 : rRadioButton.m_aRect.Union( aRect );
11893 : }
11894 0 : else if( rControl.getType() == PDFWriter::CheckBox )
11895 : {
11896 0 : const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl);
11897 0 : if( rNewWidget.m_nTextStyle == 0 )
11898 : rNewWidget.m_nTextStyle =
11899 0 : TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11900 :
11901 0 : rNewWidget.m_aValue = rBox.Checked ? OUString("Yes") : OUString("Off" );
11902 : // create default appearance before m_aRect gets transformed
11903 0 : createDefaultCheckBoxAppearance( rNewWidget, rBox );
11904 : }
11905 0 : else if( rControl.getType() == PDFWriter::ListBox )
11906 : {
11907 0 : if( rNewWidget.m_nTextStyle == 0 )
11908 0 : rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
11909 :
11910 0 : const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl);
11911 0 : rNewWidget.m_aListEntries = rLstBox.Entries;
11912 0 : rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries;
11913 0 : rNewWidget.m_aValue = rLstBox.Text;
11914 0 : if( rLstBox.DropDown )
11915 0 : rNewWidget.m_nFlags |= 0x00020000;
11916 0 : if( rLstBox.Sort )
11917 0 : rNewWidget.m_nFlags |= 0x00080000;
11918 0 : if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 )
11919 0 : rNewWidget.m_nFlags |= 0x00200000;
11920 :
11921 0 : createDefaultListBoxAppearance( rNewWidget, rLstBox );
11922 : }
11923 0 : else if( rControl.getType() == PDFWriter::ComboBox )
11924 : {
11925 0 : if( rNewWidget.m_nTextStyle == 0 )
11926 0 : rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER;
11927 :
11928 0 : const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl);
11929 0 : rNewWidget.m_aValue = rBox.Text;
11930 0 : rNewWidget.m_aListEntries = rBox.Entries;
11931 0 : rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag
11932 0 : if( rBox.Sort )
11933 0 : rNewWidget.m_nFlags |= 0x00080000;
11934 :
11935 0 : PDFWriter::ListBoxWidget aLBox;
11936 0 : aLBox.Name = rBox.Name;
11937 0 : aLBox.Description = rBox.Description;
11938 0 : aLBox.Text = rBox.Text;
11939 0 : aLBox.TextStyle = rBox.TextStyle;
11940 0 : aLBox.ReadOnly = rBox.ReadOnly;
11941 0 : aLBox.Border = rBox.Border;
11942 0 : aLBox.BorderColor = rBox.BorderColor;
11943 0 : aLBox.Background = rBox.Background;
11944 0 : aLBox.BackgroundColor = rBox.BackgroundColor;
11945 0 : aLBox.TextFont = rBox.TextFont;
11946 0 : aLBox.TextColor = rBox.TextColor;
11947 0 : aLBox.DropDown = true;
11948 0 : aLBox.Sort = rBox.Sort;
11949 0 : aLBox.MultiSelect = false;
11950 0 : aLBox.Entries = rBox.Entries;
11951 :
11952 0 : createDefaultListBoxAppearance( rNewWidget, aLBox );
11953 : }
11954 0 : else if( rControl.getType() == PDFWriter::Edit )
11955 : {
11956 0 : if( rNewWidget.m_nTextStyle == 0 )
11957 0 : rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER;
11958 :
11959 0 : const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl);
11960 0 : if( rEdit.MultiLine )
11961 : {
11962 0 : rNewWidget.m_nFlags |= 0x00001000;
11963 0 : rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK;
11964 : }
11965 0 : if( rEdit.Password )
11966 0 : rNewWidget.m_nFlags |= 0x00002000;
11967 0 : if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 )
11968 0 : rNewWidget.m_nFlags |= 0x00100000;
11969 0 : rNewWidget.m_nMaxLen = rEdit.MaxLen;
11970 0 : rNewWidget.m_aValue = rEdit.Text;
11971 :
11972 0 : createDefaultEditAppearance( rNewWidget, rEdit );
11973 : }
11974 : #if !defined(ANDROID) && !defined(IOS)
11975 0 : else if( rControl.getType() == PDFWriter::Signature)
11976 : {
11977 0 : const PDFWriter::SignatureWidget& rSig = static_cast<const PDFWriter::SignatureWidget&>(rControl);
11978 0 : sigHidden = rSig.SigHidden;
11979 :
11980 0 : if ( sigHidden )
11981 0 : rNewWidget.m_aRect = Rectangle(0, 0, 0, 0);
11982 :
11983 0 : m_nSignatureObject = createObject();
11984 0 : rNewWidget.m_aValue = OUString::number( m_nSignatureObject );
11985 0 : rNewWidget.m_aValue += " 0 R";
11986 : // let's add a fake appearance
11987 0 : rNewWidget.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream();
11988 : }
11989 : #endif
11990 :
11991 : // if control is a hidden signature, do not convert coordinates since we
11992 : // need /Rect [ 0 0 0 0 ]
11993 0 : if ( ! ( ( rControl.getType() == PDFWriter::Signature ) && ( sigHidden ) ) )
11994 : {
11995 : // convert to default user space now, since the mapmode may change
11996 : // note: create default appearances before m_aRect gets transformed
11997 0 : m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect );
11998 : }
11999 :
12000 : // insert widget to page's annotation list
12001 0 : m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject );
12002 :
12003 : // mark page as having widgets
12004 0 : m_aPages[ nPageNr ].m_bHasWidgets = true;
12005 :
12006 0 : return nNewWidget;
12007 : }
12008 :
12009 0 : void PDFWriterImpl::addStream( const OUString& rMimeType, PDFOutputStream* pStream, bool bCompress )
12010 : {
12011 0 : if( pStream )
12012 : {
12013 0 : m_aAdditionalStreams.push_back( PDFAddStream() );
12014 0 : PDFAddStream& rStream = m_aAdditionalStreams.back();
12015 0 : rStream.m_aMimeType = !rMimeType.isEmpty()
12016 : ? OUString( rMimeType )
12017 0 : : OUString( "application/octet-stream" );
12018 0 : rStream.m_pStream = pStream;
12019 0 : rStream.m_bCompress = bCompress;
12020 : }
12021 516 : }
12022 :
12023 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|