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 <config_features.h> 21 22 #include <sal/types.h> 23 24 #include <math.h> 25 #include <algorithm> 26 27 #if defined __GNUC__ && __cplusplus > 201402L 28 #pragma GCC diagnostic push 29 #pragma GCC diagnostic ignored "-Wregister" 30 #endif 31 #include <lcms2.h> 32 #if defined __GNUC__ && __cplusplus > 201402L 33 #pragma GCC diagnostic pop 34 #endif 35 36 #include <basegfx/matrix/b2dhommatrix.hxx> 37 #include <basegfx/polygon/b2dpolygon.hxx> 38 #include <basegfx/polygon/b2dpolygontools.hxx> 39 #include <basegfx/polygon/b2dpolypolygon.hxx> 40 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 41 #include <basegfx/polygon/b2dpolypolygontools.hxx> 42 #include <memory> 43 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 44 #include <com/sun/star/util/URL.hpp> 45 #include <com/sun/star/util/URLTransformer.hpp> 46 #include <comphelper/processfactory.hxx> 47 #include <comphelper/random.hxx> 48 #include <comphelper/string.hxx> 49 #include <cppuhelper/implbase.hxx> 50 #include <i18nlangtag/languagetag.hxx> 51 #include <o3tl/numeric.hxx> 52 #include <o3tl/make_unique.hxx> 53 #include <osl/file.hxx> 54 #include <osl/thread.h> 55 #include <rtl/crc.h> 56 #include <rtl/digest.h> 57 #include <rtl/ustrbuf.hxx> 58 #include <svl/urihelper.hxx> 59 #include <tools/debug.hxx> 60 #include <tools/fract.hxx> 61 #include <tools/stream.hxx> 62 #include <tools/urlobj.hxx> 63 #include <tools/zcodec.hxx> 64 #include <svl/cryptosign.hxx> 65 #include <vcl/bitmapex.hxx> 66 #include <vcl/bitmapaccess.hxx> 67 #include <vcl/cvtgrf.hxx> 68 #include <vcl/image.hxx> 69 #include <vcl/lineinfo.hxx> 70 #include <vcl/metric.hxx> 71 #include <vcl/settings.hxx> 72 #include <vcl/strhelper.hxx> 73 #include <vcl/svapp.hxx> 74 #include <vcl/virdev.hxx> 75 #include <vcl/filter/pdfdocument.hxx> 76 #include <comphelper/hash.hxx> 77 78 #include <fontsubset.hxx> 79 #include <outdev.h> 80 #include <PhysicalFontFace.hxx> 81 #include <salgdi.hxx> 82 #include <sallayout.hxx> 83 #include <textlayout.hxx> 84 #include <textlineinfo.hxx> 85 86 #include "pdfwriter_impl.hxx" 87 88 #ifdef _WIN32 89 // WinCrypt headers for PDF signing 90 // Note: this uses Windows 7 APIs and requires the relevant data types 91 #include <prewin.h> 92 #include <wincrypt.h> 93 #include <postwin.h> 94 #include <comphelper/windowserrorstring.hxx> 95 #endif 96 97 #include <config_eot.h> 98 99 #if ENABLE_EOT 100 #include <libeot/libeot.h> 101 #endif 102 103 using namespace vcl; 104 using namespace::com::sun::star; 105 106 static bool g_bDebugDisableCompression = getenv("VCL_DEBUG_DISABLE_PDFCOMPRESSION"); 107 108 #if HAVE_FEATURE_NSS 109 // Is this length truly the maximum possible, or just a number that 110 // seemed large enough when the author tested this (with some type of 111 // certificates)? I suspect the latter. 112 113 // Used to be 0x4000 = 16384, but a sample signed PDF (produced by 114 // some other software) provided by the customer has a signature 115 // content that is 30000 bytes. The SampleSignedPDFDocument.pdf from 116 // Adobe has one that is 21942 bytes. So let's be careful. Pity this 117 // can't be dynamic, at least not without restructuring the code. Also 118 // note that the checks in the code for this being too small 119 // apparently are broken, if this overflows you end up with an invalid 120 // PDF. Need to fix that. 121 122 #define MAX_SIGNATURE_CONTENT_LENGTH 50000 123 #endif 124 125 #ifdef DO_TEST_PDF 126 class PDFTestOutputStream : public PDFOutputStream 127 { 128 public: 129 virtual ~PDFTestOutputStream(); 130 virtual void write( const css::uno::Reference< css::io::XOutputStream >& xStream ); 131 }; 132 133 PDFTestOutputStream::~PDFTestOutputStream() 134 { 135 } 136 137 void PDFTestOutputStream::write( const css::uno::Reference< css::io::XOutputStream >& xStream ) 138 { 139 OString aStr( "lalala\ntest\ntest\ntest" ); 140 css::uno::Sequence< sal_Int8 > aData( aStr.getLength() ); 141 memcpy( aData.getArray(), aStr.getStr(), aStr.getLength() ); 142 xStream->writeBytes( aData ); 143 } 144 145 // this test code cannot be used to test PDF/A-1 because it forces 146 // control item (widgets) to bypass the structure controlling 147 // the embedding of such elements in actual run 148 void doTestCode() 149 { 150 static const char* pHome = getenv( "HOME" ); 151 OUString aTestFile( "file://" ); 152 aTestFile += OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 ); 153 aTestFile += "/pdf_export_test.pdf"; 154 155 PDFWriter::PDFWriterContext aContext; 156 aContext.URL = aTestFile; 157 aContext.Version = PDFWriter::PDF_1_4; 158 aContext.Tagged = true; 159 aContext.InitialPage = 2; 160 aContext.DocumentInfo.Title = "PDF export test document"; 161 aContext.DocumentInfo.Producer = "VCL"; 162 163 aContext.SignPDF = true; 164 aContext.SignLocation = "Burdur"; 165 aContext.SignReason = "Some valid reason to sign"; 166 aContext.SignContact = "signer@example.com"; 167 168 css::uno::Reference< css::beans::XMaterialHolder > xEnc; 169 PDFWriter aWriter( aContext, xEnc ); 170 aWriter.NewPage( 595, 842 ); 171 aWriter.BeginStructureElement( PDFWriter::Document ); 172 // set duration of 3 sec for first page 173 aWriter.SetAutoAdvanceTime( 3 ); 174 aWriter.SetMapMode( MapMode( MapUnit::Map100thMM ) ); 175 176 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 177 aWriter.SetLineColor( Color( COL_LIGHTGREEN ) ); 178 aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 ); 179 180 aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) ); 181 aWriter.SetTextColor( Color( COL_BLACK ) ); 182 aWriter.SetLineColor( Color( COL_BLACK ) ); 183 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) ); 184 185 Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) ); 186 aWriter.DrawRect( aRect ); 187 aWriter.DrawText( aRect, OUString( "Link annot 1" ) ); 188 sal_Int32 nFirstLink = aWriter.CreateLink( aRect ); 189 PDFNote aNote; 190 aNote.Title = "A small test note"; 191 aNote.Contents = "There is no business like show business like no business i know. Everything about it is appealing."; 192 aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote ); 193 194 Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) ); 195 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 196 aWriter.DrawRect( aTargetRect ); 197 aWriter.DrawText( aTargetRect, "Dest second link" ); 198 sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect ); 199 200 aWriter.BeginStructureElement( PDFWriter::Section ); 201 aWriter.BeginStructureElement( PDFWriter::Heading ); 202 aWriter.DrawText( Point(4500, 9000), "A small structure test" ); 203 aWriter.EndStructureElement(); 204 aWriter.BeginStructureElement( PDFWriter::Paragraph ); 205 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb ); 206 aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline ); 207 aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ), 208 "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.", 209 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak 210 ); 211 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." ); 212 aWriter.SetAlternateText( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." ); 213 aWriter.EndStructureElement(); 214 aWriter.BeginStructureElement( PDFWriter::Paragraph ); 215 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb ); 216 aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ), 217 "This paragraph is nothing special either but ends on the next page structurewise", 218 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak 219 ); 220 221 aWriter.NewPage( 595, 842 ); 222 // test AddStream interface 223 aWriter.AddStream( "text/plain", new PDFTestOutputStream(), true ); 224 // set transitional mode 225 aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 ); 226 aWriter.SetMapMode( MapMode( MapUnit::Map100thMM ) ); 227 aWriter.SetTextColor( Color( COL_BLACK ) ); 228 aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) ); 229 aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ), 230 "Here's where all things come to an end ... well at least the paragraph from the last page.", 231 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak 232 ); 233 aWriter.EndStructureElement(); 234 235 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) ); 236 // disable structure 237 aWriter.BeginStructureElement( PDFWriter::NonStructElement ); 238 aWriter.DrawRect( aRect ); 239 aWriter.BeginStructureElement( PDFWriter::Paragraph ); 240 aWriter.DrawText( aRect, "Link annot 2" ); 241 sal_Int32 nSecondLink = aWriter.CreateLink( aRect ); 242 243 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 244 aWriter.BeginStructureElement( PDFWriter::ListItem ); 245 aWriter.DrawRect( aTargetRect ); 246 aWriter.DrawText( aTargetRect, "Dest first link" ); 247 sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect ); 248 // enable structure 249 aWriter.EndStructureElement(); 250 251 aWriter.EndStructureElement(); 252 aWriter.EndStructureElement(); 253 aWriter.BeginStructureElement( PDFWriter::Figure ); 254 aWriter.BeginStructureElement( PDFWriter::Caption ); 255 aWriter.DrawText( Point( 4500, 9000 ), "Some drawing stuff inside the structure" ); 256 aWriter.EndStructureElement(); 257 258 // test clipping 259 basegfx::B2DPolyPolygon aClip; 260 basegfx::B2DPolygon aClipPoly; 261 aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) ); 262 aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) ); 263 aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) ); 264 aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) ); 265 aClipPoly.setClosed( true ); 266 aClip.append( aClipPoly ); 267 268 aWriter.Push( PushFlags::CLIPREGION | PushFlags::FILLCOLOR ); 269 aWriter.SetClipRegion( aClip ); 270 aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) ); 271 aWriter.MoveClipRegion( 1000, 500 ); 272 aWriter.SetFillColor( Color( COL_RED ) ); 273 aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) ); 274 aWriter.Pop(); 275 // test transparency 276 // draw background 277 Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) ); 278 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 279 aWriter.DrawRect( aTranspRect ); 280 aWriter.BeginTransparencyGroup(); 281 282 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 283 aWriter.DrawEllipse( aTranspRect ); 284 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) ); 285 aWriter.DrawText( aTranspRect, 286 "Some transparent text", 287 DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak ); 288 289 aWriter.EndTransparencyGroup( aTranspRect, 50 ); 290 291 // prepare an alpha mask 292 Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) ); 293 Bitmap::ScopedWriteAccess pAcc(aTransMask); 294 for( int nX = 0; nX < 256; nX++ ) 295 for( int nY = 0; nY < 256; nY++ ) 296 pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) ); 297 pAcc.reset(); 298 aTransMask.SetPrefMapMode( MapUnit::MapMM ); 299 aTransMask.SetPrefSize( Size( 10, 10 ) ); 300 301 aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask ); 302 303 aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) ); 304 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 305 aWriter.DrawRect( aTranspRect ); 306 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 307 aWriter.DrawEllipse( aTranspRect ); 308 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) ); 309 aWriter.DrawText( aTranspRect, 310 "Some transparent text", 311 DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak ); 312 aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) ); 313 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 314 aWriter.DrawRect( aTranspRect ); 315 316 Bitmap aImageBmp( Size( 256, 256 ), 24 ); 317 pAcc = Bitmap::ScopedWriteAccess(aImageBmp); 318 pAcc->SetFillColor( Color( 0xff, 0, 0xff ) ); 319 pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) ); 320 pAcc.reset(); 321 BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) ); 322 aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx ); 323 324 aWriter.EndStructureElement(); 325 aWriter.EndStructureElement(); 326 327 LineInfo aLI( LineStyle::Dash, 3 ); 328 aLI.SetDashCount( 2 ); 329 aLI.SetDashLen( 50 ); 330 aLI.SetDotCount( 2 ); 331 aLI.SetDotLen( 25 ); 332 aLI.SetDistance( 15 ); 333 Point aLIPoints[] = { Point( 4000, 10000 ), 334 Point( 8000, 12000 ), 335 Point( 3000, 19000 ) }; 336 tools::Polygon aLIPoly( 3, aLIPoints ); 337 aWriter.SetLineColor( Color( COL_BLUE ) ); 338 aWriter.SetFillColor(); 339 aWriter.DrawPolyLine( aLIPoly, aLI ); 340 341 aLI.SetDashCount( 4 ); 342 aLIPoly.Move( 1000, 1000 ); 343 aWriter.DrawPolyLine( aLIPoly, aLI ); 344 345 aWriter.NewPage( 595, 842 ); 346 aWriter.SetMapMode( MapMode( MapUnit::Map100thMM ) ); 347 Wallpaper aWall( aTransMask ); 348 aWall.SetStyle( WallpaperStyle::Tile ); 349 aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall ); 350 351 aWriter.NewPage( 595, 842 ); 352 aWriter.SetMapMode( MapMode( MapUnit::Map100thMM ) ); 353 aWriter.SetFont( Font( OUString( "Times" ), Size( 0, 500 ) ) ); 354 aWriter.SetTextColor( Color( COL_BLACK ) ); 355 aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) ); 356 aWriter.DrawRect( aRect ); 357 aWriter.DrawText( aRect, "www.heise.de" ); 358 sal_Int32 nURILink = aWriter.CreateLink( aRect ); 359 aWriter.SetLinkURL( nURILink, OUString( "http://www.heise.de" ) ); 360 361 aWriter.SetLinkDest( nFirstLink, nFirstDest ); 362 aWriter.SetLinkDest( nSecondLink, nSecondDest ); 363 364 // include a button 365 PDFWriter::PushButtonWidget aBtn; 366 aBtn.Name = "testButton"; 367 aBtn.Description = "A test button"; 368 aBtn.Text = "hit me"; 369 aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) ); 370 aBtn.Border = aBtn.Background = true; 371 aWriter.CreateControl( aBtn ); 372 373 // include a uri button 374 PDFWriter::PushButtonWidget aUriBtn; 375 aUriBtn.Name = "wwwButton"; 376 aUriBtn.Description = "A URI button"; 377 aUriBtn.Text = "to www"; 378 aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) ); 379 aUriBtn.Border = aUriBtn.Background = true; 380 aUriBtn.URL = "http://www.heise.de"; 381 aWriter.CreateControl( aUriBtn ); 382 383 // include a dest button 384 PDFWriter::PushButtonWidget aDstBtn; 385 aDstBtn.Name = "destButton"; 386 aDstBtn.Description = "A Dest button"; 387 aDstBtn.Text = "to paragraph"; 388 aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) ); 389 aDstBtn.Border = aDstBtn.Background = true; 390 aDstBtn.Dest = nFirstDest; 391 aWriter.CreateControl( aDstBtn ); 392 393 PDFWriter::CheckBoxWidget aCBox; 394 aCBox.Name = "textCheckBox"; 395 aCBox.Description = "A test check box"; 396 aCBox.Text = "check me"; 397 aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) ); 398 aCBox.Checked = true; 399 aCBox.Border = aCBox.Background = false; 400 aWriter.CreateControl( aCBox ); 401 402 PDFWriter::CheckBoxWidget aCBox2; 403 aCBox2.Name = "textCheckBox2"; 404 aCBox2.Description = "Another test check box"; 405 aCBox2.Text = "check me right"; 406 aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) ); 407 aCBox2.Checked = true; 408 aCBox2.Border = aCBox2.Background = false; 409 aCBox2.ButtonIsLeft = false; 410 aWriter.CreateControl( aCBox2 ); 411 412 PDFWriter::RadioButtonWidget aRB1; 413 aRB1.Name = "rb1_1"; 414 aRB1.Description = "radio 1 button 1"; 415 aRB1.Text = "Despair"; 416 aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) ); 417 aRB1.Selected = true; 418 aRB1.RadioGroup = 1; 419 aRB1.Border = aRB1.Background = true; 420 aRB1.ButtonIsLeft = false; 421 aRB1.BorderColor = Color( COL_LIGHTGREEN ); 422 aRB1.BackgroundColor = Color( COL_LIGHTBLUE ); 423 aRB1.TextColor = Color( COL_LIGHTRED ); 424 aRB1.TextFont = Font( OUString( "Courier" ), Size( 0, 800 ) ); 425 aWriter.CreateControl( aRB1 ); 426 427 PDFWriter::RadioButtonWidget aRB2; 428 aRB2.Name = "rb2_1"; 429 aRB2.Description = "radio 2 button 1"; 430 aRB2.Text = "Joy"; 431 aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) ); 432 aRB2.Selected = true; 433 aRB2.RadioGroup = 2; 434 aWriter.CreateControl( aRB2 ); 435 436 PDFWriter::RadioButtonWidget aRB3; 437 aRB3.Name = "rb1_2"; 438 aRB3.Description = "radio 1 button 2"; 439 aRB3.Text = "Desperation"; 440 aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) ); 441 aRB3.Selected = true; 442 aRB3.RadioGroup = 1; 443 aWriter.CreateControl( aRB3 ); 444 445 PDFWriter::EditWidget aEditBox; 446 aEditBox.Name = "testEdit"; 447 aEditBox.Description = "A test edit field"; 448 aEditBox.Text = "A little test text"; 449 aEditBox.TextStyle = DrawTextFlags::Left | DrawTextFlags::VCenter; 450 aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) ); 451 aEditBox.MaxLen = 100; 452 aEditBox.Border = aEditBox.Background = true; 453 aEditBox.BorderColor = Color( COL_BLACK ); 454 aWriter.CreateControl( aEditBox ); 455 456 // normal list box 457 PDFWriter::ListBoxWidget aLstBox; 458 aLstBox.Name = "testListBox"; 459 aLstBox.Text = "One"; 460 aLstBox.Description = "select me"; 461 aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) ); 462 aLstBox.Sort = true; 463 aLstBox.MultiSelect = true; 464 aLstBox.Border = aLstBox.Background = true; 465 aLstBox.BorderColor = Color( COL_BLACK ); 466 aLstBox.Entries.push_back( OUString( "One" ) ); 467 aLstBox.Entries.push_back( OUString( "Two" ) ); 468 aLstBox.Entries.push_back( OUString( "Three" ) ); 469 aLstBox.Entries.push_back( OUString( "Four" ) ); 470 aLstBox.SelectedEntries.push_back( 1 ); 471 aLstBox.SelectedEntries.push_back( 2 ); 472 aWriter.CreateControl( aLstBox ); 473 474 // dropdown list box 475 aLstBox.Name = "testDropDownListBox"; 476 aLstBox.DropDown = true; 477 aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) ); 478 aWriter.CreateControl( aLstBox ); 479 480 // combo box 481 PDFWriter::ComboBoxWidget aComboBox; 482 aComboBox.Name = "testComboBox"; 483 aComboBox.Text = "test a combobox"; 484 aComboBox.Entries.push_back( OUString( "Larry" ) ); 485 aComboBox.Entries.push_back( OUString( "Curly" ) ); 486 aComboBox.Entries.push_back( OUString( "Moe" ) ); 487 aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) ); 488 aWriter.CreateControl( aComboBox ); 489 490 // test outlines 491 sal_Int32 nPage1OL = aWriter.CreateOutlineItem(); 492 aWriter.SetOutlineItemText( nPage1OL, OUString( "Page 1" ) ); 493 aWriter.SetOutlineItemDest( nPage1OL, nSecondDest ); 494 aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2" ), nSecondDest ); 495 aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 revisited" ), nSecondDest ); 496 aWriter.CreateOutlineItem( nPage1OL, OUString( "Dest 2 again" ), nSecondDest ); 497 sal_Int32 nPage2OL = aWriter.CreateOutlineItem(); 498 aWriter.SetOutlineItemText( nPage2OL, OUString( "Page 2" ) ); 499 aWriter.CreateOutlineItem( nPage2OL, OUString( "Dest 1" ), nFirstDest ); 500 501 aWriter.EndStructureElement(); // close document 502 503 aWriter.Emit(); 504 } 505 #endif 506 507 static const sal_Int32 nLog10Divisor = 1; 508 static const double fDivisor = 10.0; 509 510 static inline double pixelToPoint( double px ) { return px/fDivisor; } 511 static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); } 512 513 const sal_uInt8 PDFWriterImpl::s_nPadString[32] = 514 { 515 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, 516 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A 517 }; 518 519 static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer ) 520 { 521 static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', 522 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 523 rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] ); 524 rBuffer.append( pHexDigits[ nInt & 15 ] ); 525 } 526 527 static void appendName( const OUString& rStr, OStringBuffer& rBuffer ) 528 { 529 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1 530 // I guess than when reading the #xx sequence it will count for a single character. 531 OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) ); 532 int nLen = aStr.getLength(); 533 for( int i = 0; i < nLen; i++ ) 534 { 535 /* #i16920# PDF recommendation: output UTF8, any byte 536 * outside the interval [33(=ASCII'!');126(=ASCII'~')] 537 * should be escaped hexadecimal 538 * for the sake of ghostscript which also reads PDF 539 * but has a narrower acceptance rate we only pass 540 * alphanumerics and '-' literally. 541 */ 542 if( (aStr[i] >= 'A' && aStr[i] <= 'Z' ) || 543 (aStr[i] >= 'a' && aStr[i] <= 'z' ) || 544 (aStr[i] >= '0' && aStr[i] <= '9' ) || 545 aStr[i] == '-' ) 546 { 547 rBuffer.append( aStr[i] ); 548 } 549 else 550 { 551 rBuffer.append( '#' ); 552 appendHex( (sal_Int8)aStr[i], rBuffer ); 553 } 554 } 555 } 556 557 static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer ) 558 { 559 // FIXME i59651 see above 560 while( pStr && *pStr ) 561 { 562 if( (*pStr >= 'A' && *pStr <= 'Z' ) || 563 (*pStr >= 'a' && *pStr <= 'z' ) || 564 (*pStr >= '0' && *pStr <= '9' ) || 565 *pStr == '-' ) 566 { 567 rBuffer.append( *pStr ); 568 } 569 else 570 { 571 rBuffer.append( '#' ); 572 appendHex( (sal_Int8)*pStr, rBuffer ); 573 } 574 pStr++; 575 } 576 } 577 578 //used only to emit encoded passwords 579 static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer ) 580 { 581 while( nLength ) 582 { 583 switch( *pStr ) 584 { 585 case '\n' : 586 rBuffer.append( "\\n" ); 587 break; 588 case '\r' : 589 rBuffer.append( "\\r" ); 590 break; 591 case '\t' : 592 rBuffer.append( "\\t" ); 593 break; 594 case '\b' : 595 rBuffer.append( "\\b" ); 596 break; 597 case '\f' : 598 rBuffer.append( "\\f" ); 599 break; 600 case '(' : 601 case ')' : 602 case '\\' : 603 rBuffer.append( "\\" ); 604 rBuffer.append( (sal_Char) *pStr ); 605 break; 606 default: 607 rBuffer.append( (sal_Char) *pStr ); 608 break; 609 } 610 pStr++; 611 nLength--; 612 } 613 } 614 615 /* 616 * Convert a string before using it. 617 * 618 * This string conversion function is needed because the destination name 619 * in a PDF file seen through an Internet browser should be 620 * specially crafted, in order to be used directly by the browser. 621 * In this way the fragment part of a hyperlink to a PDF file (e.g. something 622 * as 'test1/test2/a-file.pdf\#thefragment) will be (hopefully) interpreted by the 623 * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called 624 * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf 625 * and go to named destination thefragment using default zoom'. 626 * The conversion is needed because in case of a fragment in the form: Slide%201 627 * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201 628 * using this conversion, in both the generated named destinations, fragment and GoToR 629 * destination. 630 * 631 * The names for destinations are name objects and so they don't need to be encrypted 632 * even though they expose the content of PDF file (e.g. guessing the PDF content from the 633 * destination name). 634 * 635 * Further limitation: it is advisable to use standard ASCII characters for 636 * OOo bookmarks. 637 */ 638 static void appendDestinationName( const OUString& rString, OStringBuffer& rBuffer ) 639 { 640 const sal_Unicode* pStr = rString.getStr(); 641 sal_Int32 nLen = rString.getLength(); 642 for( int i = 0; i < nLen; i++ ) 643 { 644 sal_Unicode aChar = pStr[i]; 645 if( (aChar >= '0' && aChar <= '9' ) || 646 (aChar >= 'a' && aChar <= 'z' ) || 647 (aChar >= 'A' && aChar <= 'Z' ) || 648 aChar == '-' ) 649 { 650 rBuffer.append((sal_Char)aChar); 651 } 652 else 653 { 654 sal_Int8 aValueHigh = sal_Int8(aChar >> 8); 655 if(aValueHigh > 0) 656 appendHex( aValueHigh, rBuffer ); 657 appendHex( (sal_Int8)(aChar & 255 ), rBuffer ); 658 } 659 } 660 } 661 662 void PDFWriter::AppendUnicodeTextString(const OUString& rString, OStringBuffer& rBuffer) 663 { 664 rBuffer.append( "FEFF" ); 665 const sal_Unicode* pStr = rString.getStr(); 666 sal_Int32 nLen = rString.getLength(); 667 for( int i = 0; i < nLen; i++ ) 668 { 669 sal_Unicode aChar = pStr[i]; 670 appendHex( (sal_Int8)(aChar >> 8), rBuffer ); 671 appendHex( (sal_Int8)(aChar & 255 ), rBuffer ); 672 } 673 } 674 675 void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl ) 676 { 677 /* #i80258# previously we use appendName here 678 however we need a slightly different coding scheme than the normal 679 name encoding for field names 680 */ 681 const OUString& rName = (m_aContext.Version > PDFWriter::PDFVersion::PDF_1_2) ? i_rControl.Name : i_rControl.Text; 682 OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) ); 683 int nLen = aStr.getLength(); 684 685 OStringBuffer aBuffer( rName.getLength()+64 ); 686 for( int i = 0; i < nLen; i++ ) 687 { 688 /* #i16920# PDF recommendation: output UTF8, any byte 689 * outside the interval [32(=ASCII' ');126(=ASCII'~')] 690 * should be escaped hexadecimal 691 */ 692 if( aStr[i] >= 32 && aStr[i] <= 126 ) 693 aBuffer.append( aStr[i] ); 694 else 695 { 696 aBuffer.append( '#' ); 697 appendHex( (sal_Int8)aStr[i], aBuffer ); 698 } 699 } 700 701 OString aFullName( aBuffer.makeStringAndClear() ); 702 703 /* #i82785# create hierarchical fields down to the for each dot in i_rName */ 704 sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0; 705 OString aPartialName; 706 OString aDomain; 707 do 708 { 709 nLastTokenIndex = nTokenIndex; 710 aPartialName = aFullName.getToken( 0, '.', nTokenIndex ); 711 if( nTokenIndex != -1 ) 712 { 713 // find or create a hierarchical field 714 // first find the fully qualified name up to this field 715 aDomain = aFullName.copy( 0, nTokenIndex-1 ); 716 std::unordered_map< OString, sal_Int32 >::const_iterator it = m_aFieldNameMap.find( aDomain ); 717 if( it == m_aFieldNameMap.end() ) 718 { 719 // create new hierarchy field 720 sal_Int32 nNewWidget = m_aWidgets.size(); 721 m_aWidgets.emplace_back( ); 722 m_aWidgets[nNewWidget].m_nObject = createObject(); 723 m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy; 724 m_aWidgets[nNewWidget].m_aName = aPartialName; 725 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject; 726 m_aFieldNameMap[aDomain] = nNewWidget; 727 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject; 728 if( nLastTokenIndex > 0 ) 729 { 730 // this field is not a root field and 731 // needs to be inserted to its parent 732 OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) ); 733 it = m_aFieldNameMap.find( aParentDomain ); 734 OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" ); 735 if( it != m_aFieldNameMap.end() ) 736 { 737 OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" ); 738 if( it->second < sal_Int32(m_aWidgets.size()) ) 739 { 740 PDFWidget& rParentField( m_aWidgets[it->second] ); 741 rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject ); 742 rParentField.m_aKidsIndex.push_back( nNewWidget ); 743 m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject; 744 } 745 } 746 } 747 } 748 else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy ) 749 { 750 // this is invalid, someone tries to have a terminal field as parent 751 // example: a button with the name foo.bar exists and 752 // another button is named foo.bar.no 753 // workaround: put the second terminal field as much up in the hierarchy as 754 // necessary to have a non-terminal field as parent (or none at all) 755 // since it->second already is terminal, we just need to use its parent 756 aDomain.clear(); 757 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 ); 758 if( nLastTokenIndex > 0 ) 759 { 760 aDomain = aFullName.copy( 0, nLastTokenIndex-1 ); 761 OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() ); 762 aBuf.append( aDomain ); 763 aBuf.append( '.' ); 764 aBuf.append( aPartialName ); 765 aFullName = aBuf.makeStringAndClear(); 766 } 767 else 768 aFullName = aPartialName; 769 break; 770 } 771 } 772 } while( nTokenIndex != -1 ); 773 774 // insert widget into its hierarchy field 775 if( !aDomain.isEmpty() ) 776 { 777 std::unordered_map< OString, sal_Int32 >::const_iterator it = m_aFieldNameMap.find( aDomain ); 778 if( it != m_aFieldNameMap.end() ) 779 { 780 OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" ); 781 if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) ) 782 { 783 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject; 784 m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject); 785 m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex ); 786 } 787 } 788 } 789 790 if( aPartialName.isEmpty() ) 791 { 792 // how funny, an empty field name 793 if( i_rControl.getType() == PDFWriter::RadioButton ) 794 { 795 aPartialName = "RadioGroup"; 796 aPartialName += OString::number( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup ); 797 } 798 else 799 aPartialName = OString( "Widget" ); 800 } 801 802 if( ! m_aContext.AllowDuplicateFieldNames ) 803 { 804 std::unordered_map<OString, sal_Int32>::iterator it = m_aFieldNameMap.find( aFullName ); 805 806 if( it != m_aFieldNameMap.end() ) // not unique 807 { 808 std::unordered_map< OString, sal_Int32 >::const_iterator check_it; 809 OString aTry; 810 sal_Int32 nTry = 2; 811 do 812 { 813 OStringBuffer aUnique( aFullName.getLength() + 16 ); 814 aUnique.append( aFullName ); 815 aUnique.append( '_' ); 816 aUnique.append( nTry++ ); 817 aTry = aUnique.makeStringAndClear(); 818 check_it = m_aFieldNameMap.find( aTry ); 819 } while( check_it != m_aFieldNameMap.end() ); 820 aFullName = aTry; 821 m_aFieldNameMap[ aFullName ] = i_nWidgetIndex; 822 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 ); 823 } 824 else 825 m_aFieldNameMap[ aFullName ] = i_nWidgetIndex; 826 } 827 828 // finally 829 m_aWidgets[i_nWidgetIndex].m_aName = aPartialName; 830 } 831 832 static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer ) 833 { 834 if( nValue < 0 ) 835 { 836 rBuffer.append( '-' ); 837 nValue = -nValue; 838 } 839 const sal_Int32 nFactor = 10; 840 const sal_Int32 nInt = nValue / nFactor; 841 rBuffer.append( nInt ); 842 sal_Int32 nDecimal = nValue % nFactor; 843 if (nDecimal) 844 { 845 rBuffer.append('.'); 846 rBuffer.append(nDecimal); 847 } 848 } 849 850 // appends a double. PDF does not accept exponential format, only fixed point 851 static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 ) 852 { 853 bool bNeg = false; 854 if( fValue < 0.0 ) 855 { 856 bNeg = true; 857 fValue=-fValue; 858 } 859 860 sal_Int64 nInt = (sal_Int64)fValue; 861 fValue -= (double)nInt; 862 // optimizing hardware may lead to a value of 1.0 after the subtraction 863 if( rtl::math::approxEqual(fValue, 1.0) || log10( 1.0-fValue ) <= -nPrecision ) 864 { 865 nInt++; 866 fValue = 0.0; 867 } 868 sal_Int64 nFrac = 0; 869 if( fValue ) 870 { 871 fValue *= pow( 10.0, (double)nPrecision ); 872 nFrac = (sal_Int64)fValue; 873 } 874 if( bNeg && ( nInt || nFrac ) ) 875 rBuffer.append( '-' ); 876 rBuffer.append( nInt ); 877 if( nFrac ) 878 { 879 int i; 880 rBuffer.append( '.' ); 881 sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5); 882 for ( i = 0; ( i < nPrecision ) && nFrac; i++ ) 883 { 884 sal_Int64 nNumb = nFrac / nBound; 885 nFrac -= nNumb * nBound; 886 rBuffer.append( nNumb ); 887 nBound /= 10; 888 } 889 } 890 } 891 892 static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey ) 893 { 894 895 if( rColor != Color( COL_TRANSPARENT ) ) 896 { 897 if( bConvertToGrey ) 898 { 899 sal_uInt8 cByte = rColor.GetLuminance(); 900 appendDouble( (double)cByte / 255.0, rBuffer ); 901 } 902 else 903 { 904 appendDouble( (double)rColor.GetRed() / 255.0, rBuffer ); 905 rBuffer.append( ' ' ); 906 appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer ); 907 rBuffer.append( ' ' ); 908 appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer ); 909 } 910 } 911 } 912 913 void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer ) 914 { 915 if( rColor != Color( COL_TRANSPARENT ) ) 916 { 917 bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale; 918 appendColor( rColor, rBuffer, bGrey ); 919 rBuffer.append( bGrey ? " G" : " RG" ); 920 } 921 } 922 923 void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer ) 924 { 925 if( rColor != Color( COL_TRANSPARENT ) ) 926 { 927 bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale; 928 appendColor( rColor, rBuffer, bGrey ); 929 rBuffer.append( bGrey ? " g" : " rg" ); 930 } 931 } 932 933 // matrix helper class 934 // TODO: use basegfx matrix class instead or derive from it 935 namespace vcl // TODO: use anonymous namespace to keep this class local 936 { 937 /* for sparse matrices of the form (2D linear transformations) 938 * f[0] f[1] 0 939 * f[2] f[3] 0 940 * f[4] f[5] 1 941 */ 942 class Matrix3 943 { 944 double f[6]; 945 946 void set( const double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; } 947 public: 948 Matrix3(); 949 950 void skew( double alpha, double beta ); 951 void scale( double sx, double sy ); 952 void rotate( double angle ); 953 void translate( double tx, double ty ); 954 void invert(); 955 956 void append( PDFWriterImpl::PDFPage const & rPage, OStringBuffer& rBuffer ); 957 958 Point transform( const Point& rPoint ) const; 959 }; 960 } 961 962 Matrix3::Matrix3() 963 { 964 // initialize to unity 965 f[0] = 1.0; 966 f[1] = 0.0; 967 f[2] = 0.0; 968 f[3] = 1.0; 969 f[4] = 0.0; 970 f[5] = 0.0; 971 } 972 973 Point Matrix3::transform( const Point& rOrig ) const 974 { 975 double x = (double)rOrig.X(), y = (double)rOrig.Y(); 976 return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) ); 977 } 978 979 void Matrix3::skew( double alpha, double beta ) 980 { 981 double fn[6]; 982 double tb = tan( beta ); 983 fn[0] = f[0] + f[2]*tb; 984 fn[1] = f[1]; 985 fn[2] = f[2] + f[3]*tb; 986 fn[3] = f[3]; 987 fn[4] = f[4] + f[5]*tb; 988 fn[5] = f[5]; 989 if( alpha != 0.0 ) 990 { 991 double ta = tan( alpha ); 992 fn[1] += f[0]*ta; 993 fn[3] += f[2]*ta; 994 fn[5] += f[4]*ta; 995 } 996 set( fn ); 997 } 998 999 void Matrix3::scale( double sx, double sy ) 1000 { 1001 double fn[6]; 1002 fn[0] = sx*f[0]; 1003 fn[1] = sy*f[1]; 1004 fn[2] = sx*f[2]; 1005 fn[3] = sy*f[3]; 1006 fn[4] = sx*f[4]; 1007 fn[5] = sy*f[5]; 1008 set( fn ); 1009 } 1010 1011 void Matrix3::rotate( double angle ) 1012 { 1013 double fn[6]; 1014 double fSin = sin(angle); 1015 double fCos = cos(angle); 1016 fn[0] = f[0]*fCos - f[1]*fSin; 1017 fn[1] = f[0]*fSin + f[1]*fCos; 1018 fn[2] = f[2]*fCos - f[3]*fSin; 1019 fn[3] = f[2]*fSin + f[3]*fCos; 1020 fn[4] = f[4]*fCos - f[5]*fSin; 1021 fn[5] = f[4]*fSin + f[5]*fCos; 1022 set( fn ); 1023 } 1024 1025 void Matrix3::translate( double tx, double ty ) 1026 { 1027 f[4] += tx; 1028 f[5] += ty; 1029 } 1030 1031 void Matrix3::invert() 1032 { 1033 // short circuit trivial cases 1034 if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 ) 1035 { 1036 f[4] = -f[4]; 1037 f[5] = -f[5]; 1038 return; 1039 } 1040 1041 // check determinant 1042 const double fDet = f[0]*f[3]-f[1]*f[2]; 1043 if( fDet == 0.0 ) 1044 return; 1045 1046 // invert the matrix 1047 double fn[6]; 1048 fn[0] = +f[3] / fDet; 1049 fn[1] = -f[1] / fDet; 1050 fn[2] = -f[2] / fDet; 1051 fn[3] = +f[0] / fDet; 1052 1053 // apply inversion to translation 1054 fn[4] = -(f[4]*fn[0] + f[5]*fn[2]); 1055 fn[5] = -(f[4]*fn[1] + f[5]*fn[3]); 1056 1057 set( fn ); 1058 } 1059 1060 void Matrix3::append( PDFWriterImpl::PDFPage const & rPage, OStringBuffer& rBuffer ) 1061 { 1062 appendDouble( f[0], rBuffer ); 1063 rBuffer.append( ' ' ); 1064 appendDouble( f[1], rBuffer ); 1065 rBuffer.append( ' ' ); 1066 appendDouble( f[2], rBuffer ); 1067 rBuffer.append( ' ' ); 1068 appendDouble( f[3], rBuffer ); 1069 rBuffer.append( ' ' ); 1070 rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer ); 1071 } 1072 1073 static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList ) 1074 { 1075 if( rList.empty() ) 1076 return; 1077 rBuf.append( '/' ); 1078 rBuf.append( pPrefix ); 1079 rBuf.append( "<<" ); 1080 int ni = 0; 1081 for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it ) 1082 { 1083 if( !it->first.isEmpty() && it->second > 0 ) 1084 { 1085 rBuf.append( '/' ); 1086 rBuf.append( it->first ); 1087 rBuf.append( ' ' ); 1088 rBuf.append( it->second ); 1089 rBuf.append( " 0 R" ); 1090 if( ((++ni) & 7) == 0 ) 1091 rBuf.append( '\n' ); 1092 } 1093 } 1094 rBuf.append( ">>\n" ); 1095 } 1096 1097 void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject ) 1098 { 1099 rBuf.append( "<</Font " ); 1100 rBuf.append( nFontDictObject ); 1101 rBuf.append( " 0 R\n" ); 1102 appendResourceMap( rBuf, "XObject", m_aXObjects ); 1103 appendResourceMap( rBuf, "ExtGState", m_aExtGStates ); 1104 appendResourceMap( rBuf, "Shading", m_aShadings ); 1105 appendResourceMap( rBuf, "Pattern", m_aPatterns ); 1106 rBuf.append( "/ProcSet[/PDF/Text" ); 1107 if( !m_aXObjects.empty() ) 1108 rBuf.append( "/ImageC/ImageI/ImageB" ); 1109 rBuf.append( "]\n>>\n" ); 1110 }; 1111 1112 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, double nPageWidth, double nPageHeight, PDFWriter::Orientation eOrientation ) 1113 : 1114 m_pWriter( pWriter ), 1115 m_nPageWidth( nPageWidth ), 1116 m_nPageHeight( nPageHeight ), 1117 m_eOrientation( eOrientation ), 1118 m_nPageObject( 0 ), // invalid object number 1119 m_nStreamLengthObject( 0 ), 1120 m_nBeginStreamPos( 0 ), 1121 m_eTransition( PDFWriter::PageTransition::Regular ), 1122 m_nTransTime( 0 ), 1123 m_nDuration( 0 ) 1124 { 1125 // object ref must be only ever updated in emit() 1126 m_nPageObject = m_pWriter->createObject(); 1127 } 1128 1129 PDFWriterImpl::PDFPage::~PDFPage() 1130 { 1131 } 1132 1133 void PDFWriterImpl::PDFPage::beginStream() 1134 { 1135 if (g_bDebugDisableCompression) 1136 { 1137 m_pWriter->emitComment("PDFWriterImpl::PDFPage::beginStream, +"); 1138 } 1139 m_aStreamObjects.push_back(m_pWriter->createObject()); 1140 if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) ) 1141 return; 1142 1143 m_nStreamLengthObject = m_pWriter->createObject(); 1144 // write content stream header 1145 OStringBuffer aLine; 1146 aLine.append( m_aStreamObjects.back() ); 1147 aLine.append( " 0 obj\n<</Length " ); 1148 aLine.append( m_nStreamLengthObject ); 1149 aLine.append( " 0 R" ); 1150 if (!g_bDebugDisableCompression) 1151 aLine.append( "/Filter/FlateDecode" ); 1152 aLine.append( ">>\nstream\n" ); 1153 if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) ) 1154 return; 1155 if (osl::File::E_None != m_pWriter->m_aFile.getPos(m_nBeginStreamPos)) 1156 { 1157 m_pWriter->m_aFile.close(); 1158 m_pWriter->m_bOpen = false; 1159 } 1160 if (!g_bDebugDisableCompression) 1161 m_pWriter->beginCompression(); 1162 m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() ); 1163 } 1164 1165 void PDFWriterImpl::PDFPage::endStream() 1166 { 1167 if (!g_bDebugDisableCompression) 1168 m_pWriter->endCompression(); 1169 sal_uInt64 nEndStreamPos; 1170 if (osl::File::E_None != m_pWriter->m_aFile.getPos(nEndStreamPos)) 1171 { 1172 m_pWriter->m_aFile.close(); 1173 m_pWriter->m_bOpen = false; 1174 return; 1175 } 1176 m_pWriter->disableStreamEncryption(); 1177 if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) 1178 return; 1179 // emit stream length object 1180 if( ! m_pWriter->updateObject( m_nStreamLengthObject ) ) 1181 return; 1182 OStringBuffer aLine; 1183 aLine.append( m_nStreamLengthObject ); 1184 aLine.append( " 0 obj\n" ); 1185 aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) ); 1186 aLine.append( "\nendobj\n\n" ); 1187 m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ); 1188 } 1189 1190 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject ) 1191 { 1192 // emit page object 1193 if( ! m_pWriter->updateObject( m_nPageObject ) ) 1194 return false; 1195 OStringBuffer aLine; 1196 1197 aLine.append( m_nPageObject ); 1198 aLine.append( " 0 obj\n" 1199 "<</Type/Page/Parent " ); 1200 aLine.append( nParentObject ); 1201 aLine.append( " 0 R" ); 1202 aLine.append( "/Resources " ); 1203 aLine.append( m_pWriter->getResourceDictObj() ); 1204 aLine.append( " 0 R" ); 1205 if( m_nPageWidth && m_nPageHeight ) 1206 { 1207 aLine.append( "/MediaBox[0 0 " ); 1208 aLine.append( m_nPageWidth ); 1209 aLine.append( ' ' ); 1210 aLine.append( m_nPageHeight ); 1211 aLine.append( "]" ); 1212 } 1213 switch( m_eOrientation ) 1214 { 1215 case PDFWriter::Orientation::Portrait: aLine.append( "/Rotate 0\n" );break; 1216 case PDFWriter::Orientation::Inherit: break; 1217 } 1218 int nAnnots = m_aAnnotations.size(); 1219 if( nAnnots > 0 ) 1220 { 1221 aLine.append( "/Annots[\n" ); 1222 for( int i = 0; i < nAnnots; i++ ) 1223 { 1224 aLine.append( m_aAnnotations[i] ); 1225 aLine.append( " 0 R" ); 1226 aLine.append( ((i+1)%15) ? " " : "\n" ); 1227 } 1228 aLine.append( "]\n" ); 1229 } 1230 if( m_aMCIDParents.size() > 0 ) 1231 { 1232 OStringBuffer aStructParents( 1024 ); 1233 aStructParents.append( "[ " ); 1234 int nParents = m_aMCIDParents.size(); 1235 for( int i = 0; i < nParents; i++ ) 1236 { 1237 aStructParents.append( m_aMCIDParents[i] ); 1238 aStructParents.append( " 0 R" ); 1239 aStructParents.append( ((i%10) == 9) ? "\n" : " " ); 1240 } 1241 aStructParents.append( "]" ); 1242 m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() ); 1243 1244 aLine.append( "/StructParents " ); 1245 aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) ); 1246 aLine.append( "\n" ); 1247 } 1248 if( m_nDuration > 0 ) 1249 { 1250 aLine.append( "/Dur " ); 1251 aLine.append( (sal_Int32)m_nDuration ); 1252 aLine.append( "\n" ); 1253 } 1254 if( m_eTransition != PDFWriter::PageTransition::Regular && m_nTransTime > 0 ) 1255 { 1256 // transition duration 1257 aLine.append( "/Trans<</D " ); 1258 appendDouble( (double)m_nTransTime/1000.0, aLine, 3 ); 1259 aLine.append( "\n" ); 1260 const char *pStyle = nullptr, *pDm = nullptr, *pM = nullptr, *pDi = nullptr; 1261 switch( m_eTransition ) 1262 { 1263 case PDFWriter::PageTransition::SplitHorizontalInward: 1264 pStyle = "Split"; pDm = "H"; pM = "I"; break; 1265 case PDFWriter::PageTransition::SplitHorizontalOutward: 1266 pStyle = "Split"; pDm = "H"; pM = "O"; break; 1267 case PDFWriter::PageTransition::SplitVerticalInward: 1268 pStyle = "Split"; pDm = "V"; pM = "I"; break; 1269 case PDFWriter::PageTransition::SplitVerticalOutward: 1270 pStyle = "Split"; pDm = "V"; pM = "O"; break; 1271 case PDFWriter::PageTransition::BlindsHorizontal: 1272 pStyle = "Blinds"; pDm = "H"; break; 1273 case PDFWriter::PageTransition::BlindsVertical: 1274 pStyle = "Blinds"; pDm = "V"; break; 1275 case PDFWriter::PageTransition::BoxInward: 1276 pStyle = "Box"; pM = "I"; break; 1277 case PDFWriter::PageTransition::BoxOutward: 1278 pStyle = "Box"; pM = "O"; break; 1279 case PDFWriter::PageTransition::WipeLeftToRight: 1280 pStyle = "Wipe"; pDi = "0"; break; 1281 case PDFWriter::PageTransition::WipeBottomToTop: 1282 pStyle = "Wipe"; pDi = "90"; break; 1283 case PDFWriter::PageTransition::WipeRightToLeft: 1284 pStyle = "Wipe"; pDi = "180"; break; 1285 case PDFWriter::PageTransition::WipeTopToBottom: 1286 pStyle = "Wipe"; pDi = "270"; break; 1287 case PDFWriter::PageTransition::Dissolve: 1288 pStyle = "Dissolve"; break; 1289 case PDFWriter::PageTransition::Regular: 1290 break; 1291 } 1292 // transition style 1293 if( pStyle ) 1294 { 1295 aLine.append( "/S/" ); 1296 aLine.append( pStyle ); 1297 aLine.append( "\n" ); 1298 } 1299 if( pDm ) 1300 { 1301 aLine.append( "/Dm/" ); 1302 aLine.append( pDm ); 1303 aLine.append( "\n" ); 1304 } 1305 if( pM ) 1306 { 1307 aLine.append( "/M/" ); 1308 aLine.append( pM ); 1309 aLine.append( "\n" ); 1310 } 1311 if( pDi ) 1312 { 1313 aLine.append( "/Di " ); 1314 aLine.append( pDi ); 1315 aLine.append( "\n" ); 1316 } 1317 aLine.append( ">>\n" ); 1318 } 1319 if( m_pWriter->getVersion() > PDFWriter::PDFVersion::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 ) 1320 { 1321 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" ); 1322 } 1323 aLine.append( "/Contents" ); 1324 unsigned int nStreamObjects = m_aStreamObjects.size(); 1325 if( nStreamObjects > 1 ) 1326 aLine.append( '[' ); 1327 for(sal_Int32 i : m_aStreamObjects) 1328 { 1329 aLine.append( ' ' ); 1330 aLine.append( i ); 1331 aLine.append( " 0 R" ); 1332 } 1333 if( nStreamObjects > 1 ) 1334 aLine.append( ']' ); 1335 aLine.append( ">>\nendobj\n\n" ); 1336 return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ); 1337 } 1338 1339 namespace vcl 1340 { 1341 template < class GEOMETRY > 1342 GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject ) 1343 { 1344 GEOMETRY aPoint; 1345 if ( MapUnit::MapPixel == _rSource.GetMapUnit() ) 1346 { 1347 aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest ); 1348 } 1349 else 1350 { 1351 aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest ); 1352 } 1353 return aPoint; 1354 } 1355 } 1356 1357 void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer ) const 1358 { 1359 Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1360 m_pWriter->m_aMapMode, 1361 m_pWriter->getReferenceDevice(), 1362 rPoint ) ); 1363 1364 sal_Int32 nValue = aPoint.X(); 1365 1366 appendFixedInt( nValue, rBuffer ); 1367 1368 rBuffer.append( ' ' ); 1369 1370 nValue = pointToPixel(getHeight()) - aPoint.Y(); 1371 1372 appendFixedInt( nValue, rBuffer ); 1373 } 1374 1375 void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const 1376 { 1377 double fValue = pixelToPoint(rPoint.getX()); 1378 1379 appendDouble( fValue, rBuffer, nLog10Divisor ); 1380 rBuffer.append( ' ' ); 1381 fValue = getHeight() - pixelToPoint(rPoint.getY()); 1382 appendDouble( fValue, rBuffer, nLog10Divisor ); 1383 } 1384 1385 void PDFWriterImpl::PDFPage::appendRect( const tools::Rectangle& rRect, OStringBuffer& rBuffer ) const 1386 { 1387 appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer ); 1388 rBuffer.append( ' ' ); 1389 appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false ); 1390 rBuffer.append( ' ' ); 1391 appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer ); 1392 rBuffer.append( " re" ); 1393 } 1394 1395 void PDFWriterImpl::PDFPage::convertRect( tools::Rectangle& rRect ) const 1396 { 1397 Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1398 m_pWriter->m_aMapMode, 1399 m_pWriter->getReferenceDevice(), 1400 rRect.BottomLeft() + Point( 0, 1 ) 1401 ); 1402 Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1403 m_pWriter->m_aMapMode, 1404 m_pWriter->getReferenceDevice(), 1405 rRect.GetSize() ); 1406 rRect.Left() = aLL.X(); 1407 rRect.Right() = aLL.X() + aSize.Width(); 1408 rRect.Top() = pointToPixel(getHeight()) - aLL.Y(); 1409 rRect.Bottom() = rRect.Top() + aSize.Height(); 1410 } 1411 1412 void PDFWriterImpl::PDFPage::appendPolygon( const tools::Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const 1413 { 1414 sal_uInt16 nPoints = rPoly.GetSize(); 1415 /* 1416 * #108582# applications do weird things 1417 */ 1418 sal_uInt32 nBufLen = rBuffer.getLength(); 1419 if( nPoints > 0 ) 1420 { 1421 const PolyFlags* pFlagArray = rPoly.GetConstFlagAry(); 1422 appendPoint( rPoly[0], rBuffer ); 1423 rBuffer.append( " m\n" ); 1424 for( sal_uInt16 i = 1; i < nPoints; i++ ) 1425 { 1426 if( pFlagArray && pFlagArray[i] == PolyFlags::Control && nPoints-i > 2 ) 1427 { 1428 // bezier 1429 SAL_WARN_IF( pFlagArray[i+1] != PolyFlags::Control || pFlagArray[i+2] == PolyFlags::Control, "vcl.pdfwriter", "unexpected sequence of control points" ); 1430 appendPoint( rPoly[i], rBuffer ); 1431 rBuffer.append( " " ); 1432 appendPoint( rPoly[i+1], rBuffer ); 1433 rBuffer.append( " " ); 1434 appendPoint( rPoly[i+2], rBuffer ); 1435 rBuffer.append( " c" ); 1436 i += 2; // add additionally consumed points 1437 } 1438 else 1439 { 1440 // line 1441 appendPoint( rPoly[i], rBuffer ); 1442 rBuffer.append( " l" ); 1443 } 1444 if( (rBuffer.getLength() - nBufLen) > 65 ) 1445 { 1446 rBuffer.append( "\n" ); 1447 nBufLen = rBuffer.getLength(); 1448 } 1449 else 1450 rBuffer.append( " " ); 1451 } 1452 if( bClose ) 1453 rBuffer.append( "h\n" ); 1454 } 1455 } 1456 1457 void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer ) const 1458 { 1459 basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1460 m_pWriter->m_aMapMode, 1461 m_pWriter->getReferenceDevice(), 1462 rPoly ) ); 1463 1464 if( basegfx::utils::isRectangle( aPoly ) ) 1465 { 1466 basegfx::B2DRange aRange( aPoly.getB2DRange() ); 1467 basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() ); 1468 appendPixelPoint( aBL, rBuffer ); 1469 rBuffer.append( ' ' ); 1470 appendMappedLength( aRange.getWidth(), rBuffer, false, nLog10Divisor ); 1471 rBuffer.append( ' ' ); 1472 appendMappedLength( aRange.getHeight(), rBuffer, true, nLog10Divisor ); 1473 rBuffer.append( " re\n" ); 1474 return; 1475 } 1476 sal_uInt32 nPoints = aPoly.count(); 1477 if( nPoints > 0 ) 1478 { 1479 sal_uInt32 nBufLen = rBuffer.getLength(); 1480 basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) ); 1481 appendPixelPoint( aLastPoint, rBuffer ); 1482 rBuffer.append( " m\n" ); 1483 for( sal_uInt32 i = 1; i <= nPoints; i++ ) 1484 { 1485 if( i != nPoints || aPoly.isClosed() ) 1486 { 1487 sal_uInt32 nCurPoint = i % nPoints; 1488 sal_uInt32 nLastPoint = i-1; 1489 basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) ); 1490 if( aPoly.isNextControlPointUsed( nLastPoint ) && 1491 aPoly.isPrevControlPointUsed( nCurPoint ) ) 1492 { 1493 appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer ); 1494 rBuffer.append( ' ' ); 1495 appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer ); 1496 rBuffer.append( ' ' ); 1497 appendPixelPoint( aPoint, rBuffer ); 1498 rBuffer.append( " c" ); 1499 } 1500 else if( aPoly.isNextControlPointUsed( nLastPoint ) ) 1501 { 1502 appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer ); 1503 rBuffer.append( ' ' ); 1504 appendPixelPoint( aPoint, rBuffer ); 1505 rBuffer.append( " y" ); 1506 } 1507 else if( aPoly.isPrevControlPointUsed( nCurPoint ) ) 1508 { 1509 appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer ); 1510 rBuffer.append( ' ' ); 1511 appendPixelPoint( aPoint, rBuffer ); 1512 rBuffer.append( " v" ); 1513 } 1514 else 1515 { 1516 appendPixelPoint( aPoint, rBuffer ); 1517 rBuffer.append( " l" ); 1518 } 1519 if( (rBuffer.getLength() - nBufLen) > 65 ) 1520 { 1521 rBuffer.append( "\n" ); 1522 nBufLen = rBuffer.getLength(); 1523 } 1524 else 1525 rBuffer.append( " " ); 1526 } 1527 } 1528 rBuffer.append( "h\n" ); 1529 } 1530 } 1531 1532 void PDFWriterImpl::PDFPage::appendPolyPolygon( const tools::PolyPolygon& rPolyPoly, OStringBuffer& rBuffer ) const 1533 { 1534 sal_uInt16 nPolygons = rPolyPoly.Count(); 1535 for( sal_uInt16 n = 0; n < nPolygons; n++ ) 1536 appendPolygon( rPolyPoly[n], rBuffer ); 1537 } 1538 1539 void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer ) const 1540 { 1541 sal_uInt32 nPolygons = rPolyPoly.count(); 1542 for( sal_uInt32 n = 0; n < nPolygons; n++ ) 1543 appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer ); 1544 } 1545 1546 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const 1547 { 1548 sal_Int32 nValue = nLength; 1549 if ( nLength < 0 ) 1550 { 1551 rBuffer.append( '-' ); 1552 nValue = -nLength; 1553 } 1554 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1555 m_pWriter->m_aMapMode, 1556 m_pWriter->getReferenceDevice(), 1557 Size( nValue, nValue ) ) ); 1558 nValue = bVertical ? aSize.Height() : aSize.Width(); 1559 if( pOutLength ) 1560 *pOutLength = ((nLength < 0 ) ? -nValue : nValue); 1561 1562 appendFixedInt( nValue, rBuffer ); 1563 } 1564 1565 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32 nPrecision ) const 1566 { 1567 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1568 m_pWriter->m_aMapMode, 1569 m_pWriter->getReferenceDevice(), 1570 Size( 1000, 1000 ) ) ); 1571 fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0); 1572 appendDouble( fLength, rBuffer, nPrecision ); 1573 } 1574 1575 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const 1576 { 1577 if(LineStyle::Dash == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen()) 1578 { 1579 // dashed and non-degraded case, check for implementation limits of dash array 1580 // in PDF reader apps (e.g. acroread) 1581 if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10) 1582 { 1583 return false; 1584 } 1585 } 1586 1587 if(basegfx::B2DLineJoin::NONE != rInfo.GetLineJoin()) 1588 { 1589 // LineJoin used, ExtLineInfo required 1590 return false; 1591 } 1592 1593 if(css::drawing::LineCap_BUTT != rInfo.GetLineCap()) 1594 { 1595 // LineCap used, ExtLineInfo required 1596 return false; 1597 } 1598 1599 if( rInfo.GetStyle() == LineStyle::Dash ) 1600 { 1601 rBuffer.append( "[ " ); 1602 if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case 1603 { 1604 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer ); 1605 rBuffer.append( ' ' ); 1606 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); 1607 rBuffer.append( ' ' ); 1608 } 1609 else 1610 { 1611 for( int n = 0; n < rInfo.GetDashCount(); n++ ) 1612 { 1613 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer ); 1614 rBuffer.append( ' ' ); 1615 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); 1616 rBuffer.append( ' ' ); 1617 } 1618 for( int m = 0; m < rInfo.GetDotCount(); m++ ) 1619 { 1620 appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer ); 1621 rBuffer.append( ' ' ); 1622 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); 1623 rBuffer.append( ' ' ); 1624 } 1625 } 1626 rBuffer.append( "] 0 d\n" ); 1627 } 1628 1629 if( rInfo.GetWidth() > 1 ) 1630 { 1631 appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer ); 1632 rBuffer.append( " w\n" ); 1633 } 1634 else if( rInfo.GetWidth() == 0 ) 1635 { 1636 // "pixel" line 1637 appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->GetDPIX()), rBuffer ); 1638 rBuffer.append( " w\n" ); 1639 } 1640 1641 return true; 1642 } 1643 1644 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const 1645 { 1646 if( nWidth <= 0 ) 1647 return; 1648 if( nDelta < 1 ) 1649 nDelta = 1; 1650 1651 rBuffer.append( "0 " ); 1652 appendMappedLength( nY, rBuffer ); 1653 rBuffer.append( " m\n" ); 1654 for( sal_Int32 n = 0; n < nWidth; ) 1655 { 1656 n += nDelta; 1657 appendMappedLength( n, rBuffer, false ); 1658 rBuffer.append( ' ' ); 1659 appendMappedLength( nDelta+nY, rBuffer ); 1660 rBuffer.append( ' ' ); 1661 n += nDelta; 1662 appendMappedLength( n, rBuffer, false ); 1663 rBuffer.append( ' ' ); 1664 appendMappedLength( nY, rBuffer ); 1665 rBuffer.append( " v " ); 1666 if( n < nWidth ) 1667 { 1668 n += nDelta; 1669 appendMappedLength( n, rBuffer, false ); 1670 rBuffer.append( ' ' ); 1671 appendMappedLength( nY-nDelta, rBuffer ); 1672 rBuffer.append( ' ' ); 1673 n += nDelta; 1674 appendMappedLength( n, rBuffer, false ); 1675 rBuffer.append( ' ' ); 1676 appendMappedLength( nY, rBuffer ); 1677 rBuffer.append( " v\n" ); 1678 } 1679 } 1680 rBuffer.append( "S\n" ); 1681 } 1682 1683 PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, 1684 const css::uno::Reference< css::beans::XMaterialHolder >& xEnc, 1685 PDFWriter& i_rOuterFace) 1686 : 1687 m_pReferenceDevice( nullptr ), 1688 m_aMapMode( MapUnit::MapPoint, Point(), Fraction( 1, pointToPixel(1) ), Fraction( 1, pointToPixel(1) ) ), 1689 m_nCurrentStructElement( 0 ), 1690 m_bEmitStructure( true ), 1691 m_nNextFID( 1 ), 1692 m_nInheritedPageWidth( 595 ), // default A4 1693 m_nInheritedPageHeight( 842 ), // default A4 1694 m_nCurrentPage( -1 ), 1695 m_nCatalogObject(0), 1696 m_nSignatureObject( -1 ), 1697 m_nSignatureContentOffset( 0 ), 1698 m_nSignatureLastByteRangeNoOffset( 0 ), 1699 m_nResourceDict( -1 ), 1700 m_nFontDictObject( -1 ), 1701 m_aContext(rContext), 1702 m_aFile(m_aContext.URL), 1703 m_bOpen(false), 1704 m_aDocDigest( rtl_digest_createMD5() ), 1705 m_aCipher( nullptr ), 1706 m_aDigest( nullptr ), 1707 m_nKeyLength(0), 1708 m_nRC4KeyLength(0), 1709 m_bEncryptThisStream( false ), 1710 m_nAccessPermissions(0), 1711 m_pEncryptionBuffer( nullptr ), 1712 m_nEncryptionBufferSize( 0 ), 1713 m_bIsPDF_A1( false ), 1714 m_rOuterFace( i_rOuterFace ) 1715 { 1716 #ifdef DO_TEST_PDF 1717 static bool bOnce = true; 1718 if( bOnce ) 1719 { 1720 bOnce = false; 1721 doTestCode(); 1722 } 1723 #endif 1724 m_aStructure.emplace_back( ); 1725 m_aStructure[0].m_nOwnElement = 0; 1726 m_aStructure[0].m_nParentElement = 0; 1727 1728 Font aFont; 1729 aFont.SetFamilyName( "Times" ); 1730 aFont.SetFontSize( Size( 0, 12 ) ); 1731 1732 GraphicsState aState; 1733 aState.m_aMapMode = m_aMapMode; 1734 aState.m_aFont = aFont; 1735 m_aGraphicsStack.push_front( aState ); 1736 1737 osl::File::RC aError = m_aFile.open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create); 1738 if (aError != osl::File::E_None) 1739 { 1740 if (aError == osl::File::E_EXIST) 1741 { 1742 aError = m_aFile.open(osl_File_OpenFlag_Write); 1743 if (aError == osl::File::E_None) 1744 aError = m_aFile.setSize(0); 1745 } 1746 } 1747 if (aError != osl::File::E_None) 1748 return; 1749 1750 m_bOpen = true; 1751 1752 // setup DocInfo 1753 setupDocInfo(); 1754 1755 /* prepare the cypher engine, can be done in CTOR, free in DTOR */ 1756 m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream ); 1757 m_aDigest = rtl_digest_createMD5(); 1758 1759 /* the size of the Codec default maximum */ 1760 /* is this 0x4000 required to be the same as MAX_SIGNATURE_CONTENT_LENGTH or just coincidentally the same at the moment? */ 1761 if (!checkEncryptionBufferSize(0x4000)) 1762 { 1763 m_aFile.close(); 1764 m_bOpen = false; 1765 return; 1766 } 1767 1768 if( xEnc.is() ) 1769 prepareEncryption( xEnc ); 1770 1771 if( m_aContext.Encryption.Encrypt() ) 1772 { 1773 // sanity check 1774 if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE || 1775 m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE || 1776 m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH 1777 ) 1778 { 1779 // the field lengths are invalid ? This was not setup by initEncryption. 1780 // do not encrypt after all 1781 m_aContext.Encryption.OValue.clear(); 1782 m_aContext.Encryption.UValue.clear(); 1783 OSL_ENSURE( false, "encryption data failed sanity check, encryption disabled" ); 1784 } 1785 else // setup key lengths 1786 m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength ); 1787 } 1788 1789 // write header 1790 OStringBuffer aBuffer( 20 ); 1791 aBuffer.append( "%PDF-" ); 1792 switch( m_aContext.Version ) 1793 { 1794 case PDFWriter::PDFVersion::PDF_1_2: aBuffer.append( "1.2" );break; 1795 case PDFWriter::PDFVersion::PDF_1_3: aBuffer.append( "1.3" );break; 1796 case PDFWriter::PDFVersion::PDF_A_1: 1797 default: 1798 case PDFWriter::PDFVersion::PDF_1_4: aBuffer.append( "1.4" );break; 1799 case PDFWriter::PDFVersion::PDF_1_5: aBuffer.append( "1.5" );break; 1800 } 1801 // append something binary as comment (suggested in PDF Reference) 1802 aBuffer.append( "\n%\303\244\303\274\303\266\303\237\n" ); 1803 if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) ) 1804 { 1805 m_aFile.close(); 1806 m_bOpen = false; 1807 return; 1808 } 1809 1810 // insert outline root 1811 m_aOutline.emplace_back( ); 1812 1813 m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDFVersion::PDF_A_1); 1814 if( m_bIsPDF_A1 ) 1815 m_aContext.Version = PDFWriter::PDFVersion::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour 1816 } 1817 1818 PDFWriterImpl::~PDFWriterImpl() 1819 { 1820 if( m_aDocDigest ) 1821 rtl_digest_destroyMD5( m_aDocDigest ); 1822 m_pReferenceDevice.disposeAndClear(); 1823 1824 if( m_aCipher ) 1825 rtl_cipher_destroyARCFOUR( m_aCipher ); 1826 if( m_aDigest ) 1827 rtl_digest_destroyMD5( m_aDigest ); 1828 1829 rtl_freeMemory( m_pEncryptionBuffer ); 1830 } 1831 1832 void PDFWriterImpl::setupDocInfo() 1833 { 1834 std::vector< sal_uInt8 > aId; 1835 m_aCreationDateString = PDFWriter::GetDateTime(); 1836 computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString ); 1837 if( m_aContext.Encryption.DocumentIdentifier.empty() ) 1838 m_aContext.Encryption.DocumentIdentifier = aId; 1839 } 1840 1841 OString PDFWriter::GetDateTime() 1842 { 1843 OStringBuffer aRet; 1844 1845 TimeValue aTVal, aGMT; 1846 oslDateTime aDT; 1847 osl_getSystemTime(&aGMT); 1848 osl_getLocalTimeFromSystemTime(&aGMT, &aTVal); 1849 osl_getDateTimeFromTimeValue(&aTVal, &aDT); 1850 aRet.append("D:"); 1851 aRet.append((sal_Char)('0' + ((aDT.Year / 1000) % 10))); 1852 aRet.append((sal_Char)('0' + ((aDT.Year / 100) % 10))); 1853 aRet.append((sal_Char)('0' + ((aDT.Year / 10) % 10))); 1854 aRet.append((sal_Char)('0' + (aDT.Year % 10))); 1855 aRet.append((sal_Char)('0' + ((aDT.Month / 10) % 10))); 1856 aRet.append((sal_Char)('0' + (aDT.Month % 10))); 1857 aRet.append((sal_Char)('0' + ((aDT.Day / 10) % 10))); 1858 aRet.append((sal_Char)('0' + (aDT.Day % 10))); 1859 aRet.append((sal_Char)('0' + ((aDT.Hours / 10) % 10))); 1860 aRet.append((sal_Char)('0' + (aDT.Hours % 10))); 1861 aRet.append((sal_Char)('0' + ((aDT.Minutes / 10) % 10))); 1862 aRet.append((sal_Char)('0' + (aDT.Minutes % 10))); 1863 aRet.append((sal_Char)('0' + ((aDT.Seconds / 10) % 10))); 1864 aRet.append((sal_Char)('0' + (aDT.Seconds % 10))); 1865 1866 sal_uInt32 nDelta = 0; 1867 if (aGMT.Seconds > aTVal.Seconds) 1868 { 1869 aRet.append("-"); 1870 nDelta = aGMT.Seconds-aTVal.Seconds; 1871 } 1872 else if (aGMT.Seconds < aTVal.Seconds) 1873 { 1874 aRet.append("+"); 1875 nDelta = aTVal.Seconds-aGMT.Seconds; 1876 } 1877 else 1878 aRet.append("Z"); 1879 1880 if (nDelta) 1881 { 1882 aRet.append((sal_Char)('0' + ((nDelta / 36000) % 10))); 1883 aRet.append((sal_Char)('0' + ((nDelta / 3600) % 10))); 1884 aRet.append("'"); 1885 aRet.append((sal_Char)('0' + ((nDelta / 600) % 6))); 1886 aRet.append((sal_Char)('0' + ((nDelta / 60) % 10))); 1887 } 1888 aRet.append( "'" ); 1889 1890 return aRet.makeStringAndClear(); 1891 } 1892 1893 void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier, 1894 const vcl::PDFWriter::PDFDocInfo& i_rDocInfo, 1895 const OString& i_rCString1, 1896 OString& o_rCString2 1897 ) 1898 { 1899 o_rIdentifier.clear(); 1900 1901 //build the document id 1902 OString aInfoValuesOut; 1903 OStringBuffer aID( 1024 ); 1904 if( !i_rDocInfo.Title.isEmpty() ) 1905 PDFWriter::AppendUnicodeTextString(i_rDocInfo.Title, aID); 1906 if( !i_rDocInfo.Author.isEmpty() ) 1907 PDFWriter::AppendUnicodeTextString(i_rDocInfo.Author, aID); 1908 if( !i_rDocInfo.Subject.isEmpty() ) 1909 PDFWriter::AppendUnicodeTextString(i_rDocInfo.Subject, aID); 1910 if( !i_rDocInfo.Keywords.isEmpty() ) 1911 PDFWriter::AppendUnicodeTextString(i_rDocInfo.Keywords, aID); 1912 if( !i_rDocInfo.Creator.isEmpty() ) 1913 PDFWriter::AppendUnicodeTextString(i_rDocInfo.Creator, aID); 1914 if( !i_rDocInfo.Producer.isEmpty() ) 1915 PDFWriter::AppendUnicodeTextString(i_rDocInfo.Producer, aID); 1916 1917 TimeValue aTVal, aGMT; 1918 oslDateTime aDT; 1919 osl_getSystemTime( &aGMT ); 1920 osl_getLocalTimeFromSystemTime( &aGMT, &aTVal ); 1921 osl_getDateTimeFromTimeValue( &aTVal, &aDT ); 1922 OStringBuffer aCreationMetaDateString(64); 1923 1924 // i59651: we fill the Metadata date string as well, if PDF/A is requested 1925 // according to ISO 19005-1:2005 6.7.3 the date is corrected for 1926 // local time zone offset UTC only, whereas Acrobat 8 seems 1927 // to use the localtime notation only 1928 // according to a recommendation in XMP Specification (Jan 2004, page 75) 1929 // the Acrobat way seems the right approach 1930 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); 1931 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); 1932 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); 1933 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) ); 1934 aCreationMetaDateString.append( "-" ); 1935 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); 1936 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) ); 1937 aCreationMetaDateString.append( "-" ); 1938 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); 1939 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) ); 1940 aCreationMetaDateString.append( "T" ); 1941 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); 1942 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); 1943 aCreationMetaDateString.append( ":" ); 1944 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); 1945 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); 1946 aCreationMetaDateString.append( ":" ); 1947 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); 1948 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); 1949 1950 sal_uInt32 nDelta = 0; 1951 if( aGMT.Seconds > aTVal.Seconds ) 1952 { 1953 nDelta = aGMT.Seconds-aTVal.Seconds; 1954 aCreationMetaDateString.append( "-" ); 1955 } 1956 else if( aGMT.Seconds < aTVal.Seconds ) 1957 { 1958 nDelta = aTVal.Seconds-aGMT.Seconds; 1959 aCreationMetaDateString.append( "+" ); 1960 } 1961 else 1962 { 1963 aCreationMetaDateString.append( "Z" ); 1964 1965 } 1966 if( nDelta ) 1967 { 1968 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); 1969 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); 1970 aCreationMetaDateString.append( ":" ); 1971 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) ); 1972 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) ); 1973 } 1974 aID.append( i_rCString1.getStr(), i_rCString1.getLength() ); 1975 1976 aInfoValuesOut = aID.makeStringAndClear(); 1977 o_rCString2 = aCreationMetaDateString.makeStringAndClear(); 1978 1979 rtlDigest aDigest = rtl_digest_createMD5(); 1980 OSL_ENSURE( aDigest != nullptr, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" ); 1981 if( aDigest ) 1982 { 1983 rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) ); 1984 if( nError == rtl_Digest_E_None ) 1985 nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() ); 1986 if( nError == rtl_Digest_E_None ) 1987 { 1988 o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 ); 1989 //the binary form of the doc id is needed for encryption stuff 1990 rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 ); 1991 } 1992 rtl_digest_destroyMD5(aDigest); 1993 } 1994 } 1995 1996 /* i12626 methods */ 1997 /* 1998 check if the Unicode string must be encrypted or not, perform the requested task, 1999 append the string as unicode hex, encrypted if needed 2000 */ 2001 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer ) 2002 { 2003 rOutBuffer.append( "<" ); 2004 if( m_aContext.Encryption.Encrypt() ) 2005 { 2006 const sal_Unicode* pStr = rInString.getStr(); 2007 sal_Int32 nLen = rInString.getLength(); 2008 //prepare a unicode string, encrypt it 2009 if( checkEncryptionBufferSize( nLen*2 ) ) 2010 { 2011 enableStringEncryption( nInObjectNumber ); 2012 sal_uInt8 *pCopy = m_pEncryptionBuffer; 2013 sal_Int32 nChars = 2; 2014 *pCopy++ = 0xFE; 2015 *pCopy++ = 0xFF; 2016 // we need to prepare a byte stream from the unicode string buffer 2017 for( int i = 0; i < nLen; i++ ) 2018 { 2019 sal_Unicode aUnChar = pStr[i]; 2020 *pCopy++ = (sal_uInt8)( aUnChar >> 8 ); 2021 *pCopy++ = (sal_uInt8)( aUnChar & 255 ); 2022 nChars += 2; 2023 } 2024 //encrypt in place 2025 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars ); 2026 //now append, hexadecimal (appendHex), the encrypted result 2027 for(int i = 0; i < nChars; i++) 2028 appendHex( m_pEncryptionBuffer[i], rOutBuffer ); 2029 } 2030 } 2031 else 2032 PDFWriter::AppendUnicodeTextString(rInString, rOutBuffer); 2033 rOutBuffer.append( ">" ); 2034 } 2035 2036 inline void PDFWriterImpl::appendLiteralStringEncrypt( OStringBuffer const & rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer ) 2037 { 2038 rOutBuffer.append( "(" ); 2039 sal_Int32 nChars = rInString.getLength(); 2040 //check for encryption, if ok, encrypt the string, then convert with appndLiteralString 2041 if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) ) 2042 { 2043 //encrypt the string in a buffer, then append it 2044 enableStringEncryption( nInObjectNumber ); 2045 rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars ); 2046 appendLiteralString( reinterpret_cast<sal_Char*>(m_pEncryptionBuffer), nChars, rOutBuffer ); 2047 } 2048 else 2049 appendLiteralString( rInString.getStr(), nChars , rOutBuffer ); 2050 rOutBuffer.append( ")" ); 2051 } 2052 2053 inline void PDFWriterImpl::appendLiteralStringEncrypt( const OString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer ) 2054 { 2055 OStringBuffer aBufferString( rInString ); 2056 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer); 2057 } 2058 2059 void PDFWriterImpl::appendLiteralStringEncrypt( const OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc ) 2060 { 2061 OString aBufferString( OUStringToOString( rInString, nEnc ) ); 2062 sal_Int32 nLen = aBufferString.getLength(); 2063 OStringBuffer aBuf( nLen ); 2064 const sal_Char* pT = aBufferString.getStr(); 2065 2066 for( sal_Int32 i = 0; i < nLen; i++, pT++ ) 2067 { 2068 if( (*pT & 0x80) == 0 ) 2069 aBuf.append( *pT ); 2070 else 2071 { 2072 aBuf.append( '<' ); 2073 appendHex( *pT, aBuf ); 2074 aBuf.append( '>' ); 2075 } 2076 } 2077 aBufferString = aBuf.makeStringAndClear(); 2078 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer); 2079 } 2080 2081 /* end i12626 methods */ 2082 2083 void PDFWriterImpl::emitComment( const char* pComment ) 2084 { 2085 OStringBuffer aLine( 64 ); 2086 aLine.append( "% " ); 2087 aLine.append( pComment ); 2088 aLine.append( "\n" ); 2089 writeBuffer( aLine.getStr(), aLine.getLength() ); 2090 } 2091 2092 bool PDFWriterImpl::compressStream( SvMemoryStream* pStream ) 2093 { 2094 if (!g_bDebugDisableCompression) 2095 { 2096 pStream->Seek( STREAM_SEEK_TO_END ); 2097 sal_uLong nEndPos = pStream->Tell(); 2098 pStream->Seek( STREAM_SEEK_TO_BEGIN ); 2099 ZCodec aCodec( 0x4000, 0x4000 ); 2100 SvMemoryStream aStream; 2101 aCodec.BeginCompression(); 2102 aCodec.Write( aStream, static_cast<const sal_uInt8*>(pStream->GetData()), nEndPos ); 2103 aCodec.EndCompression(); 2104 nEndPos = aStream.Tell(); 2105 pStream->Seek( STREAM_SEEK_TO_BEGIN ); 2106 aStream.Seek( STREAM_SEEK_TO_BEGIN ); 2107 pStream->SetStreamSize( nEndPos ); 2108 pStream->WriteBytes( aStream.GetData(), nEndPos ); 2109 return true; 2110 } 2111 else 2112 return false; 2113 } 2114 2115 void PDFWriterImpl::beginCompression() 2116 { 2117 if (!g_bDebugDisableCompression) 2118 { 2119 m_pCodec = o3tl::make_unique<ZCodec>( 0x4000, 0x4000 ); 2120 m_pMemStream = o3tl::make_unique<SvMemoryStream>(); 2121 m_pCodec->BeginCompression(); 2122 } 2123 } 2124 2125 void PDFWriterImpl::endCompression() 2126 { 2127 if (!g_bDebugDisableCompression && m_pCodec) 2128 { 2129 m_pCodec->EndCompression(); 2130 m_pCodec.reset(); 2131 sal_uInt64 nLen = m_pMemStream->Tell(); 2132 m_pMemStream->Seek( 0 ); 2133 writeBuffer( m_pMemStream->GetData(), nLen ); 2134 m_pMemStream.reset(); 2135 } 2136 } 2137 2138 bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes ) 2139 { 2140 if( ! m_bOpen ) // we are already down the drain 2141 return false; 2142 2143 if( ! nBytes ) // huh ? 2144 return true; 2145 2146 if( !m_aOutputStreams.empty() ) 2147 { 2148 m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END ); 2149 m_aOutputStreams.front().m_pStream->WriteBytes( 2150 pBuffer, sal::static_int_cast<std::size_t>(nBytes)); 2151 return true; 2152 } 2153 2154 sal_uInt64 nWritten; 2155 if( m_pCodec ) 2156 { 2157 m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes ); 2158 nWritten = nBytes; 2159 } 2160 else 2161 { 2162 bool buffOK = true; 2163 if( m_bEncryptThisStream ) 2164 { 2165 /* implement the encryption part of the PDF spec encryption algorithm 3.1 */ 2166 buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ); 2167 if( buffOK ) 2168 rtl_cipher_encodeARCFOUR( m_aCipher, 2169 pBuffer, static_cast<sal_Size>(nBytes), 2170 m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) ); 2171 } 2172 2173 const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer : pBuffer; 2174 if( m_aDocDigest ) 2175 rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) ); 2176 2177 if (m_aFile.write(pWriteBuffer, nBytes, nWritten) != osl::File::E_None) 2178 nWritten = 0; 2179 2180 if( nWritten != nBytes ) 2181 { 2182 m_aFile.close(); 2183 m_bOpen = false; 2184 } 2185 } 2186 2187 return nWritten == nBytes; 2188 } 2189 2190 OutputDevice* PDFWriterImpl::getReferenceDevice() 2191 { 2192 if( ! m_pReferenceDevice ) 2193 { 2194 VclPtrInstance<VirtualDevice> pVDev(DeviceFormat::DEFAULT); 2195 2196 m_pReferenceDevice = pVDev; 2197 2198 if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 ) 2199 pVDev->SetReferenceDevice( VirtualDevice::RefDevMode::PDF1 ); 2200 else 2201 pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy ); 2202 2203 pVDev->SetOutputSizePixel( Size( 640, 480 ) ); 2204 pVDev->SetMapMode(MapMode(MapUnit::MapMM)); 2205 2206 m_pReferenceDevice->mpPDFWriter = this; 2207 m_pReferenceDevice->ImplUpdateFontData(); 2208 } 2209 return m_pReferenceDevice; 2210 } 2211 2212 static FontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin ) 2213 { 2214 FontAttributes aDFA; 2215 aDFA.SetFamilyName( OUString::createFromAscii( rBuiltin.m_pName ) ); 2216 aDFA.SetStyleName( OUString::createFromAscii( rBuiltin.m_pStyleName ) ); 2217 aDFA.SetFamilyType( rBuiltin.m_eFamily ); 2218 aDFA.SetSymbolFlag( rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 ); 2219 aDFA.SetPitch( rBuiltin.m_ePitch ); 2220 aDFA.SetWeight( rBuiltin.m_eWeight ); 2221 aDFA.SetItalic( rBuiltin.m_eItalic ); 2222 aDFA.SetWidthType( rBuiltin.m_eWidthType ); 2223 2224 aDFA.SetQuality( 50000 ); 2225 return aDFA; 2226 } 2227 2228 PdfBuiltinFontFace::PdfBuiltinFontFace( const PDFWriterImpl::BuiltinFont& rBuiltin ) 2229 : PhysicalFontFace( GetDevFontAttributes(rBuiltin) ), 2230 mrBuiltin( rBuiltin ) 2231 {} 2232 2233 LogicalFontInstance* PdfBuiltinFontFace::CreateFontInstance( FontSelectPattern& rFSD ) const 2234 { 2235 LogicalFontInstance* pEntry = new LogicalFontInstance( rFSD ); 2236 return pEntry; 2237 } 2238 2239 2240 void PDFWriterImpl::newPage( double nPageWidth, double nPageHeight, PDFWriter::Orientation eOrientation ) 2241 { 2242 endPage(); 2243 m_nCurrentPage = m_aPages.size(); 2244 m_aPages.emplace_back(this, nPageWidth, nPageHeight, eOrientation ); 2245 m_aPages.back().beginStream(); 2246 2247 // setup global graphics state 2248 // linewidth is "1 pixel" by default 2249 OStringBuffer aBuf( 16 ); 2250 appendDouble( 72.0/double(getReferenceDevice()->GetDPIX()), aBuf ); 2251 aBuf.append( " w\n" ); 2252 writeBuffer( aBuf.getStr(), aBuf.getLength() ); 2253 } 2254 2255 void PDFWriterImpl::endPage() 2256 { 2257 if( !m_aPages.empty() ) 2258 { 2259 // close eventual MC sequence 2260 endStructureElementMCSeq(); 2261 2262 // sanity check 2263 if( !m_aOutputStreams.empty() ) 2264 { 2265 OSL_FAIL( "redirection across pages !!!" ); 2266 m_aOutputStreams.clear(); // leak ! 2267 m_aMapMode.SetOrigin( Point() ); 2268 } 2269 2270 m_aGraphicsStack.clear(); 2271 m_aGraphicsStack.emplace_back( ); 2272 2273 // this should pop the PDF graphics stack if necessary 2274 updateGraphicsState(); 2275 2276 m_aPages.back().endStream(); 2277 2278 // reset the default font 2279 Font aFont; 2280 aFont.SetFamilyName( "Times" ); 2281 aFont.SetFontSize( Size( 0, 12 ) ); 2282 2283 m_aCurrentPDFState = m_aGraphicsStack.front(); 2284 m_aGraphicsStack.front().m_aFont = aFont; 2285 2286 for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin(); 2287 it != m_aBitmaps.end(); ++it ) 2288 { 2289 if( ! it->m_aBitmap.IsEmpty() ) 2290 { 2291 writeBitmapObject( *it ); 2292 it->m_aBitmap = BitmapEx(); 2293 } 2294 } 2295 for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg ) 2296 { 2297 if( jpeg->m_pStream ) 2298 { 2299 writeJPG( *jpeg ); 2300 jpeg->m_pStream.reset(); 2301 jpeg->m_aMask = Bitmap(); 2302 } 2303 } 2304 for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin(); 2305 t != m_aTransparentObjects.end(); ++t ) 2306 { 2307 if( t->m_pContentStream ) 2308 { 2309 writeTransparentObject( *t ); 2310 delete t->m_pContentStream; 2311 t->m_pContentStream = nullptr; 2312 } 2313 } 2314 } 2315 } 2316 2317 sal_Int32 PDFWriterImpl::createObject() 2318 { 2319 m_aObjects.push_back( ~0U ); 2320 return m_aObjects.size(); 2321 } 2322 2323 bool PDFWriterImpl::updateObject( sal_Int32 n ) 2324 { 2325 if( ! m_bOpen ) 2326 return false; 2327 2328 sal_uInt64 nOffset = ~0U; 2329 osl::File::RC aError = m_aFile.getPos(nOffset); 2330 SAL_WARN_IF( aError != osl::File::E_None, "vcl.pdfwriter", "could not register object" ); 2331 if (aError != osl::File::E_None) 2332 { 2333 m_aFile.close(); 2334 m_bOpen = false; 2335 } 2336 m_aObjects[ n-1 ] = nOffset; 2337 return aError == osl::File::E_None; 2338 } 2339 2340 #define CHECK_RETURN( x ) if( !(x) ) return 0 2341 #define CHECK_RETURN2( x ) if( !(x) ) return 2342 2343 sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject ) 2344 { 2345 if( nObject > 0 ) 2346 { 2347 OStringBuffer aLine( 1024 ); 2348 2349 aLine.append( nObject ); 2350 aLine.append( " 0 obj\n" 2351 "<</Nums[\n" ); 2352 sal_Int32 nTreeItems = m_aStructParentTree.size(); 2353 for( sal_Int32 n = 0; n < nTreeItems; n++ ) 2354 { 2355 aLine.append( n ); 2356 aLine.append( ' ' ); 2357 aLine.append( m_aStructParentTree[n] ); 2358 aLine.append( "\n" ); 2359 } 2360 aLine.append( "]>>\nendobj\n\n" ); 2361 CHECK_RETURN( updateObject( nObject ) ); 2362 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 2363 } 2364 return nObject; 2365 } 2366 2367 const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr ) 2368 { 2369 static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings; 2370 // fill maps once 2371 if( aAttributeStrings.empty() ) 2372 { 2373 aAttributeStrings[ PDFWriter::Placement ] = "Placement"; 2374 aAttributeStrings[ PDFWriter::WritingMode ] = "WritingMode"; 2375 aAttributeStrings[ PDFWriter::SpaceBefore ] = "SpaceBefore"; 2376 aAttributeStrings[ PDFWriter::SpaceAfter ] = "SpaceAfter"; 2377 aAttributeStrings[ PDFWriter::StartIndent ] = "StartIndent"; 2378 aAttributeStrings[ PDFWriter::EndIndent ] = "EndIndent"; 2379 aAttributeStrings[ PDFWriter::TextIndent ] = "TextIndent"; 2380 aAttributeStrings[ PDFWriter::TextAlign ] = "TextAlign"; 2381 aAttributeStrings[ PDFWriter::Width ] = "Width"; 2382 aAttributeStrings[ PDFWriter::Height ] = "Height"; 2383 aAttributeStrings[ PDFWriter::BlockAlign ] = "BlockAlign"; 2384 aAttributeStrings[ PDFWriter::InlineAlign ] = "InlineAlign"; 2385 aAttributeStrings[ PDFWriter::LineHeight ] = "LineHeight"; 2386 aAttributeStrings[ PDFWriter::BaselineShift ] = "BaselineShift"; 2387 aAttributeStrings[ PDFWriter::TextDecorationType ] = "TextDecorationType"; 2388 aAttributeStrings[ PDFWriter::ListNumbering ] = "ListNumbering"; 2389 aAttributeStrings[ PDFWriter::RowSpan ] = "RowSpan"; 2390 aAttributeStrings[ PDFWriter::ColSpan ] = "ColSpan"; 2391 aAttributeStrings[ PDFWriter::LinkAnnotation ] = "LinkAnnotation"; 2392 } 2393 2394 std::map< PDFWriter::StructAttribute, const char* >::const_iterator it = 2395 aAttributeStrings.find( eAttr ); 2396 2397 if( it == aAttributeStrings.end() ) 2398 SAL_INFO("vcl.pdfwriter", "invalid PDFWriter::StructAttribute " << eAttr); 2399 2400 return it != aAttributeStrings.end() ? it->second : ""; 2401 } 2402 2403 const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal ) 2404 { 2405 static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings; 2406 2407 if( aValueStrings.empty() ) 2408 { 2409 aValueStrings[ PDFWriter::NONE ] = "None"; 2410 aValueStrings[ PDFWriter::Block ] = "Block"; 2411 aValueStrings[ PDFWriter::Inline ] = "Inline"; 2412 aValueStrings[ PDFWriter::Before ] = "Before"; 2413 aValueStrings[ PDFWriter::After ] = "After"; 2414 aValueStrings[ PDFWriter::Start ] = "Start"; 2415 aValueStrings[ PDFWriter::End ] = "End"; 2416 aValueStrings[ PDFWriter::LrTb ] = "LrTb"; 2417 aValueStrings[ PDFWriter::RlTb ] = "RlTb"; 2418 aValueStrings[ PDFWriter::TbRl ] = "TbRl"; 2419 aValueStrings[ PDFWriter::Center ] = "Center"; 2420 aValueStrings[ PDFWriter::Justify ] = "Justify"; 2421 aValueStrings[ PDFWriter::Auto ] = "Auto"; 2422 aValueStrings[ PDFWriter::Middle ] = "Middle"; 2423 aValueStrings[ PDFWriter::Normal ] = "Normal"; 2424 aValueStrings[ PDFWriter::Underline ] = "Underline"; 2425 aValueStrings[ PDFWriter::Overline ] = "Overline"; 2426 aValueStrings[ PDFWriter::LineThrough ] = "LineThrough"; 2427 aValueStrings[ PDFWriter::Disc ] = "Disc"; 2428 aValueStrings[ PDFWriter::Circle ] = "Circle"; 2429 aValueStrings[ PDFWriter::Square ] = "Square"; 2430 aValueStrings[ PDFWriter::Decimal ] = "Decimal"; 2431 aValueStrings[ PDFWriter::UpperRoman ] = "UpperRoman"; 2432 aValueStrings[ PDFWriter::LowerRoman ] = "LowerRoman"; 2433 aValueStrings[ PDFWriter::UpperAlpha ] = "UpperAlpha"; 2434 aValueStrings[ PDFWriter::LowerAlpha ] = "LowerAlpha"; 2435 } 2436 2437 std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it = 2438 aValueStrings.find( eVal ); 2439 2440 if( it == aValueStrings.end() ) 2441 SAL_INFO("vcl.pdfwriter", "invalid PDFWriter::StructAttributeValue " << eVal); 2442 2443 return it != aValueStrings.end() ? it->second : ""; 2444 } 2445 2446 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt ) 2447 { 2448 o_rLine.append( "/" ); 2449 o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) ); 2450 2451 if( i_rVal.eValue != PDFWriter::Invalid ) 2452 { 2453 o_rLine.append( "/" ); 2454 o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) ); 2455 } 2456 else 2457 { 2458 // numerical value 2459 o_rLine.append( " " ); 2460 if( i_bIsFixedInt ) 2461 appendFixedInt( i_rVal.nValue, o_rLine ); 2462 else 2463 o_rLine.append( i_rVal.nValue ); 2464 } 2465 o_rLine.append( "\n" ); 2466 } 2467 2468 OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle ) 2469 { 2470 // create layout, list and table attribute sets 2471 OStringBuffer aLayout(256), aList(64), aTable(64); 2472 for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin(); 2473 it != i_rEle.m_aAttributes.end(); ++it ) 2474 { 2475 if( it->first == PDFWriter::ListNumbering ) 2476 appendStructureAttributeLine( it->first, it->second, aList, true ); 2477 else if( it->first == PDFWriter::RowSpan || 2478 it->first == PDFWriter::ColSpan ) 2479 appendStructureAttributeLine( it->first, it->second, aTable, false ); 2480 else if( it->first == PDFWriter::LinkAnnotation ) 2481 { 2482 sal_Int32 nLink = it->second.nValue; 2483 std::map< sal_Int32, sal_Int32 >::const_iterator link_it = 2484 m_aLinkPropertyMap.find( nLink ); 2485 if( link_it != m_aLinkPropertyMap.end() ) 2486 nLink = link_it->second; 2487 if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() ) 2488 { 2489 // update struct parent of link 2490 OStringBuffer aStructParentEntry( 32 ); 2491 aStructParentEntry.append( i_rEle.m_nObject ); 2492 aStructParentEntry.append( " 0 R" ); 2493 m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() ); 2494 m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1; 2495 2496 sal_Int32 nRefObject = createObject(); 2497 OStringBuffer aRef( 256 ); 2498 aRef.append( nRefObject ); 2499 aRef.append( " 0 obj\n" 2500 "<</Type/OBJR/Obj " ); 2501 aRef.append( m_aLinks[ nLink ].m_nObject ); 2502 aRef.append( " 0 R>>\n" 2503 "endobj\n\n" 2504 ); 2505 if (updateObject(nRefObject)) 2506 { 2507 writeBuffer( aRef.getStr(), aRef.getLength() ); 2508 } 2509 2510 i_rEle.m_aKids.emplace_back( nRefObject ); 2511 } 2512 else 2513 { 2514 OSL_FAIL( "unresolved link id for Link structure" ); 2515 SAL_INFO("vcl.pdfwriter", "unresolved link id " << nLink << " for Link structure"); 2516 if (g_bDebugDisableCompression) 2517 { 2518 OStringBuffer aLine( "unresolved link id " ); 2519 aLine.append( nLink ); 2520 aLine.append( " for Link structure" ); 2521 emitComment( aLine.getStr() ); 2522 } 2523 } 2524 } 2525 else 2526 appendStructureAttributeLine( it->first, it->second, aLayout, true ); 2527 } 2528 if( ! i_rEle.m_aBBox.IsEmpty() ) 2529 { 2530 aLayout.append( "/BBox[" ); 2531 appendFixedInt( i_rEle.m_aBBox.Left(), aLayout ); 2532 aLayout.append( " " ); 2533 appendFixedInt( i_rEle.m_aBBox.Top(), aLayout ); 2534 aLayout.append( " " ); 2535 appendFixedInt( i_rEle.m_aBBox.Right(), aLayout ); 2536 aLayout.append( " " ); 2537 appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout ); 2538 aLayout.append( "]\n" ); 2539 } 2540 2541 std::vector< sal_Int32 > aAttribObjects; 2542 if( !aLayout.isEmpty() ) 2543 { 2544 aAttribObjects.push_back( createObject() ); 2545 if (updateObject( aAttribObjects.back() )) 2546 { 2547 OStringBuffer aObj( 64 ); 2548 aObj.append( aAttribObjects.back() ); 2549 aObj.append( " 0 obj\n" 2550 "<</O/Layout\n" ); 2551 aLayout.append( ">>\nendobj\n\n" ); 2552 writeBuffer( aObj.getStr(), aObj.getLength() ); 2553 writeBuffer( aLayout.getStr(), aLayout.getLength() ); 2554 } 2555 } 2556 if( !aList.isEmpty() ) 2557 { 2558 aAttribObjects.push_back( createObject() ); 2559 if (updateObject( aAttribObjects.back() )) 2560 { 2561 OStringBuffer aObj( 64 ); 2562 aObj.append( aAttribObjects.back() ); 2563 aObj.append( " 0 obj\n" 2564 "<</O/List\n" ); 2565 aList.append( ">>\nendobj\n\n" ); 2566 writeBuffer( aObj.getStr(), aObj.getLength() ); 2567 writeBuffer( aList.getStr(), aList.getLength() ); 2568 } 2569 } 2570 if( !aTable.isEmpty() ) 2571 { 2572 aAttribObjects.push_back( createObject() ); 2573 if (updateObject( aAttribObjects.back() )) 2574 { 2575 OStringBuffer aObj( 64 ); 2576 aObj.append( aAttribObjects.back() ); 2577 aObj.append( " 0 obj\n" 2578 "<</O/Table\n" ); 2579 aTable.append( ">>\nendobj\n\n" ); 2580 writeBuffer( aObj.getStr(), aObj.getLength() ); 2581 writeBuffer( aTable.getStr(), aTable.getLength() ); 2582 } 2583 } 2584 2585 OStringBuffer aRet( 64 ); 2586 if( aAttribObjects.size() > 1 ) 2587 aRet.append( " [" ); 2588 for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin(); 2589 at_it != aAttribObjects.end(); ++at_it ) 2590 { 2591 aRet.append( " " ); 2592 aRet.append( *at_it ); 2593 aRet.append( " 0 R" ); 2594 } 2595 if( aAttribObjects.size() > 1 ) 2596 aRet.append( " ]" ); 2597 return aRet.makeStringAndClear(); 2598 } 2599 2600 sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle ) 2601 { 2602 if( 2603 // do not emit NonStruct and its children 2604 rEle.m_eType == PDFWriter::NonStructElement && 2605 rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root 2606 ) 2607 return 0; 2608 2609 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it ) 2610 { 2611 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) ) 2612 { 2613 PDFStructureElement& rChild = m_aStructure[ *it ]; 2614 if( rChild.m_eType != PDFWriter::NonStructElement ) 2615 { 2616 if( rChild.m_nParentElement == rEle.m_nOwnElement ) 2617 emitStructure( rChild ); 2618 else 2619 { 2620 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure element" ); 2621 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::emitStructure: invalid child structure element with id " << *it); 2622 } 2623 } 2624 } 2625 else 2626 { 2627 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" ); 2628 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::emitStructure: invalid child structure id " << *it); 2629 } 2630 } 2631 2632 OStringBuffer aLine( 512 ); 2633 aLine.append( rEle.m_nObject ); 2634 aLine.append( " 0 obj\n" 2635 "<</Type" ); 2636 sal_Int32 nParentTree = -1; 2637 if( rEle.m_nOwnElement == rEle.m_nParentElement ) 2638 { 2639 nParentTree = createObject(); 2640 CHECK_RETURN( nParentTree ); 2641 aLine.append( "/StructTreeRoot\n" ); 2642 aLine.append( "/ParentTree " ); 2643 aLine.append( nParentTree ); 2644 aLine.append( " 0 R\n" ); 2645 if( ! m_aRoleMap.empty() ) 2646 { 2647 aLine.append( "/RoleMap<<" ); 2648 for( std::unordered_map<OString,OString>::const_iterator 2649 it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it ) 2650 { 2651 aLine.append( '/' ); 2652 aLine.append(it->first); 2653 aLine.append( '/' ); 2654 aLine.append( it->second ); 2655 aLine.append( '\n' ); 2656 } 2657 aLine.append( ">>\n" ); 2658 } 2659 } 2660 else 2661 { 2662 aLine.append( "/StructElem\n" 2663 "/S/" ); 2664 if( !rEle.m_aAlias.isEmpty() ) 2665 aLine.append( rEle.m_aAlias ); 2666 else 2667 aLine.append( getStructureTag( rEle.m_eType ) ); 2668 aLine.append( "\n" 2669 "/P " ); 2670 aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject ); 2671 aLine.append( " 0 R\n" 2672 "/Pg " ); 2673 aLine.append( rEle.m_nFirstPageObject ); 2674 aLine.append( " 0 R\n" ); 2675 if( !rEle.m_aActualText.isEmpty() ) 2676 { 2677 aLine.append( "/ActualText" ); 2678 appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine ); 2679 aLine.append( "\n" ); 2680 } 2681 if( !rEle.m_aAltText.isEmpty() ) 2682 { 2683 aLine.append( "/Alt" ); 2684 appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine ); 2685 aLine.append( "\n" ); 2686 } 2687 } 2688 if( (! rEle.m_aBBox.IsEmpty()) || (! rEle.m_aAttributes.empty()) ) 2689 { 2690 OString aAttribs = emitStructureAttributes( rEle ); 2691 if( !aAttribs.isEmpty() ) 2692 { 2693 aLine.append( "/A" ); 2694 aLine.append( aAttribs ); 2695 aLine.append( "\n" ); 2696 } 2697 } 2698 if( !rEle.m_aLocale.Language.isEmpty() ) 2699 { 2700 /* PDF allows only RFC 3066, which is only partly BCP 47 and does not 2701 * include script tags and others. 2702 * http://pdf.editme.com/pdfua-naturalLanguageSpecification 2703 * http://partners.adobe.com/public/developer/en/pdf/PDFReference16.pdf#page=886 2704 * https://www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf#M13.9.19332.1Heading.97.Natural.Language.Specification 2705 * */ 2706 LanguageTag aLanguageTag( rEle.m_aLocale); 2707 OUString aLanguage, aScript, aCountry; 2708 aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry); 2709 if (!aLanguage.isEmpty()) 2710 { 2711 OUStringBuffer aLocBuf( 16 ); 2712 aLocBuf.append( aLanguage ); 2713 if( !aCountry.isEmpty() ) 2714 { 2715 aLocBuf.append( '-' ); 2716 aLocBuf.append( aCountry ); 2717 } 2718 aLine.append( "/Lang" ); 2719 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine ); 2720 aLine.append( "\n" ); 2721 } 2722 } 2723 if( ! rEle.m_aKids.empty() ) 2724 { 2725 unsigned int i = 0; 2726 aLine.append( "/K[" ); 2727 for( std::list< PDFStructureElementKid >::const_iterator it = 2728 rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ ) 2729 { 2730 if( it->nMCID == -1 ) 2731 { 2732 aLine.append( it->nObject ); 2733 aLine.append( " 0 R" ); 2734 aLine.append( ( (i & 15) == 15 ) ? "\n" : " " ); 2735 } 2736 else 2737 { 2738 if( it->nObject == rEle.m_nFirstPageObject ) 2739 { 2740 aLine.append( it->nMCID ); 2741 aLine.append( " " ); 2742 } 2743 else 2744 { 2745 aLine.append( "<</Type/MCR/Pg " ); 2746 aLine.append( it->nObject ); 2747 aLine.append( " 0 R /MCID " ); 2748 aLine.append( it->nMCID ); 2749 aLine.append( ">>\n" ); 2750 } 2751 } 2752 } 2753 aLine.append( "]\n" ); 2754 } 2755 aLine.append( ">>\nendobj\n\n" ); 2756 2757 CHECK_RETURN( updateObject( rEle.m_nObject ) ); 2758 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 2759 2760 CHECK_RETURN( emitStructParentTree( nParentTree ) ); 2761 2762 return rEle.m_nObject; 2763 } 2764 2765 bool PDFWriterImpl::emitGradients() 2766 { 2767 for( std::list<GradientEmit>::iterator it = m_aGradients.begin(); 2768 it != m_aGradients.end(); ++it ) 2769 { 2770 if ( !writeGradientFunction( *it ) ) return false; 2771 } 2772 return true; 2773 } 2774 2775 bool PDFWriterImpl::emitTilings() 2776 { 2777 OStringBuffer aTilingObj( 1024 ); 2778 2779 for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it ) 2780 { 2781 SAL_WARN_IF( !it->m_pTilingStream, "vcl.pdfwriter", "tiling without stream" ); 2782 if( ! it->m_pTilingStream ) 2783 continue; 2784 2785 aTilingObj.setLength( 0 ); 2786 2787 if (g_bDebugDisableCompression) 2788 { 2789 emitComment( "PDFWriterImpl::emitTilings" ); 2790 } 2791 2792 sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left(); 2793 sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top(); 2794 sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth(); 2795 sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight(); 2796 if( it->m_aCellSize.Width() == 0 ) 2797 it->m_aCellSize.Width() = nW; 2798 if( it->m_aCellSize.Height() == 0 ) 2799 it->m_aCellSize.Height() = nH; 2800 2801 bool bDeflate = compressStream( it->m_pTilingStream ); 2802 it->m_pTilingStream->Seek( STREAM_SEEK_TO_END ); 2803 sal_uInt64 const nTilingStreamSize = it->m_pTilingStream->Tell(); 2804 it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN ); 2805 2806 // write pattern object 2807 aTilingObj.append( it->m_nObject ); 2808 aTilingObj.append( " 0 obj\n" ); 2809 aTilingObj.append( "<</Type/Pattern/PatternType 1\n" 2810 "/PaintType 1\n" 2811 "/TilingType 2\n" 2812 "/BBox[" ); 2813 appendFixedInt( nX, aTilingObj ); 2814 aTilingObj.append( ' ' ); 2815 appendFixedInt( nY, aTilingObj ); 2816 aTilingObj.append( ' ' ); 2817 appendFixedInt( nX+nW, aTilingObj ); 2818 aTilingObj.append( ' ' ); 2819 appendFixedInt( nY+nH, aTilingObj ); 2820 aTilingObj.append( "]\n" 2821 "/XStep " ); 2822 appendFixedInt( it->m_aCellSize.Width(), aTilingObj ); 2823 aTilingObj.append( "\n" 2824 "/YStep " ); 2825 appendFixedInt( it->m_aCellSize.Height(), aTilingObj ); 2826 aTilingObj.append( "\n" ); 2827 if( it->m_aTransform.matrix[0] != 1.0 || 2828 it->m_aTransform.matrix[1] != 0.0 || 2829 it->m_aTransform.matrix[3] != 0.0 || 2830 it->m_aTransform.matrix[4] != 1.0 || 2831 it->m_aTransform.matrix[2] != 0.0 || 2832 it->m_aTransform.matrix[5] != 0.0 ) 2833 { 2834 aTilingObj.append( "/Matrix [" ); 2835 // TODO: scaling, mirroring on y, etc 2836 appendDouble( it->m_aTransform.matrix[0], aTilingObj ); 2837 aTilingObj.append( ' ' ); 2838 appendDouble( it->m_aTransform.matrix[1], aTilingObj ); 2839 aTilingObj.append( ' ' ); 2840 appendDouble( it->m_aTransform.matrix[3], aTilingObj ); 2841 aTilingObj.append( ' ' ); 2842 appendDouble( it->m_aTransform.matrix[4], aTilingObj ); 2843 aTilingObj.append( ' ' ); 2844 appendDouble( it->m_aTransform.matrix[2], aTilingObj ); 2845 aTilingObj.append( ' ' ); 2846 appendDouble( it->m_aTransform.matrix[5], aTilingObj ); 2847 aTilingObj.append( "]\n" ); 2848 } 2849 aTilingObj.append( "/Resources" ); 2850 it->m_aResources.append( aTilingObj, getFontDictObject() ); 2851 if( bDeflate ) 2852 aTilingObj.append( "/Filter/FlateDecode" ); 2853 aTilingObj.append( "/Length " ); 2854 aTilingObj.append( (sal_Int32)nTilingStreamSize ); 2855 aTilingObj.append( ">>\nstream\n" ); 2856 if ( !updateObject( it->m_nObject ) ) return false; 2857 if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false; 2858 checkAndEnableStreamEncryption( it->m_nObject ); 2859 bool written = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize ); 2860 delete it->m_pTilingStream; 2861 it->m_pTilingStream = nullptr; 2862 if( !written ) 2863 return false; 2864 disableStreamEncryption(); 2865 aTilingObj.setLength( 0 ); 2866 aTilingObj.append( "\nendstream\nendobj\n\n" ); 2867 if ( !writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ) return false; 2868 } 2869 return true; 2870 } 2871 2872 sal_Int32 PDFWriterImpl::emitBuiltinFont( const PdfBuiltinFontFace* pFD, sal_Int32 nFontObject ) 2873 { 2874 if( !pFD ) 2875 return 0; 2876 const BuiltinFont& rBuiltinFont = pFD->GetBuiltinFont(); 2877 2878 OStringBuffer aLine( 1024 ); 2879 2880 if( nFontObject <= 0 ) 2881 nFontObject = createObject(); 2882 CHECK_RETURN( updateObject( nFontObject ) ); 2883 aLine.append( nFontObject ); 2884 aLine.append( " 0 obj\n" 2885 "<</Type/Font/Subtype/Type1/BaseFont/" ); 2886 appendName( rBuiltinFont.m_pPSName, aLine ); 2887 aLine.append( "\n" ); 2888 if( rBuiltinFont.m_eCharSet == RTL_TEXTENCODING_MS_1252 ) 2889 aLine.append( "/Encoding/WinAnsiEncoding\n" ); 2890 aLine.append( ">>\nendobj\n\n" ); 2891 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 2892 return nFontObject; 2893 } 2894 2895 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const PhysicalFontFace* pFont, EmbedFont const & rEmbed ) 2896 { 2897 std::map< sal_Int32, sal_Int32 > aRet; 2898 2899 sal_Int32 nFontDescriptor = 0; 2900 OString aSubType( "/Type1" ); 2901 FontSubsetInfo aInfo; 2902 // fill in dummy values 2903 aInfo.m_nAscent = 1000; 2904 aInfo.m_nDescent = 200; 2905 aInfo.m_nCapHeight = 1000; 2906 aInfo.m_aFontBBox = tools::Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) ); 2907 aInfo.m_aPSName = pFont->GetFamilyName(); 2908 sal_Int32 pWidths[256]; 2909 memset( pWidths, 0, sizeof(pWidths) ); 2910 2911 SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics(); 2912 2913 assert(pGraphics); 2914 2915 aSubType = OString( "/TrueType" ); 2916 std::vector< sal_Int32 > aGlyphWidths; 2917 Ucs2UIntMap aUnicodeMap; 2918 pGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap ); 2919 2920 OUString aTmpName; 2921 osl_createTempFile( nullptr, nullptr, &aTmpName.pData ); 2922 sal_GlyphId aGlyphIds[ 256 ]; 2923 sal_uInt8 pEncoding[ 256 ]; 2924 sal_Int32 pDuWidths[ 256 ]; 2925 2926 memset( aGlyphIds, 0, sizeof( aGlyphIds ) ); 2927 memset( pEncoding, 0, sizeof( pEncoding ) ); 2928 memset( pDuWidths, 0, sizeof( pDuWidths ) ); 2929 2930 for( sal_Ucs c = 32; c < 256; c++ ) 2931 { 2932 pEncoding[c] = c; 2933 aGlyphIds[c] = 0; 2934 if( aUnicodeMap.find( c ) != aUnicodeMap.end() ) 2935 pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ]; 2936 } 2937 //TODO: surely this is utterly broken because aGlyphIds is just all zeros, if we 2938 //had the right glyphids here then I imagine we could replace pDuWidths with 2939 //pWidths and remove pWidths assignment above. i.e. start with the glyph ids 2940 //and map those to unicode rather than try and reverse map them ? 2941 pGraphics->CreateFontSubset( aTmpName, pFont, aGlyphIds, pEncoding, pDuWidths, 256, aInfo ); 2942 osl_removeFile( aTmpName.pData ); 2943 2944 // write font descriptor 2945 nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 ); 2946 if( nFontDescriptor ) 2947 { 2948 // write font object 2949 sal_Int32 nObject = createObject(); 2950 if( updateObject( nObject ) ) 2951 { 2952 OStringBuffer aLine( 1024 ); 2953 aLine.append( nObject ); 2954 aLine.append( " 0 obj\n" 2955 "<</Type/Font/Subtype" ); 2956 aLine.append( aSubType ); 2957 aLine.append( "/BaseFont/" ); 2958 appendName( aInfo.m_aPSName, aLine ); 2959 aLine.append( "\n" ); 2960 if( !pFont->IsSymbolFont() ) 2961 aLine.append( "/Encoding/WinAnsiEncoding\n" ); 2962 aLine.append( "/FirstChar 32 /LastChar 255\n" 2963 "/Widths[" ); 2964 for( int i = 32; i < 256; i++ ) 2965 { 2966 aLine.append( pWidths[i] ); 2967 aLine.append( ((i&15) == 15) ? "\n" : " " ); 2968 } 2969 aLine.append( "]\n" 2970 "/FontDescriptor " ); 2971 aLine.append( nFontDescriptor ); 2972 aLine.append( " 0 R>>\n" 2973 "endobj\n\n" ); 2974 writeBuffer( aLine.getStr(), aLine.getLength() ); 2975 2976 aRet[ rEmbed.m_nNormalFontID ] = nObject; 2977 } 2978 } 2979 2980 return aRet; 2981 } 2982 2983 typedef int ThreeInts[3]; 2984 static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen, 2985 ThreeInts& rSegmentLengths ) 2986 { 2987 if( !pFontBytes || (nByteLen < 0) ) 2988 return false; 2989 const unsigned char* pPtr = pFontBytes; 2990 const unsigned char* pEnd = pFontBytes + nByteLen; 2991 2992 for(int & rSegmentLength : rSegmentLengths) { 2993 // read segment1 header 2994 if( pPtr+6 >= pEnd ) 2995 return false; 2996 if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) ) 2997 return false; 2998 const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2]; 2999 if( nLen <= 0) 3000 return false; 3001 rSegmentLength = nLen; 3002 pPtr += nLen + 6; 3003 } 3004 3005 // read segment-end header 3006 if( pPtr+2 >= pEnd ) 3007 return false; 3008 if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) ) 3009 return false; 3010 3011 return true; 3012 } 3013 3014 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer ) 3015 { 3016 if( nSubsetID ) 3017 { 3018 for( int i = 0; i < 6; i++ ) 3019 { 3020 int nOffset = (nSubsetID % 26); 3021 nSubsetID /= 26; 3022 rBuffer.append( (sal_Char)('A'+nOffset) ); 3023 } 3024 rBuffer.append( '+' ); 3025 } 3026 appendName( rPSName, rBuffer ); 3027 } 3028 3029 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, 3030 const sal_Ucs* pCodeUnits, 3031 const sal_Int32* pCodeUnitsPerGlyph, 3032 const sal_Int32* pEncToUnicodeIndex, 3033 int nGlyphs ) 3034 { 3035 int nMapped = 0; 3036 for (int n = 0; n < nGlyphs; ++n) 3037 if( pCodeUnits[pEncToUnicodeIndex[n]] && pCodeUnitsPerGlyph[n] ) 3038 nMapped++; 3039 3040 if( nMapped == 0 ) 3041 return 0; 3042 3043 sal_Int32 nStream = createObject(); 3044 CHECK_RETURN( updateObject( nStream ) ); 3045 3046 OStringBuffer aContents( 1024 ); 3047 aContents.append( 3048 "/CIDInit/ProcSet findresource begin\n" 3049 "12 dict begin\n" 3050 "begincmap\n" 3051 "/CIDSystemInfo<<\n" 3052 "/Registry (Adobe)\n" 3053 "/Ordering (UCS)\n" 3054 "/Supplement 0\n" 3055 ">> def\n" 3056 "/CMapName/Adobe-Identity-UCS def\n" 3057 "/CMapType 2 def\n" 3058 "1 begincodespacerange\n" 3059 "<00> <FF>\n" 3060 "endcodespacerange\n" 3061 ); 3062 int nCount = 0; 3063 for (int n = 0; n < nGlyphs; ++n) 3064 { 3065 if( pCodeUnits[pEncToUnicodeIndex[n]] && pCodeUnitsPerGlyph[n] ) 3066 { 3067 if( (nCount % 100) == 0 ) 3068 { 3069 if( nCount ) 3070 aContents.append( "endbfchar\n" ); 3071 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) ); 3072 aContents.append( " beginbfchar\n" ); 3073 } 3074 aContents.append( '<' ); 3075 appendHex( (sal_Int8)pEncoding[n], aContents ); 3076 aContents.append( "> <" ); 3077 // TODO: handle code points>U+FFFF 3078 sal_Int32 nIndex = pEncToUnicodeIndex[n]; 3079 for( sal_Int32 j = 0; j < pCodeUnitsPerGlyph[n]; j++ ) 3080 { 3081 appendHex( (sal_Int8)(pCodeUnits[nIndex + j] / 256), aContents ); 3082 appendHex( (sal_Int8)(pCodeUnits[nIndex + j] & 255), aContents ); 3083 } 3084 aContents.append( ">\n" ); 3085 nCount++; 3086 } 3087 } 3088 aContents.append( "endbfchar\n" 3089 "endcmap\n" 3090 "CMapName currentdict /CMap define resource pop\n" 3091 "end\n" 3092 "end\n" ); 3093 SvMemoryStream aStream; 3094 if (!g_bDebugDisableCompression) 3095 { 3096 ZCodec aCodec( 0x4000, 0x4000 ); 3097 aCodec.BeginCompression(); 3098 aCodec.Write( aStream, reinterpret_cast<const sal_uInt8*>(aContents.getStr()), aContents.getLength() ); 3099 aCodec.EndCompression(); 3100 } 3101 3102 if (g_bDebugDisableCompression) 3103 { 3104 emitComment( "PDFWriterImpl::createToUnicodeCMap" ); 3105 } 3106 OStringBuffer aLine( 40 ); 3107 3108 aLine.append( nStream ); 3109 aLine.append( " 0 obj\n<</Length " ); 3110 sal_Int32 nLen = 0; 3111 if (!g_bDebugDisableCompression) 3112 { 3113 nLen = (sal_Int32)aStream.Tell(); 3114 aStream.Seek( 0 ); 3115 aLine.append( nLen ); 3116 aLine.append( "/Filter/FlateDecode" ); 3117 } 3118 else 3119 aLine.append( aContents.getLength() ); 3120 aLine.append( ">>\nstream\n" ); 3121 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3122 checkAndEnableStreamEncryption( nStream ); 3123 if (!g_bDebugDisableCompression) 3124 { 3125 CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) ); 3126 } 3127 else 3128 { 3129 CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) ); 3130 } 3131 disableStreamEncryption(); 3132 aLine.setLength( 0 ); 3133 aLine.append( "\nendstream\n" 3134 "endobj\n\n" ); 3135 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3136 return nStream; 3137 } 3138 3139 sal_Int32 PDFWriterImpl::emitFontDescriptor( const PhysicalFontFace* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream ) 3140 { 3141 OStringBuffer aLine( 1024 ); 3142 // get font flags, see PDF reference 1.4 p. 358 3143 // possibly characters outside Adobe standard encoding 3144 // so set Symbolic flag 3145 sal_Int32 nFontFlags = (1<<2); 3146 if( pFont->GetItalic() == ITALIC_NORMAL || pFont->GetItalic() == ITALIC_OBLIQUE ) 3147 nFontFlags |= (1 << 6); 3148 if( pFont->GetPitch() == PITCH_FIXED ) 3149 nFontFlags |= 1; 3150 if( pFont->GetFamilyType() == FAMILY_SCRIPT ) 3151 nFontFlags |= (1 << 3); 3152 else if( pFont->GetFamilyType() == FAMILY_ROMAN ) 3153 nFontFlags |= (1 << 1); 3154 3155 sal_Int32 nFontDescriptor = createObject(); 3156 CHECK_RETURN( updateObject( nFontDescriptor ) ); 3157 aLine.setLength( 0 ); 3158 aLine.append( nFontDescriptor ); 3159 aLine.append( " 0 obj\n" 3160 "<</Type/FontDescriptor/FontName/" ); 3161 appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine ); 3162 aLine.append( "\n" 3163 "/Flags " ); 3164 aLine.append( nFontFlags ); 3165 aLine.append( "\n" 3166 "/FontBBox[" ); 3167 // note: Top and Bottom are reversed in VCL and PDF rectangles 3168 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() ); 3169 aLine.append( ' ' ); 3170 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() ); 3171 aLine.append( ' ' ); 3172 aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() ); 3173 aLine.append( ' ' ); 3174 aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) ); 3175 aLine.append( "]/ItalicAngle " ); 3176 if( pFont->GetItalic() == ITALIC_OBLIQUE || pFont->GetItalic() == ITALIC_NORMAL ) 3177 aLine.append( "-30" ); 3178 else 3179 aLine.append( "0" ); 3180 aLine.append( "\n" 3181 "/Ascent " ); 3182 aLine.append( (sal_Int32)rInfo.m_nAscent ); 3183 aLine.append( "\n" 3184 "/Descent " ); 3185 aLine.append( (sal_Int32)-rInfo.m_nDescent ); 3186 aLine.append( "\n" 3187 "/CapHeight " ); 3188 aLine.append( (sal_Int32)rInfo.m_nCapHeight ); 3189 // According to PDF reference 1.4 StemV is required 3190 // seems a tad strange to me, but well ... 3191 aLine.append( "\n" 3192 "/StemV 80\n" ); 3193 if( nFontStream ) 3194 { 3195 aLine.append( "/FontFile" ); 3196 switch( rInfo.m_nFontType ) 3197 { 3198 case FontType::SFNT_TTF: 3199 aLine.append( '2' ); 3200 break; 3201 case FontType::TYPE1_PFA: 3202 case FontType::TYPE1_PFB: 3203 case FontType::ANY_TYPE1: 3204 break; 3205 default: 3206 OSL_FAIL( "unknown fonttype in PDF font descriptor" ); 3207 return 0; 3208 } 3209 aLine.append( ' ' ); 3210 aLine.append( nFontStream ); 3211 aLine.append( " 0 R\n" ); 3212 } 3213 aLine.append( ">>\n" 3214 "endobj\n\n" ); 3215 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3216 3217 return nFontDescriptor; 3218 } 3219 3220 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const 3221 { 3222 for( std::map< sal_Int32, sal_Int32 >::const_iterator it = 3223 m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it ) 3224 { 3225 rDict.append( m_aBuiltinFonts[it->first].getNameObject() ); 3226 rDict.append( ' ' ); 3227 rDict.append( it->second ); 3228 rDict.append( " 0 R" ); 3229 } 3230 } 3231 3232 bool PDFWriterImpl::emitFonts() 3233 { 3234 SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics(); 3235 3236 if (!pGraphics) 3237 return false; 3238 3239 OStringBuffer aLine( 1024 ); 3240 3241 std::map< sal_Int32, sal_Int32 > aFontIDToObject; 3242 3243 OUString aTmpName; 3244 osl_createTempFile( nullptr, nullptr, &aTmpName.pData ); 3245 for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it ) 3246 { 3247 for( std::list< FontEmit >::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit ) 3248 { 3249 sal_GlyphId aGlyphIds[ 256 ]; 3250 sal_Int32 pWidths[ 256 ]; 3251 sal_uInt8 pEncoding[ 256 ]; 3252 sal_Int32 pEncToUnicodeIndex[ 256 ]; 3253 sal_Int32 pCodeUnitsPerGlyph[ 256 ]; 3254 std::vector<sal_Ucs> aCodeUnits; 3255 aCodeUnits.reserve( 256 ); 3256 int nGlyphs = 1; 3257 // fill arrays and prepare encoding index map 3258 sal_Int32 nToUnicodeStream = 0; 3259 3260 memset( aGlyphIds, 0, sizeof( aGlyphIds ) ); 3261 memset( pEncoding, 0, sizeof( pEncoding ) ); 3262 memset( pCodeUnitsPerGlyph, 0, sizeof( pCodeUnitsPerGlyph ) ); 3263 memset( pEncToUnicodeIndex, 0, sizeof( pEncToUnicodeIndex ) ); 3264 for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit ) 3265 { 3266 sal_uInt8 nEnc = fit->second.getGlyphId(); 3267 3268 SAL_WARN_IF( aGlyphIds[nEnc] != 0 || pEncoding[nEnc] != 0, "vcl.pdfwriter", "duplicate glyph" ); 3269 SAL_WARN_IF( nEnc > lit->m_aMapping.size(), "vcl.pdfwriter", "invalid glyph encoding" ); 3270 3271 aGlyphIds[ nEnc ] = fit->first; 3272 pEncoding[ nEnc ] = nEnc; 3273 pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aCodeUnits.size()); 3274 pCodeUnitsPerGlyph[ nEnc ] = fit->second.countCodes(); 3275 for( sal_Int32 n = 0; n < pCodeUnitsPerGlyph[ nEnc ]; n++ ) 3276 aCodeUnits.push_back( fit->second.getCode( n ) ); 3277 if( fit->second.getCode(0) ) 3278 nToUnicodeStream = 1; 3279 if( nGlyphs < 256 ) 3280 nGlyphs++; 3281 else 3282 { 3283 OSL_FAIL( "too many glyphs for subset" ); 3284 } 3285 } 3286 FontSubsetInfo aSubsetInfo; 3287 if( pGraphics->CreateFontSubset( aTmpName, it->first, aGlyphIds, pEncoding, pWidths, nGlyphs, aSubsetInfo ) ) 3288 { 3289 // create font stream 3290 osl::File aFontFile(aTmpName); 3291 if (osl::File::E_None != aFontFile.open(osl_File_OpenFlag_Read)) return false; 3292 // get file size 3293 sal_uInt64 nLength1; 3294 if ( osl::File::E_None != aFontFile.setPos(osl_Pos_End, 0) ) return false; 3295 if ( osl::File::E_None != aFontFile.getPos(nLength1) ) return false; 3296 if ( osl::File::E_None != aFontFile.setPos(osl_Pos_Absolut, 0) ) return false; 3297 3298 if (g_bDebugDisableCompression) 3299 { 3300 emitComment( "PDFWriterImpl::emitFonts" ); 3301 } 3302 sal_Int32 nFontStream = createObject(); 3303 sal_Int32 nStreamLengthObject = createObject(); 3304 if ( !updateObject( nFontStream ) ) return false; 3305 aLine.setLength( 0 ); 3306 aLine.append( nFontStream ); 3307 aLine.append( " 0 obj\n" 3308 "<</Length " ); 3309 aLine.append( nStreamLengthObject ); 3310 if (!g_bDebugDisableCompression) 3311 aLine.append( " 0 R" 3312 "/Filter/FlateDecode" 3313 "/Length1 " ); 3314 else 3315 aLine.append( " 0 R" 3316 "/Length1 " ); 3317 3318 sal_uInt64 nStartPos = 0; 3319 if( aSubsetInfo.m_nFontType == FontType::SFNT_TTF ) 3320 { 3321 aLine.append( (sal_Int32)nLength1 ); 3322 3323 aLine.append( ">>\n" 3324 "stream\n" ); 3325 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false; 3326 if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false; 3327 3328 // copy font file 3329 beginCompression(); 3330 checkAndEnableStreamEncryption( nFontStream ); 3331 sal_Bool bEOF = false; 3332 do 3333 { 3334 char buf[8192]; 3335 sal_uInt64 nRead; 3336 if ( osl::File::E_None != aFontFile.read(buf, sizeof(buf), nRead) ) return false; 3337 if ( !writeBuffer( buf, nRead ) ) return false; 3338 if ( osl::File::E_None != aFontFile.isEndOfFile(&bEOF) ) return false; 3339 } while( ! bEOF ); 3340 } 3341 else if( aSubsetInfo.m_nFontType & FontType::CFF_FONT) 3342 { 3343 // TODO: implement 3344 OSL_FAIL( "PDFWriterImpl does not support CFF-font subsets yet!" ); 3345 } 3346 else if( aSubsetInfo.m_nFontType & FontType::TYPE1_PFB) // TODO: also support PFA? 3347 { 3348 std::unique_ptr<unsigned char[]> xBuffer(new unsigned char[nLength1]); 3349 3350 sal_uInt64 nBytesRead = 0; 3351 if ( osl::File::E_None != aFontFile.read(xBuffer.get(), nLength1, nBytesRead) ) return false; 3352 SAL_WARN_IF( nBytesRead!=nLength1, "vcl.pdfwriter", "PDF-FontSubset read incomplete!" ); 3353 if ( osl::File::E_None != aFontFile.setPos(osl_Pos_Absolut, 0) ) return false; 3354 // get the PFB-segment lengths 3355 ThreeInts aSegmentLengths = {0,0,0}; 3356 getPfbSegmentLengths(xBuffer.get(), (int)nBytesRead, aSegmentLengths); 3357 // the lengths below are mandatory for PDF-exported Type1 fonts 3358 // because the PFB segment headers get stripped! WhyOhWhy. 3359 aLine.append( (sal_Int32)aSegmentLengths[0] ); 3360 aLine.append( "/Length2 " ); 3361 aLine.append( (sal_Int32)aSegmentLengths[1] ); 3362 aLine.append( "/Length3 " ); 3363 aLine.append( (sal_Int32)aSegmentLengths[2] ); 3364 3365 aLine.append( ">>\n" 3366 "stream\n" ); 3367 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false; 3368 if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false; 3369 3370 // emit PFB-sections without section headers 3371 beginCompression(); 3372 checkAndEnableStreamEncryption( nFontStream ); 3373 if ( !writeBuffer( &xBuffer[6], aSegmentLengths[0] ) ) return false; 3374 if ( !writeBuffer( &xBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) ) return false; 3375 if ( !writeBuffer( &xBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ) return false; 3376 } 3377 else 3378 { 3379 SAL_INFO("vcl.pdfwriter", "PDF: CreateFontSubset result in not yet supported format=" << (int)aSubsetInfo.m_nFontType); 3380 aLine.append( "0 >>\nstream\n" ); 3381 } 3382 3383 endCompression(); 3384 disableStreamEncryption(); 3385 // close the file 3386 aFontFile.close(); 3387 3388 sal_uInt64 nEndPos = 0; 3389 if ( osl::File::E_None != m_aFile.getPos(nEndPos) ) return false; 3390 // end the stream 3391 aLine.setLength( 0 ); 3392 aLine.append( "\nendstream\nendobj\n\n" ); 3393 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false; 3394 3395 // emit stream length object 3396 if ( !updateObject( nStreamLengthObject ) ) return false; 3397 aLine.setLength( 0 ); 3398 aLine.append( nStreamLengthObject ); 3399 aLine.append( " 0 obj\n" ); 3400 aLine.append( (sal_Int64)(nEndPos-nStartPos) ); 3401 aLine.append( "\nendobj\n\n" ); 3402 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false; 3403 3404 // write font descriptor 3405 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream ); 3406 3407 if( nToUnicodeStream ) 3408 nToUnicodeStream = createToUnicodeCMap( pEncoding, &aCodeUnits[0], pCodeUnitsPerGlyph, pEncToUnicodeIndex, nGlyphs ); 3409 3410 sal_Int32 nFontObject = createObject(); 3411 if ( !updateObject( nFontObject ) ) return false; 3412 aLine.setLength( 0 ); 3413 aLine.append( nFontObject ); 3414 3415 aLine.append( " 0 obj\n" ); 3416 aLine.append( (aSubsetInfo.m_nFontType & FontType::ANY_TYPE1) ? 3417 "<</Type/Font/Subtype/Type1/BaseFont/" : 3418 "<</Type/Font/Subtype/TrueType/BaseFont/" ); 3419 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine ); 3420 aLine.append( "\n" 3421 "/FirstChar 0\n" 3422 "/LastChar " ); 3423 aLine.append( (sal_Int32)(nGlyphs-1) ); 3424 aLine.append( "\n" 3425 "/Widths[" ); 3426 for( int i = 0; i < nGlyphs; i++ ) 3427 { 3428 aLine.append( pWidths[ i ] ); 3429 aLine.append( ((i & 15) == 15) ? "\n" : " " ); 3430 } 3431 aLine.append( "]\n" 3432 "/FontDescriptor " ); 3433 aLine.append( nFontDescriptor ); 3434 aLine.append( " 0 R\n" ); 3435 if( nToUnicodeStream ) 3436 { 3437 aLine.append( "/ToUnicode " ); 3438 aLine.append( nToUnicodeStream ); 3439 aLine.append( " 0 R\n" ); 3440 } 3441 aLine.append( ">>\n" 3442 "endobj\n\n" ); 3443 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return false; 3444 3445 aFontIDToObject[ lit->m_nFontID ] = nFontObject; 3446 } 3447 else 3448 { 3449 const PhysicalFontFace* pFont = it->first; 3450 OStringBuffer aErrorComment( 256 ); 3451 aErrorComment.append( "CreateFontSubset failed for font \"" ); 3452 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); 3453 aErrorComment.append( '\"' ); 3454 if( pFont->GetItalic() == ITALIC_NORMAL ) 3455 aErrorComment.append( " italic" ); 3456 else if( pFont->GetItalic() == ITALIC_OBLIQUE ) 3457 aErrorComment.append( " oblique" ); 3458 aErrorComment.append( " weight=" ); 3459 aErrorComment.append( sal_Int32(pFont->GetWeight()) ); 3460 emitComment( aErrorComment.getStr() ); 3461 } 3462 } 3463 } 3464 osl_removeFile( aTmpName.pData ); 3465 3466 // emit system fonts 3467 for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit ) 3468 { 3469 std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second ); 3470 for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit ) 3471 { 3472 if ( !fit->second ) return false; 3473 aFontIDToObject[ fit->first ] = fit->second; 3474 } 3475 } 3476 3477 OStringBuffer aFontDict( 1024 ); 3478 aFontDict.append( getFontDictObject() ); 3479 aFontDict.append( " 0 obj\n" 3480 "<<" ); 3481 int ni = 0; 3482 for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit ) 3483 { 3484 aFontDict.append( "/F" ); 3485 aFontDict.append( mit->first ); 3486 aFontDict.append( ' ' ); 3487 aFontDict.append( mit->second ); 3488 aFontDict.append( " 0 R" ); 3489 if( ((++ni) & 7) == 0 ) 3490 aFontDict.append( '\n' ); 3491 } 3492 // emit builtin font for widget appearances / variable text 3493 for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin(); 3494 it != m_aBuiltinFontToObjectMap.end(); ++it ) 3495 { 3496 PdfBuiltinFontFace aData(m_aBuiltinFonts[it->first]); 3497 it->second = emitBuiltinFont( &aData, it->second ); 3498 } 3499 appendBuiltinFontsToDict( aFontDict ); 3500 aFontDict.append( "\n>>\nendobj\n\n" ); 3501 3502 if ( !updateObject( getFontDictObject() ) ) return false; 3503 if ( !writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) ) return false; 3504 return true; 3505 } 3506 3507 sal_Int32 PDFWriterImpl::emitResources() 3508 { 3509 // emit shadings 3510 if( ! m_aGradients.empty() ) 3511 CHECK_RETURN( emitGradients() ); 3512 // emit tilings 3513 if( ! m_aTilings.empty() ) 3514 CHECK_RETURN( emitTilings() ); 3515 3516 // emit font dict 3517 CHECK_RETURN( emitFonts() ); 3518 3519 // emit Resource dict 3520 OStringBuffer aLine( 512 ); 3521 sal_Int32 nResourceDict = getResourceDictObj(); 3522 CHECK_RETURN( updateObject( nResourceDict ) ); 3523 aLine.setLength( 0 ); 3524 aLine.append( nResourceDict ); 3525 aLine.append( " 0 obj\n" ); 3526 m_aGlobalResourceDict.append( aLine, getFontDictObject() ); 3527 aLine.append( "endobj\n\n" ); 3528 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3529 return nResourceDict; 3530 } 3531 3532 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts, 3533 sal_Int32 nItemLevel, 3534 sal_Int32 nCurrentItemId ) 3535 { 3536 /* The /Count number of an item is 3537 positive: the number of visible subitems 3538 negative: the negative number of subitems that will become visible if 3539 the item gets opened 3540 see PDF ref 1.4 p 478 3541 */ 3542 3543 sal_Int32 nCount = 0; 3544 3545 if( m_aContext.OpenBookmarkLevels < 0 || // all levels are visible 3546 m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible 3547 ) 3548 { 3549 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ]; 3550 sal_Int32 nChildren = rItem.m_aChildren.size(); 3551 for( sal_Int32 i = 0; i < nChildren; i++ ) 3552 nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] ); 3553 rCounts[nCurrentItemId] = nCount; 3554 // return 1 (this item) + visible sub items 3555 if( nCount < 0 ) 3556 nCount = 0; 3557 nCount++; 3558 } 3559 else 3560 { 3561 // this bookmark level is invisible 3562 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ]; 3563 sal_Int32 nChildren = rItem.m_aChildren.size(); 3564 rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size()); 3565 for( sal_Int32 i = 0; i < nChildren; i++ ) 3566 updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] ); 3567 nCount = -1; 3568 } 3569 3570 return nCount; 3571 } 3572 3573 sal_Int32 PDFWriterImpl::emitOutline() 3574 { 3575 int i, nItems = m_aOutline.size(); 3576 3577 // do we have an outline at all ? 3578 if( nItems < 2 ) 3579 return 0; 3580 3581 // reserve object numbers for all outline items 3582 for( i = 0; i < nItems; ++i ) 3583 m_aOutline[i].m_nObject = createObject(); 3584 3585 // update all parent, next and prev object ids 3586 for( i = 0; i < nItems; ++i ) 3587 { 3588 PDFOutlineEntry& rItem = m_aOutline[i]; 3589 int nChildren = rItem.m_aChildren.size(); 3590 3591 if( nChildren ) 3592 { 3593 for( int n = 0; n < nChildren; ++n ) 3594 { 3595 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ]; 3596 3597 rChild.m_nParentObject = rItem.m_nObject; 3598 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0; 3599 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0; 3600 } 3601 3602 } 3603 } 3604 3605 // calculate Count entries for all items 3606 std::vector< sal_Int32 > aCounts( nItems ); 3607 updateOutlineItemCount( aCounts, 0, 0 ); 3608 3609 // emit hierarchy 3610 for( i = 0; i < nItems; ++i ) 3611 { 3612 PDFOutlineEntry& rItem = m_aOutline[i]; 3613 OStringBuffer aLine( 1024 ); 3614 3615 CHECK_RETURN( updateObject( rItem.m_nObject ) ); 3616 aLine.append( rItem.m_nObject ); 3617 aLine.append( " 0 obj\n" ); 3618 aLine.append( "<<" ); 3619 // number of visible children (all levels) 3620 if( i > 0 || aCounts[0] > 0 ) 3621 { 3622 aLine.append( "/Count " ); 3623 aLine.append( aCounts[i] ); 3624 } 3625 if( ! rItem.m_aChildren.empty() ) 3626 { 3627 // children list: First, Last 3628 aLine.append( "/First " ); 3629 aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject ); 3630 aLine.append( " 0 R/Last " ); 3631 aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject ); 3632 aLine.append( " 0 R\n" ); 3633 } 3634 if( i > 0 ) 3635 { 3636 // Title, Dest, Parent, Prev, Next 3637 aLine.append( "/Title" ); 3638 appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine ); 3639 aLine.append( "\n" ); 3640 // Dest is not required 3641 if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() ) 3642 { 3643 aLine.append( "/Dest" ); 3644 appendDest( rItem.m_nDestID, aLine ); 3645 } 3646 aLine.append( "/Parent " ); 3647 aLine.append( rItem.m_nParentObject ); 3648 aLine.append( " 0 R" ); 3649 if( rItem.m_nPrevObject ) 3650 { 3651 aLine.append( "/Prev " ); 3652 aLine.append( rItem.m_nPrevObject ); 3653 aLine.append( " 0 R" ); 3654 } 3655 if( rItem.m_nNextObject ) 3656 { 3657 aLine.append( "/Next " ); 3658 aLine.append( rItem.m_nNextObject ); 3659 aLine.append( " 0 R" ); 3660 } 3661 } 3662 aLine.append( ">>\nendobj\n\n" ); 3663 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3664 } 3665 3666 return m_aOutline[0].m_nObject; 3667 } 3668 3669 #undef CHECK_RETURN 3670 #define CHECK_RETURN( x ) if( !x ) return false 3671 3672 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer ) 3673 { 3674 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) 3675 { 3676 SAL_INFO("vcl.pdfwriter", "ERROR: invalid dest " << (int)nDestID << " requested"); 3677 return false; 3678 } 3679 3680 const PDFDest& rDest = m_aDests[ nDestID ]; 3681 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ]; 3682 3683 rBuffer.append( '[' ); 3684 rBuffer.append( rDestPage.m_nPageObject ); 3685 rBuffer.append( " 0 R" ); 3686 3687 switch( rDest.m_eType ) 3688 { 3689 case PDFWriter::DestAreaType::XYZ: 3690 default: 3691 rBuffer.append( "/XYZ " ); 3692 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 3693 rBuffer.append( ' ' ); 3694 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 3695 rBuffer.append( " 0" ); 3696 break; 3697 case PDFWriter::DestAreaType::FitRectangle: 3698 rBuffer.append( "/FitR " ); 3699 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 3700 rBuffer.append( ' ' ); 3701 appendFixedInt( rDest.m_aRect.Top(), rBuffer ); 3702 rBuffer.append( ' ' ); 3703 appendFixedInt( rDest.m_aRect.Right(), rBuffer ); 3704 rBuffer.append( ' ' ); 3705 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 3706 break; 3707 } 3708 rBuffer.append( ']' ); 3709 3710 return true; 3711 } 3712 3713 bool PDFWriterImpl::emitScreenAnnotations() 3714 { 3715 int nAnnots = m_aScreens.size(); 3716 for (int i = 0; i < nAnnots; i++) 3717 { 3718 const PDFScreen& rScreen = m_aScreens[i]; 3719 3720 OStringBuffer aLine; 3721 bool bEmbed = false; 3722 if (!rScreen.m_aTempFileURL.isEmpty()) 3723 { 3724 bEmbed = true; 3725 if (!updateObject(rScreen.m_nTempFileObject)) 3726 continue; 3727 3728 SvFileStream aFileStream(rScreen.m_aTempFileURL, StreamMode::READ); 3729 SvMemoryStream aMemoryStream; 3730 aMemoryStream.WriteStream(aFileStream); 3731 3732 aLine.append(rScreen.m_nTempFileObject); 3733 aLine.append(" 0 obj\n"); 3734 aLine.append("<< /Type /EmbeddedFile /Length "); 3735 aLine.append(static_cast<sal_Int64>(aMemoryStream.GetSize())); 3736 aLine.append(" >>\nstream\n"); 3737 CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength())); 3738 aLine.setLength(0); 3739 3740 CHECK_RETURN(writeBuffer(aMemoryStream.GetData(), aMemoryStream.GetSize())); 3741 3742 aLine.append("\nendstream\nendobj\n\n"); 3743 CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength())); 3744 aLine.setLength(0); 3745 } 3746 3747 if (!updateObject(rScreen.m_nObject)) 3748 continue; 3749 3750 // Annot dictionary. 3751 aLine.append(rScreen.m_nObject); 3752 aLine.append(" 0 obj\n"); 3753 aLine.append("<</Type/Annot"); 3754 aLine.append("/Subtype/Screen/Rect["); 3755 appendFixedInt(rScreen.m_aRect.Left(), aLine); 3756 aLine.append(' '); 3757 appendFixedInt(rScreen.m_aRect.Top(), aLine); 3758 aLine.append(' '); 3759 appendFixedInt(rScreen.m_aRect.Right(), aLine); 3760 aLine.append(' '); 3761 appendFixedInt(rScreen.m_aRect.Bottom(), aLine); 3762 aLine.append("]"); 3763 3764 // Action dictionary. 3765 aLine.append("/A<</Type/Action /S/Rendition /AN "); 3766 aLine.append(rScreen.m_nObject); 3767 aLine.append(" 0 R "); 3768 3769 // Rendition dictionary. 3770 aLine.append("/R<</Type/Rendition /S/MR "); 3771 3772 // MediaClip dictionary. 3773 aLine.append("/C<</Type/MediaClip /S/MCD "); 3774 if (bEmbed) 3775 { 3776 aLine.append("/D << /Type /Filespec /F (<embedded file>) /EF << /F "); 3777 aLine.append(rScreen.m_nTempFileObject); 3778 aLine.append(" 0 R >> >>"); 3779 } 3780 else 3781 { 3782 // Linked. 3783 aLine.append("/D << /Type /Filespec /FS /URL /F "); 3784 appendLiteralStringEncrypt(rScreen.m_aURL, rScreen.m_nObject, aLine, osl_getThreadTextEncoding()); 3785 aLine.append(" >>"); 3786 } 3787 // Allow playing the video via a tempfile. 3788 aLine.append("/P <</TF (TEMPACCESS)>>"); 3789 // Until the real MIME type (instead of application/vnd.sun.star.media) is available here. 3790 aLine.append("/CT (video/mp4)"); 3791 aLine.append(">>"); 3792 3793 // End Rendition dictionary by requesting play/pause/stop controls. 3794 aLine.append("/P<</BE<</C true >>>>"); 3795 aLine.append(">>"); 3796 3797 // End Action dictionary. 3798 aLine.append("/OP 0 >>"); 3799 3800 // End Annot dictionary. 3801 aLine.append("/P "); 3802 aLine.append(m_aPages[rScreen.m_nPage].m_nPageObject); 3803 aLine.append(" 0 R\n>>\nendobj\n\n"); 3804 CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength())); 3805 } 3806 3807 return true; 3808 } 3809 3810 bool PDFWriterImpl::emitLinkAnnotations() 3811 { 3812 int nAnnots = m_aLinks.size(); 3813 for( int i = 0; i < nAnnots; i++ ) 3814 { 3815 const PDFLink& rLink = m_aLinks[i]; 3816 if( ! updateObject( rLink.m_nObject ) ) 3817 continue; 3818 3819 OStringBuffer aLine( 1024 ); 3820 aLine.append( rLink.m_nObject ); 3821 aLine.append( " 0 obj\n" ); 3822 // i59651: key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' 3823 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 3824 aLine.append( "<</Type/Annot" ); 3825 if( m_bIsPDF_A1 ) 3826 aLine.append( "/F 4" ); 3827 aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" ); 3828 3829 appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle 3830 aLine.append( ' ' ); 3831 appendFixedInt( rLink.m_aRect.Top(), aLine ); 3832 aLine.append( ' ' ); 3833 appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle 3834 aLine.append( ' ' ); 3835 appendFixedInt( rLink.m_aRect.Bottom(), aLine ); 3836 aLine.append( "]" ); 3837 if( rLink.m_nDest >= 0 ) 3838 { 3839 aLine.append( "/Dest" ); 3840 appendDest( rLink.m_nDest, aLine ); 3841 } 3842 else 3843 { 3844 /* 3845 destination is external to the document, so 3846 we check in the following sequence: 3847 3848 if target type is neither .pdf, nor .od[tpgs], then 3849 check if relative or absolute and act accordingly (use URI or 'launch application' as requested) 3850 end processing 3851 else if target is .od[tpgs]: then 3852 if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file 3853 processing continue 3854 3855 if (new)target is .pdf : then 3856 if GotToR is requested, then 3857 convert the target in GoToR where the fragment of the URI is 3858 considered the named destination in the target file, set relative or absolute as requested 3859 else strip the fragment from URL and then set URI or 'launch application' as requested 3860 */ 3861 3862 // FIXME: check if the decode mechanisms for URL processing throughout this implementation 3863 // are the correct one!! 3864 3865 // extract target file type 3866 auto url(URIHelper::resolveIdnaHost(rLink.m_aURL)); 3867 3868 INetURLObject aDocumentURL( m_aContext.BaseURL ); 3869 INetURLObject aTargetURL( url ); 3870 bool bSetGoToRMode = false; 3871 bool bTargetHasPDFExtension = false; 3872 INetProtocol eTargetProtocol = aTargetURL.GetProtocol(); 3873 bool bIsUNCPath = false; 3874 3875 // check if the protocol is a known one, or if there is no protocol at all (on target only) 3876 // if there is no protocol, make the target relative to the current document directory 3877 // getting the needed URL information from the current document path 3878 if( eTargetProtocol == INetProtocol::NotValid ) 3879 { 3880 if( url.getLength() > 4 && url.startsWith("\\\\\\\\")) 3881 { 3882 bIsUNCPath = true; 3883 } 3884 else 3885 { 3886 INetURLObject aNewBase( aDocumentURL );//duplicate document URL 3887 aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the 3888 //target document 3889 aNewBase.insertName( url ); 3890 aTargetURL = aNewBase;//reassign the new target URL 3891 //recompute the target protocol, with the new URL 3892 //normal URL processing resumes 3893 eTargetProtocol = aTargetURL.GetProtocol(); 3894 } 3895 } 3896 3897 OUString aFileExtension = aTargetURL.GetFileExtension(); 3898 3899 // Check if the URL ends in '/': if yes it's a directory, 3900 // it will be forced to a URI link. 3901 // possibly a malformed URI, leave it as it is, force as URI 3902 if( aTargetURL.hasFinalSlash() ) 3903 m_aContext.DefaultLinkAction = PDFWriter::URIAction; 3904 3905 if( !aFileExtension.isEmpty() ) 3906 { 3907 if( m_aContext.ConvertOOoTargetToPDFTarget ) 3908 { 3909 bool bChangeFileExtensionToPDF = false; 3910 //examine the file type (.odm .odt. .odp, odg, ods) 3911 if( aFileExtension.equalsIgnoreAsciiCase( "odm" ) ) 3912 bChangeFileExtensionToPDF = true; 3913 if( aFileExtension.equalsIgnoreAsciiCase( "odt" ) ) 3914 bChangeFileExtensionToPDF = true; 3915 else if( aFileExtension.equalsIgnoreAsciiCase( "odp" ) ) 3916 bChangeFileExtensionToPDF = true; 3917 else if( aFileExtension.equalsIgnoreAsciiCase( "odg" ) ) 3918 bChangeFileExtensionToPDF = true; 3919 else if( aFileExtension.equalsIgnoreAsciiCase( "ods" ) ) 3920 bChangeFileExtensionToPDF = true; 3921 if( bChangeFileExtensionToPDF ) 3922 aTargetURL.setExtension("pdf" ); 3923 } 3924 //check if extension is pdf, see if GoToR should be forced 3925 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase( "pdf" ); 3926 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension ) 3927 bSetGoToRMode = true; 3928 } 3929 //prepare the URL, if relative or not 3930 INetProtocol eBaseProtocol = aDocumentURL.GetProtocol(); 3931 //queue the string common to all types of actions 3932 aLine.append( "/A<</Type/Action/S"); 3933 if( bIsUNCPath ) // handle Win UNC paths 3934 { 3935 aLine.append( "/Launch/Win<</F" ); 3936 // INetURLObject is not good with UNC paths, use original path 3937 appendLiteralStringEncrypt( url, rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 3938 aLine.append( ">>" ); 3939 } 3940 else 3941 { 3942 bool bSetRelative = false; 3943 bool bFileSpec = false; 3944 //check if relative file link is requested and if the protocol is 'file://' 3945 if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INetProtocol::File ) 3946 bSetRelative = true; 3947 3948 OUString aFragment = aTargetURL.GetMark( INetURLObject::DecodeMechanism::NONE /*DecodeMechanism::WithCharset*/ ); //fragment as is, 3949 if( !bSetGoToRMode ) 3950 { 3951 switch( m_aContext.DefaultLinkAction ) 3952 { 3953 default: 3954 case PDFWriter::URIAction : 3955 case PDFWriter::URIActionDestination : 3956 aLine.append( "/URI/URI" ); 3957 break; 3958 case PDFWriter::LaunchAction: 3959 // now: 3960 // if a launch action is requested and the hyperlink target has a fragment 3961 // and the target file does not have a pdf extension, or it's not a 'file:://' 3962 // protocol then force the uri action on it 3963 // This code will permit the correct opening of application on web pages, 3964 // the one that normally have fragments (but I may be wrong...) 3965 // and will force the use of URI when the protocol is not file: 3966 if( (!aFragment.isEmpty() && !bTargetHasPDFExtension) || 3967 eTargetProtocol != INetProtocol::File ) 3968 { 3969 aLine.append( "/URI/URI" ); 3970 } 3971 else 3972 { 3973 aLine.append( "/Launch/F" ); 3974 bFileSpec = true; 3975 } 3976 break; 3977 } 3978 } 3979 3980 //fragment are encoded in the same way as in the named destination processing 3981 if( bSetGoToRMode ) 3982 { 3983 //add the fragment 3984 OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DecodeMechanism::WithCharset ); 3985 aLine.append("/GoToR"); 3986 aLine.append("/F"); 3987 appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark, 3988 INetURLObject::EncodeMechanism::WasEncoded, 3989 INetURLObject::DecodeMechanism::WithCharset ) : 3990 aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 3991 if( !aFragment.isEmpty() ) 3992 { 3993 aLine.append("/D/"); 3994 appendDestinationName( aFragment , aLine ); 3995 } 3996 } 3997 else 3998 { 3999 // change the fragment to accommodate the bookmark (only if the file extension 4000 // is PDF and the requested action is of the correct type) 4001 if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination && 4002 bTargetHasPDFExtension && !aFragment.isEmpty() ) 4003 { 4004 OStringBuffer aLineLoc( 1024 ); 4005 appendDestinationName( aFragment , aLineLoc ); 4006 //substitute the fragment 4007 aTargetURL.SetMark( OStringToOUString(aLineLoc.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US) ); 4008 } 4009 OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DecodeMechanism::WithCharset : INetURLObject::DecodeMechanism::NONE ); 4010 appendLiteralStringEncrypt(bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL, 4011 INetURLObject::EncodeMechanism::WasEncoded, 4012 bFileSpec ? INetURLObject::DecodeMechanism::WithCharset : INetURLObject::DecodeMechanism::NONE 4013 ) : 4014 aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 4015 } 4016 } 4017 aLine.append( ">>\n" ); 4018 } 4019 if( rLink.m_nStructParent > 0 ) 4020 { 4021 aLine.append( "/StructParent " ); 4022 aLine.append( rLink.m_nStructParent ); 4023 } 4024 aLine.append( ">>\nendobj\n\n" ); 4025 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4026 } 4027 4028 return true; 4029 } 4030 4031 bool PDFWriterImpl::emitNoteAnnotations() 4032 { 4033 // emit note annotations 4034 int nAnnots = m_aNotes.size(); 4035 for( int i = 0; i < nAnnots; i++ ) 4036 { 4037 const PDFNoteEntry& rNote = m_aNotes[i]; 4038 if( ! updateObject( rNote.m_nObject ) ) 4039 return false; 4040 4041 OStringBuffer aLine( 1024 ); 4042 aLine.append( rNote.m_nObject ); 4043 aLine.append( " 0 obj\n" ); 4044 // i59651: key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' 4045 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 4046 aLine.append( "<</Type/Annot" ); 4047 if( m_bIsPDF_A1 ) 4048 aLine.append( "/F 4" ); 4049 aLine.append( "/Subtype/Text/Rect[" ); 4050 4051 appendFixedInt( rNote.m_aRect.Left(), aLine ); 4052 aLine.append( ' ' ); 4053 appendFixedInt( rNote.m_aRect.Top(), aLine ); 4054 aLine.append( ' ' ); 4055 appendFixedInt( rNote.m_aRect.Right(), aLine ); 4056 aLine.append( ' ' ); 4057 appendFixedInt( rNote.m_aRect.Bottom(), aLine ); 4058 aLine.append( "]" ); 4059 4060 // contents of the note (type text string) 4061 aLine.append( "/Contents\n" ); 4062 appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine ); 4063 aLine.append( "\n" ); 4064 4065 // optional title 4066 if( !rNote.m_aContents.Title.isEmpty() ) 4067 { 4068 aLine.append( "/T" ); 4069 appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine ); 4070 aLine.append( "\n" ); 4071 } 4072 4073 aLine.append( ">>\nendobj\n\n" ); 4074 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4075 } 4076 return true; 4077 } 4078 4079 Font PDFWriterImpl::replaceFont( const vcl::Font& rControlFont, const vcl::Font& rAppSetFont ) 4080 { 4081 bool bAdjustSize = false; 4082 4083 Font aFont( rControlFont ); 4084 if( aFont.GetFamilyName().isEmpty() ) 4085 { 4086 aFont = rAppSetFont; 4087 if( rControlFont.GetFontHeight() ) 4088 aFont.SetFontSize( Size( 0, rControlFont.GetFontHeight() ) ); 4089 else 4090 bAdjustSize = true; 4091 if( rControlFont.GetItalic() != ITALIC_DONTKNOW ) 4092 aFont.SetItalic( rControlFont.GetItalic() ); 4093 if( rControlFont.GetWeight() != WEIGHT_DONTKNOW ) 4094 aFont.SetWeight( rControlFont.GetWeight() ); 4095 } 4096 else if( ! aFont.GetFontHeight() ) 4097 { 4098 aFont.SetFontSize( rAppSetFont.GetFontSize() ); 4099 bAdjustSize = true; 4100 } 4101 if( bAdjustSize ) 4102 { 4103 Size aFontSize = aFont.GetFontSize(); 4104 OutputDevice* pDefDev = Application::GetDefaultDevice(); 4105 aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() ); 4106 aFont.SetFontSize( aFontSize ); 4107 } 4108 return aFont; 4109 } 4110 4111 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const vcl::Font& rFont ) 4112 { 4113 sal_Int32 nBest = 4; // default to Helvetica 4114 OUString aFontName( rFont.GetFamilyName() ); 4115 aFontName = aFontName.toAsciiLowerCase(); 4116 4117 if( aFontName.indexOf( "times" ) != -1 ) 4118 nBest = 8; 4119 else if( aFontName.indexOf( "courier" ) != -1 ) 4120 nBest = 0; 4121 else if( aFontName.indexOf( "dingbats" ) != -1 ) 4122 nBest = 13; 4123 else if( aFontName.indexOf( "symbol" ) != -1 ) 4124 nBest = 12; 4125 if( nBest < 12 ) 4126 { 4127 if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL ) 4128 nBest += 1; 4129 if( rFont.GetWeight() > WEIGHT_MEDIUM ) 4130 nBest += 2; 4131 } 4132 4133 if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() ) 4134 m_aBuiltinFontToObjectMap[ nBest ] = createObject(); 4135 4136 return nBest; 4137 } 4138 4139 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 ) 4140 { 4141 return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1; 4142 } 4143 4144 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget ) 4145 { 4146 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 4147 4148 // save graphics state 4149 push( PushFlags::ALL ); 4150 4151 // transform relative to control's coordinates since an 4152 // appearance stream is a form XObject 4153 // this relies on the m_aRect member of rButton NOT already being transformed 4154 // to default user space 4155 if( rWidget.Background || rWidget.Border ) 4156 { 4157 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) ); 4158 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) ); 4159 drawRectangle( rWidget.Location ); 4160 } 4161 // prepare font to use 4162 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() ); 4163 setFont( aFont ); 4164 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) ); 4165 4166 drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle ); 4167 4168 // create DA string while local mapmode is still in place 4169 // (that is before endRedirect()) 4170 OStringBuffer aDA( 256 ); 4171 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA ); 4172 Font aDummyFont( "Helvetica", aFont.GetFontSize() ); 4173 sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont ); 4174 aDA.append( ' ' ); 4175 aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() ); 4176 aDA.append( ' ' ); 4177 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetFontHeight() ), aDA ); 4178 aDA.append( " Tf" ); 4179 rButton.m_aDAString = aDA.makeStringAndClear(); 4180 4181 pop(); 4182 4183 rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream(); 4184 4185 /* seems like a bad hack but at least works in both AR5 and 6: 4186 we draw the button ourselves and tell AR 4187 the button would be totally transparent with no text 4188 4189 One would expect that simply setting a normal appearance 4190 should suffice, but no, as soon as the user actually presses 4191 the button and an action is tied to it (gasp! a button that 4192 does something) the appearance gets replaced by some crap that AR 4193 creates on the fly even if no DA or MK is given. On AR6 at least 4194 the DA and MK work as expected, but on AR5 this creates a region 4195 filled with the background color but nor text. Urgh. 4196 */ 4197 rButton.m_aMKDict = "/BC [] /BG [] /CA"; 4198 rButton.m_aMKDictCAString = ""; 4199 } 4200 4201 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern, 4202 const PDFWriter::AnyWidget& rWidget, 4203 const StyleSettings& rSettings ) 4204 { 4205 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() ); 4206 4207 if( rWidget.Background || rWidget.Border ) 4208 { 4209 if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) ) 4210 { 4211 sal_Int32 nDelta = getReferenceDevice()->GetDPIX() / 500; 4212 if( nDelta < 1 ) 4213 nDelta = 1; 4214 setLineColor( Color( COL_TRANSPARENT ) ); 4215 tools::Rectangle aRect = rIntern.m_aRect; 4216 setFillColor( rSettings.GetLightBorderColor() ); 4217 drawRectangle( aRect ); 4218 aRect.Left() += nDelta; aRect.Top() += nDelta; 4219 aRect.Right() -= nDelta; aRect.Bottom() -= nDelta; 4220 setFillColor( rSettings.GetFieldColor() ); 4221 drawRectangle( aRect ); 4222 setFillColor( rSettings.GetLightColor() ); 4223 drawRectangle( tools::Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) ); 4224 drawRectangle( tools::Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) ); 4225 setFillColor( rSettings.GetDarkShadowColor() ); 4226 drawRectangle( tools::Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) ); 4227 drawRectangle( tools::Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) ); 4228 } 4229 else 4230 { 4231 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) ); 4232 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 4233 drawRectangle( rIntern.m_aRect ); 4234 } 4235 4236 if( rWidget.Border ) 4237 { 4238 // adjust edit area accounting for border 4239 sal_Int32 nDelta = aFont.GetFontHeight()/4; 4240 if( nDelta < 1 ) 4241 nDelta = 1; 4242 rIntern.m_aRect.Left() += nDelta; 4243 rIntern.m_aRect.Top() += nDelta; 4244 rIntern.m_aRect.Right() -= nDelta; 4245 rIntern.m_aRect.Bottom()-= nDelta; 4246 } 4247 } 4248 return aFont; 4249 } 4250 4251 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget ) 4252 { 4253 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 4254 SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 ); 4255 4256 push( PushFlags::ALL ); 4257 4258 // prepare font to use, draw field border 4259 Font aFont = drawFieldBorder( rEdit, rWidget, rSettings ); 4260 sal_Int32 nBest = getSystemFont( aFont ); 4261 4262 // prepare DA string 4263 OStringBuffer aDA( 32 ); 4264 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA ); 4265 aDA.append( ' ' ); 4266 aDA.append( "/F" ); 4267 aDA.append( nBest ); 4268 4269 OStringBuffer aDR( 32 ); 4270 aDR.append( "/Font " ); 4271 aDR.append( getFontDictObject() ); 4272 aDR.append( " 0 R" ); 4273 rEdit.m_aDRDict = aDR.makeStringAndClear(); 4274 aDA.append( ' ' ); 4275 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetFontHeight() ), aDA ); 4276 aDA.append( " Tf" ); 4277 4278 /* create an empty appearance stream, let the viewer create 4279 the appearance at runtime. This is because AR5 seems to 4280 paint the widget appearance always, and a dynamically created 4281 appearance on top of it. AR6 is well behaved in that regard, so 4282 that behaviour seems to be a bug. Anyway this empty appearance 4283 relies on /NeedAppearances in the AcroForm dictionary set to "true" 4284 */ 4285 beginRedirect( pEditStream, rEdit.m_aRect ); 4286 OStringBuffer aAppearance( 32 ); 4287 aAppearance.append( "/Tx BMC\nEMC\n" ); 4288 writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); 4289 4290 endRedirect(); 4291 pop(); 4292 4293 rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream; 4294 4295 rEdit.m_aDAString = aDA.makeStringAndClear(); 4296 } 4297 4298 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget ) 4299 { 4300 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 4301 SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 ); 4302 4303 push( PushFlags::ALL ); 4304 4305 // prepare font to use, draw field border 4306 Font aFont = drawFieldBorder( rBox, rWidget, rSettings ); 4307 sal_Int32 nBest = getSystemFont( aFont ); 4308 4309 beginRedirect( pListBoxStream, rBox.m_aRect ); 4310 OStringBuffer aAppearance( 64 ); 4311 4312 setLineColor( Color( COL_TRANSPARENT ) ); 4313 setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) ); 4314 drawRectangle( rBox.m_aRect ); 4315 4316 // empty appearance, see createDefaultEditAppearance for reference 4317 aAppearance.append( "/Tx BMC\nEMC\n" ); 4318 writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); 4319 4320 endRedirect(); 4321 pop(); 4322 4323 rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream; 4324 4325 // prepare DA string 4326 OStringBuffer aDA( 256 ); 4327 // prepare DA string 4328 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA ); 4329 aDA.append( ' ' ); 4330 aDA.append( "/F" ); 4331 aDA.append( nBest ); 4332 4333 OStringBuffer aDR( 32 ); 4334 aDR.append( "/Font " ); 4335 aDR.append( getFontDictObject() ); 4336 aDR.append( " 0 R" ); 4337 rBox.m_aDRDict = aDR.makeStringAndClear(); 4338 aDA.append( ' ' ); 4339 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetFontHeight() ), aDA ); 4340 aDA.append( " Tf" ); 4341 rBox.m_aDAString = aDA.makeStringAndClear(); 4342 } 4343 4344 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget ) 4345 { 4346 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 4347 4348 // save graphics state 4349 push( PushFlags::ALL ); 4350 4351 if( rWidget.Background || rWidget.Border ) 4352 { 4353 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) ); 4354 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 4355 drawRectangle( rBox.m_aRect ); 4356 } 4357 4358 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() ); 4359 setFont( aFont ); 4360 Size aFontSize = aFont.GetFontSize(); 4361 if( aFontSize.Height() > rBox.m_aRect.GetHeight() ) 4362 aFontSize.Height() = rBox.m_aRect.GetHeight(); 4363 sal_Int32 nDelta = aFontSize.Height()/10; 4364 if( nDelta < 1 ) 4365 nDelta = 1; 4366 4367 tools::Rectangle aCheckRect, aTextRect; 4368 { 4369 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta; 4370 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 4371 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 4372 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 4373 4374 // #i74206# handle small controls without text area 4375 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 4376 { 4377 aCheckRect.Right() -= nDelta; 4378 aCheckRect.Top() += nDelta/2; 4379 aCheckRect.Bottom() -= nDelta - (nDelta/2); 4380 } 4381 4382 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta; 4383 aTextRect.Top() = rBox.m_aRect.Top(); 4384 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 4385 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 4386 } 4387 setLineColor( Color( COL_BLACK ) ); 4388 setFillColor( Color( COL_TRANSPARENT ) ); 4389 OStringBuffer aLW( 32 ); 4390 aLW.append( "q " ); 4391 m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW ); 4392 aLW.append( " w " ); 4393 writeBuffer( aLW.getStr(), aLW.getLength() ); 4394 drawRectangle( aCheckRect ); 4395 writeBuffer( " Q\n", 3 ); 4396 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 4397 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle ); 4398 4399 pop(); 4400 4401 OStringBuffer aDA( 256 ); 4402 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 4403 sal_Int32 nBest = getBestBuiltinFont( Font( "ZapfDingbats", aFont.GetFontSize() ) ); 4404 aDA.append( ' ' ); 4405 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 4406 aDA.append( " 0 Tf" ); 4407 rBox.m_aDAString = aDA.makeStringAndClear(); 4408 rBox.m_aMKDict = "/CA"; 4409 rBox.m_aMKDictCAString = "8"; 4410 rBox.m_aRect = aCheckRect; 4411 4412 // create appearance streams 4413 sal_Char cMark = '8'; 4414 sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)]; 4415 nCharXOffset *= aCheckRect.GetHeight(); 4416 nCharXOffset /= 2000; 4417 sal_Int32 nCharYOffset = 1000- 4418 (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative 4419 nCharYOffset *= aCheckRect.GetHeight(); 4420 nCharYOffset /= 2000; 4421 4422 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 ); 4423 beginRedirect( pCheckStream, aCheckRect ); 4424 aDA.append( "/Tx BMC\nq BT\n" ); 4425 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 4426 aDA.append( ' ' ); 4427 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 4428 aDA.append( ' ' ); 4429 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA ); 4430 aDA.append( " Tf\n" ); 4431 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA ); 4432 aDA.append( " " ); 4433 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA ); 4434 aDA.append( " Td (" ); 4435 aDA.append( cMark ); 4436 aDA.append( ") Tj\nET\nQ\nEMC\n" ); 4437 writeBuffer( aDA.getStr(), aDA.getLength() ); 4438 endRedirect(); 4439 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream; 4440 4441 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 ); 4442 beginRedirect( pUncheckStream, aCheckRect ); 4443 writeBuffer( "/Tx BMC\nEMC\n", 12 ); 4444 endRedirect(); 4445 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream; 4446 } 4447 4448 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget ) 4449 { 4450 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 4451 4452 // save graphics state 4453 push( PushFlags::ALL ); 4454 4455 if( rWidget.Background || rWidget.Border ) 4456 { 4457 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) ); 4458 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 4459 drawRectangle( rBox.m_aRect ); 4460 } 4461 4462 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() ); 4463 setFont( aFont ); 4464 Size aFontSize = aFont.GetFontSize(); 4465 if( aFontSize.Height() > rBox.m_aRect.GetHeight() ) 4466 aFontSize.Height() = rBox.m_aRect.GetHeight(); 4467 sal_Int32 nDelta = aFontSize.Height()/10; 4468 if( nDelta < 1 ) 4469 nDelta = 1; 4470 4471 tools::Rectangle aCheckRect, aTextRect; 4472 { 4473 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta; 4474 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 4475 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 4476 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 4477 4478 // #i74206# handle small controls without text area 4479 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 4480 { 4481 aCheckRect.Right() -= nDelta; 4482 aCheckRect.Top() += nDelta/2; 4483 aCheckRect.Bottom() -= nDelta - (nDelta/2); 4484 } 4485 4486 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta; 4487 aTextRect.Top() = rBox.m_aRect.Top(); 4488 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 4489 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 4490 } 4491 setLineColor( Color( COL_BLACK ) ); 4492 setFillColor( Color( COL_TRANSPARENT ) ); 4493 OStringBuffer aLW( 32 ); 4494 aLW.append( "q " ); 4495 m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW ); 4496 aLW.append( " w " ); 4497 writeBuffer( aLW.getStr(), aLW.getLength() ); 4498 drawEllipse( aCheckRect ); 4499 writeBuffer( " Q\n", 3 ); 4500 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 4501 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle ); 4502 4503 pop(); 4504 4505 OStringBuffer aDA( 256 ); 4506 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 4507 sal_Int32 nBest = getBestBuiltinFont( Font( "ZapfDingbats", aFont.GetFontSize() ) ); 4508 aDA.append( ' ' ); 4509 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 4510 aDA.append( " 0 Tf" ); 4511 rBox.m_aDAString = aDA.makeStringAndClear(); 4512 //to encrypt this (el) 4513 rBox.m_aMKDict = "/CA"; 4514 //after this assignement, to m_aMKDic cannot be added anything 4515 rBox.m_aMKDictCAString = "l"; 4516 4517 rBox.m_aRect = aCheckRect; 4518 4519 // create appearance streams 4520 push( PushFlags::ALL); 4521 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 ); 4522 4523 beginRedirect( pCheckStream, aCheckRect ); 4524 aDA.append( "/Tx BMC\nq BT\n" ); 4525 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 4526 aDA.append( ' ' ); 4527 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 4528 aDA.append( ' ' ); 4529 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA ); 4530 aDA.append( " Tf\n0 0 Td\nET\nQ\n" ); 4531 writeBuffer( aDA.getStr(), aDA.getLength() ); 4532 setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 4533 setLineColor( Color( COL_TRANSPARENT ) ); 4534 aCheckRect.Left() += 3*nDelta; 4535 aCheckRect.Top() += 3*nDelta; 4536 aCheckRect.Bottom() -= 3*nDelta; 4537 aCheckRect.Right() -= 3*nDelta; 4538 drawEllipse( aCheckRect ); 4539 writeBuffer( "\nEMC\n", 5 ); 4540 endRedirect(); 4541 4542 pop(); 4543 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream; 4544 4545 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 ); 4546 beginRedirect( pUncheckStream, aCheckRect ); 4547 writeBuffer( "/Tx BMC\nEMC\n", 12 ); 4548 endRedirect(); 4549 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream; 4550 } 4551 4552 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict ) 4553 { 4554 // TODO: check and insert default streams 4555 OString aStandardAppearance; 4556 switch( rWidget.m_eType ) 4557 { 4558 case PDFWriter::CheckBox: 4559 aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US ); 4560 break; 4561 default: 4562 break; 4563 } 4564 4565 if( !rWidget.m_aAppearances.empty() ) 4566 { 4567 rAnnotDict.append( "/AP<<\n" ); 4568 for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it ) 4569 { 4570 rAnnotDict.append( "/" ); 4571 rAnnotDict.append( dict_it->first ); 4572 bool bUseSubDict = (dict_it->second.size() > 1); 4573 rAnnotDict.append( bUseSubDict ? "<<" : " " ); 4574 4575 for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin(); 4576 stream_it != dict_it->second.end(); ++stream_it ) 4577 { 4578 SvMemoryStream* pApppearanceStream = stream_it->second; 4579 dict_it->second[ stream_it->first ] = nullptr; 4580 4581 bool bDeflate = compressStream( pApppearanceStream ); 4582 4583 pApppearanceStream->Seek( STREAM_SEEK_TO_END ); 4584 sal_Int64 nStreamLen = pApppearanceStream->Tell(); 4585 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN ); 4586 sal_Int32 nObject = createObject(); 4587 CHECK_RETURN( updateObject( nObject ) ); 4588 if (g_bDebugDisableCompression) 4589 { 4590 emitComment( "PDFWriterImpl::emitAppearances" ); 4591 } 4592 OStringBuffer aLine; 4593 aLine.append( nObject ); 4594 4595 aLine.append( " 0 obj\n" 4596 "<</Type/XObject\n" 4597 "/Subtype/Form\n" 4598 "/BBox[0 0 " ); 4599 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine ); 4600 aLine.append( " " ); 4601 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine ); 4602 aLine.append( "]\n" 4603 "/Resources " ); 4604 aLine.append( getResourceDictObj() ); 4605 aLine.append( " 0 R\n" 4606 "/Length " ); 4607 aLine.append( nStreamLen ); 4608 aLine.append( "\n" ); 4609 if( bDeflate ) 4610 aLine.append( "/Filter/FlateDecode\n" ); 4611 aLine.append( ">>\nstream\n" ); 4612 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4613 checkAndEnableStreamEncryption( nObject ); 4614 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) ); 4615 disableStreamEncryption(); 4616 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) ); 4617 4618 if( bUseSubDict ) 4619 { 4620 rAnnotDict.append( " /" ); 4621 rAnnotDict.append( stream_it->first ); 4622 rAnnotDict.append( " " ); 4623 } 4624 rAnnotDict.append( nObject ); 4625 rAnnotDict.append( " 0 R" ); 4626 4627 delete pApppearanceStream; 4628 } 4629 4630 rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" ); 4631 } 4632 rAnnotDict.append( ">>\n" ); 4633 if( !aStandardAppearance.isEmpty() ) 4634 { 4635 rAnnotDict.append( "/AS /" ); 4636 rAnnotDict.append( aStandardAppearance ); 4637 rAnnotDict.append( "\n" ); 4638 } 4639 } 4640 4641 return true; 4642 } 4643 4644 bool PDFWriterImpl::emitWidgetAnnotations() 4645 { 4646 ensureUniqueRadioOnValues(); 4647 4648 int nAnnots = m_aWidgets.size(); 4649 for( int a = 0; a < nAnnots; a++ ) 4650 { 4651 PDFWidget& rWidget = m_aWidgets[a]; 4652 4653 OStringBuffer aLine( 1024 ); 4654 OStringBuffer aValue( 256 ); 4655 aLine.append( rWidget.m_nObject ); 4656 aLine.append( " 0 obj\n" 4657 "<<" ); 4658 if( rWidget.m_eType != PDFWriter::Hierarchy ) 4659 { 4660 // emit widget annotation only for terminal fields 4661 if( rWidget.m_aKids.empty() ) 4662 { 4663 int iRectMargin; 4664 4665 aLine.append( "/Type/Annot/Subtype/Widget/F " ); 4666 4667 if (rWidget.m_eType == PDFWriter::Signature) 4668 { 4669 aLine.append( "132\n" ); // Print & Locked 4670 iRectMargin = 0; 4671 } 4672 else 4673 { 4674 aLine.append( "4\n" ); 4675 iRectMargin = 1; 4676 } 4677 4678 aLine.append("/Rect[" ); 4679 appendFixedInt( rWidget.m_aRect.Left()-iRectMargin, aLine ); 4680 aLine.append( ' ' ); 4681 appendFixedInt( rWidget.m_aRect.Top()+iRectMargin, aLine ); 4682 aLine.append( ' ' ); 4683 appendFixedInt( rWidget.m_aRect.Right()+iRectMargin, aLine ); 4684 aLine.append( ' ' ); 4685 appendFixedInt( rWidget.m_aRect.Bottom()-iRectMargin, aLine ); 4686 aLine.append( "]\n" ); 4687 } 4688 aLine.append( "/FT/" ); 4689 switch( rWidget.m_eType ) 4690 { 4691 case PDFWriter::RadioButton: 4692 case PDFWriter::CheckBox: 4693 // for radio buttons only the RadioButton field, not the 4694 // CheckBox children should have a value, else acrobat reader 4695 // does not always check the right button 4696 // of course real check boxes (not belonging to a radio group) 4697 // need their values, too 4698 if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 ) 4699 { 4700 aValue.append( "/" ); 4701 // check for radio group with all buttons unpressed 4702 if( rWidget.m_aValue.isEmpty() ) 4703 aValue.append( "Off" ); 4704 else 4705 appendName( rWidget.m_aValue, aValue ); 4706 } 4707 SAL_FALLTHROUGH; 4708 case PDFWriter::PushButton: 4709 aLine.append( "Btn" ); 4710 break; 4711 case PDFWriter::ListBox: 4712 if( rWidget.m_nFlags & 0x200000 ) // multiselect 4713 { 4714 aValue.append( "[" ); 4715 for( size_t i = 0; i < rWidget.m_aSelectedEntries.size(); i++ ) 4716 { 4717 sal_Int32 nEntry = rWidget.m_aSelectedEntries[i]; 4718 if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) ) 4719 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue ); 4720 } 4721 aValue.append( "]" ); 4722 } 4723 else if( rWidget.m_aSelectedEntries.size() > 0 && 4724 rWidget.m_aSelectedEntries[0] >= 0 && 4725 rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) ) 4726 { 4727 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue ); 4728 } 4729 else 4730 appendUnicodeTextStringEncrypt( OUString(), rWidget.m_nObject, aValue ); 4731 aLine.append( "Ch" ); 4732 break; 4733 case PDFWriter::ComboBox: 4734 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue ); 4735 aLine.append( "Ch" ); 4736 break; 4737 case PDFWriter::Edit: 4738 aLine.append( "Tx" ); 4739 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue ); 4740 break; 4741 case PDFWriter::Signature: 4742 aLine.append( "Sig" ); 4743 aValue.append(OUStringToOString(rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US)); 4744 break; 4745 case PDFWriter::Hierarchy: // make the compiler happy 4746 break; 4747 } 4748 aLine.append( "\n" ); 4749 aLine.append( "/P " ); 4750 aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject ); 4751 aLine.append( " 0 R\n" ); 4752 } 4753 if( rWidget.m_nParent ) 4754 { 4755 aLine.append( "/Parent " ); 4756 aLine.append( rWidget.m_nParent ); 4757 aLine.append( " 0 R\n" ); 4758 } 4759 if( rWidget.m_aKids.size() ) 4760 { 4761 aLine.append( "/Kids[" ); 4762 for( size_t i = 0; i < rWidget.m_aKids.size(); i++ ) 4763 { 4764 aLine.append( rWidget.m_aKids[i] ); 4765 aLine.append( " 0 R" ); 4766 aLine.append( ( (i&15) == 15 ) ? "\n" : " " ); 4767 } 4768 aLine.append( "]\n" ); 4769 } 4770 if( !rWidget.m_aName.isEmpty() ) 4771 { 4772 aLine.append( "/T" ); 4773 appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine ); 4774 aLine.append( "\n" ); 4775 } 4776 if( m_aContext.Version > PDFWriter::PDFVersion::PDF_1_2 && !rWidget.m_aDescription.isEmpty() ) 4777 { 4778 // the alternate field name should be unicode able since it is 4779 // supposed to be used in UI 4780 aLine.append( "/TU" ); 4781 appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine ); 4782 aLine.append( "\n" ); 4783 } 4784 4785 if( rWidget.m_nFlags ) 4786 { 4787 aLine.append( "/Ff " ); 4788 aLine.append( rWidget.m_nFlags ); 4789 aLine.append( "\n" ); 4790 } 4791 if( !aValue.isEmpty() ) 4792 { 4793 OString aVal = aValue.makeStringAndClear(); 4794 aLine.append( "/V " ); 4795 aLine.append( aVal ); 4796 aLine.append( "\n" 4797 "/DV " ); 4798 aLine.append( aVal ); 4799 aLine.append( "\n" ); 4800 } 4801 if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox ) 4802 { 4803 sal_Int32 nTI = -1; 4804 aLine.append( "/Opt[\n" ); 4805 sal_Int32 i = 0; 4806 for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i ) 4807 { 4808 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine ); 4809 aLine.append( "\n" ); 4810 if( *it == rWidget.m_aValue ) 4811 nTI = i; 4812 } 4813 aLine.append( "]\n" ); 4814 if( nTI > 0 ) 4815 { 4816 aLine.append( "/TI " ); 4817 aLine.append( nTI ); 4818 aLine.append( "\n" ); 4819 if( rWidget.m_nFlags & 0x200000 ) // Multiselect 4820 { 4821 aLine.append( "/I [" ); 4822 aLine.append( nTI ); 4823 aLine.append( "]\n" ); 4824 } 4825 } 4826 } 4827 if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 ) 4828 { 4829 aLine.append( "/MaxLen " ); 4830 aLine.append( rWidget.m_nMaxLen ); 4831 aLine.append( "\n" ); 4832 } 4833 if( rWidget.m_eType == PDFWriter::PushButton ) 4834 { 4835 if(!m_bIsPDF_A1) 4836 { 4837 OStringBuffer aDest; 4838 if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) ) 4839 { 4840 aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " ); 4841 aLine.append( aDest.makeStringAndClear() ); 4842 aLine.append( ">>>>\n" ); 4843 } 4844 else if( rWidget.m_aListEntries.empty() ) 4845 { 4846 // create a reset form action 4847 aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" ); 4848 } 4849 else if( rWidget.m_bSubmit ) 4850 { 4851 // create a submit form action 4852 aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" ); 4853 appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() ); 4854 aLine.append( "/Flags " ); 4855 4856 sal_Int32 nFlags = 0; 4857 switch( m_aContext.SubmitFormat ) 4858 { 4859 case PDFWriter::HTML: 4860 nFlags |= 4; 4861 break; 4862 case PDFWriter::XML: 4863 if( m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 ) 4864 nFlags |= 32; 4865 break; 4866 case PDFWriter::PDF: 4867 if( m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 ) 4868 nFlags |= 256; 4869 break; 4870 case PDFWriter::FDF: 4871 default: 4872 break; 4873 } 4874 if( rWidget.m_bSubmitGet ) 4875 nFlags |= 8; 4876 aLine.append( nFlags ); 4877 aLine.append( ">>>>\n" ); 4878 } 4879 else 4880 { 4881 // create a URI action 4882 aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" ); 4883 aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) ); 4884 aLine.append( ")>>>>\n" ); 4885 } 4886 } 4887 else 4888 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA ); 4889 } 4890 if( !rWidget.m_aDAString.isEmpty() ) 4891 { 4892 if( !rWidget.m_aDRDict.isEmpty() ) 4893 { 4894 aLine.append( "/DR<<" ); 4895 aLine.append( rWidget.m_aDRDict ); 4896 aLine.append( ">>\n" ); 4897 } 4898 else 4899 { 4900 aLine.append( "/DR<</Font<<" ); 4901 appendBuiltinFontsToDict( aLine ); 4902 aLine.append( ">>>>\n" ); 4903 } 4904 aLine.append( "/DA" ); 4905 appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine ); 4906 aLine.append( "\n" ); 4907 if( rWidget.m_nTextStyle & DrawTextFlags::Center ) 4908 aLine.append( "/Q 1\n" ); 4909 else if( rWidget.m_nTextStyle & DrawTextFlags::Right ) 4910 aLine.append( "/Q 2\n" ); 4911 } 4912 // appearance characteristics for terminal fields 4913 // which are supposed to have an appearance constructed 4914 // by the viewer application 4915 if( !rWidget.m_aMKDict.isEmpty() ) 4916 { 4917 aLine.append( "/MK<<" ); 4918 aLine.append( rWidget.m_aMKDict ); 4919 //add the CA string, encrypting it 4920 appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine); 4921 aLine.append( ">>\n" ); 4922 } 4923 4924 CHECK_RETURN( emitAppearances( rWidget, aLine ) ); 4925 4926 aLine.append( ">>\n" 4927 "endobj\n\n" ); 4928 CHECK_RETURN( updateObject( rWidget.m_nObject ) ); 4929 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4930 } 4931 return true; 4932 } 4933 4934 bool PDFWriterImpl::emitAnnotations() 4935 { 4936 if( m_aPages.size() < 1 ) 4937 return false; 4938 4939 CHECK_RETURN( emitLinkAnnotations() ); 4940 CHECK_RETURN(emitScreenAnnotations()); 4941 CHECK_RETURN( emitNoteAnnotations() ); 4942 CHECK_RETURN( emitWidgetAnnotations() ); 4943 4944 return true; 4945 } 4946 4947 bool PDFWriterImpl::emitEmbeddedFiles() 4948 { 4949 for (auto& rEmbeddedFile : m_aEmbeddedFiles) 4950 { 4951 if (!updateObject(rEmbeddedFile.m_nObject)) 4952 continue; 4953 4954 OStringBuffer aLine; 4955 aLine.append(rEmbeddedFile.m_nObject); 4956 aLine.append(" 0 obj\n"); 4957 aLine.append("<< /Type /EmbeddedFile /Length "); 4958 aLine.append(static_cast<sal_Int64>(rEmbeddedFile.m_aData.getLength())); 4959 aLine.append(" >>\nstream\n"); 4960 CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength())); 4961 aLine.setLength(0); 4962 4963 CHECK_RETURN(writeBuffer(rEmbeddedFile.m_aData.getArray(), rEmbeddedFile.m_aData.getLength())); 4964 4965 aLine.append("\nendstream\nendobj\n\n"); 4966 CHECK_RETURN(writeBuffer(aLine.getStr(), aLine.getLength())); 4967 } 4968 return true; 4969 } 4970 4971 #undef CHECK_RETURN 4972 #define CHECK_RETURN( x ) if( !x ) return false 4973 4974 bool PDFWriterImpl::emitCatalog() 4975 { 4976 // build page tree 4977 // currently there is only one node that contains all leaves 4978 4979 // first create a page tree node id 4980 sal_Int32 nTreeNode = createObject(); 4981 4982 // emit global resource dictionary (page emit needs it) 4983 CHECK_RETURN( emitResources() ); 4984 4985 // emit all pages 4986 for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it ) 4987 if( ! it->emit( nTreeNode ) ) 4988 return false; 4989 4990 sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations(); 4991 4992 sal_Int32 nOutlineDict = emitOutline(); 4993 4994 // emit Output intent 4995 sal_Int32 nOutputIntentObject = emitOutputIntent(); 4996 4997 // emit metadata 4998 sal_Int32 nMetadataObject = emitDocumentMetadata(); 4999 5000 sal_Int32 nStructureDict = 0; 5001 if(m_aStructure.size() > 1) 5002 { 5003 // check if dummy structure containers are needed 5004 addInternalStructureContainer(m_aStructure[0]); 5005 nStructureDict = m_aStructure[0].m_nObject = createObject(); 5006 emitStructure( m_aStructure[ 0 ] ); 5007 } 5008 5009 // adjust tree node file offset 5010 if( ! updateObject( nTreeNode ) ) 5011 return false; 5012 5013 // emit tree node 5014 OStringBuffer aLine( 2048 ); 5015 aLine.append( nTreeNode ); 5016 aLine.append( " 0 obj\n" ); 5017 aLine.append( "<</Type/Pages\n" ); 5018 aLine.append( "/Resources " ); 5019 aLine.append( getResourceDictObj() ); 5020 aLine.append( " 0 R\n" ); 5021 5022 sal_Int32 nMediaBoxWidth = 0; 5023 sal_Int32 nMediaBoxHeight = 0; 5024 if( m_aPages.empty() ) // sanity check, this should not happen 5025 { 5026 nMediaBoxWidth = m_nInheritedPageWidth; 5027 nMediaBoxHeight = m_nInheritedPageHeight; 5028 } 5029 else 5030 { 5031 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter ) 5032 { 5033 if( iter->m_nPageWidth > nMediaBoxWidth ) 5034 nMediaBoxWidth = iter->m_nPageWidth; 5035 if( iter->m_nPageHeight > nMediaBoxHeight ) 5036 nMediaBoxHeight = iter->m_nPageHeight; 5037 } 5038 } 5039 aLine.append( "/MediaBox[ 0 0 " ); 5040 aLine.append( nMediaBoxWidth ); 5041 aLine.append( ' ' ); 5042 aLine.append( nMediaBoxHeight ); 5043 aLine.append( " ]\n" 5044 "/Kids[ " ); 5045 unsigned int i = 0; 5046 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ ) 5047 { 5048 aLine.append( iter->m_nPageObject ); 5049 aLine.append( " 0 R" ); 5050 aLine.append( ( (i&15) == 15 ) ? "\n" : " " ); 5051 } 5052 aLine.append( "]\n" 5053 "/Count " ); 5054 aLine.append( (sal_Int32)m_aPages.size() ); 5055 aLine.append( ">>\n" 5056 "endobj\n\n" ); 5057 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5058 5059 // emit annotation objects 5060 CHECK_RETURN( emitAnnotations() ); 5061 CHECK_RETURN( emitEmbeddedFiles() ); 5062 5063 // emit Catalog 5064 m_nCatalogObject = createObject(); 5065 if( ! updateObject( m_nCatalogObject ) ) 5066 return false; 5067 aLine.setLength( 0 ); 5068 aLine.append( m_nCatalogObject ); 5069 aLine.append( " 0 obj\n" 5070 "<</Type/Catalog/Pages " ); 5071 aLine.append( nTreeNode ); 5072 aLine.append( " 0 R\n" ); 5073 5074 // check if there are named destinations to emit (root must be inside the catalog) 5075 if( nNamedDestinationsDictionary ) 5076 { 5077 aLine.append("/Dests "); 5078 aLine.append( nNamedDestinationsDictionary ); 5079 aLine.append( " 0 R\n" ); 5080 } 5081 5082 if( m_aContext.PageLayout != PDFWriter::DefaultLayout ) 5083 switch( m_aContext.PageLayout ) 5084 { 5085 default : 5086 case PDFWriter::SinglePage : 5087 aLine.append( "/PageLayout/SinglePage\n" ); 5088 break; 5089 case PDFWriter::Continuous : 5090 aLine.append( "/PageLayout/OneColumn\n" ); 5091 break; 5092 case PDFWriter::ContinuousFacing : 5093 // the flag m_aContext.FirstPageLeft below is used to set the page on the left side 5094 aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side 5095 break; 5096 } 5097 if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode ) 5098 switch( m_aContext.PDFDocumentMode ) 5099 { 5100 default : 5101 aLine.append( "/PageMode/UseNone\n" ); 5102 break; 5103 case PDFWriter::UseOutlines : 5104 aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open 5105 break; 5106 case PDFWriter::UseThumbs : 5107 aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open 5108 break; 5109 } 5110 else if( m_aContext.OpenInFullScreenMode ) 5111 aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen 5112 5113 OStringBuffer aInitPageRef; 5114 if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() ) 5115 { 5116 aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject ); 5117 aInitPageRef.append( " 0 R" ); 5118 } 5119 else 5120 aInitPageRef.append( "0" ); 5121 5122 switch( m_aContext.PDFDocumentAction ) 5123 { 5124 case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default 5125 default: 5126 if( aInitPageRef.getLength() > 1 ) 5127 { 5128 aLine.append( "/OpenAction[" ); 5129 aLine.append( aInitPageRef.makeStringAndClear() ); 5130 aLine.append( " /XYZ null null 0]\n" ); 5131 } 5132 break; 5133 case PDFWriter::FitInWindow : 5134 aLine.append( "/OpenAction[" ); 5135 aLine.append( aInitPageRef.makeStringAndClear() ); 5136 aLine.append( " /Fit]\n" ); //Open fit page 5137 break; 5138 case PDFWriter::FitWidth : 5139 aLine.append( "/OpenAction[" ); 5140 aLine.append( aInitPageRef.makeStringAndClear() ); 5141 aLine.append( " /FitH " ); 5142 aLine.append( m_nInheritedPageHeight );//Open fit width 5143 aLine.append( "]\n" ); 5144 break; 5145 case PDFWriter::FitVisible : 5146 aLine.append( "/OpenAction[" ); 5147 aLine.append( aInitPageRef.makeStringAndClear() ); 5148 aLine.append( " /FitBH " ); 5149 aLine.append( m_nInheritedPageHeight );//Open fit visible 5150 aLine.append( "]\n" ); 5151 break; 5152 case PDFWriter::ActionZoom : 5153 aLine.append( "/OpenAction[" ); 5154 aLine.append( aInitPageRef.makeStringAndClear() ); 5155 aLine.append( " /XYZ null null " ); 5156 if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 ) 5157 aLine.append( (double)m_aContext.Zoom/100.0 ); 5158 else 5159 aLine.append( "0" ); 5160 aLine.append( "]\n" ); 5161 break; 5162 } 5163 5164 // viewer preferences, if we had some, then emit 5165 if( m_aContext.HideViewerToolbar || 5166 ( m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 && !m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle ) || 5167 m_aContext.HideViewerMenubar || 5168 m_aContext.HideViewerWindowControls || m_aContext.FitWindow || 5169 m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) || 5170 m_aContext.OpenInFullScreenMode ) 5171 { 5172 aLine.append( "/ViewerPreferences<<" ); 5173 if( m_aContext.HideViewerToolbar ) 5174 aLine.append( "/HideToolbar true\n" ); 5175 if( m_aContext.HideViewerMenubar ) 5176 aLine.append( "/HideMenubar true\n" ); 5177 if( m_aContext.HideViewerWindowControls ) 5178 aLine.append( "/HideWindowUI true\n" ); 5179 if( m_aContext.FitWindow ) 5180 aLine.append( "/FitWindow true\n" ); 5181 if( m_aContext.CenterWindow ) 5182 aLine.append( "/CenterWindow true\n" ); 5183 if( m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 && !m_aContext.DocumentInfo.Title.isEmpty() && m_aContext.DisplayPDFDocumentTitle ) 5184 aLine.append( "/DisplayDocTitle true\n" ); 5185 if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) 5186 aLine.append( "/Direction/R2L\n" ); 5187 if( m_aContext.OpenInFullScreenMode ) 5188 switch( m_aContext.PDFDocumentMode ) 5189 { 5190 default : 5191 case PDFWriter::ModeDefault : 5192 aLine.append( "/NonFullScreenPageMode/UseNone\n" ); 5193 break; 5194 case PDFWriter::UseOutlines : 5195 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" ); 5196 break; 5197 case PDFWriter::UseThumbs : 5198 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" ); 5199 break; 5200 } 5201 aLine.append( ">>\n" ); 5202 } 5203 5204 if( nOutlineDict ) 5205 { 5206 aLine.append( "/Outlines " ); 5207 aLine.append( nOutlineDict ); 5208 aLine.append( " 0 R\n" ); 5209 } 5210 if( nStructureDict ) 5211 { 5212 aLine.append( "/StructTreeRoot " ); 5213 aLine.append( nStructureDict ); 5214 aLine.append( " 0 R\n" ); 5215 } 5216 if( !m_aContext.DocumentLocale.Language.isEmpty() ) 5217 { 5218 /* PDF allows only RFC 3066, see above in emitStructure(). */ 5219 LanguageTag aLanguageTag( m_aContext.DocumentLocale); 5220 OUString aLanguage, aScript, aCountry; 5221 aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, aCountry); 5222 if (!aLanguage.isEmpty()) 5223 { 5224 OUStringBuffer aLocBuf( 16 ); 5225 aLocBuf.append( aLanguage ); 5226 if( !aCountry.isEmpty() ) 5227 { 5228 aLocBuf.append( '-' ); 5229 aLocBuf.append( aCountry ); 5230 } 5231 aLine.append( "/Lang" ); 5232 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine ); 5233 aLine.append( "\n" ); 5234 } 5235 } 5236 if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 ) 5237 { 5238 aLine.append( "/MarkInfo<</Marked true>>\n" ); 5239 } 5240 if( m_aWidgets.size() > 0 ) 5241 { 5242 aLine.append( "/AcroForm<</Fields[\n" ); 5243 int nWidgets = m_aWidgets.size(); 5244 int nOut = 0; 5245 for( int j = 0; j < nWidgets; j++ ) 5246 { 5247 // output only root fields 5248 if( m_aWidgets[j].m_nParent < 1 ) 5249 { 5250 aLine.append( m_aWidgets[j].m_nObject ); 5251 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " ); 5252 } 5253 } 5254 aLine.append( "\n]" ); 5255 5256 #if HAVE_FEATURE_NSS 5257 if (m_nSignatureObject != -1) 5258 aLine.append( "/SigFlags 3"); 5259 #endif 5260 5261 aLine.append( "/DR " ); 5262 aLine.append( getResourceDictObj() ); 5263 aLine.append( " 0 R" ); 5264 // NeedAppearances must not be used if PDF is signed 5265 if( m_bIsPDF_A1 5266 #if HAVE_FEATURE_NSS 5267 || ( m_nSignatureObject != -1 ) 5268 #endif 5269 ) 5270 aLine.append( ">>\n" ); 5271 else 5272 aLine.append( "/NeedAppearances true>>\n" ); 5273 } 5274 5275 //check if there is a Metadata object 5276 if( nOutputIntentObject ) 5277 { 5278 aLine.append("/OutputIntents["); 5279 aLine.append( nOutputIntentObject ); 5280 aLine.append( " 0 R]" ); 5281 } 5282 5283 if( nMetadataObject ) 5284 { 5285 aLine.append("/Metadata "); 5286 aLine.append( nMetadataObject ); 5287 aLine.append( " 0 R" ); 5288 } 5289 5290 aLine.append( ">>\n" 5291 "endobj\n\n" ); 5292 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5293 5294 return true; 5295 } 5296 5297 #if HAVE_FEATURE_NSS 5298 5299 bool PDFWriterImpl::emitSignature() 5300 { 5301 if( !updateObject( m_nSignatureObject ) ) 5302 return false; 5303 5304 OStringBuffer aLine( 0x5000 ); 5305 aLine.append( m_nSignatureObject ); 5306 aLine.append( " 0 obj\n" ); 5307 aLine.append("<</Contents <" ); 5308 5309 sal_uInt64 nOffset = ~0U; 5310 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) ); 5311 5312 m_nSignatureContentOffset = nOffset + aLine.getLength(); 5313 5314 // reserve some space for the PKCS#7 object 5315 OStringBuffer aContentFiller( MAX_SIGNATURE_CONTENT_LENGTH ); 5316 comphelper::string::padToLength(aContentFiller, MAX_SIGNATURE_CONTENT_LENGTH, '0'); 5317 aLine.append( aContentFiller.makeStringAndClear() ); 5318 aLine.append( ">\n/Type/Sig/SubFilter/adbe.pkcs7.detached"); 5319 5320 if( !m_aContext.DocumentInfo.Author.isEmpty() ) 5321 { 5322 aLine.append( "/Name" ); 5323 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, m_nSignatureObject, aLine ); 5324 } 5325 5326 aLine.append( " /M "); 5327 appendLiteralStringEncrypt( m_aCreationDateString, m_nSignatureObject, aLine ); 5328 5329 aLine.append( " /ByteRange [ 0 "); 5330 aLine.append( m_nSignatureContentOffset - 1 ); 5331 aLine.append( " " ); 5332 aLine.append( m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1 ); 5333 aLine.append( " " ); 5334 5335 m_nSignatureLastByteRangeNoOffset = nOffset + aLine.getLength(); 5336 5337 // mark the last ByteRange no and add some space. Now, we don't know 5338 // how many bytes we need for this ByteRange value 5339 // The real value will be overwritten in the finalizeSignature method 5340 OStringBuffer aByteRangeFiller( 100 ); 5341 comphelper::string::padToLength(aByteRangeFiller, 100, ' '); 5342 aLine.append( aByteRangeFiller.makeStringAndClear() ); 5343 aLine.append(" /Filter/Adobe.PPKMS"); 5344 5345 //emit reason, location and contactinfo 5346 if ( !m_aContext.SignReason.isEmpty() ) 5347 { 5348 aLine.append("/Reason"); 5349 appendUnicodeTextStringEncrypt( m_aContext.SignReason, m_nSignatureObject, aLine ); 5350 } 5351 5352 if ( !m_aContext.SignLocation.isEmpty() ) 5353 { 5354 aLine.append("/Location"); 5355 appendUnicodeTextStringEncrypt( m_aContext.SignLocation, m_nSignatureObject, aLine ); 5356 } 5357 5358 if ( !m_aContext.SignContact.isEmpty() ) 5359 { 5360 aLine.append("/ContactInfo"); 5361 appendUnicodeTextStringEncrypt( m_aContext.SignContact, m_nSignatureObject, aLine ); 5362 } 5363 5364 aLine.append(" >>\nendobj\n\n" ); 5365 5366 if (!writeBuffer( aLine.getStr(), aLine.getLength() )) 5367 return false; 5368 5369 return true; 5370 } 5371 5372 bool PDFWriterImpl::finalizeSignature() 5373 { 5374 if (!m_aContext.SignCertificate.is()) 5375 return false; 5376 5377 // 1- calculate last ByteRange value 5378 sal_uInt64 nOffset = ~0U; 5379 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nOffset) ) ); 5380 5381 sal_Int64 nLastByteRangeNo = nOffset - (m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1); 5382 5383 // 2- overwrite the value to the m_nSignatureLastByteRangeNoOffset position 5384 sal_uInt64 nWritten = 0; 5385 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureLastByteRangeNoOffset) ) ); 5386 OStringBuffer aByteRangeNo( 256 ); 5387 aByteRangeNo.append( nLastByteRangeNo ); 5388 aByteRangeNo.append( " ]" ); 5389 5390 if (m_aFile.write(aByteRangeNo.getStr(), aByteRangeNo.getLength(), nWritten) != osl::File::E_None) 5391 { 5392 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) ); 5393 return false; 5394 } 5395 5396 // 3- create the PKCS#7 object using NSS 5397 5398 // Prepare buffer and calculate PDF file digest 5399 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, 0)) ); 5400 5401 std::unique_ptr<char[]> buffer1(new char[m_nSignatureContentOffset + 1]); 5402 sal_uInt64 bytesRead1; 5403 5404 //FIXME: Check if hash is calculated from the correct byterange 5405 if (osl::File::E_None != m_aFile.read(buffer1.get(), m_nSignatureContentOffset - 1 , bytesRead1) || 5406 bytesRead1 != (sal_uInt64)m_nSignatureContentOffset - 1) 5407 { 5408 SAL_WARN("vcl.pdfwriter", "First buffer read failed"); 5409 return false; 5410 } 5411 5412 std::unique_ptr<char[]> buffer2(new char[nLastByteRangeNo + 1]); 5413 sal_uInt64 bytesRead2; 5414 5415 if (osl::File::E_None != m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1) || 5416 osl::File::E_None != m_aFile.read(buffer2.get(), nLastByteRangeNo, bytesRead2) || 5417 bytesRead2 != (sal_uInt64) nLastByteRangeNo) 5418 { 5419 SAL_WARN("vcl.pdfwriter", "Second buffer read failed"); 5420 return false; 5421 } 5422 5423 OStringBuffer aCMSHexBuffer; 5424 svl::crypto::Signing aSigning(m_aContext.SignCertificate); 5425 aSigning.AddDataRange(buffer1.get(), bytesRead1); 5426 aSigning.AddDataRange(buffer2.get(), bytesRead2); 5427 aSigning.SetSignTSA(m_aContext.SignTSA); 5428 aSigning.SetSignPassword(m_aContext.SignPassword); 5429 if (!aSigning.Sign(aCMSHexBuffer)) 5430 { 5431 SAL_WARN("vcl.pdfwriter", "PDFWriter::Sign() failed"); 5432 return false; 5433 } 5434 5435 assert(aCMSHexBuffer.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH); 5436 5437 // Set file pointer to the m_nSignatureContentOffset, we're ready to overwrite PKCS7 object 5438 nWritten = 0; 5439 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, m_nSignatureContentOffset)) ); 5440 m_aFile.write(aCMSHexBuffer.getStr(), aCMSHexBuffer.getLength(), nWritten); 5441 5442 CHECK_RETURN( (osl::File::E_None == m_aFile.setPos(osl_Pos_Absolut, nOffset)) ); 5443 return true; 5444 } 5445 5446 #endif //HAVE_FEATURE_NSS 5447 5448 sal_Int32 PDFWriterImpl::emitInfoDict( ) 5449 { 5450 sal_Int32 nObject = createObject(); 5451 5452 if( updateObject( nObject ) ) 5453 { 5454 OStringBuffer aLine( 1024 ); 5455 aLine.append( nObject ); 5456 aLine.append( " 0 obj\n" 5457 "<<" ); 5458 if( !m_aContext.DocumentInfo.Title.isEmpty() ) 5459 { 5460 aLine.append( "/Title" ); 5461 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine ); 5462 aLine.append( "\n" ); 5463 } 5464 if( !m_aContext.DocumentInfo.Author.isEmpty() ) 5465 { 5466 aLine.append( "/Author" ); 5467 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine ); 5468 aLine.append( "\n" ); 5469 } 5470 if( !m_aContext.DocumentInfo.Subject.isEmpty() ) 5471 { 5472 aLine.append( "/Subject" ); 5473 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine ); 5474 aLine.append( "\n" ); 5475 } 5476 if( !m_aContext.DocumentInfo.Keywords.isEmpty() ) 5477 { 5478 aLine.append( "/Keywords" ); 5479 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine ); 5480 aLine.append( "\n" ); 5481 } 5482 if( !m_aContext.DocumentInfo.Creator.isEmpty() ) 5483 { 5484 aLine.append( "/Creator" ); 5485 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine ); 5486 aLine.append( "\n" ); 5487 } 5488 if( !m_aContext.DocumentInfo.Producer.isEmpty() ) 5489 { 5490 aLine.append( "/Producer" ); 5491 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine ); 5492 aLine.append( "\n" ); 5493 } 5494 5495 aLine.append( "/CreationDate" ); 5496 appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine ); 5497 aLine.append( ">>\nendobj\n\n" ); 5498 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 5499 nObject = 0; 5500 } 5501 else 5502 nObject = 0; 5503 5504 return nObject; 5505 } 5506 5507 // Part of this function may be shared with method appendDest. 5508 sal_Int32 PDFWriterImpl::emitNamedDestinations() 5509 { 5510 sal_Int32 nCount = m_aNamedDests.size(); 5511 if( nCount <= 0 ) 5512 return 0;//define internal error 5513 5514 //get the object number for all the destinations 5515 sal_Int32 nObject = createObject(); 5516 5517 if( updateObject( nObject ) ) 5518 { 5519 //emit the dictionary 5520 OStringBuffer aLine( 1024 ); 5521 aLine.append( nObject ); 5522 aLine.append( " 0 obj\n" 5523 "<<" ); 5524 5525 sal_Int32 nDestID; 5526 for( nDestID = 0; nDestID < nCount; nDestID++ ) 5527 { 5528 const PDFNamedDest& rDest = m_aNamedDests[ nDestID ]; 5529 // In order to correctly function both under an Internet browser and 5530 // directly with a reader (provided the reader has the feature) we 5531 // need to set the name of the destination the same way it will be encoded 5532 // in an Internet link 5533 INetURLObject aLocalURL( "http://ahost.ax" ); //dummy location, won't be used 5534 aLocalURL.SetMark( rDest.m_aDestName ); 5535 5536 const OUString aName = aLocalURL.GetMark( INetURLObject::DecodeMechanism::NONE ); //same coding as 5537 // in link creation ( see PDFWriterImpl::emitLinkAnnotations ) 5538 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ]; 5539 5540 aLine.append( '/' ); 5541 appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog ) 5542 aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function 5543 //maps the preceding character properly 5544 aLine.append( rDestPage.m_nPageObject ); 5545 aLine.append( " 0 R" ); 5546 5547 switch( rDest.m_eType ) 5548 { 5549 case PDFWriter::DestAreaType::XYZ: 5550 default: 5551 aLine.append( "/XYZ " ); 5552 appendFixedInt( rDest.m_aRect.Left(), aLine ); 5553 aLine.append( ' ' ); 5554 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 5555 aLine.append( " 0" ); 5556 break; 5557 case PDFWriter::DestAreaType::FitRectangle: 5558 aLine.append( "/FitR " ); 5559 appendFixedInt( rDest.m_aRect.Left(), aLine ); 5560 aLine.append( ' ' ); 5561 appendFixedInt( rDest.m_aRect.Top(), aLine ); 5562 aLine.append( ' ' ); 5563 appendFixedInt( rDest.m_aRect.Right(), aLine ); 5564 aLine.append( ' ' ); 5565 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 5566 break; 5567 } 5568 aLine.append( "]\n" ); 5569 } 5570 5571 //close 5572 aLine.append( ">>\nendobj\n\n" ); 5573 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 5574 nObject = 0; 5575 } 5576 else 5577 nObject = 0; 5578 5579 return nObject; 5580 } 5581 5582 // emits the output intent dictionary 5583 sal_Int32 PDFWriterImpl::emitOutputIntent() 5584 { 5585 if( !m_bIsPDF_A1 ) 5586 return 0; 5587 5588 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1 5589 5590 OStringBuffer aLine( 1024 ); 5591 sal_Int32 nICCObject = createObject(); 5592 sal_Int32 nStreamLengthObject = createObject(); 5593 5594 aLine.append( nICCObject ); 5595 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16) 5596 aLine.append( " 0 obj\n<</N 3/Length " ); 5597 aLine.append( nStreamLengthObject ); 5598 aLine.append( " 0 R" ); 5599 if (!g_bDebugDisableCompression) 5600 aLine.append( "/Filter/FlateDecode" ); 5601 aLine.append( ">>\nstream\n" ); 5602 if ( !updateObject( nICCObject ) ) return 0; 5603 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0; 5604 //get file position 5605 sal_uInt64 nBeginStreamPos = 0; 5606 m_aFile.getPos(nBeginStreamPos); 5607 beginCompression(); 5608 checkAndEnableStreamEncryption( nICCObject ); 5609 cmsHPROFILE hProfile = cmsCreate_sRGBProfile(); 5610 //force ICC profile version 2.1 5611 cmsSetProfileVersion(hProfile, 2.1); 5612 cmsUInt32Number nBytesNeeded = 0; 5613 cmsSaveProfileToMem(hProfile, nullptr, &nBytesNeeded); 5614 if (!nBytesNeeded) 5615 return 0; 5616 std::vector<unsigned char> aBuffer(nBytesNeeded); 5617 cmsSaveProfileToMem(hProfile, &aBuffer[0], &nBytesNeeded); 5618 cmsCloseProfile(hProfile); 5619 bool written = writeBuffer( &aBuffer[0], (sal_Int32) aBuffer.size() ); 5620 disableStreamEncryption(); 5621 endCompression(); 5622 sal_uInt64 nEndStreamPos = 0; 5623 m_aFile.getPos(nEndStreamPos); 5624 5625 if( !written ) 5626 return 0; 5627 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) 5628 return 0 ; 5629 aLine.setLength( 0 ); 5630 5631 //emit the stream length object 5632 if ( !updateObject( nStreamLengthObject ) ) return 0; 5633 aLine.setLength( 0 ); 5634 aLine.append( nStreamLengthObject ); 5635 aLine.append( " 0 obj\n" ); 5636 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) ); 5637 aLine.append( "\nendobj\n\n" ); 5638 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0; 5639 aLine.setLength( 0 ); 5640 5641 //emit the OutputIntent dictionary 5642 sal_Int32 nOIObject = createObject(); 5643 if ( !updateObject( nOIObject ) ) return 0; 5644 aLine.append( nOIObject ); 5645 aLine.append( " 0 obj\n" 5646 "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier"); 5647 5648 OUString const aComment( "sRGB IEC61966-2.1" ); 5649 appendLiteralStringEncrypt( aComment ,nOIObject, aLine ); 5650 aLine.append("/DestOutputProfile "); 5651 aLine.append( nICCObject ); 5652 aLine.append( " 0 R>>\nendobj\n\n" ); 5653 if ( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) return 0; 5654 5655 return nOIObject; 5656 } 5657 5658 // formats the string for the XML stream 5659 static void escapeStringXML( const OUString& rStr, OUString &rValue) 5660 { 5661 const sal_Unicode* pUni = rStr.getStr(); 5662 int nLen = rStr.getLength(); 5663 for( ; nLen; nLen--, pUni++ ) 5664 { 5665 switch( *pUni ) 5666 { 5667 case u'&': 5668 rValue += "&"; 5669 break; 5670 case u'<': 5671 rValue += "<"; 5672 break; 5673 case u'>': 5674 rValue += ">"; 5675 break; 5676 case u'\'': 5677 rValue += "'"; 5678 break; 5679 case u'"': 5680 rValue += """; 5681 break; 5682 default: 5683 rValue += OUStringLiteral1( *pUni ); 5684 break; 5685 } 5686 } 5687 } 5688 5689 // emits the document metadata 5690 sal_Int32 PDFWriterImpl::emitDocumentMetadata() 5691 { 5692 if( !m_bIsPDF_A1 ) 5693 return 0; 5694 5695 //get the object number for all the destinations 5696 sal_Int32 nObject = createObject(); 5697 5698 if( updateObject( nObject ) ) 5699 { 5700 // the following string are written in UTF-8 unicode 5701 OStringBuffer aMetadataStream( 8192 ); 5702 5703 aMetadataStream.append( "<?xpacket begin=\"" ); 5704 // these lines write Unicode "zero width non-breaking space character" (U+FEFF) 5705 // (aka byte-order mark ) used as a byte-order marker. 5706 aMetadataStream.append( OUStringToOString( OUString( u'\xFEFF' ), RTL_TEXTENCODING_UTF8 ) ); 5707 aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" ); 5708 aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" ); 5709 aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" ); 5710 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 ) 5711 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 5712 aMetadataStream.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" ); 5713 aMetadataStream.append( " <pdfaid:part>1</pdfaid:part>\n" ); 5714 aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" ); 5715 aMetadataStream.append( " </rdf:Description>\n" ); 5716 //... Dublin Core properties go here 5717 if( !m_aContext.DocumentInfo.Title.isEmpty() || 5718 !m_aContext.DocumentInfo.Author.isEmpty() || 5719 !m_aContext.DocumentInfo.Subject.isEmpty() ) 5720 { 5721 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 5722 aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" ); 5723 if( !m_aContext.DocumentInfo.Title.isEmpty() ) 5724 { 5725 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) 5726 aMetadataStream.append( " <dc:title>\n" ); 5727 aMetadataStream.append( " <rdf:Alt>\n" ); 5728 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); 5729 OUString aTitle; 5730 escapeStringXML( m_aContext.DocumentInfo.Title, aTitle ); 5731 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) ); 5732 aMetadataStream.append( "</rdf:li>\n" ); 5733 aMetadataStream.append( " </rdf:Alt>\n" ); 5734 aMetadataStream.append( " </dc:title>\n" ); 5735 } 5736 if( !m_aContext.DocumentInfo.Author.isEmpty() ) 5737 { 5738 aMetadataStream.append( " <dc:creator>\n" ); 5739 aMetadataStream.append( " <rdf:Seq>\n" ); 5740 aMetadataStream.append( " <rdf:li>" ); 5741 OUString aAuthor; 5742 escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor ); 5743 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) ); 5744 aMetadataStream.append( "</rdf:li>\n" ); 5745 aMetadataStream.append( " </rdf:Seq>\n" ); 5746 aMetadataStream.append( " </dc:creator>\n" ); 5747 } 5748 if( !m_aContext.DocumentInfo.Subject.isEmpty() ) 5749 { 5750 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) 5751 aMetadataStream.append( " <dc:description>\n" ); 5752 aMetadataStream.append( " <rdf:Alt>\n" ); 5753 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); 5754 OUString aSubject; 5755 escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject ); 5756 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) ); 5757 aMetadataStream.append( "</rdf:li>\n" ); 5758 aMetadataStream.append( " </rdf:Alt>\n" ); 5759 aMetadataStream.append( " </dc:description>\n" ); 5760 } 5761 aMetadataStream.append( " </rdf:Description>\n" ); 5762 } 5763 5764 //... PDF properties go here 5765 if( !m_aContext.DocumentInfo.Producer.isEmpty() || 5766 !m_aContext.DocumentInfo.Keywords.isEmpty() ) 5767 { 5768 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 5769 aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" ); 5770 if( !m_aContext.DocumentInfo.Producer.isEmpty() ) 5771 { 5772 aMetadataStream.append( " <pdf:Producer>" ); 5773 OUString aProducer; 5774 escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer ); 5775 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) ); 5776 aMetadataStream.append( "</pdf:Producer>\n" ); 5777 } 5778 if( !m_aContext.DocumentInfo.Keywords.isEmpty() ) 5779 { 5780 aMetadataStream.append( " <pdf:Keywords>" ); 5781 OUString aKeywords; 5782 escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords ); 5783 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) ); 5784 aMetadataStream.append( "</pdf:Keywords>\n" ); 5785 } 5786 aMetadataStream.append( " </rdf:Description>\n" ); 5787 } 5788 5789 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 5790 aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" ); 5791 if( !m_aContext.DocumentInfo.Creator.isEmpty() ) 5792 { 5793 aMetadataStream.append( " <xmp:CreatorTool>" ); 5794 OUString aCreator; 5795 escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator ); 5796 aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) ); 5797 aMetadataStream.append( "</xmp:CreatorTool>\n" ); 5798 } 5799 //creation date 5800 aMetadataStream.append( " <xmp:CreateDate>" ); 5801 aMetadataStream.append( m_aCreationMetaDateString ); 5802 aMetadataStream.append( "</xmp:CreateDate>\n" ); 5803 5804 aMetadataStream.append( " </rdf:Description>\n" ); 5805 aMetadataStream.append( " </rdf:RDF>\n" ); 5806 aMetadataStream.append( "</x:xmpmeta>\n" ); 5807 5808 //add the padding 5809 for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ ) 5810 { 5811 aMetadataStream.append( " " ); 5812 if( nSpaces % 100 == 0 ) 5813 aMetadataStream.append( "\n" ); 5814 } 5815 5816 aMetadataStream.append( "<?xpacket end=\"w\"?>\n" ); 5817 5818 OStringBuffer aMetadataObj( 1024 ); 5819 5820 aMetadataObj.append( nObject ); 5821 aMetadataObj.append( " 0 obj\n" ); 5822 5823 aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " ); 5824 5825 aMetadataObj.append( aMetadataStream.getLength() ); 5826 aMetadataObj.append( ">>\nstream\n" ); 5827 if ( !writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) ) 5828 return 0; 5829 //emit the stream 5830 if ( !writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) ) 5831 return 0; 5832 5833 aMetadataObj.setLength( 0 ); 5834 aMetadataObj.append( "\nendstream\nendobj\n\n" ); 5835 if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) ) 5836 nObject = 0; 5837 } 5838 else 5839 nObject = 0; 5840 5841 return nObject; 5842 } 5843 5844 bool PDFWriterImpl::emitTrailer() 5845 { 5846 // emit doc info 5847 sal_Int32 nDocInfoObject = emitInfoDict( ); 5848 5849 sal_Int32 nSecObject = 0; 5850 5851 if( m_aContext.Encryption.Encrypt() ) 5852 { 5853 //emit the security information 5854 //must be emitted as indirect dictionary object, since 5855 //Acrobat Reader 5 works only with this kind of implementation 5856 nSecObject = createObject(); 5857 5858 if( updateObject( nSecObject ) ) 5859 { 5860 OStringBuffer aLineS( 1024 ); 5861 aLineS.append( nSecObject ); 5862 aLineS.append( " 0 obj\n" 5863 "<</Filter/Standard/V " ); 5864 // check the version 5865 aLineS.append( "2/Length 128/R 3" ); 5866 5867 // emit the owner password, must not be encrypted 5868 aLineS.append( "/O(" ); 5869 appendLiteralString( reinterpret_cast<char*>(&m_aContext.Encryption.OValue[0]), sal_Int32(m_aContext.Encryption.OValue.size()), aLineS ); 5870 aLineS.append( ")/U(" ); 5871 appendLiteralString( reinterpret_cast<char*>(&m_aContext.Encryption.UValue[0]), sal_Int32(m_aContext.Encryption.UValue.size()), aLineS ); 5872 aLineS.append( ")/P " );// the permission set 5873 aLineS.append( m_nAccessPermissions ); 5874 aLineS.append( ">>\nendobj\n\n" ); 5875 if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) ) 5876 nSecObject = 0; 5877 } 5878 else 5879 nSecObject = 0; 5880 } 5881 // emit xref table 5882 // remember start 5883 sal_uInt64 nXRefOffset = 0; 5884 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nXRefOffset )) ); 5885 CHECK_RETURN( writeBuffer( "xref\n", 5 ) ); 5886 5887 sal_Int32 nObjects = m_aObjects.size(); 5888 OStringBuffer aLine; 5889 aLine.append( "0 " ); 5890 aLine.append( (sal_Int32)(nObjects+1) ); 5891 aLine.append( "\n" ); 5892 aLine.append( "0000000000 65535 f \n" ); 5893 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5894 5895 for( sal_Int32 i = 0; i < nObjects; i++ ) 5896 { 5897 aLine.setLength( 0 ); 5898 OString aOffset = OString::number( m_aObjects[i] ); 5899 for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ ) 5900 aLine.append( '0' ); 5901 aLine.append( aOffset ); 5902 aLine.append( " 00000 n \n" ); 5903 SAL_WARN_IF( aLine.getLength() != 20, "vcl.pdfwriter", "invalid xref entry" ); 5904 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5905 } 5906 5907 // prepare document checksum 5908 OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 ); 5909 if( m_aDocDigest ) 5910 { 5911 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; 5912 rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) ); 5913 for(sal_uInt8 i : nMD5Sum) 5914 appendHex( i, aDocChecksum ); 5915 } 5916 // document id set in setDocInfo method 5917 // emit trailer 5918 aLine.setLength( 0 ); 5919 aLine.append( "trailer\n" 5920 "<</Size " ); 5921 aLine.append( (sal_Int32)(nObjects+1) ); 5922 aLine.append( "/Root " ); 5923 aLine.append( m_nCatalogObject ); 5924 aLine.append( " 0 R\n" ); 5925 if( nSecObject ) 5926 { 5927 aLine.append( "/Encrypt "); 5928 aLine.append( nSecObject ); 5929 aLine.append( " 0 R\n" ); 5930 } 5931 if( nDocInfoObject ) 5932 { 5933 aLine.append( "/Info " ); 5934 aLine.append( nDocInfoObject ); 5935 aLine.append( " 0 R\n" ); 5936 } 5937 if( ! m_aContext.Encryption.DocumentIdentifier.empty() ) 5938 { 5939 aLine.append( "/ID [ <" ); 5940 for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); 5941 it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) 5942 { 5943 appendHex( sal_Int8(*it), aLine ); 5944 } 5945 aLine.append( ">\n" 5946 "<" ); 5947 for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); 5948 it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) 5949 { 5950 appendHex( sal_Int8(*it), aLine ); 5951 } 5952 aLine.append( "> ]\n" ); 5953 } 5954 if( !aDocChecksum.isEmpty() ) 5955 { 5956 aLine.append( "/DocChecksum /" ); 5957 aLine.append( aDocChecksum.makeStringAndClear() ); 5958 aLine.append( "\n" ); 5959 } 5960 if( m_aAdditionalStreams.size() > 0 ) 5961 { 5962 aLine.append( "/AdditionalStreams [" ); 5963 for(const PDFAddStream & rAdditionalStream : m_aAdditionalStreams) 5964 { 5965 aLine.append( "/" ); 5966 appendName( rAdditionalStream.m_aMimeType, aLine ); 5967 aLine.append( " " ); 5968 aLine.append( rAdditionalStream.m_nStreamObject ); 5969 aLine.append( " 0 R\n" ); 5970 } 5971 aLine.append( "]\n" ); 5972 } 5973 aLine.append( ">>\n" 5974 "startxref\n" ); 5975 aLine.append( (sal_Int64)nXRefOffset ); 5976 aLine.append( "\n" 5977 "%%EOF\n" ); 5978 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5979 5980 return true; 5981 } 5982 5983 struct AnnotationSortEntry 5984 { 5985 sal_Int32 nTabOrder; 5986 sal_Int32 nObject; 5987 sal_Int32 nWidgetIndex; 5988 5989 AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) : 5990 nTabOrder( nTab ), 5991 nObject( nObj ), 5992 nWidgetIndex( nI ) 5993 {} 5994 }; 5995 5996 struct AnnotSortContainer 5997 { 5998 std::set< sal_Int32 > aObjects; 5999 std::vector< AnnotationSortEntry > aSortedAnnots; 6000 }; 6001 6002 struct AnnotSorterLess 6003 { 6004 std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets; 6005 6006 explicit AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {} 6007 6008 bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight ) 6009 { 6010 if( rLeft.nTabOrder < rRight.nTabOrder ) 6011 return true; 6012 if( rRight.nTabOrder < rLeft.nTabOrder ) 6013 return false; 6014 if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 ) 6015 return false; 6016 if( rRight.nWidgetIndex < 0 ) 6017 return true; 6018 if( rLeft.nWidgetIndex < 0 ) 6019 return false; 6020 // remember: widget rects are in PDF coordinates, so they are ordered down up 6021 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() > 6022 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() ) 6023 return true; 6024 if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() > 6025 m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() ) 6026 return false; 6027 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() < 6028 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() ) 6029 return true; 6030 return false; 6031 } 6032 }; 6033 6034 void PDFWriterImpl::sortWidgets() 6035 { 6036 // sort widget annotations on each page as per their 6037 // TabOrder attribute 6038 std::unordered_map< sal_Int32, AnnotSortContainer > sorted; 6039 int nWidgets = m_aWidgets.size(); 6040 for( int nW = 0; nW < nWidgets; nW++ ) 6041 { 6042 const PDFWidget& rWidget = m_aWidgets[nW]; 6043 if( rWidget.m_nPage >= 0 ) 6044 { 6045 AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ]; 6046 // optimize vector allocation 6047 if( rCont.aSortedAnnots.empty() ) 6048 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() ); 6049 // insert widget to tab sorter 6050 // RadioButtons are not page annotations, only their individual check boxes are 6051 if( rWidget.m_eType != PDFWriter::RadioButton ) 6052 { 6053 rCont.aObjects.insert( rWidget.m_nObject ); 6054 rCont.aSortedAnnots.emplace_back( rWidget.m_nTabOrder, rWidget.m_nObject, nW ); 6055 } 6056 } 6057 } 6058 for( std::unordered_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it ) 6059 { 6060 // append entries for non widget annotations 6061 PDFPage& rPage = m_aPages[ it->first ]; 6062 unsigned int nAnnots = rPage.m_aAnnotations.size(); 6063 for( unsigned int nA = 0; nA < nAnnots; nA++ ) 6064 if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end()) 6065 it->second.aSortedAnnots.emplace_back( 10000, rPage.m_aAnnotations[nA], -1 ); 6066 6067 AnnotSorterLess aLess( m_aWidgets ); 6068 std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess ); 6069 // sanity check 6070 if( it->second.aSortedAnnots.size() == nAnnots) 6071 { 6072 for( unsigned int nA = 0; nA < nAnnots; nA++ ) 6073 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject; 6074 } 6075 else 6076 { 6077 SAL_WARN( "vcl.pdfwriter", "wrong number of sorted annotations" ); 6078 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions " 6079 "on page nr " << (long int)it->first << ", " << 6080 (long int)it->second.aSortedAnnots.size() << " sorted and " << 6081 (long int)nAnnots << " unsorted"); 6082 } 6083 } 6084 6085 // FIXME: implement tab order in structure tree for PDF 1.5 6086 } 6087 6088 namespace vcl { 6089 class PDFStreamIf : 6090 public cppu::WeakImplHelper< css::io::XOutputStream > 6091 { 6092 PDFWriterImpl* m_pWriter; 6093 bool m_bWrite; 6094 public: 6095 explicit PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {} 6096 6097 virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) override; 6098 virtual void SAL_CALL flush() override; 6099 virtual void SAL_CALL closeOutput() override; 6100 }; 6101 } 6102 6103 void SAL_CALL PDFStreamIf::writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) 6104 { 6105 if( m_bWrite && aData.getLength() ) 6106 { 6107 sal_Int32 nBytes = aData.getLength(); 6108 m_pWriter->writeBuffer( aData.getConstArray(), nBytes ); 6109 } 6110 } 6111 6112 void SAL_CALL PDFStreamIf::flush() 6113 { 6114 } 6115 6116 void SAL_CALL PDFStreamIf::closeOutput() 6117 { 6118 m_bWrite = false; 6119 } 6120 6121 bool PDFWriterImpl::emitAdditionalStreams() 6122 { 6123 unsigned int nStreams = m_aAdditionalStreams.size(); 6124 for( unsigned int i = 0; i < nStreams; i++ ) 6125 { 6126 PDFAddStream& rStream = m_aAdditionalStreams[i]; 6127 rStream.m_nStreamObject = createObject(); 6128 sal_Int32 nSizeObject = createObject(); 6129 6130 if( ! updateObject( rStream.m_nStreamObject ) ) 6131 return false; 6132 6133 OStringBuffer aLine; 6134 aLine.append( rStream.m_nStreamObject ); 6135 aLine.append( " 0 obj\n<</Length " ); 6136 aLine.append( nSizeObject ); 6137 aLine.append( " 0 R" ); 6138 if( rStream.m_bCompress ) 6139 aLine.append( "/Filter/FlateDecode" ); 6140 aLine.append( ">>\nstream\n" ); 6141 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6142 return false; 6143 sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0; 6144 if( osl::File::E_None != m_aFile.getPos(nBeginStreamPos) ) 6145 { 6146 m_aFile.close(); 6147 m_bOpen = false; 6148 } 6149 if( rStream.m_bCompress ) 6150 beginCompression(); 6151 6152 checkAndEnableStreamEncryption( rStream.m_nStreamObject ); 6153 css::uno::Reference< css::io::XOutputStream > xStream( new PDFStreamIf( this ) ); 6154 assert(rStream.m_pStream); 6155 if (!rStream.m_pStream) 6156 return false; 6157 rStream.m_pStream->write( xStream ); 6158 xStream.clear(); 6159 delete rStream.m_pStream; 6160 rStream.m_pStream = nullptr; 6161 disableStreamEncryption(); 6162 6163 if( rStream.m_bCompress ) 6164 endCompression(); 6165 6166 if (osl::File::E_None != m_aFile.getPos(nEndStreamPos)) 6167 { 6168 m_aFile.close(); 6169 m_bOpen = false; 6170 return false; 6171 } 6172 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) 6173 return false ; 6174 // emit stream length object 6175 if( ! updateObject( nSizeObject ) ) 6176 return false; 6177 aLine.setLength( 0 ); 6178 aLine.append( nSizeObject ); 6179 aLine.append( " 0 obj\n" ); 6180 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) ); 6181 aLine.append( "\nendobj\n\n" ); 6182 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6183 return false; 6184 } 6185 return true; 6186 } 6187 6188 bool PDFWriterImpl::emit() 6189 { 6190 endPage(); 6191 6192 // resort structure tree and annotations if necessary 6193 // needed for widget tab order 6194 sortWidgets(); 6195 6196 #if HAVE_FEATURE_NSS 6197 if( m_aContext.SignPDF ) 6198 { 6199 // sign the document 6200 PDFWriter::SignatureWidget aSignature; 6201 aSignature.Name = "Signature1"; 6202 createControl( aSignature, 0 ); 6203 } 6204 #endif 6205 6206 // emit additional streams 6207 CHECK_RETURN( emitAdditionalStreams() ); 6208 6209 // emit catalog 6210 CHECK_RETURN( emitCatalog() ); 6211 6212 #if HAVE_FEATURE_NSS 6213 if (m_nSignatureObject != -1) // if document is signed, emit sigdict 6214 { 6215 if( !emitSignature() ) 6216 { 6217 m_aErrors.insert( PDFWriter::Error_Signature_Failed ); 6218 return false; 6219 } 6220 } 6221 #endif 6222 6223 // emit trailer 6224 CHECK_RETURN( emitTrailer() ); 6225 6226 #if HAVE_FEATURE_NSS 6227 if (m_nSignatureObject != -1) // finalize the signature 6228 { 6229 if( !finalizeSignature() ) 6230 { 6231 m_aErrors.insert( PDFWriter::Error_Signature_Failed ); 6232 return false; 6233 } 6234 } 6235 #endif 6236 6237 m_aFile.close(); 6238 m_bOpen = false; 6239 6240 return true; 6241 } 6242 6243 6244 sal_Int32 PDFWriterImpl::getSystemFont( const vcl::Font& i_rFont ) 6245 { 6246 getReferenceDevice()->Push(); 6247 getReferenceDevice()->SetFont( i_rFont ); 6248 getReferenceDevice()->ImplNewFont(); 6249 6250 const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontInstance->maFontSelData.mpFontData; 6251 sal_Int32 nFontID = 0; 6252 FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont ); 6253 if( it != m_aSystemFonts.end() ) 6254 nFontID = it->second.m_nNormalFontID; 6255 else 6256 { 6257 nFontID = m_nNextFID++; 6258 m_aSystemFonts[ pDevFont ] = EmbedFont(); 6259 m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID; 6260 } 6261 6262 getReferenceDevice()->Pop(); 6263 getReferenceDevice()->ImplNewFont(); 6264 6265 return nFontID; 6266 } 6267 6268 void PDFWriterImpl::registerGlyphs( int nGlyphs, 6269 const GlyphItem** pGlyphs, 6270 sal_Int32* pGlyphWidths, 6271 sal_Ucs* pCodeUnits, 6272 sal_Int32 const * pCodeUnitsPerGlyph, 6273 sal_uInt8* pMappedGlyphs, 6274 sal_Int32* pMappedFontObjects, 6275 const PhysicalFontFace* pFallbackFonts[] ) 6276 { 6277 SalGraphics *pGraphics = m_pReferenceDevice->GetGraphics(); 6278 6279 if (!pGraphics) 6280 return; 6281 6282 const PhysicalFontFace* pDevFont = m_pReferenceDevice->mpFontInstance->maFontSelData.mpFontData; 6283 sal_Ucs* pCurUnicode = pCodeUnits; 6284 for( int i = 0; i < nGlyphs; pCurUnicode += pCodeUnitsPerGlyph[i] , i++ ) 6285 { 6286 const int nFontGlyphId = pGlyphs[i]->maGlyphId; 6287 const PhysicalFontFace* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont; 6288 6289 FontSubset& rSubset = m_aSubsets[ pCurrentFont ]; 6290 // search for font specific glyphID 6291 FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId ); 6292 if( it != rSubset.m_aMapping.end() ) 6293 { 6294 pMappedFontObjects[i] = it->second.m_nFontID; 6295 pMappedGlyphs[i] = it->second.m_nSubsetGlyphID; 6296 } 6297 else 6298 { 6299 // create new subset if necessary 6300 if( rSubset.m_aSubsets.empty() 6301 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) ) 6302 { 6303 rSubset.m_aSubsets.emplace_back( m_nNextFID++ ); 6304 } 6305 6306 // copy font id 6307 pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID; 6308 // create new glyph in subset 6309 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1); 6310 pMappedGlyphs[i] = nNewId; 6311 6312 // add new glyph to emitted font subset 6313 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ]; 6314 rNewGlyphEmit.setGlyphId( nNewId ); 6315 for( sal_Int32 n = 0; n < pCodeUnitsPerGlyph[i]; n++ ) 6316 rNewGlyphEmit.addCode( pCurUnicode[n] ); 6317 6318 // add new glyph to font mapping 6319 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ]; 6320 rNewGlyph.m_nFontID = pMappedFontObjects[i]; 6321 rNewGlyph.m_nSubsetGlyphID = nNewId; 6322 } 6323 if (!getReferenceDevice()->AcquireGraphics()) 6324 return; 6325 pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont, 6326 nFontGlyphId, 6327 pGlyphs[i]->IsVertical(), 6328 pGraphics ); 6329 } 6330 } 6331 6332 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const OUString& rText, bool bTextLines ) 6333 { 6334 push( PushFlags::ALL ); 6335 6336 FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief(); 6337 6338 Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor(); 6339 Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor; 6340 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 6341 Color aReliefColor( COL_LIGHTGRAY ); 6342 if( aTextColor == COL_BLACK ) 6343 aTextColor = Color( COL_WHITE ); 6344 if( aTextLineColor == COL_BLACK ) 6345 aTextLineColor = Color( COL_WHITE ); 6346 if( aOverlineColor == COL_BLACK ) 6347 aOverlineColor = Color( COL_WHITE ); 6348 if( aTextColor == COL_WHITE ) 6349 aReliefColor = Color( COL_BLACK ); 6350 6351 Font aSetFont = m_aCurrentPDFState.m_aFont; 6352 aSetFont.SetRelief( FontRelief::NONE ); 6353 aSetFont.SetShadow( false ); 6354 6355 aSetFont.SetColor( aReliefColor ); 6356 setTextLineColor( aReliefColor ); 6357 setOverlineColor( aReliefColor ); 6358 setFont( aSetFont ); 6359 long nOff = 1 + getReferenceDevice()->mnDPIX/300; 6360 if( eRelief == FontRelief::Engraved ) 6361 nOff = -nOff; 6362 6363 rLayout.DrawOffset() += Point( nOff, nOff ); 6364 updateGraphicsState(); 6365 drawLayout( rLayout, rText, bTextLines ); 6366 6367 rLayout.DrawOffset() -= Point( nOff, nOff ); 6368 setTextLineColor( aTextLineColor ); 6369 setOverlineColor( aOverlineColor ); 6370 aSetFont.SetColor( aTextColor ); 6371 setFont( aSetFont ); 6372 updateGraphicsState(); 6373 drawLayout( rLayout, rText, bTextLines ); 6374 6375 // clean up the mess 6376 pop(); 6377 } 6378 6379 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const OUString& rText, bool bTextLines ) 6380 { 6381 Font aSaveFont = m_aCurrentPDFState.m_aFont; 6382 Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor; 6383 Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 6384 6385 Font& rFont = m_aCurrentPDFState.m_aFont; 6386 if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 ) 6387 rFont.SetColor( Color( COL_LIGHTGRAY ) ); 6388 else 6389 rFont.SetColor( Color( COL_BLACK ) ); 6390 rFont.SetShadow( false ); 6391 rFont.SetOutline( false ); 6392 setFont( rFont ); 6393 setTextLineColor( rFont.GetColor() ); 6394 setOverlineColor( rFont.GetColor() ); 6395 updateGraphicsState(); 6396 6397 long nOff = 1 + ((m_pReferenceDevice->mpFontInstance->mnLineHeight-24)/24); 6398 if( rFont.IsOutline() ) 6399 nOff++; 6400 rLayout.DrawBase() += Point( nOff, nOff ); 6401 drawLayout( rLayout, rText, bTextLines ); 6402 rLayout.DrawBase() -= Point( nOff, nOff ); 6403 6404 setFont( aSaveFont ); 6405 setTextLineColor( aSaveTextLineColor ); 6406 setOverlineColor( aSaveOverlineColor ); 6407 updateGraphicsState(); 6408 } 6409 6410 void PDFWriterImpl::drawVerticalGlyphs( 6411 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs, 6412 OStringBuffer& rLine, 6413 const Point& rAlignOffset, 6414 const Matrix3& rRotScale, 6415 double fAngle, 6416 double fXScale, 6417 double fSkew, 6418 sal_Int32 nFontHeight ) 6419 { 6420 long nXOffset = 0; 6421 Point aCurPos( rGlyphs[0].m_aPos ); 6422 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos ); 6423 aCurPos += rAlignOffset; 6424 for( size_t i = 0; i < rGlyphs.size(); i++ ) 6425 { 6426 // have to emit each glyph on its own 6427 double fDeltaAngle = 0.0; 6428 double fYScale = 1.0; 6429 double fTempXScale = fXScale; 6430 double fSkewB = fSkew; 6431 double fSkewA = 0.0; 6432 6433 Point aDeltaPos; 6434 if (rGlyphs[i].m_bVertical) 6435 { 6436 fDeltaAngle = M_PI/2.0; 6437 aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent(); 6438 aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale); 6439 fYScale = fXScale; 6440 fTempXScale = 1.0; 6441 fSkewA = -fSkewB; 6442 fSkewB = 0.0; 6443 } 6444 aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) ); 6445 if( i < rGlyphs.size()-1 ) 6446 // #i120627# the text on the Y axis is reversed when export ppt file to PDF format 6447 { 6448 long nOffsetX = rGlyphs[i+1].m_aPos.X() - rGlyphs[i].m_aPos.X(); 6449 long nOffsetY = rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y(); 6450 nXOffset += (int)sqrt(double(nOffsetX*nOffsetX + nOffsetY*nOffsetY)); 6451 } 6452 if( ! rGlyphs[i].m_nGlyphId ) 6453 continue; 6454 6455 aDeltaPos = rRotScale.transform( aDeltaPos ); 6456 6457 Matrix3 aMat; 6458 if( fSkewB != 0.0 || fSkewA != 0.0 ) 6459 aMat.skew( fSkewA, fSkewB ); 6460 aMat.scale( fTempXScale, fYScale ); 6461 aMat.rotate( fAngle+fDeltaAngle ); 6462 aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() ); 6463 aMat.append( m_aPages.back(), rLine ); 6464 rLine.append( " Tm" ); 6465 if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId ) 6466 { 6467 rLine.append( " /F" ); 6468 rLine.append( rGlyphs[i].m_nMappedFontId ); 6469 rLine.append( ' ' ); 6470 m_aPages.back().appendMappedLength( nFontHeight, rLine ); 6471 rLine.append( " Tf" ); 6472 } 6473 rLine.append( "<" ); 6474 appendHex( rGlyphs[i].m_nMappedGlyphId, rLine ); 6475 rLine.append( ">Tj\n" ); 6476 } 6477 } 6478 6479 void PDFWriterImpl::drawHorizontalGlyphs( 6480 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs, 6481 OStringBuffer& rLine, 6482 const Point& rAlignOffset, 6483 double fAngle, 6484 double fXScale, 6485 double fSkew, 6486 sal_Int32 nFontHeight, 6487 sal_Int32 nPixelFontHeight 6488 ) 6489 { 6490 // horizontal (= normal) case 6491 6492 // fill in run end indices 6493 // end is marked by index of the first glyph of the next run 6494 // a run is marked by same mapped font id and same Y position 6495 std::vector< sal_uInt32 > aRunEnds; 6496 aRunEnds.reserve( rGlyphs.size() ); 6497 for( size_t i = 1; i < rGlyphs.size(); i++ ) 6498 { 6499 if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId || 6500 rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() ) 6501 { 6502 aRunEnds.push_back(i); 6503 } 6504 } 6505 // last run ends at last glyph 6506 aRunEnds.push_back( rGlyphs.size() ); 6507 6508 // loop over runs of the same font 6509 sal_uInt32 nBeginRun = 0; 6510 for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ ) 6511 { 6512 // setup text matrix 6513 Point aCurPos = rGlyphs[nBeginRun].m_aPos; 6514 // back transformation to current coordinate system 6515 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos ); 6516 aCurPos += rAlignOffset; 6517 // the first run can be set with "Td" operator 6518 // subsequent use of that operator would move 6519 // the textline matrix relative to what was set before 6520 // making use of that would drive us into rounding issues 6521 Matrix3 aMat; 6522 if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 ) 6523 { 6524 m_aPages.back().appendPoint( aCurPos, rLine ); 6525 rLine.append( " Td " ); 6526 } 6527 else 6528 { 6529 if( fSkew != 0.0 ) 6530 aMat.skew( 0.0, fSkew ); 6531 aMat.scale( fXScale, 1.0 ); 6532 aMat.rotate( fAngle ); 6533 aMat.translate( aCurPos.X(), aCurPos.Y() ); 6534 aMat.append( m_aPages.back(), rLine ); 6535 rLine.append( " Tm\n" ); 6536 } 6537 // set up correct font 6538 rLine.append( "/F" ); 6539 rLine.append( rGlyphs[nBeginRun].m_nMappedFontId ); 6540 rLine.append( ' ' ); 6541 m_aPages.back().appendMappedLength( nFontHeight, rLine ); 6542 rLine.append( " Tf" ); 6543 6544 // output glyphs using Tj or TJ 6545 OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 ); 6546 aKernedLine.append( "[<" ); 6547 aUnkernedLine.append( '<' ); 6548 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine ); 6549 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine ); 6550 6551 aMat.invert(); 6552 bool bNeedKern = false; 6553 for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ ) 6554 { 6555 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine ); 6556 // check if default glyph positioning is sufficient 6557 const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos ); 6558 const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos ); 6559 double fAdvance = aThisPos.X() - aPrevPos.X(); 6560 fAdvance *= 1000.0 / nPixelFontHeight; 6561 const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5); 6562 if( nAdjustment != 0 ) 6563 { 6564 // apply individual glyph positioning 6565 bNeedKern = true; 6566 aKernedLine.append( ">" ); 6567 aKernedLine.append( nAdjustment ); 6568 aKernedLine.append( "<" ); 6569 } 6570 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine ); 6571 } 6572 aKernedLine.append( ">]TJ\n" ); 6573 aUnkernedLine.append( ">Tj\n" ); 6574 rLine.append( 6575 (bNeedKern ? aKernedLine : aUnkernedLine).makeStringAndClear() ); 6576 6577 // set beginning of next run 6578 nBeginRun = aRunEnds[nRun]; 6579 } 6580 } 6581 6582 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool bTextLines ) 6583 { 6584 // relief takes precedence over shadow (see outdev3.cxx) 6585 if( m_aCurrentPDFState.m_aFont.GetRelief() != FontRelief::NONE ) 6586 { 6587 drawRelief( rLayout, rText, bTextLines ); 6588 return; 6589 } 6590 else if( m_aCurrentPDFState.m_aFont.IsShadow() ) 6591 drawShadow( rLayout, rText, bTextLines ); 6592 6593 OStringBuffer aLine( 512 ); 6594 6595 const int nMaxGlyphs = 256; 6596 6597 const GlyphItem* pGlyphs[nMaxGlyphs] = { nullptr }; 6598 const PhysicalFontFace* pFallbackFonts[nMaxGlyphs] = { nullptr }; 6599 sal_Int32 pGlyphWidths[nMaxGlyphs]; 6600 sal_uInt8 pMappedGlyphs[nMaxGlyphs]; 6601 sal_Int32 pMappedFontObjects[nMaxGlyphs]; 6602 std::vector<sal_Ucs> aCodeUnits; 6603 aCodeUnits.reserve(nMaxGlyphs); 6604 std::vector<sal_Int32> aCodeUnitsPerGlyph; 6605 aCodeUnits.reserve(nMaxGlyphs); 6606 bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical(); 6607 int nGlyphs; 6608 int nIndex = 0; 6609 int nMaxCharPos = rText.getLength()-1; 6610 double fXScale = 1.0; 6611 double fSkew = 0.0; 6612 sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontInstance->maFontSelData.mnHeight; 6613 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlignment(); 6614 6615 // transform font height back to current units 6616 // note: the layout calculates in outdevs device pixel !! 6617 sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight ); 6618 if( m_aCurrentPDFState.m_aFont.GetAverageFontWidth() ) 6619 { 6620 Font aFont( m_aCurrentPDFState.m_aFont ); 6621 aFont.SetAverageFontWidth( 0 ); 6622 FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont ); 6623 if( aMetric.GetAverageFontWidth() != m_aCurrentPDFState.m_aFont.GetAverageFontWidth() ) 6624 { 6625 fXScale = 6626 (double)m_aCurrentPDFState.m_aFont.GetAverageFontWidth() / 6627 (double)aMetric.GetAverageFontWidth(); 6628 } 6629 // force state before GetFontMetric 6630 m_pReferenceDevice->ImplNewFont(); 6631 } 6632 6633 // perform artificial italics if necessary 6634 if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL || 6635 m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) && 6636 !( m_pReferenceDevice->mpFontInstance->maFontSelData.mpFontData->GetItalic() == ITALIC_NORMAL || 6637 m_pReferenceDevice->mpFontInstance->maFontSelData.mpFontData->GetItalic() == ITALIC_OBLIQUE ) 6638 ) 6639 { 6640 fSkew = M_PI/12.0; 6641 } 6642 6643 // if the mapmode is distorted we need to adjust for that also 6644 if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() ) 6645 { 6646 fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY()); 6647 } 6648 6649 int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation(); 6650 // normalize angles 6651 while( nAngle < 0 ) 6652 nAngle += 3600; 6653 nAngle = nAngle % 3600; 6654 double fAngle = (double)nAngle * M_PI / 1800.0; 6655 6656 Matrix3 aRotScale; 6657 aRotScale.scale( fXScale, 1.0 ); 6658 if( fAngle != 0.0 ) 6659 aRotScale.rotate( -fAngle ); 6660 6661 bool bPop = false; 6662 bool bABold = false; 6663 // artificial bold necessary ? 6664 if( m_pReferenceDevice->mpFontInstance->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM && 6665 m_pReferenceDevice->mpFontInstance->maFontSelData.GetWeight() > WEIGHT_MEDIUM ) 6666 { 6667 if( ! bPop ) 6668 aLine.append( "q " ); 6669 bPop = true; 6670 bABold = true; 6671 } 6672 // setup text colors (if necessary) 6673 Color aStrokeColor( COL_TRANSPARENT ); 6674 Color aNonStrokeColor( COL_TRANSPARENT ); 6675 6676 if( m_aCurrentPDFState.m_aFont.IsOutline() ) 6677 { 6678 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 6679 aNonStrokeColor = Color( COL_WHITE ); 6680 } 6681 else 6682 aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 6683 if( bABold ) 6684 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 6685 6686 if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor ) 6687 { 6688 if( ! bPop ) 6689 aLine.append( "q " ); 6690 bPop = true; 6691 appendStrokingColor( aStrokeColor, aLine ); 6692 aLine.append( "\n" ); 6693 } 6694 if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor ) 6695 { 6696 if( ! bPop ) 6697 aLine.append( "q " ); 6698 bPop = true; 6699 appendNonStrokingColor( aNonStrokeColor, aLine ); 6700 aLine.append( "\n" ); 6701 } 6702 6703 // begin text object 6704 aLine.append( "BT\n" ); 6705 // outline attribute ? 6706 if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold ) 6707 { 6708 // set correct text mode, set stroke width 6709 aLine.append( "2 Tr " ); // fill, then stroke 6710 6711 if( m_aCurrentPDFState.m_aFont.IsOutline() ) 6712 { 6713 // unclear what to do in case of outline and artificial bold 6714 // for the time being outline wins 6715 aLine.append( "0.25 w \n" ); 6716 } 6717 else 6718 { 6719 double fW = (double)m_aCurrentPDFState.m_aFont.GetFontHeight() / 30.0; 6720 m_aPages.back().appendMappedLength( fW, aLine ); 6721 aLine.append ( " w\n" ); 6722 } 6723 } 6724 6725 FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric(); 6726 6727 // collect the glyphs into a single array 6728 const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686# 6729 std::vector< PDFGlyph > aGlyphs; 6730 aGlyphs.reserve( nTmpMaxGlyphs ); 6731 // first get all the glyphs and register them; coordinates still in Pixel 6732 Point aGNGlyphPos; 6733 while ((nGlyphs = rLayout.GetNextGlyphs(nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, pFallbackFonts)) != 0) 6734 { 6735 aCodeUnits.clear(); 6736 for( int i = 0; i < nGlyphs; i++ ) 6737 { 6738 // default case: 1 glyph is one unicode 6739 aCodeUnitsPerGlyph.push_back(1); 6740 if (pGlyphs[i]->mnCharPos >= 0 && pGlyphs[i]->mnCharPos <= nMaxCharPos) 6741 { 6742 int nChars = 1; 6743 // try to handle ligatures and such 6744 if( i < nGlyphs-1 ) 6745 { 6746 nChars = pGlyphs[i+1]->mnCharPos - pGlyphs[i]->mnCharPos; 6747 int start = pGlyphs[i]->mnCharPos; 6748 // #i115618# fix for simple RTL+CTL cases 6749 // supports RTL ligatures. TODO: more complex CTL, etc. 6750 if( nChars < 0 ) 6751 { 6752 nChars = -nChars; 6753 start = pGlyphs[i+1]->mnCharPos + 1; 6754 } 6755 else if (nChars == 0) 6756 nChars = 1; 6757 aCodeUnitsPerGlyph.back() = nChars; 6758 for( int n = 0; n < nChars; n++ ) 6759 aCodeUnits.push_back( rText[ start + n ] ); 6760 } 6761 else 6762 aCodeUnits.push_back(rText[pGlyphs[i]->mnCharPos]); 6763 } 6764 else 6765 aCodeUnits.push_back( 0 ); 6766 // note: in case of ctl one character may result 6767 // in multiple glyphs. The current SalLayout 6768 // implementations set -1 then to indicate that no direct 6769 // mapping is possible 6770 } 6771 6772 registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, aCodeUnits.data(), aCodeUnitsPerGlyph.data(), pMappedGlyphs, pMappedFontObjects, pFallbackFonts ); 6773 6774 for( int i = 0; i < nGlyphs; i++ ) 6775 { 6776 aGlyphs.emplace_back( aGNGlyphPos, 6777 pGlyphWidths[i], 6778 pGlyphs[i]->maGlyphId, 6779 pMappedFontObjects[i], 6780 pMappedGlyphs[i], 6781 pGlyphs[i]->IsVertical() ); 6782 if( bVertical ) 6783 aGNGlyphPos.Y() += pGlyphs[i]->mnNewWidth/rLayout.GetUnitsPerPixel(); 6784 else 6785 aGNGlyphPos.X() += pGlyphs[i]->mnNewWidth/rLayout.GetUnitsPerPixel(); 6786 } 6787 } 6788 6789 // Avoid fill color when map mode is in pixels, the below code assumes 6790 // logic map mode. 6791 bool bPixel = m_aCurrentPDFState.m_aMapMode.GetMapUnit() == MapUnit::MapPixel; 6792 if (m_aCurrentPDFState.m_aFont.GetFillColor() != Color(COL_TRANSPARENT) && !bPixel) 6793 { 6794 // PDF doesn't have a text fill color, so draw a rectangle before 6795 // drawing the actual text. 6796 push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR); 6797 setFillColor(m_aCurrentPDFState.m_aFont.GetFillColor()); 6798 // Avoid border around the rectangle for Writer shape text. 6799 setLineColor(Color(COL_TRANSPARENT)); 6800 6801 // The rectangle is the bounding box of the text, but also includes 6802 // ascent / descent to match the on-screen rendering. 6803 tools::Rectangle aRectangle; 6804 // This is the top left of the text without ascent / descent. 6805 aRectangle.SetPos(m_pReferenceDevice->PixelToLogic(rLayout.GetDrawPosition())); 6806 aRectangle.setY(aRectangle.getY() - aRefDevFontMetric.GetAscent()); 6807 aRectangle.SetSize(m_pReferenceDevice->PixelToLogic(Size(rLayout.GetTextWidth(), 0))); 6808 // This includes ascent / descent. 6809 aRectangle.setHeight(aRefDevFontMetric.GetLineHeight()); 6810 6811 LogicalFontInstance* pFontInstance = m_pReferenceDevice->mpFontInstance; 6812 if (pFontInstance->mnOrientation) 6813 { 6814 // Adapt rectangle for rotated text. 6815 tools::Polygon aPolygon(aRectangle); 6816 aPolygon.Rotate(m_pReferenceDevice->PixelToLogic(rLayout.GetDrawPosition()), pFontInstance->mnOrientation); 6817 drawPolygon(aPolygon); 6818 } 6819 else 6820 drawRectangle(aRectangle); 6821 6822 pop(); 6823 } 6824 6825 Point aAlignOffset; 6826 if ( eAlign == ALIGN_BOTTOM ) 6827 aAlignOffset.Y() -= aRefDevFontMetric.GetDescent(); 6828 else if ( eAlign == ALIGN_TOP ) 6829 aAlignOffset.Y() += aRefDevFontMetric.GetAscent(); 6830 if( aAlignOffset.X() || aAlignOffset.Y() ) 6831 aAlignOffset = aRotScale.transform( aAlignOffset ); 6832 6833 /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original 6834 string contained only on of the UTF16 BOMs 6835 */ 6836 if( ! aGlyphs.empty() ) 6837 { 6838 if( bVertical ) 6839 drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight ); 6840 else 6841 drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight ); 6842 } 6843 6844 // end textobject 6845 aLine.append( "ET\n" ); 6846 if( bPop ) 6847 aLine.append( "Q\n" ); 6848 6849 writeBuffer( aLine.getStr(), aLine.getLength() ); 6850 6851 // draw eventual textlines 6852 FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout(); 6853 FontLineStyle eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline(); 6854 FontLineStyle eOverline = m_aCurrentPDFState.m_aFont.GetOverline(); 6855 if( bTextLines && 6856 ( 6857 ( eUnderline != LINESTYLE_NONE && eUnderline != LINESTYLE_DONTKNOW ) || 6858 ( eOverline != LINESTYLE_NONE && eOverline != LINESTYLE_DONTKNOW ) || 6859 ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW ) 6860 ) 6861 ) 6862 { 6863 bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont ); 6864 if( m_aCurrentPDFState.m_aFont.IsWordLineMode() ) 6865 { 6866 Point aPos, aStartPt; 6867 sal_Int32 nWidth = 0; 6868 const GlyphItem* pGlyph; 6869 int nStart = 0; 6870 while (rLayout.GetNextGlyphs(1, &pGlyph, aPos, nStart)) 6871 { 6872 if (!pGlyph->IsSpacing()) 6873 { 6874 if( !nWidth ) 6875 aStartPt = aPos; 6876 6877 nWidth += pGlyph->mnNewWidth; 6878 } 6879 else if( nWidth > 0 ) 6880 { 6881 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 6882 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 6883 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 6884 nWidth = 0; 6885 } 6886 } 6887 6888 if( nWidth > 0 ) 6889 { 6890 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 6891 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 6892 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 6893 } 6894 } 6895 else 6896 { 6897 Point aStartPt = rLayout.GetDrawPosition(); 6898 int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel(); 6899 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 6900 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 6901 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 6902 } 6903 } 6904 6905 // write eventual emphasis marks 6906 if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & FontEmphasisMark::Style ) 6907 { 6908 tools::PolyPolygon aEmphPoly; 6909 tools::Rectangle aEmphRect1; 6910 tools::Rectangle aEmphRect2; 6911 long nEmphYOff; 6912 long nEmphWidth; 6913 long nEmphHeight; 6914 bool bEmphPolyLine; 6915 FontEmphasisMark nEmphMark; 6916 6917 push( PushFlags::ALL ); 6918 6919 aLine.setLength( 0 ); 6920 aLine.append( "q\n" ); 6921 6922 nEmphMark = OutputDevice::ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont ); 6923 if ( nEmphMark & FontEmphasisMark::PosBelow ) 6924 nEmphHeight = m_pReferenceDevice->mnEmphasisDescent; 6925 else 6926 nEmphHeight = m_pReferenceDevice->mnEmphasisAscent; 6927 m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly, 6928 bEmphPolyLine, 6929 aEmphRect1, 6930 aEmphRect2, 6931 nEmphYOff, 6932 nEmphWidth, 6933 nEmphMark, 6934 m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight) ); 6935 if ( bEmphPolyLine ) 6936 { 6937 setLineColor( m_aCurrentPDFState.m_aFont.GetColor() ); 6938 setFillColor( Color( COL_TRANSPARENT ) ); 6939 } 6940 else 6941 { 6942 setFillColor( m_aCurrentPDFState.m_aFont.GetColor() ); 6943 setLineColor( Color( COL_TRANSPARENT ) ); 6944 } 6945 writeBuffer( aLine.getStr(), aLine.getLength() ); 6946 6947 Point aOffset = Point(0,0); 6948 6949 if ( nEmphMark & FontEmphasisMark::PosBelow ) 6950 aOffset.Y() += m_pReferenceDevice->mpFontInstance->mxFontMetric->GetDescent() + nEmphYOff; 6951 else 6952 aOffset.Y() -= m_pReferenceDevice->mpFontInstance->mxFontMetric->GetAscent() + nEmphYOff; 6953 6954 long nEmphWidth2 = nEmphWidth / 2; 6955 long nEmphHeight2 = nEmphHeight / 2; 6956 aOffset += Point( nEmphWidth2, nEmphHeight2 ); 6957 6958 if ( eAlign == ALIGN_BOTTOM ) 6959 aOffset.Y() -= m_pReferenceDevice->mpFontInstance->mxFontMetric->GetDescent(); 6960 else if ( eAlign == ALIGN_TOP ) 6961 aOffset.Y() += m_pReferenceDevice->mpFontInstance->mxFontMetric->GetAscent(); 6962 6963 Point aPos; 6964 const GlyphItem* pGlyph; 6965 int nStart = 0; 6966 while (rLayout.GetNextGlyphs(1, &pGlyph, aPos, nStart)) 6967 { 6968 if (pGlyph->IsSpacing()) 6969 { 6970 Point aAdjOffset = aOffset; 6971 aAdjOffset.X() += (pGlyph->mnNewWidth - nEmphWidth) / 2; 6972 aAdjOffset = aRotScale.transform( aAdjOffset ); 6973 6974 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 ); 6975 6976 aPos += aAdjOffset; 6977 aPos = m_pReferenceDevice->PixelToLogic( aPos ); 6978 drawEmphasisMark( aPos.X(), aPos.Y(), 6979 aEmphPoly, bEmphPolyLine, 6980 aEmphRect1, aEmphRect2 ); 6981 } 6982 } 6983 6984 writeBuffer( "Q\n", 2 ); 6985 pop(); 6986 } 6987 } 6988 6989 void PDFWriterImpl::drawEmphasisMark( long nX, long nY, 6990 const tools::PolyPolygon& rPolyPoly, bool bPolyLine, 6991 const tools::Rectangle& rRect1, const tools::Rectangle& rRect2 ) 6992 { 6993 // TODO: pass nWidth as width of this mark 6994 // long nWidth = 0; 6995 6996 if ( rPolyPoly.Count() ) 6997 { 6998 if ( bPolyLine ) 6999 { 7000 tools::Polygon aPoly = rPolyPoly.GetObject( 0 ); 7001 aPoly.Move( nX, nY ); 7002 drawPolyLine( aPoly ); 7003 } 7004 else 7005 { 7006 tools::PolyPolygon aPolyPoly = rPolyPoly; 7007 aPolyPoly.Move( nX, nY ); 7008 drawPolyPolygon( aPolyPoly ); 7009 } 7010 } 7011 7012 if ( !rRect1.IsEmpty() ) 7013 { 7014 tools::Rectangle aRect( Point( nX+rRect1.Left(), 7015 nY+rRect1.Top() ), rRect1.GetSize() ); 7016 drawRectangle( aRect ); 7017 } 7018 7019 if ( !rRect2.IsEmpty() ) 7020 { 7021 tools::Rectangle aRect( Point( nX+rRect2.Left(), 7022 nY+rRect2.Top() ), rRect2.GetSize() ); 7023 7024 drawRectangle( aRect ); 7025 } 7026 } 7027 7028 void PDFWriterImpl::drawText( const Point& rPos, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen, bool bTextLines ) 7029 { 7030 MARK( "drawText" ); 7031 7032 updateGraphicsState(); 7033 7034 // get a layout from the OutputDevice's SalGraphics 7035 // this also enforces font substitution and sets the font on SalGraphics 7036 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos ); 7037 if( pLayout ) 7038 { 7039 drawLayout( *pLayout, rText, bTextLines ); 7040 delete pLayout; 7041 } 7042 } 7043 7044 void PDFWriterImpl::drawTextArray( const Point& rPos, const OUString& rText, const long* pDXArray, sal_Int32 nIndex, sal_Int32 nLen ) 7045 { 7046 MARK( "drawText with array" ); 7047 7048 updateGraphicsState(); 7049 7050 // get a layout from the OutputDevice's SalGraphics 7051 // this also enforces font substitution and sets the font on SalGraphics 7052 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray ); 7053 if( pLayout ) 7054 { 7055 drawLayout( *pLayout, rText, true ); 7056 delete pLayout; 7057 } 7058 } 7059 7060 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const OUString& rText, sal_Int32 nIndex, sal_Int32 nLen ) 7061 { 7062 MARK( "drawStretchText" ); 7063 7064 updateGraphicsState(); 7065 7066 // get a layout from the OutputDevice's SalGraphics 7067 // this also enforces font substitution and sets the font on SalGraphics 7068 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth ); 7069 if( pLayout ) 7070 { 7071 drawLayout( *pLayout, rText, true ); 7072 delete pLayout; 7073 } 7074 } 7075 7076 void PDFWriterImpl::drawText( const tools::Rectangle& rRect, const OUString& rOrigStr, DrawTextFlags nStyle ) 7077 { 7078 long nWidth = rRect.GetWidth(); 7079 long nHeight = rRect.GetHeight(); 7080 7081 if ( nWidth <= 0 || nHeight <= 0 ) 7082 return; 7083 7084 MARK( "drawText with rectangle" ); 7085 7086 updateGraphicsState(); 7087 7088 // clip with rectangle 7089 OStringBuffer aLine; 7090 aLine.append( "q " ); 7091 m_aPages.back().appendRect( rRect, aLine ); 7092 aLine.append( " W* n\n" ); 7093 writeBuffer( aLine.getStr(), aLine.getLength() ); 7094 7095 // if disabled text is needed, put in here 7096 7097 Point aPos = rRect.TopLeft(); 7098 7099 long nTextHeight = m_pReferenceDevice->GetTextHeight(); 7100 sal_Int32 nMnemonicPos = -1; 7101 7102 OUString aStr = rOrigStr; 7103 if ( nStyle & DrawTextFlags::Mnemonic ) 7104 aStr = OutputDevice::GetNonMnemonicString( aStr, nMnemonicPos ); 7105 7106 // multiline text 7107 if ( nStyle & DrawTextFlags::MultiLine ) 7108 { 7109 OUString aLastLine; 7110 ImplMultiTextLineInfo aMultiLineInfo; 7111 ImplTextLineInfo* pLineInfo; 7112 sal_Int32 i; 7113 sal_Int32 nLines; 7114 sal_Int32 nFormatLines; 7115 7116 if ( nTextHeight ) 7117 { 7118 vcl::DefaultTextLayout aLayout( *m_pReferenceDevice ); 7119 OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout ); 7120 nLines = nHeight/nTextHeight; 7121 nFormatLines = aMultiLineInfo.Count(); 7122 if ( !nLines ) 7123 nLines = 1; 7124 if ( nFormatLines > nLines ) 7125 { 7126 if ( nStyle & DrawTextFlags::EndEllipsis ) 7127 { 7128 // handle last line 7129 nFormatLines = nLines-1; 7130 7131 pLineInfo = aMultiLineInfo.GetLine( nFormatLines ); 7132 aLastLine = convertLineEnd(aStr.copy(pLineInfo->GetIndex()), LINEEND_LF); 7133 // replace line feed by space 7134 aLastLine = aLastLine.replace('\n', ' '); 7135 aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle ); 7136 nStyle &= ~DrawTextFlags(DrawTextFlags::VCenter | DrawTextFlags::Bottom); 7137 nStyle |= DrawTextFlags::Top; 7138 } 7139 } 7140 7141 // vertical alignment 7142 if ( nStyle & DrawTextFlags::Bottom ) 7143 aPos.Y() += nHeight-(nFormatLines*nTextHeight); 7144 else if ( nStyle & DrawTextFlags::VCenter ) 7145 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2; 7146 7147 // draw all lines excluding the last 7148 for ( i = 0; i < nFormatLines; i++ ) 7149 { 7150 pLineInfo = aMultiLineInfo.GetLine( i ); 7151 if ( nStyle & DrawTextFlags::Right ) 7152 aPos.X() += nWidth-pLineInfo->GetWidth(); 7153 else if ( nStyle & DrawTextFlags::Center ) 7154 aPos.X() += (nWidth-pLineInfo->GetWidth())/2; 7155 sal_Int32 nIndex = pLineInfo->GetIndex(); 7156 sal_Int32 nLineLen = pLineInfo->GetLen(); 7157 drawText( aPos, aStr, nIndex, nLineLen ); 7158 // mnemonics should not appear in documents, 7159 // if the need arises, put them in here 7160 aPos.Y() += nTextHeight; 7161 aPos.X() = rRect.Left(); 7162 } 7163 7164 // output last line left adjusted since it was shortened 7165 if (!aLastLine.isEmpty()) 7166 drawText( aPos, aLastLine, 0, aLastLine.getLength() ); 7167 } 7168 } 7169 else 7170 { 7171 long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); 7172 7173 // Evt. Text kuerzen 7174 if ( nTextWidth > nWidth ) 7175 { 7176 if ( nStyle & (DrawTextFlags::EndEllipsis | DrawTextFlags::PathEllipsis | DrawTextFlags::NewsEllipsis) ) 7177 { 7178 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle ); 7179 nStyle &= ~DrawTextFlags(DrawTextFlags::Center | DrawTextFlags::Right); 7180 nStyle |= DrawTextFlags::Left; 7181 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); 7182 } 7183 } 7184 7185 // vertical alignment 7186 if ( nStyle & DrawTextFlags::Right ) 7187 aPos.X() += nWidth-nTextWidth; 7188 else if ( nStyle & DrawTextFlags::Center ) 7189 aPos.X() += (nWidth-nTextWidth)/2; 7190 7191 if ( nStyle & DrawTextFlags::Bottom ) 7192 aPos.Y() += nHeight-nTextHeight; 7193 else if ( nStyle & DrawTextFlags::VCenter ) 7194 aPos.Y() += (nHeight-nTextHeight)/2; 7195 7196 // mnemonics should be inserted here if the need arises 7197 7198 // draw the actual text 7199 drawText( aPos, aStr, 0, aStr.getLength() ); 7200 } 7201 7202 // reset clip region to original value 7203 aLine.setLength( 0 ); 7204 aLine.append( "Q\n" ); 7205 writeBuffer( aLine.getStr(), aLine.getLength() ); 7206 } 7207 7208 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop ) 7209 { 7210 MARK( "drawLine" ); 7211 7212 updateGraphicsState(); 7213 7214 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7215 return; 7216 7217 OStringBuffer aLine; 7218 m_aPages.back().appendPoint( rStart, aLine ); 7219 aLine.append( " m " ); 7220 m_aPages.back().appendPoint( rStop, aLine ); 7221 aLine.append( " l S\n" ); 7222 7223 writeBuffer( aLine.getStr(), aLine.getLength() ); 7224 } 7225 7226 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo ) 7227 { 7228 MARK( "drawLine with LineInfo" ); 7229 updateGraphicsState(); 7230 7231 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7232 return; 7233 7234 if( rInfo.GetStyle() == LineStyle::Solid && rInfo.GetWidth() < 2 ) 7235 { 7236 drawLine( rStart, rStop ); 7237 return; 7238 } 7239 7240 OStringBuffer aLine; 7241 7242 aLine.append( "q " ); 7243 if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) 7244 { 7245 m_aPages.back().appendPoint( rStart, aLine ); 7246 aLine.append( " m " ); 7247 m_aPages.back().appendPoint( rStop, aLine ); 7248 aLine.append( " l S Q\n" ); 7249 7250 writeBuffer( aLine.getStr(), aLine.getLength() ); 7251 } 7252 else 7253 { 7254 PDFWriter::ExtLineInfo aInfo; 7255 convertLineInfoToExtLineInfo( rInfo, aInfo ); 7256 Point aPolyPoints[2] = { rStart, rStop }; 7257 tools::Polygon aPoly( 2, aPolyPoints ); 7258 drawPolyLine( aPoly, aInfo ); 7259 } 7260 } 7261 7262 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x ) 7263 7264 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontLineStyle eTextLine, Color aColor, bool bIsAbove ) 7265 { 7266 // note: units in pFontInstance are ref device pixel 7267 LogicalFontInstance* pFontInstance = m_pReferenceDevice->mpFontInstance; 7268 long nLineHeight = 0; 7269 long nLinePos = 0; 7270 7271 appendStrokingColor( aColor, aLine ); 7272 aLine.append( "\n" ); 7273 7274 if ( bIsAbove ) 7275 { 7276 if ( !pFontInstance->mxFontMetric->GetAboveWavelineUnderlineSize() ) 7277 m_pReferenceDevice->ImplInitAboveTextLineSize(); 7278 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetAboveWavelineUnderlineSize() ); 7279 nLinePos = HCONV( pFontInstance->mxFontMetric->GetAboveWavelineUnderlineOffset() ); 7280 } 7281 else 7282 { 7283 if ( !pFontInstance->mxFontMetric->GetWavelineUnderlineSize() ) 7284 m_pReferenceDevice->ImplInitTextLineSize(); 7285 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetWavelineUnderlineSize() ); 7286 nLinePos = HCONV( pFontInstance->mxFontMetric->GetWavelineUnderlineOffset() ); 7287 } 7288 if ( (eTextLine == LINESTYLE_SMALLWAVE) && (nLineHeight > 3) ) 7289 nLineHeight = 3; 7290 7291 long nLineWidth = getReferenceDevice()->mnDPIX/450; 7292 if ( ! nLineWidth ) 7293 nLineWidth = 1; 7294 7295 if ( eTextLine == LINESTYLE_BOLDWAVE ) 7296 nLineWidth = 3*nLineWidth; 7297 7298 m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine ); 7299 aLine.append( " w " ); 7300 7301 if ( eTextLine == LINESTYLE_DOUBLEWAVE ) 7302 { 7303 long nOrgLineHeight = nLineHeight; 7304 nLineHeight /= 3; 7305 if ( nLineHeight < 2 ) 7306 { 7307 if ( nOrgLineHeight > 1 ) 7308 nLineHeight = 2; 7309 else 7310 nLineHeight = 1; 7311 } 7312 long nLineDY = nOrgLineHeight-(nLineHeight*2); 7313 if ( nLineDY < nLineWidth ) 7314 nLineDY = nLineWidth; 7315 long nLineDY2 = nLineDY/2; 7316 if ( !nLineDY2 ) 7317 nLineDY2 = 1; 7318 7319 nLinePos -= nLineWidth-nLineDY2; 7320 7321 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); 7322 7323 nLinePos += nLineWidth+nLineDY; 7324 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); 7325 } 7326 else 7327 { 7328 if ( eTextLine != LINESTYLE_BOLDWAVE ) 7329 nLinePos -= nLineWidth/2; 7330 m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine ); 7331 } 7332 } 7333 7334 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontLineStyle eTextLine, Color aColor, bool bIsAbove ) 7335 { 7336 // note: units in pFontInstance are ref device pixel 7337 LogicalFontInstance* pFontInstance = m_pReferenceDevice->mpFontInstance; 7338 long nLineHeight = 0; 7339 long nLinePos = 0; 7340 long nLinePos2 = 0; 7341 7342 if ( eTextLine > LINESTYLE_BOLDWAVE ) 7343 eTextLine = LINESTYLE_SINGLE; 7344 7345 switch ( eTextLine ) 7346 { 7347 case LINESTYLE_SINGLE: 7348 case LINESTYLE_DOTTED: 7349 case LINESTYLE_DASH: 7350 case LINESTYLE_LONGDASH: 7351 case LINESTYLE_DASHDOT: 7352 case LINESTYLE_DASHDOTDOT: 7353 if ( bIsAbove ) 7354 { 7355 if ( !pFontInstance->mxFontMetric->GetAboveUnderlineSize() ) 7356 m_pReferenceDevice->ImplInitAboveTextLineSize(); 7357 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetAboveUnderlineSize() ); 7358 nLinePos = HCONV( pFontInstance->mxFontMetric->GetAboveUnderlineOffset() ); 7359 } 7360 else 7361 { 7362 if ( !pFontInstance->mxFontMetric->GetUnderlineSize() ) 7363 m_pReferenceDevice->ImplInitTextLineSize(); 7364 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetUnderlineSize() ); 7365 nLinePos = HCONV( pFontInstance->mxFontMetric->GetUnderlineOffset() ); 7366 } 7367 break; 7368 case LINESTYLE_BOLD: 7369 case LINESTYLE_BOLDDOTTED: 7370 case LINESTYLE_BOLDDASH: 7371 case LINESTYLE_BOLDLONGDASH: 7372 case LINESTYLE_BOLDDASHDOT: 7373 case LINESTYLE_BOLDDASHDOTDOT: 7374 if ( bIsAbove ) 7375 { 7376 if ( !pFontInstance->mxFontMetric->GetAboveBoldUnderlineSize() ) 7377 m_pReferenceDevice->ImplInitAboveTextLineSize(); 7378 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetAboveBoldUnderlineSize() ); 7379 nLinePos = HCONV( pFontInstance->mxFontMetric->GetAboveBoldUnderlineOffset() ); 7380 } 7381 else 7382 { 7383 if ( !pFontInstance->mxFontMetric->GetBoldUnderlineSize() ) 7384 m_pReferenceDevice->ImplInitTextLineSize(); 7385 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetBoldUnderlineSize() ); 7386 nLinePos = HCONV( pFontInstance->mxFontMetric->GetBoldUnderlineOffset() ); 7387 nLinePos += nLineHeight/2; 7388 } 7389 break; 7390 case LINESTYLE_DOUBLE: 7391 if ( bIsAbove ) 7392 { 7393 if ( !pFontInstance->mxFontMetric->GetAboveDoubleUnderlineSize() ) 7394 m_pReferenceDevice->ImplInitAboveTextLineSize(); 7395 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetAboveDoubleUnderlineSize() ); 7396 nLinePos = HCONV( pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset1() ); 7397 nLinePos2 = HCONV( pFontInstance->mxFontMetric->GetAboveDoubleUnderlineOffset2() ); 7398 } 7399 else 7400 { 7401 if ( !pFontInstance->mxFontMetric->GetDoubleUnderlineSize() ) 7402 m_pReferenceDevice->ImplInitTextLineSize(); 7403 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetDoubleUnderlineSize() ); 7404 nLinePos = HCONV( pFontInstance->mxFontMetric->GetDoubleUnderlineOffset1() ); 7405 nLinePos2 = HCONV( pFontInstance->mxFontMetric->GetDoubleUnderlineOffset2() ); 7406 } 7407 break; 7408 default: 7409 break; 7410 } 7411 7412 if ( nLineHeight ) 7413 { 7414 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine ); 7415 aLine.append( " w " ); 7416 appendStrokingColor( aColor, aLine ); 7417 aLine.append( "\n" ); 7418 7419 switch ( eTextLine ) 7420 { 7421 case LINESTYLE_DOTTED: 7422 case LINESTYLE_BOLDDOTTED: 7423 aLine.append( "[ " ); 7424 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 7425 aLine.append( " ] 0 d\n" ); 7426 break; 7427 case LINESTYLE_DASH: 7428 case LINESTYLE_LONGDASH: 7429 case LINESTYLE_BOLDDASH: 7430 case LINESTYLE_BOLDLONGDASH: 7431 { 7432 sal_Int32 nDashLength = 4*nLineHeight; 7433 sal_Int32 nVoidLength = 2*nLineHeight; 7434 if ( ( eTextLine == LINESTYLE_LONGDASH ) || ( eTextLine == LINESTYLE_BOLDLONGDASH ) ) 7435 nDashLength = 8*nLineHeight; 7436 7437 aLine.append( "[ " ); 7438 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 7439 aLine.append( ' ' ); 7440 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 7441 aLine.append( " ] 0 d\n" ); 7442 } 7443 break; 7444 case LINESTYLE_DASHDOT: 7445 case LINESTYLE_BOLDDASHDOT: 7446 { 7447 sal_Int32 nDashLength = 4*nLineHeight; 7448 sal_Int32 nVoidLength = 2*nLineHeight; 7449 aLine.append( "[ " ); 7450 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 7451 aLine.append( ' ' ); 7452 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 7453 aLine.append( ' ' ); 7454 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 7455 aLine.append( ' ' ); 7456 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 7457 aLine.append( " ] 0 d\n" ); 7458 } 7459 break; 7460 case LINESTYLE_DASHDOTDOT: 7461 case LINESTYLE_BOLDDASHDOTDOT: 7462 { 7463 sal_Int32 nDashLength = 4*nLineHeight; 7464 sal_Int32 nVoidLength = 2*nLineHeight; 7465 aLine.append( "[ " ); 7466 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 7467 aLine.append( ' ' ); 7468 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 7469 aLine.append( ' ' ); 7470 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 7471 aLine.append( ' ' ); 7472 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 7473 aLine.append( ' ' ); 7474 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 7475 aLine.append( ' ' ); 7476 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 7477 aLine.append( " ] 0 d\n" ); 7478 } 7479 break; 7480 default: 7481 break; 7482 } 7483 7484 aLine.append( "0 " ); 7485 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine ); 7486 aLine.append( " m " ); 7487 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); 7488 aLine.append( ' ' ); 7489 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine ); 7490 aLine.append( " l S\n" ); 7491 if ( eTextLine == LINESTYLE_DOUBLE ) 7492 { 7493 aLine.append( "0 " ); 7494 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine ); 7495 aLine.append( " m " ); 7496 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); 7497 aLine.append( ' ' ); 7498 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine ); 7499 aLine.append( " l S\n" ); 7500 } 7501 } 7502 } 7503 7504 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor ) 7505 { 7506 // note: units in pFontInstance are ref device pixel 7507 LogicalFontInstance* pFontInstance = m_pReferenceDevice->mpFontInstance; 7508 long nLineHeight = 0; 7509 long nLinePos = 0; 7510 long nLinePos2 = 0; 7511 7512 if ( eStrikeout > STRIKEOUT_X ) 7513 eStrikeout = STRIKEOUT_SINGLE; 7514 7515 switch ( eStrikeout ) 7516 { 7517 case STRIKEOUT_SINGLE: 7518 if ( !pFontInstance->mxFontMetric->GetStrikeoutSize() ) 7519 m_pReferenceDevice->ImplInitTextLineSize(); 7520 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetStrikeoutSize() ); 7521 nLinePos = HCONV( pFontInstance->mxFontMetric->GetStrikeoutOffset() ); 7522 break; 7523 case STRIKEOUT_BOLD: 7524 if ( !pFontInstance->mxFontMetric->GetBoldStrikeoutSize() ) 7525 m_pReferenceDevice->ImplInitTextLineSize(); 7526 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetBoldStrikeoutSize() ); 7527 nLinePos = HCONV( pFontInstance->mxFontMetric->GetBoldStrikeoutOffset() ); 7528 break; 7529 case STRIKEOUT_DOUBLE: 7530 if ( !pFontInstance->mxFontMetric->GetDoubleStrikeoutSize() ) 7531 m_pReferenceDevice->ImplInitTextLineSize(); 7532 nLineHeight = HCONV( pFontInstance->mxFontMetric->GetDoubleStrikeoutSize() ); 7533 nLinePos = HCONV( pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset1() ); 7534 nLinePos2 = HCONV( pFontInstance->mxFontMetric->GetDoubleStrikeoutOffset2() ); 7535 break; 7536 default: 7537 break; 7538 } 7539 7540 if ( nLineHeight ) 7541 { 7542 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine ); 7543 aLine.append( " w " ); 7544 appendStrokingColor( aColor, aLine ); 7545 aLine.append( "\n" ); 7546 7547 aLine.append( "0 " ); 7548 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine ); 7549 aLine.append( " m " ); 7550 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine ); 7551 aLine.append( ' ' ); 7552 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine ); 7553 aLine.append( " l S\n" ); 7554 7555 if ( eStrikeout == STRIKEOUT_DOUBLE ) 7556 { 7557 aLine.append( "0 " ); 7558 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine ); 7559 aLine.append( " m " ); 7560 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine ); 7561 aLine.append( ' ' ); 7562 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine ); 7563 aLine.append( " l S\n" ); 7564 } 7565 } 7566 } 7567 7568 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout ) 7569 { 7570 //See qadevOOo/testdocs/StrikeThrough.odt for examples if you need 7571 //to tweak this 7572 7573 OUString aStrikeoutChar = eStrikeout == STRIKEOUT_SLASH ? OUString( "/" ) : OUString( "X" ); 7574 OUString aStrikeout = aStrikeoutChar; 7575 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth ) 7576 aStrikeout += aStrikeout; 7577 7578 // do not get broader than nWidth modulo 1 character 7579 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth ) 7580 aStrikeout = aStrikeout.replaceAt( 0, 1, "" ); 7581 aStrikeout += aStrikeoutChar; 7582 bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow(); 7583 if ( bShadow ) 7584 { 7585 Font aFont = m_aCurrentPDFState.m_aFont; 7586 aFont.SetShadow( false ); 7587 setFont( aFont ); 7588 updateGraphicsState(); 7589 } 7590 7591 // strikeout string is left aligned non-CTL text 7592 ComplexTextLayoutFlags nOrigTLM = m_pReferenceDevice->GetLayoutMode(); 7593 m_pReferenceDevice->SetLayoutMode(ComplexTextLayoutFlags::BiDiStrong); 7594 7595 push( PushFlags::CLIPREGION ); 7596 FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric(); 7597 tools::Rectangle aRect; 7598 aRect.Left() = rPos.X(); 7599 aRect.Right() = aRect.Left()+nWidth; 7600 aRect.Bottom() = rPos.Y()+aRefDevFontMetric.GetDescent(); 7601 aRect.Top() = rPos.Y()-aRefDevFontMetric.GetAscent(); 7602 7603 LogicalFontInstance* pFontInstance = m_pReferenceDevice->mpFontInstance; 7604 if (pFontInstance->mnOrientation) 7605 { 7606 tools::Polygon aPoly( aRect ); 7607 aPoly.Rotate( rPos, pFontInstance->mnOrientation); 7608 aRect = aPoly.GetBoundRect(); 7609 } 7610 7611 intersectClipRegion( aRect ); 7612 drawText( rPos, aStrikeout, 0, aStrikeout.getLength(), false ); 7613 pop(); 7614 7615 m_pReferenceDevice->SetLayoutMode( nOrigTLM ); 7616 7617 if ( bShadow ) 7618 { 7619 Font aFont = m_aCurrentPDFState.m_aFont; 7620 aFont.SetShadow( true ); 7621 setFont( aFont ); 7622 updateGraphicsState(); 7623 } 7624 } 7625 7626 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontLineStyle eUnderline, FontLineStyle eOverline, bool bUnderlineAbove ) 7627 { 7628 if ( !nWidth || 7629 ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) && 7630 ((eUnderline == LINESTYLE_NONE)||(eUnderline == LINESTYLE_DONTKNOW)) && 7631 ((eOverline == LINESTYLE_NONE)||(eOverline == LINESTYLE_DONTKNOW)) ) ) 7632 return; 7633 7634 MARK( "drawTextLine" ); 7635 updateGraphicsState(); 7636 7637 // note: units in pFontInstance are ref device pixel 7638 LogicalFontInstance* pFontInstance = m_pReferenceDevice->mpFontInstance; 7639 Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor; 7640 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 7641 Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor(); 7642 bool bStrikeoutDone = false; 7643 bool bUnderlineDone = false; 7644 bool bOverlineDone = false; 7645 7646 if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) ) 7647 { 7648 drawStrikeoutChar( rPos, nWidth, eStrikeout ); 7649 bStrikeoutDone = true; 7650 } 7651 7652 Point aPos( rPos ); 7653 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlignment(); 7654 if( eAlign == ALIGN_TOP ) 7655 aPos.Y() += HCONV( pFontInstance->mxFontMetric->GetAscent() ); 7656 else if( eAlign == ALIGN_BOTTOM ) 7657 aPos.Y() -= HCONV( pFontInstance->mxFontMetric->GetDescent() ); 7658 7659 OStringBuffer aLine( 512 ); 7660 // save GS 7661 aLine.append( "q " ); 7662 7663 // rotate and translate matrix 7664 double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0; 7665 Matrix3 aMat; 7666 aMat.rotate( fAngle ); 7667 aMat.translate( aPos.X(), aPos.Y() ); 7668 aMat.append( m_aPages.back(), aLine ); 7669 aLine.append( " cm\n" ); 7670 7671 if ( aUnderlineColor.GetTransparency() != 0 ) 7672 aUnderlineColor = aStrikeoutColor; 7673 7674 if ( (eUnderline == LINESTYLE_SMALLWAVE) || 7675 (eUnderline == LINESTYLE_WAVE) || 7676 (eUnderline == LINESTYLE_DOUBLEWAVE) || 7677 (eUnderline == LINESTYLE_BOLDWAVE) ) 7678 { 7679 drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 7680 bUnderlineDone = true; 7681 } 7682 7683 if ( (eOverline == LINESTYLE_SMALLWAVE) || 7684 (eOverline == LINESTYLE_WAVE) || 7685 (eOverline == LINESTYLE_DOUBLEWAVE) || 7686 (eOverline == LINESTYLE_BOLDWAVE) ) 7687 { 7688 drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); 7689 bOverlineDone = true; 7690 } 7691 7692 if ( !bUnderlineDone ) 7693 { 7694 drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 7695 } 7696 7697 if ( !bOverlineDone ) 7698 { 7699 drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); 7700 } 7701 7702 if ( !bStrikeoutDone ) 7703 { 7704 drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor ); 7705 } 7706 7707 aLine.append( "Q\n" ); 7708 writeBuffer( aLine.getStr(), aLine.getLength() ); 7709 } 7710 7711 void PDFWriterImpl::drawPolygon( const tools::Polygon& rPoly ) 7712 { 7713 MARK( "drawPolygon" ); 7714 7715 updateGraphicsState(); 7716 7717 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 7718 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 7719 return; 7720 7721 int nPoints = rPoly.GetSize(); 7722 OStringBuffer aLine( 20 * nPoints ); 7723 m_aPages.back().appendPolygon( rPoly, aLine ); 7724 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 7725 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 7726 aLine.append( "B*\n" ); 7727 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 7728 aLine.append( "S\n" ); 7729 else 7730 aLine.append( "f*\n" ); 7731 7732 writeBuffer( aLine.getStr(), aLine.getLength() ); 7733 } 7734 7735 void PDFWriterImpl::drawPolyPolygon( const tools::PolyPolygon& rPolyPoly ) 7736 { 7737 MARK( "drawPolyPolygon" ); 7738 7739 updateGraphicsState(); 7740 7741 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 7742 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 7743 return; 7744 7745 int nPolygons = rPolyPoly.Count(); 7746 7747 OStringBuffer aLine( 40 * nPolygons ); 7748 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 7749 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 7750 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 7751 aLine.append( "B*\n" ); 7752 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 7753 aLine.append( "S\n" ); 7754 else 7755 aLine.append( "f*\n" ); 7756 7757 writeBuffer( aLine.getStr(), aLine.getLength() ); 7758 } 7759 7760 void PDFWriterImpl::drawTransparent( const tools::PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent ) 7761 { 7762 SAL_WARN_IF( nTransparentPercent > 100, "vcl.pdfwriter", "invalid alpha value" ); 7763 nTransparentPercent = nTransparentPercent % 100; 7764 7765 MARK( "drawTransparent" ); 7766 7767 updateGraphicsState(); 7768 7769 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 7770 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 7771 return; 7772 7773 if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDFVersion::PDF_1_4 ) 7774 { 7775 m_aErrors.insert( m_bIsPDF_A1 ? 7776 PDFWriter::Warning_Transparency_Omitted_PDFA : 7777 PDFWriter::Warning_Transparency_Omitted_PDF13 ); 7778 7779 drawPolyPolygon( rPolyPoly ); 7780 return; 7781 } 7782 7783 // create XObject 7784 m_aTransparentObjects.emplace_back( ); 7785 // FIXME: polygons with beziers may yield incorrect bound rect 7786 m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect(); 7787 // convert rectangle to default user space 7788 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 7789 m_aTransparentObjects.back().m_nObject = createObject(); 7790 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 7791 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; 7792 m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 ); 7793 // create XObject's content stream 7794 OStringBuffer aContent( 256 ); 7795 m_aPages.back().appendPolyPolygon( rPolyPoly, aContent ); 7796 if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) && 7797 m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) ) 7798 aContent.append( " B*\n" ); 7799 else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) ) 7800 aContent.append( " S\n" ); 7801 else 7802 aContent.append( " f*\n" ); 7803 m_aTransparentObjects.back().m_pContentStream->WriteBytes( 7804 aContent.getStr(), aContent.getLength() ); 7805 7806 OStringBuffer aObjName( 16 ); 7807 aObjName.append( "Tr" ); 7808 aObjName.append( m_aTransparentObjects.back().m_nObject ); 7809 OString aTrName( aObjName.makeStringAndClear() ); 7810 aObjName.append( "EGS" ); 7811 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 7812 OString aExtName( aObjName.makeStringAndClear() ); 7813 7814 OStringBuffer aLine( 80 ); 7815 // insert XObject 7816 aLine.append( "q /" ); 7817 aLine.append( aExtName ); 7818 aLine.append( " gs /" ); 7819 aLine.append( aTrName ); 7820 aLine.append( " Do Q\n" ); 7821 writeBuffer( aLine.getStr(), aLine.getLength() ); 7822 7823 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 7824 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 7825 } 7826 7827 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject ) 7828 { 7829 if( nObject >= 0 ) 7830 { 7831 switch( eKind ) 7832 { 7833 case ResXObject: 7834 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject; 7835 if( ! m_aOutputStreams.empty() ) 7836 m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject; 7837 break; 7838 case ResExtGState: 7839 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject; 7840 if( ! m_aOutputStreams.empty() ) 7841 m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject; 7842 break; 7843 case ResShading: 7844 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject; 7845 if( ! m_aOutputStreams.empty() ) 7846 m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject; 7847 break; 7848 case ResPattern: 7849 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject; 7850 if( ! m_aOutputStreams.empty() ) 7851 m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject; 7852 break; 7853 } 7854 } 7855 } 7856 7857 void PDFWriterImpl::beginRedirect( SvStream* pStream, const tools::Rectangle& rTargetRect ) 7858 { 7859 push( PushFlags::ALL ); 7860 7861 // force reemitting clip region inside the new stream, and 7862 // prevent emitting an unbalanced "Q" at the start 7863 clearClipRegion(); 7864 // this is needed to point m_aCurrentPDFState at the pushed state 7865 // ... but it's pointless to actually write into the "outer" stream here! 7866 updateGraphicsState(NOWRITE); 7867 7868 m_aOutputStreams.push_front( StreamRedirect() ); 7869 m_aOutputStreams.front().m_pStream = pStream; 7870 m_aOutputStreams.front().m_aMapMode = m_aMapMode; 7871 7872 if( !rTargetRect.IsEmpty() ) 7873 { 7874 m_aOutputStreams.front().m_aTargetRect = 7875 lcl_convert( m_aGraphicsStack.front().m_aMapMode, 7876 m_aMapMode, 7877 getReferenceDevice(), 7878 rTargetRect ); 7879 Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft(); 7880 long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight()); 7881 aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom()); 7882 m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta ); 7883 } 7884 7885 // setup graphics state for independent object stream 7886 7887 // force reemitting colors 7888 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); 7889 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); 7890 } 7891 7892 SvStream* PDFWriterImpl::endRedirect() 7893 { 7894 SvStream* pStream = nullptr; 7895 if( ! m_aOutputStreams.empty() ) 7896 { 7897 pStream = m_aOutputStreams.front().m_pStream; 7898 m_aMapMode = m_aOutputStreams.front().m_aMapMode; 7899 m_aOutputStreams.pop_front(); 7900 } 7901 7902 pop(); 7903 7904 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); 7905 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); 7906 7907 // needed after pop() to set m_aCurrentPDFState 7908 updateGraphicsState(NOWRITE); 7909 7910 return pStream; 7911 } 7912 7913 void PDFWriterImpl::beginTransparencyGroup() 7914 { 7915 updateGraphicsState(); 7916 if( m_aContext.Version >= PDFWriter::PDFVersion::PDF_1_4 ) 7917 beginRedirect( new SvMemoryStream( 1024, 1024 ), tools::Rectangle() ); 7918 } 7919 7920 void PDFWriterImpl::endTransparencyGroup( const tools::Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent ) 7921 { 7922 SAL_WARN_IF( nTransparentPercent > 100, "vcl.pdfwriter", "invalid alpha value" ); 7923 nTransparentPercent = nTransparentPercent % 100; 7924 7925 if( m_aContext.Version >= PDFWriter::PDFVersion::PDF_1_4 ) 7926 { 7927 // create XObject 7928 m_aTransparentObjects.emplace_back( ); 7929 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox; 7930 // convert rectangle to default user space 7931 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 7932 m_aTransparentObjects.back().m_nObject = createObject(); 7933 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; 7934 // get XObject's content stream 7935 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect()); 7936 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 7937 7938 OStringBuffer aObjName( 16 ); 7939 aObjName.append( "Tr" ); 7940 aObjName.append( m_aTransparentObjects.back().m_nObject ); 7941 OString aTrName( aObjName.makeStringAndClear() ); 7942 aObjName.append( "EGS" ); 7943 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 7944 OString aExtName( aObjName.makeStringAndClear() ); 7945 7946 OStringBuffer aLine( 80 ); 7947 // insert XObject 7948 aLine.append( "q /" ); 7949 aLine.append( aExtName ); 7950 aLine.append( " gs /" ); 7951 aLine.append( aTrName ); 7952 aLine.append( " Do Q\n" ); 7953 writeBuffer( aLine.getStr(), aLine.getLength() ); 7954 7955 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 7956 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 7957 } 7958 } 7959 7960 void PDFWriterImpl::drawRectangle( const tools::Rectangle& rRect ) 7961 { 7962 MARK( "drawRectangle" ); 7963 7964 updateGraphicsState(); 7965 7966 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 7967 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 7968 return; 7969 7970 OStringBuffer aLine( 40 ); 7971 m_aPages.back().appendRect( rRect, aLine ); 7972 7973 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 7974 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 7975 aLine.append( " B*\n" ); 7976 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 7977 aLine.append( " S\n" ); 7978 else 7979 aLine.append( " f*\n" ); 7980 7981 writeBuffer( aLine.getStr(), aLine.getLength() ); 7982 } 7983 7984 void PDFWriterImpl::drawRectangle( const tools::Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound ) 7985 { 7986 MARK( "drawRectangle with rounded edges" ); 7987 7988 if( !nHorzRound && !nVertRound ) 7989 drawRectangle( rRect ); 7990 7991 updateGraphicsState(); 7992 7993 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 7994 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 7995 return; 7996 7997 if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 ) 7998 nHorzRound = rRect.GetWidth()/2; 7999 if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 ) 8000 nVertRound = rRect.GetHeight()/2; 8001 8002 Point aPoints[16]; 8003 const double kappa = 0.5522847498; 8004 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5); 8005 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5); 8006 8007 aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() ); 8008 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); 8009 aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() ); 8010 aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() ); 8011 8012 aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound ); 8013 aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky ); 8014 aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound ); 8015 aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky ); 8016 8017 aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 ); 8018 aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() ); 8019 aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() ); 8020 aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() ); 8021 8022 aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound ); 8023 aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky ); 8024 aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound ); 8025 aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky ); 8026 8027 OStringBuffer aLine( 80 ); 8028 m_aPages.back().appendPoint( aPoints[1], aLine ); 8029 aLine.append( " m " ); 8030 m_aPages.back().appendPoint( aPoints[2], aLine ); 8031 aLine.append( " l " ); 8032 m_aPages.back().appendPoint( aPoints[3], aLine ); 8033 aLine.append( ' ' ); 8034 m_aPages.back().appendPoint( aPoints[4], aLine ); 8035 aLine.append( ' ' ); 8036 m_aPages.back().appendPoint( aPoints[5], aLine ); 8037 aLine.append( " c\n" ); 8038 m_aPages.back().appendPoint( aPoints[6], aLine ); 8039 aLine.append( " l " ); 8040 m_aPages.back().appendPoint( aPoints[7], aLine ); 8041 aLine.append( ' ' ); 8042 m_aPages.back().appendPoint( aPoints[8], aLine ); 8043 aLine.append( ' ' ); 8044 m_aPages.back().appendPoint( aPoints[9], aLine ); 8045 aLine.append( " c\n" ); 8046 m_aPages.back().appendPoint( aPoints[10], aLine ); 8047 aLine.append( " l " ); 8048 m_aPages.back().appendPoint( aPoints[11], aLine ); 8049 aLine.append( ' ' ); 8050 m_aPages.back().appendPoint( aPoints[12], aLine ); 8051 aLine.append( ' ' ); 8052 m_aPages.back().appendPoint( aPoints[13], aLine ); 8053 aLine.append( " c\n" ); 8054 m_aPages.back().appendPoint( aPoints[14], aLine ); 8055 aLine.append( " l " ); 8056 m_aPages.back().appendPoint( aPoints[15], aLine ); 8057 aLine.append( ' ' ); 8058 m_aPages.back().appendPoint( aPoints[0], aLine ); 8059 aLine.append( ' ' ); 8060 m_aPages.back().appendPoint( aPoints[1], aLine ); 8061 aLine.append( " c " ); 8062 8063 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8064 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8065 aLine.append( "b*\n" ); 8066 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8067 aLine.append( "s\n" ); 8068 else 8069 aLine.append( "f*\n" ); 8070 8071 writeBuffer( aLine.getStr(), aLine.getLength() ); 8072 } 8073 8074 void PDFWriterImpl::drawEllipse( const tools::Rectangle& rRect ) 8075 { 8076 MARK( "drawEllipse" ); 8077 8078 updateGraphicsState(); 8079 8080 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8081 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8082 return; 8083 8084 Point aPoints[12]; 8085 const double kappa = 0.5522847498; 8086 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5); 8087 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5); 8088 8089 aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() ); 8090 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); 8091 aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() ); 8092 8093 aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 ); 8094 aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky ); 8095 aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky ); 8096 8097 aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 ); 8098 aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() ); 8099 aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() ); 8100 8101 aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 ); 8102 aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky ); 8103 aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky ); 8104 8105 OStringBuffer aLine( 80 ); 8106 m_aPages.back().appendPoint( aPoints[1], aLine ); 8107 aLine.append( " m " ); 8108 m_aPages.back().appendPoint( aPoints[2], aLine ); 8109 aLine.append( ' ' ); 8110 m_aPages.back().appendPoint( aPoints[3], aLine ); 8111 aLine.append( ' ' ); 8112 m_aPages.back().appendPoint( aPoints[4], aLine ); 8113 aLine.append( " c\n" ); 8114 m_aPages.back().appendPoint( aPoints[5], aLine ); 8115 aLine.append( ' ' ); 8116 m_aPages.back().appendPoint( aPoints[6], aLine ); 8117 aLine.append( ' ' ); 8118 m_aPages.back().appendPoint( aPoints[7], aLine ); 8119 aLine.append( " c\n" ); 8120 m_aPages.back().appendPoint( aPoints[8], aLine ); 8121 aLine.append( ' ' ); 8122 m_aPages.back().appendPoint( aPoints[9], aLine ); 8123 aLine.append( ' ' ); 8124 m_aPages.back().appendPoint( aPoints[10], aLine ); 8125 aLine.append( " c\n" ); 8126 m_aPages.back().appendPoint( aPoints[11], aLine ); 8127 aLine.append( ' ' ); 8128 m_aPages.back().appendPoint( aPoints[0], aLine ); 8129 aLine.append( ' ' ); 8130 m_aPages.back().appendPoint( aPoints[1], aLine ); 8131 aLine.append( " c " ); 8132 8133 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8134 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8135 aLine.append( "b*\n" ); 8136 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8137 aLine.append( "s\n" ); 8138 else 8139 aLine.append( "f*\n" ); 8140 8141 writeBuffer( aLine.getStr(), aLine.getLength() ); 8142 } 8143 8144 static double calcAngle( const tools::Rectangle& rRect, const Point& rPoint ) 8145 { 8146 Point aOrigin((rRect.Left()+rRect.Right()+1)/2, 8147 (rRect.Top()+rRect.Bottom()+1)/2); 8148 Point aPoint = rPoint - aOrigin; 8149 8150 double fX = (double)aPoint.X(); 8151 double fY = (double)-aPoint.Y(); 8152 8153 if ((rRect.GetHeight() == 0) || (rRect.GetWidth() == 0)) 8154 throw o3tl::divide_by_zero(); 8155 8156 if( rRect.GetWidth() > rRect.GetHeight() ) 8157 fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight()); 8158 else if( rRect.GetHeight() > rRect.GetWidth() ) 8159 fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth()); 8160 return atan2( fY, fX ); 8161 } 8162 8163 void PDFWriterImpl::drawArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord ) 8164 { 8165 MARK( "drawArc" ); 8166 8167 updateGraphicsState(); 8168 8169 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8170 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8171 return; 8172 8173 // calculate start and stop angles 8174 const double fStartAngle = calcAngle( rRect, rStart ); 8175 double fStopAngle = calcAngle( rRect, rStop ); 8176 while( fStopAngle < fStartAngle ) 8177 fStopAngle += 2.0*M_PI; 8178 const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1; 8179 const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments; 8180 const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0); 8181 const double halfWidth = (double)rRect.GetWidth()/2.0; 8182 const double halfHeight = (double)rRect.GetHeight()/2.0; 8183 8184 const Point aCenter( (rRect.Left()+rRect.Right()+1)/2, 8185 (rRect.Top()+rRect.Bottom()+1)/2 ); 8186 8187 OStringBuffer aLine( 30*nFragments ); 8188 Point aPoint( (int)(halfWidth * cos(fStartAngle) ), 8189 -(int)(halfHeight * sin(fStartAngle) ) ); 8190 aPoint += aCenter; 8191 m_aPages.back().appendPoint( aPoint, aLine ); 8192 aLine.append( " m " ); 8193 if( !basegfx::fTools::equal(fStartAngle, fStopAngle) ) 8194 { 8195 for( int i = 0; i < nFragments; i++ ) 8196 { 8197 const double fStartFragment = fStartAngle + (double)i*fFragmentDelta; 8198 const double fStopFragment = fStartFragment + fFragmentDelta; 8199 aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ), 8200 -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) ); 8201 aPoint += aCenter; 8202 m_aPages.back().appendPoint( aPoint, aLine ); 8203 aLine.append( ' ' ); 8204 8205 aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ), 8206 -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) ); 8207 aPoint += aCenter; 8208 m_aPages.back().appendPoint( aPoint, aLine ); 8209 aLine.append( ' ' ); 8210 8211 aPoint = Point( (int)(halfWidth * cos(fStopFragment) ), 8212 -(int)(halfHeight * sin(fStopFragment) ) ); 8213 aPoint += aCenter; 8214 m_aPages.back().appendPoint( aPoint, aLine ); 8215 aLine.append( " c\n" ); 8216 } 8217 } 8218 if( bWithChord || bWithPie ) 8219 { 8220 if( bWithPie ) 8221 { 8222 m_aPages.back().appendPoint( aCenter, aLine ); 8223 aLine.append( " l " ); 8224 } 8225 aLine.append( "h " ); 8226 } 8227 if( ! bWithChord && ! bWithPie ) 8228 aLine.append( "S\n" ); 8229 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8230 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8231 aLine.append( "B*\n" ); 8232 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8233 aLine.append( "S\n" ); 8234 else 8235 aLine.append( "f*\n" ); 8236 8237 writeBuffer( aLine.getStr(), aLine.getLength() ); 8238 } 8239 8240 void PDFWriterImpl::drawPolyLine( const tools::Polygon& rPoly ) 8241 { 8242 MARK( "drawPolyLine" ); 8243 8244 sal_uInt16 nPoints = rPoly.GetSize(); 8245 if( nPoints < 2 ) 8246 return; 8247 8248 updateGraphicsState(); 8249 8250 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 8251 return; 8252 8253 OStringBuffer aLine( 20 * nPoints ); 8254 m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] ); 8255 aLine.append( "S\n" ); 8256 8257 writeBuffer( aLine.getStr(), aLine.getLength() ); 8258 } 8259 8260 void PDFWriterImpl::drawPolyLine( const tools::Polygon& rPoly, const LineInfo& rInfo ) 8261 { 8262 MARK( "drawPolyLine with LineInfo" ); 8263 8264 updateGraphicsState(); 8265 8266 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 8267 return; 8268 8269 OStringBuffer aLine; 8270 aLine.append( "q " ); 8271 if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) 8272 { 8273 writeBuffer( aLine.getStr(), aLine.getLength() ); 8274 drawPolyLine( rPoly ); 8275 writeBuffer( "Q\n", 2 ); 8276 } 8277 else 8278 { 8279 PDFWriter::ExtLineInfo aInfo; 8280 convertLineInfoToExtLineInfo( rInfo, aInfo ); 8281 drawPolyLine( rPoly, aInfo ); 8282 } 8283 } 8284 8285 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut ) 8286 { 8287 SAL_WARN_IF( rIn.GetStyle() != LineStyle::Dash, "vcl.pdfwriter", "invalid conversion" ); 8288 rOut.m_fLineWidth = rIn.GetWidth(); 8289 rOut.m_fTransparency = 0.0; 8290 rOut.m_eCap = PDFWriter::capButt; 8291 rOut.m_eJoin = PDFWriter::joinMiter; 8292 rOut.m_fMiterLimit = 10; 8293 rOut.m_aDashArray.clear(); 8294 8295 // add DashDot to DashArray 8296 const int nDashes = rIn.GetDashCount(); 8297 const int nDashLen = rIn.GetDashLen(); 8298 const int nDistance = rIn.GetDistance(); 8299 8300 for( int n = 0; n < nDashes; n++ ) 8301 { 8302 rOut.m_aDashArray.push_back( nDashLen ); 8303 rOut.m_aDashArray.push_back( nDistance ); 8304 } 8305 const int nDots = rIn.GetDotCount(); 8306 const int nDotLen = rIn.GetDotLen(); 8307 8308 for( int n = 0; n < nDots; n++ ) 8309 { 8310 rOut.m_aDashArray.push_back( nDotLen ); 8311 rOut.m_aDashArray.push_back( nDistance ); 8312 } 8313 8314 // add LineJoin 8315 switch(rIn.GetLineJoin()) 8316 { 8317 case basegfx::B2DLineJoin::Bevel : 8318 { 8319 rOut.m_eJoin = PDFWriter::joinBevel; 8320 break; 8321 } 8322 // Pdf has no 'none' lineJoin, default is miter 8323 case basegfx::B2DLineJoin::NONE : 8324 case basegfx::B2DLineJoin::Miter : 8325 { 8326 rOut.m_eJoin = PDFWriter::joinMiter; 8327 break; 8328 } 8329 case basegfx::B2DLineJoin::Round : 8330 { 8331 rOut.m_eJoin = PDFWriter::joinRound; 8332 break; 8333 } 8334 } 8335 8336 // add LineCap 8337 switch(rIn.GetLineCap()) 8338 { 8339 default: /* css::drawing::LineCap_BUTT */ 8340 { 8341 rOut.m_eCap = PDFWriter::capButt; 8342 break; 8343 } 8344 case css::drawing::LineCap_ROUND: 8345 { 8346 rOut.m_eCap = PDFWriter::capRound; 8347 break; 8348 } 8349 case css::drawing::LineCap_SQUARE: 8350 { 8351 rOut.m_eCap = PDFWriter::capSquare; 8352 break; 8353 } 8354 } 8355 } 8356 8357 void PDFWriterImpl::drawPolyLine( const tools::Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo ) 8358 { 8359 MARK( "drawPolyLine with ExtLineInfo" ); 8360 8361 updateGraphicsState(); 8362 8363 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 8364 return; 8365 8366 if( rInfo.m_fTransparency >= 1.0 ) 8367 return; 8368 8369 if( rInfo.m_fTransparency != 0.0 ) 8370 beginTransparencyGroup(); 8371 8372 OStringBuffer aLine; 8373 aLine.append( "q " ); 8374 m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine ); 8375 aLine.append( " w" ); 8376 if( rInfo.m_aDashArray.size() < 10 ) // implementation limit of acrobat reader 8377 { 8378 switch( rInfo.m_eCap ) 8379 { 8380 default: 8381 case PDFWriter::capButt: aLine.append( " 0 J" );break; 8382 case PDFWriter::capRound: aLine.append( " 1 J" );break; 8383 case PDFWriter::capSquare: aLine.append( " 2 J" );break; 8384 } 8385 switch( rInfo.m_eJoin ) 8386 { 8387 default: 8388 case PDFWriter::joinMiter: 8389 { 8390 double fLimit = rInfo.m_fMiterLimit; 8391 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit ) 8392 fLimit = fLimit / rInfo.m_fLineWidth; 8393 if( fLimit < 1.0 ) 8394 fLimit = 1.0; 8395 aLine.append( " 0 j " ); 8396 appendDouble( fLimit, aLine ); 8397 aLine.append( " M" ); 8398 } 8399 break; 8400 case PDFWriter::joinRound: aLine.append( " 1 j" );break; 8401 case PDFWriter::joinBevel: aLine.append( " 2 j" );break; 8402 } 8403 if( rInfo.m_aDashArray.size() > 0 ) 8404 { 8405 aLine.append( " [ " ); 8406 for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin(); 8407 it != rInfo.m_aDashArray.end(); ++it ) 8408 { 8409 m_aPages.back().appendMappedLength( *it, aLine ); 8410 aLine.append( ' ' ); 8411 } 8412 aLine.append( "] 0 d" ); 8413 } 8414 aLine.append( "\n" ); 8415 writeBuffer( aLine.getStr(), aLine.getLength() ); 8416 drawPolyLine( rPoly ); 8417 } 8418 else 8419 { 8420 basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon()); 8421 basegfx::B2DPolyPolygon aPolyPoly; 8422 8423 basegfx::utils::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly); 8424 8425 // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments. 8426 // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality) 8427 // this line needs to be removed and the loop below adapted accordingly 8428 aPolyPoly = basegfx::utils::adaptiveSubdivideByAngle(aPolyPoly); 8429 8430 const sal_uInt32 nPolygonCount(aPolyPoly.count()); 8431 8432 for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ ) 8433 { 8434 aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " ); 8435 aPoly = aPolyPoly.getB2DPolygon( nPoly ); 8436 const sal_uInt32 nPointCount(aPoly.count()); 8437 8438 if(nPointCount) 8439 { 8440 const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1); 8441 basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0)); 8442 8443 for(sal_uInt32 a(0); a < nEdgeCount; a++) 8444 { 8445 if( a > 0 ) 8446 aLine.append( " " ); 8447 const sal_uInt32 nNextIndex((a + 1) % nPointCount); 8448 const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex)); 8449 8450 m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()), 8451 FRound(aCurrent.getY()) ), 8452 aLine ); 8453 aLine.append( " m " ); 8454 m_aPages.back().appendPoint( Point( FRound(aNext.getX()), 8455 FRound(aNext.getY()) ), 8456 aLine ); 8457 aLine.append( " l" ); 8458 8459 // prepare next edge 8460 aCurrent = aNext; 8461 } 8462 } 8463 } 8464 aLine.append( " S " ); 8465 writeBuffer( aLine.getStr(), aLine.getLength() ); 8466 } 8467 writeBuffer( "Q\n", 2 ); 8468 8469 if( rInfo.m_fTransparency != 0.0 ) 8470 { 8471 // FIXME: actually this may be incorrect with bezier polygons 8472 tools::Rectangle aBoundRect( rPoly.GetBoundRect() ); 8473 // avoid clipping with thick lines 8474 if( rInfo.m_fLineWidth > 0.0 ) 8475 { 8476 sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth); 8477 aBoundRect.Top() -= nLW; 8478 aBoundRect.Left() -= nLW; 8479 aBoundRect.Right() += nLW; 8480 aBoundRect.Bottom() += nLW; 8481 } 8482 endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) ); 8483 } 8484 } 8485 8486 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor ) 8487 { 8488 MARK( "drawPixel" ); 8489 8490 Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor ); 8491 8492 if( aColor == Color( COL_TRANSPARENT ) ) 8493 return; 8494 8495 // pixels are drawn in line color, so have to set 8496 // the nonstroking color to line color 8497 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; 8498 setFillColor( aColor ); 8499 8500 updateGraphicsState(); 8501 8502 OStringBuffer aLine( 20 ); 8503 m_aPages.back().appendPoint( rPoint, aLine ); 8504 aLine.append( ' ' ); 8505 appendDouble( 1.0/double(getReferenceDevice()->GetDPIX()), aLine ); 8506 aLine.append( ' ' ); 8507 appendDouble( 1.0/double(getReferenceDevice()->GetDPIY()), aLine ); 8508 aLine.append( " re f\n" ); 8509 writeBuffer( aLine.getStr(), aLine.getLength() ); 8510 8511 setFillColor( aOldFillColor ); 8512 } 8513 8514 void PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject ) 8515 { 8516 CHECK_RETURN2( updateObject( rObject.m_nObject ) ); 8517 8518 bool bFlateFilter = compressStream( rObject.m_pContentStream ); 8519 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END ); 8520 sal_uLong nSize = rObject.m_pContentStream->Tell(); 8521 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN ); 8522 if (g_bDebugDisableCompression) 8523 { 8524 emitComment( "PDFWriterImpl::writeTransparentObject" ); 8525 } 8526 OStringBuffer aLine( 512 ); 8527 CHECK_RETURN2( updateObject( rObject.m_nObject ) ); 8528 aLine.append( rObject.m_nObject ); 8529 aLine.append( " 0 obj\n" 8530 "<</Type/XObject\n" 8531 "/Subtype/Form\n" 8532 "/BBox[ " ); 8533 appendFixedInt( rObject.m_aBoundRect.Left(), aLine ); 8534 aLine.append( ' ' ); 8535 appendFixedInt( rObject.m_aBoundRect.Top(), aLine ); 8536 aLine.append( ' ' ); 8537 appendFixedInt( rObject.m_aBoundRect.Right(), aLine ); 8538 aLine.append( ' ' ); 8539 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine ); 8540 aLine.append( " ]\n" ); 8541 if( ! rObject.m_pSoftMaskStream ) 8542 { 8543 if( ! m_bIsPDF_A1 ) 8544 { 8545 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" ); 8546 } 8547 } 8548 /* #i42884# the PDF reference recommends that each Form XObject 8549 * should have a resource dict; alas if that is the same object 8550 * as the one of the page it triggers an endless recursion in 8551 * acroread 5 (6 and up have that fixed). Since we have only one 8552 * resource dict anyway, let's use the one from the page by NOT 8553 * emitting a Resources entry. 8554 */ 8555 8556 aLine.append( "/Length " ); 8557 aLine.append( (sal_Int32)nSize ); 8558 aLine.append( "\n" ); 8559 if( bFlateFilter ) 8560 aLine.append( "/Filter/FlateDecode\n" ); 8561 aLine.append( ">>\n" 8562 "stream\n" ); 8563 CHECK_RETURN2( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 8564 checkAndEnableStreamEncryption( rObject.m_nObject ); 8565 CHECK_RETURN2( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) ); 8566 disableStreamEncryption(); 8567 aLine.setLength( 0 ); 8568 aLine.append( "\n" 8569 "endstream\n" 8570 "endobj\n\n" ); 8571 CHECK_RETURN2( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 8572 8573 // write ExtGState dict for this XObject 8574 aLine.setLength( 0 ); 8575 aLine.append( rObject.m_nExtGStateObject ); 8576 aLine.append( " 0 obj\n" 8577 "<<" ); 8578 if( ! rObject.m_pSoftMaskStream ) 8579 { 8580 if( m_bIsPDF_A1 ) 8581 { 8582 aLine.append( "/CA 1.0/ca 1.0" ); 8583 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 8584 } 8585 else 8586 { 8587 aLine.append( "/CA " ); 8588 appendDouble( rObject.m_fAlpha, aLine ); 8589 aLine.append( "\n" 8590 " /ca " ); 8591 appendDouble( rObject.m_fAlpha, aLine ); 8592 } 8593 aLine.append( "\n" ); 8594 } 8595 else 8596 { 8597 if( m_bIsPDF_A1 ) 8598 { 8599 aLine.append( "/SMask/None" ); 8600 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 8601 } 8602 else 8603 { 8604 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END ); 8605 sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell(); 8606 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN ); 8607 sal_Int32 nMaskObject = createObject(); 8608 aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " ); 8609 aLine.append( nMaskObject ); 8610 aLine.append( " 0 R>>\n" ); 8611 8612 OStringBuffer aMask; 8613 aMask.append( nMaskObject ); 8614 aMask.append( " 0 obj\n" 8615 "<</Type/XObject\n" 8616 "/Subtype/Form\n" 8617 "/BBox[" ); 8618 appendFixedInt( rObject.m_aBoundRect.Left(), aMask ); 8619 aMask.append( ' ' ); 8620 appendFixedInt( rObject.m_aBoundRect.Top(), aMask ); 8621 aMask.append( ' ' ); 8622 appendFixedInt( rObject.m_aBoundRect.Right(), aMask ); 8623 aMask.append( ' ' ); 8624 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask ); 8625 aMask.append( "]\n" ); 8626 8627 /* #i42884# see above */ 8628 aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" ); 8629 aMask.append( "/Length " ); 8630 aMask.append( nMaskSize ); 8631 aMask.append( ">>\n" 8632 "stream\n" ); 8633 CHECK_RETURN2( updateObject( nMaskObject ) ); 8634 checkAndEnableStreamEncryption( nMaskObject ); 8635 CHECK_RETURN2( writeBuffer( aMask.getStr(), aMask.getLength() ) ); 8636 CHECK_RETURN2( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) ); 8637 disableStreamEncryption(); 8638 aMask.setLength( 0 ); 8639 aMask.append( "\nendstream\n" 8640 "endobj\n\n" ); 8641 CHECK_RETURN2( writeBuffer( aMask.getStr(), aMask.getLength() ) ); 8642 } 8643 } 8644 aLine.append( ">>\n" 8645 "endobj\n\n" ); 8646 CHECK_RETURN2( updateObject( rObject.m_nExtGStateObject ) ); 8647 CHECK_RETURN2( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 8648 } 8649 8650 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject ) 8651 { 8652 // LO internal gradient -> PDF shading type: 8653 // * GradientStyle::Linear: axial shading, using sampled-function with 2 samples 8654 // [t=0:colorStart, t=1:colorEnd] 8655 // * GradientStyle::Axial: axial shading, using sampled-function with 3 samples 8656 // [t=0:colorEnd, t=0.5:colorStart, t=1:colorEnd] 8657 // * other styles: function shading with aSize.Width() * aSize.Height() samples 8658 sal_Int32 nFunctionObject = createObject(); 8659 CHECK_RETURN( updateObject( nFunctionObject ) ); 8660 8661 ScopedVclPtrInstance< VirtualDevice > aDev; 8662 aDev->SetOutputSizePixel( rObject.m_aSize ); 8663 aDev->SetMapMode( MapMode( MapUnit::MapPixel ) ); 8664 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 8665 aDev->SetDrawMode( aDev->GetDrawMode() | 8666 ( DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText | 8667 DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient ) ); 8668 aDev->DrawGradient( tools::Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient ); 8669 8670 Bitmap aSample = aDev->GetBitmap( Point( 0, 0 ), rObject.m_aSize ); 8671 Bitmap::ScopedReadAccess pAccess(aSample); 8672 8673 Size aSize = aSample.GetSizePixel(); 8674 8675 sal_Int32 nStreamLengthObject = createObject(); 8676 if (g_bDebugDisableCompression) 8677 { 8678 emitComment( "PDFWriterImpl::writeGradientFunction" ); 8679 } 8680 OStringBuffer aLine( 120 ); 8681 aLine.append( nFunctionObject ); 8682 aLine.append( " 0 obj\n" 8683 "<</FunctionType 0\n"); 8684 switch (rObject.m_aGradient.GetStyle()) 8685 { 8686 case GradientStyle::Linear: 8687 case GradientStyle::Axial: 8688 aLine.append("/Domain[ 0 1]\n"); 8689 break; 8690 default: 8691 aLine.append("/Domain[ 0 1 0 1]\n"); 8692 } 8693 aLine.append("/Size[ " ); 8694 switch (rObject.m_aGradient.GetStyle()) 8695 { 8696 case GradientStyle::Linear: 8697 aLine.append('2'); 8698 break; 8699 case GradientStyle::Axial: 8700 aLine.append('3'); 8701 break; 8702 default: 8703 aLine.append( (sal_Int32)aSize.Width() ); 8704 aLine.append( ' ' ); 8705 aLine.append( (sal_Int32)aSize.Height() ); 8706 } 8707 aLine.append( " ]\n" 8708 "/BitsPerSample 8\n" 8709 "/Range[ 0 1 0 1 0 1 ]\n" 8710 "/Order 3\n" 8711 "/Length " ); 8712 aLine.append( nStreamLengthObject ); 8713 if (!g_bDebugDisableCompression) 8714 aLine.append( " 0 R\n" 8715 "/Filter/FlateDecode" 8716 ">>\n" 8717 "stream\n" ); 8718 else 8719 aLine.append( " 0 R\n" 8720 ">>\n" 8721 "stream\n" ); 8722 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 8723 8724 sal_uInt64 nStartStreamPos = 0; 8725 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nStartStreamPos)) ); 8726 8727 checkAndEnableStreamEncryption( nFunctionObject ); 8728 beginCompression(); 8729 sal_uInt8 aCol[3]; 8730 switch (rObject.m_aGradient.GetStyle()) 8731 { 8732 case GradientStyle::Axial: 8733 aCol[0] = rObject.m_aGradient.GetEndColor().GetRed(); 8734 aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen(); 8735 aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue(); 8736 CHECK_RETURN( writeBuffer( aCol, 3 ) ); 8737 SAL_FALLTHROUGH; 8738 case GradientStyle::Linear: 8739 { 8740 aCol[0] = rObject.m_aGradient.GetStartColor().GetRed(); 8741 aCol[1] = rObject.m_aGradient.GetStartColor().GetGreen(); 8742 aCol[2] = rObject.m_aGradient.GetStartColor().GetBlue(); 8743 CHECK_RETURN( writeBuffer( aCol, 3 ) ); 8744 8745 aCol[0] = rObject.m_aGradient.GetEndColor().GetRed(); 8746 aCol[1] = rObject.m_aGradient.GetEndColor().GetGreen(); 8747 aCol[2] = rObject.m_aGradient.GetEndColor().GetBlue(); 8748 CHECK_RETURN( writeBuffer( aCol, 3 ) ); 8749 break; 8750 } 8751 default: 8752 for( int y = aSize.Height()-1; y >= 0; y-- ) 8753 { 8754 for( long x = 0; x < aSize.Width(); x++ ) 8755 { 8756 BitmapColor aColor = pAccess->GetColor( y, x ); 8757 aCol[0] = aColor.GetRed(); 8758 aCol[1] = aColor.GetGreen(); 8759 aCol[2] = aColor.GetBlue(); 8760 CHECK_RETURN( writeBuffer( aCol, 3 ) ); 8761 } 8762 } 8763 } 8764 endCompression(); 8765 disableStreamEncryption(); 8766 8767 sal_uInt64 nEndStreamPos = 0; 8768 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nEndStreamPos)) ); 8769 8770 aLine.setLength( 0 ); 8771 aLine.append( "\nendstream\nendobj\n\n" ); 8772 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 8773 8774 // write stream length 8775 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 8776 aLine.setLength( 0 ); 8777 aLine.append( nStreamLengthObject ); 8778 aLine.append( " 0 obj\n" ); 8779 aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) ); 8780 aLine.append( "\nendobj\n\n" ); 8781 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 8782 8783 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 8784 aLine.setLength( 0 ); 8785 aLine.append( rObject.m_nObject ); 8786 aLine.append( " 0 obj\n"); 8787 switch (rObject.m_aGradient.GetStyle()) 8788 { 8789 case GradientStyle::Linear: 8790 case GradientStyle::Axial: 8791 aLine.append("<</ShadingType 2\n"); 8792 break; 8793 default: 8794 aLine.append("<</ShadingType 1\n"); 8795 } 8796 aLine.append("/ColorSpace/DeviceRGB\n" 8797 "/AntiAlias true\n"); 8798 8799 // Determination of shading axis 8800 // See: OutputDevice::ImplDrawLinearGradient for reference 8801 tools::Rectangle aRect; 8802 aRect.Left() = aRect.Top() = 0; 8803 aRect.Right() = aSize.Width(); 8804 aRect.Bottom() = aSize.Height(); 8805 8806 tools::Rectangle aBoundRect; 8807 Point aCenter; 8808 sal_uInt16 nAngle = rObject.m_aGradient.GetAngle() % 3600; 8809 rObject.m_aGradient.GetBoundRect( aRect, aBoundRect, aCenter ); 8810 8811 const bool bLinear = (rObject.m_aGradient.GetStyle() == GradientStyle::Linear); 8812 double fBorder = aBoundRect.GetHeight() * rObject.m_aGradient.GetBorder() / 100.0; 8813 if ( !bLinear ) 8814 { 8815 fBorder /= 2.0; 8816 } 8817 8818 aBoundRect.Bottom() -= fBorder; 8819 if (!bLinear) 8820 { 8821 aBoundRect.Top() += fBorder; 8822 } 8823 8824 switch (rObject.m_aGradient.GetStyle()) 8825 { 8826 case GradientStyle::Linear: 8827 case GradientStyle::Axial: 8828 { 8829 aLine.append("/Domain[ 0 1 ]\n" 8830 "/Coords[ " ); 8831 tools::Polygon aPoly( 2 ); 8832 aPoly[0] = aBoundRect.BottomCenter(); 8833 aPoly[1] = aBoundRect.TopCenter(); 8834 aPoly.Rotate( aCenter, 3600 - nAngle ); 8835 8836 aLine.append( (sal_Int32) aPoly[0].X() ); 8837 aLine.append( " " ); 8838 aLine.append( (sal_Int32) aPoly[0].Y() ); 8839 aLine.append( " " ); 8840 aLine.append( (sal_Int32) aPoly[1].X()); 8841 aLine.append( " "); 8842 aLine.append( (sal_Int32) aPoly[1].Y()); 8843 aLine.append( " ]\n"); 8844 aLine.append("/Extend [true true]\n"); 8845 break; 8846 } 8847 default: 8848 aLine.append("/Domain[ 0 1 0 1 ]\n" 8849 "/Matrix[ " ); 8850 aLine.append( (sal_Int32)aSize.Width() ); 8851 aLine.append( " 0 0 " ); 8852 aLine.append( (sal_Int32)aSize.Height() ); 8853 aLine.append( " 0 0 ]\n"); 8854 } 8855 aLine.append("/Function " ); 8856 aLine.append( nFunctionObject ); 8857 aLine.append( " 0 R\n" 8858 ">>\n" 8859 "endobj\n\n" ); 8860 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 8861 8862 return true; 8863 } 8864 8865 void PDFWriterImpl::writeJPG( JPGEmit& rObject ) 8866 { 8867 if (rObject.m_aReferenceXObject.m_aPDFData.hasElements() && !m_aContext.UseReferenceXObject) 8868 { 8869 writeReferenceXObject(rObject.m_aReferenceXObject); 8870 return; 8871 } 8872 8873 CHECK_RETURN2( rObject.m_pStream ); 8874 CHECK_RETURN2( updateObject( rObject.m_nObject ) ); 8875 8876 sal_Int32 nLength = 0; 8877 rObject.m_pStream->Seek( STREAM_SEEK_TO_END ); 8878 nLength = rObject.m_pStream->Tell(); 8879 rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN ); 8880 8881 sal_Int32 nMaskObject = 0; 8882 if( !!rObject.m_aMask ) 8883 { 8884 if( rObject.m_aMask.GetBitCount() == 1 || 8885 ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDFVersion::PDF_1_4 && !m_bIsPDF_A1 ) 8886 ) 8887 { 8888 nMaskObject = createObject(); 8889 } 8890 else if( m_bIsPDF_A1 ) 8891 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 8892 else if( m_aContext.Version < PDFWriter::PDFVersion::PDF_1_4 ) 8893 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 ); 8894 8895 } 8896 if (g_bDebugDisableCompression) 8897 { 8898 emitComment( "PDFWriterImpl::writeJPG" ); 8899 } 8900 8901 OStringBuffer aLine(200); 8902 aLine.append( rObject.m_nObject ); 8903 aLine.append( " 0 obj\n" 8904 "<</Type/XObject/Subtype/Image/Width " ); 8905 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() ); 8906 aLine.append( " /Height " ); 8907 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() ); 8908 aLine.append( " /BitsPerComponent 8 " ); 8909 if( rObject.m_bTrueColor ) 8910 aLine.append( "/ColorSpace/DeviceRGB" ); 8911 else 8912 aLine.append( "/ColorSpace/DeviceGray" ); 8913 aLine.append( "/Filter/DCTDecode/Length " ); 8914 aLine.append( nLength ); 8915 if( nMaskObject ) 8916 { 8917 aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " ); 8918 aLine.append( nMaskObject ); 8919 aLine.append( " 0 R " ); 8920 } 8921 aLine.append( ">>\nstream\n" ); 8922 CHECK_RETURN2( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 8923 8924 checkAndEnableStreamEncryption( rObject.m_nObject ); 8925 CHECK_RETURN2( writeBuffer( rObject.m_pStream->GetData(), nLength ) ); 8926 disableStreamEncryption(); 8927 8928 aLine.setLength( 0 ); 8929 aLine.append( "\nendstream\nendobj\n\n" ); 8930 CHECK_RETURN2( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 8931 8932 if( nMaskObject ) 8933 { 8934 BitmapEmit aEmit; 8935 aEmit.m_nObject = nMaskObject; 8936 if( rObject.m_aMask.GetBitCount() == 1 ) 8937 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask ); 8938 else if( rObject.m_aMask.GetBitCount() == 8 ) 8939 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) ); 8940 writeBitmapObject( aEmit, true ); 8941 } 8942 8943 writeReferenceXObject(rObject.m_aReferenceXObject); 8944 } 8945 8946 sal_Int32 PDFWriterImpl::copyExternalResource(SvMemoryStream& rDocBuffer, filter::PDFObjectElement& rObject, std::map<sal_Int32, sal_Int32>& rCopiedResources) 8947 { 8948 auto it = rCopiedResources.find(rObject.GetObjectValue()); 8949 if (it != rCopiedResources.end()) 8950 // This resource was already copied once, nothing to do. 8951 return it->second; 8952 8953 sal_Int32 nObject = createObject(); 8954 // Remember what is the ID of this object in our output. 8955 rCopiedResources[rObject.GetObjectValue()] = nObject; 8956 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::copyExternalResource: " << rObject.GetObjectValue() << " -> " << nObject); 8957 8958 OStringBuffer aLine; 8959 aLine.append(nObject); 8960 aLine.append(" 0 obj\n"); 8961 if (rObject.GetDictionary()) 8962 { 8963 aLine.append("<<"); 8964 8965 // Complex case: can't copy the dictionary byte array as is, as it may contain references. 8966 bool bDone = false; 8967 sal_uInt64 nCopyStart = 0; 8968 for (auto pReference : rObject.GetDictionaryReferences()) 8969 { 8970 if (pReference) 8971 { 8972 filter::PDFObjectElement* pReferenced = pReference->LookupObject(); 8973 if (pReferenced) 8974 { 8975 // Copy the referenced object. 8976 sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced, rCopiedResources); 8977 8978 sal_uInt64 nReferenceStart = pReference->GetObjectElement().GetLocation(); 8979 sal_uInt64 nReferenceEnd = pReference->GetOffset(); 8980 sal_uInt64 nOffset = 0; 8981 if (nCopyStart == 0) 8982 // Dict start -> reference start. 8983 nOffset = rObject.GetDictionaryOffset(); 8984 else 8985 // Previous reference end -> reference start. 8986 nOffset = nCopyStart; 8987 aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nOffset, nReferenceStart - nOffset); 8988 // Write the updated reference. 8989 aLine.append(" "); 8990 aLine.append(nRef); 8991 aLine.append(" 0 R"); 8992 // Start copying here next time. 8993 nCopyStart = nReferenceEnd; 8994 8995 bDone = true; 8996 } 8997 } 8998 } 8999 9000 if (bDone) 9001 { 9002 // Copy the last part here, in the complex case. 9003 sal_uInt64 nDictEnd = rObject.GetDictionaryOffset() + rObject.GetDictionaryLength(); 9004 aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nCopyStart, nDictEnd - nCopyStart); 9005 } 9006 else 9007 // Can copy it as-is. 9008 aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + rObject.GetDictionaryOffset(), rObject.GetDictionaryLength()); 9009 9010 aLine.append(">>\n"); 9011 } 9012 9013 if (filter::PDFStreamElement* pStream = rObject.GetStream()) 9014 { 9015 aLine.append("stream\n"); 9016 SvMemoryStream& rStream = pStream->GetMemory(); 9017 aLine.append(static_cast<const sal_Char*>(rStream.GetData()), rStream.GetSize()); 9018 aLine.append("\nendstream\n"); 9019 } 9020 9021 if (filter::PDFArrayElement* pArray = rObject.GetArray()) 9022 { 9023 aLine.append("["); 9024 9025 const std::vector<filter::PDFElement*>& rElements = pArray->GetElements(); 9026 bool bDone = false; 9027 // Complex case: can't copy the array byte array as is, as it may contain references. 9028 sal_uInt64 nCopyStart = 0; 9029 for (const auto pElement : rElements) 9030 { 9031 auto pReference = dynamic_cast<filter::PDFReferenceElement*>(pElement); 9032 if (pReference) 9033 { 9034 filter::PDFObjectElement* pReferenced = pReference->LookupObject(); 9035 if (pReferenced) 9036 { 9037 // Copy the referenced object. 9038 sal_Int32 nRef = copyExternalResource(rDocBuffer, *pReferenced, rCopiedResources); 9039 9040 sal_uInt64 nReferenceStart = pReference->GetObjectElement().GetLocation(); 9041 sal_uInt64 nReferenceEnd = pReference->GetOffset(); 9042 sal_uInt64 nOffset = 0; 9043 if (nCopyStart == 0) 9044 // Array start -> reference start. 9045 nOffset = rObject.GetArrayOffset(); 9046 else 9047 // Previous reference end -> reference start. 9048 nOffset = nCopyStart; 9049 aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nOffset, nReferenceStart - nOffset); 9050 9051 // Write the updated reference. 9052 aLine.append(" "); 9053 aLine.append(nRef); 9054 aLine.append(" 0 R"); 9055 // Start copying here next time. 9056 nCopyStart = nReferenceEnd; 9057 9058 bDone = true; 9059 } 9060 } 9061 } 9062 9063 if (bDone) 9064 { 9065 // Copy the last part here, in the complex case. 9066 sal_uInt64 nArrEnd = rObject.GetArrayOffset() + rObject.GetArrayLength(); 9067 aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + nCopyStart, nArrEnd - nCopyStart); 9068 } 9069 else 9070 // Can copy it as-is. 9071 aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + rObject.GetArrayOffset(), rObject.GetArrayLength()); 9072 9073 aLine.append("]\n"); 9074 } 9075 9076 // If the object has a number element outside a dictionary or array, copy that. 9077 if (filter::PDFNumberElement* pNumber = rObject.GetNumberElement()) 9078 { 9079 aLine.append(static_cast<const sal_Char*>(rDocBuffer.GetData()) + pNumber->GetLocation(), pNumber->GetLength()); 9080 aLine.append("\n"); 9081 } 9082 9083 9084 aLine.append("endobj\n\n"); 9085 9086 // We have the whole object, now write it to the output. 9087 if (!updateObject(nObject)) 9088 return -1; 9089 if (!writeBuffer(aLine.getStr(), aLine.getLength())) 9090 return -1; 9091 9092 return nObject; 9093 } 9094 9095 OString PDFWriterImpl::copyExternalResources(filter::PDFObjectElement& rPage, const OString& rKind, std::map<sal_Int32, sal_Int32>& rCopiedResources) 9096 { 9097 // A name - object ID map, IDs as they appear in our output, not the 9098 // original ones. 9099 std::map<OString, sal_Int32> aRet; 9100 9101 // Get the rKind subset of the resource dictionary. 9102 std::map<OString, filter::PDFElement*> aItems; 9103 if (auto pResources = dynamic_cast<filter::PDFDictionaryElement*>(rPage.Lookup("Resources"))) 9104 { 9105 // Resources is a direct dictionary. 9106 if (auto pDictionary = dynamic_cast<filter::PDFDictionaryElement*>(pResources->LookupElement(rKind))) 9107 aItems = pDictionary->GetItems(); 9108 } 9109 else if (filter::PDFObjectElement* pPageResources = rPage.LookupObject("Resources")) 9110 { 9111 // Resources is an indirect object. 9112 filter::PDFElement* pValue = pPageResources->Lookup(rKind); 9113 if (auto pDictionary = dynamic_cast<filter::PDFDictionaryElement*>(pValue)) 9114 // Kind is a direct dictionary. 9115 aItems = pDictionary->GetItems(); 9116 else if (filter::PDFObjectElement* pObject = pPageResources->LookupObject(rKind)) 9117 // Kind is an indirect object. 9118 aItems = pObject->GetDictionaryItems(); 9119 } 9120 if (aItems.empty()) 9121 return OString(); 9122 9123 SvMemoryStream& rDocBuffer = rPage.GetDocument().GetEditBuffer(); 9124 9125 for (const auto& rItem : aItems) 9126 { 9127 // For each item copy it over to our output then insert it into aRet. 9128 auto pReference = dynamic_cast<filter::PDFReferenceElement*>(rItem.second); 9129 if (!pReference) 9130 continue; 9131 9132 filter::PDFObjectElement* pValue = pReference->LookupObject(); 9133 if (!pValue) 9134 continue; 9135 9136 // Then copying over an object copy its dictionary and its stream. 9137 sal_Int32 nObject = copyExternalResource(rDocBuffer, *pValue, rCopiedResources); 9138 aRet[rItem.first] = nObject; 9139 } 9140 9141 // Build the dictionary entry string. 9142 OString sRet = "/" + rKind + "<<"; 9143 for (const auto& rPair : aRet) 9144 { 9145 sRet += "/" + rPair.first + " " + OString::number(rPair.second) + " 0 R"; 9146 } 9147 sRet += ">>"; 9148 9149 return sRet; 9150 } 9151 9152 void PDFWriterImpl::writeReferenceXObject(ReferenceXObjectEmit& rEmit) 9153 { 9154 if (rEmit.m_nFormObject <= 0) 9155 return; 9156 9157 // Count /Matrix and /BBox. 9158 // vcl::ImportPDF() works with 96 DPI so use the same values here, too. 9159 sal_Int32 nOldDPIX = getReferenceDevice()->GetDPIX(); 9160 getReferenceDevice()->SetDPIX(96); 9161 sal_Int32 nOldDPIY = getReferenceDevice()->GetDPIY(); 9162 getReferenceDevice()->SetDPIY(96); 9163 Size aSize = getReferenceDevice()->PixelToLogic(rEmit.m_aPixelSize, MapMode(m_aMapMode.GetMapUnit())); 9164 getReferenceDevice()->SetDPIX(nOldDPIX); 9165 getReferenceDevice()->SetDPIY(nOldDPIY); 9166 double fScaleX = 1.0 / aSize.Width(); 9167 double fScaleY = 1.0 / aSize.Height(); 9168 9169 sal_Int32 nWrappedFormObject = 0; 9170 if (!m_aContext.UseReferenceXObject) 9171 { 9172 // Parse the PDF data, we need that to write the PDF dictionary of our 9173 // object. 9174 SvMemoryStream aPDFStream; 9175 aPDFStream.WriteBytes(rEmit.m_aPDFData.getArray(), rEmit.m_aPDFData.getLength()); 9176 aPDFStream.Seek(0); 9177 filter::PDFDocument aPDFDocument; 9178 if (!aPDFDocument.Read(aPDFStream)) 9179 { 9180 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: reading the PDF document failed"); 9181 return; 9182 } 9183 std::vector<filter::PDFObjectElement*> aPages = aPDFDocument.GetPages(); 9184 if (aPages.empty()) 9185 { 9186 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no pages"); 9187 return; 9188 } 9189 9190 filter::PDFObjectElement* pPage = aPages[0]; 9191 if (!pPage) 9192 { 9193 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no page"); 9194 return; 9195 } 9196 9197 std::vector<filter::PDFObjectElement*> aContentStreams; 9198 if (filter::PDFObjectElement* pContentStream = pPage->LookupObject("Contents")) 9199 aContentStreams.push_back(pContentStream); 9200 else if (auto pArray = dynamic_cast<filter::PDFArrayElement*>(pPage->Lookup("Contents"))) 9201 { 9202 for (const auto pElement : pArray->GetElements()) 9203 { 9204 auto pReference = dynamic_cast<filter::PDFReferenceElement*>(pElement); 9205 if (!pReference) 9206 continue; 9207 9208 filter::PDFObjectElement* pObject = pReference->LookupObject(); 9209 if (!pObject) 9210 continue; 9211 9212 aContentStreams.push_back(pObject); 9213 } 9214 } 9215 9216 if (aContentStreams.empty()) 9217 { 9218 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: no content stream"); 9219 return; 9220 } 9221 9222 // Maps from source object id (PDF image) to target object id (export result). 9223 std::map<sal_Int32, sal_Int32> aCopiedResources; 9224 9225 nWrappedFormObject = createObject(); 9226 // Write the form XObject wrapped below. This is a separate object from 9227 // the wrapper, this way there is no need to alter the stream contents. 9228 9229 OStringBuffer aLine; 9230 aLine.append(nWrappedFormObject); 9231 aLine.append(" 0 obj\n"); 9232 aLine.append("<< /Type /XObject"); 9233 aLine.append(" /Subtype /Form"); 9234 aLine.append(" /Resources <<"); 9235 static const std::initializer_list<OString> aKeys = 9236 { 9237 "ColorSpace", 9238 "ExtGState", 9239 "Font", 9240 "XObject", 9241 "Shading" 9242 }; 9243 for (const auto& rKey : aKeys) 9244 aLine.append(copyExternalResources(*pPage, rKey, aCopiedResources)); 9245 aLine.append(">>"); 9246 aLine.append(" /BBox [ 0 0 "); 9247 aLine.append(aSize.Width()); 9248 aLine.append(" "); 9249 aLine.append(aSize.Height()); 9250 aLine.append(" ]"); 9251 9252 if (!g_bDebugDisableCompression) 9253 aLine.append(" /Filter/FlateDecode"); 9254 aLine.append(" /Length "); 9255 9256 SvMemoryStream aStream; 9257 for (auto pContent : aContentStreams) 9258 { 9259 filter::PDFStreamElement* pPageStream = pContent->GetStream(); 9260 if (!pPageStream) 9261 { 9262 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: contents has no stream"); 9263 continue; 9264 } 9265 9266 SvMemoryStream& rPageStream = pPageStream->GetMemory(); 9267 9268 auto pFilter = dynamic_cast<filter::PDFNameElement*>(pContent->Lookup("Filter")); 9269 if (pFilter) 9270 { 9271 if (pFilter->GetValue() != "FlateDecode") 9272 continue; 9273 9274 SvMemoryStream aMemoryStream; 9275 ZCodec aZCodec; 9276 rPageStream.Seek(0); 9277 aZCodec.BeginCompression(); 9278 aZCodec.Decompress(rPageStream, aMemoryStream); 9279 if (!aZCodec.EndCompression()) 9280 { 9281 SAL_WARN("vcl.pdfwriter", "PDFWriterImpl::writeReferenceXObject: decompression failed"); 9282 continue; 9283 } 9284 9285 aStream.WriteBytes(aMemoryStream.GetData(), aMemoryStream.GetSize()); 9286 } 9287 else 9288 aStream.WriteBytes(rPageStream.GetData(), rPageStream.GetSize()); 9289 } 9290 9291 compressStream(&aStream); 9292 sal_Int32 nLength = aStream.Tell(); 9293 aLine.append(nLength); 9294 9295 aLine.append(">>\nstream\n"); 9296 // Copy the original page streams to the form XObject stream. 9297 aLine.append(static_cast<const sal_Char*>(aStream.GetData()), aStream.GetSize()); 9298 aLine.append("\nendstream\nendobj\n\n"); 9299 if (!updateObject(nWrappedFormObject)) 9300 return; 9301 if (!writeBuffer(aLine.getStr(), aLine.getLength())) 9302 return; 9303 } 9304 9305 OStringBuffer aLine; 9306 if (!updateObject(rEmit.m_nFormObject)) 9307 return; 9308 9309 // Now have all the info to write the form XObject. 9310 aLine.append(rEmit.m_nFormObject); 9311 aLine.append(" 0 obj\n"); 9312 aLine.append("<< /Type /XObject"); 9313 aLine.append(" /Subtype /Form"); 9314 aLine.append(" /Resources << /XObject<<"); 9315 9316 sal_Int32 nObject = m_aContext.UseReferenceXObject ? rEmit.m_nBitmapObject : nWrappedFormObject; 9317 aLine.append(" /Im"); 9318 aLine.append(nObject); 9319 aLine.append(" "); 9320 aLine.append(nObject); 9321 aLine.append(" 0 R"); 9322 9323 aLine.append(">> >>"); 9324 aLine.append(" /Matrix [ "); 9325 appendDouble(fScaleX, aLine); 9326 aLine.append(" 0 0 "); 9327 appendDouble(fScaleY, aLine); 9328 aLine.append(" 0 0 ]"); 9329 aLine.append(" /BBox [ 0 0 "); 9330 aLine.append(aSize.Width()); 9331 aLine.append(" "); 9332 aLine.append(aSize.Height()); 9333 aLine.append(" ]\n"); 9334 9335 if (m_aContext.UseReferenceXObject && rEmit.m_nEmbeddedObject > 0) 9336 { 9337 // Write the reference dictionary. 9338 aLine.append("/Ref<< /F << /Type /Filespec /F (<embedded file>) /EF << /F "); 9339 aLine.append(rEmit.m_nEmbeddedObject); 9340 aLine.append(" 0 R >> >> /Page 0 >>\n"); 9341 } 9342 9343 aLine.append("/Length "); 9344 9345 OStringBuffer aStream; 9346 aStream.append("q "); 9347 if (m_aContext.UseReferenceXObject) 9348 { 9349 // Reference XObject markup is used, just refer to the fallback bitmap 9350 // here. 9351 aStream.append(aSize.Width()); 9352 aStream.append(" 0 0 "); 9353 aStream.append(aSize.Height()); 9354 aStream.append(" 0 0 cm\n"); 9355 aStream.append("/Im"); 9356 aStream.append(rEmit.m_nBitmapObject); 9357 aStream.append(" Do\n"); 9358 } 9359 else 9360 { 9361 // Reset line width to the default. 9362 aStream.append(" 1 w\n"); 9363 9364 // No reference XObject, draw the form XObject containing the original 9365 // page streams. 9366 aStream.append("/Im"); 9367 aStream.append(nWrappedFormObject); 9368 aStream.append(" Do\n"); 9369 } 9370 aStream.append("Q"); 9371 aLine.append(aStream.getLength()); 9372 9373 aLine.append(">>\nstream\n"); 9374 aLine.append(aStream.getStr()); 9375 aLine.append("\nendstream\nendobj\n\n"); 9376 CHECK_RETURN2(writeBuffer(aLine.getStr(), aLine.getLength())); 9377 } 9378 9379 namespace 9380 { 9381 unsigned char reverseByte(unsigned char b) 9382 { 9383 b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; 9384 b = (b & 0xCC) >> 2 | (b & 0x33) << 2; 9385 b = (b & 0xAA) >> 1 | (b & 0x55) << 1; 9386 return b; 9387 } 9388 9389 //tdf#103051 convert any N1BitLsbPal to N1BitMsbPal 9390 Bitmap getExportBitmap(const Bitmap &rBitmap) 9391 { 9392 Bitmap::ScopedReadAccess pAccess(const_cast<Bitmap&>(rBitmap)); 9393 const ScanlineFormat eFormat = pAccess->GetScanlineFormat(); 9394 if (eFormat != ScanlineFormat::N1BitLsbPal) 9395 return rBitmap; 9396 Bitmap aNewBmp(rBitmap); 9397 Bitmap::ScopedWriteAccess xWriteAcc(aNewBmp); 9398 const int nScanLineBytes = (pAccess->Width() + 7U) / 8U; 9399 for (long nY = 0L; nY < xWriteAcc->Height(); ++nY) 9400 { 9401 Scanline pBitSwap = xWriteAcc->GetScanline(nY); 9402 for (int x = 0; x < nScanLineBytes; ++x) 9403 pBitSwap[x] = reverseByte(pBitSwap[x]); 9404 } 9405 return aNewBmp; 9406 } 9407 } 9408 9409 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask ) 9410 { 9411 if (rObject.m_aReferenceXObject.m_aPDFData.hasElements() && !m_aContext.UseReferenceXObject) 9412 { 9413 writeReferenceXObject(rObject.m_aReferenceXObject); 9414 return true; 9415 } 9416 9417 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9418 9419 Bitmap aBitmap; 9420 Color aTransparentColor( COL_TRANSPARENT ); 9421 bool bWriteMask = false; 9422 if( ! bMask ) 9423 { 9424 aBitmap = getExportBitmap(rObject.m_aBitmap.GetBitmap()); 9425 if( rObject.m_aBitmap.IsAlpha() ) 9426 { 9427 if( m_aContext.Version >= PDFWriter::PDFVersion::PDF_1_4 ) 9428 bWriteMask = true; 9429 // else draw without alpha channel 9430 } 9431 else 9432 { 9433 switch( rObject.m_aBitmap.GetTransparentType() ) 9434 { 9435 case TransparentType::NONE: 9436 break; 9437 case TransparentType::Color: 9438 aTransparentColor = rObject.m_aBitmap.GetTransparentColor(); 9439 break; 9440 case TransparentType::Bitmap: 9441 bWriteMask = true; 9442 break; 9443 } 9444 } 9445 } 9446 else 9447 { 9448 if( m_aContext.Version < PDFWriter::PDFVersion::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() ) 9449 { 9450 aBitmap = getExportBitmap(rObject.m_aBitmap.GetMask()); 9451 aBitmap.Convert( BmpConversion::N1BitThreshold ); 9452 SAL_WARN_IF( aBitmap.GetBitCount() != 1, "vcl.pdfwriter", "mask conversion failed" ); 9453 } 9454 else if( aBitmap.GetBitCount() != 8 ) 9455 { 9456 aBitmap = getExportBitmap(rObject.m_aBitmap.GetAlpha().GetBitmap()); 9457 aBitmap.Convert( BmpConversion::N8BitGreys ); 9458 SAL_WARN_IF( aBitmap.GetBitCount() != 8, "vcl.pdfwriter", "alpha mask conversion failed" ); 9459 } 9460 } 9461 9462 Bitmap::ScopedReadAccess pAccess(aBitmap); 9463 9464 bool bTrueColor; 9465 sal_Int32 nBitsPerComponent; 9466 switch( aBitmap.GetBitCount() ) 9467 { 9468 case 1: 9469 case 2: 9470 case 4: 9471 case 8: 9472 bTrueColor = false; 9473 nBitsPerComponent = aBitmap.GetBitCount(); 9474 break; 9475 default: 9476 bTrueColor = true; 9477 nBitsPerComponent = 8; 9478 break; 9479 } 9480 9481 sal_Int32 nStreamLengthObject = createObject(); 9482 sal_Int32 nMaskObject = 0; 9483 9484 if (g_bDebugDisableCompression) 9485 { 9486 emitComment( "PDFWriterImpl::writeBitmapObject" ); 9487 } 9488 OStringBuffer aLine(1024); 9489 aLine.append( rObject.m_nObject ); 9490 aLine.append( " 0 obj\n" 9491 "<</Type/XObject/Subtype/Image/Width " ); 9492 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); 9493 aLine.append( "/Height " ); 9494 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() ); 9495 aLine.append( "/BitsPerComponent " ); 9496 aLine.append( nBitsPerComponent ); 9497 aLine.append( "/Length " ); 9498 aLine.append( nStreamLengthObject ); 9499 aLine.append( " 0 R\n" ); 9500 if (!g_bDebugDisableCompression) 9501 { 9502 if( nBitsPerComponent != 1 ) 9503 { 9504 aLine.append( "/Filter/FlateDecode" ); 9505 } 9506 else 9507 { 9508 aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " ); 9509 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); 9510 aLine.append( ">>\n" ); 9511 } 9512 } 9513 if( ! bMask ) 9514 { 9515 aLine.append( "/ColorSpace" ); 9516 if( bTrueColor ) 9517 aLine.append( "/DeviceRGB\n" ); 9518 else if( aBitmap.HasGreyPalette() ) 9519 { 9520 aLine.append( "/DeviceGray\n" ); 9521 if( aBitmap.GetBitCount() == 1 ) 9522 { 9523 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette 9524 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); 9525 SAL_WARN_IF( nBlackIndex != 0 && nBlackIndex != 1, "vcl.pdfwriter", "wrong black index" ); 9526 if( nBlackIndex == 1 ) 9527 aLine.append( "/Decode[1 0]\n" ); 9528 } 9529 } 9530 else 9531 { 9532 aLine.append( "[ /Indexed/DeviceRGB " ); 9533 aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) ); 9534 aLine.append( "\n<" ); 9535 if( m_aContext.Encryption.Encrypt() ) 9536 { 9537 enableStringEncryption( rObject.m_nObject ); 9538 //check encryption buffer size 9539 if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) ) 9540 { 9541 int nChar = 0; 9542 //fill the encryption buffer 9543 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9544 { 9545 const BitmapColor& rColor = pAccess->GetPaletteColor( i ); 9546 m_pEncryptionBuffer[nChar++] = rColor.GetRed(); 9547 m_pEncryptionBuffer[nChar++] = rColor.GetGreen(); 9548 m_pEncryptionBuffer[nChar++] = rColor.GetBlue(); 9549 } 9550 //encrypt the colorspace lookup table 9551 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar ); 9552 //now queue the data for output 9553 nChar = 0; 9554 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9555 { 9556 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9557 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9558 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9559 } 9560 } 9561 } 9562 else //no encryption requested (PDF/A-1a program flow drops here) 9563 { 9564 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9565 { 9566 const BitmapColor& rColor = pAccess->GetPaletteColor( i ); 9567 appendHex( rColor.GetRed(), aLine ); 9568 appendHex( rColor.GetGreen(), aLine ); 9569 appendHex( rColor.GetBlue(), aLine ); 9570 } 9571 } 9572 aLine.append( ">\n]\n" ); 9573 } 9574 } 9575 else 9576 { 9577 if( aBitmap.GetBitCount() == 1 ) 9578 { 9579 aLine.append( "/ImageMask true\n" ); 9580 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); 9581 SAL_WARN_IF( nBlackIndex != 0 && nBlackIndex != 1, "vcl.pdfwriter", "wrong black index" ); 9582 if( nBlackIndex ) 9583 aLine.append( "/Decode[ 1 0 ]\n" ); 9584 else 9585 aLine.append( "/Decode[ 0 1 ]\n" ); 9586 } 9587 else if( aBitmap.GetBitCount() == 8 ) 9588 { 9589 aLine.append( "/ColorSpace/DeviceGray\n" 9590 "/Decode [ 1 0 ]\n" ); 9591 } 9592 } 9593 9594 if( ! bMask && m_aContext.Version > PDFWriter::PDFVersion::PDF_1_2 && !m_bIsPDF_A1 ) 9595 { 9596 if( bWriteMask ) 9597 { 9598 nMaskObject = createObject(); 9599 if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 ) 9600 aLine.append( "/SMask " ); 9601 else 9602 aLine.append( "/Mask " ); 9603 aLine.append( nMaskObject ); 9604 aLine.append( " 0 R\n" ); 9605 } 9606 else if( aTransparentColor != Color( COL_TRANSPARENT ) ) 9607 { 9608 aLine.append( "/Mask[ " ); 9609 if( bTrueColor ) 9610 { 9611 aLine.append( (sal_Int32)aTransparentColor.GetRed() ); 9612 aLine.append( ' ' ); 9613 aLine.append( (sal_Int32)aTransparentColor.GetRed() ); 9614 aLine.append( ' ' ); 9615 aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); 9616 aLine.append( ' ' ); 9617 aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); 9618 aLine.append( ' ' ); 9619 aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); 9620 aLine.append( ' ' ); 9621 aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); 9622 } 9623 else 9624 { 9625 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) ); 9626 aLine.append( nIndex ); 9627 } 9628 aLine.append( " ]\n" ); 9629 } 9630 } 9631 else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) ) 9632 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9633 9634 aLine.append( ">>\n" 9635 "stream\n" ); 9636 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9637 sal_uInt64 nStartPos = 0; 9638 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nStartPos)) ); 9639 9640 checkAndEnableStreamEncryption( rObject.m_nObject ); 9641 if (!g_bDebugDisableCompression && nBitsPerComponent == 1) 9642 { 9643 writeG4Stream(pAccess.get()); 9644 } 9645 else 9646 { 9647 beginCompression(); 9648 if( ! bTrueColor || pAccess->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb ) 9649 { 9650 //With PDF bitmaps, each row is padded to a BYTE boundary (multiple of 8 bits). 9651 const int nScanLineBytes = ((pAccess->GetBitCount() * pAccess->Width()) + 7U) / 8U; 9652 9653 for( long i = 0; i < pAccess->Height(); i++ ) 9654 { 9655 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) ); 9656 } 9657 } 9658 else 9659 { 9660 const int nScanLineBytes = pAccess->Width()*3; 9661 std::unique_ptr<sal_uInt8[]> xCol(new sal_uInt8[nScanLineBytes]); 9662 for( long y = 0; y < pAccess->Height(); y++ ) 9663 { 9664 for( long x = 0; x < pAccess->Width(); x++ ) 9665 { 9666 BitmapColor aColor = pAccess->GetColor( y, x ); 9667 xCol[3*x+0] = aColor.GetRed(); 9668 xCol[3*x+1] = aColor.GetGreen(); 9669 xCol[3*x+2] = aColor.GetBlue(); 9670 } 9671 CHECK_RETURN(writeBuffer(xCol.get(), nScanLineBytes)); 9672 } 9673 } 9674 endCompression(); 9675 } 9676 disableStreamEncryption(); 9677 9678 sal_uInt64 nEndPos = 0; 9679 CHECK_RETURN( (osl::File::E_None == m_aFile.getPos(nEndPos)) ); 9680 aLine.setLength( 0 ); 9681 aLine.append( "\nendstream\nendobj\n\n" ); 9682 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9683 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 9684 aLine.setLength( 0 ); 9685 aLine.append( nStreamLengthObject ); 9686 aLine.append( " 0 obj\n" ); 9687 aLine.append( (sal_Int64)(nEndPos-nStartPos) ); 9688 aLine.append( "\nendobj\n\n" ); 9689 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9690 9691 if( nMaskObject ) 9692 { 9693 BitmapEmit aEmit; 9694 aEmit.m_nObject = nMaskObject; 9695 aEmit.m_aBitmap = rObject.m_aBitmap; 9696 return writeBitmapObject( aEmit, true ); 9697 } 9698 9699 writeReferenceXObject(rObject.m_aReferenceXObject); 9700 9701 return true; 9702 } 9703 9704 void PDFWriterImpl::createEmbeddedFile(const Graphic& rGraphic, ReferenceXObjectEmit& rEmit, sal_Int32 nBitmapObject) 9705 { 9706 // The bitmap object is always a valid identifier, even if the graphic has 9707 // no pdf data. 9708 rEmit.m_nBitmapObject = nBitmapObject; 9709 9710 if (!rGraphic.getPdfData().hasElements()) 9711 return; 9712 9713 if (m_aContext.UseReferenceXObject) 9714 { 9715 // Store the original PDF data as an embedded file. 9716 m_aEmbeddedFiles.emplace_back(); 9717 m_aEmbeddedFiles.back().m_nObject = createObject(); 9718 m_aEmbeddedFiles.back().m_aData = rGraphic.getPdfData(); 9719 9720 rEmit.m_nEmbeddedObject = m_aEmbeddedFiles.back().m_nObject; 9721 } 9722 else 9723 rEmit.m_aPDFData = rGraphic.getPdfData(); 9724 9725 rEmit.m_nFormObject = createObject(); 9726 rEmit.m_aPixelSize = rGraphic.GetBitmap().GetPrefSize(); 9727 } 9728 9729 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const tools::Rectangle& rTargetArea, const Bitmap& rMask, const Graphic& rGraphic ) 9730 { 9731 MARK( "drawJPGBitmap" ); 9732 9733 OStringBuffer aLine( 80 ); 9734 updateGraphicsState(); 9735 9736 // #i40055# sanity check 9737 if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) ) 9738 return; 9739 if( ! (rSizePixel.Width() && rSizePixel.Height()) ) 9740 return; 9741 9742 rDCTData.Seek( 0 ); 9743 if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 9744 { 9745 // need to convert to grayscale; 9746 // load stream to bitmap and draw the bitmap instead 9747 Graphic aGraphic; 9748 GraphicConverter::Import( rDCTData, aGraphic, ConvertDataFormat::JPG ); 9749 Bitmap aBmp( aGraphic.GetBitmap() ); 9750 if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() ) 9751 { 9752 BitmapEx aBmpEx( aBmp, rMask ); 9753 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx ); 9754 } 9755 else 9756 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp ); 9757 return; 9758 } 9759 9760 SvMemoryStream* pStream = new SvMemoryStream; 9761 pStream->WriteStream( rDCTData ); 9762 pStream->Seek( STREAM_SEEK_TO_END ); 9763 9764 BitmapID aID; 9765 aID.m_aPixelSize = rSizePixel; 9766 aID.m_nSize = pStream->Tell(); 9767 pStream->Seek( STREAM_SEEK_TO_BEGIN ); 9768 aID.m_nChecksum = vcl_get_checksum( 0, pStream->GetData(), aID.m_nSize ); 9769 if( ! rMask.IsEmpty() ) 9770 aID.m_nMaskChecksum = rMask.GetChecksum(); 9771 9772 std::list< JPGEmit >::const_iterator it; 9773 for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it ) 9774 ; 9775 if( it == m_aJPGs.end() ) 9776 { 9777 m_aJPGs.emplace( m_aJPGs.begin() ); 9778 JPGEmit& rEmit = m_aJPGs.front(); 9779 if (!rGraphic.getPdfData().hasElements() || m_aContext.UseReferenceXObject) 9780 rEmit.m_nObject = createObject(); 9781 rEmit.m_aID = aID; 9782 rEmit.m_pStream.reset( pStream ); 9783 rEmit.m_bTrueColor = bIsTrueColor; 9784 if( !! rMask && rMask.GetSizePixel() == rSizePixel ) 9785 rEmit.m_aMask = rMask; 9786 createEmbeddedFile(rGraphic, rEmit.m_aReferenceXObject, rEmit.m_nObject); 9787 9788 it = m_aJPGs.begin(); 9789 } 9790 else 9791 delete pStream; 9792 9793 aLine.append( "q " ); 9794 sal_Int32 nCheckWidth = 0; 9795 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth ); 9796 aLine.append( " 0 0 " ); 9797 sal_Int32 nCheckHeight = 0; 9798 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight ); 9799 aLine.append( ' ' ); 9800 m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine ); 9801 aLine.append( " cm\n/Im" ); 9802 sal_Int32 nObject = it->m_aReferenceXObject.getObject(); 9803 aLine.append(nObject); 9804 aLine.append( " Do Q\n" ); 9805 if( nCheckWidth == 0 || nCheckHeight == 0 ) 9806 { 9807 // #i97512# avoid invalid current matrix 9808 aLine.setLength( 0 ); 9809 aLine.append( "\n%jpeg image /Im" ); 9810 aLine.append( it->m_nObject ); 9811 aLine.append( " scaled to zero size, omitted\n" ); 9812 } 9813 writeBuffer( aLine.getStr(), aLine.getLength() ); 9814 9815 OStringBuffer aObjName( 16 ); 9816 aObjName.append( "Im" ); 9817 aObjName.append(nObject); 9818 pushResource( ResXObject, aObjName.makeStringAndClear(), nObject ); 9819 9820 } 9821 9822 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor ) 9823 { 9824 OStringBuffer aLine( 80 ); 9825 updateGraphicsState(); 9826 9827 aLine.append( "q " ); 9828 if( rFillColor != Color( COL_TRANSPARENT ) ) 9829 { 9830 appendNonStrokingColor( rFillColor, aLine ); 9831 aLine.append( ' ' ); 9832 } 9833 sal_Int32 nCheckWidth = 0; 9834 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth ); 9835 aLine.append( " 0 0 " ); 9836 sal_Int32 nCheckHeight = 0; 9837 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight ); 9838 aLine.append( ' ' ); 9839 m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine ); 9840 aLine.append( " cm\n/Im" ); 9841 sal_Int32 nObject = rBitmap.m_aReferenceXObject.getObject(); 9842 aLine.append(nObject); 9843 aLine.append( " Do Q\n" ); 9844 if( nCheckWidth == 0 || nCheckHeight == 0 ) 9845 { 9846 // #i97512# avoid invalid current matrix 9847 aLine.setLength( 0 ); 9848 aLine.append( "\n%bitmap image /Im" ); 9849 aLine.append( rBitmap.m_nObject ); 9850 aLine.append( " scaled to zero size, omitted\n" ); 9851 } 9852 writeBuffer( aLine.getStr(), aLine.getLength() ); 9853 } 9854 9855 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, const Graphic& rGraphic ) 9856 { 9857 BitmapEx aBitmap( i_rBitmap ); 9858 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 9859 { 9860 BmpConversion eConv = BmpConversion::N8BitGreys; 9861 int nDepth = aBitmap.GetBitmap().GetBitCount(); 9862 if( nDepth <= 4 ) 9863 eConv = BmpConversion::N4BitGreys; 9864 if( nDepth > 1 ) 9865 aBitmap.Convert( eConv ); 9866 } 9867 BitmapID aID; 9868 aID.m_aPixelSize = aBitmap.GetSizePixel(); 9869 aID.m_nSize = aBitmap.GetBitCount(); 9870 aID.m_nChecksum = aBitmap.GetBitmap().GetChecksum(); 9871 aID.m_nMaskChecksum = 0; 9872 if( aBitmap.IsAlpha() ) 9873 aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum(); 9874 else 9875 { 9876 Bitmap aMask = aBitmap.GetMask(); 9877 if( ! aMask.IsEmpty() ) 9878 aID.m_nMaskChecksum = aMask.GetChecksum(); 9879 } 9880 std::list< BitmapEmit >::const_iterator it; 9881 for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it ) 9882 { 9883 if( aID == it->m_aID ) 9884 break; 9885 } 9886 if( it == m_aBitmaps.end() ) 9887 { 9888 m_aBitmaps.push_front( BitmapEmit() ); 9889 m_aBitmaps.front().m_aID = aID; 9890 m_aBitmaps.front().m_aBitmap = aBitmap; 9891 if (!rGraphic.getPdfData().hasElements() || m_aContext.UseReferenceXObject) 9892 m_aBitmaps.front().m_nObject = createObject(); 9893 createEmbeddedFile(rGraphic, m_aBitmaps.front().m_aReferenceXObject, m_aBitmaps.front().m_nObject); 9894 it = m_aBitmaps.begin(); 9895 } 9896 9897 OStringBuffer aObjName( 16 ); 9898 aObjName.append( "Im" ); 9899 sal_Int32 nObject = it->m_aReferenceXObject.getObject(); 9900 aObjName.append(nObject); 9901 pushResource( ResXObject, aObjName.makeStringAndClear(), nObject ); 9902 9903 return *it; 9904 } 9905 9906 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Graphic& rGraphic ) 9907 { 9908 MARK( "drawBitmap (Bitmap)" ); 9909 9910 // #i40055# sanity check 9911 if( ! (rDestSize.Width() && rDestSize.Height()) ) 9912 return; 9913 9914 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ), rGraphic ); 9915 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); 9916 } 9917 9918 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap ) 9919 { 9920 MARK( "drawBitmap (BitmapEx)" ); 9921 9922 // #i40055# sanity check 9923 if( ! (rDestSize.Width() && rDestSize.Height()) ) 9924 return; 9925 9926 const BitmapEmit& rEmit = createBitmapEmit( rBitmap, Graphic() ); 9927 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); 9928 } 9929 9930 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize ) 9931 { 9932 Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode, 9933 MapMode( MapUnit::MapPoint ), 9934 getReferenceDevice(), 9935 rSize ) ); 9936 // check if we already have this gradient 9937 std::list<GradientEmit>::iterator it; 9938 // rounding to point will generally lose some pixels 9939 // round up to point boundary 9940 aPtSize.Width()++; 9941 aPtSize.Height()++; 9942 for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it ) 9943 { 9944 if( it->m_aGradient == rGradient ) 9945 { 9946 if( it->m_aSize == aPtSize ) 9947 break; 9948 } 9949 } 9950 if( it == m_aGradients.end() ) 9951 { 9952 m_aGradients.push_front( GradientEmit() ); 9953 m_aGradients.front().m_aGradient = rGradient; 9954 m_aGradients.front().m_nObject = createObject(); 9955 m_aGradients.front().m_aSize = aPtSize; 9956 it = m_aGradients.begin(); 9957 } 9958 9959 OStringBuffer aObjName( 16 ); 9960 aObjName.append( 'P' ); 9961 aObjName.append( it->m_nObject ); 9962 pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject ); 9963 9964 return it->m_nObject; 9965 } 9966 9967 void PDFWriterImpl::drawGradient( const tools::Rectangle& rRect, const Gradient& rGradient ) 9968 { 9969 MARK( "drawGradient (Rectangle)" ); 9970 9971 if( m_aContext.Version == PDFWriter::PDFVersion::PDF_1_2 ) 9972 { 9973 drawRectangle( rRect ); 9974 return; 9975 } 9976 9977 sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() ); 9978 9979 Point aTranslate( rRect.BottomLeft() ); 9980 aTranslate += Point( 0, 1 ); 9981 9982 updateGraphicsState(); 9983 9984 OStringBuffer aLine( 80 ); 9985 aLine.append( "q 1 0 0 1 " ); 9986 m_aPages.back().appendPoint( aTranslate, aLine ); 9987 aLine.append( " cm " ); 9988 // if a stroke is appended reset the clip region before stroke 9989 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 9990 aLine.append( "q " ); 9991 aLine.append( "0 0 " ); 9992 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); 9993 aLine.append( ' ' ); 9994 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine ); 9995 aLine.append( " re W n\n" ); 9996 9997 aLine.append( "/P" ); 9998 aLine.append( nGradient ); 9999 aLine.append( " sh " ); 10000 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10001 { 10002 aLine.append( "Q 0 0 " ); 10003 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); 10004 aLine.append( ' ' ); 10005 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine ); 10006 aLine.append( " re S " ); 10007 } 10008 aLine.append( "Q\n" ); 10009 writeBuffer( aLine.getStr(), aLine.getLength() ); 10010 } 10011 10012 void PDFWriterImpl::drawHatch( const tools::PolyPolygon& rPolyPoly, const Hatch& rHatch ) 10013 { 10014 MARK( "drawHatch" ); 10015 10016 updateGraphicsState(); 10017 10018 if( rPolyPoly.Count() ) 10019 { 10020 tools::PolyPolygon aPolyPoly( rPolyPoly ); 10021 10022 aPolyPoly.Optimize( PolyOptimizeFlags::NO_SAME ); 10023 push( PushFlags::LINECOLOR ); 10024 setLineColor( rHatch.GetColor() ); 10025 getReferenceDevice()->DrawHatch( aPolyPoly, rHatch, false ); 10026 pop(); 10027 } 10028 } 10029 10030 void PDFWriterImpl::drawWallpaper( const tools::Rectangle& rRect, const Wallpaper& rWall ) 10031 { 10032 MARK( "drawWallpaper" ); 10033 10034 bool bDrawColor = false; 10035 bool bDrawGradient = false; 10036 bool bDrawBitmap = false; 10037 10038 BitmapEx aBitmap; 10039 Point aBmpPos = rRect.TopLeft(); 10040 Size aBmpSize; 10041 if( rWall.IsBitmap() ) 10042 { 10043 aBitmap = rWall.GetBitmap(); 10044 aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(), 10045 getMapMode(), 10046 getReferenceDevice(), 10047 aBitmap.GetPrefSize() ); 10048 tools::Rectangle aRect( rRect ); 10049 if( rWall.IsRect() ) 10050 { 10051 aRect = rWall.GetRect(); 10052 aBmpPos = aRect.TopLeft(); 10053 aBmpSize = aRect.GetSize(); 10054 } 10055 if( rWall.GetStyle() != WallpaperStyle::Scale ) 10056 { 10057 if( rWall.GetStyle() != WallpaperStyle::Tile ) 10058 { 10059 bDrawBitmap = true; 10060 if( rWall.IsGradient() ) 10061 bDrawGradient = true; 10062 else 10063 bDrawColor = true; 10064 switch( rWall.GetStyle() ) 10065 { 10066 case WallpaperStyle::TopLeft: 10067 break; 10068 case WallpaperStyle::Top: 10069 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10070 break; 10071 case WallpaperStyle::Left: 10072 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10073 break; 10074 case WallpaperStyle::TopRight: 10075 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10076 break; 10077 case WallpaperStyle::Center: 10078 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10079 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10080 break; 10081 case WallpaperStyle::Right: 10082 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10083 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10084 break; 10085 case WallpaperStyle::BottomLeft: 10086 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10087 break; 10088 case WallpaperStyle::Bottom: 10089 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10090 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10091 break; 10092 case WallpaperStyle::BottomRight: 10093 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10094 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10095 break; 10096 default: ; 10097 } 10098 } 10099 else 10100 { 10101 // push the bitmap 10102 const BitmapEmit& rEmit = createBitmapEmit( aBitmap, Graphic() ); 10103 10104 // convert to page coordinates; this needs to be done here 10105 // since the emit does not know the page anymore 10106 tools::Rectangle aConvertRect( aBmpPos, aBmpSize ); 10107 m_aPages.back().convertRect( aConvertRect ); 10108 10109 OStringBuffer aNameBuf(16); 10110 aNameBuf.append( "Im" ); 10111 aNameBuf.append( rEmit.m_nObject ); 10112 OString aImageName( aNameBuf.makeStringAndClear() ); 10113 10114 // push the pattern 10115 OStringBuffer aTilingStream( 32 ); 10116 appendFixedInt( aConvertRect.GetWidth(), aTilingStream ); 10117 aTilingStream.append( " 0 0 " ); 10118 appendFixedInt( aConvertRect.GetHeight(), aTilingStream ); 10119 aTilingStream.append( " 0 0 cm\n/" ); 10120 aTilingStream.append( aImageName ); 10121 aTilingStream.append( " Do\n" ); 10122 10123 m_aTilings.emplace_back( ); 10124 m_aTilings.back().m_nObject = createObject(); 10125 m_aTilings.back().m_aRectangle = tools::Rectangle( Point( 0, 0 ), aConvertRect.GetSize() ); 10126 m_aTilings.back().m_pTilingStream = new SvMemoryStream(); 10127 m_aTilings.back().m_pTilingStream->WriteBytes( 10128 aTilingStream.getStr(), aTilingStream.getLength() ); 10129 // phase the tiling so wallpaper begins on upper left 10130 if ((aConvertRect.GetWidth() == 0) || (aConvertRect.GetHeight() == 0)) 10131 throw o3tl::divide_by_zero(); 10132 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor; 10133 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor; 10134 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject; 10135 10136 updateGraphicsState(); 10137 10138 OStringBuffer aObjName( 16 ); 10139 aObjName.append( 'P' ); 10140 aObjName.append( m_aTilings.back().m_nObject ); 10141 OString aPatternName( aObjName.makeStringAndClear() ); 10142 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject ); 10143 10144 // fill a rRect with the pattern 10145 OStringBuffer aLine( 100 ); 10146 aLine.append( "q /Pattern cs /" ); 10147 aLine.append( aPatternName ); 10148 aLine.append( " scn " ); 10149 m_aPages.back().appendRect( rRect, aLine ); 10150 aLine.append( " f Q\n" ); 10151 writeBuffer( aLine.getStr(), aLine.getLength() ); 10152 } 10153 } 10154 else 10155 { 10156 aBmpPos = aRect.TopLeft(); 10157 aBmpSize = aRect.GetSize(); 10158 bDrawBitmap = true; 10159 } 10160 10161 if( aBitmap.IsTransparent() ) 10162 { 10163 if( rWall.IsGradient() ) 10164 bDrawGradient = true; 10165 else 10166 bDrawColor = true; 10167 } 10168 } 10169 else if( rWall.IsGradient() ) 10170 bDrawGradient = true; 10171 else 10172 bDrawColor = true; 10173 10174 if( bDrawGradient ) 10175 { 10176 drawGradient( rRect, rWall.GetGradient() ); 10177 } 10178 if( bDrawColor ) 10179 { 10180 Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor; 10181 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; 10182 setLineColor( Color( COL_TRANSPARENT ) ); 10183 setFillColor( rWall.GetColor() ); 10184 drawRectangle( rRect ); 10185 setLineColor( aOldLineColor ); 10186 setFillColor( aOldFillColor ); 10187 } 10188 if( bDrawBitmap ) 10189 { 10190 // set temporary clip region since aBmpPos and aBmpSize 10191 // may be outside rRect 10192 OStringBuffer aLine( 20 ); 10193 aLine.append( "q " ); 10194 m_aPages.back().appendRect( rRect, aLine ); 10195 aLine.append( " W n\n" ); 10196 writeBuffer( aLine.getStr(), aLine.getLength() ); 10197 drawBitmap( aBmpPos, aBmpSize, aBitmap ); 10198 writeBuffer( "Q\n", 2 ); 10199 } 10200 } 10201 10202 void PDFWriterImpl::updateGraphicsState(Mode const mode) 10203 { 10204 OStringBuffer aLine( 256 ); 10205 GraphicsState& rNewState = m_aGraphicsStack.front(); 10206 // first set clip region since it might invalidate everything else 10207 10208 if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::ClipRegion) ) 10209 { 10210 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::ClipRegion; 10211 10212 if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion || 10213 ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) ) 10214 { 10215 if( m_aCurrentPDFState.m_bClipRegion ) 10216 { 10217 aLine.append( "Q " ); 10218 // invalidate everything but the clip region 10219 m_aCurrentPDFState = GraphicsState(); 10220 rNewState.m_nUpdateFlags = ~GraphicsStateUpdateFlags::ClipRegion; 10221 } 10222 if( rNewState.m_bClipRegion ) 10223 { 10224 // clip region is always stored in private PDF mapmode 10225 MapMode aNewMapMode = rNewState.m_aMapMode; 10226 rNewState.m_aMapMode = m_aMapMode; 10227 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10228 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; 10229 10230 aLine.append("q "); 10231 if ( rNewState.m_aClipRegion.count() ) 10232 { 10233 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine ); 10234 aLine.append( "W* n\n" ); 10235 } 10236 10237 rNewState.m_aMapMode = aNewMapMode; 10238 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10239 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; 10240 } 10241 } 10242 } 10243 10244 if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::MapMode) ) 10245 { 10246 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::MapMode; 10247 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10248 } 10249 10250 if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::Font) ) 10251 { 10252 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::Font; 10253 getReferenceDevice()->SetFont( rNewState.m_aFont ); 10254 getReferenceDevice()->ImplNewFont(); 10255 } 10256 10257 if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::LayoutMode) ) 10258 { 10259 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::LayoutMode; 10260 getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode ); 10261 } 10262 10263 if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::DigitLanguage) ) 10264 { 10265 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::DigitLanguage; 10266 getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage ); 10267 } 10268 10269 if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::LineColor) ) 10270 { 10271 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::LineColor; 10272 if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor && 10273 rNewState.m_aLineColor != Color( COL_TRANSPARENT ) ) 10274 { 10275 appendStrokingColor( rNewState.m_aLineColor, aLine ); 10276 aLine.append( "\n" ); 10277 } 10278 } 10279 10280 if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::FillColor) ) 10281 { 10282 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::FillColor; 10283 if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor && 10284 rNewState.m_aFillColor != Color( COL_TRANSPARENT ) ) 10285 { 10286 appendNonStrokingColor( rNewState.m_aFillColor, aLine ); 10287 aLine.append( "\n" ); 10288 } 10289 } 10290 10291 if( (rNewState.m_nUpdateFlags & GraphicsStateUpdateFlags::TransparentPercent) ) 10292 { 10293 rNewState.m_nUpdateFlags &= ~GraphicsStateUpdateFlags::TransparentPercent; 10294 if( m_aContext.Version >= PDFWriter::PDFVersion::PDF_1_4 ) 10295 { 10296 // TODO: switch extended graphicsstate 10297 } 10298 } 10299 10300 // everything is up to date now 10301 m_aCurrentPDFState = m_aGraphicsStack.front(); 10302 if ((mode != NOWRITE) && !aLine.isEmpty()) 10303 writeBuffer( aLine.getStr(), aLine.getLength() ); 10304 } 10305 10306 /* #i47544# imitate OutputDevice behaviour: 10307 * if a font with a nontransparent color is set, it overwrites the current 10308 * text color. OTOH setting the text color will overwrite the color of the font. 10309 */ 10310 void PDFWriterImpl::setFont( const vcl::Font& rFont ) 10311 { 10312 Color aColor = rFont.GetColor(); 10313 if( aColor == Color( COL_TRANSPARENT ) ) 10314 aColor = m_aGraphicsStack.front().m_aFont.GetColor(); 10315 m_aGraphicsStack.front().m_aFont = rFont; 10316 m_aGraphicsStack.front().m_aFont.SetColor( aColor ); 10317 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::Font; 10318 } 10319 10320 void PDFWriterImpl::push( PushFlags nFlags ) 10321 { 10322 OSL_ENSURE( !m_aGraphicsStack.empty(), "invalid graphics stack" ); 10323 m_aGraphicsStack.push_front( m_aGraphicsStack.front() ); 10324 m_aGraphicsStack.front().m_nFlags = nFlags; 10325 } 10326 10327 void PDFWriterImpl::pop() 10328 { 10329 OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" ); 10330 if( m_aGraphicsStack.size() < 2 ) 10331 return; 10332 10333 GraphicsState aState = m_aGraphicsStack.front(); 10334 m_aGraphicsStack.pop_front(); 10335 GraphicsState& rOld = m_aGraphicsStack.front(); 10336 10337 // move those parameters back that were not pushed 10338 // in the first place 10339 if( ! (aState.m_nFlags & PushFlags::LINECOLOR) ) 10340 setLineColor( aState.m_aLineColor ); 10341 if( ! (aState.m_nFlags & PushFlags::FILLCOLOR) ) 10342 setFillColor( aState.m_aFillColor ); 10343 if( ! (aState.m_nFlags & PushFlags::FONT) ) 10344 setFont( aState.m_aFont ); 10345 if( ! (aState.m_nFlags & PushFlags::TEXTCOLOR) ) 10346 setTextColor( aState.m_aFont.GetColor() ); 10347 if( ! (aState.m_nFlags & PushFlags::MAPMODE) ) 10348 setMapMode( aState.m_aMapMode ); 10349 if( ! (aState.m_nFlags & PushFlags::CLIPREGION) ) 10350 { 10351 // do not use setClipRegion here 10352 // it would convert again assuming the current mapmode 10353 rOld.m_aClipRegion = aState.m_aClipRegion; 10354 rOld.m_bClipRegion = aState.m_bClipRegion; 10355 } 10356 if( ! (aState.m_nFlags & PushFlags::TEXTLINECOLOR ) ) 10357 setTextLineColor( aState.m_aTextLineColor ); 10358 if( ! (aState.m_nFlags & PushFlags::OVERLINECOLOR ) ) 10359 setOverlineColor( aState.m_aOverlineColor ); 10360 if( ! (aState.m_nFlags & PushFlags::TEXTALIGN ) ) 10361 setTextAlign( aState.m_aFont.GetAlignment() ); 10362 if( ! (aState.m_nFlags & PushFlags::TEXTFILLCOLOR) ) 10363 setTextFillColor( aState.m_aFont.GetFillColor() ); 10364 if( ! (aState.m_nFlags & PushFlags::REFPOINT) ) 10365 { 10366 // what ? 10367 } 10368 // invalidate graphics state 10369 m_aGraphicsStack.front().m_nUpdateFlags = GraphicsStateUpdateFlags::All; 10370 } 10371 10372 void PDFWriterImpl::setMapMode( const MapMode& rMapMode ) 10373 { 10374 m_aGraphicsStack.front().m_aMapMode = rMapMode; 10375 getReferenceDevice()->SetMapMode( rMapMode ); 10376 m_aCurrentPDFState.m_aMapMode = rMapMode; 10377 } 10378 10379 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion ) 10380 { 10381 basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ); 10382 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); 10383 m_aGraphicsStack.front().m_aClipRegion = aRegion; 10384 m_aGraphicsStack.front().m_bClipRegion = true; 10385 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion; 10386 } 10387 10388 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY ) 10389 { 10390 if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() ) 10391 { 10392 Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10393 m_aMapMode, 10394 getReferenceDevice(), 10395 Point( nX, nY ) ) ); 10396 aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10397 m_aMapMode, 10398 getReferenceDevice(), 10399 Point() ); 10400 basegfx::B2DHomMatrix aMat; 10401 aMat.translate( aPoint.X(), aPoint.Y() ); 10402 m_aGraphicsStack.front().m_aClipRegion.transform( aMat ); 10403 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion; 10404 } 10405 } 10406 10407 void PDFWriterImpl::intersectClipRegion( const tools::Rectangle& rRect ) 10408 { 10409 basegfx::B2DPolyPolygon aRect( basegfx::utils::createPolygonFromRect( 10410 basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); 10411 intersectClipRegion( aRect ); 10412 } 10413 10414 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion ) 10415 { 10416 basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) ); 10417 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); 10418 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsStateUpdateFlags::ClipRegion; 10419 if( m_aGraphicsStack.front().m_bClipRegion ) 10420 { 10421 basegfx::B2DPolyPolygon aOld( basegfx::utils::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) ); 10422 aRegion = basegfx::utils::prepareForPolygonOperation( aRegion ); 10423 m_aGraphicsStack.front().m_aClipRegion = basegfx::utils::solvePolygonOperationAnd( aOld, aRegion ); 10424 } 10425 else 10426 { 10427 m_aGraphicsStack.front().m_aClipRegion = aRegion; 10428 m_aGraphicsStack.front().m_bClipRegion = true; 10429 } 10430 return true; 10431 } 10432 10433 void PDFWriterImpl::createNote( const tools::Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr ) 10434 { 10435 if( nPageNr < 0 ) 10436 nPageNr = m_nCurrentPage; 10437 10438 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10439 return; 10440 10441 m_aNotes.emplace_back( ); 10442 m_aNotes.back().m_nObject = createObject(); 10443 m_aNotes.back().m_aContents = rNote; 10444 m_aNotes.back().m_aRect = rRect; 10445 // convert to default user space now, since the mapmode may change 10446 m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect ); 10447 10448 // insert note to page's annotation list 10449 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject ); 10450 } 10451 10452 sal_Int32 PDFWriterImpl::createLink( const tools::Rectangle& rRect, sal_Int32 nPageNr ) 10453 { 10454 if( nPageNr < 0 ) 10455 nPageNr = m_nCurrentPage; 10456 10457 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10458 return -1; 10459 10460 sal_Int32 nRet = m_aLinks.size(); 10461 10462 m_aLinks.emplace_back( ); 10463 m_aLinks.back().m_nObject = createObject(); 10464 m_aLinks.back().m_nPage = nPageNr; 10465 m_aLinks.back().m_aRect = rRect; 10466 // convert to default user space now, since the mapmode may change 10467 m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect ); 10468 10469 // insert link to page's annotation list 10470 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject ); 10471 10472 return nRet; 10473 } 10474 10475 sal_Int32 PDFWriterImpl::createScreen(const tools::Rectangle& rRect, sal_Int32 nPageNr) 10476 { 10477 if (nPageNr < 0) 10478 nPageNr = m_nCurrentPage; 10479 10480 if (nPageNr < 0 || nPageNr >= static_cast<sal_Int32>(m_aPages.size())) 10481 return -1; 10482 10483 sal_Int32 nRet = m_aScreens.size(); 10484 10485 m_aScreens.emplace_back(); 10486 m_aScreens.back().m_nObject = createObject(); 10487 m_aScreens.back().m_nPage = nPageNr; 10488 m_aScreens.back().m_aRect = rRect; 10489 // Convert to default user space now, since the mapmode may change. 10490 m_aPages[nPageNr].convertRect(m_aScreens.back().m_aRect); 10491 10492 // Insert link to page's annotation list. 10493 m_aPages[nPageNr].m_aAnnotations.push_back(m_aScreens.back().m_nObject); 10494 10495 return nRet; 10496 } 10497 10498 sal_Int32 PDFWriterImpl::createNamedDest( const OUString& sDestName, const tools::Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10499 { 10500 if( nPageNr < 0 ) 10501 nPageNr = m_nCurrentPage; 10502 10503 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10504 return -1; 10505 10506 sal_Int32 nRet = m_aNamedDests.size(); 10507 10508 m_aNamedDests.emplace_back( ); 10509 m_aNamedDests.back().m_aDestName = sDestName; 10510 m_aNamedDests.back().m_nPage = nPageNr; 10511 m_aNamedDests.back().m_eType = eType; 10512 m_aNamedDests.back().m_aRect = rRect; 10513 // convert to default user space now, since the mapmode may change 10514 m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect ); 10515 10516 return nRet; 10517 } 10518 10519 sal_Int32 PDFWriterImpl::createDest( const tools::Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10520 { 10521 if( nPageNr < 0 ) 10522 nPageNr = m_nCurrentPage; 10523 10524 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10525 return -1; 10526 10527 sal_Int32 nRet = m_aDests.size(); 10528 10529 m_aDests.emplace_back( ); 10530 m_aDests.back().m_nPage = nPageNr; 10531 m_aDests.back().m_eType = eType; 10532 m_aDests.back().m_aRect = rRect; 10533 // convert to default user space now, since the mapmode may change 10534 m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect ); 10535 10536 return nRet; 10537 } 10538 10539 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const tools::Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10540 { 10541 return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType ); 10542 } 10543 10544 void PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId ) 10545 { 10546 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) 10547 return; 10548 if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() ) 10549 return; 10550 10551 m_aLinks[ nLinkId ].m_nDest = nDestId; 10552 } 10553 10554 void PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL ) 10555 { 10556 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) 10557 return; 10558 10559 m_aLinks[ nLinkId ].m_nDest = -1; 10560 10561 using namespace ::com::sun::star; 10562 10563 if (!m_xTrans.is()) 10564 { 10565 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() ); 10566 m_xTrans = util::URLTransformer::create(xContext); 10567 } 10568 10569 util::URL aURL; 10570 aURL.Complete = rURL; 10571 10572 m_xTrans->parseStrict( aURL ); 10573 10574 m_aLinks[ nLinkId ].m_aURL = aURL.Complete; 10575 } 10576 10577 void PDFWriterImpl::setScreenURL(sal_Int32 nScreenId, const OUString& rURL) 10578 { 10579 if (nScreenId < 0 || nScreenId >= static_cast<sal_Int32>(m_aScreens.size())) 10580 return; 10581 10582 m_aScreens[nScreenId].m_aURL = rURL; 10583 } 10584 10585 void PDFWriterImpl::setScreenStream(sal_Int32 nScreenId, const OUString& rURL) 10586 { 10587 if (nScreenId < 0 || nScreenId >= static_cast<sal_Int32>(m_aScreens.size())) 10588 return; 10589 10590 m_aScreens[nScreenId].m_aTempFileURL = rURL; 10591 m_aScreens[nScreenId].m_nTempFileObject = createObject(); 10592 } 10593 10594 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId ) 10595 { 10596 m_aLinkPropertyMap[ nPropertyId ] = nLinkId; 10597 } 10598 10599 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID ) 10600 { 10601 // create new item 10602 sal_Int32 nNewItem = m_aOutline.size(); 10603 m_aOutline.emplace_back( ); 10604 10605 // set item attributes 10606 setOutlineItemParent( nNewItem, nParent ); 10607 setOutlineItemText( nNewItem, rText ); 10608 setOutlineItemDest( nNewItem, nDestID ); 10609 10610 return nNewItem; 10611 } 10612 10613 void PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent ) 10614 { 10615 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) 10616 return; 10617 10618 if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem ) 10619 { 10620 nNewParent = 0; 10621 } 10622 // insert item to new parent's list of children 10623 m_aOutline[ nNewParent ].m_aChildren.push_back( nItem ); 10624 } 10625 10626 void PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText ) 10627 { 10628 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) 10629 return; 10630 10631 m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText ); 10632 } 10633 10634 void PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID ) 10635 { 10636 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist 10637 return; 10638 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist 10639 return; 10640 m_aOutline[nItem].m_nDestID = nDestID; 10641 } 10642 10643 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType ) 10644 { 10645 static std::map< PDFWriter::StructElement, const char* > aTagStrings; 10646 if( aTagStrings.empty() ) 10647 { 10648 aTagStrings[ PDFWriter::NonStructElement] = "NonStruct"; 10649 aTagStrings[ PDFWriter::Document ] = "Document"; 10650 aTagStrings[ PDFWriter::Part ] = "Part"; 10651 aTagStrings[ PDFWriter::Article ] = "Art"; 10652 aTagStrings[ PDFWriter::Section ] = "Sect"; 10653 aTagStrings[ PDFWriter::Division ] = "Div"; 10654 aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote"; 10655 aTagStrings[ PDFWriter::Caption ] = "Caption"; 10656 aTagStrings[ PDFWriter::TOC ] = "TOC"; 10657 aTagStrings[ PDFWriter::TOCI ] = "TOCI"; 10658 aTagStrings[ PDFWriter::Index ] = "Index"; 10659 aTagStrings[ PDFWriter::Paragraph ] = "P"; 10660 aTagStrings[ PDFWriter::Heading ] = "H"; 10661 aTagStrings[ PDFWriter::H1 ] = "H1"; 10662 aTagStrings[ PDFWriter::H2 ] = "H2"; 10663 aTagStrings[ PDFWriter::H3 ] = "H3"; 10664 aTagStrings[ PDFWriter::H4 ] = "H4"; 10665 aTagStrings[ PDFWriter::H5 ] = "H5"; 10666 aTagStrings[ PDFWriter::H6 ] = "H6"; 10667 aTagStrings[ PDFWriter::List ] = "L"; 10668 aTagStrings[ PDFWriter::ListItem ] = "LI"; 10669 aTagStrings[ PDFWriter::LILabel ] = "Lbl"; 10670 aTagStrings[ PDFWriter::LIBody ] = "LBody"; 10671 aTagStrings[ PDFWriter::Table ] = "Table"; 10672 aTagStrings[ PDFWriter::TableRow ] = "TR"; 10673 aTagStrings[ PDFWriter::TableHeader ] = "TH"; 10674 aTagStrings[ PDFWriter::TableData ] = "TD"; 10675 aTagStrings[ PDFWriter::Span ] = "Span"; 10676 aTagStrings[ PDFWriter::Quote ] = "Quote"; 10677 aTagStrings[ PDFWriter::Note ] = "Note"; 10678 aTagStrings[ PDFWriter::Reference ] = "Reference"; 10679 aTagStrings[ PDFWriter::BibEntry ] = "BibEntry"; 10680 aTagStrings[ PDFWriter::Code ] = "Code"; 10681 aTagStrings[ PDFWriter::Link ] = "Link"; 10682 aTagStrings[ PDFWriter::Figure ] = "Figure"; 10683 aTagStrings[ PDFWriter::Formula ] = "Formula"; 10684 aTagStrings[ PDFWriter::Form ] = "Form"; 10685 } 10686 10687 std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType ); 10688 10689 return it != aTagStrings.end() ? it->second : "Div"; 10690 } 10691 10692 void PDFWriterImpl::beginStructureElementMCSeq() 10693 { 10694 if( m_bEmitStructure && 10695 m_nCurrentStructElement > 0 && // StructTreeRoot 10696 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence 10697 ) 10698 { 10699 PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ]; 10700 OStringBuffer aLine( 128 ); 10701 sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size(); 10702 aLine.append( "/" ); 10703 if( !rEle.m_aAlias.isEmpty() ) 10704 aLine.append( rEle.m_aAlias ); 10705 else 10706 aLine.append( getStructureTag( rEle.m_eType ) ); 10707 aLine.append( "<</MCID " ); 10708 aLine.append( nMCID ); 10709 aLine.append( ">>BDC\n" ); 10710 writeBuffer( aLine.getStr(), aLine.getLength() ); 10711 10712 // update the element's content list 10713 SAL_INFO("vcl.pdfwriter", "beginning marked content id " << nMCID << " on page object " 10714 << m_aPages[ m_nCurrentPage ].m_nPageObject << ", structure first page = " 10715 << rEle.m_nFirstPageObject); 10716 rEle.m_aKids.emplace_back( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ); 10717 // update the page's mcid parent list 10718 m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject ); 10719 // mark element MC sequence as open 10720 rEle.m_bOpenMCSeq = true; 10721 } 10722 // handle artifacts 10723 else if( ! m_bEmitStructure && m_aContext.Tagged && 10724 m_nCurrentStructElement > 0 && 10725 m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement && 10726 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence 10727 ) 10728 { 10729 OStringBuffer aLine( 128 ); 10730 aLine.append( "/Artifact BMC\n" ); 10731 writeBuffer( aLine.getStr(), aLine.getLength() ); 10732 // mark element MC sequence as open 10733 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true; 10734 } 10735 } 10736 10737 void PDFWriterImpl::endStructureElementMCSeq() 10738 { 10739 if( m_nCurrentStructElement > 0 && // StructTreeRoot 10740 ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) && 10741 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence 10742 ) 10743 { 10744 writeBuffer( "EMC\n", 4 ); 10745 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false; 10746 } 10747 } 10748 10749 bool PDFWriterImpl::checkEmitStructure() 10750 { 10751 bool bEmit = false; 10752 if( m_aContext.Tagged ) 10753 { 10754 bEmit = true; 10755 sal_Int32 nEle = m_nCurrentStructElement; 10756 while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) ) 10757 { 10758 if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement ) 10759 { 10760 bEmit = false; 10761 break; 10762 } 10763 nEle = m_aStructure[ nEle ].m_nParentElement; 10764 } 10765 } 10766 return bEmit; 10767 } 10768 10769 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const OUString& rAlias ) 10770 { 10771 if( m_nCurrentPage < 0 ) 10772 return -1; 10773 10774 if( ! m_aContext.Tagged ) 10775 return -1; 10776 10777 // close eventual current MC sequence 10778 endStructureElementMCSeq(); 10779 10780 if( m_nCurrentStructElement == 0 && 10781 eType != PDFWriter::Document && eType != PDFWriter::NonStructElement ) 10782 { 10783 // struct tree root hit, but not beginning document 10784 // this might happen with setCurrentStructureElement 10785 // silently insert structure into document again if one properly exists 10786 if( ! m_aStructure[ 0 ].m_aChildren.empty() ) 10787 { 10788 PDFWriter::StructElement childType = PDFWriter::NonStructElement; 10789 sal_Int32 nNewCurElement = 0; 10790 const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren; 10791 for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin(); 10792 childType != PDFWriter::Document && it != rRootChildren.end(); ++it ) 10793 { 10794 nNewCurElement = *it; 10795 childType = m_aStructure[ nNewCurElement ].m_eType; 10796 } 10797 if( childType == PDFWriter::Document ) 10798 { 10799 m_nCurrentStructElement = nNewCurElement; 10800 SAL_WARN( "vcl.pdfwriter", "Structure element inserted to StructTreeRoot that is not a document" ); 10801 } 10802 else { 10803 OSL_FAIL( "document structure in disorder !" ); 10804 } 10805 } 10806 else { 10807 OSL_FAIL( "PDF document structure MUST be contained in a Document element" ); 10808 } 10809 } 10810 10811 sal_Int32 nNewId = sal_Int32(m_aStructure.size()); 10812 m_aStructure.emplace_back( ); 10813 PDFStructureElement& rEle = m_aStructure.back(); 10814 rEle.m_eType = eType; 10815 rEle.m_nOwnElement = nNewId; 10816 rEle.m_nParentElement = m_nCurrentStructElement; 10817 rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject; 10818 m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId ); 10819 m_nCurrentStructElement = nNewId; 10820 10821 // handle alias names 10822 if( !rAlias.isEmpty() && eType != PDFWriter::NonStructElement ) 10823 { 10824 OStringBuffer aNameBuf( rAlias.getLength() ); 10825 appendName( rAlias, aNameBuf ); 10826 OString aAliasName( aNameBuf.makeStringAndClear() ); 10827 rEle.m_aAlias = aAliasName; 10828 m_aRoleMap[ aAliasName ] = getStructureTag( eType ); 10829 } 10830 10831 if (g_bDebugDisableCompression) 10832 { 10833 OStringBuffer aLine( "beginStructureElement " ); 10834 aLine.append( m_nCurrentStructElement ); 10835 aLine.append( ": " ); 10836 aLine.append( getStructureTag( eType ) ); 10837 if( !rEle.m_aAlias.isEmpty() ) 10838 { 10839 aLine.append( " aliased as \"" ); 10840 aLine.append( rEle.m_aAlias ); 10841 aLine.append( '\"' ); 10842 } 10843 emitComment( aLine.getStr() ); 10844 } 10845 10846 // check whether to emit structure henceforth 10847 m_bEmitStructure = checkEmitStructure(); 10848 10849 if( m_bEmitStructure ) // don't create nonexistent objects 10850 { 10851 rEle.m_nObject = createObject(); 10852 // update parent's kids list 10853 m_aStructure[ rEle.m_nParentElement ].m_aKids.emplace_back(rEle.m_nObject); 10854 } 10855 return nNewId; 10856 } 10857 10858 void PDFWriterImpl::endStructureElement() 10859 { 10860 if( m_nCurrentPage < 0 ) 10861 return; 10862 10863 if( ! m_aContext.Tagged ) 10864 return; 10865 10866 if( m_nCurrentStructElement == 0 ) 10867 { 10868 // hit the struct tree root, that means there is an endStructureElement 10869 // without corresponding beginStructureElement 10870 return; 10871 } 10872 10873 // end the marked content sequence 10874 endStructureElementMCSeq(); 10875 10876 OStringBuffer aLine; 10877 if (g_bDebugDisableCompression) 10878 { 10879 aLine.append( "endStructureElement " ); 10880 aLine.append( m_nCurrentStructElement ); 10881 aLine.append( ": " ); 10882 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); 10883 if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() ) 10884 { 10885 aLine.append( " aliased as \"" ); 10886 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); 10887 aLine.append( '\"' ); 10888 } 10889 } 10890 10891 // "end" the structure element, the parent becomes current element 10892 m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement; 10893 10894 // check whether to emit structure henceforth 10895 m_bEmitStructure = checkEmitStructure(); 10896 10897 if (g_bDebugDisableCompression && m_bEmitStructure) 10898 { 10899 emitComment( aLine.getStr() ); 10900 } 10901 } 10902 10903 /* 10904 * This function adds an internal structure list container to overcome the 8191 elements array limitation 10905 * in kids element emission. 10906 * Recursive function 10907 * 10908 */ 10909 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle ) 10910 { 10911 if( rEle.m_eType == PDFWriter::NonStructElement && 10912 rEle.m_nOwnElement != rEle.m_nParentElement ) 10913 return; 10914 10915 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it ) 10916 { 10917 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) ) 10918 { 10919 PDFStructureElement& rChild = m_aStructure[ *it ]; 10920 if( rChild.m_eType != PDFWriter::NonStructElement ) 10921 { 10922 //triggered when a child of the rEle element is found 10923 if( rChild.m_nParentElement == rEle.m_nOwnElement ) 10924 addInternalStructureContainer( rChild );//examine the child 10925 else 10926 { 10927 OSL_FAIL( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" ); 10928 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::addInternalStructureContainer: invalid child structure element with id " << *it ); 10929 } 10930 } 10931 } 10932 else 10933 { 10934 OSL_FAIL( "PDFWriterImpl::emitStructure: invalid child structure id" ); 10935 SAL_INFO("vcl.pdfwriter", "PDFWriterImpl::addInternalStructureContainer: invalid child structure id " << *it ); 10936 } 10937 } 10938 10939 if( rEle.m_nOwnElement != rEle.m_nParentElement ) 10940 { 10941 if( !rEle.m_aKids.empty() ) 10942 { 10943 if( rEle.m_aKids.size() > ncMaxPDFArraySize ) { 10944 //then we need to add the containers for the kids elements 10945 // a list to be used for the new kid element 10946 std::list< PDFStructureElementKid > aNewKids; 10947 std::list< sal_Int32 > aNewChildren; 10948 10949 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?) 10950 OString aAliasName( "Div" ); 10951 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division ); 10952 10953 while( rEle.m_aKids.size() > ncMaxPDFArraySize ) 10954 { 10955 sal_Int32 nCurrentStructElement = rEle.m_nOwnElement; 10956 sal_Int32 nNewId = sal_Int32(m_aStructure.size()); 10957 m_aStructure.emplace_back( ); 10958 PDFStructureElement& rEleNew = m_aStructure.back(); 10959 rEleNew.m_aAlias = aAliasName; 10960 rEleNew.m_eType = PDFWriter::Division; // a new Div type container 10961 rEleNew.m_nOwnElement = nNewId; 10962 rEleNew.m_nParentElement = nCurrentStructElement; 10963 //inherit the same page as the first child to be reparented 10964 rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject; 10965 rEleNew.m_nObject = createObject();//assign a PDF object number 10966 //add the object to the kid list of the parent 10967 aNewKids.emplace_back( rEleNew.m_nObject ); 10968 aNewChildren.push_back( nNewId ); 10969 10970 std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() ); 10971 std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() ); 10972 advance( aChildEndIt, ncMaxPDFArraySize ); 10973 advance( aKidEndIt, ncMaxPDFArraySize ); 10974 10975 rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(), 10976 rEle.m_aKids, 10977 rEle.m_aKids.begin(), 10978 aKidEndIt ); 10979 rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(), 10980 rEle.m_aChildren, 10981 rEle.m_aChildren.begin(), 10982 aChildEndIt ); 10983 // set the kid's new parent 10984 for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin(); 10985 it != rEleNew.m_aChildren.end(); ++it ) 10986 { 10987 m_aStructure[ *it ].m_nParentElement = nNewId; 10988 } 10989 } 10990 //finally add the new kids resulting from the container added 10991 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() ); 10992 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() ); 10993 } 10994 } 10995 } 10996 } 10997 10998 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle ) 10999 { 11000 bool bSuccess = false; 11001 11002 if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) ) 11003 { 11004 // end eventual previous marked content sequence 11005 endStructureElementMCSeq(); 11006 11007 m_nCurrentStructElement = nEle; 11008 m_bEmitStructure = checkEmitStructure(); 11009 if (g_bDebugDisableCompression) 11010 { 11011 OStringBuffer aLine( "setCurrentStructureElement " ); 11012 aLine.append( m_nCurrentStructElement ); 11013 aLine.append( ": " ); 11014 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); 11015 if( !m_aStructure[ m_nCurrentStructElement ].m_aAlias.isEmpty() ) 11016 { 11017 aLine.append( " aliased as \"" ); 11018 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); 11019 aLine.append( '\"' ); 11020 } 11021 if( ! m_bEmitStructure ) 11022 aLine.append( " (inside NonStruct)" ); 11023 emitComment( aLine.getStr() ); 11024 } 11025 bSuccess = true; 11026 } 11027 11028 return bSuccess; 11029 } 11030 11031 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal ) 11032 { 11033 if( !m_aContext.Tagged ) 11034 return false; 11035 11036 bool bInsert = false; 11037 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11038 { 11039 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11040 switch( eAttr ) 11041 { 11042 case PDFWriter::Placement: 11043 if( eVal == PDFWriter::Block || 11044 eVal == PDFWriter::Inline || 11045 eVal == PDFWriter::Before || 11046 eVal == PDFWriter::Start || 11047 eVal == PDFWriter::End ) 11048 bInsert = true; 11049 break; 11050 case PDFWriter::WritingMode: 11051 if( eVal == PDFWriter::LrTb || 11052 eVal == PDFWriter::RlTb || 11053 eVal == PDFWriter::TbRl ) 11054 { 11055 bInsert = true; 11056 } 11057 break; 11058 case PDFWriter::TextAlign: 11059 if( eVal == PDFWriter::Start || 11060 eVal == PDFWriter::Center || 11061 eVal == PDFWriter::End || 11062 eVal == PDFWriter::Justify ) 11063 { 11064 if( eType == PDFWriter::Paragraph || 11065 eType == PDFWriter::Heading || 11066 eType == PDFWriter::H1 || 11067 eType == PDFWriter::H2 || 11068 eType == PDFWriter::H3 || 11069 eType == PDFWriter::H4 || 11070 eType == PDFWriter::H5 || 11071 eType == PDFWriter::H6 || 11072 eType == PDFWriter::List || 11073 eType == PDFWriter::ListItem || 11074 eType == PDFWriter::LILabel || 11075 eType == PDFWriter::LIBody || 11076 eType == PDFWriter::Table || 11077 eType == PDFWriter::TableRow || 11078 eType == PDFWriter::TableHeader || 11079 eType == PDFWriter::TableData ) 11080 { 11081 bInsert = true; 11082 } 11083 } 11084 break; 11085 case PDFWriter::Width: 11086 case PDFWriter::Height: 11087 if( eVal == PDFWriter::Auto ) 11088 { 11089 if( eType == PDFWriter::Figure || 11090 eType == PDFWriter::Formula || 11091 eType == PDFWriter::Form || 11092 eType == PDFWriter::Table || 11093 eType == PDFWriter::TableHeader || 11094 eType == PDFWriter::TableData ) 11095 { 11096 bInsert = true; 11097 } 11098 } 11099 break; 11100 case PDFWriter::BlockAlign: 11101 if( eVal == PDFWriter::Before || 11102 eVal == PDFWriter::Middle || 11103 eVal == PDFWriter::After || 11104 eVal == PDFWriter::Justify ) 11105 { 11106 if( eType == PDFWriter::TableHeader || 11107 eType == PDFWriter::TableData ) 11108 { 11109 bInsert = true; 11110 } 11111 } 11112 break; 11113 case PDFWriter::InlineAlign: 11114 if( eVal == PDFWriter::Start || 11115 eVal == PDFWriter::Center || 11116 eVal == PDFWriter::End ) 11117 { 11118 if( eType == PDFWriter::TableHeader || 11119 eType == PDFWriter::TableData ) 11120 { 11121 bInsert = true; 11122 } 11123 } 11124 break; 11125 case PDFWriter::LineHeight: 11126 if( eVal == PDFWriter::Normal || 11127 eVal == PDFWriter::Auto ) 11128 { 11129 // only for ILSE and BLSE 11130 if( eType == PDFWriter::Paragraph || 11131 eType == PDFWriter::Heading || 11132 eType == PDFWriter::H1 || 11133 eType == PDFWriter::H2 || 11134 eType == PDFWriter::H3 || 11135 eType == PDFWriter::H4 || 11136 eType == PDFWriter::H5 || 11137 eType == PDFWriter::H6 || 11138 eType == PDFWriter::List || 11139 eType == PDFWriter::ListItem || 11140 eType == PDFWriter::LILabel || 11141 eType == PDFWriter::LIBody || 11142 eType == PDFWriter::Table || 11143 eType == PDFWriter::TableRow || 11144 eType == PDFWriter::TableHeader || 11145 eType == PDFWriter::TableData || 11146 eType == PDFWriter::Span || 11147 eType == PDFWriter::Quote || 11148 eType == PDFWriter::Note || 11149 eType == PDFWriter::Reference || 11150 eType == PDFWriter::BibEntry || 11151 eType == PDFWriter::Code || 11152 eType == PDFWriter::Link ) 11153 { 11154 bInsert = true; 11155 } 11156 } 11157 break; 11158 case PDFWriter::TextDecorationType: 11159 if( eVal == PDFWriter::NONE || 11160 eVal == PDFWriter::Underline || 11161 eVal == PDFWriter::Overline || 11162 eVal == PDFWriter::LineThrough ) 11163 { 11164 // only for ILSE and BLSE 11165 if( eType == PDFWriter::Paragraph || 11166 eType == PDFWriter::Heading || 11167 eType == PDFWriter::H1 || 11168 eType == PDFWriter::H2 || 11169 eType == PDFWriter::H3 || 11170 eType == PDFWriter::H4 || 11171 eType == PDFWriter::H5 || 11172 eType == PDFWriter::H6 || 11173 eType == PDFWriter::List || 11174 eType == PDFWriter::ListItem || 11175 eType == PDFWriter::LILabel || 11176 eType == PDFWriter::LIBody || 11177 eType == PDFWriter::Table || 11178 eType == PDFWriter::TableRow || 11179 eType == PDFWriter::TableHeader || 11180 eType == PDFWriter::TableData || 11181 eType == PDFWriter::Span || 11182 eType == PDFWriter::Quote || 11183 eType == PDFWriter::Note || 11184 eType == PDFWriter::Reference || 11185 eType == PDFWriter::BibEntry || 11186 eType == PDFWriter::Code || 11187 eType == PDFWriter::Link ) 11188 { 11189 bInsert = true; 11190 } 11191 } 11192 break; 11193 case PDFWriter::ListNumbering: 11194 if( eVal == PDFWriter::NONE || 11195 eVal == PDFWriter::Disc || 11196 eVal == PDFWriter::Circle || 11197 eVal == PDFWriter::Square || 11198 eVal == PDFWriter::Decimal || 11199 eVal == PDFWriter::UpperRoman || 11200 eVal == PDFWriter::LowerRoman || 11201 eVal == PDFWriter::UpperAlpha || 11202 eVal == PDFWriter::LowerAlpha ) 11203 { 11204 if( eType == PDFWriter::List ) 11205 bInsert = true; 11206 } 11207 break; 11208 default: break; 11209 } 11210 } 11211 11212 if( bInsert ) 11213 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal ); 11214 else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11215 SAL_INFO("vcl.pdfwriter", 11216 "rejecting setStructureAttribute( " << getAttributeTag( eAttr ) 11217 << ", " << getAttributeValueTag( eVal ) 11218 << " ) on " << getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) 11219 << " (" << m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() 11220 << ") element"); 11221 11222 return bInsert; 11223 } 11224 11225 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue ) 11226 { 11227 if( ! m_aContext.Tagged ) 11228 return false; 11229 11230 bool bInsert = false; 11231 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11232 { 11233 if( eAttr == PDFWriter::Language ) 11234 { 11235 m_aStructure[ m_nCurrentStructElement ].m_aLocale = LanguageTag( LanguageType(nValue) ).getLocale(); 11236 return true; 11237 } 11238 11239 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11240 switch( eAttr ) 11241 { 11242 case PDFWriter::SpaceBefore: 11243 case PDFWriter::SpaceAfter: 11244 case PDFWriter::StartIndent: 11245 case PDFWriter::EndIndent: 11246 // just for BLSE 11247 if( eType == PDFWriter::Paragraph || 11248 eType == PDFWriter::Heading || 11249 eType == PDFWriter::H1 || 11250 eType == PDFWriter::H2 || 11251 eType == PDFWriter::H3 || 11252 eType == PDFWriter::H4 || 11253 eType == PDFWriter::H5 || 11254 eType == PDFWriter::H6 || 11255 eType == PDFWriter::List || 11256 eType == PDFWriter::ListItem || 11257 eType == PDFWriter::LILabel || 11258 eType == PDFWriter::LIBody || 11259 eType == PDFWriter::Table || 11260 eType == PDFWriter::TableRow || 11261 eType == PDFWriter::TableHeader || 11262 eType == PDFWriter::TableData ) 11263 { 11264 bInsert = true; 11265 } 11266 break; 11267 case PDFWriter::TextIndent: 11268 // paragraph like BLSE and additional elements 11269 if( eType == PDFWriter::Paragraph || 11270 eType == PDFWriter::Heading || 11271 eType == PDFWriter::H1 || 11272 eType == PDFWriter::H2 || 11273 eType == PDFWriter::H3 || 11274 eType == PDFWriter::H4 || 11275 eType == PDFWriter::H5 || 11276 eType == PDFWriter::H6 || 11277 eType == PDFWriter::LILabel || 11278 eType == PDFWriter::LIBody || 11279 eType == PDFWriter::TableHeader || 11280 eType == PDFWriter::TableData ) 11281 { 11282 bInsert = true; 11283 } 11284 break; 11285 case PDFWriter::Width: 11286 case PDFWriter::Height: 11287 if( eType == PDFWriter::Figure || 11288 eType == PDFWriter::Formula || 11289 eType == PDFWriter::Form || 11290 eType == PDFWriter::Table || 11291 eType == PDFWriter::TableHeader || 11292 eType == PDFWriter::TableData ) 11293 { 11294 bInsert = true; 11295 } 11296 break; 11297 case PDFWriter::LineHeight: 11298 case PDFWriter::BaselineShift: 11299 // only for ILSE and BLSE 11300 if( eType == PDFWriter::Paragraph || 11301 eType == PDFWriter::Heading || 11302 eType == PDFWriter::H1 || 11303 eType == PDFWriter::H2 || 11304 eType == PDFWriter::H3 || 11305 eType == PDFWriter::H4 || 11306 eType == PDFWriter::H5 || 11307 eType == PDFWriter::H6 || 11308 eType == PDFWriter::List || 11309 eType == PDFWriter::ListItem || 11310 eType == PDFWriter::LILabel || 11311 eType == PDFWriter::LIBody || 11312 eType == PDFWriter::Table || 11313 eType == PDFWriter::TableRow || 11314 eType == PDFWriter::TableHeader || 11315 eType == PDFWriter::TableData || 11316 eType == PDFWriter::Span || 11317 eType == PDFWriter::Quote || 11318 eType == PDFWriter::Note || 11319 eType == PDFWriter::Reference || 11320 eType == PDFWriter::BibEntry || 11321 eType == PDFWriter::Code || 11322 eType == PDFWriter::Link ) 11323 { 11324 bInsert = true; 11325 } 11326 break; 11327 case PDFWriter::RowSpan: 11328 case PDFWriter::ColSpan: 11329 // only for table cells 11330 if( eType == PDFWriter::TableHeader || 11331 eType == PDFWriter::TableData ) 11332 { 11333 bInsert = true; 11334 } 11335 break; 11336 case PDFWriter::LinkAnnotation: 11337 if( eType == PDFWriter::Link ) 11338 bInsert = true; 11339 break; 11340 default: break; 11341 } 11342 } 11343 11344 if( bInsert ) 11345 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue ); 11346 else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11347 SAL_INFO("vcl.pdfwriter", 11348 "rejecting setStructureAttributeNumerical( " << getAttributeTag( eAttr ) 11349 << ", " << (int)nValue 11350 << " ) on " << getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) 11351 << " (" << m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() 11352 << ") element"); 11353 11354 return bInsert; 11355 } 11356 11357 void PDFWriterImpl::setStructureBoundingBox( const tools::Rectangle& rRect ) 11358 { 11359 sal_Int32 nPageNr = m_nCurrentPage; 11360 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged ) 11361 return; 11362 11363 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11364 { 11365 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11366 if( eType == PDFWriter::Figure || 11367 eType == PDFWriter::Formula || 11368 eType == PDFWriter::Form || 11369 eType == PDFWriter::Table ) 11370 { 11371 m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect; 11372 // convert to default user space now, since the mapmode may change 11373 m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox ); 11374 } 11375 } 11376 } 11377 11378 void PDFWriterImpl::setActualText( const OUString& rText ) 11379 { 11380 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) 11381 { 11382 m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText; 11383 } 11384 } 11385 11386 void PDFWriterImpl::setAlternateText( const OUString& rText ) 11387 { 11388 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) 11389 { 11390 m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText; 11391 } 11392 } 11393 11394 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr ) 11395 { 11396 if( nPageNr < 0 ) 11397 nPageNr = m_nCurrentPage; 11398 11399 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11400 return; 11401 11402 m_aPages[ nPageNr ].m_nDuration = nSeconds; 11403 } 11404 11405 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr ) 11406 { 11407 if( nPageNr < 0 ) 11408 nPageNr = m_nCurrentPage; 11409 11410 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11411 return; 11412 11413 m_aPages[ nPageNr ].m_eTransition = eType; 11414 m_aPages[ nPageNr ].m_nTransTime = nMilliSec; 11415 } 11416 11417 void PDFWriterImpl::ensureUniqueRadioOnValues() 11418 { 11419 // loop over radio groups 11420 for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin(); 11421 group != m_aRadioGroupWidgets.end(); ++group ) 11422 { 11423 PDFWidget& rGroupWidget = m_aWidgets[ group->second ]; 11424 // check whether all kids have a unique OnValue 11425 std::unordered_map< OUString, sal_Int32 > aOnValues; 11426 int nChildren = rGroupWidget.m_aKidsIndex.size(); 11427 bool bIsUnique = true; 11428 for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ ) 11429 { 11430 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11431 const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue; 11432 SAL_INFO("vcl.pdfwriter", "OnValue: " << rVal); 11433 if( aOnValues.find( rVal ) == aOnValues.end() ) 11434 { 11435 aOnValues[ rVal ] = 1; 11436 } 11437 else 11438 { 11439 bIsUnique = false; 11440 } 11441 } 11442 if( ! bIsUnique ) 11443 { 11444 SAL_INFO("vcl.pdfwriter", "enforcing unique OnValues" ); 11445 // make unique by using ascending OnValues 11446 for( int nKid = 0; nKid < nChildren; nKid++ ) 11447 { 11448 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11449 PDFWidget& rKid = m_aWidgets[nKidIndex]; 11450 rKid.m_aOnValue = OUString::number( nKid+1 ); 11451 if( rKid.m_aValue != "Off" ) 11452 rKid.m_aValue = rKid.m_aOnValue; 11453 } 11454 } 11455 // finally move the "Yes" appearance to the OnValue appearance 11456 for( int nKid = 0; nKid < nChildren; nKid++ ) 11457 { 11458 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11459 PDFWidget& rKid = m_aWidgets[nKidIndex]; 11460 PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" ); 11461 if( app_it != rKid.m_aAppearances.end() ) 11462 { 11463 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" ); 11464 if( stream_it != app_it->second.end() ) 11465 { 11466 SvMemoryStream* pStream = stream_it->second; 11467 app_it->second.erase( stream_it ); 11468 OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 ); 11469 appendName( rKid.m_aOnValue, aBuf ); 11470 (app_it->second)[ aBuf.makeStringAndClear() ] = pStream; 11471 } 11472 else 11473 SAL_INFO("vcl.pdfwriter", "error: RadioButton without \"Yes\" stream" ); 11474 } 11475 // update selected radio button 11476 if( rKid.m_aValue != "Off" ) 11477 { 11478 rGroupWidget.m_aValue = rKid.m_aValue; 11479 } 11480 } 11481 } 11482 } 11483 11484 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn ) 11485 { 11486 sal_Int32 nRadioGroupWidget = -1; 11487 11488 std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup ); 11489 11490 if( it == m_aRadioGroupWidgets.end() ) 11491 { 11492 m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget = 11493 sal_Int32(m_aWidgets.size()); 11494 11495 // new group, insert the radiobutton 11496 m_aWidgets.emplace_back( ); 11497 m_aWidgets.back().m_nObject = createObject(); 11498 m_aWidgets.back().m_nPage = m_nCurrentPage; 11499 m_aWidgets.back().m_eType = PDFWriter::RadioButton; 11500 m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup; 11501 m_aWidgets.back().m_nFlags |= 0x0000C000; // NoToggleToOff and Radio bits 11502 11503 createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn ); 11504 } 11505 else 11506 nRadioGroupWidget = it->second; 11507 11508 return nRadioGroupWidget; 11509 } 11510 11511 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr ) 11512 { 11513 if( nPageNr < 0 ) 11514 nPageNr = m_nCurrentPage; 11515 11516 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11517 return -1; 11518 11519 bool sigHidden(true); 11520 sal_Int32 nNewWidget = m_aWidgets.size(); 11521 m_aWidgets.emplace_back( ); 11522 11523 m_aWidgets.back().m_nObject = createObject(); 11524 m_aWidgets.back().m_aRect = rControl.Location; 11525 m_aWidgets.back().m_nPage = nPageNr; 11526 m_aWidgets.back().m_eType = rControl.getType(); 11527 11528 sal_Int32 nRadioGroupWidget = -1; 11529 // for unknown reasons the radio buttons of a radio group must not have a 11530 // field name, else the buttons are in fact check boxes - 11531 // that is multiple buttons of the radio group can be selected 11532 if( rControl.getType() == PDFWriter::RadioButton ) 11533 nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) ); 11534 else 11535 { 11536 createWidgetFieldName( nNewWidget, rControl ); 11537 } 11538 11539 // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid 11540 PDFWidget& rNewWidget = m_aWidgets[nNewWidget]; 11541 rNewWidget.m_aDescription = rControl.Description; 11542 rNewWidget.m_aText = rControl.Text; 11543 rNewWidget.m_nTextStyle = rControl.TextStyle & 11544 ( DrawTextFlags::Left | DrawTextFlags::Center | DrawTextFlags::Right | DrawTextFlags::Top | 11545 DrawTextFlags::VCenter | DrawTextFlags::Bottom | 11546 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak ); 11547 rNewWidget.m_nTabOrder = rControl.TabOrder; 11548 11549 // various properties are set via the flags (/Ff) property of the field dict 11550 if( rControl.ReadOnly ) 11551 rNewWidget.m_nFlags |= 1; 11552 if( rControl.getType() == PDFWriter::PushButton ) 11553 { 11554 const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl); 11555 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE ) 11556 rNewWidget.m_nTextStyle = 11557 DrawTextFlags::Center | DrawTextFlags::VCenter | 11558 DrawTextFlags::MultiLine | DrawTextFlags::WordBreak; 11559 11560 rNewWidget.m_nFlags |= 0x00010000; 11561 if( !rBtn.URL.isEmpty() ) 11562 rNewWidget.m_aListEntries.push_back( rBtn.URL ); 11563 rNewWidget.m_bSubmit = rBtn.Submit; 11564 rNewWidget.m_bSubmitGet = rBtn.SubmitGet; 11565 rNewWidget.m_nDest = rBtn.Dest; 11566 createDefaultPushButtonAppearance( rNewWidget, rBtn ); 11567 } 11568 else if( rControl.getType() == PDFWriter::RadioButton ) 11569 { 11570 const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl); 11571 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE ) 11572 rNewWidget.m_nTextStyle = 11573 DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak; 11574 /* PDF sees a RadioButton group as one radio button with 11575 * children which are in turn check boxes 11576 * 11577 * so we need to create a radio button on demand for a new group 11578 * and insert a checkbox for each RadioButtonWidget as its child 11579 */ 11580 rNewWidget.m_eType = PDFWriter::CheckBox; 11581 rNewWidget.m_nRadioGroup = rBtn.RadioGroup; 11582 11583 SAL_WARN_IF( nRadioGroupWidget < 0 || nRadioGroupWidget >= (sal_Int32)m_aWidgets.size(), "vcl.pdfwriter", "no radio group parent" ); 11584 11585 PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget]; 11586 rRadioButton.m_aKids.push_back( rNewWidget.m_nObject ); 11587 rRadioButton.m_aKidsIndex.push_back( nNewWidget ); 11588 rNewWidget.m_nParent = rRadioButton.m_nObject; 11589 11590 rNewWidget.m_aValue = "Off"; 11591 rNewWidget.m_aOnValue = rBtn.OnValue; 11592 if( rRadioButton.m_aValue.isEmpty() && rBtn.Selected ) 11593 { 11594 rNewWidget.m_aValue = rNewWidget.m_aOnValue; 11595 rRadioButton.m_aValue = rNewWidget.m_aOnValue; 11596 } 11597 createDefaultRadioButtonAppearance( rNewWidget, rBtn ); 11598 11599 // union rect of radio group 11600 tools::Rectangle aRect = rNewWidget.m_aRect; 11601 m_aPages[ nPageNr ].convertRect( aRect ); 11602 rRadioButton.m_aRect.Union( aRect ); 11603 } 11604 else if( rControl.getType() == PDFWriter::CheckBox ) 11605 { 11606 const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl); 11607 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE ) 11608 rNewWidget.m_nTextStyle = 11609 DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak; 11610 11611 rNewWidget.m_aValue = rBox.Checked ? OUString("Yes") : OUString("Off" ); 11612 // create default appearance before m_aRect gets transformed 11613 createDefaultCheckBoxAppearance( rNewWidget, rBox ); 11614 } 11615 else if( rControl.getType() == PDFWriter::ListBox ) 11616 { 11617 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE ) 11618 rNewWidget.m_nTextStyle = DrawTextFlags::VCenter; 11619 11620 const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl); 11621 rNewWidget.m_aListEntries = rLstBox.Entries; 11622 rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries; 11623 rNewWidget.m_aValue = rLstBox.Text; 11624 if( rLstBox.DropDown ) 11625 rNewWidget.m_nFlags |= 0x00020000; 11626 if( rLstBox.MultiSelect && !rLstBox.DropDown && m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 ) 11627 rNewWidget.m_nFlags |= 0x00200000; 11628 11629 createDefaultListBoxAppearance( rNewWidget, rLstBox ); 11630 } 11631 else if( rControl.getType() == PDFWriter::ComboBox ) 11632 { 11633 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE ) 11634 rNewWidget.m_nTextStyle = DrawTextFlags::VCenter; 11635 11636 const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl); 11637 rNewWidget.m_aValue = rBox.Text; 11638 rNewWidget.m_aListEntries = rBox.Entries; 11639 rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag 11640 11641 PDFWriter::ListBoxWidget aLBox; 11642 aLBox.Name = rBox.Name; 11643 aLBox.Description = rBox.Description; 11644 aLBox.Text = rBox.Text; 11645 aLBox.TextStyle = rBox.TextStyle; 11646 aLBox.ReadOnly = rBox.ReadOnly; 11647 aLBox.Border = rBox.Border; 11648 aLBox.BorderColor = rBox.BorderColor; 11649 aLBox.Background = rBox.Background; 11650 aLBox.BackgroundColor = rBox.BackgroundColor; 11651 aLBox.TextFont = rBox.TextFont; 11652 aLBox.TextColor = rBox.TextColor; 11653 aLBox.DropDown = true; 11654 aLBox.MultiSelect = false; 11655 aLBox.Entries = rBox.Entries; 11656 11657 createDefaultListBoxAppearance( rNewWidget, aLBox ); 11658 } 11659 else if( rControl.getType() == PDFWriter::Edit ) 11660 { 11661 if( rNewWidget.m_nTextStyle == DrawTextFlags::NONE ) 11662 rNewWidget.m_nTextStyle = DrawTextFlags::Left | DrawTextFlags::VCenter; 11663 11664 const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl); 11665 if( rEdit.MultiLine ) 11666 { 11667 rNewWidget.m_nFlags |= 0x00001000; 11668 rNewWidget.m_nTextStyle |= DrawTextFlags::MultiLine | DrawTextFlags::WordBreak; 11669 } 11670 if( rEdit.Password ) 11671 rNewWidget.m_nFlags |= 0x00002000; 11672 if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDFVersion::PDF_1_3 ) 11673 rNewWidget.m_nFlags |= 0x00100000; 11674 rNewWidget.m_nMaxLen = rEdit.MaxLen; 11675 rNewWidget.m_aValue = rEdit.Text; 11676 11677 createDefaultEditAppearance( rNewWidget, rEdit ); 11678 } 11679 #if HAVE_FEATURE_NSS 11680 else if( rControl.getType() == PDFWriter::Signature) 11681 { 11682 sigHidden = true; 11683 11684 rNewWidget.m_aRect = tools::Rectangle(0, 0, 0, 0); 11685 11686 m_nSignatureObject = createObject(); 11687 rNewWidget.m_aValue = OUString::number( m_nSignatureObject ); 11688 rNewWidget.m_aValue += " 0 R"; 11689 // let's add a fake appearance 11690 rNewWidget.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream(); 11691 } 11692 #endif 11693 11694 // if control is a hidden signature, do not convert coordinates since we 11695 // need /Rect [ 0 0 0 0 ] 11696 if ( ! ( ( rControl.getType() == PDFWriter::Signature ) && sigHidden ) ) 11697 { 11698 // convert to default user space now, since the mapmode may change 11699 // note: create default appearances before m_aRect gets transformed 11700 m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect ); 11701 } 11702 11703 // insert widget to page's annotation list 11704 m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject ); 11705 11706 return nNewWidget; 11707 } 11708 11709 void PDFWriterImpl::addStream( const OUString& rMimeType, PDFOutputStream* pStream ) 11710 { 11711 if( pStream ) 11712 { 11713 m_aAdditionalStreams.emplace_back( ); 11714 PDFAddStream& rStream = m_aAdditionalStreams.back(); 11715 rStream.m_aMimeType = !rMimeType.isEmpty() 11716 ? rMimeType 11717 : OUString( "application/octet-stream" ); 11718 rStream.m_pStream = pStream; 11719 rStream.m_bCompress = false; 11720 } 11721 } 11722 11723 void PDFWriterImpl::MARK( const char* pString ) 11724 { 11725 beginStructureElementMCSeq(); 11726 if (g_bDebugDisableCompression) 11727 emitComment( pString ); 11728 } 11729 11730 sal_Int32 PDFWriterImpl::ReferenceXObjectEmit::getObject() const 11731 { 11732 if (m_nFormObject > 0) 11733 return m_nFormObject; 11734 else 11735 return m_nBitmapObject; 11736 } 11737 11738 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 11739
