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