1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_vcl.hxx" 30 31 #define _USE_MATH_DEFINES 32 #include <math.h> 33 #include <algorithm> 34 35 #include <tools/urlobj.hxx> 36 37 #include <pdfwriter_impl.hxx> 38 39 #include <basegfx/polygon/b2dpolygon.hxx> 40 #include <basegfx/polygon/b2dpolypolygon.hxx> 41 #include <basegfx/polygon/b2dpolygontools.hxx> 42 #include <basegfx/polygon/b2dpolypolygontools.hxx> 43 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 44 #include <basegfx/matrix/b2dhommatrix.hxx> 45 46 #include <osl/thread.h> 47 #include <osl/file.h> 48 49 #include <rtl/crc.h> 50 #include <rtl/digest.h> 51 #include <rtl/ustrbuf.hxx> 52 53 #include <tools/debug.hxx> 54 #include <tools/zcodec.hxx> 55 #include <tools/stream.hxx> 56 57 #include <i18npool/mslangid.hxx> 58 59 #include <vcl/virdev.hxx> 60 #include <vcl/bmpacc.hxx> 61 #include <vcl/bitmapex.hxx> 62 #include <vcl/image.hxx> 63 #include <vcl/metric.hxx> 64 #include <vcl/svapp.hxx> 65 #include <vcl/lineinfo.hxx> 66 #include "vcl/cvtgrf.hxx" 67 #include "vcl/strhelper.hxx" 68 69 #include <fontsubset.hxx> 70 #include <outdev.h> 71 #include <sallayout.hxx> 72 #include <textlayout.hxx> 73 #include <salgdi.hxx> 74 75 #include <icc/sRGB-IEC61966-2.1.hxx> 76 77 #include <comphelper/processfactory.hxx> 78 79 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 80 #include <com/sun/star/util/URL.hpp> 81 82 #include "cppuhelper/implbase1.hxx" 83 84 using namespace vcl; 85 using namespace rtl; 86 87 #if (OSL_DEBUG_LEVEL < 2) 88 #define COMPRESS_PAGES 89 #else 90 #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams 91 #endif 92 93 #ifdef DO_TEST_PDF 94 class PDFTestOutputStream : public PDFOutputStream 95 { 96 public: 97 virtual ~PDFTestOutputStream(); 98 virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream ); 99 }; 100 101 PDFTestOutputStream::~PDFTestOutputStream() 102 { 103 } 104 105 void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream ) 106 { 107 OString aStr( "lalala\ntest\ntest\ntest" ); 108 com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() ); 109 rtl_copyMemory( aData.getArray(), aStr.getStr(), aStr.getLength() ); 110 xStream->writeBytes( aData ); 111 } 112 113 // this test code cannot be used to test PDF/A-1 because it forces 114 // control item (widgets) to bypass the structure controlling 115 // the embedding of such elements in actual run 116 void doTestCode() 117 { 118 static const char* pHome = getenv( "HOME" ); 119 rtl::OUString aTestFile( RTL_CONSTASCII_USTRINGPARAM( "file://" ) ); 120 aTestFile += rtl::OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 ); 121 aTestFile += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/pdf_export_test.pdf" ) ); 122 123 PDFWriter::PDFWriterContext aContext; 124 aContext.URL = aTestFile; 125 aContext.Version = PDFWriter::PDF_1_4; 126 aContext.Tagged = true; 127 aContext.InitialPage = 2; 128 aContext.DocumentInfo.Title = OUString( RTL_CONSTASCII_USTRINGPARAM( "PDF export test document" ) ); 129 aContext.DocumentInfo.Producer = OUString( RTL_CONSTASCII_USTRINGPARAM( "VCL" ) ); 130 131 PDFWriter aWriter( aContext ); 132 aWriter.NewPage( 595, 842 ); 133 aWriter.BeginStructureElement( PDFWriter::Document ); 134 // set duration of 3 sec for first page 135 aWriter.SetAutoAdvanceTime( 3 ); 136 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); 137 138 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 139 aWriter.SetLineColor( Color( COL_LIGHTGREEN ) ); 140 aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 ); 141 142 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) ); 143 aWriter.SetTextColor( Color( COL_BLACK ) ); 144 aWriter.SetLineColor( Color( COL_BLACK ) ); 145 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) ); 146 147 Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) ); 148 aWriter.DrawRect( aRect ); 149 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 1" ) ) ); 150 sal_Int32 nFirstLink = aWriter.CreateLink( aRect ); 151 PDFNote aNote; 152 aNote.Title = String( RTL_CONSTASCII_USTRINGPARAM( "A small test note" ) ); 153 aNote.Contents = String( RTL_CONSTASCII_USTRINGPARAM( "There is no business like show business like no business i know. Everything about it is appealing." ) ); 154 aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote ); 155 156 Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) ); 157 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 158 aWriter.DrawRect( aTargetRect ); 159 aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest second link" ) ) ); 160 sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect ); 161 162 aWriter.BeginStructureElement( PDFWriter::Section ); 163 aWriter.BeginStructureElement( PDFWriter::Heading ); 164 aWriter.DrawText( Point(4500, 9000), String( RTL_CONSTASCII_USTRINGPARAM( "A small structure test" ) ) ); 165 aWriter.EndStructureElement(); 166 aWriter.BeginStructureElement( PDFWriter::Paragraph ); 167 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb ); 168 aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline ); 169 aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ), 170 String( RTL_CONSTASCII_USTRINGPARAM( "It was the best of PDF, it was the worst of PDF ... or so. This is a pretty nonsensical text to denote a paragraph. I suggest you stop reading it. Because if you read on you might get bored. So continue on your on risk. Hey, you're still here ? Why do you continue to read this as it is of no use at all ? OK, it's your time, but still... . Woah, i even get bored writing this, so let's end this here and now." ) ), 171 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK 172 ); 173 aWriter.SetActualText( String( RTL_CONSTASCII_USTRINGPARAM( "It was the best of PDF, it was the worst of PDF ... or so. This is a pretty nonsensical text to denote a paragraph. I suggest you stop reading it. Because if you read on you might get bored. So continue on your on risk. Hey, you're still here ? Why do you continue to read this as it is of no use at all ? OK, it's your time, but still... . Woah, i even get bored writing this, so let's end this here and now." ) ) ); 174 aWriter.SetAlternateText( String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." ) ) ); 175 aWriter.EndStructureElement(); 176 sal_Int32 nLongPara = aWriter.BeginStructureElement( PDFWriter::Paragraph ); 177 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb ); 178 aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ), 179 String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph is nothing special either but ends on the next page structurewise" ) ), 180 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK 181 ); 182 183 aWriter.NewPage( 595, 842 ); 184 // test AddStream interface 185 aWriter.AddStream( String( RTL_CONSTASCII_USTRINGPARAM( "text/plain" ) ), new PDFTestOutputStream(), true ); 186 // set transitional mode 187 aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 ); 188 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); 189 aWriter.SetTextColor( Color( COL_BLACK ) ); 190 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) ); 191 aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ), 192 String( RTL_CONSTASCII_USTRINGPARAM( "Here's where all things come to an end ... well at least the paragaph from the last page." ) ), 193 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK 194 ); 195 aWriter.EndStructureElement(); 196 197 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) ); 198 // disable structure 199 aWriter.BeginStructureElement( PDFWriter::NonStructElement ); 200 aWriter.DrawRect( aRect ); 201 aWriter.BeginStructureElement( PDFWriter::Paragraph ); 202 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 2" ) ) ); 203 sal_Int32 nSecondLink = aWriter.CreateLink( aRect ); 204 205 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 206 aWriter.BeginStructureElement( PDFWriter::ListItem ); 207 aWriter.DrawRect( aTargetRect ); 208 aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest first link" ) ) ); 209 sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect ); 210 // enable structure 211 aWriter.EndStructureElement(); 212 // add something to the long paragraph as an afterthought 213 sal_Int32 nSaveStruct = aWriter.GetCurrentStructureElement(); 214 aWriter.SetCurrentStructureElement( nLongPara ); 215 aWriter.DrawText( Rectangle( Point( 4500,4500 ), Size( 12000, 1000 ) ), 216 String( RTL_CONSTASCII_USTRINGPARAM( "Add something to the longish paragraph above." ) ), 217 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 218 aWriter.SetCurrentStructureElement( nSaveStruct ); 219 aWriter.EndStructureElement(); 220 aWriter.EndStructureElement(); 221 aWriter.BeginStructureElement( PDFWriter::Figure ); 222 aWriter.BeginStructureElement( PDFWriter::Caption ); 223 aWriter.DrawText( Point( 4500, 9000 ), String( RTL_CONSTASCII_USTRINGPARAM( "Some drawing stuff inside the structure" ) ) ); 224 aWriter.EndStructureElement(); 225 226 // test clipping 227 basegfx::B2DPolyPolygon aClip; 228 basegfx::B2DPolygon aClipPoly; 229 aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) ); 230 aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) ); 231 aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) ); 232 aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) ); 233 aClipPoly.setClosed( true ); 234 //aClipPoly.flip(); 235 aClip.append( aClipPoly ); 236 237 aWriter.Push( PUSH_CLIPREGION | PUSH_FILLCOLOR ); 238 aWriter.SetClipRegion( aClip ); 239 aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) ); 240 aWriter.MoveClipRegion( 1000, 500 ); 241 aWriter.SetFillColor( Color( COL_RED ) ); 242 aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) ); 243 aWriter.Pop(); 244 // test transparency 245 // draw background 246 Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) ); 247 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 248 aWriter.DrawRect( aTranspRect ); 249 aWriter.BeginTransparencyGroup(); 250 251 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 252 aWriter.DrawEllipse( aTranspRect ); 253 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) ); 254 aWriter.DrawText( aTranspRect, 255 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ), 256 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 257 258 aWriter.EndTransparencyGroup( aTranspRect, 50 ); 259 260 // prepare an alpha mask 261 Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) ); 262 BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess(); 263 for( int nX = 0; nX < 256; nX++ ) 264 for( int nY = 0; nY < 256; nY++ ) 265 pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) ); 266 aTransMask.ReleaseAccess( pAcc ); 267 aTransMask.SetPrefMapMode( MAP_MM ); 268 aTransMask.SetPrefSize( Size( 10, 10 ) ); 269 270 aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask ); 271 272 aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) ); 273 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 274 aWriter.DrawRect( aTranspRect ); 275 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 276 aWriter.DrawEllipse( aTranspRect ); 277 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) ); 278 aWriter.DrawText( aTranspRect, 279 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ), 280 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 281 aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) ); 282 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 283 aWriter.DrawRect( aTranspRect ); 284 aWriter.BeginTransparencyGroup(); 285 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 286 aWriter.DrawEllipse( aTranspRect ); 287 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) ); 288 aWriter.DrawText( aTranspRect, 289 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ), 290 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 291 aWriter.EndTransparencyGroup( aTranspRect, aTransMask ); 292 293 Bitmap aImageBmp( Size( 256, 256 ), 24 ); 294 pAcc = aImageBmp.AcquireWriteAccess(); 295 pAcc->SetFillColor( Color( 0xff, 0, 0xff ) ); 296 pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) ); 297 aImageBmp.ReleaseAccess( pAcc ); 298 BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) ); 299 aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx ); 300 301 302 aWriter.EndStructureElement(); 303 aWriter.EndStructureElement(); 304 305 LineInfo aLI( LINE_DASH, 3 ); 306 aLI.SetDashCount( 2 ); 307 aLI.SetDashLen( 50 ); 308 aLI.SetDotCount( 2 ); 309 aLI.SetDotLen( 25 ); 310 aLI.SetDistance( 15 ); 311 Point aLIPoints[] = { Point( 4000, 10000 ), 312 Point( 8000, 12000 ), 313 Point( 3000, 19000 ) }; 314 Polygon aLIPoly( 3, aLIPoints ); 315 aWriter.SetLineColor( Color( COL_BLUE ) ); 316 aWriter.SetFillColor(); 317 aWriter.DrawPolyLine( aLIPoly, aLI ); 318 319 aLI.SetDashCount( 4 ); 320 aLIPoly.Move( 1000, 1000 ); 321 aWriter.DrawPolyLine( aLIPoly, aLI ); 322 323 aWriter.NewPage( 595, 842 ); 324 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); 325 Wallpaper aWall( aTransMask ); 326 aWall.SetStyle( WALLPAPER_TILE ); 327 aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall ); 328 329 aWriter.Push( PUSH_ALL ); 330 aWriter.BeginPattern(Rectangle(Point(0,0),Size(2000,1000))); 331 aWriter.SetFillColor( Color( COL_RED ) ); 332 aWriter.SetLineColor( Color( COL_LIGHTBLUE ) ); 333 Point aFillPoints[] = { Point( 1000, 0 ), 334 Point( 0, 1000 ), 335 Point( 2000, 1000 ) }; 336 aWriter.DrawPolygon( Polygon( 3, aFillPoints ) ); 337 aWriter.DrawBitmap( Point( 200, 200 ), Size( 1600, 600 ), aTransMask ); 338 aWriter.DrawText( Rectangle( Point( 200, 200 ), Size( 1600, 600 ) ), String( RTL_CONSTASCII_USTRINGPARAM( "Pattern" ) ) ); 339 sal_Int32 nPattern = aWriter.EndPattern( SvtGraphicFill::Transform() ); 340 aWriter.Pop(); 341 Rectangle aPolyRect( Point( 3800, 11200 ), Size( 10200, 6300 ) ); 342 aWriter.DrawPolyPolygon( PolyPolygon( Polygon( aPolyRect ) ), nPattern, true ); 343 aWriter.SetFillColor(); 344 aWriter.SetLineColor( Color( COL_LIGHTBLUE ) ); 345 aWriter.DrawRect( aPolyRect ); 346 347 aWriter.NewPage( 595, 842 ); 348 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); 349 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) ); 350 aWriter.SetTextColor( Color( COL_BLACK ) ); 351 aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) ); 352 aWriter.DrawRect( aRect ); 353 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "www.heise.de" ) ) ); 354 sal_Int32 nURILink = aWriter.CreateLink( aRect ); 355 aWriter.SetLinkURL( nURILink, OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) ) ); 356 357 aWriter.SetLinkDest( nFirstLink, nFirstDest ); 358 aWriter.SetLinkDest( nSecondLink, nSecondDest ); 359 360 // include a button 361 PDFWriter::PushButtonWidget aBtn; 362 aBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testButton" ) ); 363 aBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test button" ) ); 364 aBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "hit me" ) ); 365 aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) ); 366 aBtn.Border = aBtn.Background = true; 367 aWriter.CreateControl( aBtn ); 368 369 // include a uri button 370 PDFWriter::PushButtonWidget aUriBtn; 371 aUriBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "wwwButton" ) ); 372 aUriBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A URI button" ) ); 373 aUriBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to www" ) ); 374 aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) ); 375 aUriBtn.Border = aUriBtn.Background = true; 376 aUriBtn.URL = OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) ); 377 aWriter.CreateControl( aUriBtn ); 378 379 // include a dest button 380 PDFWriter::PushButtonWidget aDstBtn; 381 aDstBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "destButton" ) ); 382 aDstBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A Dest button" ) ); 383 aDstBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to paragraph" ) ); 384 aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) ); 385 aDstBtn.Border = aDstBtn.Background = true; 386 aDstBtn.Dest = nFirstDest; 387 aWriter.CreateControl( aDstBtn ); 388 389 PDFWriter::CheckBoxWidget aCBox; 390 aCBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox" ) ); 391 aCBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test check box" ) ); 392 aCBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me" ) ); 393 aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) ); 394 aCBox.Checked = true; 395 aCBox.Border = aCBox.Background = false; 396 aWriter.CreateControl( aCBox ); 397 398 PDFWriter::CheckBoxWidget aCBox2; 399 aCBox2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox2" ) ); 400 aCBox2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "Another test check box" ) ); 401 aCBox2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me right" ) ); 402 aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) ); 403 aCBox2.Checked = true; 404 aCBox2.Border = aCBox2.Background = false; 405 aCBox2.ButtonIsLeft = false; 406 aWriter.CreateControl( aCBox2 ); 407 408 PDFWriter::RadioButtonWidget aRB1; 409 aRB1.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_1" ) ); 410 aRB1.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 1" ) ); 411 aRB1.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Despair" ) ); 412 aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) ); 413 aRB1.Selected = true; 414 aRB1.RadioGroup = 1; 415 aRB1.Border = aRB1.Background = true; 416 aRB1.ButtonIsLeft = false; 417 aRB1.BorderColor = Color( COL_LIGHTGREEN ); 418 aRB1.BackgroundColor = Color( COL_LIGHTBLUE ); 419 aRB1.TextColor = Color( COL_LIGHTRED ); 420 aRB1.TextFont = Font( String( RTL_CONSTASCII_USTRINGPARAM( "Courier" ) ), Size( 0, 800 ) ); 421 aWriter.CreateControl( aRB1 ); 422 423 PDFWriter::RadioButtonWidget aRB2; 424 aRB2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb2_1" ) ); 425 aRB2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 2 button 1" ) ); 426 aRB2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Joy" ) ); 427 aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) ); 428 aRB2.Selected = true; 429 aRB2.RadioGroup = 2; 430 aWriter.CreateControl( aRB2 ); 431 432 PDFWriter::RadioButtonWidget aRB3; 433 aRB3.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_2" ) ); 434 aRB3.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 2" ) ); 435 aRB3.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Desperation" ) ); 436 aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) ); 437 aRB3.Selected = true; 438 aRB3.RadioGroup = 1; 439 aWriter.CreateControl( aRB3 ); 440 441 PDFWriter::EditWidget aEditBox; 442 aEditBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testEdit" ) ); 443 aEditBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test edit field" ) ); 444 aEditBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "A little test text" ) ); 445 aEditBox.TextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER; 446 aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) ); 447 aEditBox.MaxLen = 100; 448 aEditBox.Border = aEditBox.Background = true; 449 aEditBox.BorderColor = Color( COL_BLACK ); 450 aWriter.CreateControl( aEditBox ); 451 452 // normal list box 453 PDFWriter::ListBoxWidget aLstBox; 454 aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testListBox" ) ); 455 aLstBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) ); 456 aLstBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "select me" ) ); 457 aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) ); 458 aLstBox.Sort = true; 459 aLstBox.MultiSelect = true; 460 aLstBox.Border = aLstBox.Background = true; 461 aLstBox.BorderColor = Color( COL_BLACK ); 462 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) ) ); 463 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Two" ) ) ); 464 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Three" ) ) ); 465 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Four" ) ) ); 466 aLstBox.SelectedEntries.push_back( 1 ); 467 aLstBox.SelectedEntries.push_back( 2 ); 468 aWriter.CreateControl( aLstBox ); 469 470 // dropdown list box 471 aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testDropDownListBox" ) ); 472 aLstBox.DropDown = true; 473 aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) ); 474 aWriter.CreateControl( aLstBox ); 475 476 // combo box 477 PDFWriter::ComboBoxWidget aComboBox; 478 aComboBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testComboBox" ) ); 479 aComboBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "test a combobox" ) ); 480 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Larry" ) ) ); 481 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Curly" ) ) ); 482 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Moe" ) ) ); 483 aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) ); 484 aWriter.CreateControl( aComboBox ); 485 486 // test outlines 487 sal_Int32 nPage1OL = aWriter.CreateOutlineItem(); 488 aWriter.SetOutlineItemText( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 1" ) ) ); 489 aWriter.SetOutlineItemDest( nPage1OL, nSecondDest ); 490 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2" ) ), nSecondDest ); 491 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 revisited" ) ), nSecondDest ); 492 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 again" ) ), nSecondDest ); 493 sal_Int32 nPage2OL = aWriter.CreateOutlineItem(); 494 aWriter.SetOutlineItemText( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 2" ) ) ); 495 aWriter.CreateOutlineItem( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 1" ) ), nFirstDest ); 496 497 aWriter.EndStructureElement(); // close document 498 aWriter.Emit(); 499 } 500 #endif 501 502 static const sal_Int32 nLog10Divisor = 1; 503 static const double fDivisor = 10.0; 504 505 static inline double pixelToPoint( sal_Int32 px ) { return double(px)/fDivisor; } 506 static inline double pixelToPoint( double px ) { return px/fDivisor; } 507 static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); } 508 509 const sal_uInt8 PDFWriterImpl::s_nPadString[32] = 510 { 511 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, 512 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A 513 }; 514 515 static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer ) 516 { 517 static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', 518 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 519 rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] ); 520 rBuffer.append( pHexDigits[ nInt & 15 ] ); 521 } 522 523 static void appendName( const OUString& rStr, OStringBuffer& rBuffer ) 524 { 525 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1 526 // I guess than when reading the #xx sequence it will count for a single character. 527 OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) ); 528 const sal_Char* pStr = aStr.getStr(); 529 int nLen = aStr.getLength(); 530 for( int i = 0; i < nLen; i++ ) 531 { 532 /* #i16920# PDF recommendation: output UTF8, any byte 533 * outside the interval [33(=ASCII'!');126(=ASCII'~')] 534 * should be escaped hexadecimal 535 * for the sake of ghostscript which also reads PDF 536 * but has a narrower acceptance rate we only pass 537 * alphanumerics and '-' literally. 538 */ 539 if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) || 540 (pStr[i] >= 'a' && pStr[i] <= 'z' ) || 541 (pStr[i] >= '0' && pStr[i] <= '9' ) || 542 pStr[i] == '-' ) 543 { 544 rBuffer.append( pStr[i] ); 545 } 546 else 547 { 548 rBuffer.append( '#' ); 549 appendHex( (sal_Int8)pStr[i], rBuffer ); 550 } 551 } 552 } 553 554 static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer ) 555 { 556 //FIXME i59651 see above 557 while( pStr && *pStr ) 558 { 559 if( (*pStr >= 'A' && *pStr <= 'Z' ) || 560 (*pStr >= 'a' && *pStr <= 'z' ) || 561 (*pStr >= '0' && *pStr <= '9' ) || 562 *pStr == '-' ) 563 { 564 rBuffer.append( *pStr ); 565 } 566 else 567 { 568 rBuffer.append( '#' ); 569 appendHex( (sal_Int8)*pStr, rBuffer ); 570 } 571 pStr++; 572 } 573 } 574 575 //used only to emit encoded passwords 576 static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer ) 577 { 578 while( nLength ) 579 { 580 switch( *pStr ) 581 { 582 case '\n' : 583 rBuffer.append( "\\n" ); 584 break; 585 case '\r' : 586 rBuffer.append( "\\r" ); 587 break; 588 case '\t' : 589 rBuffer.append( "\\t" ); 590 break; 591 case '\b' : 592 rBuffer.append( "\\b" ); 593 break; 594 case '\f' : 595 rBuffer.append( "\\f" ); 596 break; 597 case '(' : 598 case ')' : 599 case '\\' : 600 rBuffer.append( "\\" ); 601 rBuffer.append( (sal_Char) *pStr ); 602 break; 603 default: 604 rBuffer.append( (sal_Char) *pStr ); 605 break; 606 } 607 pStr++; 608 nLength--; 609 } 610 } 611 612 /**--->i56629 613 * Convert a string before using it. 614 * 615 * This string conversion function is needed because the destination name 616 * in a PDF file seen through an Internet browser should be 617 * specially crafted, in order to be used directly by the browser. 618 * In this way the fragment part of a hyperlink to a PDF file (e.g. something 619 * as 'test1/test2/a-file.pdf#thefragment) will be (hopefully) interpreted by the 620 * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called 621 * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf 622 * and go to named destination thefragment using default zoom'. 623 * The conversion is needed because in case of a fragment in the form: Slide%201 624 * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201 625 * using this conversion, in both the generated named destinations, fragment and GoToR 626 * destination. 627 * 628 * The names for destinations are name objects and so they don't need to be encrypted 629 * even though they expose the content of PDF file (e.g. guessing the PDF content from the 630 * destination name). 631 * 632 * Fhurter limitation: it is advisable to use standard ASCII characters for 633 * OOo bookmarks. 634 */ 635 static void appendDestinationName( const rtl::OUString& rString, OStringBuffer& rBuffer ) 636 { 637 const sal_Unicode* pStr = rString.getStr(); 638 sal_Int32 nLen = rString.getLength(); 639 for( int i = 0; i < nLen; i++ ) 640 { 641 sal_Unicode aChar = pStr[i]; 642 if( (aChar >= '0' && aChar <= '9' ) || 643 (aChar >= 'a' && aChar <= 'z' ) || 644 (aChar >= 'A' && aChar <= 'Z' ) || 645 aChar == '-' ) 646 { 647 rBuffer.append((sal_Char)aChar); 648 } 649 else 650 { 651 sal_Int8 aValueHigh = sal_Int8(aChar >> 8); 652 if(aValueHigh > 0) 653 appendHex( aValueHigh, rBuffer ); 654 appendHex( (sal_Int8)(aChar & 255 ), rBuffer ); 655 } 656 } 657 } 658 //<--- i56629 659 660 static void appendUnicodeTextString( const rtl::OUString& rString, OStringBuffer& rBuffer ) 661 { 662 rBuffer.append( "FEFF" ); 663 const sal_Unicode* pStr = rString.getStr(); 664 sal_Int32 nLen = rString.getLength(); 665 for( int i = 0; i < nLen; i++ ) 666 { 667 sal_Unicode aChar = pStr[i]; 668 appendHex( (sal_Int8)(aChar >> 8), rBuffer ); 669 appendHex( (sal_Int8)(aChar & 255 ), rBuffer ); 670 } 671 } 672 673 void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl ) 674 { 675 /* #i80258# previously we use appendName here 676 however we need a slightly different coding scheme than the normal 677 name encoding for field names 678 */ 679 const OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ? i_rControl.Name : i_rControl.Text; 680 OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) ); 681 const sal_Char* pStr = aStr.getStr(); 682 int nLen = aStr.getLength(); 683 684 OStringBuffer aBuffer( rName.getLength()+64 ); 685 for( int i = 0; i < nLen; i++ ) 686 { 687 /* #i16920# PDF recommendation: output UTF8, any byte 688 * outside the interval [32(=ASCII' ');126(=ASCII'~')] 689 * should be escaped hexadecimal 690 */ 691 if( (pStr[i] >= 32 && pStr[i] <= 126 ) ) 692 aBuffer.append( pStr[i] ); 693 else 694 { 695 aBuffer.append( '#' ); 696 appendHex( (sal_Int8)pStr[i], aBuffer ); 697 } 698 } 699 700 OString aFullName( aBuffer.makeStringAndClear() ); 701 702 /* #i82785# create hierarchical fields down to the for each dot in i_rName */ 703 sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0; 704 OString aPartialName; 705 OString aDomain; 706 do 707 { 708 nLastTokenIndex = nTokenIndex; 709 aPartialName = aFullName.getToken( 0, '.', nTokenIndex ); 710 if( nTokenIndex != -1 ) 711 { 712 // find or create a hierarchical field 713 // first find the fully qualified name up to this field 714 aDomain = aFullName.copy( 0, nTokenIndex-1 ); 715 std::hash_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain ); 716 if( it == m_aFieldNameMap.end() ) 717 { 718 // create new hierarchy field 719 sal_Int32 nNewWidget = m_aWidgets.size(); 720 m_aWidgets.push_back( PDFWidget() ); 721 m_aWidgets[nNewWidget].m_nObject = createObject(); 722 m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy; 723 m_aWidgets[nNewWidget].m_aName = aPartialName; 724 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject; 725 m_aFieldNameMap[aDomain] = nNewWidget; 726 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject; 727 if( nLastTokenIndex > 0 ) 728 { 729 // this field is not a root field and 730 // needs to be inserted to its parent 731 OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) ); 732 it = m_aFieldNameMap.find( aParentDomain ); 733 OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" ); 734 if( it != m_aFieldNameMap.end() ) 735 { 736 OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" ); 737 if( it->second < sal_Int32(m_aWidgets.size()) ) 738 { 739 PDFWidget& rParentField( m_aWidgets[it->second] ); 740 rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject ); 741 rParentField.m_aKidsIndex.push_back( nNewWidget ); 742 m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject; 743 } 744 } 745 } 746 } 747 else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy ) 748 { 749 // this is invalid, someone tries to have a terminal field as parent 750 // example: a button with the name foo.bar exists and 751 // another button is named foo.bar.no 752 // workaround: put the second terminal field as much up in the hierarchy as 753 // necessary to have a non-terminal field as parent (or none at all) 754 // since it->second already is terminal, we just need to use its parent 755 aDomain = OString(); 756 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 ); 757 if( nLastTokenIndex > 0 ) 758 { 759 aDomain = aFullName.copy( 0, nLastTokenIndex-1 ); 760 OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() ); 761 aBuf.append( aDomain ); 762 aBuf.append( '.' ); 763 aBuf.append( aPartialName ); 764 aFullName = aBuf.makeStringAndClear(); 765 } 766 else 767 aFullName = aPartialName; 768 break; 769 } 770 } 771 } while( nTokenIndex != -1 ); 772 773 // insert widget into its hierarchy field 774 if( aDomain.getLength() ) 775 { 776 std::hash_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain ); 777 if( it != m_aFieldNameMap.end() ) 778 { 779 OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" ); 780 if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) ) 781 { 782 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject; 783 m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject); 784 m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex ); 785 } 786 } 787 } 788 789 if( aPartialName.getLength() == 0 ) 790 { 791 // how funny, an empty field name 792 if( i_rControl.getType() == PDFWriter::RadioButton ) 793 { 794 aPartialName = "RadioGroup"; 795 aPartialName += OString::valueOf( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup ); 796 } 797 else 798 aPartialName = OString( "Widget" ); 799 } 800 801 if( ! m_aContext.AllowDuplicateFieldNames ) 802 { 803 std::hash_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aFullName ); 804 805 if( it != m_aFieldNameMap.end() ) // not unique 806 { 807 std::hash_map< OString, sal_Int32, OStringHash >::const_iterator check_it; 808 OString aTry; 809 sal_Int32 nTry = 2; 810 do 811 { 812 OStringBuffer aUnique( aFullName.getLength() + 16 ); 813 aUnique.append( aFullName ); 814 aUnique.append( '_' ); 815 aUnique.append( nTry++ ); 816 aTry = aUnique.makeStringAndClear(); 817 check_it = m_aFieldNameMap.find( aTry ); 818 } while( check_it != m_aFieldNameMap.end() ); 819 aFullName = aTry; 820 m_aFieldNameMap[ aFullName ] = i_nWidgetIndex; 821 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 ); 822 } 823 else 824 m_aFieldNameMap[ aFullName ] = i_nWidgetIndex; 825 } 826 827 // finally 828 m_aWidgets[i_nWidgetIndex].m_aName = aPartialName; 829 } 830 831 static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor ) 832 { 833 if( nValue < 0 ) 834 { 835 rBuffer.append( '-' ); 836 nValue = -nValue; 837 } 838 sal_Int32 nFactor = 1, nDiv = nPrecision; 839 while( nDiv-- ) 840 nFactor *= 10; 841 842 sal_Int32 nInt = nValue / nFactor; 843 rBuffer.append( nInt ); 844 if( nFactor > 1 ) 845 { 846 sal_Int32 nDecimal = nValue % nFactor; 847 if( nDecimal ) 848 { 849 rBuffer.append( '.' ); 850 // omit trailing zeros 851 while( (nDecimal % 10) == 0 ) 852 nDecimal /= 10; 853 rBuffer.append( nDecimal ); 854 } 855 } 856 } 857 858 859 // appends a double. PDF does not accept exponential format, only fixed point 860 static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 ) 861 { 862 bool bNeg = false; 863 if( fValue < 0.0 ) 864 { 865 bNeg = true; 866 fValue=-fValue; 867 } 868 869 sal_Int64 nInt = (sal_Int64)fValue; 870 fValue -= (double)nInt; 871 // optimizing hardware may lead to a value of 1.0 after the subtraction 872 if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision ) 873 { 874 nInt++; 875 fValue = 0.0; 876 } 877 sal_Int64 nFrac = 0; 878 if( fValue ) 879 { 880 fValue *= pow( 10.0, (double)nPrecision ); 881 nFrac = (sal_Int64)fValue; 882 } 883 if( bNeg && ( nInt || nFrac ) ) 884 rBuffer.append( '-' ); 885 rBuffer.append( nInt ); 886 if( nFrac ) 887 { 888 int i; 889 rBuffer.append( '.' ); 890 sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5); 891 for ( i = 0; ( i < nPrecision ) && nFrac; i++ ) 892 { 893 sal_Int64 nNumb = nFrac / nBound; 894 nFrac -= nNumb * nBound; 895 rBuffer.append( nNumb ); 896 nBound /= 10; 897 } 898 } 899 } 900 901 902 static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey = false ) 903 { 904 905 if( rColor != Color( COL_TRANSPARENT ) ) 906 { 907 if( bConvertToGrey ) 908 { 909 sal_uInt8 cByte = rColor.GetLuminance(); 910 appendDouble( (double)cByte / 255.0, rBuffer ); 911 } 912 else 913 { 914 appendDouble( (double)rColor.GetRed() / 255.0, rBuffer ); 915 rBuffer.append( ' ' ); 916 appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer ); 917 rBuffer.append( ' ' ); 918 appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer ); 919 } 920 } 921 } 922 923 void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer ) 924 { 925 if( rColor != Color( COL_TRANSPARENT ) ) 926 { 927 bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale; 928 appendColor( rColor, rBuffer, bGrey ); 929 rBuffer.append( bGrey ? " G" : " RG" ); 930 } 931 } 932 933 void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer ) 934 { 935 if( rColor != Color( COL_TRANSPARENT ) ) 936 { 937 bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale; 938 appendColor( rColor, rBuffer, bGrey ); 939 rBuffer.append( bGrey ? " g" : " rg" ); 940 } 941 } 942 943 // matrix helper class 944 // TODO: use basegfx matrix class instead or derive from it 945 namespace vcl // TODO: use anonymous namespace to keep this class local 946 { 947 /* for sparse matrices of the form (2D linear transformations) 948 * f[0] f[1] 0 949 * f[2] f[3] 0 950 * f[4] f[5] 1 951 */ 952 class Matrix3 953 { 954 double f[6]; 955 956 void set( double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; } 957 public: 958 Matrix3(); 959 ~Matrix3() {} 960 961 void skew( double alpha, double beta ); 962 void scale( double sx, double sy ); 963 void rotate( double angle ); 964 void translate( double tx, double ty ); 965 bool invert(); 966 967 void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL ); 968 969 Point transform( const Point& rPoint ) const; 970 }; 971 } 972 973 Matrix3::Matrix3() 974 { 975 // initialize to unity 976 f[0] = 1.0; 977 f[1] = 0.0; 978 f[2] = 0.0; 979 f[3] = 1.0; 980 f[4] = 0.0; 981 f[5] = 0.0; 982 } 983 984 Point Matrix3::transform( const Point& rOrig ) const 985 { 986 double x = (double)rOrig.X(), y = (double)rOrig.Y(); 987 return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) ); 988 } 989 990 void Matrix3::skew( double alpha, double beta ) 991 { 992 double fn[6]; 993 double tb = tan( beta ); 994 fn[0] = f[0] + f[2]*tb; 995 fn[1] = f[1]; 996 fn[2] = f[2] + f[3]*tb; 997 fn[3] = f[3]; 998 fn[4] = f[4] + f[5]*tb; 999 fn[5] = f[5]; 1000 if( alpha != 0.0 ) 1001 { 1002 double ta = tan( alpha ); 1003 fn[1] += f[0]*ta; 1004 fn[3] += f[2]*ta; 1005 fn[5] += f[4]*ta; 1006 } 1007 set( fn ); 1008 } 1009 1010 void Matrix3::scale( double sx, double sy ) 1011 { 1012 double fn[6]; 1013 fn[0] = sx*f[0]; 1014 fn[1] = sy*f[1]; 1015 fn[2] = sx*f[2]; 1016 fn[3] = sy*f[3]; 1017 fn[4] = sx*f[4]; 1018 fn[5] = sy*f[5]; 1019 set( fn ); 1020 } 1021 1022 void Matrix3::rotate( double angle ) 1023 { 1024 double fn[6]; 1025 double fSin = sin(angle); 1026 double fCos = cos(angle); 1027 fn[0] = f[0]*fCos - f[1]*fSin; 1028 fn[1] = f[0]*fSin + f[1]*fCos; 1029 fn[2] = f[2]*fCos - f[3]*fSin; 1030 fn[3] = f[2]*fSin + f[3]*fCos; 1031 fn[4] = f[4]*fCos - f[5]*fSin; 1032 fn[5] = f[4]*fSin + f[5]*fCos; 1033 set( fn ); 1034 } 1035 1036 void Matrix3::translate( double tx, double ty ) 1037 { 1038 f[4] += tx; 1039 f[5] += ty; 1040 } 1041 1042 bool Matrix3::invert() 1043 { 1044 // short circuit trivial cases 1045 if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 ) 1046 { 1047 f[4] = -f[4]; 1048 f[5] = -f[5]; 1049 return true; 1050 } 1051 1052 // check determinant 1053 const double fDet = f[0]*f[3]-f[1]*f[2]; 1054 if( fDet == 0.0 ) 1055 return false; 1056 1057 // invert the matrix 1058 double fn[6]; 1059 fn[0] = +f[3] / fDet; 1060 fn[1] = -f[1] / fDet; 1061 fn[2] = -f[2] / fDet; 1062 fn[3] = +f[0] / fDet; 1063 1064 // apply inversion to translation 1065 fn[4] = -(f[4]*fn[0] + f[5]*fn[2]); 1066 fn[5] = -(f[4]*fn[1] + f[5]*fn[3]); 1067 1068 set( fn ); 1069 return true; 1070 } 1071 1072 void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack ) 1073 { 1074 appendDouble( f[0], rBuffer ); 1075 rBuffer.append( ' ' ); 1076 appendDouble( f[1], rBuffer ); 1077 rBuffer.append( ' ' ); 1078 appendDouble( f[2], rBuffer ); 1079 rBuffer.append( ' ' ); 1080 appendDouble( f[3], rBuffer ); 1081 rBuffer.append( ' ' ); 1082 rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack ); 1083 } 1084 1085 static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList ) 1086 { 1087 if( rList.empty() ) 1088 return; 1089 rBuf.append( '/' ); 1090 rBuf.append( pPrefix ); 1091 rBuf.append( "<<" ); 1092 int ni = 0; 1093 for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it ) 1094 { 1095 if( it->first.getLength() && it->second > 0 ) 1096 { 1097 rBuf.append( '/' ); 1098 rBuf.append( it->first ); 1099 rBuf.append( ' ' ); 1100 rBuf.append( it->second ); 1101 rBuf.append( " 0 R" ); 1102 if( ((++ni) & 7) == 0 ) 1103 rBuf.append( '\n' ); 1104 } 1105 } 1106 rBuf.append( ">>\n" ); 1107 } 1108 1109 void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject ) 1110 { 1111 rBuf.append( "<</Font " ); 1112 rBuf.append( nFontDictObject ); 1113 rBuf.append( " 0 R\n" ); 1114 appendResourceMap( rBuf, "XObject", m_aXObjects ); 1115 appendResourceMap( rBuf, "ExtGState", m_aExtGStates ); 1116 appendResourceMap( rBuf, "Shading", m_aShadings ); 1117 appendResourceMap( rBuf, "Pattern", m_aPatterns ); 1118 rBuf.append( "/ProcSet[/PDF/Text" ); 1119 if( !m_aXObjects.empty() ) 1120 rBuf.append( "/ImageC/ImageI/ImageB" ); 1121 rBuf.append( "]\n>>\n" ); 1122 }; 1123 1124 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation ) 1125 : 1126 m_pWriter( pWriter ), 1127 m_nPageWidth( nPageWidth ), 1128 m_nPageHeight( nPageHeight ), 1129 m_eOrientation( eOrientation ), 1130 m_nPageObject( 0 ), // invalid object number 1131 m_nPageIndex( -1 ), // invalid index 1132 m_nStreamLengthObject( 0 ), 1133 m_nBeginStreamPos( 0 ), 1134 m_eTransition( PDFWriter::Regular ), 1135 m_nTransTime( 0 ), 1136 m_nDuration( 0 ), 1137 m_bHasWidgets( false ) 1138 { 1139 // object ref must be only ever updated in emit() 1140 m_nPageObject = m_pWriter->createObject(); 1141 } 1142 1143 PDFWriterImpl::PDFPage::~PDFPage() 1144 { 1145 } 1146 1147 void PDFWriterImpl::PDFPage::beginStream() 1148 { 1149 #if OSL_DEBUG_LEVEL > 1 1150 { 1151 OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" ); 1152 m_pWriter->emitComment( aLine.getStr() ); 1153 } 1154 #endif 1155 m_aStreamObjects.push_back(m_pWriter->createObject()); 1156 if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) ) 1157 return; 1158 1159 m_nStreamLengthObject = m_pWriter->createObject(); 1160 // write content stream header 1161 OStringBuffer aLine; 1162 aLine.append( m_aStreamObjects.back() ); 1163 aLine.append( " 0 obj\n<</Length " ); 1164 aLine.append( m_nStreamLengthObject ); 1165 aLine.append( " 0 R" ); 1166 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION ) 1167 aLine.append( "/Filter/FlateDecode" ); 1168 #endif 1169 aLine.append( ">>\nstream\n" ); 1170 if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) ) 1171 return; 1172 if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &m_nBeginStreamPos ) ) 1173 { 1174 osl_closeFile( m_pWriter->m_aFile ); 1175 m_pWriter->m_bOpen = false; 1176 } 1177 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION ) 1178 m_pWriter->beginCompression(); 1179 #endif 1180 m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() ); 1181 } 1182 1183 void PDFWriterImpl::PDFPage::endStream() 1184 { 1185 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION ) 1186 m_pWriter->endCompression(); 1187 #endif 1188 sal_uInt64 nEndStreamPos; 1189 if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &nEndStreamPos ) ) 1190 { 1191 osl_closeFile( m_pWriter->m_aFile ); 1192 m_pWriter->m_bOpen = false; 1193 return; 1194 } 1195 m_pWriter->disableStreamEncryption(); 1196 if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) 1197 return; 1198 // emit stream length object 1199 if( ! m_pWriter->updateObject( m_nStreamLengthObject ) ) 1200 return; 1201 OStringBuffer aLine; 1202 aLine.append( m_nStreamLengthObject ); 1203 aLine.append( " 0 obj\n" ); 1204 aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) ); 1205 aLine.append( "\nendobj\n\n" ); 1206 m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ); 1207 } 1208 1209 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject ) 1210 { 1211 // emit page object 1212 if( ! m_pWriter->updateObject( m_nPageObject ) ) 1213 return false; 1214 OStringBuffer aLine; 1215 1216 aLine.append( m_nPageObject ); 1217 aLine.append( " 0 obj\n" 1218 "<</Type/Page/Parent " ); 1219 aLine.append( nParentObject ); 1220 aLine.append( " 0 R" ); 1221 aLine.append( "/Resources " ); 1222 aLine.append( m_pWriter->getResourceDictObj() ); 1223 aLine.append( " 0 R" ); 1224 if( m_nPageWidth && m_nPageHeight ) 1225 { 1226 aLine.append( "/MediaBox[0 0 " ); 1227 aLine.append( m_nPageWidth ); 1228 aLine.append( ' ' ); 1229 aLine.append( m_nPageHeight ); 1230 aLine.append( "]" ); 1231 } 1232 switch( m_eOrientation ) 1233 { 1234 case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break; 1235 case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break; 1236 case PDFWriter::Portrait: aLine.append( "/Rotate 0\n" );break; 1237 1238 case PDFWriter::Inherit: 1239 default: 1240 break; 1241 } 1242 int nAnnots = m_aAnnotations.size(); 1243 if( nAnnots > 0 ) 1244 { 1245 aLine.append( "/Annots[\n" ); 1246 for( int i = 0; i < nAnnots; i++ ) 1247 { 1248 aLine.append( m_aAnnotations[i] ); 1249 aLine.append( " 0 R" ); 1250 aLine.append( ((i+1)%15) ? " " : "\n" ); 1251 } 1252 aLine.append( "]\n" ); 1253 } 1254 #if 0 1255 // FIXME: implement tab order as Structure Tree 1256 if( m_bHasWidgets && m_pWriter->getVersion() >= PDFWriter::PDF_1_5 ) 1257 aLine.append( " /Tabs /S\n" ); 1258 #endif 1259 if( m_aMCIDParents.size() > 0 ) 1260 { 1261 OStringBuffer aStructParents( 1024 ); 1262 aStructParents.append( "[ " ); 1263 int nParents = m_aMCIDParents.size(); 1264 for( int i = 0; i < nParents; i++ ) 1265 { 1266 aStructParents.append( m_aMCIDParents[i] ); 1267 aStructParents.append( " 0 R" ); 1268 aStructParents.append( ((i%10) == 9) ? "\n" : " " ); 1269 } 1270 aStructParents.append( "]" ); 1271 m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() ); 1272 1273 aLine.append( "/StructParents " ); 1274 aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) ); 1275 aLine.append( "\n" ); 1276 } 1277 if( m_nDuration > 0 ) 1278 { 1279 aLine.append( "/Dur " ); 1280 aLine.append( (sal_Int32)m_nDuration ); 1281 aLine.append( "\n" ); 1282 } 1283 if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 ) 1284 { 1285 // transition duration 1286 aLine.append( "/Trans<</D " ); 1287 appendDouble( (double)m_nTransTime/1000.0, aLine, 3 ); 1288 aLine.append( "\n" ); 1289 const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL; 1290 switch( m_eTransition ) 1291 { 1292 case PDFWriter::SplitHorizontalInward: 1293 pStyle = "Split"; pDm = "H"; pM = "I"; break; 1294 case PDFWriter::SplitHorizontalOutward: 1295 pStyle = "Split"; pDm = "H"; pM = "O"; break; 1296 case PDFWriter::SplitVerticalInward: 1297 pStyle = "Split"; pDm = "V"; pM = "I"; break; 1298 case PDFWriter::SplitVerticalOutward: 1299 pStyle = "Split"; pDm = "V"; pM = "O"; break; 1300 case PDFWriter::BlindsHorizontal: 1301 pStyle = "Blinds"; pDm = "H"; break; 1302 case PDFWriter::BlindsVertical: 1303 pStyle = "Blinds"; pDm = "V"; break; 1304 case PDFWriter::BoxInward: 1305 pStyle = "Box"; pM = "I"; break; 1306 case PDFWriter::BoxOutward: 1307 pStyle = "Box"; pM = "O"; break; 1308 case PDFWriter::WipeLeftToRight: 1309 pStyle = "Wipe"; pDi = "0"; break; 1310 case PDFWriter::WipeBottomToTop: 1311 pStyle = "Wipe"; pDi = "90"; break; 1312 case PDFWriter::WipeRightToLeft: 1313 pStyle = "Wipe"; pDi = "180"; break; 1314 case PDFWriter::WipeTopToBottom: 1315 pStyle = "Wipe"; pDi = "270"; break; 1316 case PDFWriter::Dissolve: 1317 pStyle = "Dissolve"; break; 1318 case PDFWriter::GlitterLeftToRight: 1319 pStyle = "Glitter"; pDi = "0"; break; 1320 case PDFWriter::GlitterTopToBottom: 1321 pStyle = "Glitter"; pDi = "270"; break; 1322 case PDFWriter::GlitterTopLeftToBottomRight: 1323 pStyle = "Glitter"; pDi = "315"; break; 1324 case PDFWriter::Regular: 1325 break; 1326 } 1327 // transition style 1328 if( pStyle ) 1329 { 1330 aLine.append( "/S/" ); 1331 aLine.append( pStyle ); 1332 aLine.append( "\n" ); 1333 } 1334 if( pDm ) 1335 { 1336 aLine.append( "/Dm/" ); 1337 aLine.append( pDm ); 1338 aLine.append( "\n" ); 1339 } 1340 if( pM ) 1341 { 1342 aLine.append( "/M/" ); 1343 aLine.append( pM ); 1344 aLine.append( "\n" ); 1345 } 1346 if( pDi ) 1347 { 1348 aLine.append( "/Di " ); 1349 aLine.append( pDi ); 1350 aLine.append( "\n" ); 1351 } 1352 aLine.append( ">>\n" ); 1353 } 1354 if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 ) 1355 { 1356 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" ); 1357 } 1358 aLine.append( "/Contents" ); 1359 unsigned int nStreamObjects = m_aStreamObjects.size(); 1360 if( nStreamObjects > 1 ) 1361 aLine.append( '[' ); 1362 for( unsigned int i = 0; i < m_aStreamObjects.size(); i++ ) 1363 { 1364 aLine.append( ' ' ); 1365 aLine.append( m_aStreamObjects[i] ); 1366 aLine.append( " 0 R" ); 1367 } 1368 if( nStreamObjects > 1 ) 1369 aLine.append( ']' ); 1370 aLine.append( ">>\nendobj\n\n" ); 1371 return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ); 1372 } 1373 1374 namespace vcl 1375 { 1376 template < class GEOMETRY > 1377 GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject ) 1378 { 1379 GEOMETRY aPoint; 1380 if ( MAP_PIXEL == _rSource.GetMapUnit() ) 1381 { 1382 aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest ); 1383 } 1384 else 1385 { 1386 aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest ); 1387 } 1388 return aPoint; 1389 } 1390 } 1391 1392 void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const 1393 { 1394 if( pOutPoint ) 1395 { 1396 Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1397 m_pWriter->m_aMapMode, 1398 m_pWriter->getReferenceDevice(), 1399 rPoint ) ); 1400 *pOutPoint = aPoint; 1401 } 1402 1403 Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1404 m_pWriter->m_aMapMode, 1405 m_pWriter->getReferenceDevice(), 1406 rPoint ) ); 1407 1408 sal_Int32 nValue = aPoint.X(); 1409 if( bNeg ) 1410 nValue = -nValue; 1411 1412 appendFixedInt( nValue, rBuffer ); 1413 1414 rBuffer.append( ' ' ); 1415 1416 nValue = pointToPixel(getHeight()) - aPoint.Y(); 1417 if( bNeg ) 1418 nValue = -nValue; 1419 1420 appendFixedInt( nValue, rBuffer ); 1421 } 1422 1423 void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const 1424 { 1425 double fValue = pixelToPoint(rPoint.getX()); 1426 1427 appendDouble( fValue, rBuffer, nLog10Divisor ); 1428 1429 rBuffer.append( ' ' ); 1430 1431 fValue = double(getHeight()) - pixelToPoint(rPoint.getY()); 1432 1433 appendDouble( fValue, rBuffer, nLog10Divisor ); 1434 } 1435 1436 void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const 1437 { 1438 appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer ); 1439 rBuffer.append( ' ' ); 1440 appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false ); 1441 rBuffer.append( ' ' ); 1442 appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true ); 1443 rBuffer.append( " re" ); 1444 } 1445 1446 void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const 1447 { 1448 Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1449 m_pWriter->m_aMapMode, 1450 m_pWriter->getReferenceDevice(), 1451 rRect.BottomLeft() + Point( 0, 1 ) 1452 ); 1453 Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1454 m_pWriter->m_aMapMode, 1455 m_pWriter->getReferenceDevice(), 1456 rRect.GetSize() ); 1457 rRect.Left() = aLL.X(); 1458 rRect.Right() = aLL.X() + aSize.Width(); 1459 rRect.Top() = pointToPixel(getHeight()) - aLL.Y(); 1460 rRect.Bottom() = rRect.Top() + aSize.Height(); 1461 } 1462 1463 void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const 1464 { 1465 sal_uInt16 nPoints = rPoly.GetSize(); 1466 /* 1467 * #108582# applications do weird things 1468 */ 1469 sal_uInt32 nBufLen = rBuffer.getLength(); 1470 if( nPoints > 0 ) 1471 { 1472 const sal_uInt8* pFlagArray = rPoly.GetConstFlagAry(); 1473 appendPoint( rPoly[0], rBuffer ); 1474 rBuffer.append( " m\n" ); 1475 for( sal_uInt16 i = 1; i < nPoints; i++ ) 1476 { 1477 if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 ) 1478 { 1479 // bezier 1480 DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" ); 1481 appendPoint( rPoly[i], rBuffer ); 1482 rBuffer.append( " " ); 1483 appendPoint( rPoly[i+1], rBuffer ); 1484 rBuffer.append( " " ); 1485 appendPoint( rPoly[i+2], rBuffer ); 1486 rBuffer.append( " c" ); 1487 i += 2; // add additionally consumed points 1488 } 1489 else 1490 { 1491 // line 1492 appendPoint( rPoly[i], rBuffer ); 1493 rBuffer.append( " l" ); 1494 } 1495 if( (rBuffer.getLength() - nBufLen) > 65 ) 1496 { 1497 rBuffer.append( "\n" ); 1498 nBufLen = rBuffer.getLength(); 1499 } 1500 else 1501 rBuffer.append( " " ); 1502 } 1503 if( bClose ) 1504 rBuffer.append( "h\n" ); 1505 } 1506 } 1507 1508 void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const 1509 { 1510 basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1511 m_pWriter->m_aMapMode, 1512 m_pWriter->getReferenceDevice(), 1513 rPoly ) ); 1514 1515 if( basegfx::tools::isRectangle( aPoly ) ) 1516 { 1517 basegfx::B2DRange aRange( aPoly.getB2DRange() ); 1518 basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() ); 1519 appendPixelPoint( aBL, rBuffer ); 1520 rBuffer.append( ' ' ); 1521 appendMappedLength( aRange.getWidth(), rBuffer, false, NULL, nLog10Divisor ); 1522 rBuffer.append( ' ' ); 1523 appendMappedLength( aRange.getHeight(), rBuffer, true, NULL, nLog10Divisor ); 1524 rBuffer.append( " re\n" ); 1525 return; 1526 } 1527 sal_uInt32 nPoints = aPoly.count(); 1528 if( nPoints > 0 ) 1529 { 1530 sal_uInt32 nBufLen = rBuffer.getLength(); 1531 basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) ); 1532 appendPixelPoint( aLastPoint, rBuffer ); 1533 rBuffer.append( " m\n" ); 1534 for( sal_uInt32 i = 1; i <= nPoints; i++ ) 1535 { 1536 if( i != nPoints || aPoly.isClosed() ) 1537 { 1538 sal_uInt32 nCurPoint = i % nPoints; 1539 sal_uInt32 nLastPoint = i-1; 1540 basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) ); 1541 if( aPoly.isNextControlPointUsed( nLastPoint ) && 1542 aPoly.isPrevControlPointUsed( nCurPoint ) ) 1543 { 1544 appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer ); 1545 rBuffer.append( ' ' ); 1546 appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer ); 1547 rBuffer.append( ' ' ); 1548 appendPixelPoint( aPoint, rBuffer ); 1549 rBuffer.append( " c" ); 1550 } 1551 else if( aPoly.isNextControlPointUsed( nLastPoint ) ) 1552 { 1553 appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer ); 1554 rBuffer.append( ' ' ); 1555 appendPixelPoint( aPoint, rBuffer ); 1556 rBuffer.append( " y" ); 1557 } 1558 else if( aPoly.isPrevControlPointUsed( nCurPoint ) ) 1559 { 1560 appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer ); 1561 rBuffer.append( ' ' ); 1562 appendPixelPoint( aPoint, rBuffer ); 1563 rBuffer.append( " v" ); 1564 } 1565 else 1566 { 1567 appendPixelPoint( aPoint, rBuffer ); 1568 rBuffer.append( " l" ); 1569 } 1570 if( (rBuffer.getLength() - nBufLen) > 65 ) 1571 { 1572 rBuffer.append( "\n" ); 1573 nBufLen = rBuffer.getLength(); 1574 } 1575 else 1576 rBuffer.append( " " ); 1577 } 1578 } 1579 if( bClose ) 1580 rBuffer.append( "h\n" ); 1581 } 1582 } 1583 1584 void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const 1585 { 1586 sal_uInt16 nPolygons = rPolyPoly.Count(); 1587 for( sal_uInt16 n = 0; n < nPolygons; n++ ) 1588 appendPolygon( rPolyPoly[n], rBuffer, bClose ); 1589 } 1590 1591 void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const 1592 { 1593 sal_uInt32 nPolygons = rPolyPoly.count(); 1594 for( sal_uInt32 n = 0; n < nPolygons; n++ ) 1595 appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer, bClose ); 1596 } 1597 1598 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const 1599 { 1600 sal_Int32 nValue = nLength; 1601 if ( nLength < 0 ) 1602 { 1603 rBuffer.append( '-' ); 1604 nValue = -nLength; 1605 } 1606 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1607 m_pWriter->m_aMapMode, 1608 m_pWriter->getReferenceDevice(), 1609 Size( nValue, nValue ) ) ); 1610 nValue = bVertical ? aSize.Height() : aSize.Width(); 1611 if( pOutLength ) 1612 *pOutLength = ((nLength < 0 ) ? -nValue : nValue); 1613 1614 appendFixedInt( nValue, rBuffer, 1 ); 1615 } 1616 1617 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength, sal_Int32 nPrecision ) const 1618 { 1619 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1620 m_pWriter->m_aMapMode, 1621 m_pWriter->getReferenceDevice(), 1622 Size( 1000, 1000 ) ) ); 1623 if( pOutLength ) 1624 *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0); 1625 fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0); 1626 appendDouble( fLength, rBuffer, nPrecision ); 1627 } 1628 1629 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const 1630 { 1631 bool bRet = true; 1632 if( rInfo.GetStyle() == LINE_DASH ) 1633 { 1634 rBuffer.append( "[ " ); 1635 if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case 1636 { 1637 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer ); 1638 rBuffer.append( ' ' ); 1639 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); 1640 rBuffer.append( ' ' ); 1641 } 1642 else 1643 { 1644 // check for implementation limits of dash array 1645 // in PDF reader apps (e.g. acroread) 1646 if( 2*(rInfo.GetDashCount() + rInfo.GetDotCount()) > 10 ) 1647 bRet = false; 1648 for( int n = 0; n < rInfo.GetDashCount(); n++ ) 1649 { 1650 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer ); 1651 rBuffer.append( ' ' ); 1652 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); 1653 rBuffer.append( ' ' ); 1654 } 1655 for( int m = 0; m < rInfo.GetDotCount(); m++ ) 1656 { 1657 appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer ); 1658 rBuffer.append( ' ' ); 1659 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); 1660 rBuffer.append( ' ' ); 1661 } 1662 } 1663 rBuffer.append( "] 0 d\n" ); 1664 } 1665 if( rInfo.GetWidth() > 1 ) 1666 { 1667 appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer ); 1668 rBuffer.append( " w\n" ); 1669 } 1670 else if( rInfo.GetWidth() == 0 ) 1671 { 1672 // "pixel" line 1673 appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->ImplGetDPIX()), rBuffer ); 1674 rBuffer.append( " w\n" ); 1675 } 1676 return bRet; 1677 } 1678 1679 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const 1680 { 1681 if( nWidth <= 0 ) 1682 return; 1683 if( nDelta < 1 ) 1684 nDelta = 1; 1685 1686 rBuffer.append( "0 " ); 1687 appendMappedLength( nY, rBuffer, true ); 1688 rBuffer.append( " m\n" ); 1689 for( sal_Int32 n = 0; n < nWidth; ) 1690 { 1691 n += nDelta; 1692 appendMappedLength( n, rBuffer, false ); 1693 rBuffer.append( ' ' ); 1694 appendMappedLength( nDelta+nY, rBuffer, true ); 1695 rBuffer.append( ' ' ); 1696 n += nDelta; 1697 appendMappedLength( n, rBuffer, false ); 1698 rBuffer.append( ' ' ); 1699 appendMappedLength( nY, rBuffer, true ); 1700 rBuffer.append( " v " ); 1701 if( n < nWidth ) 1702 { 1703 n += nDelta; 1704 appendMappedLength( n, rBuffer, false ); 1705 rBuffer.append( ' ' ); 1706 appendMappedLength( nY-nDelta, rBuffer, true ); 1707 rBuffer.append( ' ' ); 1708 n += nDelta; 1709 appendMappedLength( n, rBuffer, false ); 1710 rBuffer.append( ' ' ); 1711 appendMappedLength( nY, rBuffer, true ); 1712 rBuffer.append( " v\n" ); 1713 } 1714 } 1715 rBuffer.append( "S\n" ); 1716 } 1717 1718 /* 1719 * class PDFWriterImpl 1720 */ 1721 1722 PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, 1723 const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc, 1724 PDFWriter& i_rOuterFace) 1725 : 1726 m_pReferenceDevice( NULL ), 1727 m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ), 1728 m_nCurrentStructElement( 0 ), 1729 m_bEmitStructure( true ), 1730 m_bNewMCID( false ), 1731 m_nCurrentControl( -1 ), 1732 m_bEmbedStandardFonts( false ), 1733 m_nNextFID( 1 ), 1734 m_nInheritedPageWidth( 595 ), // default A4 1735 m_nInheritedPageHeight( 842 ), // default A4 1736 m_eInheritedOrientation( PDFWriter::Portrait ), 1737 m_nCurrentPage( -1 ), 1738 m_nResourceDict( -1 ), 1739 m_nFontDictObject( -1 ), 1740 m_pCodec( NULL ), 1741 m_aDocDigest( rtl_digest_createMD5() ), 1742 m_aCipher( (rtlCipher)NULL ), 1743 m_aDigest( NULL ), 1744 m_bEncryptThisStream( false ), 1745 m_pEncryptionBuffer( NULL ), 1746 m_nEncryptionBufferSize( 0 ), 1747 m_bIsPDF_A1( false ), 1748 m_rOuterFace( i_rOuterFace ) 1749 { 1750 #ifdef DO_TEST_PDF 1751 static bool bOnce = true; 1752 if( bOnce ) 1753 { 1754 bOnce = false; 1755 doTestCode(); 1756 } 1757 #endif 1758 m_aContext = rContext; 1759 m_aStructure.push_back( PDFStructureElement() ); 1760 m_aStructure[0].m_nOwnElement = 0; 1761 m_aStructure[0].m_nParentElement = 0; 1762 1763 Font aFont; 1764 aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) ); 1765 aFont.SetSize( Size( 0, 12 ) ); 1766 1767 GraphicsState aState; 1768 aState.m_aMapMode = m_aMapMode; 1769 aState.m_aFont = aFont; 1770 m_aGraphicsStack.push_front( aState ); 1771 1772 oslFileError aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ); 1773 if( aError != osl_File_E_None ) 1774 { 1775 if( aError == osl_File_E_EXIST ) 1776 { 1777 aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write ); 1778 if( aError == osl_File_E_None ) 1779 aError = osl_setFileSize( m_aFile, 0 ); 1780 } 1781 } 1782 if( aError != osl_File_E_None ) 1783 return; 1784 1785 m_bOpen = true; 1786 1787 // setup DocInfo 1788 setupDocInfo(); 1789 1790 /* prepare the cypher engine, can be done in CTOR, free in DTOR */ 1791 m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream ); 1792 m_aDigest = rtl_digest_createMD5(); 1793 1794 /* the size of the Codec default maximum */ 1795 checkEncryptionBufferSize( 0x4000 ); 1796 1797 if( xEnc.is() ) 1798 prepareEncryption( xEnc ); 1799 1800 if( m_aContext.Encryption.Encrypt() ) 1801 { 1802 // sanity check 1803 if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE || 1804 m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE || 1805 m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH 1806 ) 1807 { 1808 // the field lengths are invalid ? This was not setup by initEncryption. 1809 // do not encrypt after all 1810 m_aContext.Encryption.OValue.clear(); 1811 m_aContext.Encryption.UValue.clear(); 1812 OSL_ENSURE( 0, "encryption data failed sanity check, encryption disabled" ); 1813 } 1814 else // setup key lengths 1815 m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength ); 1816 } 1817 1818 // write header 1819 OStringBuffer aBuffer( 20 ); 1820 aBuffer.append( "%PDF-" ); 1821 switch( m_aContext.Version ) 1822 { 1823 case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break; 1824 case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break; 1825 case PDFWriter::PDF_A_1: 1826 default: 1827 case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break; 1828 case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break; 1829 } 1830 // append something binary as comment (suggested in PDF Reference) 1831 aBuffer.append( "\n%äüöß\n" ); 1832 if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) ) 1833 { 1834 osl_closeFile( m_aFile ); 1835 m_bOpen = false; 1836 return; 1837 } 1838 1839 // insert outline root 1840 m_aOutline.push_back( PDFOutlineEntry() ); 1841 1842 m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1); 1843 if( m_bIsPDF_A1 ) 1844 m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour 1845 1846 m_bEmbedStandardFonts = m_aContext.EmbedStandardFonts; 1847 } 1848 1849 PDFWriterImpl::~PDFWriterImpl() 1850 { 1851 if( m_aDocDigest ) 1852 rtl_digest_destroyMD5( m_aDocDigest ); 1853 delete static_cast<VirtualDevice*>(m_pReferenceDevice); 1854 1855 if( m_aCipher ) 1856 rtl_cipher_destroyARCFOUR( m_aCipher ); 1857 if( m_aDigest ) 1858 rtl_digest_destroyMD5( m_aDigest ); 1859 1860 rtl_freeMemory( m_pEncryptionBuffer ); 1861 } 1862 1863 void PDFWriterImpl::setupDocInfo() 1864 { 1865 std::vector< sal_uInt8 > aId; 1866 computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString ); 1867 if( m_aContext.Encryption.DocumentIdentifier.empty() ) 1868 m_aContext.Encryption.DocumentIdentifier = aId; 1869 } 1870 1871 void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier, 1872 const vcl::PDFWriter::PDFDocInfo& i_rDocInfo, 1873 rtl::OString& o_rCString1, 1874 rtl::OString& o_rCString2 1875 ) 1876 { 1877 o_rIdentifier.clear(); 1878 1879 //build the document id 1880 rtl::OString aInfoValuesOut; 1881 OStringBuffer aID( 1024 ); 1882 if( i_rDocInfo.Title.Len() ) 1883 appendUnicodeTextString( i_rDocInfo.Title, aID ); 1884 if( i_rDocInfo.Author.Len() ) 1885 appendUnicodeTextString( i_rDocInfo.Author, aID ); 1886 if( i_rDocInfo.Subject.Len() ) 1887 appendUnicodeTextString( i_rDocInfo.Subject, aID ); 1888 if( i_rDocInfo.Keywords.Len() ) 1889 appendUnicodeTextString( i_rDocInfo.Keywords, aID ); 1890 if( i_rDocInfo.Creator.Len() ) 1891 appendUnicodeTextString( i_rDocInfo.Creator, aID ); 1892 if( i_rDocInfo.Producer.Len() ) 1893 appendUnicodeTextString( i_rDocInfo.Producer, aID ); 1894 1895 TimeValue aTVal, aGMT; 1896 oslDateTime aDT; 1897 osl_getSystemTime( &aGMT ); 1898 osl_getLocalTimeFromSystemTime( &aGMT, &aTVal ); 1899 osl_getDateTimeFromTimeValue( &aTVal, &aDT ); 1900 rtl::OStringBuffer aCreationDateString(64), aCreationMetaDateString(64); 1901 aCreationDateString.append( "D:" ); 1902 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); 1903 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); 1904 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); 1905 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) ); 1906 aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); 1907 aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) ); 1908 aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); 1909 aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) ); 1910 aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); 1911 aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); 1912 aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); 1913 aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); 1914 aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); 1915 aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); 1916 1917 //--> i59651, we fill the Metadata date string as well, if PDF/A is requested 1918 // according to ISO 19005-1:2005 6.7.3 the date is corrected for 1919 // local time zone offset UTC only, whereas Acrobat 8 seems 1920 // to use the localtime notation only 1921 // according to a raccomandation in XMP Specification (Jan 2004, page 75) 1922 // the Acrobat way seems the right approach 1923 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); 1924 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); 1925 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); 1926 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) ); 1927 aCreationMetaDateString.append( "-" ); 1928 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); 1929 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) ); 1930 aCreationMetaDateString.append( "-" ); 1931 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); 1932 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) ); 1933 aCreationMetaDateString.append( "T" ); 1934 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); 1935 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); 1936 aCreationMetaDateString.append( ":" ); 1937 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); 1938 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); 1939 aCreationMetaDateString.append( ":" ); 1940 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); 1941 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); 1942 1943 sal_uInt32 nDelta = 0; 1944 if( aGMT.Seconds > aTVal.Seconds ) 1945 { 1946 aCreationDateString.append( "-" ); 1947 nDelta = aGMT.Seconds-aTVal.Seconds; 1948 aCreationMetaDateString.append( "-" ); 1949 } 1950 else if( aGMT.Seconds < aTVal.Seconds ) 1951 { 1952 aCreationDateString.append( "+" ); 1953 nDelta = aTVal.Seconds-aGMT.Seconds; 1954 aCreationMetaDateString.append( "+" ); 1955 } 1956 else 1957 { 1958 aCreationDateString.append( "Z" ); 1959 aCreationMetaDateString.append( "Z" ); 1960 1961 } 1962 if( nDelta ) 1963 { 1964 aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); 1965 aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); 1966 aCreationDateString.append( "'" ); 1967 aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) ); 1968 aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) ); 1969 1970 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); 1971 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); 1972 aCreationMetaDateString.append( ":" ); 1973 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) ); 1974 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) ); 1975 } 1976 aCreationDateString.append( "'" ); 1977 aID.append( aCreationDateString.getStr(), aCreationDateString.getLength() ); 1978 1979 aInfoValuesOut = aID.makeStringAndClear(); 1980 o_rCString1 = aCreationDateString.makeStringAndClear(); 1981 o_rCString2 = aCreationMetaDateString.makeStringAndClear(); 1982 1983 rtlDigest aDigest = rtl_digest_createMD5(); 1984 OSL_ENSURE( aDigest != NULL, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" ); 1985 if( aDigest ) 1986 { 1987 rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) ); 1988 if( nError == rtl_Digest_E_None ) 1989 nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() ); 1990 if( nError == rtl_Digest_E_None ) 1991 { 1992 o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 ); 1993 //the binary form of the doc id is needed for encryption stuff 1994 rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 ); 1995 } 1996 } 1997 } 1998 1999 /* i12626 methods */ 2000 /* 2001 check if the Unicode string must be encrypted or not, perform the requested task, 2002 append the string as unicode hex, encrypted if needed 2003 */ 2004 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer ) 2005 { 2006 rOutBuffer.append( "<" ); 2007 if( m_aContext.Encryption.Encrypt() ) 2008 { 2009 const sal_Unicode* pStr = rInString.getStr(); 2010 sal_Int32 nLen = rInString.getLength(); 2011 //prepare a unicode string, encrypt it 2012 if( checkEncryptionBufferSize( nLen*2 ) ) 2013 { 2014 enableStringEncryption( nInObjectNumber ); 2015 register sal_uInt8 *pCopy = m_pEncryptionBuffer; 2016 sal_Int32 nChars = 2; 2017 *pCopy++ = 0xFE; 2018 *pCopy++ = 0xFF; 2019 // we need to prepare a byte stream from the unicode string buffer 2020 for( register int i = 0; i < nLen; i++ ) 2021 { 2022 register sal_Unicode aUnChar = pStr[i]; 2023 *pCopy++ = (sal_uInt8)( aUnChar >> 8 ); 2024 *pCopy++ = (sal_uInt8)( aUnChar & 255 ); 2025 nChars += 2; 2026 } 2027 //encrypt in place 2028 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars ); 2029 //now append, hexadecimal (appendHex), the encrypted result 2030 for(register int i = 0; i < nChars; i++) 2031 appendHex( m_pEncryptionBuffer[i], rOutBuffer ); 2032 } 2033 } 2034 else 2035 appendUnicodeTextString( rInString, rOutBuffer ); 2036 rOutBuffer.append( ">" ); 2037 } 2038 2039 inline void PDFWriterImpl::appendLiteralStringEncrypt( rtl::OStringBuffer& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer ) 2040 { 2041 rOutBuffer.append( "(" ); 2042 sal_Int32 nChars = rInString.getLength(); 2043 //check for encryption, if ok, encrypt the string, then convert with appndLiteralString 2044 if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) ) 2045 { 2046 //encrypt the string in a buffer, then append it 2047 enableStringEncryption( nInObjectNumber ); 2048 rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars ); 2049 appendLiteralString( (const sal_Char*)m_pEncryptionBuffer, nChars, rOutBuffer ); 2050 } 2051 else 2052 appendLiteralString( rInString.getStr(), nChars , rOutBuffer ); 2053 rOutBuffer.append( ")" ); 2054 } 2055 2056 inline void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer ) 2057 { 2058 rtl::OStringBuffer aBufferString( rInString ); 2059 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer); 2060 } 2061 2062 void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc ) 2063 { 2064 rtl::OString aBufferString( rtl::OUStringToOString( rInString, nEnc ) ); 2065 sal_Int32 nLen = aBufferString.getLength(); 2066 rtl::OStringBuffer aBuf( nLen ); 2067 const sal_Char* pT = aBufferString.getStr(); 2068 2069 for( sal_Int32 i = 0; i < nLen; i++, pT++ ) 2070 { 2071 if( (*pT & 0x80) == 0 ) 2072 aBuf.append( *pT ); 2073 else 2074 { 2075 aBuf.append( '<' ); 2076 appendHex( *pT, aBuf ); 2077 aBuf.append( '>' ); 2078 } 2079 } 2080 aBufferString = aBuf.makeStringAndClear(); 2081 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer); 2082 } 2083 2084 /* end i12626 methods */ 2085 2086 void PDFWriterImpl::emitComment( const char* pComment ) 2087 { 2088 OStringBuffer aLine( 64 ); 2089 aLine.append( "% " ); 2090 aLine.append( (const sal_Char*)pComment ); 2091 aLine.append( "\n" ); 2092 writeBuffer( aLine.getStr(), aLine.getLength() ); 2093 } 2094 2095 bool PDFWriterImpl::compressStream( SvMemoryStream* pStream ) 2096 { 2097 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 2098 pStream->Seek( STREAM_SEEK_TO_END ); 2099 sal_uLong nEndPos = pStream->Tell(); 2100 pStream->Seek( STREAM_SEEK_TO_BEGIN ); 2101 ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 ); 2102 SvMemoryStream aStream; 2103 pCodec->BeginCompression(); 2104 pCodec->Write( aStream, (const sal_uInt8*)pStream->GetData(), nEndPos ); 2105 pCodec->EndCompression(); 2106 delete pCodec; 2107 nEndPos = aStream.Tell(); 2108 pStream->Seek( STREAM_SEEK_TO_BEGIN ); 2109 aStream.Seek( STREAM_SEEK_TO_BEGIN ); 2110 pStream->SetStreamSize( nEndPos ); 2111 pStream->Write( aStream.GetData(), nEndPos ); 2112 return true; 2113 #else 2114 (void)pStream; 2115 return false; 2116 #endif 2117 } 2118 2119 void PDFWriterImpl::beginCompression() 2120 { 2121 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 2122 m_pCodec = new ZCodec( 0x4000, 0x4000 ); 2123 m_pMemStream = new SvMemoryStream(); 2124 m_pCodec->BeginCompression(); 2125 #endif 2126 } 2127 2128 void PDFWriterImpl::endCompression() 2129 { 2130 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 2131 if( m_pCodec ) 2132 { 2133 m_pCodec->EndCompression(); 2134 delete m_pCodec; 2135 m_pCodec = NULL; 2136 sal_uInt64 nLen = m_pMemStream->Tell(); 2137 m_pMemStream->Seek( 0 ); 2138 writeBuffer( m_pMemStream->GetData(), nLen ); 2139 delete m_pMemStream; 2140 m_pMemStream = NULL; 2141 } 2142 #endif 2143 } 2144 2145 bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes ) 2146 { 2147 if( ! m_bOpen ) // we are already down the drain 2148 return false; 2149 2150 if( ! nBytes ) // huh ? 2151 return true; 2152 2153 if( m_aOutputStreams.begin() != m_aOutputStreams.end() ) 2154 { 2155 m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END ); 2156 m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) ); 2157 return true; 2158 } 2159 2160 sal_uInt64 nWritten; 2161 if( m_pCodec ) 2162 { 2163 m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes ); 2164 nWritten = nBytes; 2165 } 2166 else 2167 { 2168 sal_Bool buffOK = sal_True; 2169 if( m_bEncryptThisStream ) 2170 { 2171 /* implement the encryption part of the PDF spec encryption algorithm 3.1 */ 2172 if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) != sal_False ) 2173 rtl_cipher_encodeARCFOUR( m_aCipher, 2174 (sal_uInt8*)pBuffer, static_cast<sal_Size>(nBytes), 2175 m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) ); 2176 } 2177 2178 const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer : pBuffer; 2179 if( m_aDocDigest ) 2180 rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) ); 2181 2182 if( osl_writeFile( m_aFile, 2183 pWriteBuffer, 2184 nBytes, &nWritten ) != osl_File_E_None ) 2185 nWritten = 0; 2186 2187 if( nWritten != nBytes ) 2188 { 2189 osl_closeFile( m_aFile ); 2190 m_bOpen = false; 2191 } 2192 } 2193 2194 return nWritten == nBytes; 2195 } 2196 2197 OutputDevice* PDFWriterImpl::getReferenceDevice() 2198 { 2199 if( ! m_pReferenceDevice ) 2200 { 2201 VirtualDevice* pVDev = new VirtualDevice( 0 ); 2202 2203 m_pReferenceDevice = pVDev; 2204 2205 if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 ) 2206 pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 ); 2207 else 2208 pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy ); 2209 2210 pVDev->SetOutputSizePixel( Size( 640, 480 ) ); 2211 pVDev->SetMapMode( MAP_MM ); 2212 2213 m_pReferenceDevice->mpPDFWriter = this; 2214 m_pReferenceDevice->ImplUpdateFontData( sal_True ); 2215 } 2216 return m_pReferenceDevice; 2217 } 2218 2219 class ImplPdfBuiltinFontData : public ImplFontData 2220 { 2221 private: 2222 const PDFWriterImpl::BuiltinFont& mrBuiltin; 2223 2224 public: 2225 enum {PDF_FONT_MAGIC = 0xBDFF0A1C }; 2226 ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& ); 2227 const PDFWriterImpl::BuiltinFont* GetBuiltinFont() const { return &mrBuiltin; } 2228 2229 virtual ImplFontData* Clone() const { return new ImplPdfBuiltinFontData(*this); } 2230 virtual ImplFontEntry* CreateFontInstance( ImplFontSelectData& ) const; 2231 virtual sal_IntPtr GetFontId() const { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); } 2232 }; 2233 2234 inline const ImplPdfBuiltinFontData* GetPdfFontData( const ImplFontData* pFontData ) 2235 { 2236 const ImplPdfBuiltinFontData* pFD = NULL; 2237 if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) ) 2238 pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData ); 2239 return pFD; 2240 } 2241 2242 static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin ) 2243 { 2244 ImplDevFontAttributes aDFA; 2245 aDFA.maName = String::CreateFromAscii( rBuiltin.m_pName ); 2246 aDFA.maStyleName = String::CreateFromAscii( rBuiltin.m_pStyleName ); 2247 aDFA.meFamily = rBuiltin.m_eFamily; 2248 aDFA.mbSymbolFlag = (rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 ); 2249 aDFA.mePitch = rBuiltin.m_ePitch; 2250 aDFA.meWeight = rBuiltin.m_eWeight; 2251 aDFA.meItalic = rBuiltin.m_eItalic; 2252 aDFA.meWidthType = rBuiltin.m_eWidthType; 2253 2254 aDFA.mbOrientation = true; 2255 aDFA.mbDevice = true; 2256 aDFA.mnQuality = 50000; 2257 aDFA.mbSubsettable = false; 2258 aDFA.mbEmbeddable = false; 2259 return aDFA; 2260 } 2261 2262 ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin ) 2263 : ImplFontData( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ), 2264 mrBuiltin( rBuiltin ) 2265 {} 2266 2267 ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const 2268 { 2269 ImplFontEntry* pEntry = new ImplFontEntry( rFSD ); 2270 return pEntry; 2271 } 2272 2273 ImplDevFontList* PDFWriterImpl::filterDevFontList( ImplDevFontList* pFontList ) 2274 { 2275 DBG_ASSERT( m_aSubsets.size() == 0, "Fonts changing during PDF generation, document will be invalid" ); 2276 ImplDevFontList* pFiltered = pFontList->Clone( true, true ); 2277 2278 // append the PDF builtin fonts 2279 if( !m_bIsPDF_A1 && !m_bEmbedStandardFonts) 2280 for( unsigned int i = 0; i < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); i++ ) 2281 { 2282 ImplFontData* pNewData = new ImplPdfBuiltinFontData( m_aBuiltinFonts[i] ); 2283 pFiltered->Add( pNewData ); 2284 } 2285 return pFiltered; 2286 } 2287 2288 bool PDFWriterImpl::isBuiltinFont( const ImplFontData* pFont ) const 2289 { 2290 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont ); 2291 return (pFD != NULL); 2292 } 2293 2294 void PDFWriterImpl::getFontMetric( ImplFontSelectData* pSelect, ImplFontMetricData* pMetric ) const 2295 { 2296 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData ); 2297 if( !pFD ) 2298 return; 2299 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); 2300 2301 pMetric->mnOrientation = sal::static_int_cast<short>(pSelect->mnOrientation); 2302 pMetric->meFamily = pBuiltinFont->m_eFamily; 2303 pMetric->mePitch = pBuiltinFont->m_ePitch; 2304 pMetric->meWeight = pBuiltinFont->m_eWeight; 2305 pMetric->meItalic = pBuiltinFont->m_eItalic; 2306 pMetric->mbSymbolFlag = pFD->IsSymbolFont(); 2307 pMetric->mnWidth = pSelect->mnHeight; 2308 pMetric->mnAscent = ( pSelect->mnHeight * +pBuiltinFont->m_nAscent + 500 ) / 1000; 2309 pMetric->mnDescent = ( pSelect->mnHeight * -pBuiltinFont->m_nDescent + 500 ) / 1000; 2310 pMetric->mnIntLeading = 0; 2311 pMetric->mnExtLeading = 0; 2312 pMetric->mnSlant = 0; 2313 pMetric->mbScalableFont = true; 2314 pMetric->mbDevice = true; 2315 } 2316 2317 // ----------------------------------------------------------------------- 2318 2319 namespace vcl { 2320 2321 class PDFSalLayout : public GenericSalLayout 2322 { 2323 PDFWriterImpl& mrPDFWriterImpl; 2324 const PDFWriterImpl::BuiltinFont& mrBuiltinFont; 2325 bool mbIsSymbolFont; 2326 long mnPixelPerEM; 2327 String maOrigText; 2328 2329 public: 2330 PDFSalLayout( PDFWriterImpl&, 2331 const PDFWriterImpl::BuiltinFont&, 2332 long nPixelPerEM, int nOrientation ); 2333 2334 void SetText( const String& rText ) { maOrigText = rText; } 2335 virtual bool LayoutText( ImplLayoutArgs& ); 2336 virtual void InitFont() const; 2337 virtual void DrawText( SalGraphics& ) const; 2338 }; 2339 2340 } 2341 2342 // ----------------------------------------------------------------------- 2343 2344 PDFSalLayout::PDFSalLayout( PDFWriterImpl& rPDFWriterImpl, 2345 const PDFWriterImpl::BuiltinFont& rBuiltinFont, 2346 long nPixelPerEM, int nOrientation ) 2347 : mrPDFWriterImpl( rPDFWriterImpl ), 2348 mrBuiltinFont( rBuiltinFont ), 2349 mnPixelPerEM( nPixelPerEM ) 2350 { 2351 mbIsSymbolFont = (rBuiltinFont.m_eCharSet != RTL_TEXTENCODING_MS_1252); 2352 SetOrientation( nOrientation ); 2353 } 2354 2355 // ----------------------------------------------------------------------- 2356 2357 bool PDFSalLayout::LayoutText( ImplLayoutArgs& rArgs ) 2358 { 2359 const String aText( rArgs.mpStr+rArgs.mnMinCharPos, sal::static_int_cast<xub_StrLen>(rArgs.mnEndCharPos-rArgs.mnMinCharPos) ); 2360 SetText( aText ); 2361 SetUnitsPerPixel( 1000 ); 2362 2363 rtl_UnicodeToTextConverter aConv = rtl_createTextToUnicodeConverter( mrBuiltinFont.m_eCharSet ); 2364 2365 Point aNewPos( 0, 0 ); 2366 bool bRightToLeft; 2367 for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); ) 2368 { 2369 // TODO: handle unicode surrogates 2370 // on the other hand the PDF builtin fonts don't support them anyway 2371 sal_Unicode cChar = rArgs.mpStr[ nCharPos ]; 2372 if( bRightToLeft ) 2373 cChar = static_cast<sal_Unicode>(GetMirroredChar( cChar )); 2374 2375 if( 1 ) // TODO: shortcut for ASCII? 2376 { 2377 sal_Char aBuf[4]; 2378 sal_uInt32 nInfo; 2379 sal_Size nSrcCvtChars; 2380 2381 sal_Size nConv = rtl_convertUnicodeToText( aConv, 2382 NULL, 2383 &cChar, 1, 2384 aBuf, sizeof(aBuf)/sizeof(*aBuf), 2385 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR, 2386 &nInfo, &nSrcCvtChars ); 2387 // check whether conversion was possible 2388 // else fallback font is needed as the standard fonts 2389 // are handled via WinAnsi encoding 2390 if( nConv > 0 ) 2391 cChar = ((sal_Unicode)aBuf[0]) & 0x00ff; 2392 } 2393 if( cChar & 0xff00 ) 2394 { 2395 cChar = 0; // NotDef glyph 2396 rArgs.NeedFallback( nCharPos, bRightToLeft ); 2397 } 2398 2399 long nGlyphWidth = (long)mrBuiltinFont.m_aWidths[cChar] * mnPixelPerEM; 2400 long nGlyphFlags = 0; // builtin fonts don't have diacritic glyphs 2401 if( bRightToLeft ) 2402 nGlyphFlags |= GlyphItem::IS_RTL_GLYPH; 2403 // TODO: get kerning from builtin fonts 2404 GlyphItem aGI( nCharPos, cChar, aNewPos, nGlyphFlags, nGlyphWidth ); 2405 AppendGlyph( aGI ); 2406 2407 aNewPos.X() += nGlyphWidth; 2408 } 2409 2410 rtl_destroyUnicodeToTextConverter( aConv ); 2411 2412 return true; 2413 } 2414 2415 // ----------------------------------------------------------------------- 2416 2417 void PDFSalLayout::InitFont() const 2418 { 2419 // TODO: recreate font with all its attributes 2420 } 2421 2422 // ----------------------------------------------------------------------- 2423 2424 void PDFSalLayout::DrawText( SalGraphics& ) const 2425 { 2426 mrPDFWriterImpl.drawLayout( *const_cast<PDFSalLayout*>(this), maOrigText, true ); 2427 } 2428 2429 // ----------------------------------------------------------------------- 2430 2431 SalLayout* PDFWriterImpl::GetTextLayout( ImplLayoutArgs& rArgs, ImplFontSelectData* pSelect ) 2432 { 2433 DBG_ASSERT( (pSelect->mpFontData != NULL), 2434 "PDFWriterImpl::GetTextLayout mpFontData is NULL" ); 2435 2436 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData ); 2437 if( !pFD ) 2438 return NULL; 2439 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); 2440 2441 long nPixelPerEM = pSelect->mnWidth ? pSelect->mnWidth : pSelect->mnHeight; 2442 int nOrientation = pSelect->mnOrientation; 2443 PDFSalLayout* pLayout = new PDFSalLayout( *this, *pBuiltinFont, nPixelPerEM, nOrientation ); 2444 pLayout->SetText( rArgs.mpStr ); 2445 return pLayout; 2446 } 2447 2448 sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation ) 2449 { 2450 endPage(); 2451 m_nCurrentPage = m_aPages.size(); 2452 m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) ); 2453 m_aPages.back().m_nPageIndex = m_nCurrentPage; 2454 m_aPages.back().beginStream(); 2455 2456 // setup global graphics state 2457 // linewidth is "1 pixel" by default 2458 OStringBuffer aBuf( 16 ); 2459 appendDouble( 72.0/double(getReferenceDevice()->ImplGetDPIX()), aBuf ); 2460 aBuf.append( " w\n" ); 2461 writeBuffer( aBuf.getStr(), aBuf.getLength() ); 2462 2463 return m_nCurrentPage; 2464 } 2465 2466 void PDFWriterImpl::endPage() 2467 { 2468 if( m_aPages.begin() != m_aPages.end() ) 2469 { 2470 // close eventual MC sequence 2471 endStructureElementMCSeq(); 2472 2473 // sanity check 2474 if( m_aOutputStreams.begin() != m_aOutputStreams.end() ) 2475 { 2476 DBG_ERROR( "redirection across pages !!!" ); 2477 m_aOutputStreams.clear(); // leak ! 2478 m_aMapMode.SetOrigin( Point() ); 2479 } 2480 2481 m_aGraphicsStack.clear(); 2482 m_aGraphicsStack.push_back( GraphicsState() ); 2483 2484 // this should pop the PDF graphics stack if necessary 2485 updateGraphicsState(); 2486 2487 m_aPages.back().endStream(); 2488 2489 // reset the default font 2490 Font aFont; 2491 aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) ); 2492 aFont.SetSize( Size( 0, 12 ) ); 2493 2494 m_aCurrentPDFState = m_aGraphicsStack.front(); 2495 m_aGraphicsStack.front().m_aFont = aFont; 2496 2497 for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin(); 2498 it != m_aBitmaps.end(); ++it ) 2499 { 2500 if( ! it->m_aBitmap.IsEmpty() ) 2501 { 2502 writeBitmapObject( *it ); 2503 it->m_aBitmap = BitmapEx(); 2504 } 2505 } 2506 for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg ) 2507 { 2508 if( jpeg->m_pStream ) 2509 { 2510 writeJPG( *jpeg ); 2511 delete jpeg->m_pStream; 2512 jpeg->m_pStream = NULL; 2513 jpeg->m_aMask = Bitmap(); 2514 } 2515 } 2516 for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin(); 2517 t != m_aTransparentObjects.end(); ++t ) 2518 { 2519 if( t->m_pContentStream ) 2520 { 2521 writeTransparentObject( *t ); 2522 delete t->m_pContentStream; 2523 t->m_pContentStream = NULL; 2524 } 2525 } 2526 } 2527 } 2528 2529 sal_Int32 PDFWriterImpl::createObject() 2530 { 2531 m_aObjects.push_back( ~0U ); 2532 return m_aObjects.size(); 2533 } 2534 2535 bool PDFWriterImpl::updateObject( sal_Int32 n ) 2536 { 2537 if( ! m_bOpen ) 2538 return false; 2539 2540 sal_uInt64 nOffset = ~0U; 2541 oslFileError aError = osl_getFilePos( m_aFile, &nOffset ); 2542 DBG_ASSERT( aError == osl_File_E_None, "could not register object" ); 2543 if( aError != osl_File_E_None ) 2544 { 2545 osl_closeFile( m_aFile ); 2546 m_bOpen = false; 2547 } 2548 m_aObjects[ n-1 ] = nOffset; 2549 return aError == osl_File_E_None; 2550 } 2551 2552 #define CHECK_RETURN( x ) if( !(x) ) return 0 2553 2554 sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject ) 2555 { 2556 if( nObject > 0 ) 2557 { 2558 OStringBuffer aLine( 1024 ); 2559 2560 aLine.append( nObject ); 2561 aLine.append( " 0 obj\n" 2562 "<</Nums[\n" ); 2563 sal_Int32 nTreeItems = m_aStructParentTree.size(); 2564 for( sal_Int32 n = 0; n < nTreeItems; n++ ) 2565 { 2566 aLine.append( n ); 2567 aLine.append( ' ' ); 2568 aLine.append( m_aStructParentTree[n] ); 2569 aLine.append( "\n" ); 2570 } 2571 aLine.append( "]>>\nendobj\n\n" ); 2572 CHECK_RETURN( updateObject( nObject ) ); 2573 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 2574 } 2575 return nObject; 2576 } 2577 2578 const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr ) 2579 { 2580 static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings; 2581 // fill maps once 2582 if( aAttributeStrings.empty() ) 2583 { 2584 aAttributeStrings[ PDFWriter::Placement ] = "Placement"; 2585 aAttributeStrings[ PDFWriter::WritingMode ] = "WritingMode"; 2586 aAttributeStrings[ PDFWriter::SpaceBefore ] = "SpaceBefore"; 2587 aAttributeStrings[ PDFWriter::SpaceAfter ] = "SpaceAfter"; 2588 aAttributeStrings[ PDFWriter::StartIndent ] = "StartIndent"; 2589 aAttributeStrings[ PDFWriter::EndIndent ] = "EndIndent"; 2590 aAttributeStrings[ PDFWriter::TextIndent ] = "TextIndent"; 2591 aAttributeStrings[ PDFWriter::TextAlign ] = "TextAlign"; 2592 aAttributeStrings[ PDFWriter::Width ] = "Width"; 2593 aAttributeStrings[ PDFWriter::Height ] = "Height"; 2594 aAttributeStrings[ PDFWriter::BlockAlign ] = "BlockAlign"; 2595 aAttributeStrings[ PDFWriter::InlineAlign ] = "InlineAlign"; 2596 aAttributeStrings[ PDFWriter::LineHeight ] = "LineHeight"; 2597 aAttributeStrings[ PDFWriter::BaselineShift ] = "BaselineShift"; 2598 aAttributeStrings[ PDFWriter::TextDecorationType ] = "TextDecorationType"; 2599 aAttributeStrings[ PDFWriter::ListNumbering ] = "ListNumbering"; 2600 aAttributeStrings[ PDFWriter::RowSpan ] = "RowSpan"; 2601 aAttributeStrings[ PDFWriter::ColSpan ] = "ColSpan"; 2602 aAttributeStrings[ PDFWriter::LinkAnnotation ] = "LinkAnnotation"; 2603 } 2604 2605 std::map< PDFWriter::StructAttribute, const char* >::const_iterator it = 2606 aAttributeStrings.find( eAttr ); 2607 2608 #if OSL_DEBUG_LEVEL > 1 2609 if( it == aAttributeStrings.end() ) 2610 fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr ); 2611 #endif 2612 2613 return it != aAttributeStrings.end() ? it->second : ""; 2614 } 2615 2616 const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal ) 2617 { 2618 static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings; 2619 2620 if( aValueStrings.empty() ) 2621 { 2622 aValueStrings[ PDFWriter::NONE ] = "None"; 2623 aValueStrings[ PDFWriter::Block ] = "Block"; 2624 aValueStrings[ PDFWriter::Inline ] = "Inline"; 2625 aValueStrings[ PDFWriter::Before ] = "Before"; 2626 aValueStrings[ PDFWriter::After ] = "After"; 2627 aValueStrings[ PDFWriter::Start ] = "Start"; 2628 aValueStrings[ PDFWriter::End ] = "End"; 2629 aValueStrings[ PDFWriter::LrTb ] = "LrTb"; 2630 aValueStrings[ PDFWriter::RlTb ] = "RlTb"; 2631 aValueStrings[ PDFWriter::TbRl ] = "TbRl"; 2632 aValueStrings[ PDFWriter::Center ] = "Center"; 2633 aValueStrings[ PDFWriter::Justify ] = "Justify"; 2634 aValueStrings[ PDFWriter::Auto ] = "Auto"; 2635 aValueStrings[ PDFWriter::Middle ] = "Middle"; 2636 aValueStrings[ PDFWriter::Normal ] = "Normal"; 2637 aValueStrings[ PDFWriter::Underline ] = "Underline"; 2638 aValueStrings[ PDFWriter::Overline ] = "Overline"; 2639 aValueStrings[ PDFWriter::LineThrough ] = "LineThrough"; 2640 aValueStrings[ PDFWriter::Disc ] = "Disc"; 2641 aValueStrings[ PDFWriter::Circle ] = "Circle"; 2642 aValueStrings[ PDFWriter::Square ] = "Square"; 2643 aValueStrings[ PDFWriter::Decimal ] = "Decimal"; 2644 aValueStrings[ PDFWriter::UpperRoman ] = "UpperRoman"; 2645 aValueStrings[ PDFWriter::LowerRoman ] = "LowerRoman"; 2646 aValueStrings[ PDFWriter::UpperAlpha ] = "UpperAlpha"; 2647 aValueStrings[ PDFWriter::LowerAlpha ] = "LowerAlpha"; 2648 } 2649 2650 std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it = 2651 aValueStrings.find( eVal ); 2652 2653 #if OSL_DEBUG_LEVEL > 1 2654 if( it == aValueStrings.end() ) 2655 fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal ); 2656 #endif 2657 2658 return it != aValueStrings.end() ? it->second : ""; 2659 } 2660 2661 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt ) 2662 { 2663 o_rLine.append( "/" ); 2664 o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) ); 2665 2666 if( i_rVal.eValue != PDFWriter::Invalid ) 2667 { 2668 o_rLine.append( "/" ); 2669 o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) ); 2670 } 2671 else 2672 { 2673 // numerical value 2674 o_rLine.append( " " ); 2675 if( i_bIsFixedInt ) 2676 appendFixedInt( i_rVal.nValue, o_rLine ); 2677 else 2678 o_rLine.append( i_rVal.nValue ); 2679 } 2680 o_rLine.append( "\n" ); 2681 } 2682 2683 OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle ) 2684 { 2685 // create layout, list and table attribute sets 2686 OStringBuffer aLayout(256), aList(64), aTable(64); 2687 for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin(); 2688 it != i_rEle.m_aAttributes.end(); ++it ) 2689 { 2690 if( it->first == PDFWriter::ListNumbering ) 2691 appendStructureAttributeLine( it->first, it->second, aList, true ); 2692 else if( it->first == PDFWriter::RowSpan || 2693 it->first == PDFWriter::ColSpan ) 2694 appendStructureAttributeLine( it->first, it->second, aTable, false ); 2695 else if( it->first == PDFWriter::LinkAnnotation ) 2696 { 2697 sal_Int32 nLink = it->second.nValue; 2698 std::map< sal_Int32, sal_Int32 >::const_iterator link_it = 2699 m_aLinkPropertyMap.find( nLink ); 2700 if( link_it != m_aLinkPropertyMap.end() ) 2701 nLink = link_it->second; 2702 if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() ) 2703 { 2704 // update struct parent of link 2705 OStringBuffer aStructParentEntry( 32 ); 2706 aStructParentEntry.append( i_rEle.m_nObject ); 2707 aStructParentEntry.append( " 0 R" ); 2708 m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() ); 2709 m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1; 2710 2711 sal_Int32 nRefObject = createObject(); 2712 OStringBuffer aRef( 256 ); 2713 aRef.append( nRefObject ); 2714 aRef.append( " 0 obj\n" 2715 "<</Type/OBJR/Obj " ); 2716 aRef.append( m_aLinks[ nLink ].m_nObject ); 2717 aRef.append( " 0 R>>\n" 2718 "endobj\n\n" 2719 ); 2720 updateObject( nRefObject ); 2721 writeBuffer( aRef.getStr(), aRef.getLength() ); 2722 2723 i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) ); 2724 } 2725 else 2726 { 2727 DBG_ERROR( "unresolved link id for Link structure" ); 2728 #if OSL_DEBUG_LEVEL > 1 2729 fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink ); 2730 { 2731 OStringBuffer aLine( "unresolved link id " ); 2732 aLine.append( nLink ); 2733 aLine.append( " for Link structure" ); 2734 emitComment( aLine.getStr() ); 2735 } 2736 #endif 2737 } 2738 } 2739 else 2740 appendStructureAttributeLine( it->first, it->second, aLayout, true ); 2741 } 2742 if( ! i_rEle.m_aBBox.IsEmpty() ) 2743 { 2744 aLayout.append( "/BBox[" ); 2745 appendFixedInt( i_rEle.m_aBBox.Left(), aLayout ); 2746 aLayout.append( " " ); 2747 appendFixedInt( i_rEle.m_aBBox.Top(), aLayout ); 2748 aLayout.append( " " ); 2749 appendFixedInt( i_rEle.m_aBBox.Right(), aLayout ); 2750 aLayout.append( " " ); 2751 appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout ); 2752 aLayout.append( "]\n" ); 2753 } 2754 2755 std::vector< sal_Int32 > aAttribObjects; 2756 if( aLayout.getLength() ) 2757 { 2758 aAttribObjects.push_back( createObject() ); 2759 updateObject( aAttribObjects.back() ); 2760 OStringBuffer aObj( 64 ); 2761 aObj.append( aAttribObjects.back() ); 2762 aObj.append( " 0 obj\n" 2763 "<</O/Layout\n" ); 2764 aLayout.append( ">>\nendobj\n\n" ); 2765 writeBuffer( aObj.getStr(), aObj.getLength() ); 2766 writeBuffer( aLayout.getStr(), aLayout.getLength() ); 2767 } 2768 if( aList.getLength() ) 2769 { 2770 aAttribObjects.push_back( createObject() ); 2771 updateObject( aAttribObjects.back() ); 2772 OStringBuffer aObj( 64 ); 2773 aObj.append( aAttribObjects.back() ); 2774 aObj.append( " 0 obj\n" 2775 "<</O/List\n" ); 2776 aList.append( ">>\nendobj\n\n" ); 2777 writeBuffer( aObj.getStr(), aObj.getLength() ); 2778 writeBuffer( aList.getStr(), aList.getLength() ); 2779 } 2780 if( aTable.getLength() ) 2781 { 2782 aAttribObjects.push_back( createObject() ); 2783 updateObject( aAttribObjects.back() ); 2784 OStringBuffer aObj( 64 ); 2785 aObj.append( aAttribObjects.back() ); 2786 aObj.append( " 0 obj\n" 2787 "<</O/Table\n" ); 2788 aTable.append( ">>\nendobj\n\n" ); 2789 writeBuffer( aObj.getStr(), aObj.getLength() ); 2790 writeBuffer( aTable.getStr(), aTable.getLength() ); 2791 } 2792 2793 OStringBuffer aRet( 64 ); 2794 if( aAttribObjects.size() > 1 ) 2795 aRet.append( " [" ); 2796 for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin(); 2797 at_it != aAttribObjects.end(); ++at_it ) 2798 { 2799 aRet.append( " " ); 2800 aRet.append( *at_it ); 2801 aRet.append( " 0 R" ); 2802 } 2803 if( aAttribObjects.size() > 1 ) 2804 aRet.append( " ]" ); 2805 return aRet.makeStringAndClear(); 2806 } 2807 2808 sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle ) 2809 { 2810 if( 2811 // do not emit NonStruct and its children 2812 rEle.m_eType == PDFWriter::NonStructElement && 2813 rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root 2814 ) 2815 return 0; 2816 2817 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it ) 2818 { 2819 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) ) 2820 { 2821 PDFStructureElement& rChild = m_aStructure[ *it ]; 2822 if( rChild.m_eType != PDFWriter::NonStructElement ) 2823 { 2824 if( rChild.m_nParentElement == rEle.m_nOwnElement ) 2825 emitStructure( rChild ); 2826 else 2827 { 2828 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure element" ); 2829 #if OSL_DEBUG_LEVEL > 1 2830 fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it ); 2831 #endif 2832 } 2833 } 2834 } 2835 else 2836 { 2837 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" ); 2838 #if OSL_DEBUG_LEVEL > 1 2839 fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it ); 2840 #endif 2841 } 2842 } 2843 2844 OStringBuffer aLine( 512 ); 2845 aLine.append( rEle.m_nObject ); 2846 aLine.append( " 0 obj\n" 2847 "<</Type" ); 2848 sal_Int32 nParentTree = -1; 2849 if( rEle.m_nOwnElement == rEle.m_nParentElement ) 2850 { 2851 nParentTree = createObject(); 2852 CHECK_RETURN( nParentTree ); 2853 aLine.append( "/StructTreeRoot\n" ); 2854 aLine.append( "/ParentTree " ); 2855 aLine.append( nParentTree ); 2856 aLine.append( " 0 R\n" ); 2857 if( ! m_aRoleMap.empty() ) 2858 { 2859 aLine.append( "/RoleMap<<" ); 2860 for( std::hash_map<OString,OString,OStringHash>::const_iterator 2861 it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it ) 2862 { 2863 aLine.append( '/' ); 2864 aLine.append(it->first); 2865 aLine.append( '/' ); 2866 aLine.append( it->second ); 2867 aLine.append( '\n' ); 2868 } 2869 aLine.append( ">>\n" ); 2870 } 2871 } 2872 else 2873 { 2874 aLine.append( "/StructElem\n" 2875 "/S/" ); 2876 if( rEle.m_aAlias.getLength() > 0 ) 2877 aLine.append( rEle.m_aAlias ); 2878 else 2879 aLine.append( getStructureTag( rEle.m_eType ) ); 2880 aLine.append( "\n" 2881 "/P " ); 2882 aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject ); 2883 aLine.append( " 0 R\n" 2884 "/Pg " ); 2885 aLine.append( rEle.m_nFirstPageObject ); 2886 aLine.append( " 0 R\n" ); 2887 if( rEle.m_aActualText.getLength() ) 2888 { 2889 aLine.append( "/ActualText" ); 2890 appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine ); 2891 aLine.append( "\n" ); 2892 } 2893 if( rEle.m_aAltText.getLength() ) 2894 { 2895 aLine.append( "/Alt" ); 2896 appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine ); 2897 aLine.append( "\n" ); 2898 } 2899 } 2900 if( ! rEle.m_aBBox.IsEmpty() || rEle.m_aAttributes.size() ) 2901 { 2902 OString aAttribs = emitStructureAttributes( rEle ); 2903 if( aAttribs.getLength() ) 2904 { 2905 aLine.append( "/A" ); 2906 aLine.append( aAttribs ); 2907 aLine.append( "\n" ); 2908 } 2909 } 2910 if( rEle.m_aLocale.Language.getLength() > 0 ) 2911 { 2912 OUStringBuffer aLocBuf( 16 ); 2913 aLocBuf.append( rEle.m_aLocale.Language.toAsciiLowerCase() ); 2914 if( rEle.m_aLocale.Country.getLength() > 0 ) 2915 { 2916 aLocBuf.append( sal_Unicode('-') ); 2917 aLocBuf.append( rEle.m_aLocale.Country ); 2918 } 2919 aLine.append( "/Lang" ); 2920 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine ); 2921 aLine.append( "\n" ); 2922 } 2923 if( ! rEle.m_aKids.empty() ) 2924 { 2925 unsigned int i = 0; 2926 aLine.append( "/K[" ); 2927 for( std::list< PDFStructureElementKid >::const_iterator it = 2928 rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ ) 2929 { 2930 if( it->nMCID == -1 ) 2931 { 2932 aLine.append( it->nObject ); 2933 aLine.append( " 0 R" ); 2934 aLine.append( ( (i & 15) == 15 ) ? "\n" : " " ); 2935 } 2936 else 2937 { 2938 if( it->nObject == rEle.m_nFirstPageObject ) 2939 { 2940 aLine.append( it->nMCID ); 2941 aLine.append( " " ); 2942 } 2943 else 2944 { 2945 aLine.append( "<</Type/MCR/Pg " ); 2946 aLine.append( it->nObject ); 2947 aLine.append( " 0 R /MCID " ); 2948 aLine.append( it->nMCID ); 2949 aLine.append( ">>\n" ); 2950 } 2951 } 2952 } 2953 aLine.append( "]\n" ); 2954 } 2955 aLine.append( ">>\nendobj\n\n" ); 2956 2957 CHECK_RETURN( updateObject( rEle.m_nObject ) ); 2958 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 2959 2960 CHECK_RETURN( emitStructParentTree( nParentTree ) ); 2961 2962 return rEle.m_nObject; 2963 } 2964 2965 bool PDFWriterImpl::emitGradients() 2966 { 2967 for( std::list<GradientEmit>::iterator it = m_aGradients.begin(); 2968 it != m_aGradients.end(); ++it ) 2969 { 2970 CHECK_RETURN( writeGradientFunction( *it ) ); 2971 } 2972 return true; 2973 } 2974 2975 bool PDFWriterImpl::emitTilings() 2976 { 2977 OStringBuffer aTilingObj( 1024 ); 2978 2979 for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it ) 2980 { 2981 DBG_ASSERT( it->m_pTilingStream, "tiling without stream" ); 2982 if( ! it->m_pTilingStream ) 2983 continue; 2984 2985 aTilingObj.setLength( 0 ); 2986 2987 #if OSL_DEBUG_LEVEL > 1 2988 emitComment( "PDFWriterImpl::emitTilings" ); 2989 #endif 2990 2991 sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left(); 2992 sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top(); 2993 sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth(); 2994 sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight(); 2995 if( it->m_aCellSize.Width() == 0 ) 2996 it->m_aCellSize.Width() = nW; 2997 if( it->m_aCellSize.Height() == 0 ) 2998 it->m_aCellSize.Height() = nH; 2999 3000 bool bDeflate = compressStream( it->m_pTilingStream ); 3001 it->m_pTilingStream->Seek( STREAM_SEEK_TO_END ); 3002 sal_Size nTilingStreamSize = it->m_pTilingStream->Tell(); 3003 it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN ); 3004 3005 // write pattern object 3006 aTilingObj.append( it->m_nObject ); 3007 aTilingObj.append( " 0 obj\n" ); 3008 aTilingObj.append( "<</Type/Pattern/PatternType 1\n" 3009 "/PaintType 1\n" 3010 "/TilingType 2\n" 3011 "/BBox[" ); 3012 appendFixedInt( nX, aTilingObj ); 3013 aTilingObj.append( ' ' ); 3014 appendFixedInt( nY, aTilingObj ); 3015 aTilingObj.append( ' ' ); 3016 appendFixedInt( nX+nW, aTilingObj ); 3017 aTilingObj.append( ' ' ); 3018 appendFixedInt( nY+nH, aTilingObj ); 3019 aTilingObj.append( "]\n" 3020 "/XStep " ); 3021 appendFixedInt( it->m_aCellSize.Width(), aTilingObj ); 3022 aTilingObj.append( "\n" 3023 "/YStep " ); 3024 appendFixedInt( it->m_aCellSize.Height(), aTilingObj ); 3025 aTilingObj.append( "\n" ); 3026 if( it->m_aTransform.matrix[0] != 1.0 || 3027 it->m_aTransform.matrix[1] != 0.0 || 3028 it->m_aTransform.matrix[3] != 0.0 || 3029 it->m_aTransform.matrix[4] != 1.0 || 3030 it->m_aTransform.matrix[2] != 0.0 || 3031 it->m_aTransform.matrix[5] != 0.0 ) 3032 { 3033 aTilingObj.append( "/Matrix [" ); 3034 // TODO: scaling, mirroring on y, etc 3035 appendDouble( it->m_aTransform.matrix[0], aTilingObj ); 3036 aTilingObj.append( ' ' ); 3037 appendDouble( it->m_aTransform.matrix[1], aTilingObj ); 3038 aTilingObj.append( ' ' ); 3039 appendDouble( it->m_aTransform.matrix[3], aTilingObj ); 3040 aTilingObj.append( ' ' ); 3041 appendDouble( it->m_aTransform.matrix[4], aTilingObj ); 3042 aTilingObj.append( ' ' ); 3043 appendDouble( it->m_aTransform.matrix[2], aTilingObj ); 3044 aTilingObj.append( ' ' ); 3045 appendDouble( it->m_aTransform.matrix[5], aTilingObj ); 3046 aTilingObj.append( "]\n" ); 3047 } 3048 aTilingObj.append( "/Resources" ); 3049 it->m_aResources.append( aTilingObj, getFontDictObject() ); 3050 if( bDeflate ) 3051 aTilingObj.append( "/Filter/FlateDecode" ); 3052 aTilingObj.append( "/Length " ); 3053 aTilingObj.append( (sal_Int32)nTilingStreamSize ); 3054 aTilingObj.append( ">>\nstream\n" ); 3055 CHECK_RETURN( updateObject( it->m_nObject ) ); 3056 CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ); 3057 checkAndEnableStreamEncryption( it->m_nObject ); 3058 nTilingStreamSize = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize ); 3059 delete it->m_pTilingStream; 3060 it->m_pTilingStream = NULL; 3061 if( nTilingStreamSize == 0 ) 3062 return false; 3063 disableStreamEncryption(); 3064 aTilingObj.setLength( 0 ); 3065 aTilingObj.append( "\nendstream\nendobj\n\n" ); 3066 CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ); 3067 } 3068 return true; 3069 } 3070 3071 sal_Int32 PDFWriterImpl::emitBuiltinFont( const ImplFontData* pFont, sal_Int32 nFontObject ) 3072 { 3073 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont ); 3074 if( !pFD ) 3075 return 0; 3076 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); 3077 3078 OStringBuffer aLine( 1024 ); 3079 3080 if( nFontObject <= 0 ) 3081 nFontObject = createObject(); 3082 CHECK_RETURN( updateObject( nFontObject ) ); 3083 aLine.append( nFontObject ); 3084 aLine.append( " 0 obj\n" 3085 "<</Type/Font/Subtype/Type1/BaseFont/" ); 3086 appendName( pBuiltinFont->m_pPSName, aLine ); 3087 aLine.append( "\n" ); 3088 if( pBuiltinFont->m_eCharSet == RTL_TEXTENCODING_MS_1252 ) 3089 aLine.append( "/Encoding/WinAnsiEncoding\n" ); 3090 aLine.append( ">>\nendobj\n\n" ); 3091 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3092 return nFontObject; 3093 } 3094 3095 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const ImplFontData* pFont, EmbedFont& rEmbed ) 3096 { 3097 std::map< sal_Int32, sal_Int32 > aRet; 3098 if( isBuiltinFont( pFont ) ) 3099 { 3100 aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont ); 3101 return aRet; 3102 } 3103 3104 sal_Int32 nFontObject = 0; 3105 sal_Int32 nFontDescriptor = 0; 3106 rtl::OString aSubType( "/Type1" ); 3107 FontSubsetInfo aInfo; 3108 // fill in dummy values 3109 aInfo.m_nAscent = 1000; 3110 aInfo.m_nDescent = 200; 3111 aInfo.m_nCapHeight = 1000; 3112 aInfo.m_aFontBBox = Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) ); 3113 aInfo.m_aPSName = pFont->maName; 3114 sal_Int32 pWidths[256]; 3115 rtl_zeroMemory( pWidths, sizeof(pWidths) ); 3116 if( pFont->IsEmbeddable() ) 3117 { 3118 const unsigned char* pFontData = NULL; 3119 long nFontLen = 0; 3120 sal_Ucs nEncodedCodes[256]; 3121 sal_Int32 pEncWidths[256]; 3122 if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pEncWidths, aInfo, &nFontLen )) != NULL ) 3123 { 3124 m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen ); 3125 for( int i = 0; i < 256; i++ ) 3126 { 3127 if( nEncodedCodes[i] >= 32 && nEncodedCodes[i] < 256 ) 3128 { 3129 pWidths[i] = pEncWidths[ i ]; 3130 } 3131 } 3132 } 3133 } 3134 else if( pFont->mbSubsettable ) 3135 { 3136 aSubType = rtl::OString( "/TrueType" ); 3137 Int32Vector aGlyphWidths; 3138 Ucs2UIntMap aUnicodeMap; 3139 m_pReferenceDevice->mpGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap ); 3140 3141 OUString aTmpName; 3142 osl_createTempFile( NULL, NULL, &aTmpName.pData ); 3143 sal_Int32 pGlyphIDs[ 256 ]; 3144 sal_uInt8 pEncoding[ 256 ]; 3145 sal_Ucs pUnicodes[ 256 ]; 3146 sal_Int32 pDuWidths[ 256 ]; 3147 3148 memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) ); 3149 memset( pEncoding, 0, sizeof( pEncoding ) ); 3150 memset( pUnicodes, 0, sizeof( pUnicodes ) ); 3151 memset( pDuWidths, 0, sizeof( pDuWidths ) ); 3152 3153 for( sal_Ucs c = 32; c < 256; c++ ) 3154 { 3155 pUnicodes[c] = c; 3156 pEncoding[c] = c; 3157 pGlyphIDs[c] = 0; 3158 if( aUnicodeMap.find( c ) != aUnicodeMap.end() ) 3159 pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ]; 3160 } 3161 3162 m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, pFont, pGlyphIDs, pEncoding, pDuWidths, 256, aInfo ); 3163 osl_removeFile( aTmpName.pData ); 3164 } 3165 else 3166 { 3167 DBG_ERROR( "system font neither embeddable nor subsettable" ); 3168 } 3169 3170 // write font descriptor 3171 nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 ); 3172 if( nFontDescriptor ) 3173 { 3174 // write font object 3175 sal_Int32 nObject = createObject(); 3176 if( updateObject( nObject ) ) 3177 { 3178 OStringBuffer aLine( 1024 ); 3179 aLine.append( nObject ); 3180 aLine.append( " 0 obj\n" 3181 "<</Type/Font/Subtype" ); 3182 aLine.append( aSubType ); 3183 aLine.append( "/BaseFont/" ); 3184 appendName( aInfo.m_aPSName, aLine ); 3185 aLine.append( "\n" ); 3186 if( !pFont->mbSymbolFlag ) 3187 aLine.append( "/Encoding/WinAnsiEncoding\n" ); 3188 aLine.append( "/FirstChar 32 /LastChar 255\n" 3189 "/Widths[" ); 3190 for( int i = 32; i < 256; i++ ) 3191 { 3192 aLine.append( pWidths[i] ); 3193 aLine.append( ((i&15) == 15) ? "\n" : " " ); 3194 } 3195 aLine.append( "]\n" 3196 "/FontDescriptor " ); 3197 aLine.append( nFontDescriptor ); 3198 aLine.append( " 0 R>>\n" 3199 "endobj\n\n" ); 3200 writeBuffer( aLine.getStr(), aLine.getLength() ); 3201 3202 nFontObject = nObject; 3203 aRet[ rEmbed.m_nNormalFontID ] = nObject; 3204 } 3205 } 3206 3207 return aRet; 3208 } 3209 3210 typedef int ThreeInts[3]; 3211 static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen, 3212 ThreeInts& rSegmentLengths ) 3213 { 3214 if( !pFontBytes || (nByteLen < 0) ) 3215 return false; 3216 const unsigned char* pPtr = pFontBytes; 3217 const unsigned char* pEnd = pFontBytes + nByteLen; 3218 3219 for( int i = 0; i < 3; ++i) { 3220 // read segment1 header 3221 if( pPtr+6 >= pEnd ) 3222 return false; 3223 if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) ) 3224 return false; 3225 const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2]; 3226 if( nLen <= 0) 3227 return false; 3228 rSegmentLengths[i] = nLen; 3229 pPtr += nLen + 6; 3230 } 3231 3232 // read segment-end header 3233 if( pPtr+2 >= pEnd ) 3234 return false; 3235 if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) ) 3236 return false; 3237 3238 return true; 3239 } 3240 3241 struct FontException : public std::exception 3242 { 3243 }; 3244 3245 // TODO: always subset instead of embedding the full font => this method becomes obsolete then 3246 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFontData* pFont, EmbedFont& rEmbed ) 3247 { 3248 std::map< sal_Int32, sal_Int32 > aRet; 3249 if( isBuiltinFont( pFont ) ) 3250 { 3251 aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont ); 3252 return aRet; 3253 } 3254 3255 sal_Int32 nFontObject = 0; 3256 sal_Int32 nStreamObject = 0; 3257 sal_Int32 nFontDescriptor = 0; 3258 3259 // prepare font encoding 3260 const Ucs2SIntMap* pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pFont, NULL ); 3261 sal_Int32 nToUnicodeStream = 0; 3262 sal_uInt8 nEncoding[256]; 3263 sal_Ucs nEncodedCodes[256]; 3264 std::vector<sal_Ucs> aUnicodes; 3265 aUnicodes.reserve( 256 ); 3266 sal_Int32 pUnicodesPerGlyph[256]; 3267 sal_Int32 pEncToUnicodeIndex[256]; 3268 if( pEncoding ) 3269 { 3270 rtl_zeroMemory( nEncoding, sizeof(nEncoding) ); 3271 rtl_zeroMemory( nEncodedCodes, sizeof(nEncodedCodes) ); 3272 rtl_zeroMemory( pUnicodesPerGlyph, sizeof(pUnicodesPerGlyph) ); 3273 rtl_zeroMemory( pEncToUnicodeIndex, sizeof(pEncToUnicodeIndex) ); 3274 for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it ) 3275 { 3276 if( it->second != -1 ) 3277 { 3278 sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff); 3279 nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode ); 3280 nEncodedCodes[ nCode ] = it->first; 3281 pEncToUnicodeIndex[ nCode ] = static_cast<sal_Int32>(aUnicodes.size()); 3282 aUnicodes.push_back( it->first ); 3283 pUnicodesPerGlyph[ nCode ] = 1; 3284 } 3285 } 3286 } 3287 3288 FontSubsetInfo aInfo; 3289 sal_Int32 pWidths[256]; 3290 const unsigned char* pFontData = NULL; 3291 long nFontLen = 0; 3292 sal_Int32 nLength1, nLength2; 3293 try 3294 { 3295 if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL ) 3296 { 3297 if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 ) 3298 throw FontException(); 3299 // see whether it is pfb or pfa; if it is a pfb, fill ranges 3300 // of 6 bytes that are not part of the font program 3301 std::list< int > aSections; 3302 std::list< int >::const_iterator it; 3303 int nIndex = 0; 3304 while( pFontData[nIndex] == 0x80 && nIndex < nFontLen-1 ) 3305 { 3306 aSections.push_back( nIndex ); 3307 if( pFontData[nIndex+1] == 0x03 ) 3308 break; 3309 sal_Int32 nBytes = 3310 ((sal_Int32)pFontData[nIndex+2]) | 3311 ((sal_Int32)pFontData[nIndex+3]) << 8 | 3312 ((sal_Int32)pFontData[nIndex+4]) << 16 | 3313 ((sal_Int32)pFontData[nIndex+5]) << 24; 3314 nIndex += nBytes+6; 3315 } 3316 3317 // search for eexec 3318 // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below 3319 nIndex = 0; 3320 int nEndAsciiIndex; 3321 int nBeginBinaryIndex; 3322 int nEndBinaryIndex; 3323 do 3324 { 3325 while( nIndex < nFontLen-4 && 3326 ( pFontData[nIndex] != 'e' || 3327 pFontData[nIndex+1] != 'e' || 3328 pFontData[nIndex+2] != 'x' || 3329 pFontData[nIndex+3] != 'e' || 3330 pFontData[nIndex+4] != 'c' 3331 ) 3332 ) 3333 nIndex++; 3334 // check whether we are in a excluded section 3335 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) 3336 ; 3337 } while( it != aSections.end() && nIndex < nFontLen-4 ); 3338 // this should end the ascii part 3339 if( nIndex > nFontLen-5 ) 3340 throw FontException(); 3341 3342 nEndAsciiIndex = nIndex+4; 3343 // now count backwards until we can account for 512 '0' 3344 // which is the endmarker of the (hopefully) binary data 3345 // do not count the pfb header sections 3346 int nFound = 0; 3347 nIndex = nFontLen-1; 3348 while( nIndex > 0 && nFound < 512 ) 3349 { 3350 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) 3351 ; 3352 if( it == aSections.end() ) 3353 { 3354 // inside the 512 '0' block there may only be whitespace 3355 // according to T1 spec; probably it would be to simple 3356 // if all fonts complied 3357 if( pFontData[nIndex] == '0' ) 3358 nFound++; 3359 else if( nFound > 0 && 3360 pFontData[nIndex] != '\r' && 3361 pFontData[nIndex] != '\t' && 3362 pFontData[nIndex] != '\n' && 3363 pFontData[nIndex] != ' ' ) 3364 break; 3365 } 3366 nIndex--; 3367 } 3368 3369 if( nIndex < 1 || nIndex <= nEndAsciiIndex ) 3370 throw FontException(); 3371 3372 // nLength3 is the rest of the file - excluding any section headers 3373 // nIndex now points to the first of the 512 '0' characters marking the 3374 // fixed content portion 3375 sal_Int32 nLength3 = nFontLen - nIndex; 3376 for( it = aSections.begin(); it != aSections.end(); ++it ) 3377 { 3378 // special case: nIndex inside a section marker 3379 if( nIndex >= (*it) && (*it)+6 > nIndex ) 3380 nLength3 -= (*it)+6 - nIndex; 3381 else if( *it >= nIndex ) 3382 { 3383 if( *it < nFontLen - 6 ) 3384 nLength3 -= 6; 3385 else // the last section 0x8003 is only 2 bytes after all 3386 nLength3 -= (nFontLen - *it); 3387 } 3388 } 3389 3390 // there may be whitespace to ignore before the 512 '0' 3391 while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' ) 3392 { 3393 nIndex--; 3394 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) 3395 ; 3396 if( it != aSections.end() ) 3397 { 3398 nIndex = (*it)-1; 3399 break; // this is surely a binary boundary, in ascii case it wouldn't matter 3400 } 3401 } 3402 nEndBinaryIndex = nIndex; 3403 3404 // search for beginning of binary section 3405 nBeginBinaryIndex = nEndAsciiIndex; 3406 do 3407 { 3408 nBeginBinaryIndex++; 3409 for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it ) 3410 ; 3411 } while( nBeginBinaryIndex < nEndBinaryIndex && 3412 ( pFontData[nBeginBinaryIndex] == '\r' || 3413 pFontData[nBeginBinaryIndex] == '\n' || 3414 it != aSections.end() ) ); 3415 3416 // it seems to be vital to copy the exact whitespace between binary data 3417 // and eexec, else a invalid font results. so make nEndAsciiIndex 3418 // always immediate in front of nBeginBinaryIndex 3419 nEndAsciiIndex = nBeginBinaryIndex-1; 3420 for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it ) 3421 ; 3422 if( it != aSections.end() ) 3423 nEndAsciiIndex = (*it)-1; 3424 3425 nLength1 = nEndAsciiIndex+1; // including the last character 3426 for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it ) 3427 nLength1 -= 6; // decrease by pfb section size 3428 3429 // if the first four bytes are all ascii hex characters, then binary data 3430 // has to be converted to real binary data 3431 for( nIndex = 0; nIndex < 4 && 3432 ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) || 3433 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) || 3434 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' ) 3435 ); ++nIndex ) 3436 ; 3437 bool bConvertHexData = true; 3438 if( nIndex < 4 ) 3439 { 3440 bConvertHexData = false; 3441 nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte 3442 for( it = aSections.begin(); it != aSections.end(); ++it ) 3443 if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex ) 3444 nLength2 -= 6; 3445 } 3446 else 3447 { 3448 // count the hex ascii characters to get nLength2 3449 nLength2 = 0; 3450 int nNextSectionIndex = 0; 3451 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) 3452 ; 3453 if( it != aSections.end() ) 3454 nNextSectionIndex = *it; 3455 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) 3456 { 3457 if( nIndex == nNextSectionIndex ) 3458 { 3459 nIndex += 6; 3460 ++it; 3461 nNextSectionIndex = (it == aSections.end() ? 0 : *it ); 3462 } 3463 if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) || 3464 ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) || 3465 ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) ) 3466 nLength2++; 3467 } 3468 DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" ); 3469 nLength2 /= 2; 3470 } 3471 3472 // now we can actually write the font stream ! 3473 #if OSL_DEBUG_LEVEL > 1 3474 emitComment( " PDFWriterImpl::emitEmbeddedFont" ); 3475 #endif 3476 OStringBuffer aLine( 512 ); 3477 nStreamObject = createObject(); 3478 if( !updateObject(nStreamObject)) 3479 throw FontException(); 3480 sal_Int32 nStreamLengthObject = createObject(); 3481 aLine.append( nStreamObject ); 3482 aLine.append( " 0 obj\n" 3483 "<</Length " ); 3484 aLine.append( nStreamLengthObject ); 3485 aLine.append( " 0 R" 3486 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3487 "/Filter/FlateDecode" 3488 #endif 3489 "/Length1 " ); 3490 aLine.append( nLength1 ); 3491 aLine.append( " /Length2 " ); 3492 aLine.append( nLength2 ); 3493 aLine.append( " /Length3 "); 3494 aLine.append( nLength3 ); 3495 aLine.append( ">>\n" 3496 "stream\n" ); 3497 if( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3498 throw FontException(); 3499 3500 sal_uInt64 nBeginStreamPos = 0; 3501 osl_getFilePos( m_aFile, &nBeginStreamPos ); 3502 3503 beginCompression(); 3504 checkAndEnableStreamEncryption( nStreamObject ); 3505 3506 // write ascii section 3507 if( aSections.begin() == aSections.end() ) 3508 { 3509 if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) ) 3510 throw FontException(); 3511 } 3512 else 3513 { 3514 // first section always starts at 0 3515 it = aSections.begin(); 3516 nIndex = (*it)+6; 3517 ++it; 3518 while( *it < nEndAsciiIndex ) 3519 { 3520 if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) ) 3521 throw FontException(); 3522 nIndex = (*it)+6; 3523 ++it; 3524 } 3525 // write partial last section 3526 if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) ) 3527 throw FontException(); 3528 } 3529 3530 // write binary section 3531 if( ! bConvertHexData ) 3532 { 3533 if( aSections.begin() == aSections.end() ) 3534 { 3535 if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) ) 3536 throw FontException(); 3537 } 3538 else 3539 { 3540 for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it ) 3541 ; 3542 // write first partial section 3543 if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) ) 3544 throw FontException(); 3545 // write following sections 3546 while( it != aSections.end() ) 3547 { 3548 nIndex = (*it)+6; 3549 ++it; 3550 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes 3551 { 3552 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; 3553 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) 3554 throw FontException(); 3555 } 3556 } 3557 } 3558 } 3559 else 3560 { 3561 boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] ); 3562 rtl_zeroMemory( pWriteBuffer.get(), nLength2 ); 3563 int nWriteIndex = 0; 3564 3565 int nNextSectionIndex = 0; 3566 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) 3567 ; 3568 if( it != aSections.end() ) 3569 nNextSectionIndex = *it; 3570 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) 3571 { 3572 if( nIndex == nNextSectionIndex ) 3573 { 3574 nIndex += 6; 3575 ++it; 3576 nNextSectionIndex = (it == aSections.end() ? nFontLen : *it ); 3577 } 3578 unsigned char cNibble = 0x80; 3579 if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) 3580 cNibble = pFontData[nIndex] - '0'; 3581 else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) 3582 cNibble = pFontData[nIndex] - 'a' + 10; 3583 else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) 3584 cNibble = pFontData[nIndex] - 'A' + 10; 3585 if( cNibble != 0x80 ) 3586 { 3587 if( !(nWriteIndex & 1 ) ) 3588 cNibble <<= 4; 3589 pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble; 3590 nWriteIndex++; 3591 } 3592 } 3593 if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) ) 3594 throw FontException(); 3595 if( aSections.empty() ) 3596 { 3597 if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) ) 3598 throw FontException(); 3599 } 3600 else 3601 { 3602 // write rest of this section 3603 if( nIndex < nNextSectionIndex ) 3604 { 3605 if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) ) 3606 throw FontException(); 3607 } 3608 // write following sections 3609 while( it != aSections.end() ) 3610 { 3611 nIndex = (*it)+6; 3612 ++it; 3613 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes 3614 { 3615 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; 3616 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) 3617 throw FontException(); 3618 } 3619 } 3620 } 3621 } 3622 endCompression(); 3623 disableStreamEncryption(); 3624 3625 3626 sal_uInt64 nEndStreamPos = 0; 3627 osl_getFilePos( m_aFile, &nEndStreamPos ); 3628 3629 // and finally close the stream 3630 aLine.setLength( 0 ); 3631 aLine.append( "\nendstream\nendobj\n\n" ); 3632 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3633 throw FontException(); 3634 3635 // write stream length object 3636 aLine.setLength( 0 ); 3637 if( ! updateObject( nStreamLengthObject ) ) 3638 throw FontException(); 3639 aLine.append( nStreamLengthObject ); 3640 aLine.append( " 0 obj\n" ); 3641 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) ); 3642 aLine.append( "\nendobj\n\n" ); 3643 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3644 throw FontException(); 3645 } 3646 else 3647 { 3648 rtl::OStringBuffer aErrorComment( 256 ); 3649 aErrorComment.append( "GetEmbedFontData failed for font \"" ); 3650 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); 3651 aErrorComment.append( '\"' ); 3652 if( pFont->GetSlant() == ITALIC_NORMAL ) 3653 aErrorComment.append( " italic" ); 3654 else if( pFont->GetSlant() == ITALIC_OBLIQUE ) 3655 aErrorComment.append( " oblique" ); 3656 aErrorComment.append( " weight=" ); 3657 aErrorComment.append( sal_Int32(pFont->GetWeight()) ); 3658 emitComment( aErrorComment.getStr() ); 3659 } 3660 3661 if( nStreamObject ) 3662 // write font descriptor 3663 nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject ); 3664 3665 if( nFontDescriptor ) 3666 { 3667 if( pEncoding ) 3668 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, sizeof(nEncoding)/sizeof(nEncoding[0]) ); 3669 3670 // write font object 3671 sal_Int32 nObject = createObject(); 3672 if( ! updateObject( nObject ) ) 3673 throw FontException(); 3674 3675 OStringBuffer aLine( 1024 ); 3676 aLine.append( nObject ); 3677 aLine.append( " 0 obj\n" 3678 "<</Type/Font/Subtype/Type1/BaseFont/" ); 3679 appendName( aInfo.m_aPSName, aLine ); 3680 aLine.append( "\n" ); 3681 if( !pFont->mbSymbolFlag && pEncoding == 0 ) 3682 aLine.append( "/Encoding/WinAnsiEncoding\n" ); 3683 if( nToUnicodeStream ) 3684 { 3685 aLine.append( "/ToUnicode " ); 3686 aLine.append( nToUnicodeStream ); 3687 aLine.append( " 0 R\n" ); 3688 } 3689 aLine.append( "/FirstChar 0 /LastChar 255\n" 3690 "/Widths[" ); 3691 for( int i = 0; i < 256; i++ ) 3692 { 3693 aLine.append( pWidths[i] ); 3694 aLine.append( ((i&15) == 15) ? "\n" : " " ); 3695 } 3696 aLine.append( "]\n" 3697 "/FontDescriptor " ); 3698 aLine.append( nFontDescriptor ); 3699 aLine.append( " 0 R>>\n" 3700 "endobj\n\n" ); 3701 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3702 throw FontException(); 3703 3704 nFontObject = nObject; 3705 3706 aRet[ rEmbed.m_nNormalFontID ] = nObject; 3707 3708 // write additional encodings 3709 for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it ) 3710 { 3711 sal_Int32 aEncWidths[ 256 ]; 3712 // emit encoding dict 3713 sal_Int32 nEncObject = createObject(); 3714 if( ! updateObject( nEncObject ) ) 3715 throw FontException(); 3716 3717 OutputDevice* pRef = getReferenceDevice(); 3718 pRef->Push( PUSH_FONT | PUSH_MAPMODE ); 3719 pRef->SetMapMode( MapMode( MAP_PIXEL ) ); 3720 Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) ); 3721 aFont.SetWeight( pFont->GetWeight() ); 3722 aFont.SetItalic( pFont->GetSlant() ); 3723 aFont.SetPitch( pFont->GetPitch() ); 3724 pRef->SetFont( aFont ); 3725 pRef->ImplNewFont(); 3726 3727 aLine.setLength( 0 ); 3728 aLine.append( nEncObject ); 3729 aLine.append( " 0 obj\n" 3730 "<</Type/Encoding/Differences[ 0\n" ); 3731 int nEncoded = 0; 3732 aUnicodes.clear(); 3733 for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it ) 3734 { 3735 String aStr( str_it->m_aUnicode ); 3736 aEncWidths[nEncoded] = pRef->GetTextWidth( aStr ); 3737 nEncodedCodes[nEncoded] = str_it->m_aUnicode; 3738 nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded); 3739 pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size()); 3740 aUnicodes.push_back( nEncodedCodes[nEncoded] ); 3741 pUnicodesPerGlyph[nEncoded] = 1; 3742 3743 aLine.append( " /" ); 3744 aLine.append( str_it->m_aName ); 3745 if( !((++nEncoded) & 15) ) 3746 aLine.append( "\n" ); 3747 } 3748 aLine.append( "]>>\n" 3749 "endobj\n\n" ); 3750 3751 pRef->Pop(); 3752 3753 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3754 throw FontException(); 3755 3756 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded ); 3757 3758 nObject = createObject(); 3759 if( ! updateObject( nObject ) ) 3760 throw FontException(); 3761 3762 aLine.setLength( 0 ); 3763 aLine.append( nObject ); 3764 aLine.append( " 0 obj\n" 3765 "<</Type/Font/Subtype/Type1/BaseFont/" ); 3766 appendName( aInfo.m_aPSName, aLine ); 3767 aLine.append( "\n" ); 3768 aLine.append( "/Encoding " ); 3769 aLine.append( nEncObject ); 3770 aLine.append( " 0 R\n" ); 3771 if( nToUnicodeStream ) 3772 { 3773 aLine.append( "/ToUnicode " ); 3774 aLine.append( nToUnicodeStream ); 3775 aLine.append( " 0 R\n" ); 3776 } 3777 aLine.append( "/FirstChar 0\n" 3778 "/LastChar " ); 3779 aLine.append( (sal_Int32)(nEncoded-1) ); 3780 aLine.append( "\n" 3781 "/Widths[" ); 3782 for( int i = 0; i < nEncoded; i++ ) 3783 { 3784 aLine.append( aEncWidths[i] ); 3785 aLine.append( ((i&15) == 15) ? "\n" : " " ); 3786 } 3787 aLine.append( " ]\n" 3788 "/FontDescriptor " ); 3789 aLine.append( nFontDescriptor ); 3790 aLine.append( " 0 R>>\n" 3791 "endobj\n\n" ); 3792 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3793 throw FontException(); 3794 3795 aRet[ enc_it->m_nFontID ] = nObject; 3796 } 3797 } 3798 } 3799 catch( FontException& ) 3800 { 3801 // these do nothing in case there was no compression or encryption ongoing 3802 endCompression(); 3803 disableStreamEncryption(); 3804 } 3805 3806 if( pFontData ) 3807 m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen ); 3808 3809 return aRet; 3810 } 3811 3812 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer ) 3813 { 3814 if( nSubsetID ) 3815 { 3816 for( int i = 0; i < 6; i++ ) 3817 { 3818 int nOffset = (nSubsetID % 26); 3819 nSubsetID /= 26; 3820 rBuffer.append( (sal_Char)('A'+nOffset) ); 3821 } 3822 rBuffer.append( '+' ); 3823 } 3824 appendName( rPSName, rBuffer ); 3825 } 3826 3827 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, 3828 sal_Ucs* pUnicodes, 3829 sal_Int32* pUnicodesPerGlyph, 3830 sal_Int32* pEncToUnicodeIndex, 3831 int nGlyphs ) 3832 { 3833 int nMapped = 0, n = 0; 3834 for( n = 0; n < nGlyphs; n++ ) 3835 if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] ) 3836 nMapped++; 3837 3838 if( nMapped == 0 ) 3839 return 0; 3840 3841 sal_Int32 nStream = createObject(); 3842 CHECK_RETURN( updateObject( nStream ) ); 3843 3844 OStringBuffer aContents( 1024 ); 3845 aContents.append( 3846 "/CIDInit/ProcSet findresource begin\n" 3847 "12 dict begin\n" 3848 "begincmap\n" 3849 "/CIDSystemInfo<<\n" 3850 "/Registry (Adobe)\n" 3851 "/Ordering (UCS)\n" 3852 "/Supplement 0\n" 3853 ">> def\n" 3854 "/CMapName/Adobe-Identity-UCS def\n" 3855 "/CMapType 2 def\n" 3856 "1 begincodespacerange\n" 3857 "<00> <FF>\n" 3858 "endcodespacerange\n" 3859 ); 3860 int nCount = 0; 3861 for( n = 0; n < nGlyphs; n++ ) 3862 { 3863 if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] ) 3864 { 3865 if( (nCount % 100) == 0 ) 3866 { 3867 if( nCount ) 3868 aContents.append( "endbfchar\n" ); 3869 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) ); 3870 aContents.append( " beginbfchar\n" ); 3871 } 3872 aContents.append( '<' ); 3873 appendHex( (sal_Int8)pEncoding[n], aContents ); 3874 aContents.append( "> <" ); 3875 // TODO: handle unicodes>U+FFFF 3876 sal_Int32 nIndex = pEncToUnicodeIndex[n]; 3877 for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ ) 3878 { 3879 appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents ); 3880 appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents ); 3881 } 3882 aContents.append( ">\n" ); 3883 nCount++; 3884 } 3885 } 3886 aContents.append( "endbfchar\n" 3887 "endcmap\n" 3888 "CMapName currentdict /CMap defineresource pop\n" 3889 "end\n" 3890 "end\n" ); 3891 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3892 ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 ); 3893 SvMemoryStream aStream; 3894 pCodec->BeginCompression(); 3895 pCodec->Write( aStream, (const sal_uInt8*)aContents.getStr(), aContents.getLength() ); 3896 pCodec->EndCompression(); 3897 delete pCodec; 3898 #endif 3899 3900 #if OSL_DEBUG_LEVEL > 1 3901 emitComment( "PDFWriterImpl::createToUnicodeCMap" ); 3902 #endif 3903 OStringBuffer aLine( 40 ); 3904 3905 aLine.append( nStream ); 3906 aLine.append( " 0 obj\n<</Length " ); 3907 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3908 sal_Int32 nLen = (sal_Int32)aStream.Tell(); 3909 aStream.Seek( 0 ); 3910 aLine.append( nLen ); 3911 aLine.append( "/Filter/FlateDecode" ); 3912 #else 3913 aLine.append( aContents.getLength() ); 3914 #endif 3915 aLine.append( ">>\nstream\n" ); 3916 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3917 checkAndEnableStreamEncryption( nStream ); 3918 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3919 CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) ); 3920 #else 3921 CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) ); 3922 #endif 3923 disableStreamEncryption(); 3924 aLine.setLength( 0 ); 3925 aLine.append( "\nendstream\n" 3926 "endobj\n\n" ); 3927 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3928 return nStream; 3929 } 3930 3931 sal_Int32 PDFWriterImpl::emitFontDescriptor( const ImplFontData* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream ) 3932 { 3933 OStringBuffer aLine( 1024 ); 3934 // get font flags, see PDF reference 1.4 p. 358 3935 // possibly characters outside Adobe standard encoding 3936 // so set Symbolic flag 3937 sal_Int32 nFontFlags = (1<<2); 3938 if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE ) 3939 nFontFlags |= (1 << 6); 3940 if( pFont->GetPitch() == PITCH_FIXED ) 3941 nFontFlags |= 1; 3942 if( pFont->GetFamilyType() == FAMILY_SCRIPT ) 3943 nFontFlags |= (1 << 3); 3944 else if( pFont->GetFamilyType() == FAMILY_ROMAN ) 3945 nFontFlags |= (1 << 1); 3946 3947 sal_Int32 nFontDescriptor = createObject(); 3948 CHECK_RETURN( updateObject( nFontDescriptor ) ); 3949 aLine.setLength( 0 ); 3950 aLine.append( nFontDescriptor ); 3951 aLine.append( " 0 obj\n" 3952 "<</Type/FontDescriptor/FontName/" ); 3953 appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine ); 3954 aLine.append( "\n" 3955 "/Flags " ); 3956 aLine.append( nFontFlags ); 3957 aLine.append( "\n" 3958 "/FontBBox[" ); 3959 // note: Top and Bottom are reversed in VCL and PDF rectangles 3960 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() ); 3961 aLine.append( ' ' ); 3962 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() ); 3963 aLine.append( ' ' ); 3964 aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() ); 3965 aLine.append( ' ' ); 3966 aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) ); 3967 aLine.append( "]/ItalicAngle " ); 3968 if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL ) 3969 aLine.append( "-30" ); 3970 else 3971 aLine.append( "0" ); 3972 aLine.append( "\n" 3973 "/Ascent " ); 3974 aLine.append( (sal_Int32)rInfo.m_nAscent ); 3975 aLine.append( "\n" 3976 "/Descent " ); 3977 aLine.append( (sal_Int32)-rInfo.m_nDescent ); 3978 aLine.append( "\n" 3979 "/CapHeight " ); 3980 aLine.append( (sal_Int32)rInfo.m_nCapHeight ); 3981 // According to PDF reference 1.4 StemV is required 3982 // seems a tad strange to me, but well ... 3983 aLine.append( "\n" 3984 "/StemV 80\n" ); 3985 if( nFontStream ) 3986 { 3987 aLine.append( "/FontFile" ); 3988 switch( rInfo.m_nFontType ) 3989 { 3990 case FontSubsetInfo::SFNT_TTF: 3991 aLine.append( '2' ); 3992 break; 3993 case FontSubsetInfo::TYPE1_PFA: 3994 case FontSubsetInfo::TYPE1_PFB: 3995 case FontSubsetInfo::ANY_TYPE1: 3996 break; 3997 default: 3998 DBG_ERROR( "unknown fonttype in PDF font descriptor" ); 3999 return 0; 4000 } 4001 aLine.append( ' ' ); 4002 aLine.append( nFontStream ); 4003 aLine.append( " 0 R\n" ); 4004 } 4005 aLine.append( ">>\n" 4006 "endobj\n\n" ); 4007 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4008 4009 return nFontDescriptor; 4010 } 4011 4012 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const 4013 { 4014 for( std::map< sal_Int32, sal_Int32 >::const_iterator it = 4015 m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it ) 4016 { 4017 rDict.append( m_aBuiltinFonts[it->first].getNameObject() ); 4018 rDict.append( ' ' ); 4019 rDict.append( it->second ); 4020 rDict.append( " 0 R" ); 4021 } 4022 } 4023 4024 bool PDFWriterImpl::emitFonts() 4025 { 4026 if( ! m_pReferenceDevice->ImplGetGraphics() ) 4027 return false; 4028 4029 OStringBuffer aLine( 1024 ); 4030 4031 std::map< sal_Int32, sal_Int32 > aFontIDToObject; 4032 4033 OUString aTmpName; 4034 osl_createTempFile( NULL, NULL, &aTmpName.pData ); 4035 for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it ) 4036 { 4037 for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit ) 4038 { 4039 sal_Int32 pGlyphIDs[ 256 ]; 4040 sal_Int32 pWidths[ 256 ]; 4041 sal_uInt8 pEncoding[ 256 ]; 4042 sal_Int32 pEncToUnicodeIndex[ 256 ]; 4043 sal_Int32 pUnicodesPerGlyph[ 256 ]; 4044 std::vector<sal_Ucs> aUnicodes; 4045 aUnicodes.reserve( 256 ); 4046 int nGlyphs = 1; 4047 // fill arrays and prepare encoding index map 4048 sal_Int32 nToUnicodeStream = 0; 4049 4050 rtl_zeroMemory( pGlyphIDs, sizeof( pGlyphIDs ) ); 4051 rtl_zeroMemory( pEncoding, sizeof( pEncoding ) ); 4052 rtl_zeroMemory( pUnicodesPerGlyph, sizeof( pUnicodesPerGlyph ) ); 4053 rtl_zeroMemory( pEncToUnicodeIndex, sizeof( pEncToUnicodeIndex ) ); 4054 for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit ) 4055 { 4056 sal_uInt8 nEnc = fit->second.getGlyphId(); 4057 4058 DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" ); 4059 DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" ); 4060 4061 pGlyphIDs[ nEnc ] = fit->first; 4062 pEncoding[ nEnc ] = nEnc; 4063 pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size()); 4064 pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes(); 4065 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ ) 4066 aUnicodes.push_back( fit->second.getCode( n ) ); 4067 if( fit->second.getCode(0) ) 4068 nToUnicodeStream = 1; 4069 if( nGlyphs < 256 ) 4070 nGlyphs++; 4071 else 4072 { 4073 DBG_ERROR( "too many glyphs for subset" ); 4074 } 4075 } 4076 FontSubsetInfo aSubsetInfo; 4077 if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) ) 4078 { 4079 // create font stream 4080 oslFileHandle aFontFile; 4081 CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) ); 4082 // get file size 4083 sal_uInt64 nLength1; 4084 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) ); 4085 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength1 ) ) ); 4086 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); 4087 4088 #if OSL_DEBUG_LEVEL > 1 4089 emitComment( "PDFWriterImpl::emitFonts" ); 4090 #endif 4091 sal_Int32 nFontStream = createObject(); 4092 sal_Int32 nStreamLengthObject = createObject(); 4093 CHECK_RETURN( updateObject( nFontStream ) ); 4094 aLine.setLength( 0 ); 4095 aLine.append( nFontStream ); 4096 aLine.append( " 0 obj\n" 4097 "<</Length " ); 4098 aLine.append( (sal_Int32)nStreamLengthObject ); 4099 aLine.append( " 0 R" 4100 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 4101 "/Filter/FlateDecode" 4102 #endif 4103 "/Length1 " ); 4104 4105 sal_uInt64 nStartPos = 0; 4106 if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF ) 4107 { 4108 aLine.append( (sal_Int32)nLength1 ); 4109 4110 aLine.append( ">>\n" 4111 "stream\n" ); 4112 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4113 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); 4114 4115 // copy font file 4116 beginCompression(); 4117 checkAndEnableStreamEncryption( nFontStream ); 4118 sal_Bool bEOF = sal_False; 4119 do 4120 { 4121 char buf[8192]; 4122 sal_uInt64 nRead; 4123 CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) ); 4124 CHECK_RETURN( writeBuffer( buf, nRead ) ); 4125 CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) ); 4126 } while( ! bEOF ); 4127 } 4128 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 ) 4129 { 4130 // TODO: implement 4131 DBG_ERROR( "PDFWriterImpl does not support CFF-font subsets yet!" ); 4132 } 4133 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA? 4134 { 4135 boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] ); 4136 4137 sal_uInt64 nBytesRead = 0; 4138 CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer.get(), nLength1, &nBytesRead ) ) ); 4139 DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" ); 4140 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); 4141 // get the PFB-segment lengths 4142 ThreeInts aSegmentLengths = {0,0,0}; 4143 getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths ); 4144 // the lengths below are mandatory for PDF-exported Type1 fonts 4145 // because the PFB segment headers get stripped! WhyOhWhy. 4146 aLine.append( (sal_Int32)aSegmentLengths[0] ); 4147 aLine.append( "/Length2 " ); 4148 aLine.append( (sal_Int32)aSegmentLengths[1] ); 4149 aLine.append( "/Length3 " ); 4150 aLine.append( (sal_Int32)aSegmentLengths[2] ); 4151 4152 aLine.append( ">>\n" 4153 "stream\n" ); 4154 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4155 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); 4156 4157 // emit PFB-sections without section headers 4158 beginCompression(); 4159 checkAndEnableStreamEncryption( nFontStream ); 4160 CHECK_RETURN( writeBuffer( &pBuffer[6], aSegmentLengths[0] ) ); 4161 CHECK_RETURN( writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) ); 4162 CHECK_RETURN( writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ); 4163 } 4164 else 4165 { 4166 fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType); 4167 aLine.append( "0 >>\nstream\n" ); 4168 } 4169 4170 endCompression(); 4171 disableStreamEncryption(); 4172 // close the file 4173 osl_closeFile( aFontFile ); 4174 4175 sal_uInt64 nEndPos = 0; 4176 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) ); 4177 // end the stream 4178 aLine.setLength( 0 ); 4179 aLine.append( "\nendstream\nendobj\n\n" ); 4180 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4181 4182 // emit stream length object 4183 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 4184 aLine.setLength( 0 ); 4185 aLine.append( nStreamLengthObject ); 4186 aLine.append( " 0 obj\n" ); 4187 aLine.append( (sal_Int64)(nEndPos-nStartPos) ); 4188 aLine.append( "\nendobj\n\n" ); 4189 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4190 4191 // write font descriptor 4192 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream ); 4193 4194 if( nToUnicodeStream ) 4195 nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs ); 4196 4197 sal_Int32 nFontObject = createObject(); 4198 CHECK_RETURN( updateObject( nFontObject ) ); 4199 aLine.setLength( 0 ); 4200 aLine.append( nFontObject ); 4201 4202 aLine.append( " 0 obj\n" ); 4203 aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ? 4204 "<</Type/Font/Subtype/Type1/BaseFont/" : 4205 "<</Type/Font/Subtype/TrueType/BaseFont/" ); 4206 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine ); 4207 aLine.append( "\n" 4208 "/FirstChar 0\n" 4209 "/LastChar " ); 4210 aLine.append( (sal_Int32)(nGlyphs-1) ); 4211 aLine.append( "\n" 4212 "/Widths[" ); 4213 for( int i = 0; i < nGlyphs; i++ ) 4214 { 4215 aLine.append( pWidths[ i ] ); 4216 aLine.append( ((i & 15) == 15) ? "\n" : " " ); 4217 } 4218 aLine.append( "]\n" 4219 "/FontDescriptor " ); 4220 aLine.append( nFontDescriptor ); 4221 aLine.append( " 0 R\n" ); 4222 if( nToUnicodeStream ) 4223 { 4224 aLine.append( "/ToUnicode " ); 4225 aLine.append( nToUnicodeStream ); 4226 aLine.append( " 0 R\n" ); 4227 } 4228 aLine.append( ">>\n" 4229 "endobj\n\n" ); 4230 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4231 4232 aFontIDToObject[ lit->m_nFontID ] = nFontObject; 4233 } 4234 else 4235 { 4236 const ImplFontData* pFont = it->first; 4237 rtl::OStringBuffer aErrorComment( 256 ); 4238 aErrorComment.append( "CreateFontSubset failed for font \"" ); 4239 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); 4240 aErrorComment.append( '\"' ); 4241 if( pFont->GetSlant() == ITALIC_NORMAL ) 4242 aErrorComment.append( " italic" ); 4243 else if( pFont->GetSlant() == ITALIC_OBLIQUE ) 4244 aErrorComment.append( " oblique" ); 4245 aErrorComment.append( " weight=" ); 4246 aErrorComment.append( sal_Int32(pFont->GetWeight()) ); 4247 emitComment( aErrorComment.getStr() ); 4248 } 4249 } 4250 } 4251 osl_removeFile( aTmpName.pData ); 4252 4253 // emit embedded fonts 4254 for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit ) 4255 { 4256 std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second ); 4257 for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit ) 4258 { 4259 CHECK_RETURN( fit->second ); 4260 aFontIDToObject[ fit->first ] = fit->second; 4261 } 4262 } 4263 4264 // emit system fonts 4265 for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit ) 4266 { 4267 std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second ); 4268 for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit ) 4269 { 4270 CHECK_RETURN( fit->second ); 4271 aFontIDToObject[ fit->first ] = fit->second; 4272 } 4273 } 4274 4275 OStringBuffer aFontDict( 1024 ); 4276 aFontDict.append( getFontDictObject() ); 4277 aFontDict.append( " 0 obj\n" 4278 "<<" ); 4279 int ni = 0; 4280 for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit ) 4281 { 4282 aFontDict.append( "/F" ); 4283 aFontDict.append( mit->first ); 4284 aFontDict.append( ' ' ); 4285 aFontDict.append( mit->second ); 4286 aFontDict.append( " 0 R" ); 4287 if( ((++ni) & 7) == 0 ) 4288 aFontDict.append( '\n' ); 4289 } 4290 // emit builtin font for widget apperances / variable text 4291 for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin(); 4292 it != m_aBuiltinFontToObjectMap.end(); ++it ) 4293 { 4294 ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]); 4295 it->second = emitBuiltinFont( &aData, it->second ); 4296 } 4297 appendBuiltinFontsToDict( aFontDict ); 4298 aFontDict.append( "\n>>\nendobj\n\n" ); 4299 4300 CHECK_RETURN( updateObject( getFontDictObject() ) ); 4301 CHECK_RETURN( writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) ); 4302 return true; 4303 } 4304 4305 sal_Int32 PDFWriterImpl::emitResources() 4306 { 4307 // emit shadings 4308 if( ! m_aGradients.empty() ) 4309 CHECK_RETURN( emitGradients() ); 4310 // emit tilings 4311 if( ! m_aTilings.empty() ) 4312 CHECK_RETURN( emitTilings() ); 4313 4314 // emit font dict 4315 CHECK_RETURN( emitFonts() ); 4316 4317 // emit Resource dict 4318 OStringBuffer aLine( 512 ); 4319 sal_Int32 nResourceDict = getResourceDictObj(); 4320 CHECK_RETURN( updateObject( nResourceDict ) ); 4321 aLine.setLength( 0 ); 4322 aLine.append( nResourceDict ); 4323 aLine.append( " 0 obj\n" ); 4324 m_aGlobalResourceDict.append( aLine, getFontDictObject() ); 4325 aLine.append( "endobj\n\n" ); 4326 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4327 return nResourceDict; 4328 } 4329 4330 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts, 4331 sal_Int32 nItemLevel, 4332 sal_Int32 nCurrentItemId ) 4333 { 4334 /* The /Count number of an item is 4335 positive: the number of visible subitems 4336 negative: the negative number of subitems that will become visible if 4337 the item gets opened 4338 see PDF ref 1.4 p 478 4339 */ 4340 4341 sal_Int32 nCount = 0; 4342 4343 if( m_aContext.OpenBookmarkLevels < 0 || // all levels arevisible 4344 m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible 4345 ) 4346 { 4347 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ]; 4348 sal_Int32 nChildren = rItem.m_aChildren.size(); 4349 for( sal_Int32 i = 0; i < nChildren; i++ ) 4350 nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] ); 4351 rCounts[nCurrentItemId] = nCount; 4352 // return 1 (this item) + visible sub items 4353 if( nCount < 0 ) 4354 nCount = 0; 4355 nCount++; 4356 } 4357 else 4358 { 4359 // this bookmark level is invisible 4360 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ]; 4361 sal_Int32 nChildren = rItem.m_aChildren.size(); 4362 rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size()); 4363 for( sal_Int32 i = 0; i < nChildren; i++ ) 4364 updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] ); 4365 nCount = -1; 4366 } 4367 4368 return nCount; 4369 } 4370 4371 sal_Int32 PDFWriterImpl::emitOutline() 4372 { 4373 int i, nItems = m_aOutline.size(); 4374 4375 // do we have an outline at all ? 4376 if( nItems < 2 ) 4377 return 0; 4378 4379 // reserve object numbers for all outline items 4380 for( i = 0; i < nItems; ++i ) 4381 m_aOutline[i].m_nObject = createObject(); 4382 4383 // update all parent, next and prev object ids 4384 for( i = 0; i < nItems; ++i ) 4385 { 4386 PDFOutlineEntry& rItem = m_aOutline[i]; 4387 int nChildren = rItem.m_aChildren.size(); 4388 4389 if( nChildren ) 4390 { 4391 for( int n = 0; n < nChildren; ++n ) 4392 { 4393 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ]; 4394 4395 rChild.m_nParentObject = rItem.m_nObject; 4396 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0; 4397 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0; 4398 } 4399 4400 } 4401 } 4402 4403 // calculate Count entries for all items 4404 std::vector< sal_Int32 > aCounts( nItems ); 4405 updateOutlineItemCount( aCounts, 0, 0 ); 4406 4407 // emit hierarchy 4408 for( i = 0; i < nItems; ++i ) 4409 { 4410 PDFOutlineEntry& rItem = m_aOutline[i]; 4411 OStringBuffer aLine( 1024 ); 4412 4413 CHECK_RETURN( updateObject( rItem.m_nObject ) ); 4414 aLine.append( rItem.m_nObject ); 4415 aLine.append( " 0 obj\n" ); 4416 aLine.append( "<<" ); 4417 // number of visible children (all levels) 4418 if( i > 0 || aCounts[0] > 0 ) 4419 { 4420 aLine.append( "/Count " ); 4421 aLine.append( aCounts[i] ); 4422 } 4423 if( ! rItem.m_aChildren.empty() ) 4424 { 4425 // children list: First, Last 4426 aLine.append( "/First " ); 4427 aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject ); 4428 aLine.append( " 0 R/Last " ); 4429 aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject ); 4430 aLine.append( " 0 R\n" ); 4431 } 4432 if( i > 0 ) 4433 { 4434 // Title, Dest, Parent, Prev, Next 4435 aLine.append( "/Title" ); 4436 appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine ); 4437 aLine.append( "\n" ); 4438 // Dest is not required 4439 if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() ) 4440 { 4441 aLine.append( "/Dest" ); 4442 appendDest( rItem.m_nDestID, aLine ); 4443 } 4444 aLine.append( "/Parent " ); 4445 aLine.append( rItem.m_nParentObject ); 4446 aLine.append( " 0 R" ); 4447 if( rItem.m_nPrevObject ) 4448 { 4449 aLine.append( "/Prev " ); 4450 aLine.append( rItem.m_nPrevObject ); 4451 aLine.append( " 0 R" ); 4452 } 4453 if( rItem.m_nNextObject ) 4454 { 4455 aLine.append( "/Next " ); 4456 aLine.append( rItem.m_nNextObject ); 4457 aLine.append( " 0 R" ); 4458 } 4459 } 4460 aLine.append( ">>\nendobj\n\n" ); 4461 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4462 } 4463 4464 return m_aOutline[0].m_nObject; 4465 } 4466 4467 #undef CHECK_RETURN 4468 #define CHECK_RETURN( x ) if( !x ) return false 4469 4470 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer ) 4471 { 4472 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) 4473 { 4474 #if OSL_DEBUG_LEVEL > 1 4475 fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID ); 4476 #endif 4477 return false; 4478 } 4479 4480 4481 const PDFDest& rDest = m_aDests[ nDestID ]; 4482 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ]; 4483 4484 rBuffer.append( '[' ); 4485 rBuffer.append( rDestPage.m_nPageObject ); 4486 rBuffer.append( " 0 R" ); 4487 4488 switch( rDest.m_eType ) 4489 { 4490 case PDFWriter::XYZ: 4491 default: 4492 rBuffer.append( "/XYZ " ); 4493 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4494 rBuffer.append( ' ' ); 4495 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4496 rBuffer.append( " 0" ); 4497 break; 4498 case PDFWriter::Fit: 4499 rBuffer.append( "/Fit" ); 4500 break; 4501 case PDFWriter::FitRectangle: 4502 rBuffer.append( "/FitR " ); 4503 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4504 rBuffer.append( ' ' ); 4505 appendFixedInt( rDest.m_aRect.Top(), rBuffer ); 4506 rBuffer.append( ' ' ); 4507 appendFixedInt( rDest.m_aRect.Right(), rBuffer ); 4508 rBuffer.append( ' ' ); 4509 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4510 break; 4511 case PDFWriter::FitHorizontal: 4512 rBuffer.append( "/FitH " ); 4513 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4514 break; 4515 case PDFWriter::FitVertical: 4516 rBuffer.append( "/FitV " ); 4517 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4518 break; 4519 case PDFWriter::FitPageBoundingBox: 4520 rBuffer.append( "/FitB" ); 4521 break; 4522 case PDFWriter::FitPageBoundingBoxHorizontal: 4523 rBuffer.append( "/FitBH " ); 4524 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4525 break; 4526 case PDFWriter::FitPageBoundingBoxVertical: 4527 rBuffer.append( "/FitBV " ); 4528 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4529 break; 4530 } 4531 rBuffer.append( ']' ); 4532 4533 return true; 4534 } 4535 4536 bool PDFWriterImpl::emitLinkAnnotations() 4537 { 4538 int nAnnots = m_aLinks.size(); 4539 for( int i = 0; i < nAnnots; i++ ) 4540 { 4541 const PDFLink& rLink = m_aLinks[i]; 4542 if( ! updateObject( rLink.m_nObject ) ) 4543 continue; 4544 4545 OStringBuffer aLine( 1024 ); 4546 aLine.append( rLink.m_nObject ); 4547 aLine.append( " 0 obj\n" ); 4548 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' 4549 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 4550 aLine.append( "<</Type/Annot" ); 4551 if( m_bIsPDF_A1 ) 4552 aLine.append( "/F 4" ); 4553 aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" ); 4554 4555 appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle 4556 aLine.append( ' ' ); 4557 appendFixedInt( rLink.m_aRect.Top(), aLine ); 4558 aLine.append( ' ' ); 4559 appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle 4560 aLine.append( ' ' ); 4561 appendFixedInt( rLink.m_aRect.Bottom(), aLine ); 4562 aLine.append( "]" ); 4563 if( rLink.m_nDest >= 0 ) 4564 { 4565 aLine.append( "/Dest" ); 4566 appendDest( rLink.m_nDest, aLine ); 4567 } 4568 else 4569 { 4570 /*--->i56629 4571 destination is external to the document, so 4572 we check in the following sequence: 4573 4574 if target type is neither .pdf, nor .od[tpgs], then 4575 check if relative or absolute and act accordingly (use URI or 'launch application' as requested) 4576 end processing 4577 else if target is .od[tpgs]: then 4578 if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file 4579 processing continue 4580 4581 if (new)target is .pdf : then 4582 if GotToR is requested, then 4583 convert the target in GoToR where the fragment of the URI is 4584 considered the named destination in the target file, set relative or absolute as requested 4585 else strip the fragment from URL and then set URI or 'launch application' as requested 4586 */ 4587 // 4588 // FIXME: check if the decode mechanisms for URL processing throughout this implementation 4589 // are the correct one!! 4590 // 4591 // extract target file type 4592 INetURLObject aDocumentURL( m_aContext.BaseURL ); 4593 INetURLObject aTargetURL( rLink.m_aURL ); 4594 sal_Int32 nChangeFileExtensionToPDF = 0; 4595 sal_Int32 nSetGoToRMode = 0; 4596 sal_Bool bTargetHasPDFExtension = sal_False; 4597 INetProtocol eTargetProtocol = aTargetURL.GetProtocol(); 4598 sal_Bool bIsUNCPath = sal_False; 4599 // check if the protocol is a known one, or if there is no protocol at all (on target only) 4600 // if there is no protocol, make the target relative to the current document directory 4601 // getting the needed URL information from the current document path 4602 if( eTargetProtocol == INET_PROT_NOT_VALID ) 4603 { 4604 if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.compareToAscii( "\\\\\\\\", 4 ) == 0) 4605 { 4606 bIsUNCPath = sal_True; 4607 } 4608 else 4609 { 4610 INetURLObject aNewBase( aDocumentURL );//duplicate document URL 4611 aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the 4612 //target document 4613 aNewBase.insertName( rLink.m_aURL ); 4614 aTargetURL = aNewBase;//reassign the new target URL 4615 //recompute the target protocol, with the new URL 4616 //normal URL processing resumes 4617 eTargetProtocol = aTargetURL.GetProtocol(); 4618 } 4619 } 4620 4621 rtl::OUString aFileExtension = aTargetURL.GetFileExtension(); 4622 4623 // Check if the URL ends in '/': if yes it's a directory, 4624 // it will be forced to a URI link. 4625 // possibly a malformed URI, leave it as it is, force as URI 4626 if( aTargetURL.hasFinalSlash() ) 4627 m_aContext.DefaultLinkAction = PDFWriter::URIAction; 4628 4629 if( aFileExtension.getLength() > 0 ) 4630 { 4631 if( m_aContext.ConvertOOoTargetToPDFTarget ) 4632 { 4633 //examine the file type (.odm .odt. .odp, odg, ods) 4634 if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odm" ) ) ) ) 4635 nChangeFileExtensionToPDF++; 4636 if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odt" ) ) ) ) 4637 nChangeFileExtensionToPDF++; 4638 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odp" ) ) ) ) 4639 nChangeFileExtensionToPDF++; 4640 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odg" ) ) ) ) 4641 nChangeFileExtensionToPDF++; 4642 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ods" ) ) ) ) 4643 nChangeFileExtensionToPDF++; 4644 if( nChangeFileExtensionToPDF ) 4645 aTargetURL.setExtension(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) ); 4646 } 4647 //check if extension is pdf, see if GoToR should be forced 4648 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) ); 4649 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension ) 4650 nSetGoToRMode++; 4651 } 4652 //prepare the URL, if relative or not 4653 INetProtocol eBaseProtocol = aDocumentURL.GetProtocol(); 4654 //queue the string common to all types of actions 4655 aLine.append( "/A<</Type/Action/S"); 4656 if( bIsUNCPath ) // handle Win UNC paths 4657 { 4658 aLine.append( "/Launch/Win<</F" ); 4659 // INetURLObject is not good with UNC paths, use original path 4660 appendLiteralStringEncrypt( rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 4661 aLine.append( ">>" ); 4662 } 4663 else 4664 { 4665 bool bSetRelative = false; 4666 bool bFileSpec = false; 4667 //check if relative file link is requested and if the protocol is 'file://' 4668 if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE ) 4669 bSetRelative = true; 4670 4671 rtl::OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is, 4672 if( nSetGoToRMode == 0 ) 4673 { 4674 switch( m_aContext.DefaultLinkAction ) 4675 { 4676 default: 4677 case PDFWriter::URIAction : 4678 case PDFWriter::URIActionDestination : 4679 aLine.append( "/URI/URI" ); 4680 break; 4681 case PDFWriter::LaunchAction: 4682 // now: 4683 // if a launch action is requested and the hyperlink target has a fragment 4684 // and the target file does not have a pdf extension, or it's not a 'file:://' protocol 4685 // then force the uri action on it 4686 // This code will permit the correct opening of application on web pages, the one that 4687 // normally have fragments (but I may be wrong...) 4688 // and will force the use of URI when the protocol is not file:// 4689 if( (aFragment.getLength() > 0 && !bTargetHasPDFExtension) || 4690 eTargetProtocol != INET_PROT_FILE ) 4691 aLine.append( "/URI/URI" ); 4692 else 4693 { 4694 aLine.append( "/Launch/F" ); 4695 bFileSpec = true; 4696 } 4697 break; 4698 } 4699 } 4700 //fragment are encoded in the same way as in the named destination processing 4701 if( nSetGoToRMode ) 4702 {//add the fragment 4703 rtl::OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET ); 4704 aLine.append("/GoToR"); 4705 aLine.append("/F"); 4706 bFileSpec = true; 4707 appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark, 4708 INetURLObject::WAS_ENCODED, 4709 INetURLObject::DECODE_WITH_CHARSET ) : 4710 aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 4711 if( aFragment.getLength() > 0 ) 4712 { 4713 aLine.append("/D/"); 4714 appendDestinationName( aFragment , aLine ); 4715 } 4716 } 4717 else 4718 { 4719 // change the fragment to accomodate the bookmark (only if the file extension is PDF and 4720 // the requested action is of the correct type) 4721 if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination && 4722 bTargetHasPDFExtension && aFragment.getLength() > 0 ) 4723 { 4724 OStringBuffer aLineLoc( 1024 ); 4725 appendDestinationName( aFragment , aLineLoc ); 4726 //substitute the fragment 4727 aTargetURL.SetMark( aLineLoc.getStr() ); 4728 } 4729 rtl::OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE ); 4730 // check if we have a URL available, if the string is empty, set it as the original one 4731 // if( aURL.getLength() == 0 ) 4732 // appendLiteralStringEncrypt( rLink.m_aURL , rLink.m_nObject, aLine ); 4733 // else 4734 appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL, 4735 INetURLObject::WAS_ENCODED, 4736 bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE 4737 ) : 4738 aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 4739 } 4740 //<--- i56629 4741 } 4742 aLine.append( ">>\n" ); 4743 } 4744 if( rLink.m_nStructParent > 0 ) 4745 { 4746 aLine.append( "/StructParent " ); 4747 aLine.append( rLink.m_nStructParent ); 4748 } 4749 aLine.append( ">>\nendobj\n\n" ); 4750 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4751 } 4752 4753 return true; 4754 } 4755 4756 bool PDFWriterImpl::emitNoteAnnotations() 4757 { 4758 // emit note annotations 4759 int nAnnots = m_aNotes.size(); 4760 for( int i = 0; i < nAnnots; i++ ) 4761 { 4762 const PDFNoteEntry& rNote = m_aNotes[i]; 4763 if( ! updateObject( rNote.m_nObject ) ) 4764 return false; 4765 4766 OStringBuffer aLine( 1024 ); 4767 aLine.append( rNote.m_nObject ); 4768 aLine.append( " 0 obj\n" ); 4769 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' 4770 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 4771 aLine.append( "<</Type/Annot" ); 4772 if( m_bIsPDF_A1 ) 4773 aLine.append( "/F 4" ); 4774 aLine.append( "/Subtype/Text/Rect[" ); 4775 4776 appendFixedInt( rNote.m_aRect.Left(), aLine ); 4777 aLine.append( ' ' ); 4778 appendFixedInt( rNote.m_aRect.Top(), aLine ); 4779 aLine.append( ' ' ); 4780 appendFixedInt( rNote.m_aRect.Right(), aLine ); 4781 aLine.append( ' ' ); 4782 appendFixedInt( rNote.m_aRect.Bottom(), aLine ); 4783 aLine.append( "]" ); 4784 4785 // contents of the note (type text string) 4786 aLine.append( "/Contents\n" ); 4787 appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine ); 4788 aLine.append( "\n" ); 4789 4790 // optional title 4791 if( rNote.m_aContents.Title.Len() ) 4792 { 4793 aLine.append( "/T" ); 4794 appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine ); 4795 aLine.append( "\n" ); 4796 } 4797 4798 aLine.append( ">>\nendobj\n\n" ); 4799 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4800 } 4801 return true; 4802 } 4803 4804 Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font& rAppSetFont ) 4805 { 4806 bool bAdjustSize = false; 4807 4808 Font aFont( rControlFont ); 4809 if( ! aFont.GetName().Len() ) 4810 { 4811 aFont = rAppSetFont; 4812 if( rControlFont.GetHeight() ) 4813 aFont.SetSize( Size( 0, rControlFont.GetHeight() ) ); 4814 else 4815 bAdjustSize = true; 4816 if( rControlFont.GetItalic() != ITALIC_DONTKNOW ) 4817 aFont.SetItalic( rControlFont.GetItalic() ); 4818 if( rControlFont.GetWeight() != WEIGHT_DONTKNOW ) 4819 aFont.SetWeight( rControlFont.GetWeight() ); 4820 } 4821 else if( ! aFont.GetHeight() ) 4822 { 4823 aFont.SetSize( rAppSetFont.GetSize() ); 4824 bAdjustSize = true; 4825 } 4826 if( bAdjustSize ) 4827 { 4828 Size aFontSize = aFont.GetSize(); 4829 OutputDevice* pDefDev = Application::GetDefaultDevice(); 4830 aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() ); 4831 aFont.SetSize( aFontSize ); 4832 } 4833 return aFont; 4834 } 4835 4836 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont ) 4837 { 4838 sal_Int32 nBest = 4; // default to Helvetica 4839 OUString aFontName( rFont.GetName() ); 4840 aFontName = aFontName.toAsciiLowerCase(); 4841 4842 if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "times" ) ) ) != -1 ) 4843 nBest = 8; 4844 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "courier" ) ) ) != -1 ) 4845 nBest = 0; 4846 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "dingbats" ) ) ) != -1 ) 4847 nBest = 13; 4848 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "symbol" ) ) ) != -1 ) 4849 nBest = 12; 4850 if( nBest < 12 ) 4851 { 4852 if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL ) 4853 nBest += 1; 4854 if( rFont.GetWeight() > WEIGHT_MEDIUM ) 4855 nBest += 2; 4856 } 4857 4858 if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() ) 4859 m_aBuiltinFontToObjectMap[ nBest ] = createObject(); 4860 4861 return nBest; 4862 } 4863 4864 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 ) 4865 { 4866 return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1; 4867 } 4868 4869 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget ) 4870 { 4871 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 4872 4873 // save graphics state 4874 push( sal::static_int_cast<sal_uInt16>(~0U) ); 4875 4876 // transform relative to control's coordinates since an 4877 // appearance stream is a form XObject 4878 // this relies on the m_aRect member of rButton NOT already being transformed 4879 // to default user space 4880 if( rWidget.Background || rWidget.Border ) 4881 { 4882 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) ); 4883 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) ); 4884 drawRectangle( rWidget.Location ); 4885 } 4886 // prepare font to use 4887 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() ); 4888 setFont( aFont ); 4889 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) ); 4890 4891 drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle ); 4892 4893 // create DA string while local mapmode is still in place 4894 // (that is before endRedirect()) 4895 OStringBuffer aDA( 256 ); 4896 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA ); 4897 Font aDummyFont( String( RTL_CONSTASCII_USTRINGPARAM( "Helvetica" ) ), aFont.GetSize() ); 4898 sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont ); 4899 aDA.append( ' ' ); 4900 aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() ); 4901 aDA.append( ' ' ); 4902 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); 4903 aDA.append( " Tf" ); 4904 rButton.m_aDAString = aDA.makeStringAndClear(); 4905 4906 pop(); 4907 4908 rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream(); 4909 4910 /* seems like a bad hack but at least works in both AR5 and 6: 4911 we draw the button ourselves and tell AR 4912 the button would be totally transparent with no text 4913 4914 One would expect that simply setting a normal appearance 4915 should suffice, but no, as soon as the user actually presses 4916 the button and an action is tied to it (gasp! a button that 4917 does something) the appearance gets replaced by some crap that AR 4918 creates on the fly even if no DA or MK is given. On AR6 at least 4919 the DA and MK work as expected, but on AR5 this creates a region 4920 filled with the background color but nor text. Urgh. 4921 */ 4922 rButton.m_aMKDict = "/BC [] /BG [] /CA"; 4923 rButton.m_aMKDictCAString = ""; 4924 } 4925 4926 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern, 4927 const PDFWriter::AnyWidget& rWidget, 4928 const StyleSettings& rSettings ) 4929 { 4930 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() ); 4931 4932 if( rWidget.Background || rWidget.Border ) 4933 { 4934 if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) ) 4935 { 4936 sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500; 4937 if( nDelta < 1 ) 4938 nDelta = 1; 4939 setLineColor( Color( COL_TRANSPARENT ) ); 4940 Rectangle aRect = rIntern.m_aRect; 4941 setFillColor( rSettings.GetLightBorderColor() ); 4942 drawRectangle( aRect ); 4943 aRect.Left() += nDelta; aRect.Top() += nDelta; 4944 aRect.Right() -= nDelta; aRect.Bottom() -= nDelta; 4945 setFillColor( rSettings.GetFieldColor() ); 4946 drawRectangle( aRect ); 4947 setFillColor( rSettings.GetLightColor() ); 4948 drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) ); 4949 drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) ); 4950 setFillColor( rSettings.GetDarkShadowColor() ); 4951 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) ); 4952 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) ); 4953 } 4954 else 4955 { 4956 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) ); 4957 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 4958 drawRectangle( rIntern.m_aRect ); 4959 } 4960 4961 if( rWidget.Border ) 4962 { 4963 // adjust edit area accounting for border 4964 sal_Int32 nDelta = aFont.GetHeight()/4; 4965 if( nDelta < 1 ) 4966 nDelta = 1; 4967 rIntern.m_aRect.Left() += nDelta; 4968 rIntern.m_aRect.Top() += nDelta; 4969 rIntern.m_aRect.Right() -= nDelta; 4970 rIntern.m_aRect.Bottom()-= nDelta; 4971 } 4972 } 4973 return aFont; 4974 } 4975 4976 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget ) 4977 { 4978 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 4979 SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 ); 4980 4981 push( sal::static_int_cast<sal_uInt16>(~0U) ); 4982 4983 // prepare font to use, draw field border 4984 Font aFont = drawFieldBorder( rEdit, rWidget, rSettings ); 4985 sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont ); 4986 4987 // prepare DA string 4988 OStringBuffer aDA( 32 ); 4989 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA ); 4990 aDA.append( ' ' ); 4991 if( m_aContext.FieldsUseSystemFonts ) 4992 { 4993 aDA.append( "/F" ); 4994 aDA.append( nBest ); 4995 4996 OStringBuffer aDR( 32 ); 4997 aDR.append( "/Font " ); 4998 aDR.append( getFontDictObject() ); 4999 aDR.append( " 0 R" ); 5000 rEdit.m_aDRDict = aDR.makeStringAndClear(); 5001 } 5002 else 5003 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5004 aDA.append( ' ' ); 5005 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); 5006 aDA.append( " Tf" ); 5007 5008 /* create an empty appearance stream, let the viewer create 5009 the appearance at runtime. This is because AR5 seems to 5010 paint the widget appearance always, and a dynamically created 5011 appearance on top of it. AR6 is well behaved in that regard, so 5012 that behaviour seems to be a bug. Anyway this empty appearance 5013 relies on /NeedAppearances in the AcroForm dictionary set to "true" 5014 */ 5015 beginRedirect( pEditStream, rEdit.m_aRect ); 5016 OStringBuffer aAppearance( 32 ); 5017 aAppearance.append( "/Tx BMC\nEMC\n" ); 5018 writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); 5019 5020 endRedirect(); 5021 pop(); 5022 5023 rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream; 5024 5025 rEdit.m_aDAString = aDA.makeStringAndClear(); 5026 } 5027 5028 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget ) 5029 { 5030 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 5031 SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 ); 5032 5033 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5034 5035 // prepare font to use, draw field border 5036 Font aFont = drawFieldBorder( rBox, rWidget, rSettings ); 5037 sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont ); 5038 5039 beginRedirect( pListBoxStream, rBox.m_aRect ); 5040 OStringBuffer aAppearance( 64 ); 5041 5042 #if 0 5043 if( ! rWidget.DropDown ) 5044 { 5045 // prepare linewidth for DA string hack, see below 5046 Size aFontSize = lcl_convert( m_aGraphicsStack.front().m_aMapMode, 5047 m_aMapMode, 5048 getReferenceDevice(), 5049 Size( 0, aFont.GetHeight() ) ); 5050 sal_Int32 nLW = aFontSize.Height() / 40; 5051 appendFixedInt( nLW > 0 ? nLW : 1, aAppearance ); 5052 aAppearance.append( " w\n" ); 5053 writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); 5054 aAppearance.setLength( 0 ); 5055 } 5056 #endif 5057 5058 setLineColor( Color( COL_TRANSPARENT ) ); 5059 setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) ); 5060 drawRectangle( rBox.m_aRect ); 5061 5062 // empty appearance, see createDefaultEditAppearance for reference 5063 aAppearance.append( "/Tx BMC\nEMC\n" ); 5064 writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); 5065 5066 endRedirect(); 5067 pop(); 5068 5069 rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream; 5070 5071 // prepare DA string 5072 OStringBuffer aDA( 256 ); 5073 #if 0 5074 if( !rWidget.DropDown ) 5075 { 5076 /* another of AR5's peculiarities: the selected item of a choice 5077 field is highlighted using the non stroking color - same as the 5078 text color. so workaround that by using text rendering mode 2 5079 (fill, then stroke) and set the stroking color 5080 */ 5081 appendStrokingColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ), aDA ); 5082 aDA.append( " 2 Tr " ); 5083 } 5084 #endif 5085 // prepare DA string 5086 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA ); 5087 aDA.append( ' ' ); 5088 if( m_aContext.FieldsUseSystemFonts ) 5089 { 5090 aDA.append( "/F" ); 5091 aDA.append( nBest ); 5092 5093 OStringBuffer aDR( 32 ); 5094 aDR.append( "/Font " ); 5095 aDR.append( getFontDictObject() ); 5096 aDR.append( " 0 R" ); 5097 rBox.m_aDRDict = aDR.makeStringAndClear(); 5098 } 5099 else 5100 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5101 aDA.append( ' ' ); 5102 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); 5103 aDA.append( " Tf" ); 5104 rBox.m_aDAString = aDA.makeStringAndClear(); 5105 } 5106 5107 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget ) 5108 { 5109 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 5110 5111 // save graphics state 5112 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5113 5114 if( rWidget.Background || rWidget.Border ) 5115 { 5116 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) ); 5117 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 5118 drawRectangle( rBox.m_aRect ); 5119 } 5120 5121 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() ); 5122 setFont( aFont ); 5123 Size aFontSize = aFont.GetSize(); 5124 if( aFontSize.Height() > rBox.m_aRect.GetHeight() ) 5125 aFontSize.Height() = rBox.m_aRect.GetHeight(); 5126 sal_Int32 nDelta = aFontSize.Height()/10; 5127 if( nDelta < 1 ) 5128 nDelta = 1; 5129 5130 Rectangle aCheckRect, aTextRect; 5131 if( rWidget.ButtonIsLeft ) 5132 { 5133 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta; 5134 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5135 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5136 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5137 5138 // #i74206# handle small controls without text area 5139 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5140 { 5141 aCheckRect.Right() -= nDelta; 5142 aCheckRect.Top() += nDelta/2; 5143 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5144 } 5145 5146 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta; 5147 aTextRect.Top() = rBox.m_aRect.Top(); 5148 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5149 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5150 } 5151 else 5152 { 5153 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height(); 5154 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5155 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5156 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5157 5158 // #i74206# handle small controls without text area 5159 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5160 { 5161 aCheckRect.Left() += nDelta; 5162 aCheckRect.Top() += nDelta/2; 5163 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5164 } 5165 5166 aTextRect.Left() = rBox.m_aRect.Left(); 5167 aTextRect.Top() = rBox.m_aRect.Top(); 5168 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5169 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5170 } 5171 setLineColor( Color( COL_BLACK ) ); 5172 setFillColor( Color( COL_TRANSPARENT ) ); 5173 OStringBuffer aLW( 32 ); 5174 aLW.append( "q " ); 5175 m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW ); 5176 aLW.append( " w " ); 5177 writeBuffer( aLW.getStr(), aLW.getLength() ); 5178 drawRectangle( aCheckRect ); 5179 writeBuffer( " Q\n", 3 ); 5180 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 5181 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle ); 5182 5183 pop(); 5184 5185 OStringBuffer aDA( 256 ); 5186 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5187 sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) ); 5188 aDA.append( ' ' ); 5189 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5190 aDA.append( " 0 Tf" ); 5191 rBox.m_aDAString = aDA.makeStringAndClear(); 5192 rBox.m_aMKDict = "/CA"; 5193 rBox.m_aMKDictCAString = "8"; 5194 rBox.m_aRect = aCheckRect; 5195 5196 // create appearance streams 5197 sal_Char cMark = '8'; 5198 sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)]; 5199 nCharXOffset *= aCheckRect.GetHeight(); 5200 nCharXOffset /= 2000; 5201 sal_Int32 nCharYOffset = 1000- 5202 (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative 5203 nCharYOffset *= aCheckRect.GetHeight(); 5204 nCharYOffset /= 2000; 5205 5206 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 ); 5207 beginRedirect( pCheckStream, aCheckRect ); 5208 aDA.append( "/Tx BMC\nq BT\n" ); 5209 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5210 aDA.append( ' ' ); 5211 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5212 aDA.append( ' ' ); 5213 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA ); 5214 aDA.append( " Tf\n" ); 5215 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA ); 5216 aDA.append( " " ); 5217 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA ); 5218 aDA.append( " Td (" ); 5219 aDA.append( cMark ); 5220 aDA.append( ") Tj\nET\nQ\nEMC\n" ); 5221 writeBuffer( aDA.getStr(), aDA.getLength() ); 5222 endRedirect(); 5223 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream; 5224 5225 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 ); 5226 beginRedirect( pUncheckStream, aCheckRect ); 5227 writeBuffer( "/Tx BMC\nEMC\n", 12 ); 5228 endRedirect(); 5229 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream; 5230 } 5231 5232 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget ) 5233 { 5234 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 5235 5236 // save graphics state 5237 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5238 5239 if( rWidget.Background || rWidget.Border ) 5240 { 5241 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) ); 5242 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 5243 drawRectangle( rBox.m_aRect ); 5244 } 5245 5246 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() ); 5247 setFont( aFont ); 5248 Size aFontSize = aFont.GetSize(); 5249 if( aFontSize.Height() > rBox.m_aRect.GetHeight() ) 5250 aFontSize.Height() = rBox.m_aRect.GetHeight(); 5251 sal_Int32 nDelta = aFontSize.Height()/10; 5252 if( nDelta < 1 ) 5253 nDelta = 1; 5254 5255 Rectangle aCheckRect, aTextRect; 5256 if( rWidget.ButtonIsLeft ) 5257 { 5258 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta; 5259 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5260 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5261 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5262 5263 // #i74206# handle small controls without text area 5264 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5265 { 5266 aCheckRect.Right() -= nDelta; 5267 aCheckRect.Top() += nDelta/2; 5268 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5269 } 5270 5271 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta; 5272 aTextRect.Top() = rBox.m_aRect.Top(); 5273 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5274 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5275 } 5276 else 5277 { 5278 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height(); 5279 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5280 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5281 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5282 5283 // #i74206# handle small controls without text area 5284 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5285 { 5286 aCheckRect.Left() += nDelta; 5287 aCheckRect.Top() += nDelta/2; 5288 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5289 } 5290 5291 aTextRect.Left() = rBox.m_aRect.Left(); 5292 aTextRect.Top() = rBox.m_aRect.Top(); 5293 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5294 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5295 } 5296 setLineColor( Color( COL_BLACK ) ); 5297 setFillColor( Color( COL_TRANSPARENT ) ); 5298 OStringBuffer aLW( 32 ); 5299 aLW.append( "q " ); 5300 m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW ); 5301 aLW.append( " w " ); 5302 writeBuffer( aLW.getStr(), aLW.getLength() ); 5303 drawEllipse( aCheckRect ); 5304 writeBuffer( " Q\n", 3 ); 5305 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 5306 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle ); 5307 5308 pop(); 5309 5310 OStringBuffer aDA( 256 ); 5311 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5312 sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) ); 5313 aDA.append( ' ' ); 5314 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5315 aDA.append( " 0 Tf" ); 5316 rBox.m_aDAString = aDA.makeStringAndClear(); 5317 //to encrypt this (el) 5318 rBox.m_aMKDict = "/CA"; 5319 //after this assignement, to m_aMKDic cannot be added anything 5320 rBox.m_aMKDictCAString = "l"; 5321 5322 rBox.m_aRect = aCheckRect; 5323 5324 // create appearance streams 5325 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5326 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 ); 5327 5328 beginRedirect( pCheckStream, aCheckRect ); 5329 aDA.append( "/Tx BMC\nq BT\n" ); 5330 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5331 aDA.append( ' ' ); 5332 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5333 aDA.append( ' ' ); 5334 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA ); 5335 aDA.append( " Tf\n0 0 Td\nET\nQ\n" ); 5336 writeBuffer( aDA.getStr(), aDA.getLength() ); 5337 setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 5338 setLineColor( Color( COL_TRANSPARENT ) ); 5339 aCheckRect.Left() += 3*nDelta; 5340 aCheckRect.Top() += 3*nDelta; 5341 aCheckRect.Bottom() -= 3*nDelta; 5342 aCheckRect.Right() -= 3*nDelta; 5343 drawEllipse( aCheckRect ); 5344 writeBuffer( "\nEMC\n", 5 ); 5345 endRedirect(); 5346 5347 pop(); 5348 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream; 5349 5350 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 ); 5351 beginRedirect( pUncheckStream, aCheckRect ); 5352 writeBuffer( "/Tx BMC\nEMC\n", 12 ); 5353 endRedirect(); 5354 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream; 5355 } 5356 5357 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict ) 5358 { 5359 5360 // TODO: check and insert default streams 5361 rtl::OString aStandardAppearance; 5362 switch( rWidget.m_eType ) 5363 { 5364 case PDFWriter::CheckBox: 5365 aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US ); 5366 break; 5367 default: 5368 break; 5369 } 5370 5371 if( rWidget.m_aAppearances.size() ) 5372 { 5373 rAnnotDict.append( "/AP<<\n" ); 5374 for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it ) 5375 { 5376 rAnnotDict.append( "/" ); 5377 rAnnotDict.append( dict_it->first ); 5378 bool bUseSubDict = (dict_it->second.size() > 1); 5379 rAnnotDict.append( bUseSubDict ? "<<" : " " ); 5380 5381 for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin(); 5382 stream_it != dict_it->second.end(); ++stream_it ) 5383 { 5384 SvMemoryStream* pApppearanceStream = stream_it->second; 5385 dict_it->second[ stream_it->first ] = NULL; 5386 5387 bool bDeflate = compressStream( pApppearanceStream ); 5388 5389 pApppearanceStream->Seek( STREAM_SEEK_TO_END ); 5390 sal_Int64 nStreamLen = pApppearanceStream->Tell(); 5391 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN ); 5392 sal_Int32 nObject = createObject(); 5393 CHECK_RETURN( updateObject( nObject ) ); 5394 #if OSL_DEBUG_LEVEL > 1 5395 emitComment( "PDFWriterImpl::emitAppearances" ); 5396 #endif 5397 OStringBuffer aLine; 5398 aLine.append( nObject ); 5399 5400 aLine.append( " 0 obj\n" 5401 "<</Type/XObject\n" 5402 "/Subtype/Form\n" 5403 "/BBox[0 0 " ); 5404 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine ); 5405 aLine.append( " " ); 5406 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine ); 5407 aLine.append( "]\n" 5408 "/Resources " ); 5409 aLine.append( getResourceDictObj() ); 5410 aLine.append( " 0 R\n" 5411 "/Length " ); 5412 aLine.append( nStreamLen ); 5413 aLine.append( "\n" ); 5414 if( bDeflate ) 5415 aLine.append( "/Filter/FlateDecode\n" ); 5416 aLine.append( ">>\nstream\n" ); 5417 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5418 checkAndEnableStreamEncryption( nObject ); 5419 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) ); 5420 disableStreamEncryption(); 5421 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) ); 5422 5423 if( bUseSubDict ) 5424 { 5425 rAnnotDict.append( " /" ); 5426 rAnnotDict.append( stream_it->first ); 5427 rAnnotDict.append( " " ); 5428 } 5429 rAnnotDict.append( nObject ); 5430 rAnnotDict.append( " 0 R" ); 5431 5432 delete pApppearanceStream; 5433 } 5434 5435 rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" ); 5436 } 5437 rAnnotDict.append( ">>\n" ); 5438 if( aStandardAppearance.getLength() ) 5439 { 5440 rAnnotDict.append( "/AS /" ); 5441 rAnnotDict.append( aStandardAppearance ); 5442 rAnnotDict.append( "\n" ); 5443 } 5444 } 5445 5446 return true; 5447 } 5448 5449 bool PDFWriterImpl::emitWidgetAnnotations() 5450 { 5451 ensureUniqueRadioOnValues(); 5452 5453 int nAnnots = m_aWidgets.size(); 5454 for( int a = 0; a < nAnnots; a++ ) 5455 { 5456 PDFWidget& rWidget = m_aWidgets[a]; 5457 5458 OStringBuffer aLine( 1024 ); 5459 OStringBuffer aValue( 256 ); 5460 aLine.append( rWidget.m_nObject ); 5461 aLine.append( " 0 obj\n" 5462 "<<" ); 5463 if( rWidget.m_eType != PDFWriter::Hierarchy ) 5464 { 5465 // emit widget annotation only for terminal fields 5466 if( rWidget.m_aKids.empty() ) 5467 { 5468 aLine.append( "/Type/Annot/Subtype/Widget/F 4\n" 5469 "/Rect[" ); 5470 appendFixedInt( rWidget.m_aRect.Left()-1, aLine ); 5471 aLine.append( ' ' ); 5472 appendFixedInt( rWidget.m_aRect.Top()+1, aLine ); 5473 aLine.append( ' ' ); 5474 appendFixedInt( rWidget.m_aRect.Right()+1, aLine ); 5475 aLine.append( ' ' ); 5476 appendFixedInt( rWidget.m_aRect.Bottom()-1, aLine ); 5477 aLine.append( "]\n" ); 5478 } 5479 aLine.append( "/FT/" ); 5480 switch( rWidget.m_eType ) 5481 { 5482 case PDFWriter::RadioButton: 5483 case PDFWriter::CheckBox: 5484 // for radio buttons only the RadioButton field, not the 5485 // CheckBox children should have a value, else acrobat reader 5486 // does not always check the right button 5487 // of course real check boxes (not belonging to a readio group) 5488 // need their values, too 5489 if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 ) 5490 { 5491 aValue.append( "/" ); 5492 // check for radio group with all buttons unpressed 5493 if( rWidget.m_aValue.getLength() == 0 ) 5494 aValue.append( "Off" ); 5495 else 5496 appendName( rWidget.m_aValue, aValue ); 5497 } 5498 case PDFWriter::PushButton: 5499 aLine.append( "Btn" ); 5500 break; 5501 case PDFWriter::ListBox: 5502 if( rWidget.m_nFlags & 0x200000 ) // multiselect 5503 { 5504 aValue.append( "[" ); 5505 for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ ) 5506 { 5507 sal_Int32 nEntry = rWidget.m_aSelectedEntries[i]; 5508 if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) ) 5509 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue ); 5510 } 5511 aValue.append( "]" ); 5512 } 5513 else if( rWidget.m_aSelectedEntries.size() > 0 && 5514 rWidget.m_aSelectedEntries[0] >= 0 && 5515 rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) ) 5516 { 5517 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue ); 5518 } 5519 else 5520 appendUnicodeTextStringEncrypt( rtl::OUString(), rWidget.m_nObject, aValue ); 5521 aLine.append( "Ch" ); 5522 break; 5523 case PDFWriter::ComboBox: 5524 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue ); 5525 aLine.append( "Ch" ); 5526 break; 5527 case PDFWriter::Edit: 5528 aLine.append( "Tx" ); 5529 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue ); 5530 break; 5531 case PDFWriter::Hierarchy: // make the compiler happy 5532 break; 5533 } 5534 aLine.append( "\n" ); 5535 aLine.append( "/P " ); 5536 aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject ); 5537 aLine.append( " 0 R\n" ); 5538 } 5539 if( rWidget.m_nParent ) 5540 { 5541 aLine.append( "/Parent " ); 5542 aLine.append( rWidget.m_nParent ); 5543 aLine.append( " 0 R\n" ); 5544 } 5545 if( rWidget.m_aKids.size() ) 5546 { 5547 aLine.append( "/Kids[" ); 5548 for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ ) 5549 { 5550 aLine.append( rWidget.m_aKids[i] ); 5551 aLine.append( " 0 R" ); 5552 aLine.append( ( (i&15) == 15 ) ? "\n" : " " ); 5553 } 5554 aLine.append( "]\n" ); 5555 } 5556 if( rWidget.m_aName.getLength() ) 5557 { 5558 aLine.append( "/T" ); 5559 appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine ); 5560 aLine.append( "\n" ); 5561 } 5562 if( m_aContext.Version > PDFWriter::PDF_1_2 && rWidget.m_aDescription.getLength() ) 5563 { 5564 // the alternate field name should be unicode able since it is 5565 // supposed to be used in UI 5566 aLine.append( "/TU" ); 5567 appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine ); 5568 aLine.append( "\n" ); 5569 } 5570 5571 if( rWidget.m_nFlags ) 5572 { 5573 aLine.append( "/Ff " ); 5574 aLine.append( rWidget.m_nFlags ); 5575 aLine.append( "\n" ); 5576 } 5577 if( aValue.getLength() ) 5578 { 5579 OString aVal = aValue.makeStringAndClear(); 5580 aLine.append( "/V " ); 5581 aLine.append( aVal ); 5582 aLine.append( "\n" 5583 "/DV " ); 5584 aLine.append( aVal ); 5585 aLine.append( "\n" ); 5586 } 5587 if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox ) 5588 { 5589 sal_Int32 nTI = -1; 5590 aLine.append( "/Opt[\n" ); 5591 sal_Int32 i = 0; 5592 for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i ) 5593 { 5594 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine ); 5595 aLine.append( "\n" ); 5596 if( *it == rWidget.m_aValue ) 5597 nTI = i; 5598 } 5599 aLine.append( "]\n" ); 5600 if( nTI > 0 ) 5601 { 5602 aLine.append( "/TI " ); 5603 aLine.append( nTI ); 5604 aLine.append( "\n" ); 5605 if( rWidget.m_nFlags & 0x200000 ) // Multiselect 5606 { 5607 aLine.append( "/I [" ); 5608 aLine.append( nTI ); 5609 aLine.append( "]\n" ); 5610 } 5611 } 5612 } 5613 if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 ) 5614 { 5615 aLine.append( "/MaxLen " ); 5616 aLine.append( rWidget.m_nMaxLen ); 5617 aLine.append( "\n" ); 5618 } 5619 if( rWidget.m_eType == PDFWriter::PushButton ) 5620 { 5621 if(!m_bIsPDF_A1) 5622 { 5623 OStringBuffer aDest; 5624 if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) ) 5625 { 5626 aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " ); 5627 aLine.append( aDest.makeStringAndClear() ); 5628 aLine.append( ">>>>\n" ); 5629 } 5630 else if( rWidget.m_aListEntries.empty() ) 5631 { 5632 // create a reset form action 5633 aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" ); 5634 } 5635 else if( rWidget.m_bSubmit ) 5636 { 5637 // create a submit form action 5638 aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" ); 5639 appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() ); 5640 aLine.append( "/Flags " ); 5641 5642 sal_Int32 nFlags = 0; 5643 switch( m_aContext.SubmitFormat ) 5644 { 5645 case PDFWriter::HTML: 5646 nFlags |= 4; 5647 break; 5648 case PDFWriter::XML: 5649 if( m_aContext.Version > PDFWriter::PDF_1_3 ) 5650 nFlags |= 32; 5651 break; 5652 case PDFWriter::PDF: 5653 if( m_aContext.Version > PDFWriter::PDF_1_3 ) 5654 nFlags |= 256; 5655 break; 5656 case PDFWriter::FDF: 5657 default: 5658 break; 5659 } 5660 if( rWidget.m_bSubmitGet ) 5661 nFlags |= 8; 5662 aLine.append( nFlags ); 5663 aLine.append( ">>>>\n" ); 5664 } 5665 else 5666 { 5667 // create a URI action 5668 aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" ); 5669 aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) ); 5670 aLine.append( ")>>>>\n" ); 5671 } 5672 } 5673 else 5674 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA ); 5675 } 5676 if( rWidget.m_aDAString.getLength() ) 5677 { 5678 if( rWidget.m_aDRDict.getLength() ) 5679 { 5680 aLine.append( "/DR<<" ); 5681 aLine.append( rWidget.m_aDRDict ); 5682 aLine.append( ">>\n" ); 5683 } 5684 else 5685 { 5686 aLine.append( "/DR<</Font<<" ); 5687 appendBuiltinFontsToDict( aLine ); 5688 aLine.append( ">>>>\n" ); 5689 } 5690 aLine.append( "/DA" ); 5691 appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine ); 5692 aLine.append( "\n" ); 5693 if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER ) 5694 aLine.append( "/Q 1\n" ); 5695 else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT ) 5696 aLine.append( "/Q 2\n" ); 5697 } 5698 // appearance charactristics for terminal fields 5699 // which are supposed to have an appearance constructed 5700 // by the viewer application 5701 if( rWidget.m_aMKDict.getLength() ) 5702 { 5703 aLine.append( "/MK<<" ); 5704 aLine.append( rWidget.m_aMKDict ); 5705 //add the CA string, encrypting it 5706 appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine); 5707 aLine.append( ">>\n" ); 5708 } 5709 5710 CHECK_RETURN( emitAppearances( rWidget, aLine ) ); 5711 5712 aLine.append( ">>\n" 5713 "endobj\n\n" ); 5714 CHECK_RETURN( updateObject( rWidget.m_nObject ) ); 5715 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5716 } 5717 return true; 5718 } 5719 5720 bool PDFWriterImpl::emitAnnotations() 5721 { 5722 if( m_aPages.size() < 1 ) 5723 return false; 5724 5725 CHECK_RETURN( emitLinkAnnotations() ); 5726 5727 CHECK_RETURN( emitNoteAnnotations() ); 5728 5729 CHECK_RETURN( emitWidgetAnnotations() ); 5730 5731 return true; 5732 } 5733 5734 #undef CHECK_RETURN 5735 #define CHECK_RETURN( x ) if( !x ) return false 5736 5737 bool PDFWriterImpl::emitCatalog() 5738 { 5739 // build page tree 5740 // currently there is only one node that contains all leaves 5741 5742 // first create a page tree node id 5743 sal_Int32 nTreeNode = createObject(); 5744 5745 // emit global resource dictionary (page emit needs it) 5746 CHECK_RETURN( emitResources() ); 5747 5748 // emit all pages 5749 for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it ) 5750 if( ! it->emit( nTreeNode ) ) 5751 return false; 5752 5753 sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations(); 5754 5755 sal_Int32 nOutlineDict = emitOutline(); 5756 5757 //emit Output intent i59651 5758 sal_Int32 nOutputIntentObject = emitOutputIntent(); 5759 5760 //emit metadata 5761 sal_Int32 nMetadataObject = emitDocumentMetadata(); 5762 5763 sal_Int32 nStructureDict = 0; 5764 if(m_aStructure.size() > 1) 5765 { 5766 ///check if dummy structure containers are needed 5767 addInternalStructureContainer(m_aStructure[0]); 5768 nStructureDict = m_aStructure[0].m_nObject = createObject(); 5769 emitStructure( m_aStructure[ 0 ] ); 5770 } 5771 5772 // adjust tree node file offset 5773 if( ! updateObject( nTreeNode ) ) 5774 return false; 5775 5776 // emit tree node 5777 OStringBuffer aLine( 2048 ); 5778 aLine.append( nTreeNode ); 5779 aLine.append( " 0 obj\n" ); 5780 aLine.append( "<</Type/Pages\n" ); 5781 aLine.append( "/Resources " ); 5782 aLine.append( getResourceDictObj() ); 5783 aLine.append( " 0 R\n" ); 5784 5785 switch( m_eInheritedOrientation ) 5786 { 5787 case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break; 5788 case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break; 5789 5790 case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant 5791 case PDFWriter::Portrait: 5792 default: 5793 break; 5794 } 5795 sal_Int32 nMediaBoxWidth = 0; 5796 sal_Int32 nMediaBoxHeight = 0; 5797 if( m_aPages.empty() ) // sanity check, this should not happen 5798 { 5799 nMediaBoxWidth = m_nInheritedPageWidth; 5800 nMediaBoxHeight = m_nInheritedPageHeight; 5801 } 5802 else 5803 { 5804 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter ) 5805 { 5806 if( iter->m_nPageWidth > nMediaBoxWidth ) 5807 nMediaBoxWidth = iter->m_nPageWidth; 5808 if( iter->m_nPageHeight > nMediaBoxHeight ) 5809 nMediaBoxHeight = iter->m_nPageHeight; 5810 } 5811 } 5812 aLine.append( "/MediaBox[ 0 0 " ); 5813 aLine.append( nMediaBoxWidth ); 5814 aLine.append( ' ' ); 5815 aLine.append( nMediaBoxHeight ); 5816 aLine.append( " ]\n" 5817 "/Kids[ " ); 5818 unsigned int i = 0; 5819 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ ) 5820 { 5821 aLine.append( iter->m_nPageObject ); 5822 aLine.append( " 0 R" ); 5823 aLine.append( ( (i&15) == 15 ) ? "\n" : " " ); 5824 } 5825 aLine.append( "]\n" 5826 "/Count " ); 5827 aLine.append( (sal_Int32)m_aPages.size() ); 5828 aLine.append( ">>\n" 5829 "endobj\n\n" ); 5830 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5831 5832 // emit annotation objects 5833 CHECK_RETURN( emitAnnotations() ); 5834 5835 // emit Catalog 5836 m_nCatalogObject = createObject(); 5837 if( ! updateObject( m_nCatalogObject ) ) 5838 return false; 5839 aLine.setLength( 0 ); 5840 aLine.append( m_nCatalogObject ); 5841 aLine.append( " 0 obj\n" 5842 "<</Type/Catalog/Pages " ); 5843 aLine.append( nTreeNode ); 5844 aLine.append( " 0 R\n" ); 5845 //--->i56629 5846 //check if there are named destinations to emit (root must be inside the catalog) 5847 if( nNamedDestinationsDictionary ) 5848 { 5849 aLine.append("/Dests "); 5850 aLine.append( nNamedDestinationsDictionary ); 5851 aLine.append( " 0 R\n" ); 5852 } 5853 //<---- 5854 if( m_aContext.PageLayout != PDFWriter::DefaultLayout ) 5855 switch( m_aContext.PageLayout ) 5856 { 5857 default : 5858 case PDFWriter::SinglePage : 5859 aLine.append( "/PageLayout/SinglePage\n" ); 5860 break; 5861 case PDFWriter::Continuous : 5862 aLine.append( "/PageLayout/OneColumn\n" ); 5863 break; 5864 case PDFWriter::ContinuousFacing : 5865 //the flag m_aContext.FirstPageLeft below is used to set the page on the left side 5866 aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side 5867 break; 5868 } 5869 if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode ) 5870 switch( m_aContext.PDFDocumentMode ) 5871 { 5872 default : 5873 aLine.append( "/PageMode/UseNone\n" ); 5874 break; 5875 case PDFWriter::UseOutlines : 5876 aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open 5877 break; 5878 case PDFWriter::UseThumbs : 5879 aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open 5880 break; 5881 } 5882 else if( m_aContext.OpenInFullScreenMode ) 5883 aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen 5884 5885 OStringBuffer aInitPageRef; 5886 if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() ) 5887 { 5888 aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject ); 5889 aInitPageRef.append( " 0 R" ); 5890 } 5891 else 5892 aInitPageRef.append( "0" ); 5893 switch( m_aContext.PDFDocumentAction ) 5894 { 5895 case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default 5896 default: 5897 if( aInitPageRef.getLength() > 1 ) 5898 { 5899 aLine.append( "/OpenAction[" ); 5900 aLine.append( aInitPageRef ); 5901 aLine.append( " /XYZ null null 0]\n" ); 5902 } 5903 break; 5904 case PDFWriter::FitInWindow : 5905 aLine.append( "/OpenAction[" ); 5906 aLine.append( aInitPageRef ); 5907 aLine.append( " /Fit]\n" ); //Open fit page 5908 break; 5909 case PDFWriter::FitWidth : 5910 aLine.append( "/OpenAction[" ); 5911 aLine.append( aInitPageRef ); 5912 aLine.append( " /FitH " ); 5913 aLine.append( m_nInheritedPageHeight );//Open fit width 5914 aLine.append( "]\n" ); 5915 break; 5916 case PDFWriter::FitVisible : 5917 aLine.append( "/OpenAction[" ); 5918 aLine.append( aInitPageRef ); 5919 aLine.append( " /FitBH " ); 5920 aLine.append( m_nInheritedPageHeight );//Open fit visible 5921 aLine.append( "]\n" ); 5922 break; 5923 case PDFWriter::ActionZoom : 5924 aLine.append( "/OpenAction[" ); 5925 aLine.append( aInitPageRef ); 5926 aLine.append( " /XYZ null null " ); 5927 if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 ) 5928 aLine.append( (double)m_aContext.Zoom/100.0 ); 5929 else 5930 aLine.append( "0" ); 5931 aLine.append( "]\n" ); 5932 break; 5933 } 5934 // viewer preferences, if we had some, then emit 5935 if( m_aContext.HideViewerToolbar || 5936 ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) || 5937 m_aContext.HideViewerMenubar || 5938 m_aContext.HideViewerWindowControls || m_aContext.FitWindow || 5939 m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) || 5940 m_aContext.OpenInFullScreenMode ) 5941 { 5942 aLine.append( "/ViewerPreferences<<" ); 5943 if( m_aContext.HideViewerToolbar ) 5944 aLine.append( "/HideToolbar true\n" ); 5945 if( m_aContext.HideViewerMenubar ) 5946 aLine.append( "/HideMenubar true\n" ); 5947 if( m_aContext.HideViewerWindowControls ) 5948 aLine.append( "/HideWindowUI true\n" ); 5949 if( m_aContext.FitWindow ) 5950 aLine.append( "/FitWindow true\n" ); 5951 if( m_aContext.CenterWindow ) 5952 aLine.append( "/CenterWindow true\n" ); 5953 if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) 5954 aLine.append( "/DisplayDocTitle true\n" ); 5955 if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) 5956 aLine.append( "/Direction/R2L\n" ); 5957 if( m_aContext.OpenInFullScreenMode ) 5958 switch( m_aContext.PDFDocumentMode ) 5959 { 5960 default : 5961 case PDFWriter::ModeDefault : 5962 aLine.append( "/NonFullScreenPageMode/UseNone\n" ); 5963 break; 5964 case PDFWriter::UseOutlines : 5965 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" ); 5966 break; 5967 case PDFWriter::UseThumbs : 5968 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" ); 5969 break; 5970 } 5971 aLine.append( ">>\n" ); 5972 } 5973 5974 if( nOutlineDict ) 5975 { 5976 aLine.append( "/Outlines " ); 5977 aLine.append( nOutlineDict ); 5978 aLine.append( " 0 R\n" ); 5979 } 5980 if( nStructureDict ) 5981 { 5982 aLine.append( "/StructTreeRoot " ); 5983 aLine.append( nStructureDict ); 5984 aLine.append( " 0 R\n" ); 5985 } 5986 if( m_aContext.DocumentLocale.Language.getLength() > 0 ) 5987 { 5988 OUStringBuffer aLocBuf( 16 ); 5989 aLocBuf.append( m_aContext.DocumentLocale.Language.toAsciiLowerCase() ); 5990 if( m_aContext.DocumentLocale.Country.getLength() > 0 ) 5991 { 5992 aLocBuf.append( sal_Unicode('-') ); 5993 aLocBuf.append( m_aContext.DocumentLocale.Country ); 5994 } 5995 aLine.append( "/Lang" ); 5996 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine ); 5997 aLine.append( "\n" ); 5998 } 5999 if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 ) 6000 { 6001 aLine.append( "/MarkInfo<</Marked true>>\n" ); 6002 } 6003 if( m_aWidgets.size() > 0 ) 6004 { 6005 aLine.append( "/AcroForm<</Fields[\n" ); 6006 int nWidgets = m_aWidgets.size(); 6007 int nOut = 0; 6008 for( int j = 0; j < nWidgets; j++ ) 6009 { 6010 // output only root fields 6011 if( m_aWidgets[j].m_nParent < 1 ) 6012 { 6013 aLine.append( m_aWidgets[j].m_nObject ); 6014 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " ); 6015 } 6016 } 6017 aLine.append( "\n]/DR " ); 6018 aLine.append( getResourceDictObj() ); 6019 aLine.append( " 0 R" ); 6020 if( m_bIsPDF_A1 ) 6021 aLine.append( ">>\n" ); 6022 else 6023 aLine.append( "/NeedAppearances true>>\n" ); 6024 } 6025 //--->i59651 6026 //check if there is a Metadata object 6027 if( nOutputIntentObject ) 6028 { 6029 aLine.append("/OutputIntents["); 6030 aLine.append( nOutputIntentObject ); 6031 aLine.append( " 0 R]" ); 6032 } 6033 if( nMetadataObject ) 6034 { 6035 aLine.append("/Metadata "); 6036 aLine.append( nMetadataObject ); 6037 aLine.append( " 0 R" ); 6038 } 6039 //<---- 6040 aLine.append( ">>\n" 6041 "endobj\n\n" ); 6042 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6043 6044 return true; 6045 } 6046 6047 sal_Int32 PDFWriterImpl::emitInfoDict( ) 6048 { 6049 sal_Int32 nObject = createObject(); 6050 6051 if( updateObject( nObject ) ) 6052 { 6053 OStringBuffer aLine( 1024 ); 6054 aLine.append( nObject ); 6055 aLine.append( " 0 obj\n" 6056 "<<" ); 6057 if( m_aContext.DocumentInfo.Title.Len() ) 6058 { 6059 aLine.append( "/Title" ); 6060 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine ); 6061 aLine.append( "\n" ); 6062 } 6063 if( m_aContext.DocumentInfo.Author.Len() ) 6064 { 6065 aLine.append( "/Author" ); 6066 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine ); 6067 aLine.append( "\n" ); 6068 } 6069 if( m_aContext.DocumentInfo.Subject.Len() ) 6070 { 6071 aLine.append( "/Subject" ); 6072 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine ); 6073 aLine.append( "\n" ); 6074 } 6075 if( m_aContext.DocumentInfo.Keywords.Len() ) 6076 { 6077 aLine.append( "/Keywords" ); 6078 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine ); 6079 aLine.append( "\n" ); 6080 } 6081 if( m_aContext.DocumentInfo.Creator.Len() ) 6082 { 6083 aLine.append( "/Creator" ); 6084 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine ); 6085 aLine.append( "\n" ); 6086 } 6087 if( m_aContext.DocumentInfo.Producer.Len() ) 6088 { 6089 aLine.append( "/Producer" ); 6090 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine ); 6091 aLine.append( "\n" ); 6092 } 6093 6094 aLine.append( "/CreationDate" ); 6095 appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine ); 6096 aLine.append( ">>\nendobj\n\n" ); 6097 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6098 nObject = 0; 6099 } 6100 else 6101 nObject = 0; 6102 6103 return nObject; 6104 } 6105 6106 //--->i56629 6107 // Part of this function may be shared with method appendDest. 6108 // 6109 sal_Int32 PDFWriterImpl::emitNamedDestinations() 6110 { 6111 sal_Int32 nCount = m_aNamedDests.size(); 6112 if( nCount <= 0 ) 6113 return 0;//define internal error 6114 6115 //get the object number for all the destinations 6116 sal_Int32 nObject = createObject(); 6117 6118 if( updateObject( nObject ) ) 6119 { 6120 //emit the dictionary 6121 OStringBuffer aLine( 1024 ); 6122 aLine.append( nObject ); 6123 aLine.append( " 0 obj\n" 6124 "<<" ); 6125 6126 sal_Int32 nDestID; 6127 for( nDestID = 0; nDestID < nCount; nDestID++ ) 6128 { 6129 const PDFNamedDest& rDest = m_aNamedDests[ nDestID ]; 6130 // In order to correctly function both under an Internet browser and 6131 // directly with a reader (provided the reader has the feature) we 6132 // need to set the name of the destination the same way it will be encoded 6133 // in an Internet link 6134 INetURLObject aLocalURL( 6135 OUString( RTL_CONSTASCII_USTRINGPARAM( "http://ahost.ax" ) ) ); //dummy location, won't be used 6136 aLocalURL.SetMark( rDest.m_aDestName ); 6137 6138 const rtl::OUString aName = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as 6139 // in link creation ( see PDFWriterImpl::emitLinkAnnotations ) 6140 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ]; 6141 6142 aLine.append( '/' ); 6143 appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog ) 6144 aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function 6145 //maps the preceeding character properly 6146 aLine.append( rDestPage.m_nPageObject ); 6147 aLine.append( " 0 R" ); 6148 6149 switch( rDest.m_eType ) 6150 { 6151 case PDFWriter::XYZ: 6152 default: 6153 aLine.append( "/XYZ " ); 6154 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6155 aLine.append( ' ' ); 6156 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6157 aLine.append( " 0" ); 6158 break; 6159 case PDFWriter::Fit: 6160 aLine.append( "/Fit" ); 6161 break; 6162 case PDFWriter::FitRectangle: 6163 aLine.append( "/FitR " ); 6164 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6165 aLine.append( ' ' ); 6166 appendFixedInt( rDest.m_aRect.Top(), aLine ); 6167 aLine.append( ' ' ); 6168 appendFixedInt( rDest.m_aRect.Right(), aLine ); 6169 aLine.append( ' ' ); 6170 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6171 break; 6172 case PDFWriter::FitHorizontal: 6173 aLine.append( "/FitH " ); 6174 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6175 break; 6176 case PDFWriter::FitVertical: 6177 aLine.append( "/FitV " ); 6178 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6179 break; 6180 case PDFWriter::FitPageBoundingBox: 6181 aLine.append( "/FitB" ); 6182 break; 6183 case PDFWriter::FitPageBoundingBoxHorizontal: 6184 aLine.append( "/FitBH " ); 6185 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6186 break; 6187 case PDFWriter::FitPageBoundingBoxVertical: 6188 aLine.append( "/FitBV " ); 6189 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6190 break; 6191 } 6192 aLine.append( "]\n" ); 6193 } 6194 //close 6195 6196 aLine.append( ">>\nendobj\n\n" ); 6197 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6198 nObject = 0; 6199 } 6200 else 6201 nObject = 0; 6202 6203 return nObject; 6204 } 6205 //<--- i56629 6206 6207 //--->i59651 6208 // emits the output intent dictionary 6209 6210 sal_Int32 PDFWriterImpl::emitOutputIntent() 6211 { 6212 if( !m_bIsPDF_A1 ) 6213 return 0; 6214 6215 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1 6216 6217 OStringBuffer aLine( 1024 ); 6218 sal_Int32 nICCObject = createObject(); 6219 sal_Int32 nStreamLengthObject = createObject(); 6220 6221 aLine.append( nICCObject ); 6222 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16) 6223 aLine.append( " 0 obj\n<</N 3/Length " ); 6224 aLine.append( nStreamLengthObject ); 6225 aLine.append( " 0 R" ); 6226 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 6227 aLine.append( "/Filter/FlateDecode" ); 6228 #endif 6229 aLine.append( ">>\nstream\n" ); 6230 CHECK_RETURN( updateObject( nICCObject ) ); 6231 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6232 //get file position 6233 sal_uInt64 nBeginStreamPos = 0; 6234 osl_getFilePos( m_aFile, &nBeginStreamPos ); 6235 beginCompression(); 6236 checkAndEnableStreamEncryption( nICCObject ); 6237 sal_Int32 nStreamSize = writeBuffer( nsRGB_ICC_profile, (sal_Int32) sizeof( nsRGB_ICC_profile ) ); 6238 disableStreamEncryption(); 6239 endCompression(); 6240 sal_uInt64 nEndStreamPos = 0; 6241 osl_getFilePos( m_aFile, &nEndStreamPos ); 6242 6243 if( nStreamSize == 0 ) 6244 return 0; 6245 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) 6246 return 0 ; 6247 aLine.setLength( 0 ); 6248 6249 //emit the stream length object 6250 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 6251 aLine.setLength( 0 ); 6252 aLine.append( nStreamLengthObject ); 6253 aLine.append( " 0 obj\n" ); 6254 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) ); 6255 aLine.append( "\nendobj\n\n" ); 6256 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6257 aLine.setLength( 0 ); 6258 6259 //emit the OutputIntent dictionary 6260 sal_Int32 nOIObject = createObject(); 6261 CHECK_RETURN( updateObject( nOIObject ) ); 6262 aLine.append( nOIObject ); 6263 aLine.append( " 0 obj\n" 6264 "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier"); 6265 6266 rtl::OUString aComment( RTL_CONSTASCII_USTRINGPARAM( "sRGB IEC61966-2.1" ) ); 6267 appendLiteralStringEncrypt( aComment ,nOIObject, aLine ); 6268 aLine.append("/DestOutputProfile "); 6269 aLine.append( nICCObject ); 6270 aLine.append( " 0 R>>\nendobj\n\n" );; 6271 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6272 6273 return nOIObject; 6274 } 6275 6276 // formats the string for the XML stream 6277 static void escapeStringXML( const rtl::OUString& rStr, rtl::OUString &rValue) 6278 { 6279 const sal_Unicode* pUni = rStr.getStr(); 6280 int nLen = rStr.getLength(); 6281 for( ; nLen; nLen--, pUni++ ) 6282 { 6283 switch( *pUni ) 6284 { 6285 case sal_Unicode('&'): 6286 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&" ) ); 6287 break; 6288 case sal_Unicode('<'): 6289 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "<" ) ); 6290 break; 6291 case sal_Unicode('>'): 6292 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ">" ) ); 6293 break; 6294 case sal_Unicode('\''): 6295 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "'" ) ); 6296 break; 6297 case sal_Unicode('"'): 6298 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( """ ) ); 6299 break; 6300 default: 6301 rValue += rtl::OUString( *pUni ); 6302 break; 6303 } 6304 } 6305 } 6306 6307 // emits the document metadata 6308 // 6309 sal_Int32 PDFWriterImpl::emitDocumentMetadata() 6310 { 6311 if( !m_bIsPDF_A1 ) 6312 return 0; 6313 6314 //get the object number for all the destinations 6315 sal_Int32 nObject = createObject(); 6316 6317 if( updateObject( nObject ) ) 6318 { 6319 // the following string are written in UTF-8 unicode 6320 OStringBuffer aMetadataStream( 8192 ); 6321 6322 aMetadataStream.append( "<?xpacket begin=\"" ); 6323 // this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used 6324 // as a byte-order marker. 6325 aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) ); 6326 aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" ); 6327 aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" ); 6328 aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" ); 6329 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 ) 6330 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6331 aMetadataStream.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" ); 6332 aMetadataStream.append( " <pdfaid:part>1</pdfaid:part>\n" ); 6333 aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" ); 6334 aMetadataStream.append( " </rdf:Description>\n" ); 6335 //... Dublin Core properties go here 6336 if( m_aContext.DocumentInfo.Title.Len() || 6337 m_aContext.DocumentInfo.Author.Len() || 6338 m_aContext.DocumentInfo.Subject.Len() ) 6339 { 6340 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6341 aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" ); 6342 if( m_aContext.DocumentInfo.Title.Len() ) 6343 { 6344 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) 6345 aMetadataStream.append( " <dc:title>\n" ); 6346 aMetadataStream.append( " <rdf:Alt>\n" ); 6347 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); 6348 rtl::OUString aTitle; 6349 escapeStringXML( m_aContext.DocumentInfo.Title, aTitle ); 6350 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) ); 6351 aMetadataStream.append( "</rdf:li>\n" ); 6352 aMetadataStream.append( " </rdf:Alt>\n" ); 6353 aMetadataStream.append( " </dc:title>\n" ); 6354 } 6355 if( m_aContext.DocumentInfo.Author.Len() ) 6356 { 6357 aMetadataStream.append( " <dc:creator>\n" ); 6358 aMetadataStream.append( " <rdf:Seq>\n" ); 6359 aMetadataStream.append( " <rdf:li>" ); 6360 rtl::OUString aAuthor; 6361 escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor ); 6362 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) ); 6363 aMetadataStream.append( "</rdf:li>\n" ); 6364 aMetadataStream.append( " </rdf:Seq>\n" ); 6365 aMetadataStream.append( " </dc:creator>\n" ); 6366 } 6367 if( m_aContext.DocumentInfo.Subject.Len() ) 6368 { 6369 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) 6370 aMetadataStream.append( " <dc:description>\n" ); 6371 aMetadataStream.append( " <rdf:Alt>\n" ); 6372 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); 6373 rtl::OUString aSubject; 6374 escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject ); 6375 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) ); 6376 aMetadataStream.append( "</rdf:li>\n" ); 6377 aMetadataStream.append( " </rdf:Alt>\n" ); 6378 aMetadataStream.append( " </dc:description>\n" ); 6379 } 6380 aMetadataStream.append( " </rdf:Description>\n" ); 6381 } 6382 6383 //... PDF properties go here 6384 if( m_aContext.DocumentInfo.Producer.Len() || 6385 m_aContext.DocumentInfo.Keywords.Len() ) 6386 { 6387 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6388 aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" ); 6389 if( m_aContext.DocumentInfo.Producer.Len() ) 6390 { 6391 aMetadataStream.append( " <pdf:Producer>" ); 6392 rtl::OUString aProducer; 6393 escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer ); 6394 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) ); 6395 aMetadataStream.append( "</pdf:Producer>\n" ); 6396 } 6397 if( m_aContext.DocumentInfo.Keywords.Len() ) 6398 { 6399 aMetadataStream.append( " <pdf:Keywords>" ); 6400 rtl::OUString aKeywords; 6401 escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords ); 6402 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) ); 6403 aMetadataStream.append( "</pdf:Keywords>\n" ); 6404 } 6405 aMetadataStream.append( " </rdf:Description>\n" ); 6406 } 6407 6408 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6409 aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" ); 6410 if( m_aContext.DocumentInfo.Creator.Len() ) 6411 { 6412 aMetadataStream.append( " <xmp:CreatorTool>" ); 6413 rtl::OUString aCreator; 6414 escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator ); 6415 aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) ); 6416 aMetadataStream.append( "</xmp:CreatorTool>\n" ); 6417 } 6418 //creation date 6419 aMetadataStream.append( " <xmp:CreateDate>" ); 6420 aMetadataStream.append( m_aCreationMetaDateString ); 6421 aMetadataStream.append( "</xmp:CreateDate>\n" ); 6422 6423 aMetadataStream.append( " </rdf:Description>\n" ); 6424 aMetadataStream.append( " </rdf:RDF>\n" ); 6425 aMetadataStream.append( "</x:xmpmeta>\n" ); 6426 6427 //add the padding 6428 for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ ) 6429 { 6430 aMetadataStream.append( " " ); 6431 if( nSpaces % 100 == 0 ) 6432 aMetadataStream.append( "\n" ); 6433 } 6434 6435 aMetadataStream.append( "<?xpacket end=\"w\"?>\n" ); 6436 6437 OStringBuffer aMetadataObj( 1024 ); 6438 6439 aMetadataObj.append( nObject ); 6440 aMetadataObj.append( " 0 obj\n" ); 6441 6442 aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " ); 6443 6444 aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() ); 6445 aMetadataObj.append( ">>\nstream\n" ); 6446 CHECK_RETURN( writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) ); 6447 //emit the stream 6448 CHECK_RETURN( writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) ); 6449 6450 aMetadataObj.setLength( 0 ); 6451 aMetadataObj.append( "\nendstream\nendobj\n\n" ); 6452 if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) ) 6453 nObject = 0; 6454 } 6455 else 6456 nObject = 0; 6457 6458 return nObject; 6459 } 6460 //<---i59651 6461 6462 bool PDFWriterImpl::emitTrailer() 6463 { 6464 // emit doc info 6465 OString aInfoValuesOut; 6466 sal_Int32 nDocInfoObject = emitInfoDict( ); 6467 6468 sal_Int32 nSecObject = 0; 6469 6470 if( m_aContext.Encryption.Encrypt() ) 6471 { 6472 //emit the security information 6473 //must be emitted as indirect dictionary object, since 6474 //Acrobat Reader 5 works only with this kind of implementation 6475 nSecObject = createObject(); 6476 6477 if( updateObject( nSecObject ) ) 6478 { 6479 OStringBuffer aLineS( 1024 ); 6480 aLineS.append( nSecObject ); 6481 aLineS.append( " 0 obj\n" 6482 "<</Filter/Standard/V " ); 6483 // check the version 6484 if( m_aContext.Encryption.Security128bit ) 6485 aLineS.append( "2/Length 128/R 3" ); 6486 else 6487 aLineS.append( "1/R 2" ); 6488 6489 // emit the owner password, must not be encrypted 6490 aLineS.append( "/O(" ); 6491 appendLiteralString( (const sal_Char*)&m_aContext.Encryption.OValue[0], sal_Int32(m_aContext.Encryption.OValue.size()), aLineS ); 6492 aLineS.append( ")/U(" ); 6493 appendLiteralString( (const sal_Char*)&m_aContext.Encryption.UValue[0], sal_Int32(m_aContext.Encryption.UValue.size()), aLineS ); 6494 aLineS.append( ")/P " );// the permission set 6495 aLineS.append( m_nAccessPermissions ); 6496 aLineS.append( ">>\nendobj\n\n" ); 6497 if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) ) 6498 nSecObject = 0; 6499 } 6500 else 6501 nSecObject = 0; 6502 } 6503 // emit xref table 6504 // remember start 6505 sal_uInt64 nXRefOffset = 0; 6506 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) ); 6507 CHECK_RETURN( writeBuffer( "xref\n", 5 ) ); 6508 6509 sal_Int32 nObjects = m_aObjects.size(); 6510 OStringBuffer aLine; 6511 aLine.append( "0 " ); 6512 aLine.append( (sal_Int32)(nObjects+1) ); 6513 aLine.append( "\n" ); 6514 aLine.append( "0000000000 65535 f \n" ); 6515 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6516 6517 for( sal_Int32 i = 0; i < nObjects; i++ ) 6518 { 6519 aLine.setLength( 0 ); 6520 OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] ); 6521 for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ ) 6522 aLine.append( '0' ); 6523 aLine.append( aOffset ); 6524 aLine.append( " 00000 n \n" ); 6525 DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" ); 6526 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6527 } 6528 6529 // prepare document checksum 6530 OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 ); 6531 if( m_aDocDigest ) 6532 { 6533 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; 6534 rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) ); 6535 for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ ) 6536 appendHex( nMD5Sum[i], aDocChecksum ); 6537 } 6538 // document id set in setDocInfo method 6539 // emit trailer 6540 aLine.setLength( 0 ); 6541 aLine.append( "trailer\n" 6542 "<</Size " ); 6543 aLine.append( (sal_Int32)(nObjects+1) ); 6544 aLine.append( "/Root " ); 6545 aLine.append( m_nCatalogObject ); 6546 aLine.append( " 0 R\n" ); 6547 if( nSecObject |= 0 ) 6548 { 6549 aLine.append( "/Encrypt "); 6550 aLine.append( nSecObject ); 6551 aLine.append( " 0 R\n" ); 6552 } 6553 if( nDocInfoObject ) 6554 { 6555 aLine.append( "/Info " ); 6556 aLine.append( nDocInfoObject ); 6557 aLine.append( " 0 R\n" ); 6558 } 6559 if( ! m_aContext.Encryption.DocumentIdentifier.empty() ) 6560 { 6561 aLine.append( "/ID [ <" ); 6562 for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); 6563 it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) 6564 { 6565 appendHex( sal_Int8(*it), aLine ); 6566 } 6567 aLine.append( ">\n" 6568 "<" ); 6569 for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); 6570 it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) 6571 { 6572 appendHex( sal_Int8(*it), aLine ); 6573 } 6574 aLine.append( "> ]\n" ); 6575 } 6576 if( aDocChecksum.getLength() ) 6577 { 6578 aLine.append( "/DocChecksum /" ); 6579 aLine.append( aDocChecksum ); 6580 aLine.append( "\n" ); 6581 } 6582 if( m_aAdditionalStreams.size() > 0 ) 6583 { 6584 aLine.append( "/AdditionalStreams [" ); 6585 for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ ) 6586 { 6587 aLine.append( "/" ); 6588 appendName( m_aAdditionalStreams[i].m_aMimeType, aLine ); 6589 aLine.append( " " ); 6590 aLine.append( m_aAdditionalStreams[i].m_nStreamObject ); 6591 aLine.append( " 0 R\n" ); 6592 } 6593 aLine.append( "]\n" ); 6594 } 6595 aLine.append( ">>\n" 6596 "startxref\n" ); 6597 aLine.append( (sal_Int64)nXRefOffset ); 6598 aLine.append( "\n" 6599 "%%EOF\n" ); 6600 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6601 6602 return true; 6603 } 6604 6605 struct AnnotationSortEntry 6606 { 6607 sal_Int32 nTabOrder; 6608 sal_Int32 nObject; 6609 sal_Int32 nWidgetIndex; 6610 6611 AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) : 6612 nTabOrder( nTab ), 6613 nObject( nObj ), 6614 nWidgetIndex( nI ) 6615 {} 6616 }; 6617 6618 struct AnnotSortContainer 6619 { 6620 std::set< sal_Int32 > aObjects; 6621 std::vector< AnnotationSortEntry > aSortedAnnots; 6622 }; 6623 6624 struct AnnotSorterLess 6625 { 6626 std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets; 6627 6628 AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {} 6629 6630 bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight ) 6631 { 6632 if( rLeft.nTabOrder < rRight.nTabOrder ) 6633 return true; 6634 if( rRight.nTabOrder < rLeft.nTabOrder ) 6635 return false; 6636 if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 ) 6637 return false; 6638 if( rRight.nWidgetIndex < 0 ) 6639 return true; 6640 if( rLeft.nWidgetIndex < 0 ) 6641 return false; 6642 // remember: widget rects are in PDF coordinates, so they are ordered down up 6643 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() > 6644 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() ) 6645 return true; 6646 if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() > 6647 m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() ) 6648 return false; 6649 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() < 6650 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() ) 6651 return true; 6652 return false; 6653 } 6654 }; 6655 6656 void PDFWriterImpl::sortWidgets() 6657 { 6658 // sort widget annotations on each page as per their 6659 // TabOrder attribute 6660 std::hash_map< sal_Int32, AnnotSortContainer > sorted; 6661 int nWidgets = m_aWidgets.size(); 6662 for( int nW = 0; nW < nWidgets; nW++ ) 6663 { 6664 const PDFWidget& rWidget = m_aWidgets[nW]; 6665 if( rWidget.m_nPage >= 0 ) 6666 { 6667 AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ]; 6668 // optimize vector allocation 6669 if( rCont.aSortedAnnots.empty() ) 6670 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() ); 6671 // insert widget to tab sorter 6672 // RadioButtons are not page annotations, only their individual check boxes are 6673 if( rWidget.m_eType != PDFWriter::RadioButton ) 6674 { 6675 rCont.aObjects.insert( rWidget.m_nObject ); 6676 rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) ); 6677 } 6678 } 6679 } 6680 for( std::hash_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it ) 6681 { 6682 // append entries for non widget annotations 6683 PDFPage& rPage = m_aPages[ it->first ]; 6684 unsigned int nAnnots = rPage.m_aAnnotations.size(); 6685 for( unsigned int nA = 0; nA < nAnnots; nA++ ) 6686 if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end()) 6687 it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) ); 6688 6689 AnnotSorterLess aLess( m_aWidgets ); 6690 std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess ); 6691 // sanity check 6692 if( it->second.aSortedAnnots.size() == nAnnots) 6693 { 6694 for( unsigned int nA = 0; nA < nAnnots; nA++ ) 6695 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject; 6696 } 6697 else 6698 { 6699 DBG_ASSERT( 0, "wrong number of sorted annotations" ); 6700 #if OSL_DEBUG_LEVEL > 0 6701 fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n" 6702 " %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots ); 6703 #endif 6704 } 6705 } 6706 6707 // FIXME: implement tab order in structure tree for PDF 1.5 6708 } 6709 6710 namespace vcl { 6711 class PDFStreamIf : 6712 public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream > 6713 { 6714 PDFWriterImpl* m_pWriter; 6715 bool m_bWrite; 6716 public: 6717 PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {} 6718 virtual ~PDFStreamIf(); 6719 6720 virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw(); 6721 virtual void SAL_CALL flush() throw(); 6722 virtual void SAL_CALL closeOutput() throw(); 6723 }; 6724 } 6725 6726 PDFStreamIf::~PDFStreamIf() 6727 { 6728 } 6729 6730 void SAL_CALL PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw() 6731 { 6732 if( m_bWrite ) 6733 { 6734 sal_Int32 nBytes = aData.getLength(); 6735 if( nBytes > 0 ) 6736 m_pWriter->writeBuffer( aData.getConstArray(), nBytes ); 6737 } 6738 } 6739 6740 void SAL_CALL PDFStreamIf::flush() throw() 6741 { 6742 } 6743 6744 void SAL_CALL PDFStreamIf::closeOutput() throw() 6745 { 6746 m_bWrite = false; 6747 } 6748 6749 bool PDFWriterImpl::emitAdditionalStreams() 6750 { 6751 unsigned int nStreams = m_aAdditionalStreams.size(); 6752 for( unsigned int i = 0; i < nStreams; i++ ) 6753 { 6754 PDFAddStream& rStream = m_aAdditionalStreams[i]; 6755 rStream.m_nStreamObject = createObject(); 6756 sal_Int32 nSizeObject = createObject(); 6757 6758 if( ! updateObject( rStream.m_nStreamObject ) ) 6759 return false; 6760 6761 OStringBuffer aLine; 6762 aLine.append( rStream.m_nStreamObject ); 6763 aLine.append( " 0 obj\n<</Length " ); 6764 aLine.append( nSizeObject ); 6765 aLine.append( " 0 R" ); 6766 if( rStream.m_bCompress ) 6767 aLine.append( "/Filter/FlateDecode" ); 6768 aLine.append( ">>\nstream\n" ); 6769 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6770 return false; 6771 sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0; 6772 if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) ) 6773 { 6774 osl_closeFile( m_aFile ); 6775 m_bOpen = false; 6776 } 6777 if( rStream.m_bCompress ) 6778 beginCompression(); 6779 6780 checkAndEnableStreamEncryption( rStream.m_nStreamObject ); 6781 com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) ); 6782 rStream.m_pStream->write( xStream ); 6783 xStream.clear(); 6784 delete rStream.m_pStream; 6785 rStream.m_pStream = NULL; 6786 disableStreamEncryption(); 6787 6788 if( rStream.m_bCompress ) 6789 endCompression(); 6790 6791 if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) ) 6792 { 6793 osl_closeFile( m_aFile ); 6794 m_bOpen = false; 6795 return false; 6796 } 6797 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) 6798 return false ; 6799 // emit stream length object 6800 if( ! updateObject( nSizeObject ) ) 6801 return false; 6802 aLine.setLength( 0 ); 6803 aLine.append( nSizeObject ); 6804 aLine.append( " 0 obj\n" ); 6805 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) ); 6806 aLine.append( "\nendobj\n\n" ); 6807 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6808 return false; 6809 } 6810 return true; 6811 } 6812 6813 bool PDFWriterImpl::emit() 6814 { 6815 endPage(); 6816 6817 // resort structure tree and annotations if necessary 6818 // needed for widget tab order 6819 sortWidgets(); 6820 6821 // emit additional streams 6822 CHECK_RETURN( emitAdditionalStreams() ); 6823 6824 // emit catalog 6825 CHECK_RETURN( emitCatalog() ); 6826 6827 // emit trailer 6828 CHECK_RETURN( emitTrailer() ); 6829 6830 osl_closeFile( m_aFile ); 6831 m_bOpen = false; 6832 6833 return true; 6834 } 6835 6836 std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors() 6837 { 6838 return m_aErrors; 6839 } 6840 6841 sal_Int32 PDFWriterImpl::getSystemFont( const Font& i_rFont ) 6842 { 6843 getReferenceDevice()->Push(); 6844 getReferenceDevice()->SetFont( i_rFont ); 6845 getReferenceDevice()->ImplNewFont(); 6846 6847 const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData; 6848 sal_Int32 nFontID = 0; 6849 FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont ); 6850 if( it != m_aSystemFonts.end() ) 6851 nFontID = it->second.m_nNormalFontID; 6852 else 6853 { 6854 nFontID = m_nNextFID++; 6855 m_aSystemFonts[ pDevFont ] = EmbedFont(); 6856 m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID; 6857 } 6858 6859 getReferenceDevice()->Pop(); 6860 getReferenceDevice()->ImplNewFont(); 6861 6862 return nFontID; 6863 } 6864 6865 void PDFWriterImpl::registerGlyphs( int nGlyphs, 6866 sal_GlyphId* pGlyphs, 6867 sal_Int32* pGlyphWidths, 6868 sal_Ucs* pUnicodes, 6869 sal_Int32* pUnicodesPerGlyph, 6870 sal_uInt8* pMappedGlyphs, 6871 sal_Int32* pMappedFontObjects, 6872 const ImplFontData* pFallbackFonts[] ) 6873 { 6874 const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData; 6875 sal_Ucs* pCurUnicode = pUnicodes; 6876 for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ ) 6877 { 6878 const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB); 6879 const ImplFontData* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont; 6880 6881 if( isBuiltinFont( pCurrentFont ) ) 6882 { 6883 sal_Int32 nFontID = 0; 6884 FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont ); 6885 if( it != m_aEmbeddedFonts.end() ) 6886 nFontID = it->second.m_nNormalFontID; 6887 else 6888 { 6889 nFontID = m_nNextFID++; 6890 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont(); 6891 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID; 6892 } 6893 6894 pGlyphWidths[ i ] = 0; 6895 pMappedGlyphs[ i ] = sal::static_int_cast<sal_Int8>( nFontGlyphId ); 6896 pMappedFontObjects[ i ] = nFontID; 6897 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pCurrentFont ); 6898 if( pFD ) 6899 { 6900 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); 6901 pGlyphWidths[i] = pBuiltinFont->m_aWidths[ nFontGlyphId & 0x00ff ]; 6902 } 6903 } 6904 else if( pCurrentFont->mbSubsettable ) 6905 { 6906 FontSubset& rSubset = m_aSubsets[ pCurrentFont ]; 6907 // search for font specific glyphID 6908 FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId ); 6909 if( it != rSubset.m_aMapping.end() ) 6910 { 6911 pMappedFontObjects[i] = it->second.m_nFontID; 6912 pMappedGlyphs[i] = it->second.m_nSubsetGlyphID; 6913 } 6914 else 6915 { 6916 // create new subset if necessary 6917 if( rSubset.m_aSubsets.empty() 6918 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) ) 6919 { 6920 rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) ); 6921 } 6922 6923 // copy font id 6924 pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID; 6925 // create new glyph in subset 6926 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1); 6927 pMappedGlyphs[i] = nNewId; 6928 6929 // add new glyph to emitted font subset 6930 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ]; 6931 rNewGlyphEmit.setGlyphId( nNewId ); 6932 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ ) 6933 rNewGlyphEmit.addCode( pCurUnicode[n] ); 6934 6935 // add new glyph to font mapping 6936 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ]; 6937 rNewGlyph.m_nFontID = pMappedFontObjects[i]; 6938 rNewGlyph.m_nSubsetGlyphID = nNewId; 6939 } 6940 getReferenceDevice()->ImplGetGraphics(); 6941 const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0); 6942 pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont, 6943 nFontGlyphId, 6944 bVertical, 6945 m_pReferenceDevice->mpGraphics ); 6946 } 6947 else if( pCurrentFont->IsEmbeddable() ) 6948 { 6949 sal_Int32 nFontID = 0; 6950 FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont ); 6951 if( it != m_aEmbeddedFonts.end() ) 6952 nFontID = it->second.m_nNormalFontID; 6953 else 6954 { 6955 nFontID = m_nNextFID++; 6956 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont(); 6957 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID; 6958 } 6959 EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont]; 6960 6961 const Ucs2SIntMap* pEncoding = NULL; 6962 const Ucs2OStrMap* pNonEncoded = NULL; 6963 getReferenceDevice()->ImplGetGraphics(); 6964 pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded ); 6965 6966 Ucs2SIntMap::const_iterator enc_it; 6967 Ucs2OStrMap::const_iterator nonenc_it; 6968 6969 sal_Int32 nCurFontID = nFontID; 6970 sal_Ucs cChar = *pCurUnicode; 6971 if( pEncoding ) 6972 { 6973 enc_it = pEncoding->find( cChar ); 6974 if( enc_it != pEncoding->end() && enc_it->second > 0 ) 6975 { 6976 DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" ); 6977 cChar = (sal_Ucs)enc_it->second; 6978 } 6979 else if( (enc_it == pEncoding->end() || enc_it->second == -1) && 6980 pNonEncoded && 6981 (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() ) 6982 { 6983 nCurFontID = 0; 6984 // find non encoded glyph 6985 for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it ) 6986 { 6987 if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() ) 6988 { 6989 nCurFontID = nec_it->m_nFontID; 6990 cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ]; 6991 break; 6992 } 6993 } 6994 if( nCurFontID == 0 ) // new nonencoded glyph 6995 { 6996 if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 ) 6997 { 6998 rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() ); 6999 rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++; 7000 } 7001 EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back(); 7002 rEncoding.m_aEncVector.push_back( EmbedCode() ); 7003 rEncoding.m_aEncVector.back().m_aUnicode = cChar; 7004 rEncoding.m_aEncVector.back().m_aName = nonenc_it->second; 7005 rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1); 7006 nCurFontID = rEncoding.m_nFontID; 7007 cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ]; 7008 } 7009 } 7010 else 7011 pEncoding = NULL; 7012 } 7013 if( ! pEncoding ) 7014 { 7015 if( cChar & 0xff00 ) 7016 { 7017 // some characters can be used by conversion 7018 if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area 7019 cChar -= 0xf000; 7020 else 7021 { 7022 String aString(cChar); 7023 ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 ); 7024 cChar = ((sal_Ucs)aChar.GetChar( 0 )) & 0x00ff; 7025 } 7026 } 7027 } 7028 7029 pMappedGlyphs[ i ] = (sal_Int8)cChar; 7030 pMappedFontObjects[ i ] = nCurFontID; 7031 pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont, 7032 (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR, 7033 false, 7034 m_pReferenceDevice->mpGraphics ); 7035 } 7036 } 7037 } 7038 7039 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines ) 7040 { 7041 push( PUSH_ALL ); 7042 7043 FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief(); 7044 7045 Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor(); 7046 Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor; 7047 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 7048 Color aReliefColor( COL_LIGHTGRAY ); 7049 if( aTextColor == COL_BLACK ) 7050 aTextColor = Color( COL_WHITE ); 7051 if( aTextLineColor == COL_BLACK ) 7052 aTextLineColor = Color( COL_WHITE ); 7053 if( aOverlineColor == COL_BLACK ) 7054 aOverlineColor = Color( COL_WHITE ); 7055 if( aTextColor == COL_WHITE ) 7056 aReliefColor = Color( COL_BLACK ); 7057 7058 Font aSetFont = m_aCurrentPDFState.m_aFont; 7059 aSetFont.SetRelief( RELIEF_NONE ); 7060 aSetFont.SetShadow( sal_False ); 7061 7062 aSetFont.SetColor( aReliefColor ); 7063 setTextLineColor( aReliefColor ); 7064 setOverlineColor( aReliefColor ); 7065 setFont( aSetFont ); 7066 long nOff = 1 + getReferenceDevice()->mnDPIX/300; 7067 if( eRelief == RELIEF_ENGRAVED ) 7068 nOff = -nOff; 7069 7070 rLayout.DrawOffset() += Point( nOff, nOff ); 7071 updateGraphicsState(); 7072 drawLayout( rLayout, rText, bTextLines ); 7073 7074 rLayout.DrawOffset() -= Point( nOff, nOff ); 7075 setTextLineColor( aTextLineColor ); 7076 setOverlineColor( aOverlineColor ); 7077 aSetFont.SetColor( aTextColor ); 7078 setFont( aSetFont ); 7079 updateGraphicsState(); 7080 drawLayout( rLayout, rText, bTextLines ); 7081 7082 // clean up the mess 7083 pop(); 7084 } 7085 7086 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines ) 7087 { 7088 Font aSaveFont = m_aCurrentPDFState.m_aFont; 7089 Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor; 7090 Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 7091 7092 Font& rFont = m_aCurrentPDFState.m_aFont; 7093 if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 ) 7094 rFont.SetColor( Color( COL_LIGHTGRAY ) ); 7095 else 7096 rFont.SetColor( Color( COL_BLACK ) ); 7097 rFont.SetShadow( sal_False ); 7098 rFont.SetOutline( sal_False ); 7099 setFont( rFont ); 7100 setTextLineColor( rFont.GetColor() ); 7101 setOverlineColor( rFont.GetColor() ); 7102 updateGraphicsState(); 7103 7104 long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24); 7105 if( rFont.IsOutline() ) 7106 nOff++; 7107 rLayout.DrawBase() += Point( nOff, nOff ); 7108 drawLayout( rLayout, rText, bTextLines ); 7109 rLayout.DrawBase() -= Point( nOff, nOff ); 7110 7111 setFont( aSaveFont ); 7112 setTextLineColor( aSaveTextLineColor ); 7113 setOverlineColor( aSaveOverlineColor ); 7114 updateGraphicsState(); 7115 } 7116 7117 void PDFWriterImpl::drawVerticalGlyphs( 7118 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs, 7119 OStringBuffer& rLine, 7120 const Point& rAlignOffset, 7121 const Matrix3& rRotScale, 7122 double fAngle, 7123 double fXScale, 7124 double fSkew, 7125 sal_Int32 nFontHeight ) 7126 { 7127 long nXOffset = 0; 7128 Point aCurPos( rGlyphs[0].m_aPos ); 7129 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos ); 7130 aCurPos += rAlignOffset; 7131 for( size_t i = 0; i < rGlyphs.size(); i++ ) 7132 { 7133 // have to emit each glyph on its own 7134 double fDeltaAngle = 0.0; 7135 double fYScale = 1.0; 7136 double fTempXScale = fXScale; 7137 double fSkewB = fSkew; 7138 double fSkewA = 0.0; 7139 7140 Point aDeltaPos; 7141 if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL ) 7142 { 7143 fDeltaAngle = M_PI/2.0; 7144 aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent(); 7145 aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale); 7146 fYScale = fXScale; 7147 fTempXScale = 1.0; 7148 fSkewA = -fSkewB; 7149 fSkewB = 0.0; 7150 } 7151 else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR ) 7152 { 7153 fDeltaAngle = -M_PI/2.0; 7154 aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale); 7155 aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent(); 7156 fYScale = fXScale; 7157 fTempXScale = 1.0; 7158 fSkewA = fSkewB; 7159 fSkewB = 0.0; 7160 } 7161 aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) ); 7162 if( i < rGlyphs.size()-1 ) 7163 nXOffset += rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y(); 7164 if( ! rGlyphs[i].m_nGlyphId ) 7165 continue; 7166 7167 aDeltaPos = rRotScale.transform( aDeltaPos ); 7168 7169 Matrix3 aMat; 7170 if( fSkewB != 0.0 || fSkewA != 0.0 ) 7171 aMat.skew( fSkewA, fSkewB ); 7172 aMat.scale( fTempXScale, fYScale ); 7173 aMat.rotate( fAngle+fDeltaAngle ); 7174 aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() ); 7175 aMat.append( m_aPages.back(), rLine ); 7176 rLine.append( " Tm" ); 7177 if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId ) 7178 { 7179 rLine.append( " /F" ); 7180 rLine.append( rGlyphs[i].m_nMappedFontId ); 7181 rLine.append( ' ' ); 7182 m_aPages.back().appendMappedLength( nFontHeight, rLine, true ); 7183 rLine.append( " Tf" ); 7184 } 7185 rLine.append( "<" ); 7186 appendHex( rGlyphs[i].m_nMappedGlyphId, rLine ); 7187 rLine.append( ">Tj\n" ); 7188 } 7189 } 7190 7191 void PDFWriterImpl::drawHorizontalGlyphs( 7192 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs, 7193 OStringBuffer& rLine, 7194 const Point& rAlignOffset, 7195 double fAngle, 7196 double fXScale, 7197 double fSkew, 7198 sal_Int32 nFontHeight, 7199 sal_Int32 nPixelFontHeight 7200 ) 7201 { 7202 // horizontal (= normal) case 7203 7204 // fill in run end indices 7205 // end is marked by index of the first glyph of the next run 7206 // a run is marked by same mapped font id and same Y position 7207 std::vector< sal_uInt32 > aRunEnds; 7208 aRunEnds.reserve( rGlyphs.size() ); 7209 for( size_t i = 1; i < rGlyphs.size(); i++ ) 7210 { 7211 if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId || 7212 rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() ) 7213 { 7214 aRunEnds.push_back(i); 7215 } 7216 } 7217 // last run ends at last glyph 7218 aRunEnds.push_back( rGlyphs.size() ); 7219 7220 // loop over runs of the same font 7221 sal_uInt32 nBeginRun = 0; 7222 for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ ) 7223 { 7224 // setup text matrix 7225 Point aCurPos = rGlyphs[nBeginRun].m_aPos; 7226 // back transformation to current coordinate system 7227 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos ); 7228 aCurPos += rAlignOffset; 7229 // the first run can be set with "Td" operator 7230 // subsequent use of that operator would move 7231 // the texline matrix relative to what was set before 7232 // making use of that would drive us into rounding issues 7233 Matrix3 aMat; 7234 if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 ) 7235 { 7236 m_aPages.back().appendPoint( aCurPos, rLine, false ); 7237 rLine.append( " Td " ); 7238 } 7239 else 7240 { 7241 if( fSkew != 0.0 ) 7242 aMat.skew( 0.0, fSkew ); 7243 aMat.scale( fXScale, 1.0 ); 7244 aMat.rotate( fAngle ); 7245 aMat.translate( aCurPos.X(), aCurPos.Y() ); 7246 aMat.append( m_aPages.back(), rLine ); 7247 rLine.append( " Tm\n" ); 7248 } 7249 // set up correct font 7250 rLine.append( "/F" ); 7251 rLine.append( rGlyphs[nBeginRun].m_nMappedFontId ); 7252 rLine.append( ' ' ); 7253 m_aPages.back().appendMappedLength( nFontHeight, rLine, true ); 7254 rLine.append( " Tf" ); 7255 7256 // output glyphs using Tj or TJ 7257 OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 ); 7258 aKernedLine.append( "[<" ); 7259 aUnkernedLine.append( '<' ); 7260 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine ); 7261 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine ); 7262 7263 aMat.invert(); 7264 bool bNeedKern = false; 7265 for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ ) 7266 { 7267 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine ); 7268 // check if default glyph positioning is sufficient 7269 const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos ); 7270 const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos ); 7271 double fAdvance = aThisPos.X() - aPrevPos.X(); 7272 fAdvance *= 1000.0 / nPixelFontHeight; 7273 const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5); 7274 if( nAdjustment != 0 ) 7275 { 7276 // apply individual glyph positioning 7277 bNeedKern = true; 7278 aKernedLine.append( ">" ); 7279 aKernedLine.append( nAdjustment ); 7280 aKernedLine.append( "<" ); 7281 } 7282 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine ); 7283 } 7284 aKernedLine.append( ">]TJ\n" ); 7285 aUnkernedLine.append( ">Tj\n" ); 7286 rLine.append( bNeedKern ? aKernedLine : aUnkernedLine ); 7287 7288 // set beginning of next run 7289 nBeginRun = aRunEnds[nRun]; 7290 } 7291 } 7292 7293 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines ) 7294 { 7295 // relief takes precedence over shadow (see outdev3.cxx) 7296 if( m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE ) 7297 { 7298 drawRelief( rLayout, rText, bTextLines ); 7299 return; 7300 } 7301 else if( m_aCurrentPDFState.m_aFont.IsShadow() ) 7302 drawShadow( rLayout, rText, bTextLines ); 7303 7304 OStringBuffer aLine( 512 ); 7305 7306 const int nMaxGlyphs = 256; 7307 7308 sal_GlyphId pGlyphs[nMaxGlyphs]; 7309 sal_Int32 pGlyphWidths[nMaxGlyphs]; 7310 sal_uInt8 pMappedGlyphs[nMaxGlyphs]; 7311 sal_Int32 pMappedFontObjects[nMaxGlyphs]; 7312 std::vector<sal_Ucs> aUnicodes; 7313 aUnicodes.reserve( nMaxGlyphs ); 7314 sal_Int32 pUnicodesPerGlyph[nMaxGlyphs]; 7315 int pCharPosAry[nMaxGlyphs]; 7316 sal_Int32 nAdvanceWidths[nMaxGlyphs]; 7317 const ImplFontData* pFallbackFonts[nMaxGlyphs]; 7318 bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical(); 7319 int nGlyphs; 7320 int nIndex = 0; 7321 int nMinCharPos = 0, nMaxCharPos = rText.Len()-1; 7322 double fXScale = 1.0; 7323 double fSkew = 0.0; 7324 sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight; 7325 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); 7326 7327 // transform font height back to current units 7328 // note: the layout calculates in outdevs device pixel !! 7329 sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight ); 7330 if( m_aCurrentPDFState.m_aFont.GetWidth() ) 7331 { 7332 Font aFont( m_aCurrentPDFState.m_aFont ); 7333 aFont.SetWidth( 0 ); 7334 FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont ); 7335 if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() ) 7336 { 7337 fXScale = 7338 (double)m_aCurrentPDFState.m_aFont.GetWidth() / 7339 (double)aMetric.GetWidth(); 7340 } 7341 // force state before GetFontMetric 7342 m_pReferenceDevice->ImplNewFont(); 7343 } 7344 7345 // perform artificial italics if necessary 7346 if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL || 7347 m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) && 7348 !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL || 7349 m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE ) 7350 ) 7351 { 7352 fSkew = M_PI/12.0; 7353 } 7354 7355 // if the mapmode is distorted we need to adjust for that also 7356 if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() ) 7357 { 7358 fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY()); 7359 } 7360 7361 int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation(); 7362 // normalize angles 7363 while( nAngle < 0 ) 7364 nAngle += 3600; 7365 nAngle = nAngle % 3600; 7366 double fAngle = (double)nAngle * M_PI / 1800.0; 7367 7368 Matrix3 aRotScale; 7369 aRotScale.scale( fXScale, 1.0 ); 7370 if( fAngle != 0.0 ) 7371 aRotScale.rotate( -fAngle ); 7372 7373 bool bPop = false; 7374 bool bABold = false; 7375 // artificial bold necessary ? 7376 if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM && 7377 m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM ) 7378 { 7379 if( ! bPop ) 7380 aLine.append( "q " ); 7381 bPop = true; 7382 bABold = true; 7383 } 7384 // setup text colors (if necessary) 7385 Color aStrokeColor( COL_TRANSPARENT ); 7386 Color aNonStrokeColor( COL_TRANSPARENT ); 7387 7388 if( m_aCurrentPDFState.m_aFont.IsOutline() ) 7389 { 7390 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7391 aNonStrokeColor = Color( COL_WHITE ); 7392 } 7393 else 7394 aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7395 if( bABold ) 7396 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7397 7398 if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor ) 7399 { 7400 if( ! bPop ) 7401 aLine.append( "q " ); 7402 bPop = true; 7403 appendStrokingColor( aStrokeColor, aLine ); 7404 aLine.append( "\n" ); 7405 } 7406 if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor ) 7407 { 7408 if( ! bPop ) 7409 aLine.append( "q " ); 7410 bPop = true; 7411 appendNonStrokingColor( aNonStrokeColor, aLine ); 7412 aLine.append( "\n" ); 7413 } 7414 7415 // begin text object 7416 aLine.append( "BT\n" ); 7417 // outline attribute ? 7418 if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold ) 7419 { 7420 // set correct text mode, set stroke width 7421 aLine.append( "2 Tr " ); // fill, then stroke 7422 7423 if( m_aCurrentPDFState.m_aFont.IsOutline() ) 7424 { 7425 // unclear what to do in case of outline and artificial bold 7426 // for the time being outline wins 7427 aLine.append( "0.25 w \n" ); 7428 } 7429 else 7430 { 7431 double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0; 7432 m_aPages.back().appendMappedLength( fW, aLine ); 7433 aLine.append ( " w\n" ); 7434 } 7435 } 7436 7437 FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric(); 7438 7439 // collect the glyphs into a single array 7440 const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686# 7441 std::vector< PDFGlyph > aGlyphs; 7442 aGlyphs.reserve( nTmpMaxGlyphs ); 7443 // first get all the glyphs and register them; coordinates still in Pixel 7444 Point aGNGlyphPos; 7445 while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 ) 7446 { 7447 aUnicodes.clear(); 7448 for( int i = 0; i < nGlyphs; i++ ) 7449 { 7450 pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] ); 7451 7452 // default case: 1 glyph is one unicode 7453 pUnicodesPerGlyph[i] = 1; 7454 if( (pGlyphs[i] & GF_ISCHAR) ) 7455 { 7456 aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) ); 7457 } 7458 else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos ) 7459 { 7460 int nChars = 1; 7461 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) ); 7462 pUnicodesPerGlyph[i] = 1; 7463 // try to handle ligatures and such 7464 if( i < nGlyphs-1 ) 7465 { 7466 nChars = pCharPosAry[i+1] - pCharPosAry[i]; 7467 // #i115618# fix for simple RTL+CTL cases 7468 // TODO: sanitize for RTL ligatures, more complex CTL, etc. 7469 if( nChars < 0 ) 7470 nChars = -nChars; 7471 else if( nChars == 0 ) 7472 nChars = 1; 7473 pUnicodesPerGlyph[i] = nChars; 7474 for( int n = 1; n < nChars; n++ ) 7475 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) ); 7476 } 7477 // #i36691# hack that is needed because currently the pGlyphs[] 7478 // argument is ignored for embeddable fonts and so the layout 7479 // engine's glyph work is ignored (i.e. char mirroring) 7480 // TODO: a real solution would be to map the layout engine's 7481 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font) 7482 // back to unicode and then to embeddable font's encoding 7483 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL ) 7484 { 7485 size_t nI = aUnicodes.size()-1; 7486 for( int n = 0; n < nChars; n++, nI-- ) 7487 aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI])); 7488 } 7489 } 7490 else 7491 aUnicodes.push_back( 0 ); 7492 // note: in case of ctl one character may result 7493 // in multiple glyphs. The current SalLayout 7494 // implementations set -1 then to indicate that no direct 7495 // mapping is possible 7496 } 7497 7498 registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts ); 7499 7500 for( int i = 0; i < nGlyphs; i++ ) 7501 { 7502 aGlyphs.push_back( PDFGlyph( aGNGlyphPos, 7503 pGlyphWidths[i], 7504 pGlyphs[i], 7505 pMappedFontObjects[i], 7506 pMappedGlyphs[i] ) ); 7507 if( bVertical ) 7508 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel(); 7509 else 7510 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel(); 7511 } 7512 } 7513 7514 Point aAlignOffset; 7515 if ( eAlign == ALIGN_BOTTOM ) 7516 aAlignOffset.Y() -= aRefDevFontMetric.GetDescent(); 7517 else if ( eAlign == ALIGN_TOP ) 7518 aAlignOffset.Y() += aRefDevFontMetric.GetAscent(); 7519 if( aAlignOffset.X() || aAlignOffset.Y() ) 7520 aAlignOffset = aRotScale.transform( aAlignOffset ); 7521 7522 /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original 7523 string contained only on of the UTF16 BOMs 7524 */ 7525 if( ! aGlyphs.empty() ) 7526 { 7527 if( bVertical ) 7528 drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight ); 7529 else 7530 drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight ); 7531 } 7532 7533 // end textobject 7534 aLine.append( "ET\n" ); 7535 if( bPop ) 7536 aLine.append( "Q\n" ); 7537 7538 writeBuffer( aLine.getStr(), aLine.getLength() ); 7539 7540 // draw eventual textlines 7541 FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout(); 7542 FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline(); 7543 FontUnderline eOverline = m_aCurrentPDFState.m_aFont.GetOverline(); 7544 if( bTextLines && 7545 ( 7546 ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) || 7547 ( eOverline != UNDERLINE_NONE && eOverline != UNDERLINE_DONTKNOW ) || 7548 ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW ) 7549 ) 7550 ) 7551 { 7552 sal_Bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont ); 7553 if( m_aCurrentPDFState.m_aFont.IsWordLineMode() ) 7554 { 7555 Point aPos, aStartPt; 7556 sal_Int32 nWidth = 0, nAdvance=0; 7557 for( int nStart = 0;;) 7558 { 7559 sal_GlyphId nGlyphIndex; 7560 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) 7561 break; 7562 7563 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) 7564 { 7565 if( !nWidth ) 7566 aStartPt = aPos; 7567 7568 nWidth += nAdvance; 7569 } 7570 else if( nWidth > 0 ) 7571 { 7572 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7573 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7574 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7575 nWidth = 0; 7576 } 7577 } 7578 7579 if( nWidth > 0 ) 7580 { 7581 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7582 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7583 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7584 } 7585 } 7586 else 7587 { 7588 Point aStartPt = rLayout.GetDrawPosition(); 7589 int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel(); 7590 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7591 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7592 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7593 } 7594 } 7595 7596 // write eventual emphasis marks 7597 if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE ) 7598 { 7599 PolyPolygon aEmphPoly; 7600 Rectangle aEmphRect1; 7601 Rectangle aEmphRect2; 7602 long nEmphYOff; 7603 long nEmphWidth; 7604 long nEmphHeight; 7605 sal_Bool bEmphPolyLine; 7606 FontEmphasisMark nEmphMark; 7607 7608 push( PUSH_ALL ); 7609 7610 aLine.setLength( 0 ); 7611 aLine.append( "q\n" ); 7612 7613 nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont ); 7614 if ( nEmphMark & EMPHASISMARK_POS_BELOW ) 7615 nEmphHeight = m_pReferenceDevice->mnEmphasisDescent; 7616 else 7617 nEmphHeight = m_pReferenceDevice->mnEmphasisAscent; 7618 m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly, 7619 bEmphPolyLine, 7620 aEmphRect1, 7621 aEmphRect2, 7622 nEmphYOff, 7623 nEmphWidth, 7624 nEmphMark, 7625 m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight), 7626 m_pReferenceDevice->mpFontEntry->mnOrientation ); 7627 if ( bEmphPolyLine ) 7628 { 7629 setLineColor( m_aCurrentPDFState.m_aFont.GetColor() ); 7630 setFillColor( Color( COL_TRANSPARENT ) ); 7631 } 7632 else 7633 { 7634 setFillColor( m_aCurrentPDFState.m_aFont.GetColor() ); 7635 setLineColor( Color( COL_TRANSPARENT ) ); 7636 } 7637 writeBuffer( aLine.getStr(), aLine.getLength() ); 7638 7639 Point aOffset = Point(0,0); 7640 7641 if ( nEmphMark & EMPHASISMARK_POS_BELOW ) 7642 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff; 7643 else 7644 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff; 7645 7646 long nEmphWidth2 = nEmphWidth / 2; 7647 long nEmphHeight2 = nEmphHeight / 2; 7648 aOffset += Point( nEmphWidth2, nEmphHeight2 ); 7649 7650 if ( eAlign == ALIGN_BOTTOM ) 7651 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent; 7652 else if ( eAlign == ALIGN_TOP ) 7653 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent; 7654 7655 for( int nStart = 0;;) 7656 { 7657 Point aPos; 7658 sal_GlyphId nGlyphIndex; 7659 sal_Int32 nAdvance; 7660 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) 7661 break; 7662 7663 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) 7664 { 7665 Point aAdjOffset = aOffset; 7666 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2; 7667 aAdjOffset = aRotScale.transform( aAdjOffset ); 7668 7669 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 ); 7670 7671 aPos += aAdjOffset; 7672 aPos = m_pReferenceDevice->PixelToLogic( aPos ); 7673 drawEmphasisMark( aPos.X(), aPos.Y(), 7674 aEmphPoly, bEmphPolyLine, 7675 aEmphRect1, aEmphRect2 ); 7676 } 7677 } 7678 7679 writeBuffer( "Q\n", 2 ); 7680 pop(); 7681 } 7682 7683 } 7684 7685 void PDFWriterImpl::drawEmphasisMark( long nX, long nY, 7686 const PolyPolygon& rPolyPoly, sal_Bool bPolyLine, 7687 const Rectangle& rRect1, const Rectangle& rRect2 ) 7688 { 7689 // TODO: pass nWidth as width of this mark 7690 // long nWidth = 0; 7691 7692 if ( rPolyPoly.Count() ) 7693 { 7694 if ( bPolyLine ) 7695 { 7696 Polygon aPoly = rPolyPoly.GetObject( 0 ); 7697 aPoly.Move( nX, nY ); 7698 drawPolyLine( aPoly ); 7699 } 7700 else 7701 { 7702 PolyPolygon aPolyPoly = rPolyPoly; 7703 aPolyPoly.Move( nX, nY ); 7704 drawPolyPolygon( aPolyPoly ); 7705 } 7706 } 7707 7708 if ( !rRect1.IsEmpty() ) 7709 { 7710 Rectangle aRect( Point( nX+rRect1.Left(), 7711 nY+rRect1.Top() ), rRect1.GetSize() ); 7712 drawRectangle( aRect ); 7713 } 7714 7715 if ( !rRect2.IsEmpty() ) 7716 { 7717 Rectangle aRect( Point( nX+rRect2.Left(), 7718 nY+rRect2.Top() ), rRect2.GetSize() ); 7719 7720 drawRectangle( aRect ); 7721 } 7722 } 7723 7724 void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7725 { 7726 MARK( "drawText" ); 7727 7728 updateGraphicsState(); 7729 7730 // get a layout from the OuputDevice's SalGraphics 7731 // this also enforces font substitution and sets the font on SalGraphics 7732 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos ); 7733 if( pLayout ) 7734 { 7735 drawLayout( *pLayout, rText, bTextLines ); 7736 pLayout->Release(); 7737 } 7738 } 7739 7740 void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7741 { 7742 MARK( "drawText with array" ); 7743 7744 updateGraphicsState(); 7745 7746 // get a layout from the OuputDevice's SalGraphics 7747 // this also enforces font substitution and sets the font on SalGraphics 7748 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray ); 7749 if( pLayout ) 7750 { 7751 drawLayout( *pLayout, rText, bTextLines ); 7752 pLayout->Release(); 7753 } 7754 } 7755 7756 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7757 { 7758 MARK( "drawStretchText" ); 7759 7760 updateGraphicsState(); 7761 7762 // get a layout from the OuputDevice's SalGraphics 7763 // this also enforces font substitution and sets the font on SalGraphics 7764 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth ); 7765 if( pLayout ) 7766 { 7767 drawLayout( *pLayout, rText, bTextLines ); 7768 pLayout->Release(); 7769 } 7770 } 7771 7772 void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines ) 7773 { 7774 long nWidth = rRect.GetWidth(); 7775 long nHeight = rRect.GetHeight(); 7776 7777 if ( nWidth <= 0 || nHeight <= 0 ) 7778 return; 7779 7780 MARK( "drawText with rectangle" ); 7781 7782 updateGraphicsState(); 7783 7784 // clip with rectangle 7785 OStringBuffer aLine; 7786 aLine.append( "q " ); 7787 m_aPages.back().appendRect( rRect, aLine ); 7788 aLine.append( " W* n\n" ); 7789 writeBuffer( aLine.getStr(), aLine.getLength() ); 7790 7791 // if disabled text is needed, put in here 7792 7793 Point aPos = rRect.TopLeft(); 7794 7795 long nTextHeight = m_pReferenceDevice->GetTextHeight(); 7796 xub_StrLen nMnemonicPos = STRING_NOTFOUND; 7797 7798 String aStr = rOrigStr; 7799 if ( nStyle & TEXT_DRAW_MNEMONIC ) 7800 aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos ); 7801 7802 // multiline text 7803 if ( nStyle & TEXT_DRAW_MULTILINE ) 7804 { 7805 XubString aLastLine; 7806 ImplMultiTextLineInfo aMultiLineInfo; 7807 ImplTextLineInfo* pLineInfo; 7808 long nMaxTextWidth; 7809 xub_StrLen i; 7810 xub_StrLen nLines; 7811 xub_StrLen nFormatLines; 7812 7813 if ( nTextHeight ) 7814 { 7815 ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice ); 7816 nMaxTextWidth = OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout ); 7817 nLines = (xub_StrLen)(nHeight/nTextHeight); 7818 nFormatLines = aMultiLineInfo.Count(); 7819 if ( !nLines ) 7820 nLines = 1; 7821 if ( nFormatLines > nLines ) 7822 { 7823 if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) 7824 { 7825 // handle last line 7826 nFormatLines = nLines-1; 7827 7828 pLineInfo = aMultiLineInfo.GetLine( nFormatLines ); 7829 aLastLine = aStr.Copy( pLineInfo->GetIndex() ); 7830 aLastLine.ConvertLineEnd( LINEEND_LF ); 7831 // replace line feed by space 7832 xub_StrLen nLastLineLen = aLastLine.Len(); 7833 for ( i = 0; i < nLastLineLen; i++ ) 7834 { 7835 if ( aLastLine.GetChar( i ) == _LF ) 7836 aLastLine.SetChar( i, ' ' ); 7837 } 7838 aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle ); 7839 nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM); 7840 nStyle |= TEXT_DRAW_TOP; 7841 } 7842 } 7843 7844 // vertical alignment 7845 if ( nStyle & TEXT_DRAW_BOTTOM ) 7846 aPos.Y() += nHeight-(nFormatLines*nTextHeight); 7847 else if ( nStyle & TEXT_DRAW_VCENTER ) 7848 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2; 7849 7850 // draw all lines excluding the last 7851 for ( i = 0; i < nFormatLines; i++ ) 7852 { 7853 pLineInfo = aMultiLineInfo.GetLine( i ); 7854 if ( nStyle & TEXT_DRAW_RIGHT ) 7855 aPos.X() += nWidth-pLineInfo->GetWidth(); 7856 else if ( nStyle & TEXT_DRAW_CENTER ) 7857 aPos.X() += (nWidth-pLineInfo->GetWidth())/2; 7858 xub_StrLen nIndex = pLineInfo->GetIndex(); 7859 xub_StrLen nLineLen = pLineInfo->GetLen(); 7860 drawText( aPos, aStr, nIndex, nLineLen, bTextLines ); 7861 // mnemonics should not appear in documents, 7862 // if the need arises, put them in here 7863 aPos.Y() += nTextHeight; 7864 aPos.X() = rRect.Left(); 7865 } 7866 7867 7868 // output last line left adjusted since it was shortened 7869 if ( aLastLine.Len() ) 7870 drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines ); 7871 } 7872 } 7873 else 7874 { 7875 long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); 7876 7877 // Evt. Text kuerzen 7878 if ( nTextWidth > nWidth ) 7879 { 7880 if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) ) 7881 { 7882 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle ); 7883 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT); 7884 nStyle |= TEXT_DRAW_LEFT; 7885 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); 7886 } 7887 } 7888 7889 // vertical alignment 7890 if ( nStyle & TEXT_DRAW_RIGHT ) 7891 aPos.X() += nWidth-nTextWidth; 7892 else if ( nStyle & TEXT_DRAW_CENTER ) 7893 aPos.X() += (nWidth-nTextWidth)/2; 7894 7895 if ( nStyle & TEXT_DRAW_BOTTOM ) 7896 aPos.Y() += nHeight-nTextHeight; 7897 else if ( nStyle & TEXT_DRAW_VCENTER ) 7898 aPos.Y() += (nHeight-nTextHeight)/2; 7899 7900 // mnemonics should be inserted here if the need arises 7901 7902 // draw the actual text 7903 drawText( aPos, aStr, 0, STRING_LEN, bTextLines ); 7904 } 7905 7906 // reset clip region to original value 7907 aLine.setLength( 0 ); 7908 aLine.append( "Q\n" ); 7909 writeBuffer( aLine.getStr(), aLine.getLength() ); 7910 } 7911 7912 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop ) 7913 { 7914 MARK( "drawLine" ); 7915 7916 updateGraphicsState(); 7917 7918 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7919 return; 7920 7921 OStringBuffer aLine; 7922 m_aPages.back().appendPoint( rStart, aLine ); 7923 aLine.append( " m " ); 7924 m_aPages.back().appendPoint( rStop, aLine ); 7925 aLine.append( " l S\n" ); 7926 7927 writeBuffer( aLine.getStr(), aLine.getLength() ); 7928 } 7929 7930 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo ) 7931 { 7932 MARK( "drawLine with LineInfo" ); 7933 updateGraphicsState(); 7934 7935 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7936 return; 7937 7938 if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 ) 7939 { 7940 drawLine( rStart, rStop ); 7941 return; 7942 } 7943 7944 OStringBuffer aLine; 7945 7946 aLine.append( "q " ); 7947 if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) 7948 { 7949 m_aPages.back().appendPoint( rStart, aLine ); 7950 aLine.append( " m " ); 7951 m_aPages.back().appendPoint( rStop, aLine ); 7952 aLine.append( " l S Q\n" ); 7953 7954 writeBuffer( aLine.getStr(), aLine.getLength() ); 7955 } 7956 else 7957 { 7958 PDFWriter::ExtLineInfo aInfo; 7959 convertLineInfoToExtLineInfo( rInfo, aInfo ); 7960 Point aPolyPoints[2] = { rStart, rStop }; 7961 Polygon aPoly( 2, aPolyPoints ); 7962 drawPolyLine( aPoly, aInfo ); 7963 } 7964 } 7965 7966 void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth ) 7967 { 7968 Point aDiff( rStop-rStart ); 7969 double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) ); 7970 if( fLen < 1.0 ) 7971 return; 7972 7973 MARK( "drawWaveLine" ); 7974 updateGraphicsState(); 7975 7976 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7977 return; 7978 7979 OStringBuffer aLine( 512 ); 7980 aLine.append( "q " ); 7981 m_aPages.back().appendMappedLength( nLineWidth, aLine, true ); 7982 aLine.append( " w " ); 7983 7984 appendDouble( (double)aDiff.X()/fLen, aLine ); 7985 aLine.append( ' ' ); 7986 appendDouble( -(double)aDiff.Y()/fLen, aLine ); 7987 aLine.append( ' ' ); 7988 appendDouble( (double)aDiff.Y()/fLen, aLine ); 7989 aLine.append( ' ' ); 7990 appendDouble( (double)aDiff.X()/fLen, aLine ); 7991 aLine.append( ' ' ); 7992 m_aPages.back().appendPoint( rStart, aLine ); 7993 aLine.append( " cm " ); 7994 m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine ); 7995 aLine.append( "Q\n" ); 7996 writeBuffer( aLine.getStr(), aLine.getLength() ); 7997 } 7998 7999 #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x ) 8000 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x ) 8001 8002 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ) 8003 { 8004 // note: units in pFontEntry are ref device pixel 8005 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8006 long nLineHeight = 0; 8007 long nLinePos = 0; 8008 8009 appendStrokingColor( aColor, aLine ); 8010 aLine.append( "\n" ); 8011 8012 if ( bIsAbove ) 8013 { 8014 if ( !pFontEntry->maMetric.mnAboveWUnderlineSize ) 8015 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8016 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize ); 8017 nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset ); 8018 } 8019 else 8020 { 8021 if ( !pFontEntry->maMetric.mnWUnderlineSize ) 8022 m_pReferenceDevice->ImplInitTextLineSize(); 8023 nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize ); 8024 nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset ); 8025 } 8026 if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) ) 8027 nLineHeight = 3; 8028 8029 long nLineWidth = getReferenceDevice()->mnDPIX/450; 8030 if ( ! nLineWidth ) 8031 nLineWidth = 1; 8032 8033 if ( eTextLine == UNDERLINE_BOLDWAVE ) 8034 nLineWidth = 3*nLineWidth; 8035 8036 m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine ); 8037 aLine.append( " w " ); 8038 8039 if ( eTextLine == UNDERLINE_DOUBLEWAVE ) 8040 { 8041 long nOrgLineHeight = nLineHeight; 8042 nLineHeight /= 3; 8043 if ( nLineHeight < 2 ) 8044 { 8045 if ( nOrgLineHeight > 1 ) 8046 nLineHeight = 2; 8047 else 8048 nLineHeight = 1; 8049 } 8050 long nLineDY = nOrgLineHeight-(nLineHeight*2); 8051 if ( nLineDY < nLineWidth ) 8052 nLineDY = nLineWidth; 8053 long nLineDY2 = nLineDY/2; 8054 if ( !nLineDY2 ) 8055 nLineDY2 = 1; 8056 8057 nLinePos -= nLineWidth-nLineDY2; 8058 8059 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); 8060 8061 nLinePos += nLineWidth+nLineDY; 8062 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); 8063 } 8064 else 8065 { 8066 if ( eTextLine != UNDERLINE_BOLDWAVE ) 8067 nLinePos -= nLineWidth/2; 8068 m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine ); 8069 } 8070 } 8071 8072 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ) 8073 { 8074 // note: units in pFontEntry are ref device pixel 8075 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8076 long nLineHeight = 0; 8077 long nLinePos = 0; 8078 long nLinePos2 = 0; 8079 8080 if ( eTextLine > UNDERLINE_BOLDWAVE ) 8081 eTextLine = UNDERLINE_SINGLE; 8082 8083 switch ( eTextLine ) 8084 { 8085 case UNDERLINE_SINGLE: 8086 case UNDERLINE_DOTTED: 8087 case UNDERLINE_DASH: 8088 case UNDERLINE_LONGDASH: 8089 case UNDERLINE_DASHDOT: 8090 case UNDERLINE_DASHDOTDOT: 8091 if ( bIsAbove ) 8092 { 8093 if ( !pFontEntry->maMetric.mnAboveUnderlineSize ) 8094 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8095 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize ); 8096 nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset ); 8097 } 8098 else 8099 { 8100 if ( !pFontEntry->maMetric.mnUnderlineSize ) 8101 m_pReferenceDevice->ImplInitTextLineSize(); 8102 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize ); 8103 nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset ); 8104 } 8105 break; 8106 case UNDERLINE_BOLD: 8107 case UNDERLINE_BOLDDOTTED: 8108 case UNDERLINE_BOLDDASH: 8109 case UNDERLINE_BOLDLONGDASH: 8110 case UNDERLINE_BOLDDASHDOT: 8111 case UNDERLINE_BOLDDASHDOTDOT: 8112 if ( bIsAbove ) 8113 { 8114 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize ) 8115 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8116 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize ); 8117 nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset ); 8118 } 8119 else 8120 { 8121 if ( !pFontEntry->maMetric.mnBUnderlineSize ) 8122 m_pReferenceDevice->ImplInitTextLineSize(); 8123 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize ); 8124 nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset ); 8125 nLinePos += nLineHeight/2; 8126 } 8127 break; 8128 case UNDERLINE_DOUBLE: 8129 if ( bIsAbove ) 8130 { 8131 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize ) 8132 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8133 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize ); 8134 nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 ); 8135 nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 ); 8136 } 8137 else 8138 { 8139 if ( !pFontEntry->maMetric.mnDUnderlineSize ) 8140 m_pReferenceDevice->ImplInitTextLineSize(); 8141 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize ); 8142 nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 ); 8143 nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 ); 8144 } 8145 default: 8146 break; 8147 } 8148 8149 if ( nLineHeight ) 8150 { 8151 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true ); 8152 aLine.append( " w " ); 8153 appendStrokingColor( aColor, aLine ); 8154 aLine.append( "\n" ); 8155 8156 switch ( eTextLine ) 8157 { 8158 case UNDERLINE_DOTTED: 8159 case UNDERLINE_BOLDDOTTED: 8160 aLine.append( "[ " ); 8161 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8162 aLine.append( " ] 0 d\n" ); 8163 break; 8164 case UNDERLINE_DASH: 8165 case UNDERLINE_LONGDASH: 8166 case UNDERLINE_BOLDDASH: 8167 case UNDERLINE_BOLDLONGDASH: 8168 { 8169 sal_Int32 nDashLength = 4*nLineHeight; 8170 sal_Int32 nVoidLength = 2*nLineHeight; 8171 if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) ) 8172 nDashLength = 8*nLineHeight; 8173 8174 aLine.append( "[ " ); 8175 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 8176 aLine.append( ' ' ); 8177 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8178 aLine.append( " ] 0 d\n" ); 8179 } 8180 break; 8181 case UNDERLINE_DASHDOT: 8182 case UNDERLINE_BOLDDASHDOT: 8183 { 8184 sal_Int32 nDashLength = 4*nLineHeight; 8185 sal_Int32 nVoidLength = 2*nLineHeight; 8186 aLine.append( "[ " ); 8187 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 8188 aLine.append( ' ' ); 8189 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8190 aLine.append( ' ' ); 8191 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8192 aLine.append( ' ' ); 8193 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8194 aLine.append( " ] 0 d\n" ); 8195 } 8196 break; 8197 case UNDERLINE_DASHDOTDOT: 8198 case UNDERLINE_BOLDDASHDOTDOT: 8199 { 8200 sal_Int32 nDashLength = 4*nLineHeight; 8201 sal_Int32 nVoidLength = 2*nLineHeight; 8202 aLine.append( "[ " ); 8203 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 8204 aLine.append( ' ' ); 8205 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8206 aLine.append( ' ' ); 8207 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8208 aLine.append( ' ' ); 8209 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8210 aLine.append( ' ' ); 8211 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8212 aLine.append( ' ' ); 8213 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8214 aLine.append( " ] 0 d\n" ); 8215 } 8216 break; 8217 default: 8218 break; 8219 } 8220 8221 aLine.append( "0 " ); 8222 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8223 aLine.append( " m " ); 8224 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); 8225 aLine.append( ' ' ); 8226 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8227 aLine.append( " l S\n" ); 8228 if ( eTextLine == UNDERLINE_DOUBLE ) 8229 { 8230 aLine.append( "0 " ); 8231 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8232 aLine.append( " m " ); 8233 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); 8234 aLine.append( ' ' ); 8235 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8236 aLine.append( " l S\n" ); 8237 } 8238 } 8239 } 8240 8241 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor ) 8242 { 8243 // note: units in pFontEntry are ref device pixel 8244 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8245 long nLineHeight = 0; 8246 long nLinePos = 0; 8247 long nLinePos2 = 0; 8248 8249 if ( eStrikeout > STRIKEOUT_X ) 8250 eStrikeout = STRIKEOUT_SINGLE; 8251 8252 switch ( eStrikeout ) 8253 { 8254 case STRIKEOUT_SINGLE: 8255 if ( !pFontEntry->maMetric.mnStrikeoutSize ) 8256 m_pReferenceDevice->ImplInitTextLineSize(); 8257 nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize ); 8258 nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset ); 8259 break; 8260 case STRIKEOUT_BOLD: 8261 if ( !pFontEntry->maMetric.mnBStrikeoutSize ) 8262 m_pReferenceDevice->ImplInitTextLineSize(); 8263 nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize ); 8264 nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset ); 8265 break; 8266 case STRIKEOUT_DOUBLE: 8267 if ( !pFontEntry->maMetric.mnDStrikeoutSize ) 8268 m_pReferenceDevice->ImplInitTextLineSize(); 8269 nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize ); 8270 nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 ); 8271 nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 ); 8272 break; 8273 default: 8274 break; 8275 } 8276 8277 if ( nLineHeight ) 8278 { 8279 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true ); 8280 aLine.append( " w " ); 8281 appendStrokingColor( aColor, aLine ); 8282 aLine.append( "\n" ); 8283 8284 aLine.append( "0 " ); 8285 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8286 aLine.append( " m " ); 8287 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true ); 8288 aLine.append( ' ' ); 8289 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8290 aLine.append( " l S\n" ); 8291 8292 if ( eStrikeout == STRIKEOUT_DOUBLE ) 8293 { 8294 aLine.append( "0 " ); 8295 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8296 aLine.append( " m " ); 8297 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true ); 8298 aLine.append( ' ' ); 8299 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8300 aLine.append( " l S\n" ); 8301 } 8302 } 8303 } 8304 8305 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout ) 8306 { 8307 String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" ); 8308 String aStrikeout = aStrikeoutChar; 8309 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth ) 8310 aStrikeout.Append( aStrikeout ); 8311 8312 // do not get broader than nWidth modulo 1 character 8313 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth ) 8314 aStrikeout.Erase( 0, 1 ); 8315 aStrikeout.Append( aStrikeoutChar ); 8316 sal_Bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow(); 8317 if ( bShadow ) 8318 { 8319 Font aFont = m_aCurrentPDFState.m_aFont; 8320 aFont.SetShadow( sal_False ); 8321 setFont( aFont ); 8322 updateGraphicsState(); 8323 } 8324 8325 // strikeout string is left aligned non-CTL text 8326 sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode(); 8327 m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED ); 8328 drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false ); 8329 m_pReferenceDevice->SetLayoutMode( nOrigTLM ); 8330 8331 if ( bShadow ) 8332 { 8333 Font aFont = m_aCurrentPDFState.m_aFont; 8334 aFont.SetShadow( sal_True ); 8335 setFont( aFont ); 8336 updateGraphicsState(); 8337 } 8338 } 8339 8340 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove ) 8341 { 8342 if ( !nWidth || 8343 ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) && 8344 ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) && 8345 ((eOverline == UNDERLINE_NONE)||(eOverline == UNDERLINE_DONTKNOW)) ) ) 8346 return; 8347 8348 MARK( "drawTextLine" ); 8349 updateGraphicsState(); 8350 8351 // note: units in pFontEntry are ref device pixel 8352 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8353 Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor; 8354 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 8355 Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor(); 8356 bool bStrikeoutDone = false; 8357 bool bUnderlineDone = false; 8358 bool bOverlineDone = false; 8359 8360 if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) ) 8361 { 8362 drawStrikeoutChar( rPos, nWidth, eStrikeout ); 8363 bStrikeoutDone = true; 8364 } 8365 8366 Point aPos( rPos ); 8367 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); 8368 if( eAlign == ALIGN_TOP ) 8369 aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent ); 8370 else if( eAlign == ALIGN_BOTTOM ) 8371 aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent ); 8372 8373 OStringBuffer aLine( 512 ); 8374 // save GS 8375 aLine.append( "q " ); 8376 8377 // rotate and translate matrix 8378 double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0; 8379 Matrix3 aMat; 8380 aMat.rotate( fAngle ); 8381 aMat.translate( aPos.X(), aPos.Y() ); 8382 aMat.append( m_aPages.back(), aLine ); 8383 aLine.append( " cm\n" ); 8384 8385 if ( aUnderlineColor.GetTransparency() != 0 ) 8386 aUnderlineColor = aStrikeoutColor; 8387 8388 if ( (eUnderline == UNDERLINE_SMALLWAVE) || 8389 (eUnderline == UNDERLINE_WAVE) || 8390 (eUnderline == UNDERLINE_DOUBLEWAVE) || 8391 (eUnderline == UNDERLINE_BOLDWAVE) ) 8392 { 8393 drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 8394 bUnderlineDone = true; 8395 } 8396 8397 if ( (eOverline == UNDERLINE_SMALLWAVE) || 8398 (eOverline == UNDERLINE_WAVE) || 8399 (eOverline == UNDERLINE_DOUBLEWAVE) || 8400 (eOverline == UNDERLINE_BOLDWAVE) ) 8401 { 8402 drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); 8403 bOverlineDone = true; 8404 } 8405 8406 if ( !bUnderlineDone ) 8407 { 8408 drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 8409 } 8410 8411 if ( !bOverlineDone ) 8412 { 8413 drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); 8414 } 8415 8416 if ( !bStrikeoutDone ) 8417 { 8418 drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor ); 8419 } 8420 8421 aLine.append( "Q\n" ); 8422 writeBuffer( aLine.getStr(), aLine.getLength() ); 8423 } 8424 8425 void PDFWriterImpl::drawPolygon( const Polygon& rPoly ) 8426 { 8427 MARK( "drawPolygon" ); 8428 8429 updateGraphicsState(); 8430 8431 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8432 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8433 return; 8434 8435 int nPoints = rPoly.GetSize(); 8436 OStringBuffer aLine( 20 * nPoints ); 8437 m_aPages.back().appendPolygon( rPoly, aLine ); 8438 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8439 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8440 aLine.append( "B*\n" ); 8441 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8442 aLine.append( "S\n" ); 8443 else 8444 aLine.append( "f*\n" ); 8445 8446 writeBuffer( aLine.getStr(), aLine.getLength() ); 8447 } 8448 8449 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly ) 8450 { 8451 MARK( "drawPolyPolygon" ); 8452 8453 updateGraphicsState(); 8454 8455 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8456 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8457 return; 8458 8459 int nPolygons = rPolyPoly.Count(); 8460 8461 OStringBuffer aLine( 40 * nPolygons ); 8462 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 8463 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8464 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8465 aLine.append( "B*\n" ); 8466 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8467 aLine.append( "S\n" ); 8468 else 8469 aLine.append( "f*\n" ); 8470 8471 writeBuffer( aLine.getStr(), aLine.getLength() ); 8472 } 8473 8474 void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent ) 8475 { 8476 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" ); 8477 nTransparentPercent = nTransparentPercent % 100; 8478 8479 MARK( "drawTransparent" ); 8480 8481 updateGraphicsState(); 8482 8483 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8484 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8485 return; 8486 8487 if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 ) 8488 { 8489 m_aErrors.insert( m_bIsPDF_A1 ? 8490 PDFWriter::Warning_Transparency_Omitted_PDFA : 8491 PDFWriter::Warning_Transparency_Omitted_PDF13 ); 8492 8493 drawPolyPolygon( rPolyPoly ); 8494 return; 8495 } 8496 8497 // create XObject 8498 m_aTransparentObjects.push_back( TransparencyEmit() ); 8499 // FIXME: polygons with beziers may yield incorrect bound rect 8500 m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect(); 8501 // convert rectangle to default user space 8502 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8503 m_aTransparentObjects.back().m_nObject = createObject(); 8504 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8505 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; 8506 m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 ); 8507 // create XObject's content stream 8508 OStringBuffer aContent( 256 ); 8509 m_aPages.back().appendPolyPolygon( rPolyPoly, aContent ); 8510 if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) && 8511 m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) ) 8512 aContent.append( " B*\n" ); 8513 else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) ) 8514 aContent.append( " S\n" ); 8515 else 8516 aContent.append( " f*\n" ); 8517 m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() ); 8518 8519 OStringBuffer aObjName( 16 ); 8520 aObjName.append( "Tr" ); 8521 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8522 OString aTrName( aObjName.makeStringAndClear() ); 8523 aObjName.append( "EGS" ); 8524 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8525 OString aExtName( aObjName.makeStringAndClear() ); 8526 8527 OStringBuffer aLine( 80 ); 8528 // insert XObject 8529 aLine.append( "q /" ); 8530 aLine.append( aExtName ); 8531 aLine.append( " gs /" ); 8532 aLine.append( aTrName ); 8533 aLine.append( " Do Q\n" ); 8534 writeBuffer( aLine.getStr(), aLine.getLength() ); 8535 8536 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8537 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8538 } 8539 8540 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject ) 8541 { 8542 if( nObject >= 0 ) 8543 { 8544 switch( eKind ) 8545 { 8546 case ResXObject: 8547 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject; 8548 if( ! m_aOutputStreams.empty() ) 8549 m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject; 8550 break; 8551 case ResExtGState: 8552 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject; 8553 if( ! m_aOutputStreams.empty() ) 8554 m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject; 8555 break; 8556 case ResShading: 8557 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject; 8558 if( ! m_aOutputStreams.empty() ) 8559 m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject; 8560 break; 8561 case ResPattern: 8562 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject; 8563 if( ! m_aOutputStreams.empty() ) 8564 m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject; 8565 break; 8566 } 8567 } 8568 } 8569 8570 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect ) 8571 { 8572 push( PUSH_ALL ); 8573 8574 // force reemitting clip region 8575 clearClipRegion(); 8576 updateGraphicsState(); 8577 8578 m_aOutputStreams.push_front( StreamRedirect() ); 8579 m_aOutputStreams.front().m_pStream = pStream; 8580 m_aOutputStreams.front().m_aMapMode = m_aMapMode; 8581 8582 if( !rTargetRect.IsEmpty() ) 8583 { 8584 m_aOutputStreams.front().m_aTargetRect = 8585 lcl_convert( m_aGraphicsStack.front().m_aMapMode, 8586 m_aMapMode, 8587 getReferenceDevice(), 8588 rTargetRect ); 8589 Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft(); 8590 long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight()); 8591 aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom()); 8592 m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta ); 8593 } 8594 8595 // setup graphics state for independent object stream 8596 8597 // force reemitting colors 8598 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); 8599 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); 8600 } 8601 8602 Rectangle PDFWriterImpl::getRedirectTargetRect() const 8603 { 8604 return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect; 8605 } 8606 8607 SvStream* PDFWriterImpl::endRedirect() 8608 { 8609 SvStream* pStream = NULL; 8610 if( ! m_aOutputStreams.empty() ) 8611 { 8612 pStream = m_aOutputStreams.front().m_pStream; 8613 m_aMapMode = m_aOutputStreams.front().m_aMapMode; 8614 m_aOutputStreams.pop_front(); 8615 } 8616 8617 pop(); 8618 // force reemitting colors and clip region 8619 clearClipRegion(); 8620 m_aCurrentPDFState.m_bClipRegion = m_aGraphicsStack.front().m_bClipRegion; 8621 m_aCurrentPDFState.m_aClipRegion = m_aGraphicsStack.front().m_aClipRegion; 8622 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); 8623 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); 8624 8625 updateGraphicsState(); 8626 8627 return pStream; 8628 } 8629 8630 void PDFWriterImpl::beginTransparencyGroup() 8631 { 8632 updateGraphicsState(); 8633 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8634 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() ); 8635 } 8636 8637 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent ) 8638 { 8639 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" ); 8640 nTransparentPercent = nTransparentPercent % 100; 8641 8642 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8643 { 8644 // create XObject 8645 m_aTransparentObjects.push_back( TransparencyEmit() ); 8646 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox; 8647 // convert rectangle to default user space 8648 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8649 m_aTransparentObjects.back().m_nObject = createObject(); 8650 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; 8651 // get XObject's content stream 8652 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect()); 8653 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8654 8655 OStringBuffer aObjName( 16 ); 8656 aObjName.append( "Tr" ); 8657 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8658 OString aTrName( aObjName.makeStringAndClear() ); 8659 aObjName.append( "EGS" ); 8660 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8661 OString aExtName( aObjName.makeStringAndClear() ); 8662 8663 OStringBuffer aLine( 80 ); 8664 // insert XObject 8665 aLine.append( "q /" ); 8666 aLine.append( aExtName ); 8667 aLine.append( " gs /" ); 8668 aLine.append( aTrName ); 8669 aLine.append( " Do Q\n" ); 8670 writeBuffer( aLine.getStr(), aLine.getLength() ); 8671 8672 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8673 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8674 } 8675 } 8676 8677 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask ) 8678 { 8679 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8680 { 8681 // create XObject 8682 m_aTransparentObjects.push_back( TransparencyEmit() ); 8683 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox; 8684 // convert rectangle to default user space 8685 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8686 m_aTransparentObjects.back().m_nObject = createObject(); 8687 m_aTransparentObjects.back().m_fAlpha = 0.0; 8688 // get XObject's content stream 8689 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect()); 8690 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8691 8692 // draw soft mask 8693 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() ); 8694 drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask ); 8695 m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect()); 8696 8697 OStringBuffer aObjName( 16 ); 8698 aObjName.append( "Tr" ); 8699 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8700 OString aTrName( aObjName.makeStringAndClear() ); 8701 aObjName.append( "EGS" ); 8702 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8703 OString aExtName( aObjName.makeStringAndClear() ); 8704 8705 OStringBuffer aLine( 80 ); 8706 // insert XObject 8707 aLine.append( "q /" ); 8708 aLine.append( aExtName ); 8709 aLine.append( " gs /" ); 8710 aLine.append( aTrName ); 8711 aLine.append( " Do Q\n" ); 8712 writeBuffer( aLine.getStr(), aLine.getLength() ); 8713 8714 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8715 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8716 } 8717 } 8718 8719 void PDFWriterImpl::drawRectangle( const Rectangle& rRect ) 8720 { 8721 MARK( "drawRectangle" ); 8722 8723 updateGraphicsState(); 8724 8725 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8726 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8727 return; 8728 8729 OStringBuffer aLine( 40 ); 8730 m_aPages.back().appendRect( rRect, aLine ); 8731 8732 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8733 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8734 aLine.append( " B*\n" ); 8735 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8736 aLine.append( " S\n" ); 8737 else 8738 aLine.append( " f*\n" ); 8739 8740 writeBuffer( aLine.getStr(), aLine.getLength() ); 8741 } 8742 8743 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound ) 8744 { 8745 MARK( "drawRectangle with rounded edges" ); 8746 8747 if( !nHorzRound && !nVertRound ) 8748 drawRectangle( rRect ); 8749 8750 updateGraphicsState(); 8751 8752 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8753 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8754 return; 8755 8756 if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 ) 8757 nHorzRound = rRect.GetWidth()/2; 8758 if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 ) 8759 nVertRound = rRect.GetHeight()/2; 8760 8761 Point aPoints[16]; 8762 const double kappa = 0.5522847498; 8763 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5); 8764 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5); 8765 8766 aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() ); 8767 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); 8768 aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() ); 8769 aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() ); 8770 8771 aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound ); 8772 aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky ); 8773 aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound ); 8774 aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky ); 8775 8776 aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 ); 8777 aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() ); 8778 aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() ); 8779 aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() ); 8780 8781 aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound ); 8782 aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky ); 8783 aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound ); 8784 aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky ); 8785 8786 8787 OStringBuffer aLine( 80 ); 8788 m_aPages.back().appendPoint( aPoints[1], aLine ); 8789 aLine.append( " m " ); 8790 m_aPages.back().appendPoint( aPoints[2], aLine ); 8791 aLine.append( " l " ); 8792 m_aPages.back().appendPoint( aPoints[3], aLine ); 8793 aLine.append( ' ' ); 8794 m_aPages.back().appendPoint( aPoints[4], aLine ); 8795 aLine.append( ' ' ); 8796 m_aPages.back().appendPoint( aPoints[5], aLine ); 8797 aLine.append( " c\n" ); 8798 m_aPages.back().appendPoint( aPoints[6], aLine ); 8799 aLine.append( " l " ); 8800 m_aPages.back().appendPoint( aPoints[7], aLine ); 8801 aLine.append( ' ' ); 8802 m_aPages.back().appendPoint( aPoints[8], aLine ); 8803 aLine.append( ' ' ); 8804 m_aPages.back().appendPoint( aPoints[9], aLine ); 8805 aLine.append( " c\n" ); 8806 m_aPages.back().appendPoint( aPoints[10], aLine ); 8807 aLine.append( " l " ); 8808 m_aPages.back().appendPoint( aPoints[11], aLine ); 8809 aLine.append( ' ' ); 8810 m_aPages.back().appendPoint( aPoints[12], aLine ); 8811 aLine.append( ' ' ); 8812 m_aPages.back().appendPoint( aPoints[13], aLine ); 8813 aLine.append( " c\n" ); 8814 m_aPages.back().appendPoint( aPoints[14], aLine ); 8815 aLine.append( " l " ); 8816 m_aPages.back().appendPoint( aPoints[15], aLine ); 8817 aLine.append( ' ' ); 8818 m_aPages.back().appendPoint( aPoints[0], aLine ); 8819 aLine.append( ' ' ); 8820 m_aPages.back().appendPoint( aPoints[1], aLine ); 8821 aLine.append( " c " ); 8822 8823 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8824 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8825 aLine.append( "b*\n" ); 8826 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8827 aLine.append( "s\n" ); 8828 else 8829 aLine.append( "f*\n" ); 8830 8831 writeBuffer( aLine.getStr(), aLine.getLength() ); 8832 } 8833 8834 void PDFWriterImpl::drawEllipse( const Rectangle& rRect ) 8835 { 8836 MARK( "drawEllipse" ); 8837 8838 updateGraphicsState(); 8839 8840 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8841 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8842 return; 8843 8844 Point aPoints[12]; 8845 const double kappa = 0.5522847498; 8846 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5); 8847 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5); 8848 8849 aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() ); 8850 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); 8851 aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() ); 8852 8853 aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 ); 8854 aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky ); 8855 aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky ); 8856 8857 aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 ); 8858 aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() ); 8859 aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() ); 8860 8861 aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 ); 8862 aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky ); 8863 aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky ); 8864 8865 OStringBuffer aLine( 80 ); 8866 m_aPages.back().appendPoint( aPoints[1], aLine ); 8867 aLine.append( " m " ); 8868 m_aPages.back().appendPoint( aPoints[2], aLine ); 8869 aLine.append( ' ' ); 8870 m_aPages.back().appendPoint( aPoints[3], aLine ); 8871 aLine.append( ' ' ); 8872 m_aPages.back().appendPoint( aPoints[4], aLine ); 8873 aLine.append( " c\n" ); 8874 m_aPages.back().appendPoint( aPoints[5], aLine ); 8875 aLine.append( ' ' ); 8876 m_aPages.back().appendPoint( aPoints[6], aLine ); 8877 aLine.append( ' ' ); 8878 m_aPages.back().appendPoint( aPoints[7], aLine ); 8879 aLine.append( " c\n" ); 8880 m_aPages.back().appendPoint( aPoints[8], aLine ); 8881 aLine.append( ' ' ); 8882 m_aPages.back().appendPoint( aPoints[9], aLine ); 8883 aLine.append( ' ' ); 8884 m_aPages.back().appendPoint( aPoints[10], aLine ); 8885 aLine.append( " c\n" ); 8886 m_aPages.back().appendPoint( aPoints[11], aLine ); 8887 aLine.append( ' ' ); 8888 m_aPages.back().appendPoint( aPoints[0], aLine ); 8889 aLine.append( ' ' ); 8890 m_aPages.back().appendPoint( aPoints[1], aLine ); 8891 aLine.append( " c " ); 8892 8893 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8894 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8895 aLine.append( "b*\n" ); 8896 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8897 aLine.append( "s\n" ); 8898 else 8899 aLine.append( "f*\n" ); 8900 8901 writeBuffer( aLine.getStr(), aLine.getLength() ); 8902 } 8903 8904 static double calcAngle( const Rectangle& rRect, const Point& rPoint ) 8905 { 8906 Point aOrigin((rRect.Left()+rRect.Right()+1)/2, 8907 (rRect.Top()+rRect.Bottom()+1)/2); 8908 Point aPoint = rPoint - aOrigin; 8909 8910 double fX = (double)aPoint.X(); 8911 double fY = (double)-aPoint.Y(); 8912 8913 if( rRect.GetWidth() > rRect.GetHeight() ) 8914 fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight()); 8915 else if( rRect.GetHeight() > rRect.GetWidth() ) 8916 fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth()); 8917 return atan2( fY, fX ); 8918 } 8919 8920 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord ) 8921 { 8922 MARK( "drawArc" ); 8923 8924 updateGraphicsState(); 8925 8926 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8927 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8928 return; 8929 8930 // calculate start and stop angles 8931 const double fStartAngle = calcAngle( rRect, rStart ); 8932 double fStopAngle = calcAngle( rRect, rStop ); 8933 while( fStopAngle < fStartAngle ) 8934 fStopAngle += 2.0*M_PI; 8935 const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1; 8936 const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments; 8937 const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0); 8938 const double halfWidth = (double)rRect.GetWidth()/2.0; 8939 const double halfHeight = (double)rRect.GetHeight()/2.0; 8940 8941 const Point aCenter( (rRect.Left()+rRect.Right()+1)/2, 8942 (rRect.Top()+rRect.Bottom()+1)/2 ); 8943 8944 OStringBuffer aLine( 30*nFragments ); 8945 Point aPoint( (int)(halfWidth * cos(fStartAngle) ), 8946 -(int)(halfHeight * sin(fStartAngle) ) ); 8947 aPoint += aCenter; 8948 m_aPages.back().appendPoint( aPoint, aLine ); 8949 aLine.append( " m " ); 8950 if( !basegfx::fTools::equal(fStartAngle, fStopAngle) ) 8951 { 8952 for( int i = 0; i < nFragments; i++ ) 8953 { 8954 const double fStartFragment = fStartAngle + (double)i*fFragmentDelta; 8955 const double fStopFragment = fStartFragment + fFragmentDelta; 8956 aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ), 8957 -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) ); 8958 aPoint += aCenter; 8959 m_aPages.back().appendPoint( aPoint, aLine ); 8960 aLine.append( ' ' ); 8961 8962 aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ), 8963 -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) ); 8964 aPoint += aCenter; 8965 m_aPages.back().appendPoint( aPoint, aLine ); 8966 aLine.append( ' ' ); 8967 8968 aPoint = Point( (int)(halfWidth * cos(fStopFragment) ), 8969 -(int)(halfHeight * sin(fStopFragment) ) ); 8970 aPoint += aCenter; 8971 m_aPages.back().appendPoint( aPoint, aLine ); 8972 aLine.append( " c\n" ); 8973 } 8974 } 8975 if( bWithChord || bWithPie ) 8976 { 8977 if( bWithPie ) 8978 { 8979 m_aPages.back().appendPoint( aCenter, aLine ); 8980 aLine.append( " l " ); 8981 } 8982 aLine.append( "h " ); 8983 } 8984 if( ! bWithChord && ! bWithPie ) 8985 aLine.append( "S\n" ); 8986 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8987 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8988 aLine.append( "B*\n" ); 8989 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8990 aLine.append( "S\n" ); 8991 else 8992 aLine.append( "f*\n" ); 8993 8994 writeBuffer( aLine.getStr(), aLine.getLength() ); 8995 } 8996 8997 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly ) 8998 { 8999 MARK( "drawPolyLine" ); 9000 9001 sal_uInt16 nPoints = rPoly.GetSize(); 9002 if( nPoints < 2 ) 9003 return; 9004 9005 updateGraphicsState(); 9006 9007 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9008 return; 9009 9010 OStringBuffer aLine( 20 * nPoints ); 9011 m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] ); 9012 aLine.append( "S\n" ); 9013 9014 writeBuffer( aLine.getStr(), aLine.getLength() ); 9015 } 9016 9017 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo ) 9018 { 9019 MARK( "drawPolyLine with LineInfo" ); 9020 9021 updateGraphicsState(); 9022 9023 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9024 return; 9025 9026 OStringBuffer aLine; 9027 aLine.append( "q " ); 9028 if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) 9029 { 9030 writeBuffer( aLine.getStr(), aLine.getLength() ); 9031 drawPolyLine( rPoly ); 9032 writeBuffer( "Q\n", 2 ); 9033 } 9034 else 9035 { 9036 PDFWriter::ExtLineInfo aInfo; 9037 convertLineInfoToExtLineInfo( rInfo, aInfo ); 9038 drawPolyLine( rPoly, aInfo ); 9039 } 9040 } 9041 9042 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut ) 9043 { 9044 DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" ); 9045 rOut.m_fLineWidth = rIn.GetWidth(); 9046 rOut.m_fTransparency = 0.0; 9047 rOut.m_eCap = PDFWriter::capButt; 9048 rOut.m_eJoin = PDFWriter::joinMiter; 9049 rOut.m_fMiterLimit = 10; 9050 rOut.m_aDashArray.clear(); 9051 9052 int nDashes = rIn.GetDashCount(); 9053 int nDashLen = rIn.GetDashLen(); 9054 int nDistance = rIn.GetDistance(); 9055 for( int n = 0; n < nDashes; n++ ) 9056 { 9057 rOut.m_aDashArray.push_back( nDashLen ); 9058 rOut.m_aDashArray.push_back( nDistance ); 9059 } 9060 int nDots = rIn.GetDotCount(); 9061 int nDotLen = rIn.GetDotLen(); 9062 for( int n = 0; n < nDots; n++ ) 9063 { 9064 rOut.m_aDashArray.push_back( nDotLen ); 9065 rOut.m_aDashArray.push_back( nDistance ); 9066 } 9067 } 9068 9069 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo ) 9070 { 9071 MARK( "drawPolyLine with ExtLineInfo" ); 9072 9073 updateGraphicsState(); 9074 9075 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9076 return; 9077 9078 if( rInfo.m_fTransparency >= 1.0 ) 9079 return; 9080 9081 if( rInfo.m_fTransparency != 0.0 ) 9082 beginTransparencyGroup(); 9083 9084 OStringBuffer aLine; 9085 aLine.append( "q " ); 9086 m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine ); 9087 aLine.append( " w" ); 9088 if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader 9089 { 9090 switch( rInfo.m_eCap ) 9091 { 9092 default: 9093 case PDFWriter::capButt: aLine.append( " 0 J" );break; 9094 case PDFWriter::capRound: aLine.append( " 1 J" );break; 9095 case PDFWriter::capSquare: aLine.append( " 2 J" );break; 9096 } 9097 switch( rInfo.m_eJoin ) 9098 { 9099 default: 9100 case PDFWriter::joinMiter: 9101 { 9102 double fLimit = rInfo.m_fMiterLimit; 9103 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit ) 9104 fLimit = fLimit / rInfo.m_fLineWidth; 9105 if( fLimit < 1.0 ) 9106 fLimit = 1.0; 9107 aLine.append( " 0 j " ); 9108 appendDouble( fLimit, aLine ); 9109 aLine.append( " M" ); 9110 } 9111 break; 9112 case PDFWriter::joinRound: aLine.append( " 1 j" );break; 9113 case PDFWriter::joinBevel: aLine.append( " 2 j" );break; 9114 } 9115 if( rInfo.m_aDashArray.size() > 0 ) 9116 { 9117 aLine.append( " [ " ); 9118 for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin(); 9119 it != rInfo.m_aDashArray.end(); ++it ) 9120 { 9121 m_aPages.back().appendMappedLength( *it, aLine ); 9122 aLine.append( ' ' ); 9123 } 9124 aLine.append( "] 0 d" ); 9125 } 9126 aLine.append( "\n" ); 9127 writeBuffer( aLine.getStr(), aLine.getLength() ); 9128 drawPolyLine( rPoly ); 9129 } 9130 else 9131 { 9132 basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon()); 9133 basegfx::B2DPolyPolygon aPolyPoly; 9134 9135 basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly); 9136 9137 // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments. 9138 // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality) 9139 // this line needs to be removed and the loop below adapted accordingly 9140 aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly); 9141 9142 const sal_uInt32 nPolygonCount(aPolyPoly.count()); 9143 9144 for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ ) 9145 { 9146 aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " ); 9147 aPoly = aPolyPoly.getB2DPolygon( nPoly ); 9148 const sal_uInt32 nPointCount(aPoly.count()); 9149 9150 if(nPointCount) 9151 { 9152 const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1); 9153 basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0)); 9154 9155 for(sal_uInt32 a(0); a < nEdgeCount; a++) 9156 { 9157 if( a > 0 ) 9158 aLine.append( " " ); 9159 const sal_uInt32 nNextIndex((a + 1) % nPointCount); 9160 const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex)); 9161 9162 m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()), 9163 FRound(aCurrent.getY()) ), 9164 aLine ); 9165 aLine.append( " m " ); 9166 m_aPages.back().appendPoint( Point( FRound(aNext.getX()), 9167 FRound(aNext.getY()) ), 9168 aLine ); 9169 aLine.append( " l" ); 9170 9171 // prepare next edge 9172 aCurrent = aNext; 9173 } 9174 } 9175 } 9176 aLine.append( " S " ); 9177 writeBuffer( aLine.getStr(), aLine.getLength() ); 9178 } 9179 writeBuffer( "Q\n", 2 ); 9180 9181 if( rInfo.m_fTransparency != 0.0 ) 9182 { 9183 // FIXME: actually this may be incorrect with bezier polygons 9184 Rectangle aBoundRect( rPoly.GetBoundRect() ); 9185 // avoid clipping with thick lines 9186 if( rInfo.m_fLineWidth > 0.0 ) 9187 { 9188 sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth); 9189 aBoundRect.Top() -= nLW; 9190 aBoundRect.Left() -= nLW; 9191 aBoundRect.Right() += nLW; 9192 aBoundRect.Bottom() += nLW; 9193 } 9194 endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) ); 9195 } 9196 } 9197 9198 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor ) 9199 { 9200 MARK( "drawPixel" ); 9201 9202 Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor ); 9203 9204 if( aColor == Color( COL_TRANSPARENT ) ) 9205 return; 9206 9207 // pixels are drawn in line color, so have to set 9208 // the nonstroking color to line color 9209 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; 9210 setFillColor( aColor ); 9211 9212 updateGraphicsState(); 9213 9214 OStringBuffer aLine( 20 ); 9215 m_aPages.back().appendPoint( rPoint, aLine ); 9216 aLine.append( ' ' ); 9217 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine ); 9218 aLine.append( ' ' ); 9219 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine ); 9220 aLine.append( " re f\n" ); 9221 writeBuffer( aLine.getStr(), aLine.getLength() ); 9222 9223 setFillColor( aOldFillColor ); 9224 } 9225 9226 void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors ) 9227 { 9228 MARK( "drawPixel with Polygon" ); 9229 9230 updateGraphicsState(); 9231 9232 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors ) 9233 return; 9234 9235 sal_uInt16 nPoints = rPoints.GetSize(); 9236 OStringBuffer aLine( nPoints*40 ); 9237 aLine.append( "q " ); 9238 if( ! pColors ) 9239 { 9240 appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine ); 9241 aLine.append( ' ' ); 9242 } 9243 9244 OStringBuffer aPixel(32); 9245 aPixel.append( ' ' ); 9246 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel ); 9247 aPixel.append( ' ' ); 9248 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel ); 9249 OString aPixelStr = aPixel.makeStringAndClear(); 9250 for( sal_uInt16 i = 0; i < nPoints; i++ ) 9251 { 9252 if( pColors ) 9253 { 9254 if( pColors[i] == Color( COL_TRANSPARENT ) ) 9255 continue; 9256 9257 appendNonStrokingColor( pColors[i], aLine ); 9258 aLine.append( ' ' ); 9259 } 9260 m_aPages.back().appendPoint( rPoints[i], aLine ); 9261 aLine.append( aPixelStr ); 9262 aLine.append( " re f\n" ); 9263 } 9264 aLine.append( "Q\n" ); 9265 writeBuffer( aLine.getStr(), aLine.getLength() ); 9266 } 9267 9268 class AccessReleaser 9269 { 9270 BitmapReadAccess* m_pAccess; 9271 public: 9272 AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){} 9273 ~AccessReleaser() { delete m_pAccess; } 9274 }; 9275 9276 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject ) 9277 { 9278 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9279 9280 bool bFlateFilter = compressStream( rObject.m_pContentStream ); 9281 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END ); 9282 sal_uLong nSize = rObject.m_pContentStream->Tell(); 9283 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN ); 9284 #if OSL_DEBUG_LEVEL > 1 9285 emitComment( "PDFWriterImpl::writeTransparentObject" ); 9286 #endif 9287 OStringBuffer aLine( 512 ); 9288 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9289 aLine.append( rObject.m_nObject ); 9290 aLine.append( " 0 obj\n" 9291 "<</Type/XObject\n" 9292 "/Subtype/Form\n" 9293 "/BBox[ " ); 9294 appendFixedInt( rObject.m_aBoundRect.Left(), aLine ); 9295 aLine.append( ' ' ); 9296 appendFixedInt( rObject.m_aBoundRect.Top(), aLine ); 9297 aLine.append( ' ' ); 9298 appendFixedInt( rObject.m_aBoundRect.Right(), aLine ); 9299 aLine.append( ' ' ); 9300 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine ); 9301 aLine.append( " ]\n" ); 9302 if( ! rObject.m_pSoftMaskStream ) 9303 { 9304 if( ! m_bIsPDF_A1 ) 9305 { 9306 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" ); 9307 } 9308 } 9309 /* #i42884# the PDF reference recommends that each Form XObject 9310 * should have a resource dict; alas if that is the same object 9311 * as the one of the page it triggers an endless recursion in 9312 * acroread 5 (6 and up have that fixed). Since we have only one 9313 * resource dict anyway, let's use the one from the page by NOT 9314 * emitting a Resources entry. 9315 */ 9316 #if 0 9317 aLine.append( " /Resources " ); 9318 aLine.append( getResourceDictObj() ); 9319 aLine.append( " 0 R\n" ); 9320 #endif 9321 9322 aLine.append( "/Length " ); 9323 aLine.append( (sal_Int32)(nSize) ); 9324 aLine.append( "\n" ); 9325 if( bFlateFilter ) 9326 aLine.append( "/Filter/FlateDecode\n" ); 9327 aLine.append( ">>\n" 9328 "stream\n" ); 9329 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9330 checkAndEnableStreamEncryption( rObject.m_nObject ); 9331 CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) ); 9332 disableStreamEncryption(); 9333 aLine.setLength( 0 ); 9334 aLine.append( "\n" 9335 "endstream\n" 9336 "endobj\n\n" ); 9337 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9338 9339 // write ExtGState dict for this XObject 9340 aLine.setLength( 0 ); 9341 aLine.append( rObject.m_nExtGStateObject ); 9342 aLine.append( " 0 obj\n" 9343 "<<" ); 9344 if( ! rObject.m_pSoftMaskStream ) 9345 { 9346 //i59651 9347 if( m_bIsPDF_A1 ) 9348 { 9349 aLine.append( "/CA 1.0/ca 1.0" ); 9350 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9351 } 9352 else 9353 { 9354 aLine.append( "/CA " ); 9355 appendDouble( rObject.m_fAlpha, aLine ); 9356 aLine.append( "\n" 9357 " /ca " ); 9358 appendDouble( rObject.m_fAlpha, aLine ); 9359 } 9360 aLine.append( "\n" ); 9361 } 9362 else 9363 { 9364 if( m_bIsPDF_A1 ) 9365 { 9366 aLine.append( "/SMask/None" ); 9367 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9368 } 9369 else 9370 { 9371 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END ); 9372 sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell(); 9373 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN ); 9374 sal_Int32 nMaskObject = createObject(); 9375 aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " ); 9376 aLine.append( nMaskObject ); 9377 aLine.append( " 0 R>>\n" ); 9378 9379 OStringBuffer aMask; 9380 aMask.append( nMaskObject ); 9381 aMask.append( " 0 obj\n" 9382 "<</Type/XObject\n" 9383 "/Subtype/Form\n" 9384 "/BBox[" ); 9385 appendFixedInt( rObject.m_aBoundRect.Left(), aMask ); 9386 aMask.append( ' ' ); 9387 appendFixedInt( rObject.m_aBoundRect.Top(), aMask ); 9388 aMask.append( ' ' ); 9389 appendFixedInt( rObject.m_aBoundRect.Right(), aMask ); 9390 aMask.append( ' ' ); 9391 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask ); 9392 aMask.append( "]\n" ); 9393 9394 /* #i42884# see above */ 9395 #if 0 9396 aLine.append( "/Resources " ); 9397 aMask.append( getResourceDictObj() ); 9398 aMask.append( " 0 R\n" ); 9399 #endif 9400 9401 aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" ); 9402 aMask.append( "/Length " ); 9403 aMask.append( nMaskSize ); 9404 aMask.append( ">>\n" 9405 "stream\n" ); 9406 CHECK_RETURN( updateObject( nMaskObject ) ); 9407 checkAndEnableStreamEncryption( nMaskObject ); 9408 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) ); 9409 CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) ); 9410 disableStreamEncryption(); 9411 aMask.setLength( 0 ); 9412 aMask.append( "\nendstream\n" 9413 "endobj\n\n" ); 9414 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) ); 9415 } 9416 } 9417 aLine.append( ">>\n" 9418 "endobj\n\n" ); 9419 CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) ); 9420 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9421 9422 return true; 9423 } 9424 9425 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject ) 9426 { 9427 sal_Int32 nFunctionObject = createObject(); 9428 CHECK_RETURN( updateObject( nFunctionObject ) ); 9429 9430 VirtualDevice aDev; 9431 aDev.SetOutputSizePixel( rObject.m_aSize ); 9432 aDev.SetMapMode( MapMode( MAP_PIXEL ) ); 9433 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 9434 aDev.SetDrawMode( aDev.GetDrawMode() | 9435 ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT | 9436 DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) ); 9437 aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient ); 9438 9439 Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize ); 9440 BitmapReadAccess* pAccess = aSample.AcquireReadAccess(); 9441 AccessReleaser aReleaser( pAccess ); 9442 9443 Size aSize = aSample.GetSizePixel(); 9444 9445 sal_Int32 nStreamLengthObject = createObject(); 9446 #if OSL_DEBUG_LEVEL > 1 9447 emitComment( "PDFWriterImpl::writeGradientFunction" ); 9448 #endif 9449 OStringBuffer aLine( 120 ); 9450 aLine.append( nFunctionObject ); 9451 aLine.append( " 0 obj\n" 9452 "<</FunctionType 0\n" 9453 "/Domain[ 0 1 0 1 ]\n" 9454 "/Size[ " ); 9455 aLine.append( (sal_Int32)aSize.Width() ); 9456 aLine.append( ' ' ); 9457 aLine.append( (sal_Int32)aSize.Height() ); 9458 aLine.append( " ]\n" 9459 "/BitsPerSample 8\n" 9460 "/Range[ 0 1 0 1 0 1 ]\n" 9461 "/Order 3\n" 9462 "/Length " ); 9463 aLine.append( nStreamLengthObject ); 9464 aLine.append( " 0 R\n" 9465 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9466 "/Filter/FlateDecode" 9467 #endif 9468 ">>\n" 9469 "stream\n" ); 9470 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9471 9472 sal_uInt64 nStartStreamPos = 0; 9473 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) ); 9474 9475 checkAndEnableStreamEncryption( nFunctionObject ); 9476 beginCompression(); 9477 for( int y = aSize.Height()-1; y >= 0; y-- ) 9478 { 9479 for( int x = 0; x < aSize.Width(); x++ ) 9480 { 9481 sal_uInt8 aCol[3]; 9482 BitmapColor aColor = pAccess->GetColor( y, x ); 9483 aCol[0] = aColor.GetRed(); 9484 aCol[1] = aColor.GetGreen(); 9485 aCol[2] = aColor.GetBlue(); 9486 CHECK_RETURN( writeBuffer( aCol, 3 ) ); 9487 } 9488 } 9489 endCompression(); 9490 disableStreamEncryption(); 9491 9492 sal_uInt64 nEndStreamPos = 0; 9493 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) ); 9494 9495 aLine.setLength( 0 ); 9496 aLine.append( "\nendstream\nendobj\n\n" ); 9497 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9498 9499 // write stream length 9500 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 9501 aLine.setLength( 0 ); 9502 aLine.append( nStreamLengthObject ); 9503 aLine.append( " 0 obj\n" ); 9504 aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) ); 9505 aLine.append( "\nendobj\n\n" ); 9506 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9507 9508 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9509 aLine.setLength( 0 ); 9510 aLine.append( rObject.m_nObject ); 9511 aLine.append( " 0 obj\n" 9512 "<</ShadingType 1\n" 9513 "/ColorSpace/DeviceRGB\n" 9514 "/AntiAlias true\n" 9515 "/Domain[ 0 1 0 1 ]\n" 9516 "/Matrix[ " ); 9517 aLine.append( (sal_Int32)aSize.Width() ); 9518 aLine.append( " 0 0 " ); 9519 aLine.append( (sal_Int32)aSize.Height() ); 9520 aLine.append( " 0 0 ]\n" 9521 "/Function " ); 9522 aLine.append( nFunctionObject ); 9523 aLine.append( " 0 R\n" 9524 ">>\n" 9525 "endobj\n\n" ); 9526 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9527 9528 return true; 9529 } 9530 9531 bool PDFWriterImpl::writeJPG( JPGEmit& rObject ) 9532 { 9533 CHECK_RETURN( rObject.m_pStream ); 9534 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9535 9536 sal_Int32 nLength = 0; 9537 rObject.m_pStream->Seek( STREAM_SEEK_TO_END ); 9538 nLength = rObject.m_pStream->Tell(); 9539 rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN ); 9540 9541 sal_Int32 nMaskObject = 0; 9542 if( !!rObject.m_aMask ) 9543 { 9544 if( rObject.m_aMask.GetBitCount() == 1 || 9545 ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651 9546 ) 9547 { 9548 nMaskObject = createObject(); 9549 } 9550 else if( m_bIsPDF_A1 ) 9551 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9552 else if( m_aContext.Version < PDFWriter::PDF_1_4 ) 9553 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 ); 9554 9555 } 9556 #if OSL_DEBUG_LEVEL > 1 9557 emitComment( "PDFWriterImpl::writeJPG" ); 9558 #endif 9559 9560 OStringBuffer aLine(200); 9561 aLine.append( rObject.m_nObject ); 9562 aLine.append( " 0 obj\n" 9563 "<</Type/XObject/Subtype/Image/Width " ); 9564 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() ); 9565 aLine.append( " /Height " ); 9566 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() ); 9567 aLine.append( " /BitsPerComponent 8 " ); 9568 if( rObject.m_bTrueColor ) 9569 aLine.append( "/ColorSpace/DeviceRGB" ); 9570 else 9571 aLine.append( "/ColorSpace/DeviceGray" ); 9572 aLine.append( "/Filter/DCTDecode/Length " ); 9573 aLine.append( nLength ); 9574 if( nMaskObject ) 9575 { 9576 aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " ); 9577 aLine.append( nMaskObject ); 9578 aLine.append( " 0 R " ); 9579 } 9580 aLine.append( ">>\nstream\n" ); 9581 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9582 9583 checkAndEnableStreamEncryption( rObject.m_nObject ); 9584 CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) ); 9585 disableStreamEncryption(); 9586 9587 aLine.setLength( 0 ); 9588 aLine.append( "\nendstream\nendobj\n\n" ); 9589 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9590 9591 if( nMaskObject ) 9592 { 9593 BitmapEmit aEmit; 9594 aEmit.m_nObject = nMaskObject; 9595 if( rObject.m_aMask.GetBitCount() == 1 ) 9596 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask ); 9597 else if( rObject.m_aMask.GetBitCount() == 8 ) 9598 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) ); 9599 writeBitmapObject( aEmit, true ); 9600 } 9601 9602 return true; 9603 } 9604 9605 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask ) 9606 { 9607 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9608 9609 Bitmap aBitmap; 9610 Color aTransparentColor( COL_TRANSPARENT ); 9611 bool bWriteMask = false; 9612 if( ! bMask ) 9613 { 9614 aBitmap = rObject.m_aBitmap.GetBitmap(); 9615 if( rObject.m_aBitmap.IsAlpha() ) 9616 { 9617 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 9618 bWriteMask = true; 9619 // else draw without alpha channel 9620 } 9621 else 9622 { 9623 switch( rObject.m_aBitmap.GetTransparentType() ) 9624 { 9625 case TRANSPARENT_NONE: 9626 // comes from drawMask function 9627 if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask ) 9628 bMask = true; 9629 break; 9630 case TRANSPARENT_COLOR: 9631 aTransparentColor = rObject.m_aBitmap.GetTransparentColor(); 9632 break; 9633 case TRANSPARENT_BITMAP: 9634 bWriteMask = true; 9635 break; 9636 } 9637 } 9638 } 9639 else 9640 { 9641 if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() ) 9642 { 9643 aBitmap = rObject.m_aBitmap.GetMask(); 9644 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); 9645 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); 9646 } 9647 else if( aBitmap.GetBitCount() != 8 ) 9648 { 9649 aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap(); 9650 aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS ); 9651 DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" ); 9652 } 9653 } 9654 9655 BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess(); 9656 AccessReleaser aReleaser( pAccess ); 9657 9658 bool bTrueColor; 9659 sal_Int32 nBitsPerComponent; 9660 switch( aBitmap.GetBitCount() ) 9661 { 9662 case 1: 9663 case 2: 9664 case 4: 9665 case 8: 9666 bTrueColor = false; 9667 nBitsPerComponent = aBitmap.GetBitCount(); 9668 break; 9669 default: 9670 bTrueColor = true; 9671 nBitsPerComponent = 8; 9672 break; 9673 } 9674 9675 sal_Int32 nStreamLengthObject = createObject(); 9676 sal_Int32 nMaskObject = 0; 9677 9678 #if OSL_DEBUG_LEVEL > 1 9679 emitComment( "PDFWriterImpl::writeBitmapObject" ); 9680 #endif 9681 OStringBuffer aLine(1024); 9682 aLine.append( rObject.m_nObject ); 9683 aLine.append( " 0 obj\n" 9684 "<</Type/XObject/Subtype/Image/Width " ); 9685 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); 9686 aLine.append( "/Height " ); 9687 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() ); 9688 aLine.append( "/BitsPerComponent " ); 9689 aLine.append( nBitsPerComponent ); 9690 aLine.append( "/Length " ); 9691 aLine.append( nStreamLengthObject ); 9692 aLine.append( " 0 R\n" ); 9693 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9694 if( nBitsPerComponent != 1 ) 9695 { 9696 aLine.append( "/Filter/FlateDecode" ); 9697 } 9698 else 9699 { 9700 aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " ); 9701 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); 9702 aLine.append( ">>\n" ); 9703 } 9704 #endif 9705 if( ! bMask ) 9706 { 9707 aLine.append( "/ColorSpace" ); 9708 if( bTrueColor ) 9709 aLine.append( "/DeviceRGB\n" ); 9710 else if( aBitmap.HasGreyPalette() ) 9711 { 9712 aLine.append( "/DeviceGray\n" ); 9713 if( aBitmap.GetBitCount() == 1 ) 9714 { 9715 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette 9716 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); 9717 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" ); 9718 if( nBlackIndex == 1 ) 9719 aLine.append( "/Decode[1 0]\n" ); 9720 } 9721 } 9722 else 9723 { 9724 aLine.append( "[ /Indexed/DeviceRGB " ); 9725 aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) ); 9726 aLine.append( "\n<" ); 9727 if( m_aContext.Encryption.Encrypt() ) 9728 { 9729 enableStringEncryption( rObject.m_nObject ); 9730 //check encryption buffer size 9731 if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) ) 9732 { 9733 int nChar = 0; 9734 //fill the encryption buffer 9735 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9736 { 9737 const BitmapColor& rColor = pAccess->GetPaletteColor( i ); 9738 m_pEncryptionBuffer[nChar++] = rColor.GetRed(); 9739 m_pEncryptionBuffer[nChar++] = rColor.GetGreen(); 9740 m_pEncryptionBuffer[nChar++] = rColor.GetBlue(); 9741 } 9742 //encrypt the colorspace lookup table 9743 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar ); 9744 //now queue the data for output 9745 nChar = 0; 9746 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9747 { 9748 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9749 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9750 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9751 } 9752 } 9753 } 9754 else //no encryption requested (PDF/A-1a program flow drops here) 9755 { 9756 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9757 { 9758 const BitmapColor& rColor = pAccess->GetPaletteColor( i ); 9759 appendHex( rColor.GetRed(), aLine ); 9760 appendHex( rColor.GetGreen(), aLine ); 9761 appendHex( rColor.GetBlue(), aLine ); 9762 } 9763 } 9764 aLine.append( ">\n]\n" ); 9765 } 9766 } 9767 else 9768 { 9769 if( aBitmap.GetBitCount() == 1 ) 9770 { 9771 aLine.append( "/ImageMask true\n" ); 9772 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); 9773 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" ); 9774 if( nBlackIndex ) 9775 aLine.append( "/Decode[ 1 0 ]\n" ); 9776 else 9777 aLine.append( "/Decode[ 0 1 ]\n" ); 9778 } 9779 else if( aBitmap.GetBitCount() == 8 ) 9780 { 9781 aLine.append( "/ColorSpace/DeviceGray\n" 9782 "/Decode [ 1 0 ]\n" ); 9783 } 9784 } 9785 9786 if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651 9787 { 9788 if( bWriteMask ) 9789 { 9790 nMaskObject = createObject(); 9791 if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 ) 9792 aLine.append( "/SMask " ); 9793 else 9794 aLine.append( "/Mask " ); 9795 aLine.append( nMaskObject ); 9796 aLine.append( " 0 R\n" ); 9797 } 9798 else if( aTransparentColor != Color( COL_TRANSPARENT ) ) 9799 { 9800 aLine.append( "/Mask[ " ); 9801 if( bTrueColor ) 9802 { 9803 aLine.append( (sal_Int32)aTransparentColor.GetRed() ); 9804 aLine.append( ' ' ); 9805 aLine.append( (sal_Int32)aTransparentColor.GetRed() ); 9806 aLine.append( ' ' ); 9807 aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); 9808 aLine.append( ' ' ); 9809 aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); 9810 aLine.append( ' ' ); 9811 aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); 9812 aLine.append( ' ' ); 9813 aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); 9814 } 9815 else 9816 { 9817 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) ); 9818 aLine.append( nIndex ); 9819 } 9820 aLine.append( " ]\n" ); 9821 } 9822 } 9823 else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) ) 9824 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9825 9826 aLine.append( ">>\n" 9827 "stream\n" ); 9828 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9829 sal_uInt64 nStartPos = 0; 9830 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) ); 9831 9832 checkAndEnableStreamEncryption( rObject.m_nObject ); 9833 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9834 if( nBitsPerComponent == 1 ) 9835 { 9836 writeG4Stream( pAccess ); 9837 } 9838 else 9839 #endif 9840 { 9841 beginCompression(); 9842 if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB ) 9843 { 9844 const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U ); 9845 9846 for( int i = 0; i < pAccess->Height(); i++ ) 9847 { 9848 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) ); 9849 } 9850 } 9851 else 9852 { 9853 const int nScanLineBytes = pAccess->Width()*3; 9854 boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] ); 9855 for( int y = 0; y < pAccess->Height(); y++ ) 9856 { 9857 for( int x = 0; x < pAccess->Width(); x++ ) 9858 { 9859 BitmapColor aColor = pAccess->GetColor( y, x ); 9860 pCol[3*x+0] = aColor.GetRed(); 9861 pCol[3*x+1] = aColor.GetGreen(); 9862 pCol[3*x+2] = aColor.GetBlue(); 9863 } 9864 CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) ); 9865 } 9866 } 9867 endCompression(); 9868 } 9869 disableStreamEncryption(); 9870 9871 sal_uInt64 nEndPos = 0; 9872 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) ); 9873 aLine.setLength( 0 ); 9874 aLine.append( "\nendstream\nendobj\n\n" ); 9875 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9876 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 9877 aLine.setLength( 0 ); 9878 aLine.append( nStreamLengthObject ); 9879 aLine.append( " 0 obj\n" ); 9880 aLine.append( (sal_Int64)(nEndPos-nStartPos) ); 9881 aLine.append( "\nendobj\n\n" ); 9882 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9883 9884 if( nMaskObject ) 9885 { 9886 BitmapEmit aEmit; 9887 aEmit.m_nObject = nMaskObject; 9888 aEmit.m_aBitmap = rObject.m_aBitmap; 9889 return writeBitmapObject( aEmit, true ); 9890 } 9891 9892 return true; 9893 } 9894 9895 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask ) 9896 { 9897 MARK( "drawJPGBitmap" ); 9898 9899 OStringBuffer aLine( 80 ); 9900 updateGraphicsState(); 9901 9902 // #i40055# sanity check 9903 if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) ) 9904 return; 9905 if( ! (rSizePixel.Width() && rSizePixel.Height()) ) 9906 return; 9907 9908 rDCTData.Seek( 0 ); 9909 if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 9910 { 9911 // need to convert to grayscale; 9912 // load stream to bitmap and draw the bitmap instead 9913 Graphic aGraphic; 9914 GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG ); 9915 Bitmap aBmp( aGraphic.GetBitmap() ); 9916 if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() ) 9917 { 9918 BitmapEx aBmpEx( aBmp, rMask ); 9919 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx ); 9920 } 9921 else 9922 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp ); 9923 return; 9924 } 9925 9926 SvMemoryStream* pStream = new SvMemoryStream; 9927 *pStream << rDCTData; 9928 pStream->Seek( STREAM_SEEK_TO_END ); 9929 9930 BitmapID aID; 9931 aID.m_aPixelSize = rSizePixel; 9932 aID.m_nSize = pStream->Tell(); 9933 pStream->Seek( STREAM_SEEK_TO_BEGIN ); 9934 aID.m_nChecksum = rtl_crc32( 0, pStream->GetData(), aID.m_nSize ); 9935 if( ! rMask.IsEmpty() ) 9936 aID.m_nMaskChecksum = rMask.GetChecksum(); 9937 9938 std::list< JPGEmit >::const_iterator it; 9939 for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it ) 9940 ; 9941 if( it == m_aJPGs.end() ) 9942 { 9943 m_aJPGs.push_front( JPGEmit() ); 9944 JPGEmit& rEmit = m_aJPGs.front(); 9945 rEmit.m_nObject = createObject(); 9946 rEmit.m_aID = aID; 9947 rEmit.m_pStream = pStream; 9948 rEmit.m_bTrueColor = bIsTrueColor; 9949 if( !! rMask && rMask.GetSizePixel() == rSizePixel ) 9950 rEmit.m_aMask = rMask; 9951 9952 it = m_aJPGs.begin(); 9953 } 9954 else 9955 delete pStream; 9956 9957 aLine.append( "q " ); 9958 sal_Int32 nCheckWidth = 0; 9959 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth ); 9960 aLine.append( " 0 0 " ); 9961 sal_Int32 nCheckHeight = 0; 9962 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight ); 9963 aLine.append( ' ' ); 9964 m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine ); 9965 aLine.append( " cm\n/Im" ); 9966 aLine.append( it->m_nObject ); 9967 aLine.append( " Do Q\n" ); 9968 if( nCheckWidth == 0 || nCheckHeight == 0 ) 9969 { 9970 // #i97512# avoid invalid current matrix 9971 aLine.setLength( 0 ); 9972 aLine.append( "\n%jpeg image /Im" ); 9973 aLine.append( it->m_nObject ); 9974 aLine.append( " scaled to zero size, omitted\n" ); 9975 } 9976 writeBuffer( aLine.getStr(), aLine.getLength() ); 9977 9978 OStringBuffer aObjName( 16 ); 9979 aObjName.append( "Im" ); 9980 aObjName.append( it->m_nObject ); 9981 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject ); 9982 9983 } 9984 9985 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor ) 9986 { 9987 OStringBuffer aLine( 80 ); 9988 updateGraphicsState(); 9989 9990 aLine.append( "q " ); 9991 if( rFillColor != Color( COL_TRANSPARENT ) ) 9992 { 9993 appendNonStrokingColor( rFillColor, aLine ); 9994 aLine.append( ' ' ); 9995 } 9996 sal_Int32 nCheckWidth = 0; 9997 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth ); 9998 aLine.append( " 0 0 " ); 9999 sal_Int32 nCheckHeight = 0; 10000 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight ); 10001 aLine.append( ' ' ); 10002 m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine ); 10003 aLine.append( " cm\n/Im" ); 10004 aLine.append( rBitmap.m_nObject ); 10005 aLine.append( " Do Q\n" ); 10006 if( nCheckWidth == 0 || nCheckHeight == 0 ) 10007 { 10008 // #i97512# avoid invalid current matrix 10009 aLine.setLength( 0 ); 10010 aLine.append( "\n%bitmap image /Im" ); 10011 aLine.append( rBitmap.m_nObject ); 10012 aLine.append( " scaled to zero size, omitted\n" ); 10013 } 10014 writeBuffer( aLine.getStr(), aLine.getLength() ); 10015 } 10016 10017 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask ) 10018 { 10019 BitmapEx aBitmap( i_rBitmap ); 10020 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 10021 { 10022 BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS; 10023 int nDepth = aBitmap.GetBitmap().GetBitCount(); 10024 if( nDepth <= 4 ) 10025 eConv = BMP_CONVERSION_4BIT_GREYS; 10026 if( nDepth > 1 ) 10027 aBitmap.Convert( eConv ); 10028 } 10029 BitmapID aID; 10030 aID.m_aPixelSize = aBitmap.GetSizePixel(); 10031 aID.m_nSize = aBitmap.GetBitCount(); 10032 aID.m_nChecksum = aBitmap.GetBitmap().GetChecksum(); 10033 aID.m_nMaskChecksum = 0; 10034 if( aBitmap.IsAlpha() ) 10035 aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum(); 10036 else 10037 { 10038 Bitmap aMask = aBitmap.GetMask(); 10039 if( ! aMask.IsEmpty() ) 10040 aID.m_nMaskChecksum = aMask.GetChecksum(); 10041 } 10042 std::list< BitmapEmit >::const_iterator it; 10043 for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it ) 10044 { 10045 if( aID == it->m_aID ) 10046 break; 10047 } 10048 if( it == m_aBitmaps.end() ) 10049 { 10050 m_aBitmaps.push_front( BitmapEmit() ); 10051 m_aBitmaps.front().m_aID = aID; 10052 m_aBitmaps.front().m_aBitmap = aBitmap; 10053 m_aBitmaps.front().m_nObject = createObject(); 10054 m_aBitmaps.front().m_bDrawMask = bDrawMask; 10055 it = m_aBitmaps.begin(); 10056 } 10057 10058 OStringBuffer aObjName( 16 ); 10059 aObjName.append( "Im" ); 10060 aObjName.append( it->m_nObject ); 10061 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject ); 10062 10063 return *it; 10064 } 10065 10066 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap ) 10067 { 10068 MARK( "drawBitmap (Bitmap)" ); 10069 10070 // #i40055# sanity check 10071 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10072 return; 10073 10074 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) ); 10075 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); 10076 } 10077 10078 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap ) 10079 { 10080 MARK( "drawBitmap (BitmapEx)" ); 10081 10082 // #i40055# sanity check 10083 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10084 return; 10085 10086 const BitmapEmit& rEmit = createBitmapEmit( rBitmap ); 10087 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); 10088 } 10089 10090 void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor ) 10091 { 10092 MARK( "drawMask" ); 10093 10094 // #i40055# sanity check 10095 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10096 return; 10097 10098 Bitmap aBitmap( rBitmap ); 10099 if( aBitmap.GetBitCount() > 1 ) 10100 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); 10101 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); 10102 10103 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true ); 10104 drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor ); 10105 } 10106 10107 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize ) 10108 { 10109 Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10110 MapMode( MAP_POINT ), 10111 getReferenceDevice(), 10112 rSize ) ); 10113 // check if we already have this gradient 10114 std::list<GradientEmit>::iterator it; 10115 // rounding to point will generally lose some pixels 10116 // round up to point boundary 10117 aPtSize.Width()++; 10118 aPtSize.Height()++; 10119 for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it ) 10120 { 10121 if( it->m_aGradient == rGradient ) 10122 { 10123 if( it->m_aSize == aPtSize ) 10124 break; 10125 } 10126 } 10127 if( it == m_aGradients.end() ) 10128 { 10129 m_aGradients.push_front( GradientEmit() ); 10130 m_aGradients.front().m_aGradient = rGradient; 10131 m_aGradients.front().m_nObject = createObject(); 10132 m_aGradients.front().m_aSize = aPtSize; 10133 it = m_aGradients.begin(); 10134 } 10135 10136 OStringBuffer aObjName( 16 ); 10137 aObjName.append( 'P' ); 10138 aObjName.append( it->m_nObject ); 10139 pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject ); 10140 10141 return it->m_nObject; 10142 } 10143 10144 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient ) 10145 { 10146 MARK( "drawGradient (Rectangle)" ); 10147 10148 if( m_aContext.Version == PDFWriter::PDF_1_2 ) 10149 { 10150 drawRectangle( rRect ); 10151 return; 10152 } 10153 10154 sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() ); 10155 10156 Point aTranslate( rRect.BottomLeft() ); 10157 aTranslate += Point( 0, 1 ); 10158 10159 updateGraphicsState(); 10160 10161 OStringBuffer aLine( 80 ); 10162 aLine.append( "q 1 0 0 1 " ); 10163 m_aPages.back().appendPoint( aTranslate, aLine ); 10164 aLine.append( " cm " ); 10165 // if a stroke is appended reset the clip region before stroke 10166 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10167 aLine.append( "q " ); 10168 aLine.append( "0 0 " ); 10169 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); 10170 aLine.append( ' ' ); 10171 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true ); 10172 aLine.append( " re W n\n" ); 10173 10174 aLine.append( "/P" ); 10175 aLine.append( nGradient ); 10176 aLine.append( " sh " ); 10177 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10178 { 10179 aLine.append( "Q 0 0 " ); 10180 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); 10181 aLine.append( ' ' ); 10182 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true ); 10183 aLine.append( " re S " ); 10184 } 10185 aLine.append( "Q\n" ); 10186 writeBuffer( aLine.getStr(), aLine.getLength() ); 10187 } 10188 10189 void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient ) 10190 { 10191 MARK( "drawGradient (PolyPolygon)" ); 10192 10193 if( m_aContext.Version == PDFWriter::PDF_1_2 ) 10194 { 10195 drawPolyPolygon( rPolyPoly ); 10196 return; 10197 } 10198 10199 Rectangle aBoundRect = rPolyPoly.GetBoundRect(); 10200 sal_Int32 nGradient = createGradient( rGradient, aBoundRect.GetSize() ); 10201 10202 updateGraphicsState(); 10203 10204 Point aTranslate = aBoundRect.BottomLeft(); 10205 int nPolygons = rPolyPoly.Count(); 10206 10207 OStringBuffer aLine( 80*nPolygons ); 10208 aLine.append( "q " ); 10209 // set PolyPolygon as clip path 10210 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 10211 aLine.append( "W* n\n" ); 10212 aLine.append( "1 0 0 1 " ); 10213 m_aPages.back().appendPoint( aTranslate, aLine ); 10214 aLine.append( " cm\n" ); 10215 aLine.append( "/P" ); 10216 aLine.append( nGradient ); 10217 aLine.append( " sh Q\n" ); 10218 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10219 { 10220 // and draw the surrounding path 10221 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 10222 aLine.append( "S\n" ); 10223 } 10224 writeBuffer( aLine.getStr(), aLine.getLength() ); 10225 } 10226 10227 void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch ) 10228 { 10229 MARK( "drawHatch" ); 10230 10231 updateGraphicsState(); 10232 10233 if( rPolyPoly.Count() ) 10234 { 10235 PolyPolygon aPolyPoly( rPolyPoly ); 10236 10237 aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME ); 10238 push( PUSH_LINECOLOR ); 10239 setLineColor( rHatch.GetColor() ); 10240 getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, sal_False ); 10241 pop(); 10242 } 10243 } 10244 10245 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall ) 10246 { 10247 MARK( "drawWallpaper" ); 10248 10249 bool bDrawColor = false; 10250 bool bDrawGradient = false; 10251 bool bDrawBitmap = false; 10252 10253 BitmapEx aBitmap; 10254 Point aBmpPos = rRect.TopLeft(); 10255 Size aBmpSize; 10256 if( rWall.IsBitmap() ) 10257 { 10258 aBitmap = rWall.GetBitmap(); 10259 aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(), 10260 getMapMode(), 10261 getReferenceDevice(), 10262 aBitmap.GetPrefSize() ); 10263 Rectangle aRect( rRect ); 10264 if( rWall.IsRect() ) 10265 { 10266 aRect = rWall.GetRect(); 10267 aBmpPos = aRect.TopLeft(); 10268 aBmpSize = aRect.GetSize(); 10269 } 10270 if( rWall.GetStyle() != WALLPAPER_SCALE ) 10271 { 10272 if( rWall.GetStyle() != WALLPAPER_TILE ) 10273 { 10274 bDrawBitmap = true; 10275 if( rWall.IsGradient() ) 10276 bDrawGradient = true; 10277 else 10278 bDrawColor = true; 10279 switch( rWall.GetStyle() ) 10280 { 10281 case WALLPAPER_TOPLEFT: 10282 break; 10283 case WALLPAPER_TOP: 10284 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10285 break; 10286 case WALLPAPER_LEFT: 10287 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10288 break; 10289 case WALLPAPER_TOPRIGHT: 10290 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10291 break; 10292 case WALLPAPER_CENTER: 10293 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10294 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10295 break; 10296 case WALLPAPER_RIGHT: 10297 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10298 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10299 break; 10300 case WALLPAPER_BOTTOMLEFT: 10301 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10302 break; 10303 case WALLPAPER_BOTTOM: 10304 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10305 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10306 break; 10307 case WALLPAPER_BOTTOMRIGHT: 10308 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10309 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10310 break; 10311 default: ; 10312 } 10313 } 10314 else 10315 { 10316 // push the bitmap 10317 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) ); 10318 10319 // convert to page coordinates; this needs to be done here 10320 // since the emit does not know the page anymore 10321 Rectangle aConvertRect( aBmpPos, aBmpSize ); 10322 m_aPages.back().convertRect( aConvertRect ); 10323 10324 OStringBuffer aNameBuf(16); 10325 aNameBuf.append( "Im" ); 10326 aNameBuf.append( rEmit.m_nObject ); 10327 OString aImageName( aNameBuf.makeStringAndClear() ); 10328 10329 // push the pattern 10330 OStringBuffer aTilingStream( 32 ); 10331 appendFixedInt( aConvertRect.GetWidth(), aTilingStream ); 10332 aTilingStream.append( " 0 0 " ); 10333 appendFixedInt( aConvertRect.GetHeight(), aTilingStream ); 10334 aTilingStream.append( " 0 0 cm\n/" ); 10335 aTilingStream.append( aImageName ); 10336 aTilingStream.append( " Do\n" ); 10337 10338 m_aTilings.push_back( TilingEmit() ); 10339 m_aTilings.back().m_nObject = createObject(); 10340 m_aTilings.back().m_aRectangle = Rectangle( Point( 0, 0 ), aConvertRect.GetSize() ); 10341 m_aTilings.back().m_pTilingStream = new SvMemoryStream(); 10342 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() ); 10343 // phase the tiling so wallpaper begins on upper left 10344 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor; 10345 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor; 10346 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject; 10347 10348 updateGraphicsState(); 10349 10350 OStringBuffer aObjName( 16 ); 10351 aObjName.append( 'P' ); 10352 aObjName.append( m_aTilings.back().m_nObject ); 10353 OString aPatternName( aObjName.makeStringAndClear() ); 10354 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject ); 10355 10356 // fill a rRect with the pattern 10357 OStringBuffer aLine( 100 ); 10358 aLine.append( "q /Pattern cs /" ); 10359 aLine.append( aPatternName ); 10360 aLine.append( " scn " ); 10361 m_aPages.back().appendRect( rRect, aLine ); 10362 aLine.append( " f Q\n" ); 10363 writeBuffer( aLine.getStr(), aLine.getLength() ); 10364 } 10365 } 10366 else 10367 { 10368 aBmpPos = aRect.TopLeft(); 10369 aBmpSize = aRect.GetSize(); 10370 bDrawBitmap = true; 10371 } 10372 10373 if( aBitmap.IsTransparent() ) 10374 { 10375 if( rWall.IsGradient() ) 10376 bDrawGradient = true; 10377 else 10378 bDrawColor = true; 10379 } 10380 } 10381 else if( rWall.IsGradient() ) 10382 bDrawGradient = true; 10383 else 10384 bDrawColor = true; 10385 10386 if( bDrawGradient ) 10387 { 10388 drawGradient( rRect, rWall.GetGradient() ); 10389 } 10390 if( bDrawColor ) 10391 { 10392 Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor; 10393 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; 10394 setLineColor( Color( COL_TRANSPARENT ) ); 10395 setFillColor( rWall.GetColor() ); 10396 drawRectangle( rRect ); 10397 setLineColor( aOldLineColor ); 10398 setFillColor( aOldFillColor ); 10399 } 10400 if( bDrawBitmap ) 10401 { 10402 // set temporary clip region since aBmpPos and aBmpSize 10403 // may be outside rRect 10404 OStringBuffer aLine( 20 ); 10405 aLine.append( "q " ); 10406 m_aPages.back().appendRect( rRect, aLine ); 10407 aLine.append( " W n\n" ); 10408 writeBuffer( aLine.getStr(), aLine.getLength() ); 10409 drawBitmap( aBmpPos, aBmpSize, aBitmap ); 10410 writeBuffer( "Q\n", 2 ); 10411 } 10412 } 10413 10414 void PDFWriterImpl::beginPattern( const Rectangle& rCellRect ) 10415 { 10416 beginRedirect( new SvMemoryStream(), rCellRect ); 10417 } 10418 10419 sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform ) 10420 { 10421 Rectangle aConvertRect( getRedirectTargetRect() ); 10422 DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" ); 10423 10424 // get scaling between current mapmode and PDF output 10425 Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) ); 10426 double fSX = (double(aScaling.Width()) / 10000.0); 10427 double fSY = (double(aScaling.Height()) / 10000.0); 10428 10429 // transform translation part of matrix 10430 Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] ); 10431 aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation ); 10432 10433 sal_Int32 nTilingId = m_aTilings.size(); 10434 m_aTilings.push_back( TilingEmit() ); 10435 TilingEmit& rTile = m_aTilings.back(); 10436 rTile.m_nObject = createObject(); 10437 rTile.m_aResources = m_aOutputStreams.front().m_aResourceDict; 10438 rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX; 10439 rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY; 10440 rTile.m_aTransform.matrix[2] = aTranslation.Width(); 10441 rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX; 10442 rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY; 10443 rTile.m_aTransform.matrix[5] = -aTranslation.Height(); 10444 // caution: endRedirect pops the stream, so do this last 10445 rTile.m_pTilingStream = dynamic_cast<SvMemoryStream*>(endRedirect()); 10446 // FIXME: bound rect will not work with rotated matrix 10447 rTile.m_aRectangle = Rectangle( Point(0,0), aConvertRect.GetSize() ); 10448 rTile.m_aCellSize = aConvertRect.GetSize(); 10449 10450 OStringBuffer aObjName( 16 ); 10451 aObjName.append( 'P' ); 10452 aObjName.append( rTile.m_nObject ); 10453 pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject ); 10454 return nTilingId; 10455 } 10456 10457 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill ) 10458 { 10459 if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() ) 10460 return; 10461 10462 m_aPages.back().endStream(); 10463 sal_Int32 nXObject = createObject(); 10464 OStringBuffer aNameBuf( 16 ); 10465 aNameBuf.append( "Pol" ); 10466 aNameBuf.append( nXObject ); 10467 OString aObjName( aNameBuf.makeStringAndClear() ); 10468 Rectangle aObjRect; 10469 if( updateObject( nXObject ) ) 10470 { 10471 // get bounding rect of object 10472 PolyPolygon aSubDiv; 10473 rPolyPoly.AdaptiveSubdivide( aSubDiv ); 10474 aObjRect = aSubDiv.GetBoundRect(); 10475 Rectangle aConvObjRect( aObjRect ); 10476 m_aPages.back().convertRect( aConvObjRect ); 10477 10478 // move polypolygon to bottom left of page 10479 PolyPolygon aLocalPath( rPolyPoly ); 10480 sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72; 10481 sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72; 10482 Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode ); 10483 sal_Int32 nXOff = aObjRect.Left(); 10484 sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom(); 10485 aLocalPath.Move( -nXOff, nYOff ); 10486 10487 // prepare XObject's content stream 10488 OStringBuffer aStream( 512 ); 10489 aStream.append( "/Pattern cs /P" ); 10490 aStream.append( m_aTilings[ nPattern ].m_nObject ); 10491 aStream.append( " scn\n" ); 10492 m_aPages.back().appendPolyPolygon( aLocalPath, aStream ); 10493 aStream.append( bEOFill ? "f*" : "f" ); 10494 SvMemoryStream aMemStream( aStream.getLength() ); 10495 aMemStream.Write( aStream.getStr(), aStream.getLength() ); 10496 bool bDeflate = compressStream( &aMemStream ); 10497 aMemStream.Seek( STREAM_SEEK_TO_END ); 10498 sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell(); 10499 aMemStream.Seek( STREAM_SEEK_TO_BEGIN ); 10500 10501 // add new XObject to global resource dict 10502 m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject; 10503 10504 // write XObject 10505 OStringBuffer aLine( 512 ); 10506 aLine.append( nXObject ); 10507 aLine.append( " 0 obj\n" 10508 "<</Type/XObject/Subtype/Form/BBox[0 0 " ); 10509 appendFixedInt( aConvObjRect.GetWidth(), aLine ); 10510 aLine.append( ' ' ); 10511 appendFixedInt( aConvObjRect.GetHeight(), aLine ); 10512 aLine.append( "]/Length " ); 10513 aLine.append( nStreamLen ); 10514 if( bDeflate ) 10515 aLine.append( "/Filter/FlateDecode" ); 10516 aLine.append( ">>\n" 10517 "stream\n" ); 10518 writeBuffer( aLine.getStr(), aLine.getLength() ); 10519 checkAndEnableStreamEncryption( nXObject ); 10520 writeBuffer( aMemStream.GetData(), nStreamLen ); 10521 disableStreamEncryption(); 10522 writeBuffer( "\nendstream\nendobj\n\n", 19 ); 10523 } 10524 m_aPages.back().beginStream(); 10525 OStringBuffer aLine( 80 ); 10526 aLine.append( "q 1 0 0 1 " ); 10527 m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine ); 10528 aLine.append( " cm/" ); 10529 aLine.append( aObjName ); 10530 aLine.append( " Do Q\n" ); 10531 writeBuffer( aLine.getStr(), aLine.getLength() ); 10532 } 10533 10534 void PDFWriterImpl::updateGraphicsState() 10535 { 10536 OStringBuffer aLine( 256 ); 10537 GraphicsState& rNewState = m_aGraphicsStack.front(); 10538 // first set clip region since it might invalidate everything else 10539 10540 if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) ) 10541 { 10542 rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion; 10543 10544 if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion || 10545 ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) ) 10546 { 10547 if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() ) 10548 { 10549 aLine.append( "Q " ); 10550 // invalidate everything but the clip region 10551 m_aCurrentPDFState = GraphicsState(); 10552 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion); 10553 } 10554 if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() ) 10555 { 10556 // clip region is always stored in private PDF mapmode 10557 MapMode aNewMapMode = rNewState.m_aMapMode; 10558 rNewState.m_aMapMode = m_aMapMode; 10559 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10560 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; 10561 10562 aLine.append( "q " ); 10563 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine ); 10564 aLine.append( "W* n\n" ); 10565 rNewState.m_aMapMode = aNewMapMode; 10566 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10567 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; 10568 } 10569 } 10570 } 10571 10572 if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) ) 10573 { 10574 rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode; 10575 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10576 } 10577 10578 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) ) 10579 { 10580 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont; 10581 getReferenceDevice()->SetFont( rNewState.m_aFont ); 10582 getReferenceDevice()->ImplNewFont(); 10583 } 10584 10585 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) ) 10586 { 10587 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode; 10588 getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode ); 10589 } 10590 10591 if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) ) 10592 { 10593 rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage; 10594 getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage ); 10595 } 10596 10597 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) ) 10598 { 10599 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor; 10600 if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor && 10601 rNewState.m_aLineColor != Color( COL_TRANSPARENT ) ) 10602 { 10603 appendStrokingColor( rNewState.m_aLineColor, aLine ); 10604 aLine.append( "\n" ); 10605 } 10606 } 10607 10608 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) ) 10609 { 10610 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor; 10611 if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor && 10612 rNewState.m_aFillColor != Color( COL_TRANSPARENT ) ) 10613 { 10614 appendNonStrokingColor( rNewState.m_aFillColor, aLine ); 10615 aLine.append( "\n" ); 10616 } 10617 } 10618 10619 if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) ) 10620 { 10621 rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent; 10622 if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent ) 10623 { 10624 // TODO: switch extended graphicsstate 10625 } 10626 } 10627 10628 // everything is up to date now 10629 m_aCurrentPDFState = m_aGraphicsStack.front(); 10630 if( aLine.getLength() ) 10631 writeBuffer( aLine.getStr(), aLine.getLength() ); 10632 } 10633 10634 /* #i47544# imitate OutputDevice behaviour: 10635 * if a font with a nontransparent color is set, it overwrites the current 10636 * text color. OTOH setting the text color will overwrite the color of the font. 10637 */ 10638 void PDFWriterImpl::setFont( const Font& rFont ) 10639 { 10640 Color aColor = rFont.GetColor(); 10641 if( aColor == Color( COL_TRANSPARENT ) ) 10642 aColor = m_aGraphicsStack.front().m_aFont.GetColor(); 10643 m_aGraphicsStack.front().m_aFont = rFont; 10644 m_aGraphicsStack.front().m_aFont.SetColor( aColor ); 10645 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont; 10646 } 10647 10648 void PDFWriterImpl::push( sal_uInt16 nFlags ) 10649 { 10650 OSL_ENSURE( m_aGraphicsStack.size() > 0, "invalid graphics stack" ); 10651 m_aGraphicsStack.push_front( m_aGraphicsStack.front() ); 10652 m_aGraphicsStack.front().m_nFlags = nFlags; 10653 } 10654 10655 void PDFWriterImpl::pop() 10656 { 10657 OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" ); 10658 if( m_aGraphicsStack.size() < 2 ) 10659 return; 10660 10661 GraphicsState aState = m_aGraphicsStack.front(); 10662 m_aGraphicsStack.pop_front(); 10663 GraphicsState& rOld = m_aGraphicsStack.front(); 10664 10665 // move those parameters back that were not pushed 10666 // in the first place 10667 if( ! (aState.m_nFlags & PUSH_LINECOLOR) ) 10668 setLineColor( aState.m_aLineColor ); 10669 if( ! (aState.m_nFlags & PUSH_FILLCOLOR) ) 10670 setFillColor( aState.m_aFillColor ); 10671 if( ! (aState.m_nFlags & PUSH_FONT) ) 10672 setFont( aState.m_aFont ); 10673 if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) ) 10674 setTextColor( aState.m_aFont.GetColor() ); 10675 if( ! (aState.m_nFlags & PUSH_MAPMODE) ) 10676 setMapMode( aState.m_aMapMode ); 10677 if( ! (aState.m_nFlags & PUSH_CLIPREGION) ) 10678 { 10679 // do not use setClipRegion here 10680 // it would convert again assuming the current mapmode 10681 rOld.m_aClipRegion = aState.m_aClipRegion; 10682 rOld.m_bClipRegion = aState.m_bClipRegion; 10683 } 10684 if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) ) 10685 setTextLineColor( aState.m_aTextLineColor ); 10686 if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) ) 10687 setOverlineColor( aState.m_aOverlineColor ); 10688 if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) ) 10689 setTextAlign( aState.m_aFont.GetAlign() ); 10690 if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) ) 10691 setTextFillColor( aState.m_aFont.GetFillColor() ); 10692 if( ! (aState.m_nFlags & PUSH_REFPOINT) ) 10693 { 10694 // what ? 10695 } 10696 // invalidate graphics state 10697 m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U); 10698 } 10699 10700 void PDFWriterImpl::setMapMode( const MapMode& rMapMode ) 10701 { 10702 m_aGraphicsStack.front().m_aMapMode = rMapMode; 10703 getReferenceDevice()->SetMapMode( rMapMode ); 10704 m_aCurrentPDFState.m_aMapMode = rMapMode; 10705 } 10706 10707 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion ) 10708 { 10709 basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ); 10710 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); 10711 m_aGraphicsStack.front().m_aClipRegion = aRegion; 10712 m_aGraphicsStack.front().m_bClipRegion = true; 10713 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10714 } 10715 10716 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY ) 10717 { 10718 if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() ) 10719 { 10720 Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10721 m_aMapMode, 10722 getReferenceDevice(), 10723 Point( nX, nY ) ) ); 10724 aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10725 m_aMapMode, 10726 getReferenceDevice(), 10727 Point() ); 10728 basegfx::B2DHomMatrix aMat; 10729 aMat.translate( aPoint.X(), aPoint.Y() ); 10730 m_aGraphicsStack.front().m_aClipRegion.transform( aMat ); 10731 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10732 } 10733 } 10734 10735 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect ) 10736 { 10737 basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect( 10738 basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); 10739 return intersectClipRegion( aRect ); 10740 } 10741 10742 10743 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion ) 10744 { 10745 basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) ); 10746 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); 10747 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10748 if( m_aGraphicsStack.front().m_bClipRegion ) 10749 { 10750 basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) ); 10751 aRegion = basegfx::tools::prepareForPolygonOperation( aRegion ); 10752 m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion ); 10753 } 10754 else 10755 { 10756 m_aGraphicsStack.front().m_aClipRegion = aRegion; 10757 m_aGraphicsStack.front().m_bClipRegion = true; 10758 } 10759 return true; 10760 } 10761 10762 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr ) 10763 { 10764 if( nPageNr < 0 ) 10765 nPageNr = m_nCurrentPage; 10766 10767 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10768 return; 10769 10770 m_aNotes.push_back( PDFNoteEntry() ); 10771 m_aNotes.back().m_nObject = createObject(); 10772 m_aNotes.back().m_aContents = rNote; 10773 m_aNotes.back().m_aRect = rRect; 10774 // convert to default user space now, since the mapmode may change 10775 m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect ); 10776 10777 // insert note to page's annotation list 10778 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject ); 10779 } 10780 10781 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr ) 10782 { 10783 if( nPageNr < 0 ) 10784 nPageNr = m_nCurrentPage; 10785 10786 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10787 return -1; 10788 10789 sal_Int32 nRet = m_aLinks.size(); 10790 10791 m_aLinks.push_back( PDFLink() ); 10792 m_aLinks.back().m_nObject = createObject(); 10793 m_aLinks.back().m_nPage = nPageNr; 10794 m_aLinks.back().m_aRect = rRect; 10795 // convert to default user space now, since the mapmode may change 10796 m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect ); 10797 10798 // insert link to page's annotation list 10799 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject ); 10800 10801 return nRet; 10802 } 10803 10804 //--->i56629 10805 sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10806 { 10807 if( nPageNr < 0 ) 10808 nPageNr = m_nCurrentPage; 10809 10810 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10811 return -1; 10812 10813 sal_Int32 nRet = m_aNamedDests.size(); 10814 10815 m_aNamedDests.push_back( PDFNamedDest() ); 10816 m_aNamedDests.back().m_aDestName = sDestName; 10817 m_aNamedDests.back().m_nPage = nPageNr; 10818 m_aNamedDests.back().m_eType = eType; 10819 m_aNamedDests.back().m_aRect = rRect; 10820 // convert to default user space now, since the mapmode may change 10821 m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect ); 10822 10823 return nRet; 10824 } 10825 //<---i56629 10826 10827 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10828 { 10829 if( nPageNr < 0 ) 10830 nPageNr = m_nCurrentPage; 10831 10832 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10833 return -1; 10834 10835 sal_Int32 nRet = m_aDests.size(); 10836 10837 m_aDests.push_back( PDFDest() ); 10838 m_aDests.back().m_nPage = nPageNr; 10839 m_aDests.back().m_eType = eType; 10840 m_aDests.back().m_aRect = rRect; 10841 // convert to default user space now, since the mapmode may change 10842 m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect ); 10843 10844 return nRet; 10845 } 10846 10847 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10848 { 10849 return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType ); 10850 } 10851 10852 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId ) 10853 { 10854 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) 10855 return -1; 10856 if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() ) 10857 return -2; 10858 10859 m_aLinks[ nLinkId ].m_nDest = nDestId; 10860 10861 return 0; 10862 } 10863 10864 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL ) 10865 { 10866 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) 10867 return -1; 10868 10869 m_aLinks[ nLinkId ].m_nDest = -1; 10870 10871 using namespace ::com::sun::star; 10872 10873 if (!m_xTrans.is()) 10874 { 10875 uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() ); 10876 if( xFact.is() ) 10877 { 10878 m_xTrans = uno::Reference < util::XURLTransformer >( 10879 xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY ); 10880 } 10881 } 10882 10883 util::URL aURL; 10884 aURL.Complete = rURL; 10885 10886 if (m_xTrans.is()) 10887 m_xTrans->parseStrict( aURL ); 10888 10889 m_aLinks[ nLinkId ].m_aURL = aURL.Complete; 10890 10891 return 0; 10892 } 10893 10894 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId ) 10895 { 10896 m_aLinkPropertyMap[ nPropertyId ] = nLinkId; 10897 } 10898 10899 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID ) 10900 { 10901 // create new item 10902 sal_Int32 nNewItem = m_aOutline.size(); 10903 m_aOutline.push_back( PDFOutlineEntry() ); 10904 10905 // set item attributes 10906 setOutlineItemParent( nNewItem, nParent ); 10907 setOutlineItemText( nNewItem, rText ); 10908 setOutlineItemDest( nNewItem, nDestID ); 10909 10910 return nNewItem; 10911 } 10912 10913 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent ) 10914 { 10915 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) 10916 return -1; 10917 10918 int nRet = 0; 10919 10920 if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem ) 10921 { 10922 nNewParent = 0; 10923 nRet = -2; 10924 } 10925 // remove item from previous parent 10926 sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID; 10927 if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() ) 10928 { 10929 PDFOutlineEntry& rParent = m_aOutline[ nParentID ]; 10930 10931 for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin(); 10932 it != rParent.m_aChildren.end(); ++it ) 10933 { 10934 if( *it == nItem ) 10935 { 10936 rParent.m_aChildren.erase( it ); 10937 break; 10938 } 10939 } 10940 } 10941 10942 // insert item to new parent's list of children 10943 m_aOutline[ nNewParent ].m_aChildren.push_back( nItem ); 10944 10945 return nRet; 10946 } 10947 10948 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText ) 10949 { 10950 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) 10951 return -1; 10952 10953 m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText ); 10954 return 0; 10955 } 10956 10957 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID ) 10958 { 10959 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist 10960 return -1; 10961 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist 10962 return -2; 10963 m_aOutline[nItem].m_nDestID = nDestID; 10964 return 0; 10965 } 10966 10967 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType ) 10968 { 10969 static std::map< PDFWriter::StructElement, const char* > aTagStrings; 10970 if( aTagStrings.empty() ) 10971 { 10972 aTagStrings[ PDFWriter::NonStructElement] = "NonStruct"; 10973 aTagStrings[ PDFWriter::Document ] = "Document"; 10974 aTagStrings[ PDFWriter::Part ] = "Part"; 10975 aTagStrings[ PDFWriter::Article ] = "Art"; 10976 aTagStrings[ PDFWriter::Section ] = "Sect"; 10977 aTagStrings[ PDFWriter::Division ] = "Div"; 10978 aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote"; 10979 aTagStrings[ PDFWriter::Caption ] = "Caption"; 10980 aTagStrings[ PDFWriter::TOC ] = "TOC"; 10981 aTagStrings[ PDFWriter::TOCI ] = "TOCI"; 10982 aTagStrings[ PDFWriter::Index ] = "Index"; 10983 aTagStrings[ PDFWriter::Paragraph ] = "P"; 10984 aTagStrings[ PDFWriter::Heading ] = "H"; 10985 aTagStrings[ PDFWriter::H1 ] = "H1"; 10986 aTagStrings[ PDFWriter::H2 ] = "H2"; 10987 aTagStrings[ PDFWriter::H3 ] = "H3"; 10988 aTagStrings[ PDFWriter::H4 ] = "H4"; 10989 aTagStrings[ PDFWriter::H5 ] = "H5"; 10990 aTagStrings[ PDFWriter::H6 ] = "H6"; 10991 aTagStrings[ PDFWriter::List ] = "L"; 10992 aTagStrings[ PDFWriter::ListItem ] = "LI"; 10993 aTagStrings[ PDFWriter::LILabel ] = "Lbl"; 10994 aTagStrings[ PDFWriter::LIBody ] = "LBody"; 10995 aTagStrings[ PDFWriter::Table ] = "Table"; 10996 aTagStrings[ PDFWriter::TableRow ] = "TR"; 10997 aTagStrings[ PDFWriter::TableHeader ] = "TH"; 10998 aTagStrings[ PDFWriter::TableData ] = "TD"; 10999 aTagStrings[ PDFWriter::Span ] = "Span"; 11000 aTagStrings[ PDFWriter::Quote ] = "Quote"; 11001 aTagStrings[ PDFWriter::Note ] = "Note"; 11002 aTagStrings[ PDFWriter::Reference ] = "Reference"; 11003 aTagStrings[ PDFWriter::BibEntry ] = "BibEntry"; 11004 aTagStrings[ PDFWriter::Code ] = "Code"; 11005 aTagStrings[ PDFWriter::Link ] = "Link"; 11006 aTagStrings[ PDFWriter::Figure ] = "Figure"; 11007 aTagStrings[ PDFWriter::Formula ] = "Formula"; 11008 aTagStrings[ PDFWriter::Form ] = "Form"; 11009 } 11010 11011 std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType ); 11012 11013 return it != aTagStrings.end() ? it->second : "Div"; 11014 } 11015 11016 void PDFWriterImpl::beginStructureElementMCSeq() 11017 { 11018 if( m_bEmitStructure && 11019 m_nCurrentStructElement > 0 && // StructTreeRoot 11020 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence 11021 ) 11022 { 11023 PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ]; 11024 OStringBuffer aLine( 128 ); 11025 sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size(); 11026 aLine.append( "/" ); 11027 if( rEle.m_aAlias.getLength() > 0 ) 11028 aLine.append( rEle.m_aAlias ); 11029 else 11030 aLine.append( getStructureTag( rEle.m_eType ) ); 11031 aLine.append( "<</MCID " ); 11032 aLine.append( nMCID ); 11033 aLine.append( ">>BDC\n" ); 11034 writeBuffer( aLine.getStr(), aLine.getLength() ); 11035 11036 // update the element's content list 11037 #if OSL_DEBUG_LEVEL > 1 11038 fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n", 11039 nMCID, 11040 m_aPages[ m_nCurrentPage ].m_nPageObject, 11041 rEle.m_nFirstPageObject ); 11042 #endif 11043 rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) ); 11044 // update the page's mcid parent list 11045 m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject ); 11046 // mark element MC sequence as open 11047 rEle.m_bOpenMCSeq = true; 11048 } 11049 // handle artifacts 11050 else if( ! m_bEmitStructure && m_aContext.Tagged && 11051 m_nCurrentStructElement > 0 && 11052 m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement && 11053 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence 11054 ) 11055 { 11056 OStringBuffer aLine( 128 ); 11057 aLine.append( "/Artifact BMC\n" ); 11058 writeBuffer( aLine.getStr(), aLine.getLength() ); 11059 // mark element MC sequence as open 11060 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true; 11061 } 11062 } 11063 11064 void PDFWriterImpl::endStructureElementMCSeq() 11065 { 11066 if( m_nCurrentStructElement > 0 && // StructTreeRoot 11067 ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) && 11068 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence 11069 ) 11070 { 11071 writeBuffer( "EMC\n", 4 ); 11072 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false; 11073 } 11074 } 11075 11076 bool PDFWriterImpl::checkEmitStructure() 11077 { 11078 bool bEmit = false; 11079 if( m_aContext.Tagged ) 11080 { 11081 bEmit = true; 11082 sal_Int32 nEle = m_nCurrentStructElement; 11083 while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) ) 11084 { 11085 if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement ) 11086 { 11087 bEmit = false; 11088 break; 11089 } 11090 nEle = m_aStructure[ nEle ].m_nParentElement; 11091 } 11092 } 11093 return bEmit; 11094 } 11095 11096 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias ) 11097 { 11098 if( m_nCurrentPage < 0 ) 11099 return -1; 11100 11101 if( ! m_aContext.Tagged ) 11102 return -1; 11103 11104 // close eventual current MC sequence 11105 endStructureElementMCSeq(); 11106 11107 if( m_nCurrentStructElement == 0 && 11108 eType != PDFWriter::Document && eType != PDFWriter::NonStructElement ) 11109 { 11110 // struct tree root hit, but not beginning document 11111 // this might happen with setCurrentStructureElement 11112 // silently insert structure into document again if one properly exists 11113 if( ! m_aStructure[ 0 ].m_aChildren.empty() ) 11114 { 11115 PDFWriter::StructElement childType = PDFWriter::NonStructElement; 11116 sal_Int32 nNewCurElement = 0; 11117 const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren; 11118 for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin(); 11119 childType != PDFWriter::Document && it != rRootChildren.end(); ++it ) 11120 { 11121 nNewCurElement = *it; 11122 childType = m_aStructure[ nNewCurElement ].m_eType; 11123 } 11124 if( childType == PDFWriter::Document ) 11125 { 11126 m_nCurrentStructElement = nNewCurElement; 11127 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" ); 11128 } 11129 else { 11130 DBG_ERROR( "document structure in disorder !" ); 11131 } 11132 } 11133 else { 11134 DBG_ERROR( "PDF document structure MUST be contained in a Document element" ); 11135 } 11136 } 11137 11138 sal_Int32 nNewId = sal_Int32(m_aStructure.size()); 11139 m_aStructure.push_back( PDFStructureElement() ); 11140 PDFStructureElement& rEle = m_aStructure.back(); 11141 rEle.m_eType = eType; 11142 rEle.m_nOwnElement = nNewId; 11143 rEle.m_nParentElement = m_nCurrentStructElement; 11144 rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject; 11145 m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId ); 11146 m_nCurrentStructElement = nNewId; 11147 11148 // handle alias names 11149 if( rAlias.getLength() && eType != PDFWriter::NonStructElement ) 11150 { 11151 OStringBuffer aNameBuf( rAlias.getLength() ); 11152 appendName( rAlias, aNameBuf ); 11153 OString aAliasName( aNameBuf.makeStringAndClear() ); 11154 rEle.m_aAlias = aAliasName; 11155 m_aRoleMap[ aAliasName ] = getStructureTag( eType ); 11156 } 11157 11158 #if OSL_DEBUG_LEVEL > 1 11159 OStringBuffer aLine( "beginStructureElement " ); 11160 aLine.append( m_nCurrentStructElement ); 11161 aLine.append( ": " ); 11162 aLine.append( getStructureTag( eType ) ); 11163 if( rEle.m_aAlias.getLength() ) 11164 { 11165 aLine.append( " aliased as \"" ); 11166 aLine.append( rEle.m_aAlias ); 11167 aLine.append( '\"' ); 11168 } 11169 emitComment( aLine.getStr() ); 11170 #endif 11171 11172 // check whether to emit structure henceforth 11173 m_bEmitStructure = checkEmitStructure(); 11174 11175 if( m_bEmitStructure ) // don't create nonexistant objects 11176 { 11177 rEle.m_nObject = createObject(); 11178 // update parent's kids list 11179 m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject ); 11180 } 11181 return nNewId; 11182 } 11183 11184 void PDFWriterImpl::endStructureElement() 11185 { 11186 if( m_nCurrentPage < 0 ) 11187 return; 11188 11189 if( ! m_aContext.Tagged ) 11190 return; 11191 11192 if( m_nCurrentStructElement == 0 ) 11193 { 11194 // hit the struct tree root, that means there is an endStructureElement 11195 // without corresponding beginStructureElement 11196 return; 11197 } 11198 11199 // end the marked content sequence 11200 endStructureElementMCSeq(); 11201 11202 #if OSL_DEBUG_LEVEL > 1 11203 OStringBuffer aLine( "endStructureElement " ); 11204 aLine.append( m_nCurrentStructElement ); 11205 aLine.append( ": " ); 11206 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); 11207 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() ) 11208 { 11209 aLine.append( " aliased as \"" ); 11210 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); 11211 aLine.append( '\"' ); 11212 } 11213 #endif 11214 11215 // "end" the structure element, the parent becomes current element 11216 m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement; 11217 11218 // check whether to emit structure henceforth 11219 m_bEmitStructure = checkEmitStructure(); 11220 11221 #if OSL_DEBUG_LEVEL > 1 11222 if( m_bEmitStructure ) 11223 emitComment( aLine.getStr() ); 11224 #endif 11225 } 11226 11227 //---> i94258 11228 /* 11229 * This function adds an internal structure list container to overcome the 8191 elements array limitation 11230 * in kids element emission. 11231 * Recursive function 11232 * 11233 */ 11234 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle ) 11235 { 11236 if( rEle.m_eType == PDFWriter::NonStructElement && 11237 rEle.m_nOwnElement != rEle.m_nParentElement ) 11238 return; 11239 11240 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it ) 11241 { 11242 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) ) 11243 { 11244 PDFStructureElement& rChild = m_aStructure[ *it ]; 11245 if( rChild.m_eType != PDFWriter::NonStructElement ) 11246 { 11247 //triggered when a child of the rEle element is found 11248 if( rChild.m_nParentElement == rEle.m_nOwnElement ) 11249 addInternalStructureContainer( rChild );//examine the child 11250 else 11251 { 11252 DBG_ERROR( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" ); 11253 #if OSL_DEBUG_LEVEL > 1 11254 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it ); 11255 #endif 11256 } 11257 } 11258 } 11259 else 11260 { 11261 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" ); 11262 #if OSL_DEBUG_LEVEL > 1 11263 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it ); 11264 #endif 11265 } 11266 } 11267 11268 if( rEle.m_nOwnElement != rEle.m_nParentElement ) 11269 { 11270 if( !rEle.m_aKids.empty() ) 11271 { 11272 if( rEle.m_aKids.size() > ncMaxPDFArraySize ) { 11273 //then we need to add the containers for the kids elements 11274 // a list to be used for the new kid element 11275 std::list< PDFStructureElementKid > aNewKids; 11276 std::list< sal_Int32 > aNewChildren; 11277 11278 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?) 11279 OStringBuffer aNameBuf( "Div" ); 11280 OString aAliasName( aNameBuf.makeStringAndClear() ); 11281 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division ); 11282 11283 while( rEle.m_aKids.size() > ncMaxPDFArraySize ) 11284 { 11285 sal_Int32 nCurrentStructElement = rEle.m_nOwnElement; 11286 sal_Int32 nNewId = sal_Int32(m_aStructure.size()); 11287 m_aStructure.push_back( PDFStructureElement() ); 11288 PDFStructureElement& rEleNew = m_aStructure.back(); 11289 rEleNew.m_aAlias = aAliasName; 11290 rEleNew.m_eType = PDFWriter::Division; // a new Div type container 11291 rEleNew.m_nOwnElement = nNewId; 11292 rEleNew.m_nParentElement = nCurrentStructElement; 11293 //inherit the same page as the first child to be reparented 11294 rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject; 11295 rEleNew.m_nObject = createObject();//assign a PDF object number 11296 //add the object to the kid list of the parent 11297 aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) ); 11298 aNewChildren.push_back( nNewId ); 11299 11300 std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() ); 11301 std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() ); 11302 advance( aChildEndIt, ncMaxPDFArraySize ); 11303 advance( aKidEndIt, ncMaxPDFArraySize ); 11304 11305 rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(), 11306 rEle.m_aKids, 11307 rEle.m_aKids.begin(), 11308 aKidEndIt ); 11309 rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(), 11310 rEle.m_aChildren, 11311 rEle.m_aChildren.begin(), 11312 aChildEndIt ); 11313 // set the kid's new parent 11314 for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin(); 11315 it != rEleNew.m_aChildren.end(); ++it ) 11316 { 11317 m_aStructure[ *it ].m_nParentElement = nNewId; 11318 } 11319 } 11320 //finally add the new kids resulting from the container added 11321 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() ); 11322 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() ); 11323 } 11324 } 11325 } 11326 } 11327 //<--- i94258 11328 11329 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle ) 11330 { 11331 bool bSuccess = false; 11332 11333 if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) ) 11334 { 11335 // end eventual previous marked content sequence 11336 endStructureElementMCSeq(); 11337 11338 m_nCurrentStructElement = nEle; 11339 m_bEmitStructure = checkEmitStructure(); 11340 #if OSL_DEBUG_LEVEL > 1 11341 OStringBuffer aLine( "setCurrentStructureElement " ); 11342 aLine.append( m_nCurrentStructElement ); 11343 aLine.append( ": " ); 11344 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); 11345 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() ) 11346 { 11347 aLine.append( " aliased as \"" ); 11348 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); 11349 aLine.append( '\"' ); 11350 } 11351 if( ! m_bEmitStructure ) 11352 aLine.append( " (inside NonStruct)" ); 11353 emitComment( aLine.getStr() ); 11354 #endif 11355 bSuccess = true; 11356 } 11357 11358 return bSuccess; 11359 } 11360 11361 sal_Int32 PDFWriterImpl::getCurrentStructureElement() 11362 { 11363 return m_nCurrentStructElement; 11364 } 11365 11366 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal ) 11367 { 11368 if( !m_aContext.Tagged ) 11369 return false; 11370 11371 bool bInsert = false; 11372 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11373 { 11374 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11375 switch( eAttr ) 11376 { 11377 case PDFWriter::Placement: 11378 if( eVal == PDFWriter::Block || 11379 eVal == PDFWriter::Inline || 11380 eVal == PDFWriter::Before || 11381 eVal == PDFWriter::Start || 11382 eVal == PDFWriter::End ) 11383 bInsert = true; 11384 break; 11385 case PDFWriter::WritingMode: 11386 if( eVal == PDFWriter::LrTb || 11387 eVal == PDFWriter::RlTb || 11388 eVal == PDFWriter::TbRl ) 11389 { 11390 bInsert = true; 11391 } 11392 break; 11393 case PDFWriter::TextAlign: 11394 if( eVal == PDFWriter::Start || 11395 eVal == PDFWriter::Center || 11396 eVal == PDFWriter::End || 11397 eVal == PDFWriter::Justify ) 11398 { 11399 if( eType == PDFWriter::Paragraph || 11400 eType == PDFWriter::Heading || 11401 eType == PDFWriter::H1 || 11402 eType == PDFWriter::H2 || 11403 eType == PDFWriter::H3 || 11404 eType == PDFWriter::H4 || 11405 eType == PDFWriter::H5 || 11406 eType == PDFWriter::H6 || 11407 eType == PDFWriter::List || 11408 eType == PDFWriter::ListItem || 11409 eType == PDFWriter::LILabel || 11410 eType == PDFWriter::LIBody || 11411 eType == PDFWriter::Table || 11412 eType == PDFWriter::TableRow || 11413 eType == PDFWriter::TableHeader || 11414 eType == PDFWriter::TableData ) 11415 { 11416 bInsert = true; 11417 } 11418 } 11419 break; 11420 case PDFWriter::Width: 11421 case PDFWriter::Height: 11422 if( eVal == PDFWriter::Auto ) 11423 { 11424 if( eType == PDFWriter::Figure || 11425 eType == PDFWriter::Formula || 11426 eType == PDFWriter::Form || 11427 eType == PDFWriter::Table || 11428 eType == PDFWriter::TableHeader || 11429 eType == PDFWriter::TableData ) 11430 { 11431 bInsert = true; 11432 } 11433 } 11434 break; 11435 case PDFWriter::BlockAlign: 11436 if( eVal == PDFWriter::Before || 11437 eVal == PDFWriter::Middle || 11438 eVal == PDFWriter::After || 11439 eVal == PDFWriter::Justify ) 11440 { 11441 if( eType == PDFWriter::TableHeader || 11442 eType == PDFWriter::TableData ) 11443 { 11444 bInsert = true; 11445 } 11446 } 11447 break; 11448 case PDFWriter::InlineAlign: 11449 if( eVal == PDFWriter::Start || 11450 eVal == PDFWriter::Center || 11451 eVal == PDFWriter::End ) 11452 { 11453 if( eType == PDFWriter::TableHeader || 11454 eType == PDFWriter::TableData ) 11455 { 11456 bInsert = true; 11457 } 11458 } 11459 break; 11460 case PDFWriter::LineHeight: 11461 if( eVal == PDFWriter::Normal || 11462 eVal == PDFWriter::Auto ) 11463 { 11464 // only for ILSE and BLSE 11465 if( eType == PDFWriter::Paragraph || 11466 eType == PDFWriter::Heading || 11467 eType == PDFWriter::H1 || 11468 eType == PDFWriter::H2 || 11469 eType == PDFWriter::H3 || 11470 eType == PDFWriter::H4 || 11471 eType == PDFWriter::H5 || 11472 eType == PDFWriter::H6 || 11473 eType == PDFWriter::List || 11474 eType == PDFWriter::ListItem || 11475 eType == PDFWriter::LILabel || 11476 eType == PDFWriter::LIBody || 11477 eType == PDFWriter::Table || 11478 eType == PDFWriter::TableRow || 11479 eType == PDFWriter::TableHeader || 11480 eType == PDFWriter::TableData || 11481 eType == PDFWriter::Span || 11482 eType == PDFWriter::Quote || 11483 eType == PDFWriter::Note || 11484 eType == PDFWriter::Reference || 11485 eType == PDFWriter::BibEntry || 11486 eType == PDFWriter::Code || 11487 eType == PDFWriter::Link ) 11488 { 11489 bInsert = true; 11490 } 11491 } 11492 break; 11493 case PDFWriter::TextDecorationType: 11494 if( eVal == PDFWriter::NONE || 11495 eVal == PDFWriter::Underline || 11496 eVal == PDFWriter::Overline || 11497 eVal == PDFWriter::LineThrough ) 11498 { 11499 // only for ILSE and BLSE 11500 if( eType == PDFWriter::Paragraph || 11501 eType == PDFWriter::Heading || 11502 eType == PDFWriter::H1 || 11503 eType == PDFWriter::H2 || 11504 eType == PDFWriter::H3 || 11505 eType == PDFWriter::H4 || 11506 eType == PDFWriter::H5 || 11507 eType == PDFWriter::H6 || 11508 eType == PDFWriter::List || 11509 eType == PDFWriter::ListItem || 11510 eType == PDFWriter::LILabel || 11511 eType == PDFWriter::LIBody || 11512 eType == PDFWriter::Table || 11513 eType == PDFWriter::TableRow || 11514 eType == PDFWriter::TableHeader || 11515 eType == PDFWriter::TableData || 11516 eType == PDFWriter::Span || 11517 eType == PDFWriter::Quote || 11518 eType == PDFWriter::Note || 11519 eType == PDFWriter::Reference || 11520 eType == PDFWriter::BibEntry || 11521 eType == PDFWriter::Code || 11522 eType == PDFWriter::Link ) 11523 { 11524 bInsert = true; 11525 } 11526 } 11527 break; 11528 case PDFWriter::ListNumbering: 11529 if( eVal == PDFWriter::NONE || 11530 eVal == PDFWriter::Disc || 11531 eVal == PDFWriter::Circle || 11532 eVal == PDFWriter::Square || 11533 eVal == PDFWriter::Decimal || 11534 eVal == PDFWriter::UpperRoman || 11535 eVal == PDFWriter::LowerRoman || 11536 eVal == PDFWriter::UpperAlpha || 11537 eVal == PDFWriter::LowerAlpha ) 11538 { 11539 if( eType == PDFWriter::List ) 11540 bInsert = true; 11541 } 11542 break; 11543 default: break; 11544 } 11545 } 11546 11547 if( bInsert ) 11548 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal ); 11549 #if OSL_DEBUG_LEVEL > 1 11550 else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11551 fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n", 11552 getAttributeTag( eAttr ), 11553 getAttributeValueTag( eVal ), 11554 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ), 11555 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() 11556 ); 11557 #endif 11558 11559 return bInsert; 11560 } 11561 11562 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue ) 11563 { 11564 if( ! m_aContext.Tagged ) 11565 return false; 11566 11567 bool bInsert = false; 11568 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11569 { 11570 if( eAttr == PDFWriter::Language ) 11571 { 11572 m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue ); 11573 return true; 11574 } 11575 11576 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11577 switch( eAttr ) 11578 { 11579 case PDFWriter::SpaceBefore: 11580 case PDFWriter::SpaceAfter: 11581 case PDFWriter::StartIndent: 11582 case PDFWriter::EndIndent: 11583 // just for BLSE 11584 if( eType == PDFWriter::Paragraph || 11585 eType == PDFWriter::Heading || 11586 eType == PDFWriter::H1 || 11587 eType == PDFWriter::H2 || 11588 eType == PDFWriter::H3 || 11589 eType == PDFWriter::H4 || 11590 eType == PDFWriter::H5 || 11591 eType == PDFWriter::H6 || 11592 eType == PDFWriter::List || 11593 eType == PDFWriter::ListItem || 11594 eType == PDFWriter::LILabel || 11595 eType == PDFWriter::LIBody || 11596 eType == PDFWriter::Table || 11597 eType == PDFWriter::TableRow || 11598 eType == PDFWriter::TableHeader || 11599 eType == PDFWriter::TableData ) 11600 { 11601 bInsert = true; 11602 } 11603 break; 11604 case PDFWriter::TextIndent: 11605 // paragraph like BLSE and additional elements 11606 if( eType == PDFWriter::Paragraph || 11607 eType == PDFWriter::Heading || 11608 eType == PDFWriter::H1 || 11609 eType == PDFWriter::H2 || 11610 eType == PDFWriter::H3 || 11611 eType == PDFWriter::H4 || 11612 eType == PDFWriter::H5 || 11613 eType == PDFWriter::H6 || 11614 eType == PDFWriter::LILabel || 11615 eType == PDFWriter::LIBody || 11616 eType == PDFWriter::TableHeader || 11617 eType == PDFWriter::TableData ) 11618 { 11619 bInsert = true; 11620 } 11621 break; 11622 case PDFWriter::Width: 11623 case PDFWriter::Height: 11624 if( eType == PDFWriter::Figure || 11625 eType == PDFWriter::Formula || 11626 eType == PDFWriter::Form || 11627 eType == PDFWriter::Table || 11628 eType == PDFWriter::TableHeader || 11629 eType == PDFWriter::TableData ) 11630 { 11631 bInsert = true; 11632 } 11633 break; 11634 case PDFWriter::LineHeight: 11635 case PDFWriter::BaselineShift: 11636 // only for ILSE and BLSE 11637 if( eType == PDFWriter::Paragraph || 11638 eType == PDFWriter::Heading || 11639 eType == PDFWriter::H1 || 11640 eType == PDFWriter::H2 || 11641 eType == PDFWriter::H3 || 11642 eType == PDFWriter::H4 || 11643 eType == PDFWriter::H5 || 11644 eType == PDFWriter::H6 || 11645 eType == PDFWriter::List || 11646 eType == PDFWriter::ListItem || 11647 eType == PDFWriter::LILabel || 11648 eType == PDFWriter::LIBody || 11649 eType == PDFWriter::Table || 11650 eType == PDFWriter::TableRow || 11651 eType == PDFWriter::TableHeader || 11652 eType == PDFWriter::TableData || 11653 eType == PDFWriter::Span || 11654 eType == PDFWriter::Quote || 11655 eType == PDFWriter::Note || 11656 eType == PDFWriter::Reference || 11657 eType == PDFWriter::BibEntry || 11658 eType == PDFWriter::Code || 11659 eType == PDFWriter::Link ) 11660 { 11661 bInsert = true; 11662 } 11663 break; 11664 case PDFWriter::RowSpan: 11665 case PDFWriter::ColSpan: 11666 // only for table cells 11667 if( eType == PDFWriter::TableHeader || 11668 eType == PDFWriter::TableData ) 11669 { 11670 bInsert = true; 11671 } 11672 break; 11673 case PDFWriter::LinkAnnotation: 11674 if( eType == PDFWriter::Link ) 11675 bInsert = true; 11676 break; 11677 default: break; 11678 } 11679 } 11680 11681 if( bInsert ) 11682 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue ); 11683 #if OSL_DEBUG_LEVEL > 1 11684 else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11685 fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n", 11686 getAttributeTag( eAttr ), 11687 (int)nValue, 11688 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ), 11689 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() ); 11690 #endif 11691 11692 return bInsert; 11693 } 11694 11695 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect ) 11696 { 11697 sal_Int32 nPageNr = m_nCurrentPage; 11698 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged ) 11699 return; 11700 11701 11702 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11703 { 11704 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11705 if( eType == PDFWriter::Figure || 11706 eType == PDFWriter::Formula || 11707 eType == PDFWriter::Form || 11708 eType == PDFWriter::Table ) 11709 { 11710 m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect; 11711 // convert to default user space now, since the mapmode may change 11712 m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox ); 11713 } 11714 } 11715 } 11716 11717 void PDFWriterImpl::setActualText( const String& rText ) 11718 { 11719 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) 11720 { 11721 m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText; 11722 } 11723 } 11724 11725 void PDFWriterImpl::setAlternateText( const String& rText ) 11726 { 11727 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) 11728 { 11729 m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText; 11730 } 11731 } 11732 11733 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr ) 11734 { 11735 if( nPageNr < 0 ) 11736 nPageNr = m_nCurrentPage; 11737 11738 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11739 return; 11740 11741 m_aPages[ nPageNr ].m_nDuration = nSeconds; 11742 } 11743 11744 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr ) 11745 { 11746 if( nPageNr < 0 ) 11747 nPageNr = m_nCurrentPage; 11748 11749 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11750 return; 11751 11752 m_aPages[ nPageNr ].m_eTransition = eType; 11753 m_aPages[ nPageNr ].m_nTransTime = nMilliSec; 11754 } 11755 11756 void PDFWriterImpl::ensureUniqueRadioOnValues() 11757 { 11758 // loop over radio groups 11759 for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin(); 11760 group != m_aRadioGroupWidgets.end(); ++group ) 11761 { 11762 PDFWidget& rGroupWidget = m_aWidgets[ group->second ]; 11763 // check whether all kids have a unique OnValue 11764 std::hash_map< OUString, sal_Int32, OUStringHash > aOnValues; 11765 int nChildren = rGroupWidget.m_aKidsIndex.size(); 11766 bool bIsUnique = true; 11767 for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ ) 11768 { 11769 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11770 const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue; 11771 #if OSL_DEBUG_LEVEL > 1 11772 fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() ); 11773 #endif 11774 if( aOnValues.find( rVal ) == aOnValues.end() ) 11775 { 11776 aOnValues[ rVal ] = 1; 11777 } 11778 else 11779 { 11780 bIsUnique = false; 11781 } 11782 } 11783 if( ! bIsUnique ) 11784 { 11785 #if OSL_DEBUG_LEVEL > 1 11786 fprintf( stderr, "enforcing unique OnValues\n" ); 11787 #endif 11788 // make unique by using ascending OnValues 11789 for( int nKid = 0; nKid < nChildren; nKid++ ) 11790 { 11791 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11792 PDFWidget& rKid = m_aWidgets[nKidIndex]; 11793 rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) ); 11794 if( ! rKid.m_aValue.equalsAscii( "Off" ) ) 11795 rKid.m_aValue = rKid.m_aOnValue; 11796 } 11797 } 11798 // finally move the "Yes" appearance to the OnValue appearance 11799 for( int nKid = 0; nKid < nChildren; nKid++ ) 11800 { 11801 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11802 PDFWidget& rKid = m_aWidgets[nKidIndex]; 11803 PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" ); 11804 if( app_it != rKid.m_aAppearances.end() ) 11805 { 11806 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" ); 11807 if( stream_it != app_it->second.end() ) 11808 { 11809 SvMemoryStream* pStream = stream_it->second; 11810 app_it->second.erase( stream_it ); 11811 OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 ); 11812 appendName( rKid.m_aOnValue, aBuf ); 11813 (app_it->second)[ aBuf.makeStringAndClear() ] = pStream; 11814 } 11815 #if OSL_DEBUG_LEVEL > 1 11816 else 11817 fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" ); 11818 #endif 11819 } 11820 // update selected radio button 11821 if( ! rKid.m_aValue.equalsAscii( "Off" ) ) 11822 { 11823 rGroupWidget.m_aValue = rKid.m_aValue; 11824 } 11825 } 11826 } 11827 } 11828 11829 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn ) 11830 { 11831 sal_Int32 nRadioGroupWidget = -1; 11832 11833 std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup ); 11834 11835 if( it == m_aRadioGroupWidgets.end() ) 11836 { 11837 m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget = 11838 sal_Int32(m_aWidgets.size()); 11839 11840 // new group, insert the radiobutton 11841 m_aWidgets.push_back( PDFWidget() ); 11842 m_aWidgets.back().m_nObject = createObject(); 11843 m_aWidgets.back().m_nPage = m_nCurrentPage; 11844 m_aWidgets.back().m_eType = PDFWriter::RadioButton; 11845 m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup; 11846 m_aWidgets.back().m_nFlags |= 0x0000C000; // NoToggleToOff and Radio bits 11847 11848 createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn ); 11849 } 11850 else 11851 nRadioGroupWidget = it->second; 11852 11853 return nRadioGroupWidget; 11854 } 11855 11856 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr ) 11857 { 11858 if( nPageNr < 0 ) 11859 nPageNr = m_nCurrentPage; 11860 11861 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11862 return -1; 11863 11864 sal_Int32 nNewWidget = m_aWidgets.size(); 11865 m_aWidgets.push_back( PDFWidget() ); 11866 11867 m_aWidgets.back().m_nObject = createObject(); 11868 m_aWidgets.back().m_aRect = rControl.Location; 11869 m_aWidgets.back().m_nPage = nPageNr; 11870 m_aWidgets.back().m_eType = rControl.getType(); 11871 11872 sal_Int32 nRadioGroupWidget = -1; 11873 // for unknown reasons the radio buttons of a radio group must not have a 11874 // field name, else the buttons are in fact check boxes - 11875 // that is multiple buttons of the radio group can be selected 11876 if( rControl.getType() == PDFWriter::RadioButton ) 11877 nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) ); 11878 else 11879 { 11880 createWidgetFieldName( nNewWidget, rControl ); 11881 } 11882 11883 // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid 11884 PDFWidget& rNewWidget = m_aWidgets[nNewWidget]; 11885 rNewWidget.m_aDescription = rControl.Description; 11886 rNewWidget.m_aText = rControl.Text; 11887 rNewWidget.m_nTextStyle = rControl.TextStyle & 11888 ( TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP | 11889 TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM | 11890 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 11891 rNewWidget.m_nTabOrder = rControl.TabOrder; 11892 11893 // various properties are set via the flags (/Ff) property of the field dict 11894 if( rControl.ReadOnly ) 11895 rNewWidget.m_nFlags |= 1; 11896 if( rControl.getType() == PDFWriter::PushButton ) 11897 { 11898 const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl); 11899 if( rNewWidget.m_nTextStyle == 0 ) 11900 rNewWidget.m_nTextStyle = 11901 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | 11902 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 11903 11904 rNewWidget.m_nFlags |= 0x00010000; 11905 if( rBtn.URL.getLength() ) 11906 rNewWidget.m_aListEntries.push_back( rBtn.URL ); 11907 rNewWidget.m_bSubmit = rBtn.Submit; 11908 rNewWidget.m_bSubmitGet = rBtn.SubmitGet; 11909 rNewWidget.m_nDest = rBtn.Dest; 11910 createDefaultPushButtonAppearance( rNewWidget, rBtn ); 11911 } 11912 else if( rControl.getType() == PDFWriter::RadioButton ) 11913 { 11914 const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl); 11915 if( rNewWidget.m_nTextStyle == 0 ) 11916 rNewWidget.m_nTextStyle = 11917 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 11918 /* PDF sees a RadioButton group as one radio button with 11919 * children which are in turn check boxes 11920 * 11921 * so we need to create a radio button on demand for a new group 11922 * and insert a checkbox for each RadioButtonWidget as its child 11923 */ 11924 rNewWidget.m_eType = PDFWriter::CheckBox; 11925 rNewWidget.m_nRadioGroup = rBtn.RadioGroup; 11926 11927 DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" ); 11928 11929 PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget]; 11930 rRadioButton.m_aKids.push_back( rNewWidget.m_nObject ); 11931 rRadioButton.m_aKidsIndex.push_back( nNewWidget ); 11932 rNewWidget.m_nParent = rRadioButton.m_nObject; 11933 11934 rNewWidget.m_aValue = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) ); 11935 rNewWidget.m_aOnValue = rBtn.OnValue; 11936 if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected ) 11937 { 11938 rNewWidget.m_aValue = rNewWidget.m_aOnValue; 11939 rRadioButton.m_aValue = rNewWidget.m_aOnValue; 11940 } 11941 createDefaultRadioButtonAppearance( rNewWidget, rBtn ); 11942 11943 // union rect of radio group 11944 Rectangle aRect = rNewWidget.m_aRect; 11945 m_aPages[ nPageNr ].convertRect( aRect ); 11946 rRadioButton.m_aRect.Union( aRect ); 11947 } 11948 else if( rControl.getType() == PDFWriter::CheckBox ) 11949 { 11950 const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl); 11951 if( rNewWidget.m_nTextStyle == 0 ) 11952 rNewWidget.m_nTextStyle = 11953 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 11954 11955 rNewWidget.m_aValue = OUString::createFromAscii( rBox.Checked ? "Yes" : "Off" ); 11956 // create default appearance before m_aRect gets transformed 11957 createDefaultCheckBoxAppearance( rNewWidget, rBox ); 11958 } 11959 else if( rControl.getType() == PDFWriter::ListBox ) 11960 { 11961 if( rNewWidget.m_nTextStyle == 0 ) 11962 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER; 11963 11964 const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl); 11965 rNewWidget.m_aListEntries = rLstBox.Entries; 11966 rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries; 11967 rNewWidget.m_aValue = rLstBox.Text; 11968 if( rLstBox.DropDown ) 11969 rNewWidget.m_nFlags |= 0x00020000; 11970 if( rLstBox.Sort ) 11971 rNewWidget.m_nFlags |= 0x00080000; 11972 if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 ) 11973 rNewWidget.m_nFlags |= 0x00200000; 11974 11975 createDefaultListBoxAppearance( rNewWidget, rLstBox ); 11976 } 11977 else if( rControl.getType() == PDFWriter::ComboBox ) 11978 { 11979 if( rNewWidget.m_nTextStyle == 0 ) 11980 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER; 11981 11982 const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl); 11983 rNewWidget.m_aValue = rBox.Text; 11984 rNewWidget.m_aListEntries = rBox.Entries; 11985 rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag 11986 if( rBox.Sort ) 11987 rNewWidget.m_nFlags |= 0x00080000; 11988 11989 PDFWriter::ListBoxWidget aLBox; 11990 aLBox.Name = rBox.Name; 11991 aLBox.Description = rBox.Description; 11992 aLBox.Text = rBox.Text; 11993 aLBox.TextStyle = rBox.TextStyle; 11994 aLBox.ReadOnly = rBox.ReadOnly; 11995 aLBox.Border = rBox.Border; 11996 aLBox.BorderColor = rBox.BorderColor; 11997 aLBox.Background = rBox.Background; 11998 aLBox.BackgroundColor = rBox.BackgroundColor; 11999 aLBox.TextFont = rBox.TextFont; 12000 aLBox.TextColor = rBox.TextColor; 12001 aLBox.DropDown = true; 12002 aLBox.Sort = rBox.Sort; 12003 aLBox.MultiSelect = false; 12004 aLBox.Entries = rBox.Entries; 12005 12006 createDefaultListBoxAppearance( rNewWidget, aLBox ); 12007 } 12008 else if( rControl.getType() == PDFWriter::Edit ) 12009 { 12010 if( rNewWidget.m_nTextStyle == 0 ) 12011 rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER; 12012 12013 const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl); 12014 if( rEdit.MultiLine ) 12015 { 12016 rNewWidget.m_nFlags |= 0x00001000; 12017 rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 12018 } 12019 if( rEdit.Password ) 12020 rNewWidget.m_nFlags |= 0x00002000; 12021 if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 ) 12022 rNewWidget.m_nFlags |= 0x00100000; 12023 rNewWidget.m_nMaxLen = rEdit.MaxLen; 12024 rNewWidget.m_aValue = rEdit.Text; 12025 12026 createDefaultEditAppearance( rNewWidget, rEdit ); 12027 } 12028 12029 // convert to default user space now, since the mapmode may change 12030 // note: create default appearances before m_aRect gets transformed 12031 m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect ); 12032 12033 // insert widget to page's annotation list 12034 m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject ); 12035 12036 // mark page as having widgets 12037 m_aPages[ nPageNr ].m_bHasWidgets = true; 12038 12039 return nNewWidget; 12040 } 12041 12042 void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl ) 12043 { 12044 if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() ) 12045 return; 12046 12047 PDFWidget& rWidget = m_aWidgets[ nControl ]; 12048 m_nCurrentControl = nControl; 12049 12050 SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 ); 12051 // back conversion of control rect to current MapMode; necessary because 12052 // MapMode between createControl and beginControlAppearance 12053 // could have changed; therefore the widget rectangle is 12054 // already converted 12055 Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ), 12056 rWidget.m_aRect.GetSize() ); 12057 aBack = lcl_convert( m_aMapMode, 12058 m_aGraphicsStack.front().m_aMapMode, 12059 getReferenceDevice(), 12060 aBack ); 12061 beginRedirect( pControlStream, aBack ); 12062 writeBuffer( "/Tx BMC\n", 8 ); 12063 } 12064 12065 bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState ) 12066 { 12067 bool bRet = false; 12068 if( ! m_aOutputStreams.empty() ) 12069 writeBuffer( "\nEMC\n", 5 ); 12070 SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect()); 12071 if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() ) 12072 { 12073 PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ]; 12074 OString aState, aStyle; 12075 switch( rWidget.m_eType ) 12076 { 12077 case PDFWriter::PushButton: 12078 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12079 { 12080 aState = (eState == PDFWriter::Up) ? "N" : "D"; 12081 aStyle = "Standard"; 12082 } 12083 break; 12084 case PDFWriter::CheckBox: 12085 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12086 { 12087 aState = "N"; 12088 aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes"; 12089 /* cf PDFReference 3rd ed. V1.4 p539: 12090 recommended name for on state is "Yes", 12091 recommended name for off state is "Off" 12092 */ 12093 } 12094 break; 12095 case PDFWriter::RadioButton: 12096 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12097 { 12098 aState = "N"; 12099 if( eState == PDFWriter::Up ) 12100 aStyle = "Off"; 12101 else 12102 { 12103 OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 ); 12104 appendName( rWidget.m_aOnValue, aBuf ); 12105 aStyle = aBuf.makeStringAndClear(); 12106 } 12107 } 12108 break; 12109 case PDFWriter::Edit: 12110 aState = "N"; 12111 aStyle = "Standard"; 12112 break; 12113 case PDFWriter::ListBox: 12114 case PDFWriter::ComboBox: 12115 case PDFWriter::Hierarchy: 12116 break; 12117 } 12118 if( aState.getLength() && aStyle.getLength() ) 12119 { 12120 // delete eventual existing stream 12121 PDFAppearanceStreams::iterator it = 12122 rWidget.m_aAppearances[ aState ].find( aStyle ); 12123 if( it != rWidget.m_aAppearances[ aState ].end() ) 12124 delete it->second; 12125 rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance; 12126 bRet = true; 12127 } 12128 } 12129 12130 if( ! bRet ) 12131 delete pAppearance; 12132 12133 m_nCurrentControl = -1; 12134 12135 return bRet; 12136 } 12137 12138 void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress ) 12139 { 12140 if( pStream ) 12141 { 12142 m_aAdditionalStreams.push_back( PDFAddStream() ); 12143 PDFAddStream& rStream = m_aAdditionalStreams.back(); 12144 rStream.m_aMimeType = rMimeType.Len() 12145 ? OUString( rMimeType ) 12146 : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) ); 12147 rStream.m_pStream = pStream; 12148 rStream.m_bCompress = bCompress; 12149 } 12150 } 12151 12152 12153 12154