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 if( *it >= nIndex ) 3379 { 3380 // special case: nIndex inside a section marker 3381 if( nIndex >= (*it) && (*it)+5 > nIndex ) 3382 nLength3 -= (*it)+5 - nIndex; 3383 else 3384 { 3385 if( *it < nFontLen - 6 ) 3386 nLength3 -= 6; 3387 else // the last section 0x8003 is only 2 bytes after all 3388 nLength3 -= (nFontLen - *it); 3389 } 3390 } 3391 } 3392 3393 // there may be whitespace to ignore before the 512 '0' 3394 while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' ) 3395 { 3396 nIndex--; 3397 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) 3398 ; 3399 if( it != aSections.end() ) 3400 { 3401 nIndex = (*it)-1; 3402 break; // this is surely a binary boundary, in ascii case it wouldn't matter 3403 } 3404 } 3405 nEndBinaryIndex = nIndex; 3406 3407 // search for beginning of binary section 3408 nBeginBinaryIndex = nEndAsciiIndex; 3409 do 3410 { 3411 nBeginBinaryIndex++; 3412 for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it ) 3413 ; 3414 } while( nBeginBinaryIndex < nEndBinaryIndex && 3415 ( pFontData[nBeginBinaryIndex] == '\r' || 3416 pFontData[nBeginBinaryIndex] == '\n' || 3417 it != aSections.end() ) ); 3418 3419 // it seems to be vital to copy the exact whitespace between binary data 3420 // and eexec, else a invalid font results. so make nEndAsciiIndex 3421 // always immediate in front of nBeginBinaryIndex 3422 nEndAsciiIndex = nBeginBinaryIndex-1; 3423 for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it ) 3424 ; 3425 if( it != aSections.end() ) 3426 nEndAsciiIndex = (*it)-1; 3427 3428 nLength1 = nEndAsciiIndex+1; // including the last character 3429 for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it ) 3430 nLength1 -= 6; // decrease by pfb section size 3431 3432 // if the first four bytes are all ascii hex characters, then binary data 3433 // has to be converted to real binary data 3434 for( nIndex = 0; nIndex < 4 && 3435 ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) || 3436 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) || 3437 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' ) 3438 ); ++nIndex ) 3439 ; 3440 bool bConvertHexData = true; 3441 if( nIndex < 4 ) 3442 { 3443 bConvertHexData = false; 3444 nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte 3445 for( it = aSections.begin(); it != aSections.end(); ++it ) 3446 if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex ) 3447 nLength2 -= 6; 3448 } 3449 else 3450 { 3451 // count the hex ascii characters to get nLength2 3452 nLength2 = 0; 3453 int nNextSectionIndex = 0; 3454 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) 3455 ; 3456 if( it != aSections.end() ) 3457 nNextSectionIndex = *it; 3458 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) 3459 { 3460 if( nIndex == nNextSectionIndex ) 3461 { 3462 nIndex += 6; 3463 ++it; 3464 nNextSectionIndex = (it == aSections.end() ? 0 : *it ); 3465 } 3466 if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) || 3467 ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) || 3468 ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) ) 3469 nLength2++; 3470 } 3471 DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" ); 3472 nLength2 /= 2; 3473 } 3474 3475 // now we can actually write the font stream ! 3476 #if OSL_DEBUG_LEVEL > 1 3477 emitComment( " PDFWriterImpl::emitEmbeddedFont" ); 3478 #endif 3479 OStringBuffer aLine( 512 ); 3480 nStreamObject = createObject(); 3481 if( !updateObject(nStreamObject)) 3482 throw FontException(); 3483 sal_Int32 nStreamLengthObject = createObject(); 3484 aLine.append( nStreamObject ); 3485 aLine.append( " 0 obj\n" 3486 "<</Length " ); 3487 aLine.append( nStreamLengthObject ); 3488 aLine.append( " 0 R" 3489 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3490 "/Filter/FlateDecode" 3491 #endif 3492 "/Length1 " ); 3493 aLine.append( nLength1 ); 3494 aLine.append( " /Length2 " ); 3495 aLine.append( nLength2 ); 3496 aLine.append( " /Length3 "); 3497 aLine.append( nLength3 ); 3498 aLine.append( ">>\n" 3499 "stream\n" ); 3500 if( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3501 throw FontException(); 3502 3503 sal_uInt64 nBeginStreamPos = 0; 3504 osl_getFilePos( m_aFile, &nBeginStreamPos ); 3505 3506 beginCompression(); 3507 checkAndEnableStreamEncryption( nStreamObject ); 3508 3509 // write ascii section 3510 if( aSections.begin() == aSections.end() ) 3511 { 3512 if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) ) 3513 throw FontException(); 3514 } 3515 else 3516 { 3517 // first section always starts at 0 3518 it = aSections.begin(); 3519 nIndex = (*it)+6; 3520 ++it; 3521 while( *it < nEndAsciiIndex ) 3522 { 3523 if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) ) 3524 throw FontException(); 3525 nIndex = (*it)+6; 3526 ++it; 3527 } 3528 // write partial last section 3529 if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) ) 3530 throw FontException(); 3531 } 3532 3533 // write binary section 3534 if( ! bConvertHexData ) 3535 { 3536 if( aSections.begin() == aSections.end() ) 3537 { 3538 if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) ) 3539 throw FontException(); 3540 } 3541 else 3542 { 3543 for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it ) 3544 ; 3545 // write first partial section 3546 if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) ) 3547 throw FontException(); 3548 // write following sections 3549 while( it != aSections.end() ) 3550 { 3551 nIndex = (*it)+6; 3552 ++it; 3553 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes 3554 { 3555 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; 3556 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) 3557 throw FontException(); 3558 } 3559 } 3560 } 3561 } 3562 else 3563 { 3564 boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] ); 3565 rtl_zeroMemory( pWriteBuffer.get(), nLength2 ); 3566 int nWriteIndex = 0; 3567 3568 int nNextSectionIndex = 0; 3569 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) 3570 ; 3571 if( it != aSections.end() ) 3572 nNextSectionIndex = *it; 3573 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) 3574 { 3575 if( nIndex == nNextSectionIndex ) 3576 { 3577 nIndex += 6; 3578 ++it; 3579 nNextSectionIndex = (it == aSections.end() ? nFontLen : *it ); 3580 } 3581 unsigned char cNibble = 0x80; 3582 if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) 3583 cNibble = pFontData[nIndex] - '0'; 3584 else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) 3585 cNibble = pFontData[nIndex] - 'a' + 10; 3586 else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) 3587 cNibble = pFontData[nIndex] - 'A' + 10; 3588 if( cNibble != 0x80 ) 3589 { 3590 if( !(nWriteIndex & 1 ) ) 3591 cNibble <<= 4; 3592 pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble; 3593 nWriteIndex++; 3594 } 3595 } 3596 if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) ) 3597 throw FontException(); 3598 if( aSections.empty() ) 3599 { 3600 if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) ) 3601 throw FontException(); 3602 } 3603 else 3604 { 3605 // write rest of this section 3606 if( nIndex < nNextSectionIndex ) 3607 { 3608 if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) ) 3609 throw FontException(); 3610 } 3611 // write following sections 3612 while( it != aSections.end() ) 3613 { 3614 nIndex = (*it)+6; 3615 ++it; 3616 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes 3617 { 3618 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; 3619 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) 3620 throw FontException(); 3621 } 3622 } 3623 } 3624 } 3625 endCompression(); 3626 disableStreamEncryption(); 3627 3628 3629 sal_uInt64 nEndStreamPos = 0; 3630 osl_getFilePos( m_aFile, &nEndStreamPos ); 3631 3632 // and finally close the stream 3633 aLine.setLength( 0 ); 3634 aLine.append( "\nendstream\nendobj\n\n" ); 3635 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3636 throw FontException(); 3637 3638 // write stream length object 3639 aLine.setLength( 0 ); 3640 if( ! updateObject( nStreamLengthObject ) ) 3641 throw FontException(); 3642 aLine.append( nStreamLengthObject ); 3643 aLine.append( " 0 obj\n" ); 3644 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) ); 3645 aLine.append( "\nendobj\n\n" ); 3646 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3647 throw FontException(); 3648 } 3649 else 3650 { 3651 rtl::OStringBuffer aErrorComment( 256 ); 3652 aErrorComment.append( "GetEmbedFontData failed for font \"" ); 3653 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); 3654 aErrorComment.append( '\"' ); 3655 if( pFont->GetSlant() == ITALIC_NORMAL ) 3656 aErrorComment.append( " italic" ); 3657 else if( pFont->GetSlant() == ITALIC_OBLIQUE ) 3658 aErrorComment.append( " oblique" ); 3659 aErrorComment.append( " weight=" ); 3660 aErrorComment.append( sal_Int32(pFont->GetWeight()) ); 3661 emitComment( aErrorComment.getStr() ); 3662 } 3663 3664 if( nStreamObject ) 3665 // write font descriptor 3666 nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject ); 3667 3668 if( nFontDescriptor ) 3669 { 3670 if( pEncoding ) 3671 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, sizeof(nEncoding)/sizeof(nEncoding[0]) ); 3672 3673 // write font object 3674 sal_Int32 nObject = createObject(); 3675 if( ! updateObject( nObject ) ) 3676 throw FontException(); 3677 3678 OStringBuffer aLine( 1024 ); 3679 aLine.append( nObject ); 3680 aLine.append( " 0 obj\n" 3681 "<</Type/Font/Subtype/Type1/BaseFont/" ); 3682 appendName( aInfo.m_aPSName, aLine ); 3683 aLine.append( "\n" ); 3684 if( !pFont->mbSymbolFlag && pEncoding == 0 ) 3685 aLine.append( "/Encoding/WinAnsiEncoding\n" ); 3686 if( nToUnicodeStream ) 3687 { 3688 aLine.append( "/ToUnicode " ); 3689 aLine.append( nToUnicodeStream ); 3690 aLine.append( " 0 R\n" ); 3691 } 3692 aLine.append( "/FirstChar 0 /LastChar 255\n" 3693 "/Widths[" ); 3694 for( int i = 0; i < 256; i++ ) 3695 { 3696 aLine.append( pWidths[i] ); 3697 aLine.append( ((i&15) == 15) ? "\n" : " " ); 3698 } 3699 aLine.append( "]\n" 3700 "/FontDescriptor " ); 3701 aLine.append( nFontDescriptor ); 3702 aLine.append( " 0 R>>\n" 3703 "endobj\n\n" ); 3704 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3705 throw FontException(); 3706 3707 nFontObject = nObject; 3708 3709 aRet[ rEmbed.m_nNormalFontID ] = nObject; 3710 3711 // write additional encodings 3712 for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it ) 3713 { 3714 sal_Int32 aEncWidths[ 256 ]; 3715 // emit encoding dict 3716 sal_Int32 nEncObject = createObject(); 3717 if( ! updateObject( nEncObject ) ) 3718 throw FontException(); 3719 3720 OutputDevice* pRef = getReferenceDevice(); 3721 pRef->Push( PUSH_FONT | PUSH_MAPMODE ); 3722 pRef->SetMapMode( MapMode( MAP_PIXEL ) ); 3723 Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) ); 3724 aFont.SetWeight( pFont->GetWeight() ); 3725 aFont.SetItalic( pFont->GetSlant() ); 3726 aFont.SetPitch( pFont->GetPitch() ); 3727 pRef->SetFont( aFont ); 3728 pRef->ImplNewFont(); 3729 3730 aLine.setLength( 0 ); 3731 aLine.append( nEncObject ); 3732 aLine.append( " 0 obj\n" 3733 "<</Type/Encoding/Differences[ 0\n" ); 3734 int nEncoded = 0; 3735 aUnicodes.clear(); 3736 for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it ) 3737 { 3738 String aStr( str_it->m_aUnicode ); 3739 aEncWidths[nEncoded] = pRef->GetTextWidth( aStr ); 3740 nEncodedCodes[nEncoded] = str_it->m_aUnicode; 3741 nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded); 3742 pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size()); 3743 aUnicodes.push_back( nEncodedCodes[nEncoded] ); 3744 pUnicodesPerGlyph[nEncoded] = 1; 3745 3746 aLine.append( " /" ); 3747 aLine.append( str_it->m_aName ); 3748 if( !((++nEncoded) & 15) ) 3749 aLine.append( "\n" ); 3750 } 3751 aLine.append( "]>>\n" 3752 "endobj\n\n" ); 3753 3754 pRef->Pop(); 3755 3756 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3757 throw FontException(); 3758 3759 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded ); 3760 3761 nObject = createObject(); 3762 if( ! updateObject( nObject ) ) 3763 throw FontException(); 3764 3765 aLine.setLength( 0 ); 3766 aLine.append( nObject ); 3767 aLine.append( " 0 obj\n" 3768 "<</Type/Font/Subtype/Type1/BaseFont/" ); 3769 appendName( aInfo.m_aPSName, aLine ); 3770 aLine.append( "\n" ); 3771 aLine.append( "/Encoding " ); 3772 aLine.append( nEncObject ); 3773 aLine.append( " 0 R\n" ); 3774 if( nToUnicodeStream ) 3775 { 3776 aLine.append( "/ToUnicode " ); 3777 aLine.append( nToUnicodeStream ); 3778 aLine.append( " 0 R\n" ); 3779 } 3780 aLine.append( "/FirstChar 0\n" 3781 "/LastChar " ); 3782 aLine.append( (sal_Int32)(nEncoded-1) ); 3783 aLine.append( "\n" 3784 "/Widths[" ); 3785 for( int i = 0; i < nEncoded; i++ ) 3786 { 3787 aLine.append( aEncWidths[i] ); 3788 aLine.append( ((i&15) == 15) ? "\n" : " " ); 3789 } 3790 aLine.append( " ]\n" 3791 "/FontDescriptor " ); 3792 aLine.append( nFontDescriptor ); 3793 aLine.append( " 0 R>>\n" 3794 "endobj\n\n" ); 3795 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3796 throw FontException(); 3797 3798 aRet[ enc_it->m_nFontID ] = nObject; 3799 } 3800 } 3801 } 3802 catch( FontException& ) 3803 { 3804 // these do nothing in case there was no compression or encryption ongoing 3805 endCompression(); 3806 disableStreamEncryption(); 3807 } 3808 3809 if( pFontData ) 3810 m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen ); 3811 3812 return aRet; 3813 } 3814 3815 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer ) 3816 { 3817 if( nSubsetID ) 3818 { 3819 for( int i = 0; i < 6; i++ ) 3820 { 3821 int nOffset = (nSubsetID % 26); 3822 nSubsetID /= 26; 3823 rBuffer.append( (sal_Char)('A'+nOffset) ); 3824 } 3825 rBuffer.append( '+' ); 3826 } 3827 appendName( rPSName, rBuffer ); 3828 } 3829 3830 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, 3831 sal_Ucs* pUnicodes, 3832 sal_Int32* pUnicodesPerGlyph, 3833 sal_Int32* pEncToUnicodeIndex, 3834 int nGlyphs ) 3835 { 3836 int nMapped = 0, n = 0; 3837 for( n = 0; n < nGlyphs; n++ ) 3838 if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] ) 3839 nMapped++; 3840 3841 if( nMapped == 0 ) 3842 return 0; 3843 3844 sal_Int32 nStream = createObject(); 3845 CHECK_RETURN( updateObject( nStream ) ); 3846 3847 OStringBuffer aContents( 1024 ); 3848 aContents.append( 3849 "/CIDInit/ProcSet findresource begin\n" 3850 "12 dict begin\n" 3851 "begincmap\n" 3852 "/CIDSystemInfo<<\n" 3853 "/Registry (Adobe)\n" 3854 "/Ordering (UCS)\n" 3855 "/Supplement 0\n" 3856 ">> def\n" 3857 "/CMapName/Adobe-Identity-UCS def\n" 3858 "/CMapType 2 def\n" 3859 "1 begincodespacerange\n" 3860 "<00> <FF>\n" 3861 "endcodespacerange\n" 3862 ); 3863 int nCount = 0; 3864 for( n = 0; n < nGlyphs; n++ ) 3865 { 3866 if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] ) 3867 { 3868 if( (nCount % 100) == 0 ) 3869 { 3870 if( nCount ) 3871 aContents.append( "endbfchar\n" ); 3872 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) ); 3873 aContents.append( " beginbfchar\n" ); 3874 } 3875 aContents.append( '<' ); 3876 appendHex( (sal_Int8)pEncoding[n], aContents ); 3877 aContents.append( "> <" ); 3878 // TODO: handle unicodes>U+FFFF 3879 sal_Int32 nIndex = pEncToUnicodeIndex[n]; 3880 for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ ) 3881 { 3882 appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents ); 3883 appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents ); 3884 } 3885 aContents.append( ">\n" ); 3886 nCount++; 3887 } 3888 } 3889 aContents.append( "endbfchar\n" 3890 "endcmap\n" 3891 "CMapName currentdict /CMap defineresource pop\n" 3892 "end\n" 3893 "end\n" ); 3894 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3895 ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 ); 3896 SvMemoryStream aStream; 3897 pCodec->BeginCompression(); 3898 pCodec->Write( aStream, (const sal_uInt8*)aContents.getStr(), aContents.getLength() ); 3899 pCodec->EndCompression(); 3900 delete pCodec; 3901 #endif 3902 3903 #if OSL_DEBUG_LEVEL > 1 3904 emitComment( "PDFWriterImpl::createToUnicodeCMap" ); 3905 #endif 3906 OStringBuffer aLine( 40 ); 3907 3908 aLine.append( nStream ); 3909 aLine.append( " 0 obj\n<</Length " ); 3910 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3911 sal_Int32 nLen = (sal_Int32)aStream.Tell(); 3912 aStream.Seek( 0 ); 3913 aLine.append( nLen ); 3914 aLine.append( "/Filter/FlateDecode" ); 3915 #else 3916 aLine.append( aContents.getLength() ); 3917 #endif 3918 aLine.append( ">>\nstream\n" ); 3919 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3920 checkAndEnableStreamEncryption( nStream ); 3921 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3922 CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) ); 3923 #else 3924 CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) ); 3925 #endif 3926 disableStreamEncryption(); 3927 aLine.setLength( 0 ); 3928 aLine.append( "\nendstream\n" 3929 "endobj\n\n" ); 3930 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3931 return nStream; 3932 } 3933 3934 sal_Int32 PDFWriterImpl::emitFontDescriptor( const ImplFontData* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream ) 3935 { 3936 OStringBuffer aLine( 1024 ); 3937 // get font flags, see PDF reference 1.4 p. 358 3938 // possibly characters outside Adobe standard encoding 3939 // so set Symbolic flag 3940 sal_Int32 nFontFlags = (1<<2); 3941 if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE ) 3942 nFontFlags |= (1 << 6); 3943 if( pFont->GetPitch() == PITCH_FIXED ) 3944 nFontFlags |= 1; 3945 if( pFont->GetFamilyType() == FAMILY_SCRIPT ) 3946 nFontFlags |= (1 << 3); 3947 else if( pFont->GetFamilyType() == FAMILY_ROMAN ) 3948 nFontFlags |= (1 << 1); 3949 3950 sal_Int32 nFontDescriptor = createObject(); 3951 CHECK_RETURN( updateObject( nFontDescriptor ) ); 3952 aLine.setLength( 0 ); 3953 aLine.append( nFontDescriptor ); 3954 aLine.append( " 0 obj\n" 3955 "<</Type/FontDescriptor/FontName/" ); 3956 appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine ); 3957 aLine.append( "\n" 3958 "/Flags " ); 3959 aLine.append( nFontFlags ); 3960 aLine.append( "\n" 3961 "/FontBBox[" ); 3962 // note: Top and Bottom are reversed in VCL and PDF rectangles 3963 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() ); 3964 aLine.append( ' ' ); 3965 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() ); 3966 aLine.append( ' ' ); 3967 aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() ); 3968 aLine.append( ' ' ); 3969 aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) ); 3970 aLine.append( "]/ItalicAngle " ); 3971 if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL ) 3972 aLine.append( "-30" ); 3973 else 3974 aLine.append( "0" ); 3975 aLine.append( "\n" 3976 "/Ascent " ); 3977 aLine.append( (sal_Int32)rInfo.m_nAscent ); 3978 aLine.append( "\n" 3979 "/Descent " ); 3980 aLine.append( (sal_Int32)-rInfo.m_nDescent ); 3981 aLine.append( "\n" 3982 "/CapHeight " ); 3983 aLine.append( (sal_Int32)rInfo.m_nCapHeight ); 3984 // According to PDF reference 1.4 StemV is required 3985 // seems a tad strange to me, but well ... 3986 aLine.append( "\n" 3987 "/StemV 80\n" ); 3988 if( nFontStream ) 3989 { 3990 aLine.append( "/FontFile" ); 3991 switch( rInfo.m_nFontType ) 3992 { 3993 case FontSubsetInfo::SFNT_TTF: 3994 aLine.append( '2' ); 3995 break; 3996 case FontSubsetInfo::TYPE1_PFA: 3997 case FontSubsetInfo::TYPE1_PFB: 3998 case FontSubsetInfo::ANY_TYPE1: 3999 break; 4000 default: 4001 DBG_ERROR( "unknown fonttype in PDF font descriptor" ); 4002 return 0; 4003 } 4004 aLine.append( ' ' ); 4005 aLine.append( nFontStream ); 4006 aLine.append( " 0 R\n" ); 4007 } 4008 aLine.append( ">>\n" 4009 "endobj\n\n" ); 4010 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4011 4012 return nFontDescriptor; 4013 } 4014 4015 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const 4016 { 4017 for( std::map< sal_Int32, sal_Int32 >::const_iterator it = 4018 m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it ) 4019 { 4020 rDict.append( m_aBuiltinFonts[it->first].getNameObject() ); 4021 rDict.append( ' ' ); 4022 rDict.append( it->second ); 4023 rDict.append( " 0 R" ); 4024 } 4025 } 4026 4027 bool PDFWriterImpl::emitFonts() 4028 { 4029 if( ! m_pReferenceDevice->ImplGetGraphics() ) 4030 return false; 4031 4032 OStringBuffer aLine( 1024 ); 4033 4034 std::map< sal_Int32, sal_Int32 > aFontIDToObject; 4035 4036 OUString aTmpName; 4037 osl_createTempFile( NULL, NULL, &aTmpName.pData ); 4038 for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it ) 4039 { 4040 for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit ) 4041 { 4042 sal_Int32 pGlyphIDs[ 256 ]; 4043 sal_Int32 pWidths[ 256 ]; 4044 sal_uInt8 pEncoding[ 256 ]; 4045 sal_Int32 pEncToUnicodeIndex[ 256 ]; 4046 sal_Int32 pUnicodesPerGlyph[ 256 ]; 4047 std::vector<sal_Ucs> aUnicodes; 4048 aUnicodes.reserve( 256 ); 4049 int nGlyphs = 1; 4050 // fill arrays and prepare encoding index map 4051 sal_Int32 nToUnicodeStream = 0; 4052 4053 rtl_zeroMemory( pGlyphIDs, sizeof( pGlyphIDs ) ); 4054 rtl_zeroMemory( pEncoding, sizeof( pEncoding ) ); 4055 rtl_zeroMemory( pUnicodesPerGlyph, sizeof( pUnicodesPerGlyph ) ); 4056 rtl_zeroMemory( pEncToUnicodeIndex, sizeof( pEncToUnicodeIndex ) ); 4057 for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit ) 4058 { 4059 sal_uInt8 nEnc = fit->second.getGlyphId(); 4060 4061 DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" ); 4062 DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" ); 4063 4064 pGlyphIDs[ nEnc ] = fit->first; 4065 pEncoding[ nEnc ] = nEnc; 4066 pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size()); 4067 pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes(); 4068 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ ) 4069 aUnicodes.push_back( fit->second.getCode( n ) ); 4070 if( fit->second.getCode(0) ) 4071 nToUnicodeStream = 1; 4072 if( nGlyphs < 256 ) 4073 nGlyphs++; 4074 else 4075 { 4076 DBG_ERROR( "too many glyphs for subset" ); 4077 } 4078 } 4079 FontSubsetInfo aSubsetInfo; 4080 if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) ) 4081 { 4082 // create font stream 4083 oslFileHandle aFontFile; 4084 CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) ); 4085 // get file size 4086 sal_uInt64 nLength1; 4087 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) ); 4088 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength1 ) ) ); 4089 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); 4090 4091 #if OSL_DEBUG_LEVEL > 1 4092 emitComment( "PDFWriterImpl::emitFonts" ); 4093 #endif 4094 sal_Int32 nFontStream = createObject(); 4095 sal_Int32 nStreamLengthObject = createObject(); 4096 CHECK_RETURN( updateObject( nFontStream ) ); 4097 aLine.setLength( 0 ); 4098 aLine.append( nFontStream ); 4099 aLine.append( " 0 obj\n" 4100 "<</Length " ); 4101 aLine.append( (sal_Int32)nStreamLengthObject ); 4102 aLine.append( " 0 R" 4103 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 4104 "/Filter/FlateDecode" 4105 #endif 4106 "/Length1 " ); 4107 4108 sal_uInt64 nStartPos = 0; 4109 if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF ) 4110 { 4111 aLine.append( (sal_Int32)nLength1 ); 4112 4113 aLine.append( ">>\n" 4114 "stream\n" ); 4115 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4116 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); 4117 4118 // copy font file 4119 beginCompression(); 4120 checkAndEnableStreamEncryption( nFontStream ); 4121 sal_Bool bEOF = sal_False; 4122 do 4123 { 4124 char buf[8192]; 4125 sal_uInt64 nRead; 4126 CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) ); 4127 CHECK_RETURN( writeBuffer( buf, nRead ) ); 4128 CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) ); 4129 } while( ! bEOF ); 4130 } 4131 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 ) 4132 { 4133 // TODO: implement 4134 DBG_ERROR( "PDFWriterImpl does not support CFF-font subsets yet!" ); 4135 } 4136 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA? 4137 { 4138 boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] ); 4139 4140 sal_uInt64 nBytesRead = 0; 4141 CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer.get(), nLength1, &nBytesRead ) ) ); 4142 DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" ); 4143 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); 4144 // get the PFB-segment lengths 4145 ThreeInts aSegmentLengths = {0,0,0}; 4146 getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths ); 4147 // the lengths below are mandatory for PDF-exported Type1 fonts 4148 // because the PFB segment headers get stripped! WhyOhWhy. 4149 aLine.append( (sal_Int32)aSegmentLengths[0] ); 4150 aLine.append( "/Length2 " ); 4151 aLine.append( (sal_Int32)aSegmentLengths[1] ); 4152 aLine.append( "/Length3 " ); 4153 aLine.append( (sal_Int32)aSegmentLengths[2] ); 4154 4155 aLine.append( ">>\n" 4156 "stream\n" ); 4157 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4158 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); 4159 4160 // emit PFB-sections without section headers 4161 beginCompression(); 4162 checkAndEnableStreamEncryption( nFontStream ); 4163 CHECK_RETURN( writeBuffer( &pBuffer[6], aSegmentLengths[0] ) ); 4164 CHECK_RETURN( writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) ); 4165 CHECK_RETURN( writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ); 4166 } 4167 else 4168 { 4169 fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType); 4170 aLine.append( "0 >>\nstream\n" ); 4171 } 4172 4173 endCompression(); 4174 disableStreamEncryption(); 4175 // close the file 4176 osl_closeFile( aFontFile ); 4177 4178 sal_uInt64 nEndPos = 0; 4179 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) ); 4180 // end the stream 4181 aLine.setLength( 0 ); 4182 aLine.append( "\nendstream\nendobj\n\n" ); 4183 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4184 4185 // emit stream length object 4186 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 4187 aLine.setLength( 0 ); 4188 aLine.append( nStreamLengthObject ); 4189 aLine.append( " 0 obj\n" ); 4190 aLine.append( (sal_Int64)(nEndPos-nStartPos) ); 4191 aLine.append( "\nendobj\n\n" ); 4192 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4193 4194 // write font descriptor 4195 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream ); 4196 4197 if( nToUnicodeStream ) 4198 nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs ); 4199 4200 sal_Int32 nFontObject = createObject(); 4201 CHECK_RETURN( updateObject( nFontObject ) ); 4202 aLine.setLength( 0 ); 4203 aLine.append( nFontObject ); 4204 4205 aLine.append( " 0 obj\n" ); 4206 aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ? 4207 "<</Type/Font/Subtype/Type1/BaseFont/" : 4208 "<</Type/Font/Subtype/TrueType/BaseFont/" ); 4209 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine ); 4210 aLine.append( "\n" 4211 "/FirstChar 0\n" 4212 "/LastChar " ); 4213 aLine.append( (sal_Int32)(nGlyphs-1) ); 4214 aLine.append( "\n" 4215 "/Widths[" ); 4216 for( int i = 0; i < nGlyphs; i++ ) 4217 { 4218 aLine.append( pWidths[ i ] ); 4219 aLine.append( ((i & 15) == 15) ? "\n" : " " ); 4220 } 4221 aLine.append( "]\n" 4222 "/FontDescriptor " ); 4223 aLine.append( nFontDescriptor ); 4224 aLine.append( " 0 R\n" ); 4225 if( nToUnicodeStream ) 4226 { 4227 aLine.append( "/ToUnicode " ); 4228 aLine.append( nToUnicodeStream ); 4229 aLine.append( " 0 R\n" ); 4230 } 4231 aLine.append( ">>\n" 4232 "endobj\n\n" ); 4233 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4234 4235 aFontIDToObject[ lit->m_nFontID ] = nFontObject; 4236 } 4237 else 4238 { 4239 const ImplFontData* pFont = it->first; 4240 rtl::OStringBuffer aErrorComment( 256 ); 4241 aErrorComment.append( "CreateFontSubset failed for font \"" ); 4242 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); 4243 aErrorComment.append( '\"' ); 4244 if( pFont->GetSlant() == ITALIC_NORMAL ) 4245 aErrorComment.append( " italic" ); 4246 else if( pFont->GetSlant() == ITALIC_OBLIQUE ) 4247 aErrorComment.append( " oblique" ); 4248 aErrorComment.append( " weight=" ); 4249 aErrorComment.append( sal_Int32(pFont->GetWeight()) ); 4250 emitComment( aErrorComment.getStr() ); 4251 } 4252 } 4253 } 4254 osl_removeFile( aTmpName.pData ); 4255 4256 // emit embedded fonts 4257 for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit ) 4258 { 4259 std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second ); 4260 for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit ) 4261 { 4262 CHECK_RETURN( fit->second ); 4263 aFontIDToObject[ fit->first ] = fit->second; 4264 } 4265 } 4266 4267 // emit system fonts 4268 for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit ) 4269 { 4270 std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second ); 4271 for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit ) 4272 { 4273 CHECK_RETURN( fit->second ); 4274 aFontIDToObject[ fit->first ] = fit->second; 4275 } 4276 } 4277 4278 OStringBuffer aFontDict( 1024 ); 4279 aFontDict.append( getFontDictObject() ); 4280 aFontDict.append( " 0 obj\n" 4281 "<<" ); 4282 int ni = 0; 4283 for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit ) 4284 { 4285 aFontDict.append( "/F" ); 4286 aFontDict.append( mit->first ); 4287 aFontDict.append( ' ' ); 4288 aFontDict.append( mit->second ); 4289 aFontDict.append( " 0 R" ); 4290 if( ((++ni) & 7) == 0 ) 4291 aFontDict.append( '\n' ); 4292 } 4293 // emit builtin font for widget apperances / variable text 4294 for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin(); 4295 it != m_aBuiltinFontToObjectMap.end(); ++it ) 4296 { 4297 ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]); 4298 it->second = emitBuiltinFont( &aData, it->second ); 4299 } 4300 appendBuiltinFontsToDict( aFontDict ); 4301 aFontDict.append( "\n>>\nendobj\n\n" ); 4302 4303 CHECK_RETURN( updateObject( getFontDictObject() ) ); 4304 CHECK_RETURN( writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) ); 4305 return true; 4306 } 4307 4308 sal_Int32 PDFWriterImpl::emitResources() 4309 { 4310 // emit shadings 4311 if( ! m_aGradients.empty() ) 4312 CHECK_RETURN( emitGradients() ); 4313 // emit tilings 4314 if( ! m_aTilings.empty() ) 4315 CHECK_RETURN( emitTilings() ); 4316 4317 // emit font dict 4318 CHECK_RETURN( emitFonts() ); 4319 4320 // emit Resource dict 4321 OStringBuffer aLine( 512 ); 4322 sal_Int32 nResourceDict = getResourceDictObj(); 4323 CHECK_RETURN( updateObject( nResourceDict ) ); 4324 aLine.setLength( 0 ); 4325 aLine.append( nResourceDict ); 4326 aLine.append( " 0 obj\n" ); 4327 m_aGlobalResourceDict.append( aLine, getFontDictObject() ); 4328 aLine.append( "endobj\n\n" ); 4329 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4330 return nResourceDict; 4331 } 4332 4333 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts, 4334 sal_Int32 nItemLevel, 4335 sal_Int32 nCurrentItemId ) 4336 { 4337 /* The /Count number of an item is 4338 positive: the number of visible subitems 4339 negative: the negative number of subitems that will become visible if 4340 the item gets opened 4341 see PDF ref 1.4 p 478 4342 */ 4343 4344 sal_Int32 nCount = 0; 4345 4346 if( m_aContext.OpenBookmarkLevels < 0 || // all levels arevisible 4347 m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible 4348 ) 4349 { 4350 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ]; 4351 sal_Int32 nChildren = rItem.m_aChildren.size(); 4352 for( sal_Int32 i = 0; i < nChildren; i++ ) 4353 nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] ); 4354 rCounts[nCurrentItemId] = nCount; 4355 // return 1 (this item) + visible sub items 4356 if( nCount < 0 ) 4357 nCount = 0; 4358 nCount++; 4359 } 4360 else 4361 { 4362 // this bookmark level is invisible 4363 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ]; 4364 sal_Int32 nChildren = rItem.m_aChildren.size(); 4365 rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size()); 4366 for( sal_Int32 i = 0; i < nChildren; i++ ) 4367 updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] ); 4368 nCount = -1; 4369 } 4370 4371 return nCount; 4372 } 4373 4374 sal_Int32 PDFWriterImpl::emitOutline() 4375 { 4376 int i, nItems = m_aOutline.size(); 4377 4378 // do we have an outline at all ? 4379 if( nItems < 2 ) 4380 return 0; 4381 4382 // reserve object numbers for all outline items 4383 for( i = 0; i < nItems; ++i ) 4384 m_aOutline[i].m_nObject = createObject(); 4385 4386 // update all parent, next and prev object ids 4387 for( i = 0; i < nItems; ++i ) 4388 { 4389 PDFOutlineEntry& rItem = m_aOutline[i]; 4390 int nChildren = rItem.m_aChildren.size(); 4391 4392 if( nChildren ) 4393 { 4394 for( int n = 0; n < nChildren; ++n ) 4395 { 4396 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ]; 4397 4398 rChild.m_nParentObject = rItem.m_nObject; 4399 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0; 4400 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0; 4401 } 4402 4403 } 4404 } 4405 4406 // calculate Count entries for all items 4407 std::vector< sal_Int32 > aCounts( nItems ); 4408 updateOutlineItemCount( aCounts, 0, 0 ); 4409 4410 // emit hierarchy 4411 for( i = 0; i < nItems; ++i ) 4412 { 4413 PDFOutlineEntry& rItem = m_aOutline[i]; 4414 OStringBuffer aLine( 1024 ); 4415 4416 CHECK_RETURN( updateObject( rItem.m_nObject ) ); 4417 aLine.append( rItem.m_nObject ); 4418 aLine.append( " 0 obj\n" ); 4419 aLine.append( "<<" ); 4420 // number of visible children (all levels) 4421 if( i > 0 || aCounts[0] > 0 ) 4422 { 4423 aLine.append( "/Count " ); 4424 aLine.append( aCounts[i] ); 4425 } 4426 if( ! rItem.m_aChildren.empty() ) 4427 { 4428 // children list: First, Last 4429 aLine.append( "/First " ); 4430 aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject ); 4431 aLine.append( " 0 R/Last " ); 4432 aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject ); 4433 aLine.append( " 0 R\n" ); 4434 } 4435 if( i > 0 ) 4436 { 4437 // Title, Dest, Parent, Prev, Next 4438 aLine.append( "/Title" ); 4439 appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine ); 4440 aLine.append( "\n" ); 4441 // Dest is not required 4442 if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() ) 4443 { 4444 aLine.append( "/Dest" ); 4445 appendDest( rItem.m_nDestID, aLine ); 4446 } 4447 aLine.append( "/Parent " ); 4448 aLine.append( rItem.m_nParentObject ); 4449 aLine.append( " 0 R" ); 4450 if( rItem.m_nPrevObject ) 4451 { 4452 aLine.append( "/Prev " ); 4453 aLine.append( rItem.m_nPrevObject ); 4454 aLine.append( " 0 R" ); 4455 } 4456 if( rItem.m_nNextObject ) 4457 { 4458 aLine.append( "/Next " ); 4459 aLine.append( rItem.m_nNextObject ); 4460 aLine.append( " 0 R" ); 4461 } 4462 } 4463 aLine.append( ">>\nendobj\n\n" ); 4464 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4465 } 4466 4467 return m_aOutline[0].m_nObject; 4468 } 4469 4470 #undef CHECK_RETURN 4471 #define CHECK_RETURN( x ) if( !x ) return false 4472 4473 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer ) 4474 { 4475 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) 4476 { 4477 #if OSL_DEBUG_LEVEL > 1 4478 fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID ); 4479 #endif 4480 return false; 4481 } 4482 4483 4484 const PDFDest& rDest = m_aDests[ nDestID ]; 4485 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ]; 4486 4487 rBuffer.append( '[' ); 4488 rBuffer.append( rDestPage.m_nPageObject ); 4489 rBuffer.append( " 0 R" ); 4490 4491 switch( rDest.m_eType ) 4492 { 4493 case PDFWriter::XYZ: 4494 default: 4495 rBuffer.append( "/XYZ " ); 4496 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4497 rBuffer.append( ' ' ); 4498 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4499 rBuffer.append( " 0" ); 4500 break; 4501 case PDFWriter::Fit: 4502 rBuffer.append( "/Fit" ); 4503 break; 4504 case PDFWriter::FitRectangle: 4505 rBuffer.append( "/FitR " ); 4506 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4507 rBuffer.append( ' ' ); 4508 appendFixedInt( rDest.m_aRect.Top(), rBuffer ); 4509 rBuffer.append( ' ' ); 4510 appendFixedInt( rDest.m_aRect.Right(), rBuffer ); 4511 rBuffer.append( ' ' ); 4512 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4513 break; 4514 case PDFWriter::FitHorizontal: 4515 rBuffer.append( "/FitH " ); 4516 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4517 break; 4518 case PDFWriter::FitVertical: 4519 rBuffer.append( "/FitV " ); 4520 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4521 break; 4522 case PDFWriter::FitPageBoundingBox: 4523 rBuffer.append( "/FitB" ); 4524 break; 4525 case PDFWriter::FitPageBoundingBoxHorizontal: 4526 rBuffer.append( "/FitBH " ); 4527 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4528 break; 4529 case PDFWriter::FitPageBoundingBoxVertical: 4530 rBuffer.append( "/FitBV " ); 4531 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4532 break; 4533 } 4534 rBuffer.append( ']' ); 4535 4536 return true; 4537 } 4538 4539 bool PDFWriterImpl::emitLinkAnnotations() 4540 { 4541 int nAnnots = m_aLinks.size(); 4542 for( int i = 0; i < nAnnots; i++ ) 4543 { 4544 const PDFLink& rLink = m_aLinks[i]; 4545 if( ! updateObject( rLink.m_nObject ) ) 4546 continue; 4547 4548 OStringBuffer aLine( 1024 ); 4549 aLine.append( rLink.m_nObject ); 4550 aLine.append( " 0 obj\n" ); 4551 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' 4552 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 4553 aLine.append( "<</Type/Annot" ); 4554 if( m_bIsPDF_A1 ) 4555 aLine.append( "/F 4" ); 4556 aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" ); 4557 4558 appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle 4559 aLine.append( ' ' ); 4560 appendFixedInt( rLink.m_aRect.Top(), aLine ); 4561 aLine.append( ' ' ); 4562 appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle 4563 aLine.append( ' ' ); 4564 appendFixedInt( rLink.m_aRect.Bottom(), aLine ); 4565 aLine.append( "]" ); 4566 if( rLink.m_nDest >= 0 ) 4567 { 4568 aLine.append( "/Dest" ); 4569 appendDest( rLink.m_nDest, aLine ); 4570 } 4571 else 4572 { 4573 /*--->i56629 4574 destination is external to the document, so 4575 we check in the following sequence: 4576 4577 if target type is neither .pdf, nor .od[tpgs], then 4578 check if relative or absolute and act accordingly (use URI or 'launch application' as requested) 4579 end processing 4580 else if target is .od[tpgs]: then 4581 if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file 4582 processing continue 4583 4584 if (new)target is .pdf : then 4585 if GotToR is requested, then 4586 convert the target in GoToR where the fragment of the URI is 4587 considered the named destination in the target file, set relative or absolute as requested 4588 else strip the fragment from URL and then set URI or 'launch application' as requested 4589 */ 4590 // 4591 // FIXME: check if the decode mechanisms for URL processing throughout this implementation 4592 // are the correct one!! 4593 // 4594 // extract target file type 4595 INetURLObject aDocumentURL( m_aContext.BaseURL ); 4596 INetURLObject aTargetURL( rLink.m_aURL ); 4597 sal_Int32 nChangeFileExtensionToPDF = 0; 4598 sal_Int32 nSetGoToRMode = 0; 4599 sal_Bool bTargetHasPDFExtension = sal_False; 4600 INetProtocol eTargetProtocol = aTargetURL.GetProtocol(); 4601 sal_Bool bIsUNCPath = sal_False; 4602 // check if the protocol is a known one, or if there is no protocol at all (on target only) 4603 // if there is no protocol, make the target relative to the current document directory 4604 // getting the needed URL information from the current document path 4605 if( eTargetProtocol == INET_PROT_NOT_VALID ) 4606 { 4607 if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.compareToAscii( "\\\\\\\\", 4 ) == 0) 4608 { 4609 bIsUNCPath = sal_True; 4610 } 4611 else 4612 { 4613 INetURLObject aNewBase( aDocumentURL );//duplicate document URL 4614 aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the 4615 //target document 4616 aNewBase.insertName( rLink.m_aURL ); 4617 aTargetURL = aNewBase;//reassign the new target URL 4618 //recompute the target protocol, with the new URL 4619 //normal URL processing resumes 4620 eTargetProtocol = aTargetURL.GetProtocol(); 4621 } 4622 } 4623 4624 rtl::OUString aFileExtension = aTargetURL.GetFileExtension(); 4625 4626 // Check if the URL ends in '/': if yes it's a directory, 4627 // it will be forced to a URI link. 4628 // possibly a malformed URI, leave it as it is, force as URI 4629 if( aTargetURL.hasFinalSlash() ) 4630 m_aContext.DefaultLinkAction = PDFWriter::URIAction; 4631 4632 if( aFileExtension.getLength() > 0 ) 4633 { 4634 if( m_aContext.ConvertOOoTargetToPDFTarget ) 4635 { 4636 //examine the file type (.odm .odt. .odp, odg, ods) 4637 if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odm" ) ) ) ) 4638 nChangeFileExtensionToPDF++; 4639 if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odt" ) ) ) ) 4640 nChangeFileExtensionToPDF++; 4641 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odp" ) ) ) ) 4642 nChangeFileExtensionToPDF++; 4643 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odg" ) ) ) ) 4644 nChangeFileExtensionToPDF++; 4645 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ods" ) ) ) ) 4646 nChangeFileExtensionToPDF++; 4647 if( nChangeFileExtensionToPDF ) 4648 aTargetURL.setExtension(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) ); 4649 } 4650 //check if extension is pdf, see if GoToR should be forced 4651 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) ); 4652 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension ) 4653 nSetGoToRMode++; 4654 } 4655 //prepare the URL, if relative or not 4656 INetProtocol eBaseProtocol = aDocumentURL.GetProtocol(); 4657 //queue the string common to all types of actions 4658 aLine.append( "/A<</Type/Action/S"); 4659 if( bIsUNCPath ) // handle Win UNC paths 4660 { 4661 aLine.append( "/Launch/Win<</F" ); 4662 // INetURLObject is not good with UNC paths, use original path 4663 appendLiteralStringEncrypt( rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 4664 aLine.append( ">>" ); 4665 } 4666 else 4667 { 4668 bool bSetRelative = false; 4669 bool bFileSpec = false; 4670 //check if relative file link is requested and if the protocol is 'file://' 4671 if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE ) 4672 bSetRelative = true; 4673 4674 rtl::OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is, 4675 if( nSetGoToRMode == 0 ) 4676 { 4677 switch( m_aContext.DefaultLinkAction ) 4678 { 4679 default: 4680 case PDFWriter::URIAction : 4681 case PDFWriter::URIActionDestination : 4682 aLine.append( "/URI/URI" ); 4683 break; 4684 case PDFWriter::LaunchAction: 4685 // now: 4686 // if a launch action is requested and the hyperlink target has a fragment 4687 // and the target file does not have a pdf extension, or it's not a 'file:://' protocol 4688 // then force the uri action on it 4689 // This code will permit the correct opening of application on web pages, the one that 4690 // normally have fragments (but I may be wrong...) 4691 // and will force the use of URI when the protocol is not file:// 4692 if( (aFragment.getLength() > 0 && !bTargetHasPDFExtension) || 4693 eTargetProtocol != INET_PROT_FILE ) 4694 aLine.append( "/URI/URI" ); 4695 else 4696 { 4697 aLine.append( "/Launch/F" ); 4698 bFileSpec = true; 4699 } 4700 break; 4701 } 4702 } 4703 //fragment are encoded in the same way as in the named destination processing 4704 if( nSetGoToRMode ) 4705 {//add the fragment 4706 rtl::OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET ); 4707 aLine.append("/GoToR"); 4708 aLine.append("/F"); 4709 bFileSpec = true; 4710 appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark, 4711 INetURLObject::WAS_ENCODED, 4712 INetURLObject::DECODE_WITH_CHARSET ) : 4713 aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 4714 if( aFragment.getLength() > 0 ) 4715 { 4716 aLine.append("/D/"); 4717 appendDestinationName( aFragment , aLine ); 4718 } 4719 } 4720 else 4721 { 4722 // change the fragment to accomodate the bookmark (only if the file extension is PDF and 4723 // the requested action is of the correct type) 4724 if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination && 4725 bTargetHasPDFExtension && aFragment.getLength() > 0 ) 4726 { 4727 OStringBuffer aLineLoc( 1024 ); 4728 appendDestinationName( aFragment , aLineLoc ); 4729 //substitute the fragment 4730 aTargetURL.SetMark( aLineLoc.getStr() ); 4731 } 4732 rtl::OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE ); 4733 // check if we have a URL available, if the string is empty, set it as the original one 4734 // if( aURL.getLength() == 0 ) 4735 // appendLiteralStringEncrypt( rLink.m_aURL , rLink.m_nObject, aLine ); 4736 // else 4737 appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL, 4738 INetURLObject::WAS_ENCODED, 4739 bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE 4740 ) : 4741 aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 4742 } 4743 //<--- i56629 4744 } 4745 aLine.append( ">>\n" ); 4746 } 4747 if( rLink.m_nStructParent > 0 ) 4748 { 4749 aLine.append( "/StructParent " ); 4750 aLine.append( rLink.m_nStructParent ); 4751 } 4752 aLine.append( ">>\nendobj\n\n" ); 4753 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4754 } 4755 4756 return true; 4757 } 4758 4759 bool PDFWriterImpl::emitNoteAnnotations() 4760 { 4761 // emit note annotations 4762 int nAnnots = m_aNotes.size(); 4763 for( int i = 0; i < nAnnots; i++ ) 4764 { 4765 const PDFNoteEntry& rNote = m_aNotes[i]; 4766 if( ! updateObject( rNote.m_nObject ) ) 4767 return false; 4768 4769 OStringBuffer aLine( 1024 ); 4770 aLine.append( rNote.m_nObject ); 4771 aLine.append( " 0 obj\n" ); 4772 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' 4773 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 4774 aLine.append( "<</Type/Annot" ); 4775 if( m_bIsPDF_A1 ) 4776 aLine.append( "/F 4" ); 4777 aLine.append( "/Subtype/Text/Rect[" ); 4778 4779 appendFixedInt( rNote.m_aRect.Left(), aLine ); 4780 aLine.append( ' ' ); 4781 appendFixedInt( rNote.m_aRect.Top(), aLine ); 4782 aLine.append( ' ' ); 4783 appendFixedInt( rNote.m_aRect.Right(), aLine ); 4784 aLine.append( ' ' ); 4785 appendFixedInt( rNote.m_aRect.Bottom(), aLine ); 4786 aLine.append( "]" ); 4787 4788 // contents of the note (type text string) 4789 aLine.append( "/Contents\n" ); 4790 appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine ); 4791 aLine.append( "\n" ); 4792 4793 // optional title 4794 if( rNote.m_aContents.Title.Len() ) 4795 { 4796 aLine.append( "/T" ); 4797 appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine ); 4798 aLine.append( "\n" ); 4799 } 4800 4801 aLine.append( ">>\nendobj\n\n" ); 4802 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4803 } 4804 return true; 4805 } 4806 4807 Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font& rAppSetFont ) 4808 { 4809 bool bAdjustSize = false; 4810 4811 Font aFont( rControlFont ); 4812 if( ! aFont.GetName().Len() ) 4813 { 4814 aFont = rAppSetFont; 4815 if( rControlFont.GetHeight() ) 4816 aFont.SetSize( Size( 0, rControlFont.GetHeight() ) ); 4817 else 4818 bAdjustSize = true; 4819 if( rControlFont.GetItalic() != ITALIC_DONTKNOW ) 4820 aFont.SetItalic( rControlFont.GetItalic() ); 4821 if( rControlFont.GetWeight() != WEIGHT_DONTKNOW ) 4822 aFont.SetWeight( rControlFont.GetWeight() ); 4823 } 4824 else if( ! aFont.GetHeight() ) 4825 { 4826 aFont.SetSize( rAppSetFont.GetSize() ); 4827 bAdjustSize = true; 4828 } 4829 if( bAdjustSize ) 4830 { 4831 Size aFontSize = aFont.GetSize(); 4832 OutputDevice* pDefDev = Application::GetDefaultDevice(); 4833 aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() ); 4834 aFont.SetSize( aFontSize ); 4835 } 4836 return aFont; 4837 } 4838 4839 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont ) 4840 { 4841 sal_Int32 nBest = 4; // default to Helvetica 4842 OUString aFontName( rFont.GetName() ); 4843 aFontName = aFontName.toAsciiLowerCase(); 4844 4845 if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "times" ) ) ) != -1 ) 4846 nBest = 8; 4847 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "courier" ) ) ) != -1 ) 4848 nBest = 0; 4849 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "dingbats" ) ) ) != -1 ) 4850 nBest = 13; 4851 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "symbol" ) ) ) != -1 ) 4852 nBest = 12; 4853 if( nBest < 12 ) 4854 { 4855 if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL ) 4856 nBest += 1; 4857 if( rFont.GetWeight() > WEIGHT_MEDIUM ) 4858 nBest += 2; 4859 } 4860 4861 if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() ) 4862 m_aBuiltinFontToObjectMap[ nBest ] = createObject(); 4863 4864 return nBest; 4865 } 4866 4867 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 ) 4868 { 4869 return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1; 4870 } 4871 4872 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget ) 4873 { 4874 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 4875 4876 // save graphics state 4877 push( sal::static_int_cast<sal_uInt16>(~0U) ); 4878 4879 // transform relative to control's coordinates since an 4880 // appearance stream is a form XObject 4881 // this relies on the m_aRect member of rButton NOT already being transformed 4882 // to default user space 4883 if( rWidget.Background || rWidget.Border ) 4884 { 4885 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) ); 4886 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) ); 4887 drawRectangle( rWidget.Location ); 4888 } 4889 // prepare font to use 4890 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() ); 4891 setFont( aFont ); 4892 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) ); 4893 4894 drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle ); 4895 4896 // create DA string while local mapmode is still in place 4897 // (that is before endRedirect()) 4898 OStringBuffer aDA( 256 ); 4899 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA ); 4900 Font aDummyFont( String( RTL_CONSTASCII_USTRINGPARAM( "Helvetica" ) ), aFont.GetSize() ); 4901 sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont ); 4902 aDA.append( ' ' ); 4903 aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() ); 4904 aDA.append( ' ' ); 4905 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); 4906 aDA.append( " Tf" ); 4907 rButton.m_aDAString = aDA.makeStringAndClear(); 4908 4909 pop(); 4910 4911 rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream(); 4912 4913 /* seems like a bad hack but at least works in both AR5 and 6: 4914 we draw the button ourselves and tell AR 4915 the button would be totally transparent with no text 4916 4917 One would expect that simply setting a normal appearance 4918 should suffice, but no, as soon as the user actually presses 4919 the button and an action is tied to it (gasp! a button that 4920 does something) the appearance gets replaced by some crap that AR 4921 creates on the fly even if no DA or MK is given. On AR6 at least 4922 the DA and MK work as expected, but on AR5 this creates a region 4923 filled with the background color but nor text. Urgh. 4924 */ 4925 rButton.m_aMKDict = "/BC [] /BG [] /CA"; 4926 rButton.m_aMKDictCAString = ""; 4927 } 4928 4929 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern, 4930 const PDFWriter::AnyWidget& rWidget, 4931 const StyleSettings& rSettings ) 4932 { 4933 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() ); 4934 4935 if( rWidget.Background || rWidget.Border ) 4936 { 4937 if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) ) 4938 { 4939 sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500; 4940 if( nDelta < 1 ) 4941 nDelta = 1; 4942 setLineColor( Color( COL_TRANSPARENT ) ); 4943 Rectangle aRect = rIntern.m_aRect; 4944 setFillColor( rSettings.GetLightBorderColor() ); 4945 drawRectangle( aRect ); 4946 aRect.Left() += nDelta; aRect.Top() += nDelta; 4947 aRect.Right() -= nDelta; aRect.Bottom() -= nDelta; 4948 setFillColor( rSettings.GetFieldColor() ); 4949 drawRectangle( aRect ); 4950 setFillColor( rSettings.GetLightColor() ); 4951 drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) ); 4952 drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) ); 4953 setFillColor( rSettings.GetDarkShadowColor() ); 4954 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) ); 4955 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) ); 4956 } 4957 else 4958 { 4959 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) ); 4960 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 4961 drawRectangle( rIntern.m_aRect ); 4962 } 4963 4964 if( rWidget.Border ) 4965 { 4966 // adjust edit area accounting for border 4967 sal_Int32 nDelta = aFont.GetHeight()/4; 4968 if( nDelta < 1 ) 4969 nDelta = 1; 4970 rIntern.m_aRect.Left() += nDelta; 4971 rIntern.m_aRect.Top() += nDelta; 4972 rIntern.m_aRect.Right() -= nDelta; 4973 rIntern.m_aRect.Bottom()-= nDelta; 4974 } 4975 } 4976 return aFont; 4977 } 4978 4979 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget ) 4980 { 4981 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 4982 SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 ); 4983 4984 push( sal::static_int_cast<sal_uInt16>(~0U) ); 4985 4986 // prepare font to use, draw field border 4987 Font aFont = drawFieldBorder( rEdit, rWidget, rSettings ); 4988 sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont ); 4989 4990 // prepare DA string 4991 OStringBuffer aDA( 32 ); 4992 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA ); 4993 aDA.append( ' ' ); 4994 if( m_aContext.FieldsUseSystemFonts ) 4995 { 4996 aDA.append( "/F" ); 4997 aDA.append( nBest ); 4998 4999 OStringBuffer aDR( 32 ); 5000 aDR.append( "/Font " ); 5001 aDR.append( getFontDictObject() ); 5002 aDR.append( " 0 R" ); 5003 rEdit.m_aDRDict = aDR.makeStringAndClear(); 5004 } 5005 else 5006 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5007 aDA.append( ' ' ); 5008 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); 5009 aDA.append( " Tf" ); 5010 5011 /* create an empty appearance stream, let the viewer create 5012 the appearance at runtime. This is because AR5 seems to 5013 paint the widget appearance always, and a dynamically created 5014 appearance on top of it. AR6 is well behaved in that regard, so 5015 that behaviour seems to be a bug. Anyway this empty appearance 5016 relies on /NeedAppearances in the AcroForm dictionary set to "true" 5017 */ 5018 beginRedirect( pEditStream, rEdit.m_aRect ); 5019 OStringBuffer aAppearance( 32 ); 5020 aAppearance.append( "/Tx BMC\nEMC\n" ); 5021 writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); 5022 5023 endRedirect(); 5024 pop(); 5025 5026 rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream; 5027 5028 rEdit.m_aDAString = aDA.makeStringAndClear(); 5029 } 5030 5031 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget ) 5032 { 5033 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 5034 SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 ); 5035 5036 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5037 5038 // prepare font to use, draw field border 5039 Font aFont = drawFieldBorder( rBox, rWidget, rSettings ); 5040 sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont ); 5041 5042 beginRedirect( pListBoxStream, rBox.m_aRect ); 5043 OStringBuffer aAppearance( 64 ); 5044 5045 #if 0 5046 if( ! rWidget.DropDown ) 5047 { 5048 // prepare linewidth for DA string hack, see below 5049 Size aFontSize = lcl_convert( m_aGraphicsStack.front().m_aMapMode, 5050 m_aMapMode, 5051 getReferenceDevice(), 5052 Size( 0, aFont.GetHeight() ) ); 5053 sal_Int32 nLW = aFontSize.Height() / 40; 5054 appendFixedInt( nLW > 0 ? nLW : 1, aAppearance ); 5055 aAppearance.append( " w\n" ); 5056 writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); 5057 aAppearance.setLength( 0 ); 5058 } 5059 #endif 5060 5061 setLineColor( Color( COL_TRANSPARENT ) ); 5062 setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) ); 5063 drawRectangle( rBox.m_aRect ); 5064 5065 // empty appearance, see createDefaultEditAppearance for reference 5066 aAppearance.append( "/Tx BMC\nEMC\n" ); 5067 writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); 5068 5069 endRedirect(); 5070 pop(); 5071 5072 rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream; 5073 5074 // prepare DA string 5075 OStringBuffer aDA( 256 ); 5076 #if 0 5077 if( !rWidget.DropDown ) 5078 { 5079 /* another of AR5's peculiarities: the selected item of a choice 5080 field is highlighted using the non stroking color - same as the 5081 text color. so workaround that by using text rendering mode 2 5082 (fill, then stroke) and set the stroking color 5083 */ 5084 appendStrokingColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ), aDA ); 5085 aDA.append( " 2 Tr " ); 5086 } 5087 #endif 5088 // prepare DA string 5089 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA ); 5090 aDA.append( ' ' ); 5091 if( m_aContext.FieldsUseSystemFonts ) 5092 { 5093 aDA.append( "/F" ); 5094 aDA.append( nBest ); 5095 5096 OStringBuffer aDR( 32 ); 5097 aDR.append( "/Font " ); 5098 aDR.append( getFontDictObject() ); 5099 aDR.append( " 0 R" ); 5100 rBox.m_aDRDict = aDR.makeStringAndClear(); 5101 } 5102 else 5103 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5104 aDA.append( ' ' ); 5105 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); 5106 aDA.append( " Tf" ); 5107 rBox.m_aDAString = aDA.makeStringAndClear(); 5108 } 5109 5110 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget ) 5111 { 5112 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 5113 5114 // save graphics state 5115 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5116 5117 if( rWidget.Background || rWidget.Border ) 5118 { 5119 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) ); 5120 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 5121 drawRectangle( rBox.m_aRect ); 5122 } 5123 5124 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() ); 5125 setFont( aFont ); 5126 Size aFontSize = aFont.GetSize(); 5127 if( aFontSize.Height() > rBox.m_aRect.GetHeight() ) 5128 aFontSize.Height() = rBox.m_aRect.GetHeight(); 5129 sal_Int32 nDelta = aFontSize.Height()/10; 5130 if( nDelta < 1 ) 5131 nDelta = 1; 5132 5133 Rectangle aCheckRect, aTextRect; 5134 if( rWidget.ButtonIsLeft ) 5135 { 5136 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta; 5137 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5138 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5139 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5140 5141 // #i74206# handle small controls without text area 5142 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5143 { 5144 aCheckRect.Right() -= nDelta; 5145 aCheckRect.Top() += nDelta/2; 5146 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5147 } 5148 5149 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta; 5150 aTextRect.Top() = rBox.m_aRect.Top(); 5151 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5152 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5153 } 5154 else 5155 { 5156 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height(); 5157 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5158 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5159 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5160 5161 // #i74206# handle small controls without text area 5162 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5163 { 5164 aCheckRect.Left() += nDelta; 5165 aCheckRect.Top() += nDelta/2; 5166 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5167 } 5168 5169 aTextRect.Left() = rBox.m_aRect.Left(); 5170 aTextRect.Top() = rBox.m_aRect.Top(); 5171 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5172 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5173 } 5174 setLineColor( Color( COL_BLACK ) ); 5175 setFillColor( Color( COL_TRANSPARENT ) ); 5176 OStringBuffer aLW( 32 ); 5177 aLW.append( "q " ); 5178 m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW ); 5179 aLW.append( " w " ); 5180 writeBuffer( aLW.getStr(), aLW.getLength() ); 5181 drawRectangle( aCheckRect ); 5182 writeBuffer( " Q\n", 3 ); 5183 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 5184 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle ); 5185 5186 pop(); 5187 5188 OStringBuffer aDA( 256 ); 5189 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5190 sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) ); 5191 aDA.append( ' ' ); 5192 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5193 aDA.append( " 0 Tf" ); 5194 rBox.m_aDAString = aDA.makeStringAndClear(); 5195 rBox.m_aMKDict = "/CA"; 5196 rBox.m_aMKDictCAString = "8"; 5197 rBox.m_aRect = aCheckRect; 5198 5199 // create appearance streams 5200 sal_Char cMark = '8'; 5201 sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)]; 5202 nCharXOffset *= aCheckRect.GetHeight(); 5203 nCharXOffset /= 2000; 5204 sal_Int32 nCharYOffset = 1000- 5205 (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative 5206 nCharYOffset *= aCheckRect.GetHeight(); 5207 nCharYOffset /= 2000; 5208 5209 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 ); 5210 beginRedirect( pCheckStream, aCheckRect ); 5211 aDA.append( "/Tx BMC\nq BT\n" ); 5212 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5213 aDA.append( ' ' ); 5214 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5215 aDA.append( ' ' ); 5216 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA ); 5217 aDA.append( " Tf\n" ); 5218 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA ); 5219 aDA.append( " " ); 5220 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA ); 5221 aDA.append( " Td (" ); 5222 aDA.append( cMark ); 5223 aDA.append( ") Tj\nET\nQ\nEMC\n" ); 5224 writeBuffer( aDA.getStr(), aDA.getLength() ); 5225 endRedirect(); 5226 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream; 5227 5228 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 ); 5229 beginRedirect( pUncheckStream, aCheckRect ); 5230 writeBuffer( "/Tx BMC\nEMC\n", 12 ); 5231 endRedirect(); 5232 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream; 5233 } 5234 5235 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget ) 5236 { 5237 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 5238 5239 // save graphics state 5240 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5241 5242 if( rWidget.Background || rWidget.Border ) 5243 { 5244 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) ); 5245 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 5246 drawRectangle( rBox.m_aRect ); 5247 } 5248 5249 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() ); 5250 setFont( aFont ); 5251 Size aFontSize = aFont.GetSize(); 5252 if( aFontSize.Height() > rBox.m_aRect.GetHeight() ) 5253 aFontSize.Height() = rBox.m_aRect.GetHeight(); 5254 sal_Int32 nDelta = aFontSize.Height()/10; 5255 if( nDelta < 1 ) 5256 nDelta = 1; 5257 5258 Rectangle aCheckRect, aTextRect; 5259 if( rWidget.ButtonIsLeft ) 5260 { 5261 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta; 5262 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5263 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5264 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5265 5266 // #i74206# handle small controls without text area 5267 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5268 { 5269 aCheckRect.Right() -= nDelta; 5270 aCheckRect.Top() += nDelta/2; 5271 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5272 } 5273 5274 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta; 5275 aTextRect.Top() = rBox.m_aRect.Top(); 5276 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5277 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5278 } 5279 else 5280 { 5281 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height(); 5282 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5283 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5284 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5285 5286 // #i74206# handle small controls without text area 5287 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5288 { 5289 aCheckRect.Left() += nDelta; 5290 aCheckRect.Top() += nDelta/2; 5291 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5292 } 5293 5294 aTextRect.Left() = rBox.m_aRect.Left(); 5295 aTextRect.Top() = rBox.m_aRect.Top(); 5296 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5297 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5298 } 5299 setLineColor( Color( COL_BLACK ) ); 5300 setFillColor( Color( COL_TRANSPARENT ) ); 5301 OStringBuffer aLW( 32 ); 5302 aLW.append( "q " ); 5303 m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW ); 5304 aLW.append( " w " ); 5305 writeBuffer( aLW.getStr(), aLW.getLength() ); 5306 drawEllipse( aCheckRect ); 5307 writeBuffer( " Q\n", 3 ); 5308 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 5309 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle ); 5310 5311 pop(); 5312 5313 OStringBuffer aDA( 256 ); 5314 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5315 sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) ); 5316 aDA.append( ' ' ); 5317 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5318 aDA.append( " 0 Tf" ); 5319 rBox.m_aDAString = aDA.makeStringAndClear(); 5320 //to encrypt this (el) 5321 rBox.m_aMKDict = "/CA"; 5322 //after this assignement, to m_aMKDic cannot be added anything 5323 rBox.m_aMKDictCAString = "l"; 5324 5325 rBox.m_aRect = aCheckRect; 5326 5327 // create appearance streams 5328 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5329 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 ); 5330 5331 beginRedirect( pCheckStream, aCheckRect ); 5332 aDA.append( "/Tx BMC\nq BT\n" ); 5333 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5334 aDA.append( ' ' ); 5335 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5336 aDA.append( ' ' ); 5337 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA ); 5338 aDA.append( " Tf\n0 0 Td\nET\nQ\n" ); 5339 writeBuffer( aDA.getStr(), aDA.getLength() ); 5340 setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 5341 setLineColor( Color( COL_TRANSPARENT ) ); 5342 aCheckRect.Left() += 3*nDelta; 5343 aCheckRect.Top() += 3*nDelta; 5344 aCheckRect.Bottom() -= 3*nDelta; 5345 aCheckRect.Right() -= 3*nDelta; 5346 drawEllipse( aCheckRect ); 5347 writeBuffer( "\nEMC\n", 5 ); 5348 endRedirect(); 5349 5350 pop(); 5351 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream; 5352 5353 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 ); 5354 beginRedirect( pUncheckStream, aCheckRect ); 5355 writeBuffer( "/Tx BMC\nEMC\n", 12 ); 5356 endRedirect(); 5357 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream; 5358 } 5359 5360 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict ) 5361 { 5362 5363 // TODO: check and insert default streams 5364 rtl::OString aStandardAppearance; 5365 switch( rWidget.m_eType ) 5366 { 5367 case PDFWriter::CheckBox: 5368 aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US ); 5369 break; 5370 default: 5371 break; 5372 } 5373 5374 if( rWidget.m_aAppearances.size() ) 5375 { 5376 rAnnotDict.append( "/AP<<\n" ); 5377 for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it ) 5378 { 5379 rAnnotDict.append( "/" ); 5380 rAnnotDict.append( dict_it->first ); 5381 bool bUseSubDict = (dict_it->second.size() > 1); 5382 rAnnotDict.append( bUseSubDict ? "<<" : " " ); 5383 5384 for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin(); 5385 stream_it != dict_it->second.end(); ++stream_it ) 5386 { 5387 SvMemoryStream* pApppearanceStream = stream_it->second; 5388 dict_it->second[ stream_it->first ] = NULL; 5389 5390 bool bDeflate = compressStream( pApppearanceStream ); 5391 5392 pApppearanceStream->Seek( STREAM_SEEK_TO_END ); 5393 sal_Int64 nStreamLen = pApppearanceStream->Tell(); 5394 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN ); 5395 sal_Int32 nObject = createObject(); 5396 CHECK_RETURN( updateObject( nObject ) ); 5397 #if OSL_DEBUG_LEVEL > 1 5398 emitComment( "PDFWriterImpl::emitAppearances" ); 5399 #endif 5400 OStringBuffer aLine; 5401 aLine.append( nObject ); 5402 5403 aLine.append( " 0 obj\n" 5404 "<</Type/XObject\n" 5405 "/Subtype/Form\n" 5406 "/BBox[0 0 " ); 5407 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine ); 5408 aLine.append( " " ); 5409 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine ); 5410 aLine.append( "]\n" 5411 "/Resources " ); 5412 aLine.append( getResourceDictObj() ); 5413 aLine.append( " 0 R\n" 5414 "/Length " ); 5415 aLine.append( nStreamLen ); 5416 aLine.append( "\n" ); 5417 if( bDeflate ) 5418 aLine.append( "/Filter/FlateDecode\n" ); 5419 aLine.append( ">>\nstream\n" ); 5420 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5421 checkAndEnableStreamEncryption( nObject ); 5422 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) ); 5423 disableStreamEncryption(); 5424 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) ); 5425 5426 if( bUseSubDict ) 5427 { 5428 rAnnotDict.append( " /" ); 5429 rAnnotDict.append( stream_it->first ); 5430 rAnnotDict.append( " " ); 5431 } 5432 rAnnotDict.append( nObject ); 5433 rAnnotDict.append( " 0 R" ); 5434 5435 delete pApppearanceStream; 5436 } 5437 5438 rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" ); 5439 } 5440 rAnnotDict.append( ">>\n" ); 5441 if( aStandardAppearance.getLength() ) 5442 { 5443 rAnnotDict.append( "/AS /" ); 5444 rAnnotDict.append( aStandardAppearance ); 5445 rAnnotDict.append( "\n" ); 5446 } 5447 } 5448 5449 return true; 5450 } 5451 5452 bool PDFWriterImpl::emitWidgetAnnotations() 5453 { 5454 ensureUniqueRadioOnValues(); 5455 5456 int nAnnots = m_aWidgets.size(); 5457 for( int a = 0; a < nAnnots; a++ ) 5458 { 5459 PDFWidget& rWidget = m_aWidgets[a]; 5460 5461 OStringBuffer aLine( 1024 ); 5462 OStringBuffer aValue( 256 ); 5463 aLine.append( rWidget.m_nObject ); 5464 aLine.append( " 0 obj\n" 5465 "<<" ); 5466 if( rWidget.m_eType != PDFWriter::Hierarchy ) 5467 { 5468 // emit widget annotation only for terminal fields 5469 if( rWidget.m_aKids.empty() ) 5470 { 5471 aLine.append( "/Type/Annot/Subtype/Widget/F 4\n" 5472 "/Rect[" ); 5473 appendFixedInt( rWidget.m_aRect.Left()-1, aLine ); 5474 aLine.append( ' ' ); 5475 appendFixedInt( rWidget.m_aRect.Top()+1, aLine ); 5476 aLine.append( ' ' ); 5477 appendFixedInt( rWidget.m_aRect.Right()+1, aLine ); 5478 aLine.append( ' ' ); 5479 appendFixedInt( rWidget.m_aRect.Bottom()-1, aLine ); 5480 aLine.append( "]\n" ); 5481 } 5482 aLine.append( "/FT/" ); 5483 switch( rWidget.m_eType ) 5484 { 5485 case PDFWriter::RadioButton: 5486 case PDFWriter::CheckBox: 5487 // for radio buttons only the RadioButton field, not the 5488 // CheckBox children should have a value, else acrobat reader 5489 // does not always check the right button 5490 // of course real check boxes (not belonging to a readio group) 5491 // need their values, too 5492 if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 ) 5493 { 5494 aValue.append( "/" ); 5495 // check for radio group with all buttons unpressed 5496 if( rWidget.m_aValue.getLength() == 0 ) 5497 aValue.append( "Off" ); 5498 else 5499 appendName( rWidget.m_aValue, aValue ); 5500 } 5501 case PDFWriter::PushButton: 5502 aLine.append( "Btn" ); 5503 break; 5504 case PDFWriter::ListBox: 5505 if( rWidget.m_nFlags & 0x200000 ) // multiselect 5506 { 5507 aValue.append( "[" ); 5508 for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ ) 5509 { 5510 sal_Int32 nEntry = rWidget.m_aSelectedEntries[i]; 5511 if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) ) 5512 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue ); 5513 } 5514 aValue.append( "]" ); 5515 } 5516 else if( rWidget.m_aSelectedEntries.size() > 0 && 5517 rWidget.m_aSelectedEntries[0] >= 0 && 5518 rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) ) 5519 { 5520 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue ); 5521 } 5522 else 5523 appendUnicodeTextStringEncrypt( rtl::OUString(), rWidget.m_nObject, aValue ); 5524 aLine.append( "Ch" ); 5525 break; 5526 case PDFWriter::ComboBox: 5527 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue ); 5528 aLine.append( "Ch" ); 5529 break; 5530 case PDFWriter::Edit: 5531 aLine.append( "Tx" ); 5532 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue ); 5533 break; 5534 case PDFWriter::Hierarchy: // make the compiler happy 5535 break; 5536 } 5537 aLine.append( "\n" ); 5538 aLine.append( "/P " ); 5539 aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject ); 5540 aLine.append( " 0 R\n" ); 5541 } 5542 if( rWidget.m_nParent ) 5543 { 5544 aLine.append( "/Parent " ); 5545 aLine.append( rWidget.m_nParent ); 5546 aLine.append( " 0 R\n" ); 5547 } 5548 if( rWidget.m_aKids.size() ) 5549 { 5550 aLine.append( "/Kids[" ); 5551 for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ ) 5552 { 5553 aLine.append( rWidget.m_aKids[i] ); 5554 aLine.append( " 0 R" ); 5555 aLine.append( ( (i&15) == 15 ) ? "\n" : " " ); 5556 } 5557 aLine.append( "]\n" ); 5558 } 5559 if( rWidget.m_aName.getLength() ) 5560 { 5561 aLine.append( "/T" ); 5562 appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine ); 5563 aLine.append( "\n" ); 5564 } 5565 if( m_aContext.Version > PDFWriter::PDF_1_2 && rWidget.m_aDescription.getLength() ) 5566 { 5567 // the alternate field name should be unicode able since it is 5568 // supposed to be used in UI 5569 aLine.append( "/TU" ); 5570 appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine ); 5571 aLine.append( "\n" ); 5572 } 5573 5574 if( rWidget.m_nFlags ) 5575 { 5576 aLine.append( "/Ff " ); 5577 aLine.append( rWidget.m_nFlags ); 5578 aLine.append( "\n" ); 5579 } 5580 if( aValue.getLength() ) 5581 { 5582 OString aVal = aValue.makeStringAndClear(); 5583 aLine.append( "/V " ); 5584 aLine.append( aVal ); 5585 aLine.append( "\n" 5586 "/DV " ); 5587 aLine.append( aVal ); 5588 aLine.append( "\n" ); 5589 } 5590 if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox ) 5591 { 5592 sal_Int32 nTI = -1; 5593 aLine.append( "/Opt[\n" ); 5594 sal_Int32 i = 0; 5595 for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i ) 5596 { 5597 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine ); 5598 aLine.append( "\n" ); 5599 if( *it == rWidget.m_aValue ) 5600 nTI = i; 5601 } 5602 aLine.append( "]\n" ); 5603 if( nTI > 0 ) 5604 { 5605 aLine.append( "/TI " ); 5606 aLine.append( nTI ); 5607 aLine.append( "\n" ); 5608 if( rWidget.m_nFlags & 0x200000 ) // Multiselect 5609 { 5610 aLine.append( "/I [" ); 5611 aLine.append( nTI ); 5612 aLine.append( "]\n" ); 5613 } 5614 } 5615 } 5616 if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 ) 5617 { 5618 aLine.append( "/MaxLen " ); 5619 aLine.append( rWidget.m_nMaxLen ); 5620 aLine.append( "\n" ); 5621 } 5622 if( rWidget.m_eType == PDFWriter::PushButton ) 5623 { 5624 if(!m_bIsPDF_A1) 5625 { 5626 OStringBuffer aDest; 5627 if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) ) 5628 { 5629 aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " ); 5630 aLine.append( aDest.makeStringAndClear() ); 5631 aLine.append( ">>>>\n" ); 5632 } 5633 else if( rWidget.m_aListEntries.empty() ) 5634 { 5635 // create a reset form action 5636 aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" ); 5637 } 5638 else if( rWidget.m_bSubmit ) 5639 { 5640 // create a submit form action 5641 aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" ); 5642 appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() ); 5643 aLine.append( "/Flags " ); 5644 5645 sal_Int32 nFlags = 0; 5646 switch( m_aContext.SubmitFormat ) 5647 { 5648 case PDFWriter::HTML: 5649 nFlags |= 4; 5650 break; 5651 case PDFWriter::XML: 5652 if( m_aContext.Version > PDFWriter::PDF_1_3 ) 5653 nFlags |= 32; 5654 break; 5655 case PDFWriter::PDF: 5656 if( m_aContext.Version > PDFWriter::PDF_1_3 ) 5657 nFlags |= 256; 5658 break; 5659 case PDFWriter::FDF: 5660 default: 5661 break; 5662 } 5663 if( rWidget.m_bSubmitGet ) 5664 nFlags |= 8; 5665 aLine.append( nFlags ); 5666 aLine.append( ">>>>\n" ); 5667 } 5668 else 5669 { 5670 // create a URI action 5671 aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" ); 5672 aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) ); 5673 aLine.append( ")>>>>\n" ); 5674 } 5675 } 5676 else 5677 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA ); 5678 } 5679 if( rWidget.m_aDAString.getLength() ) 5680 { 5681 if( rWidget.m_aDRDict.getLength() ) 5682 { 5683 aLine.append( "/DR<<" ); 5684 aLine.append( rWidget.m_aDRDict ); 5685 aLine.append( ">>\n" ); 5686 } 5687 else 5688 { 5689 aLine.append( "/DR<</Font<<" ); 5690 appendBuiltinFontsToDict( aLine ); 5691 aLine.append( ">>>>\n" ); 5692 } 5693 aLine.append( "/DA" ); 5694 appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine ); 5695 aLine.append( "\n" ); 5696 if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER ) 5697 aLine.append( "/Q 1\n" ); 5698 else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT ) 5699 aLine.append( "/Q 2\n" ); 5700 } 5701 // appearance charactristics for terminal fields 5702 // which are supposed to have an appearance constructed 5703 // by the viewer application 5704 if( rWidget.m_aMKDict.getLength() ) 5705 { 5706 aLine.append( "/MK<<" ); 5707 aLine.append( rWidget.m_aMKDict ); 5708 //add the CA string, encrypting it 5709 appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine); 5710 aLine.append( ">>\n" ); 5711 } 5712 5713 CHECK_RETURN( emitAppearances( rWidget, aLine ) ); 5714 5715 aLine.append( ">>\n" 5716 "endobj\n\n" ); 5717 CHECK_RETURN( updateObject( rWidget.m_nObject ) ); 5718 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5719 } 5720 return true; 5721 } 5722 5723 bool PDFWriterImpl::emitAnnotations() 5724 { 5725 if( m_aPages.size() < 1 ) 5726 return false; 5727 5728 CHECK_RETURN( emitLinkAnnotations() ); 5729 5730 CHECK_RETURN( emitNoteAnnotations() ); 5731 5732 CHECK_RETURN( emitWidgetAnnotations() ); 5733 5734 return true; 5735 } 5736 5737 #undef CHECK_RETURN 5738 #define CHECK_RETURN( x ) if( !x ) return false 5739 5740 bool PDFWriterImpl::emitCatalog() 5741 { 5742 // build page tree 5743 // currently there is only one node that contains all leaves 5744 5745 // first create a page tree node id 5746 sal_Int32 nTreeNode = createObject(); 5747 5748 // emit global resource dictionary (page emit needs it) 5749 CHECK_RETURN( emitResources() ); 5750 5751 // emit all pages 5752 for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it ) 5753 if( ! it->emit( nTreeNode ) ) 5754 return false; 5755 5756 sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations(); 5757 5758 sal_Int32 nOutlineDict = emitOutline(); 5759 5760 //emit Output intent i59651 5761 sal_Int32 nOutputIntentObject = emitOutputIntent(); 5762 5763 //emit metadata 5764 sal_Int32 nMetadataObject = emitDocumentMetadata(); 5765 5766 sal_Int32 nStructureDict = 0; 5767 if(m_aStructure.size() > 1) 5768 { 5769 ///check if dummy structure containers are needed 5770 addInternalStructureContainer(m_aStructure[0]); 5771 nStructureDict = m_aStructure[0].m_nObject = createObject(); 5772 emitStructure( m_aStructure[ 0 ] ); 5773 } 5774 5775 // adjust tree node file offset 5776 if( ! updateObject( nTreeNode ) ) 5777 return false; 5778 5779 // emit tree node 5780 OStringBuffer aLine( 2048 ); 5781 aLine.append( nTreeNode ); 5782 aLine.append( " 0 obj\n" ); 5783 aLine.append( "<</Type/Pages\n" ); 5784 aLine.append( "/Resources " ); 5785 aLine.append( getResourceDictObj() ); 5786 aLine.append( " 0 R\n" ); 5787 5788 switch( m_eInheritedOrientation ) 5789 { 5790 case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break; 5791 case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break; 5792 5793 case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant 5794 case PDFWriter::Portrait: 5795 default: 5796 break; 5797 } 5798 sal_Int32 nMediaBoxWidth = 0; 5799 sal_Int32 nMediaBoxHeight = 0; 5800 if( m_aPages.empty() ) // sanity check, this should not happen 5801 { 5802 nMediaBoxWidth = m_nInheritedPageWidth; 5803 nMediaBoxHeight = m_nInheritedPageHeight; 5804 } 5805 else 5806 { 5807 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter ) 5808 { 5809 if( iter->m_nPageWidth > nMediaBoxWidth ) 5810 nMediaBoxWidth = iter->m_nPageWidth; 5811 if( iter->m_nPageHeight > nMediaBoxHeight ) 5812 nMediaBoxHeight = iter->m_nPageHeight; 5813 } 5814 } 5815 aLine.append( "/MediaBox[ 0 0 " ); 5816 aLine.append( nMediaBoxWidth ); 5817 aLine.append( ' ' ); 5818 aLine.append( nMediaBoxHeight ); 5819 aLine.append( " ]\n" 5820 "/Kids[ " ); 5821 unsigned int i = 0; 5822 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ ) 5823 { 5824 aLine.append( iter->m_nPageObject ); 5825 aLine.append( " 0 R" ); 5826 aLine.append( ( (i&15) == 15 ) ? "\n" : " " ); 5827 } 5828 aLine.append( "]\n" 5829 "/Count " ); 5830 aLine.append( (sal_Int32)m_aPages.size() ); 5831 aLine.append( ">>\n" 5832 "endobj\n\n" ); 5833 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5834 5835 // emit annotation objects 5836 CHECK_RETURN( emitAnnotations() ); 5837 5838 // emit Catalog 5839 m_nCatalogObject = createObject(); 5840 if( ! updateObject( m_nCatalogObject ) ) 5841 return false; 5842 aLine.setLength( 0 ); 5843 aLine.append( m_nCatalogObject ); 5844 aLine.append( " 0 obj\n" 5845 "<</Type/Catalog/Pages " ); 5846 aLine.append( nTreeNode ); 5847 aLine.append( " 0 R\n" ); 5848 //--->i56629 5849 //check if there are named destinations to emit (root must be inside the catalog) 5850 if( nNamedDestinationsDictionary ) 5851 { 5852 aLine.append("/Dests "); 5853 aLine.append( nNamedDestinationsDictionary ); 5854 aLine.append( " 0 R\n" ); 5855 } 5856 //<---- 5857 if( m_aContext.PageLayout != PDFWriter::DefaultLayout ) 5858 switch( m_aContext.PageLayout ) 5859 { 5860 default : 5861 case PDFWriter::SinglePage : 5862 aLine.append( "/PageLayout/SinglePage\n" ); 5863 break; 5864 case PDFWriter::Continuous : 5865 aLine.append( "/PageLayout/OneColumn\n" ); 5866 break; 5867 case PDFWriter::ContinuousFacing : 5868 //the flag m_aContext.FirstPageLeft below is used to set the page on the left side 5869 aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side 5870 break; 5871 } 5872 if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode ) 5873 switch( m_aContext.PDFDocumentMode ) 5874 { 5875 default : 5876 aLine.append( "/PageMode/UseNone\n" ); 5877 break; 5878 case PDFWriter::UseOutlines : 5879 aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open 5880 break; 5881 case PDFWriter::UseThumbs : 5882 aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open 5883 break; 5884 } 5885 else if( m_aContext.OpenInFullScreenMode ) 5886 aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen 5887 5888 OStringBuffer aInitPageRef; 5889 if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() ) 5890 { 5891 aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject ); 5892 aInitPageRef.append( " 0 R" ); 5893 } 5894 else 5895 aInitPageRef.append( "0" ); 5896 switch( m_aContext.PDFDocumentAction ) 5897 { 5898 case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default 5899 default: 5900 if( aInitPageRef.getLength() > 1 ) 5901 { 5902 aLine.append( "/OpenAction[" ); 5903 aLine.append( aInitPageRef ); 5904 aLine.append( " /XYZ null null 0]\n" ); 5905 } 5906 break; 5907 case PDFWriter::FitInWindow : 5908 aLine.append( "/OpenAction[" ); 5909 aLine.append( aInitPageRef ); 5910 aLine.append( " /Fit]\n" ); //Open fit page 5911 break; 5912 case PDFWriter::FitWidth : 5913 aLine.append( "/OpenAction[" ); 5914 aLine.append( aInitPageRef ); 5915 aLine.append( " /FitH " ); 5916 aLine.append( m_nInheritedPageHeight );//Open fit width 5917 aLine.append( "]\n" ); 5918 break; 5919 case PDFWriter::FitVisible : 5920 aLine.append( "/OpenAction[" ); 5921 aLine.append( aInitPageRef ); 5922 aLine.append( " /FitBH " ); 5923 aLine.append( m_nInheritedPageHeight );//Open fit visible 5924 aLine.append( "]\n" ); 5925 break; 5926 case PDFWriter::ActionZoom : 5927 aLine.append( "/OpenAction[" ); 5928 aLine.append( aInitPageRef ); 5929 aLine.append( " /XYZ null null " ); 5930 if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 ) 5931 aLine.append( (double)m_aContext.Zoom/100.0 ); 5932 else 5933 aLine.append( "0" ); 5934 aLine.append( "]\n" ); 5935 break; 5936 } 5937 // viewer preferences, if we had some, then emit 5938 if( m_aContext.HideViewerToolbar || 5939 ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) || 5940 m_aContext.HideViewerMenubar || 5941 m_aContext.HideViewerWindowControls || m_aContext.FitWindow || 5942 m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) || 5943 m_aContext.OpenInFullScreenMode ) 5944 { 5945 aLine.append( "/ViewerPreferences<<" ); 5946 if( m_aContext.HideViewerToolbar ) 5947 aLine.append( "/HideToolbar true\n" ); 5948 if( m_aContext.HideViewerMenubar ) 5949 aLine.append( "/HideMenubar true\n" ); 5950 if( m_aContext.HideViewerWindowControls ) 5951 aLine.append( "/HideWindowUI true\n" ); 5952 if( m_aContext.FitWindow ) 5953 aLine.append( "/FitWindow true\n" ); 5954 if( m_aContext.CenterWindow ) 5955 aLine.append( "/CenterWindow true\n" ); 5956 if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) 5957 aLine.append( "/DisplayDocTitle true\n" ); 5958 if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) 5959 aLine.append( "/Direction/R2L\n" ); 5960 if( m_aContext.OpenInFullScreenMode ) 5961 switch( m_aContext.PDFDocumentMode ) 5962 { 5963 default : 5964 case PDFWriter::ModeDefault : 5965 aLine.append( "/NonFullScreenPageMode/UseNone\n" ); 5966 break; 5967 case PDFWriter::UseOutlines : 5968 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" ); 5969 break; 5970 case PDFWriter::UseThumbs : 5971 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" ); 5972 break; 5973 } 5974 aLine.append( ">>\n" ); 5975 } 5976 5977 if( nOutlineDict ) 5978 { 5979 aLine.append( "/Outlines " ); 5980 aLine.append( nOutlineDict ); 5981 aLine.append( " 0 R\n" ); 5982 } 5983 if( nStructureDict ) 5984 { 5985 aLine.append( "/StructTreeRoot " ); 5986 aLine.append( nStructureDict ); 5987 aLine.append( " 0 R\n" ); 5988 } 5989 if( m_aContext.DocumentLocale.Language.getLength() > 0 ) 5990 { 5991 OUStringBuffer aLocBuf( 16 ); 5992 aLocBuf.append( m_aContext.DocumentLocale.Language.toAsciiLowerCase() ); 5993 if( m_aContext.DocumentLocale.Country.getLength() > 0 ) 5994 { 5995 aLocBuf.append( sal_Unicode('-') ); 5996 aLocBuf.append( m_aContext.DocumentLocale.Country ); 5997 } 5998 aLine.append( "/Lang" ); 5999 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine ); 6000 aLine.append( "\n" ); 6001 } 6002 if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 ) 6003 { 6004 aLine.append( "/MarkInfo<</Marked true>>\n" ); 6005 } 6006 if( m_aWidgets.size() > 0 ) 6007 { 6008 aLine.append( "/AcroForm<</Fields[\n" ); 6009 int nWidgets = m_aWidgets.size(); 6010 int nOut = 0; 6011 for( int j = 0; j < nWidgets; j++ ) 6012 { 6013 // output only root fields 6014 if( m_aWidgets[j].m_nParent < 1 ) 6015 { 6016 aLine.append( m_aWidgets[j].m_nObject ); 6017 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " ); 6018 } 6019 } 6020 aLine.append( "\n]/DR " ); 6021 aLine.append( getResourceDictObj() ); 6022 aLine.append( " 0 R" ); 6023 if( m_bIsPDF_A1 ) 6024 aLine.append( ">>\n" ); 6025 else 6026 aLine.append( "/NeedAppearances true>>\n" ); 6027 } 6028 //--->i59651 6029 //check if there is a Metadata object 6030 if( nOutputIntentObject ) 6031 { 6032 aLine.append("/OutputIntents["); 6033 aLine.append( nOutputIntentObject ); 6034 aLine.append( " 0 R]" ); 6035 } 6036 if( nMetadataObject ) 6037 { 6038 aLine.append("/Metadata "); 6039 aLine.append( nMetadataObject ); 6040 aLine.append( " 0 R" ); 6041 } 6042 //<---- 6043 aLine.append( ">>\n" 6044 "endobj\n\n" ); 6045 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6046 6047 return true; 6048 } 6049 6050 sal_Int32 PDFWriterImpl::emitInfoDict( ) 6051 { 6052 sal_Int32 nObject = createObject(); 6053 6054 if( updateObject( nObject ) ) 6055 { 6056 OStringBuffer aLine( 1024 ); 6057 aLine.append( nObject ); 6058 aLine.append( " 0 obj\n" 6059 "<<" ); 6060 if( m_aContext.DocumentInfo.Title.Len() ) 6061 { 6062 aLine.append( "/Title" ); 6063 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine ); 6064 aLine.append( "\n" ); 6065 } 6066 if( m_aContext.DocumentInfo.Author.Len() ) 6067 { 6068 aLine.append( "/Author" ); 6069 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine ); 6070 aLine.append( "\n" ); 6071 } 6072 if( m_aContext.DocumentInfo.Subject.Len() ) 6073 { 6074 aLine.append( "/Subject" ); 6075 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine ); 6076 aLine.append( "\n" ); 6077 } 6078 if( m_aContext.DocumentInfo.Keywords.Len() ) 6079 { 6080 aLine.append( "/Keywords" ); 6081 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine ); 6082 aLine.append( "\n" ); 6083 } 6084 if( m_aContext.DocumentInfo.Creator.Len() ) 6085 { 6086 aLine.append( "/Creator" ); 6087 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine ); 6088 aLine.append( "\n" ); 6089 } 6090 if( m_aContext.DocumentInfo.Producer.Len() ) 6091 { 6092 aLine.append( "/Producer" ); 6093 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine ); 6094 aLine.append( "\n" ); 6095 } 6096 6097 aLine.append( "/CreationDate" ); 6098 appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine ); 6099 aLine.append( ">>\nendobj\n\n" ); 6100 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6101 nObject = 0; 6102 } 6103 else 6104 nObject = 0; 6105 6106 return nObject; 6107 } 6108 6109 //--->i56629 6110 // Part of this function may be shared with method appendDest. 6111 // 6112 sal_Int32 PDFWriterImpl::emitNamedDestinations() 6113 { 6114 sal_Int32 nCount = m_aNamedDests.size(); 6115 if( nCount <= 0 ) 6116 return 0;//define internal error 6117 6118 //get the object number for all the destinations 6119 sal_Int32 nObject = createObject(); 6120 6121 if( updateObject( nObject ) ) 6122 { 6123 //emit the dictionary 6124 OStringBuffer aLine( 1024 ); 6125 aLine.append( nObject ); 6126 aLine.append( " 0 obj\n" 6127 "<<" ); 6128 6129 sal_Int32 nDestID; 6130 for( nDestID = 0; nDestID < nCount; nDestID++ ) 6131 { 6132 const PDFNamedDest& rDest = m_aNamedDests[ nDestID ]; 6133 // In order to correctly function both under an Internet browser and 6134 // directly with a reader (provided the reader has the feature) we 6135 // need to set the name of the destination the same way it will be encoded 6136 // in an Internet link 6137 INetURLObject aLocalURL( 6138 OUString( RTL_CONSTASCII_USTRINGPARAM( "http://ahost.ax" ) ) ); //dummy location, won't be used 6139 aLocalURL.SetMark( rDest.m_aDestName ); 6140 6141 const rtl::OUString aName = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as 6142 // in link creation ( see PDFWriterImpl::emitLinkAnnotations ) 6143 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ]; 6144 6145 aLine.append( '/' ); 6146 appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog ) 6147 aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function 6148 //maps the preceeding character properly 6149 aLine.append( rDestPage.m_nPageObject ); 6150 aLine.append( " 0 R" ); 6151 6152 switch( rDest.m_eType ) 6153 { 6154 case PDFWriter::XYZ: 6155 default: 6156 aLine.append( "/XYZ " ); 6157 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6158 aLine.append( ' ' ); 6159 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6160 aLine.append( " 0" ); 6161 break; 6162 case PDFWriter::Fit: 6163 aLine.append( "/Fit" ); 6164 break; 6165 case PDFWriter::FitRectangle: 6166 aLine.append( "/FitR " ); 6167 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6168 aLine.append( ' ' ); 6169 appendFixedInt( rDest.m_aRect.Top(), aLine ); 6170 aLine.append( ' ' ); 6171 appendFixedInt( rDest.m_aRect.Right(), aLine ); 6172 aLine.append( ' ' ); 6173 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6174 break; 6175 case PDFWriter::FitHorizontal: 6176 aLine.append( "/FitH " ); 6177 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6178 break; 6179 case PDFWriter::FitVertical: 6180 aLine.append( "/FitV " ); 6181 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6182 break; 6183 case PDFWriter::FitPageBoundingBox: 6184 aLine.append( "/FitB" ); 6185 break; 6186 case PDFWriter::FitPageBoundingBoxHorizontal: 6187 aLine.append( "/FitBH " ); 6188 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6189 break; 6190 case PDFWriter::FitPageBoundingBoxVertical: 6191 aLine.append( "/FitBV " ); 6192 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6193 break; 6194 } 6195 aLine.append( "]\n" ); 6196 } 6197 //close 6198 6199 aLine.append( ">>\nendobj\n\n" ); 6200 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6201 nObject = 0; 6202 } 6203 else 6204 nObject = 0; 6205 6206 return nObject; 6207 } 6208 //<--- i56629 6209 6210 //--->i59651 6211 // emits the output intent dictionary 6212 6213 sal_Int32 PDFWriterImpl::emitOutputIntent() 6214 { 6215 if( !m_bIsPDF_A1 ) 6216 return 0; 6217 6218 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1 6219 6220 OStringBuffer aLine( 1024 ); 6221 sal_Int32 nICCObject = createObject(); 6222 sal_Int32 nStreamLengthObject = createObject(); 6223 6224 aLine.append( nICCObject ); 6225 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16) 6226 aLine.append( " 0 obj\n<</N 3/Length " ); 6227 aLine.append( nStreamLengthObject ); 6228 aLine.append( " 0 R" ); 6229 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 6230 aLine.append( "/Filter/FlateDecode" ); 6231 #endif 6232 aLine.append( ">>\nstream\n" ); 6233 CHECK_RETURN( updateObject( nICCObject ) ); 6234 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6235 //get file position 6236 sal_uInt64 nBeginStreamPos = 0; 6237 osl_getFilePos( m_aFile, &nBeginStreamPos ); 6238 beginCompression(); 6239 checkAndEnableStreamEncryption( nICCObject ); 6240 sal_Int32 nStreamSize = writeBuffer( nsRGB_ICC_profile, (sal_Int32) sizeof( nsRGB_ICC_profile ) ); 6241 disableStreamEncryption(); 6242 endCompression(); 6243 sal_uInt64 nEndStreamPos = 0; 6244 osl_getFilePos( m_aFile, &nEndStreamPos ); 6245 6246 if( nStreamSize == 0 ) 6247 return 0; 6248 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) 6249 return 0 ; 6250 aLine.setLength( 0 ); 6251 6252 //emit the stream length object 6253 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 6254 aLine.setLength( 0 ); 6255 aLine.append( nStreamLengthObject ); 6256 aLine.append( " 0 obj\n" ); 6257 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) ); 6258 aLine.append( "\nendobj\n\n" ); 6259 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6260 aLine.setLength( 0 ); 6261 6262 //emit the OutputIntent dictionary 6263 sal_Int32 nOIObject = createObject(); 6264 CHECK_RETURN( updateObject( nOIObject ) ); 6265 aLine.append( nOIObject ); 6266 aLine.append( " 0 obj\n" 6267 "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier"); 6268 6269 rtl::OUString aComment( RTL_CONSTASCII_USTRINGPARAM( "sRGB IEC61966-2.1" ) ); 6270 appendLiteralStringEncrypt( aComment ,nOIObject, aLine ); 6271 aLine.append("/DestOutputProfile "); 6272 aLine.append( nICCObject ); 6273 aLine.append( " 0 R>>\nendobj\n\n" );; 6274 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6275 6276 return nOIObject; 6277 } 6278 6279 // formats the string for the XML stream 6280 static void escapeStringXML( const rtl::OUString& rStr, rtl::OUString &rValue) 6281 { 6282 const sal_Unicode* pUni = rStr.getStr(); 6283 int nLen = rStr.getLength(); 6284 for( ; nLen; nLen--, pUni++ ) 6285 { 6286 switch( *pUni ) 6287 { 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 case sal_Unicode('"'): 6301 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( """ ) ); 6302 break; 6303 default: 6304 rValue += rtl::OUString( *pUni ); 6305 break; 6306 } 6307 } 6308 } 6309 6310 // emits the document metadata 6311 // 6312 sal_Int32 PDFWriterImpl::emitDocumentMetadata() 6313 { 6314 if( !m_bIsPDF_A1 ) 6315 return 0; 6316 6317 //get the object number for all the destinations 6318 sal_Int32 nObject = createObject(); 6319 6320 if( updateObject( nObject ) ) 6321 { 6322 // the following string are written in UTF-8 unicode 6323 OStringBuffer aMetadataStream( 8192 ); 6324 6325 aMetadataStream.append( "<?xpacket begin=\"" ); 6326 // this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used 6327 // as a byte-order marker. 6328 aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) ); 6329 aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" ); 6330 aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" ); 6331 aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" ); 6332 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 ) 6333 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6334 aMetadataStream.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" ); 6335 aMetadataStream.append( " <pdfaid:part>1</pdfaid:part>\n" ); 6336 aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" ); 6337 aMetadataStream.append( " </rdf:Description>\n" ); 6338 //... Dublin Core properties go here 6339 if( m_aContext.DocumentInfo.Title.Len() || 6340 m_aContext.DocumentInfo.Author.Len() || 6341 m_aContext.DocumentInfo.Subject.Len() ) 6342 { 6343 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6344 aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" ); 6345 if( m_aContext.DocumentInfo.Title.Len() ) 6346 { 6347 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) 6348 aMetadataStream.append( " <dc:title>\n" ); 6349 aMetadataStream.append( " <rdf:Alt>\n" ); 6350 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); 6351 rtl::OUString aTitle; 6352 escapeStringXML( m_aContext.DocumentInfo.Title, aTitle ); 6353 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) ); 6354 aMetadataStream.append( "</rdf:li>\n" ); 6355 aMetadataStream.append( " </rdf:Alt>\n" ); 6356 aMetadataStream.append( " </dc:title>\n" ); 6357 } 6358 if( m_aContext.DocumentInfo.Author.Len() ) 6359 { 6360 aMetadataStream.append( " <dc:creator>\n" ); 6361 aMetadataStream.append( " <rdf:Seq>\n" ); 6362 aMetadataStream.append( " <rdf:li>" ); 6363 rtl::OUString aAuthor; 6364 escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor ); 6365 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) ); 6366 aMetadataStream.append( "</rdf:li>\n" ); 6367 aMetadataStream.append( " </rdf:Seq>\n" ); 6368 aMetadataStream.append( " </dc:creator>\n" ); 6369 } 6370 if( m_aContext.DocumentInfo.Subject.Len() ) 6371 { 6372 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) 6373 aMetadataStream.append( " <dc:description>\n" ); 6374 aMetadataStream.append( " <rdf:Alt>\n" ); 6375 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); 6376 rtl::OUString aSubject; 6377 escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject ); 6378 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) ); 6379 aMetadataStream.append( "</rdf:li>\n" ); 6380 aMetadataStream.append( " </rdf:Alt>\n" ); 6381 aMetadataStream.append( " </dc:description>\n" ); 6382 } 6383 aMetadataStream.append( " </rdf:Description>\n" ); 6384 } 6385 6386 //... PDF properties go here 6387 if( m_aContext.DocumentInfo.Producer.Len() || 6388 m_aContext.DocumentInfo.Keywords.Len() ) 6389 { 6390 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6391 aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" ); 6392 if( m_aContext.DocumentInfo.Producer.Len() ) 6393 { 6394 aMetadataStream.append( " <pdf:Producer>" ); 6395 rtl::OUString aProducer; 6396 escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer ); 6397 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) ); 6398 aMetadataStream.append( "</pdf:Producer>\n" ); 6399 } 6400 if( m_aContext.DocumentInfo.Keywords.Len() ) 6401 { 6402 aMetadataStream.append( " <pdf:Keywords>" ); 6403 rtl::OUString aKeywords; 6404 escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords ); 6405 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) ); 6406 aMetadataStream.append( "</pdf:Keywords>\n" ); 6407 } 6408 aMetadataStream.append( " </rdf:Description>\n" ); 6409 } 6410 6411 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6412 aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" ); 6413 if( m_aContext.DocumentInfo.Creator.Len() ) 6414 { 6415 aMetadataStream.append( " <xmp:CreatorTool>" ); 6416 rtl::OUString aCreator; 6417 escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator ); 6418 aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) ); 6419 aMetadataStream.append( "</xmp:CreatorTool>\n" ); 6420 } 6421 //creation date 6422 aMetadataStream.append( " <xmp:CreateDate>" ); 6423 aMetadataStream.append( m_aCreationMetaDateString ); 6424 aMetadataStream.append( "</xmp:CreateDate>\n" ); 6425 6426 aMetadataStream.append( " </rdf:Description>\n" ); 6427 aMetadataStream.append( " </rdf:RDF>\n" ); 6428 aMetadataStream.append( "</x:xmpmeta>\n" ); 6429 6430 //add the padding 6431 for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ ) 6432 { 6433 aMetadataStream.append( " " ); 6434 if( nSpaces % 100 == 0 ) 6435 aMetadataStream.append( "\n" ); 6436 } 6437 6438 aMetadataStream.append( "<?xpacket end=\"w\"?>\n" ); 6439 6440 OStringBuffer aMetadataObj( 1024 ); 6441 6442 aMetadataObj.append( nObject ); 6443 aMetadataObj.append( " 0 obj\n" ); 6444 6445 aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " ); 6446 6447 aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() ); 6448 aMetadataObj.append( ">>\nstream\n" ); 6449 CHECK_RETURN( writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) ); 6450 //emit the stream 6451 CHECK_RETURN( writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) ); 6452 6453 aMetadataObj.setLength( 0 ); 6454 aMetadataObj.append( "\nendstream\nendobj\n\n" ); 6455 if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) ) 6456 nObject = 0; 6457 } 6458 else 6459 nObject = 0; 6460 6461 return nObject; 6462 } 6463 //<---i59651 6464 6465 bool PDFWriterImpl::emitTrailer() 6466 { 6467 // emit doc info 6468 OString aInfoValuesOut; 6469 sal_Int32 nDocInfoObject = emitInfoDict( ); 6470 6471 sal_Int32 nSecObject = 0; 6472 6473 if( m_aContext.Encryption.Encrypt() ) 6474 { 6475 //emit the security information 6476 //must be emitted as indirect dictionary object, since 6477 //Acrobat Reader 5 works only with this kind of implementation 6478 nSecObject = createObject(); 6479 6480 if( updateObject( nSecObject ) ) 6481 { 6482 OStringBuffer aLineS( 1024 ); 6483 aLineS.append( nSecObject ); 6484 aLineS.append( " 0 obj\n" 6485 "<</Filter/Standard/V " ); 6486 // check the version 6487 if( m_aContext.Encryption.Security128bit ) 6488 aLineS.append( "2/Length 128/R 3" ); 6489 else 6490 aLineS.append( "1/R 2" ); 6491 6492 // emit the owner password, must not be encrypted 6493 aLineS.append( "/O(" ); 6494 appendLiteralString( (const sal_Char*)&m_aContext.Encryption.OValue[0], sal_Int32(m_aContext.Encryption.OValue.size()), aLineS ); 6495 aLineS.append( ")/U(" ); 6496 appendLiteralString( (const sal_Char*)&m_aContext.Encryption.UValue[0], sal_Int32(m_aContext.Encryption.UValue.size()), aLineS ); 6497 aLineS.append( ")/P " );// the permission set 6498 aLineS.append( m_nAccessPermissions ); 6499 aLineS.append( ">>\nendobj\n\n" ); 6500 if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) ) 6501 nSecObject = 0; 6502 } 6503 else 6504 nSecObject = 0; 6505 } 6506 // emit xref table 6507 // remember start 6508 sal_uInt64 nXRefOffset = 0; 6509 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) ); 6510 CHECK_RETURN( writeBuffer( "xref\n", 5 ) ); 6511 6512 sal_Int32 nObjects = m_aObjects.size(); 6513 OStringBuffer aLine; 6514 aLine.append( "0 " ); 6515 aLine.append( (sal_Int32)(nObjects+1) ); 6516 aLine.append( "\n" ); 6517 aLine.append( "0000000000 65535 f \n" ); 6518 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6519 6520 for( sal_Int32 i = 0; i < nObjects; i++ ) 6521 { 6522 aLine.setLength( 0 ); 6523 OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] ); 6524 for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ ) 6525 aLine.append( '0' ); 6526 aLine.append( aOffset ); 6527 aLine.append( " 00000 n \n" ); 6528 DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" ); 6529 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6530 } 6531 6532 // prepare document checksum 6533 OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 ); 6534 if( m_aDocDigest ) 6535 { 6536 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; 6537 rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) ); 6538 for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ ) 6539 appendHex( nMD5Sum[i], aDocChecksum ); 6540 } 6541 // document id set in setDocInfo method 6542 // emit trailer 6543 aLine.setLength( 0 ); 6544 aLine.append( "trailer\n" 6545 "<</Size " ); 6546 aLine.append( (sal_Int32)(nObjects+1) ); 6547 aLine.append( "/Root " ); 6548 aLine.append( m_nCatalogObject ); 6549 aLine.append( " 0 R\n" ); 6550 if( nSecObject |= 0 ) 6551 { 6552 aLine.append( "/Encrypt "); 6553 aLine.append( nSecObject ); 6554 aLine.append( " 0 R\n" ); 6555 } 6556 if( nDocInfoObject ) 6557 { 6558 aLine.append( "/Info " ); 6559 aLine.append( nDocInfoObject ); 6560 aLine.append( " 0 R\n" ); 6561 } 6562 if( ! m_aContext.Encryption.DocumentIdentifier.empty() ) 6563 { 6564 aLine.append( "/ID [ <" ); 6565 for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); 6566 it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) 6567 { 6568 appendHex( sal_Int8(*it), aLine ); 6569 } 6570 aLine.append( ">\n" 6571 "<" ); 6572 for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); 6573 it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) 6574 { 6575 appendHex( sal_Int8(*it), aLine ); 6576 } 6577 aLine.append( "> ]\n" ); 6578 } 6579 if( aDocChecksum.getLength() ) 6580 { 6581 aLine.append( "/DocChecksum /" ); 6582 aLine.append( aDocChecksum ); 6583 aLine.append( "\n" ); 6584 } 6585 if( m_aAdditionalStreams.size() > 0 ) 6586 { 6587 aLine.append( "/AdditionalStreams [" ); 6588 for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ ) 6589 { 6590 aLine.append( "/" ); 6591 appendName( m_aAdditionalStreams[i].m_aMimeType, aLine ); 6592 aLine.append( " " ); 6593 aLine.append( m_aAdditionalStreams[i].m_nStreamObject ); 6594 aLine.append( " 0 R\n" ); 6595 } 6596 aLine.append( "]\n" ); 6597 } 6598 aLine.append( ">>\n" 6599 "startxref\n" ); 6600 aLine.append( (sal_Int64)nXRefOffset ); 6601 aLine.append( "\n" 6602 "%%EOF\n" ); 6603 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6604 6605 return true; 6606 } 6607 6608 struct AnnotationSortEntry 6609 { 6610 sal_Int32 nTabOrder; 6611 sal_Int32 nObject; 6612 sal_Int32 nWidgetIndex; 6613 6614 AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) : 6615 nTabOrder( nTab ), 6616 nObject( nObj ), 6617 nWidgetIndex( nI ) 6618 {} 6619 }; 6620 6621 struct AnnotSortContainer 6622 { 6623 std::set< sal_Int32 > aObjects; 6624 std::vector< AnnotationSortEntry > aSortedAnnots; 6625 }; 6626 6627 struct AnnotSorterLess 6628 { 6629 std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets; 6630 6631 AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {} 6632 6633 bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight ) 6634 { 6635 if( rLeft.nTabOrder < rRight.nTabOrder ) 6636 return true; 6637 if( rRight.nTabOrder < rLeft.nTabOrder ) 6638 return false; 6639 if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 ) 6640 return false; 6641 if( rRight.nWidgetIndex < 0 ) 6642 return true; 6643 if( rLeft.nWidgetIndex < 0 ) 6644 return false; 6645 // remember: widget rects are in PDF coordinates, so they are ordered down up 6646 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() > 6647 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() ) 6648 return true; 6649 if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() > 6650 m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() ) 6651 return false; 6652 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() < 6653 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() ) 6654 return true; 6655 return false; 6656 } 6657 }; 6658 6659 void PDFWriterImpl::sortWidgets() 6660 { 6661 // sort widget annotations on each page as per their 6662 // TabOrder attribute 6663 std::hash_map< sal_Int32, AnnotSortContainer > sorted; 6664 int nWidgets = m_aWidgets.size(); 6665 for( int nW = 0; nW < nWidgets; nW++ ) 6666 { 6667 const PDFWidget& rWidget = m_aWidgets[nW]; 6668 if( rWidget.m_nPage >= 0 ) 6669 { 6670 AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ]; 6671 // optimize vector allocation 6672 if( rCont.aSortedAnnots.empty() ) 6673 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() ); 6674 // insert widget to tab sorter 6675 // RadioButtons are not page annotations, only their individual check boxes are 6676 if( rWidget.m_eType != PDFWriter::RadioButton ) 6677 { 6678 rCont.aObjects.insert( rWidget.m_nObject ); 6679 rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) ); 6680 } 6681 } 6682 } 6683 for( std::hash_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it ) 6684 { 6685 // append entries for non widget annotations 6686 PDFPage& rPage = m_aPages[ it->first ]; 6687 unsigned int nAnnots = rPage.m_aAnnotations.size(); 6688 for( unsigned int nA = 0; nA < nAnnots; nA++ ) 6689 if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end()) 6690 it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) ); 6691 6692 AnnotSorterLess aLess( m_aWidgets ); 6693 std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess ); 6694 // sanity check 6695 if( it->second.aSortedAnnots.size() == nAnnots) 6696 { 6697 for( unsigned int nA = 0; nA < nAnnots; nA++ ) 6698 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject; 6699 } 6700 else 6701 { 6702 DBG_ASSERT( 0, "wrong number of sorted annotations" ); 6703 #if OSL_DEBUG_LEVEL > 0 6704 fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n" 6705 " %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots ); 6706 #endif 6707 } 6708 } 6709 6710 // FIXME: implement tab order in structure tree for PDF 1.5 6711 } 6712 6713 namespace vcl { 6714 class PDFStreamIf : 6715 public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream > 6716 { 6717 PDFWriterImpl* m_pWriter; 6718 bool m_bWrite; 6719 public: 6720 PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {} 6721 virtual ~PDFStreamIf(); 6722 6723 virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw(); 6724 virtual void SAL_CALL flush() throw(); 6725 virtual void SAL_CALL closeOutput() throw(); 6726 }; 6727 } 6728 6729 PDFStreamIf::~PDFStreamIf() 6730 { 6731 } 6732 6733 void SAL_CALL PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw() 6734 { 6735 if( m_bWrite ) 6736 { 6737 sal_Int32 nBytes = aData.getLength(); 6738 if( nBytes > 0 ) 6739 m_pWriter->writeBuffer( aData.getConstArray(), nBytes ); 6740 } 6741 } 6742 6743 void SAL_CALL PDFStreamIf::flush() throw() 6744 { 6745 } 6746 6747 void SAL_CALL PDFStreamIf::closeOutput() throw() 6748 { 6749 m_bWrite = false; 6750 } 6751 6752 bool PDFWriterImpl::emitAdditionalStreams() 6753 { 6754 unsigned int nStreams = m_aAdditionalStreams.size(); 6755 for( unsigned int i = 0; i < nStreams; i++ ) 6756 { 6757 PDFAddStream& rStream = m_aAdditionalStreams[i]; 6758 rStream.m_nStreamObject = createObject(); 6759 sal_Int32 nSizeObject = createObject(); 6760 6761 if( ! updateObject( rStream.m_nStreamObject ) ) 6762 return false; 6763 6764 OStringBuffer aLine; 6765 aLine.append( rStream.m_nStreamObject ); 6766 aLine.append( " 0 obj\n<</Length " ); 6767 aLine.append( nSizeObject ); 6768 aLine.append( " 0 R" ); 6769 if( rStream.m_bCompress ) 6770 aLine.append( "/Filter/FlateDecode" ); 6771 aLine.append( ">>\nstream\n" ); 6772 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6773 return false; 6774 sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0; 6775 if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) ) 6776 { 6777 osl_closeFile( m_aFile ); 6778 m_bOpen = false; 6779 } 6780 if( rStream.m_bCompress ) 6781 beginCompression(); 6782 6783 checkAndEnableStreamEncryption( rStream.m_nStreamObject ); 6784 com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) ); 6785 rStream.m_pStream->write( xStream ); 6786 xStream.clear(); 6787 delete rStream.m_pStream; 6788 rStream.m_pStream = NULL; 6789 disableStreamEncryption(); 6790 6791 if( rStream.m_bCompress ) 6792 endCompression(); 6793 6794 if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) ) 6795 { 6796 osl_closeFile( m_aFile ); 6797 m_bOpen = false; 6798 return false; 6799 } 6800 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) 6801 return false ; 6802 // emit stream length object 6803 if( ! updateObject( nSizeObject ) ) 6804 return false; 6805 aLine.setLength( 0 ); 6806 aLine.append( nSizeObject ); 6807 aLine.append( " 0 obj\n" ); 6808 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) ); 6809 aLine.append( "\nendobj\n\n" ); 6810 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6811 return false; 6812 } 6813 return true; 6814 } 6815 6816 bool PDFWriterImpl::emit() 6817 { 6818 endPage(); 6819 6820 // resort structure tree and annotations if necessary 6821 // needed for widget tab order 6822 sortWidgets(); 6823 6824 // emit additional streams 6825 CHECK_RETURN( emitAdditionalStreams() ); 6826 6827 // emit catalog 6828 CHECK_RETURN( emitCatalog() ); 6829 6830 // emit trailer 6831 CHECK_RETURN( emitTrailer() ); 6832 6833 osl_closeFile( m_aFile ); 6834 m_bOpen = false; 6835 6836 return true; 6837 } 6838 6839 std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors() 6840 { 6841 return m_aErrors; 6842 } 6843 6844 sal_Int32 PDFWriterImpl::getSystemFont( const Font& i_rFont ) 6845 { 6846 getReferenceDevice()->Push(); 6847 getReferenceDevice()->SetFont( i_rFont ); 6848 getReferenceDevice()->ImplNewFont(); 6849 6850 const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData; 6851 sal_Int32 nFontID = 0; 6852 FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont ); 6853 if( it != m_aSystemFonts.end() ) 6854 nFontID = it->second.m_nNormalFontID; 6855 else 6856 { 6857 nFontID = m_nNextFID++; 6858 m_aSystemFonts[ pDevFont ] = EmbedFont(); 6859 m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID; 6860 } 6861 6862 getReferenceDevice()->Pop(); 6863 getReferenceDevice()->ImplNewFont(); 6864 6865 return nFontID; 6866 } 6867 6868 void PDFWriterImpl::registerGlyphs( int nGlyphs, 6869 sal_GlyphId* pGlyphs, 6870 sal_Int32* pGlyphWidths, 6871 sal_Ucs* pUnicodes, 6872 sal_Int32* pUnicodesPerGlyph, 6873 sal_uInt8* pMappedGlyphs, 6874 sal_Int32* pMappedFontObjects, 6875 const ImplFontData* pFallbackFonts[] ) 6876 { 6877 const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData; 6878 sal_Ucs* pCurUnicode = pUnicodes; 6879 for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ ) 6880 { 6881 const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB); 6882 const ImplFontData* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont; 6883 6884 if( isBuiltinFont( pCurrentFont ) ) 6885 { 6886 sal_Int32 nFontID = 0; 6887 FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont ); 6888 if( it != m_aEmbeddedFonts.end() ) 6889 nFontID = it->second.m_nNormalFontID; 6890 else 6891 { 6892 nFontID = m_nNextFID++; 6893 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont(); 6894 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID; 6895 } 6896 6897 pGlyphWidths[ i ] = 0; 6898 pMappedGlyphs[ i ] = sal::static_int_cast<sal_Int8>( nFontGlyphId ); 6899 pMappedFontObjects[ i ] = nFontID; 6900 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pCurrentFont ); 6901 if( pFD ) 6902 { 6903 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); 6904 pGlyphWidths[i] = pBuiltinFont->m_aWidths[ nFontGlyphId & 0x00ff ]; 6905 } 6906 } 6907 else if( pCurrentFont->mbSubsettable ) 6908 { 6909 FontSubset& rSubset = m_aSubsets[ pCurrentFont ]; 6910 // search for font specific glyphID 6911 FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId ); 6912 if( it != rSubset.m_aMapping.end() ) 6913 { 6914 pMappedFontObjects[i] = it->second.m_nFontID; 6915 pMappedGlyphs[i] = it->second.m_nSubsetGlyphID; 6916 } 6917 else 6918 { 6919 // create new subset if necessary 6920 if( rSubset.m_aSubsets.empty() 6921 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) ) 6922 { 6923 rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) ); 6924 } 6925 6926 // copy font id 6927 pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID; 6928 // create new glyph in subset 6929 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1); 6930 pMappedGlyphs[i] = nNewId; 6931 6932 // add new glyph to emitted font subset 6933 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ]; 6934 rNewGlyphEmit.setGlyphId( nNewId ); 6935 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ ) 6936 rNewGlyphEmit.addCode( pCurUnicode[n] ); 6937 6938 // add new glyph to font mapping 6939 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ]; 6940 rNewGlyph.m_nFontID = pMappedFontObjects[i]; 6941 rNewGlyph.m_nSubsetGlyphID = nNewId; 6942 } 6943 getReferenceDevice()->ImplGetGraphics(); 6944 const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0); 6945 pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont, 6946 nFontGlyphId, 6947 bVertical, 6948 m_pReferenceDevice->mpGraphics ); 6949 } 6950 else if( pCurrentFont->IsEmbeddable() ) 6951 { 6952 sal_Int32 nFontID = 0; 6953 FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont ); 6954 if( it != m_aEmbeddedFonts.end() ) 6955 nFontID = it->second.m_nNormalFontID; 6956 else 6957 { 6958 nFontID = m_nNextFID++; 6959 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont(); 6960 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID; 6961 } 6962 EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont]; 6963 6964 const Ucs2SIntMap* pEncoding = NULL; 6965 const Ucs2OStrMap* pNonEncoded = NULL; 6966 getReferenceDevice()->ImplGetGraphics(); 6967 pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded ); 6968 6969 Ucs2SIntMap::const_iterator enc_it; 6970 Ucs2OStrMap::const_iterator nonenc_it; 6971 6972 sal_Int32 nCurFontID = nFontID; 6973 sal_Ucs cChar = *pCurUnicode; 6974 if( pEncoding ) 6975 { 6976 enc_it = pEncoding->find( cChar ); 6977 if( enc_it != pEncoding->end() && enc_it->second > 0 ) 6978 { 6979 DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" ); 6980 cChar = (sal_Ucs)enc_it->second; 6981 } 6982 else if( (enc_it == pEncoding->end() || enc_it->second == -1) && 6983 pNonEncoded && 6984 (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() ) 6985 { 6986 nCurFontID = 0; 6987 // find non encoded glyph 6988 for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it ) 6989 { 6990 if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() ) 6991 { 6992 nCurFontID = nec_it->m_nFontID; 6993 cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ]; 6994 break; 6995 } 6996 } 6997 if( nCurFontID == 0 ) // new nonencoded glyph 6998 { 6999 if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 ) 7000 { 7001 rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() ); 7002 rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++; 7003 } 7004 EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back(); 7005 rEncoding.m_aEncVector.push_back( EmbedCode() ); 7006 rEncoding.m_aEncVector.back().m_aUnicode = cChar; 7007 rEncoding.m_aEncVector.back().m_aName = nonenc_it->second; 7008 rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1); 7009 nCurFontID = rEncoding.m_nFontID; 7010 cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ]; 7011 } 7012 } 7013 else 7014 pEncoding = NULL; 7015 } 7016 if( ! pEncoding ) 7017 { 7018 if( cChar & 0xff00 ) 7019 { 7020 // some characters can be used by conversion 7021 if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area 7022 cChar -= 0xf000; 7023 else 7024 { 7025 String aString(cChar); 7026 ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 ); 7027 cChar = ((sal_Ucs)aChar.GetChar( 0 )) & 0x00ff; 7028 } 7029 } 7030 } 7031 7032 pMappedGlyphs[ i ] = (sal_Int8)cChar; 7033 pMappedFontObjects[ i ] = nCurFontID; 7034 pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont, 7035 (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR, 7036 false, 7037 m_pReferenceDevice->mpGraphics ); 7038 } 7039 } 7040 } 7041 7042 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines ) 7043 { 7044 push( PUSH_ALL ); 7045 7046 FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief(); 7047 7048 Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor(); 7049 Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor; 7050 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 7051 Color aReliefColor( COL_LIGHTGRAY ); 7052 if( aTextColor == COL_BLACK ) 7053 aTextColor = Color( COL_WHITE ); 7054 if( aTextLineColor == COL_BLACK ) 7055 aTextLineColor = Color( COL_WHITE ); 7056 if( aOverlineColor == COL_BLACK ) 7057 aOverlineColor = Color( COL_WHITE ); 7058 if( aTextColor == COL_WHITE ) 7059 aReliefColor = Color( COL_BLACK ); 7060 7061 Font aSetFont = m_aCurrentPDFState.m_aFont; 7062 aSetFont.SetRelief( RELIEF_NONE ); 7063 aSetFont.SetShadow( sal_False ); 7064 7065 aSetFont.SetColor( aReliefColor ); 7066 setTextLineColor( aReliefColor ); 7067 setOverlineColor( aReliefColor ); 7068 setFont( aSetFont ); 7069 long nOff = 1 + getReferenceDevice()->mnDPIX/300; 7070 if( eRelief == RELIEF_ENGRAVED ) 7071 nOff = -nOff; 7072 7073 rLayout.DrawOffset() += Point( nOff, nOff ); 7074 updateGraphicsState(); 7075 drawLayout( rLayout, rText, bTextLines ); 7076 7077 rLayout.DrawOffset() -= Point( nOff, nOff ); 7078 setTextLineColor( aTextLineColor ); 7079 setOverlineColor( aOverlineColor ); 7080 aSetFont.SetColor( aTextColor ); 7081 setFont( aSetFont ); 7082 updateGraphicsState(); 7083 drawLayout( rLayout, rText, bTextLines ); 7084 7085 // clean up the mess 7086 pop(); 7087 } 7088 7089 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines ) 7090 { 7091 Font aSaveFont = m_aCurrentPDFState.m_aFont; 7092 Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor; 7093 Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 7094 7095 Font& rFont = m_aCurrentPDFState.m_aFont; 7096 if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 ) 7097 rFont.SetColor( Color( COL_LIGHTGRAY ) ); 7098 else 7099 rFont.SetColor( Color( COL_BLACK ) ); 7100 rFont.SetShadow( sal_False ); 7101 rFont.SetOutline( sal_False ); 7102 setFont( rFont ); 7103 setTextLineColor( rFont.GetColor() ); 7104 setOverlineColor( rFont.GetColor() ); 7105 updateGraphicsState(); 7106 7107 long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24); 7108 if( rFont.IsOutline() ) 7109 nOff++; 7110 rLayout.DrawBase() += Point( nOff, nOff ); 7111 drawLayout( rLayout, rText, bTextLines ); 7112 rLayout.DrawBase() -= Point( nOff, nOff ); 7113 7114 setFont( aSaveFont ); 7115 setTextLineColor( aSaveTextLineColor ); 7116 setOverlineColor( aSaveOverlineColor ); 7117 updateGraphicsState(); 7118 } 7119 7120 void PDFWriterImpl::drawVerticalGlyphs( 7121 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs, 7122 OStringBuffer& rLine, 7123 const Point& rAlignOffset, 7124 const Matrix3& rRotScale, 7125 double fAngle, 7126 double fXScale, 7127 double fSkew, 7128 sal_Int32 nFontHeight ) 7129 { 7130 long nXOffset = 0; 7131 Point aCurPos( rGlyphs[0].m_aPos ); 7132 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos ); 7133 aCurPos += rAlignOffset; 7134 for( size_t i = 0; i < rGlyphs.size(); i++ ) 7135 { 7136 // have to emit each glyph on its own 7137 double fDeltaAngle = 0.0; 7138 double fYScale = 1.0; 7139 double fTempXScale = fXScale; 7140 double fSkewB = fSkew; 7141 double fSkewA = 0.0; 7142 7143 Point aDeltaPos; 7144 if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL ) 7145 { 7146 fDeltaAngle = M_PI/2.0; 7147 aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent(); 7148 aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale); 7149 fYScale = fXScale; 7150 fTempXScale = 1.0; 7151 fSkewA = -fSkewB; 7152 fSkewB = 0.0; 7153 } 7154 else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR ) 7155 { 7156 fDeltaAngle = -M_PI/2.0; 7157 aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale); 7158 aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent(); 7159 fYScale = fXScale; 7160 fTempXScale = 1.0; 7161 fSkewA = fSkewB; 7162 fSkewB = 0.0; 7163 } 7164 aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) ); 7165 if( i < rGlyphs.size()-1 ) 7166 nXOffset += rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y(); 7167 if( ! rGlyphs[i].m_nGlyphId ) 7168 continue; 7169 7170 aDeltaPos = rRotScale.transform( aDeltaPos ); 7171 7172 Matrix3 aMat; 7173 if( fSkewB != 0.0 || fSkewA != 0.0 ) 7174 aMat.skew( fSkewA, fSkewB ); 7175 aMat.scale( fTempXScale, fYScale ); 7176 aMat.rotate( fAngle+fDeltaAngle ); 7177 aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() ); 7178 aMat.append( m_aPages.back(), rLine ); 7179 rLine.append( " Tm" ); 7180 if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId ) 7181 { 7182 rLine.append( " /F" ); 7183 rLine.append( rGlyphs[i].m_nMappedFontId ); 7184 rLine.append( ' ' ); 7185 m_aPages.back().appendMappedLength( nFontHeight, rLine, true ); 7186 rLine.append( " Tf" ); 7187 } 7188 rLine.append( "<" ); 7189 appendHex( rGlyphs[i].m_nMappedGlyphId, rLine ); 7190 rLine.append( ">Tj\n" ); 7191 } 7192 } 7193 7194 void PDFWriterImpl::drawHorizontalGlyphs( 7195 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs, 7196 OStringBuffer& rLine, 7197 const Point& rAlignOffset, 7198 double fAngle, 7199 double fXScale, 7200 double fSkew, 7201 sal_Int32 nFontHeight, 7202 sal_Int32 nPixelFontHeight 7203 ) 7204 { 7205 // horizontal (= normal) case 7206 7207 // fill in run end indices 7208 // end is marked by index of the first glyph of the next run 7209 // a run is marked by same mapped font id and same Y position 7210 std::vector< sal_uInt32 > aRunEnds; 7211 aRunEnds.reserve( rGlyphs.size() ); 7212 for( size_t i = 1; i < rGlyphs.size(); i++ ) 7213 { 7214 if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId || 7215 rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() ) 7216 { 7217 aRunEnds.push_back(i); 7218 } 7219 } 7220 // last run ends at last glyph 7221 aRunEnds.push_back( rGlyphs.size() ); 7222 7223 // loop over runs of the same font 7224 sal_uInt32 nBeginRun = 0; 7225 for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ ) 7226 { 7227 // setup text matrix 7228 Point aCurPos = rGlyphs[nBeginRun].m_aPos; 7229 // back transformation to current coordinate system 7230 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos ); 7231 aCurPos += rAlignOffset; 7232 // the first run can be set with "Td" operator 7233 // subsequent use of that operator would move 7234 // the texline matrix relative to what was set before 7235 // making use of that would drive us into rounding issues 7236 Matrix3 aMat; 7237 if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 ) 7238 { 7239 m_aPages.back().appendPoint( aCurPos, rLine, false ); 7240 rLine.append( " Td " ); 7241 } 7242 else 7243 { 7244 if( fSkew != 0.0 ) 7245 aMat.skew( 0.0, fSkew ); 7246 aMat.scale( fXScale, 1.0 ); 7247 aMat.rotate( fAngle ); 7248 aMat.translate( aCurPos.X(), aCurPos.Y() ); 7249 aMat.append( m_aPages.back(), rLine ); 7250 rLine.append( " Tm\n" ); 7251 } 7252 // set up correct font 7253 rLine.append( "/F" ); 7254 rLine.append( rGlyphs[nBeginRun].m_nMappedFontId ); 7255 rLine.append( ' ' ); 7256 m_aPages.back().appendMappedLength( nFontHeight, rLine, true ); 7257 rLine.append( " Tf" ); 7258 7259 // output glyphs using Tj or TJ 7260 OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 ); 7261 aKernedLine.append( "[<" ); 7262 aUnkernedLine.append( '<' ); 7263 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine ); 7264 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine ); 7265 7266 aMat.invert(); 7267 bool bNeedKern = false; 7268 for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ ) 7269 { 7270 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine ); 7271 // check if default glyph positioning is sufficient 7272 const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos ); 7273 const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos ); 7274 double fAdvance = aThisPos.X() - aPrevPos.X(); 7275 fAdvance *= 1000.0 / nPixelFontHeight; 7276 const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5); 7277 if( nAdjustment != 0 ) 7278 { 7279 // apply individual glyph positioning 7280 bNeedKern = true; 7281 aKernedLine.append( ">" ); 7282 aKernedLine.append( nAdjustment ); 7283 aKernedLine.append( "<" ); 7284 } 7285 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine ); 7286 } 7287 aKernedLine.append( ">]TJ\n" ); 7288 aUnkernedLine.append( ">Tj\n" ); 7289 rLine.append( bNeedKern ? aKernedLine : aUnkernedLine ); 7290 7291 // set beginning of next run 7292 nBeginRun = aRunEnds[nRun]; 7293 } 7294 } 7295 7296 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines ) 7297 { 7298 // relief takes precedence over shadow (see outdev3.cxx) 7299 if( m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE ) 7300 { 7301 drawRelief( rLayout, rText, bTextLines ); 7302 return; 7303 } 7304 else if( m_aCurrentPDFState.m_aFont.IsShadow() ) 7305 drawShadow( rLayout, rText, bTextLines ); 7306 7307 OStringBuffer aLine( 512 ); 7308 7309 const int nMaxGlyphs = 256; 7310 7311 sal_GlyphId pGlyphs[nMaxGlyphs]; 7312 sal_Int32 pGlyphWidths[nMaxGlyphs]; 7313 sal_uInt8 pMappedGlyphs[nMaxGlyphs]; 7314 sal_Int32 pMappedFontObjects[nMaxGlyphs]; 7315 std::vector<sal_Ucs> aUnicodes; 7316 aUnicodes.reserve( nMaxGlyphs ); 7317 sal_Int32 pUnicodesPerGlyph[nMaxGlyphs]; 7318 int pCharPosAry[nMaxGlyphs]; 7319 sal_Int32 nAdvanceWidths[nMaxGlyphs]; 7320 const ImplFontData* pFallbackFonts[nMaxGlyphs]; 7321 bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical(); 7322 int nGlyphs; 7323 int nIndex = 0; 7324 int nMinCharPos = 0, nMaxCharPos = rText.Len()-1; 7325 double fXScale = 1.0; 7326 double fSkew = 0.0; 7327 sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight; 7328 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); 7329 7330 // transform font height back to current units 7331 // note: the layout calculates in outdevs device pixel !! 7332 sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight ); 7333 if( m_aCurrentPDFState.m_aFont.GetWidth() ) 7334 { 7335 Font aFont( m_aCurrentPDFState.m_aFont ); 7336 aFont.SetWidth( 0 ); 7337 FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont ); 7338 if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() ) 7339 { 7340 fXScale = 7341 (double)m_aCurrentPDFState.m_aFont.GetWidth() / 7342 (double)aMetric.GetWidth(); 7343 } 7344 // force state before GetFontMetric 7345 m_pReferenceDevice->ImplNewFont(); 7346 } 7347 7348 // perform artificial italics if necessary 7349 if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL || 7350 m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) && 7351 !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL || 7352 m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE ) 7353 ) 7354 { 7355 fSkew = M_PI/12.0; 7356 } 7357 7358 // if the mapmode is distorted we need to adjust for that also 7359 if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() ) 7360 { 7361 fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY()); 7362 } 7363 7364 int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation(); 7365 // normalize angles 7366 while( nAngle < 0 ) 7367 nAngle += 3600; 7368 nAngle = nAngle % 3600; 7369 double fAngle = (double)nAngle * M_PI / 1800.0; 7370 7371 Matrix3 aRotScale; 7372 aRotScale.scale( fXScale, 1.0 ); 7373 if( fAngle != 0.0 ) 7374 aRotScale.rotate( -fAngle ); 7375 7376 bool bPop = false; 7377 bool bABold = false; 7378 // artificial bold necessary ? 7379 if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM && 7380 m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM ) 7381 { 7382 if( ! bPop ) 7383 aLine.append( "q " ); 7384 bPop = true; 7385 bABold = true; 7386 } 7387 // setup text colors (if necessary) 7388 Color aStrokeColor( COL_TRANSPARENT ); 7389 Color aNonStrokeColor( COL_TRANSPARENT ); 7390 7391 if( m_aCurrentPDFState.m_aFont.IsOutline() ) 7392 { 7393 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7394 aNonStrokeColor = Color( COL_WHITE ); 7395 } 7396 else 7397 aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7398 if( bABold ) 7399 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7400 7401 if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor ) 7402 { 7403 if( ! bPop ) 7404 aLine.append( "q " ); 7405 bPop = true; 7406 appendStrokingColor( aStrokeColor, aLine ); 7407 aLine.append( "\n" ); 7408 } 7409 if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor ) 7410 { 7411 if( ! bPop ) 7412 aLine.append( "q " ); 7413 bPop = true; 7414 appendNonStrokingColor( aNonStrokeColor, aLine ); 7415 aLine.append( "\n" ); 7416 } 7417 7418 // begin text object 7419 aLine.append( "BT\n" ); 7420 // outline attribute ? 7421 if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold ) 7422 { 7423 // set correct text mode, set stroke width 7424 aLine.append( "2 Tr " ); // fill, then stroke 7425 7426 if( m_aCurrentPDFState.m_aFont.IsOutline() ) 7427 { 7428 // unclear what to do in case of outline and artificial bold 7429 // for the time being outline wins 7430 aLine.append( "0.25 w \n" ); 7431 } 7432 else 7433 { 7434 double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0; 7435 m_aPages.back().appendMappedLength( fW, aLine ); 7436 aLine.append ( " w\n" ); 7437 } 7438 } 7439 7440 FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric(); 7441 7442 // collect the glyphs into a single array 7443 const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686# 7444 std::vector< PDFGlyph > aGlyphs; 7445 aGlyphs.reserve( nTmpMaxGlyphs ); 7446 // first get all the glyphs and register them; coordinates still in Pixel 7447 Point aGNGlyphPos; 7448 while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 ) 7449 { 7450 aUnicodes.clear(); 7451 for( int i = 0; i < nGlyphs; i++ ) 7452 { 7453 pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] ); 7454 7455 // default case: 1 glyph is one unicode 7456 pUnicodesPerGlyph[i] = 1; 7457 if( (pGlyphs[i] & GF_ISCHAR) ) 7458 { 7459 aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) ); 7460 } 7461 else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos ) 7462 { 7463 int nChars = 1; 7464 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) ); 7465 pUnicodesPerGlyph[i] = 1; 7466 // try to handle ligatures and such 7467 if( i < nGlyphs-1 ) 7468 { 7469 nChars = pCharPosAry[i+1] - pCharPosAry[i]; 7470 // #i115618# fix for simple RTL+CTL cases 7471 // TODO: sanitize for RTL ligatures, more complex CTL, etc. 7472 if( nChars < 0 ) 7473 nChars = -nChars; 7474 else if( nChars == 0 ) 7475 nChars = 1; 7476 pUnicodesPerGlyph[i] = nChars; 7477 for( int n = 1; n < nChars; n++ ) 7478 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) ); 7479 } 7480 // #i36691# hack that is needed because currently the pGlyphs[] 7481 // argument is ignored for embeddable fonts and so the layout 7482 // engine's glyph work is ignored (i.e. char mirroring) 7483 // TODO: a real solution would be to map the layout engine's 7484 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font) 7485 // back to unicode and then to embeddable font's encoding 7486 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL ) 7487 { 7488 size_t nI = aUnicodes.size()-1; 7489 for( int n = 0; n < nChars; n++, nI-- ) 7490 aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI])); 7491 } 7492 } 7493 else 7494 aUnicodes.push_back( 0 ); 7495 // note: in case of ctl one character may result 7496 // in multiple glyphs. The current SalLayout 7497 // implementations set -1 then to indicate that no direct 7498 // mapping is possible 7499 } 7500 7501 registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts ); 7502 7503 for( int i = 0; i < nGlyphs; i++ ) 7504 { 7505 aGlyphs.push_back( PDFGlyph( aGNGlyphPos, 7506 pGlyphWidths[i], 7507 pGlyphs[i], 7508 pMappedFontObjects[i], 7509 pMappedGlyphs[i] ) ); 7510 if( bVertical ) 7511 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel(); 7512 else 7513 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel(); 7514 } 7515 } 7516 7517 Point aAlignOffset; 7518 if ( eAlign == ALIGN_BOTTOM ) 7519 aAlignOffset.Y() -= aRefDevFontMetric.GetDescent(); 7520 else if ( eAlign == ALIGN_TOP ) 7521 aAlignOffset.Y() += aRefDevFontMetric.GetAscent(); 7522 if( aAlignOffset.X() || aAlignOffset.Y() ) 7523 aAlignOffset = aRotScale.transform( aAlignOffset ); 7524 7525 /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original 7526 string contained only on of the UTF16 BOMs 7527 */ 7528 if( ! aGlyphs.empty() ) 7529 { 7530 if( bVertical ) 7531 drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight ); 7532 else 7533 drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight ); 7534 } 7535 7536 // end textobject 7537 aLine.append( "ET\n" ); 7538 if( bPop ) 7539 aLine.append( "Q\n" ); 7540 7541 writeBuffer( aLine.getStr(), aLine.getLength() ); 7542 7543 // draw eventual textlines 7544 FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout(); 7545 FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline(); 7546 FontUnderline eOverline = m_aCurrentPDFState.m_aFont.GetOverline(); 7547 if( bTextLines && 7548 ( 7549 ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) || 7550 ( eOverline != UNDERLINE_NONE && eOverline != UNDERLINE_DONTKNOW ) || 7551 ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW ) 7552 ) 7553 ) 7554 { 7555 sal_Bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont ); 7556 if( m_aCurrentPDFState.m_aFont.IsWordLineMode() ) 7557 { 7558 Point aPos, aStartPt; 7559 sal_Int32 nWidth = 0, nAdvance=0; 7560 for( int nStart = 0;;) 7561 { 7562 sal_GlyphId nGlyphIndex; 7563 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) 7564 break; 7565 7566 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) 7567 { 7568 if( !nWidth ) 7569 aStartPt = aPos; 7570 7571 nWidth += nAdvance; 7572 } 7573 else if( nWidth > 0 ) 7574 { 7575 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7576 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7577 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7578 nWidth = 0; 7579 } 7580 } 7581 7582 if( nWidth > 0 ) 7583 { 7584 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7585 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7586 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7587 } 7588 } 7589 else 7590 { 7591 Point aStartPt = rLayout.GetDrawPosition(); 7592 int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel(); 7593 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7594 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7595 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7596 } 7597 } 7598 7599 // write eventual emphasis marks 7600 if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE ) 7601 { 7602 PolyPolygon aEmphPoly; 7603 Rectangle aEmphRect1; 7604 Rectangle aEmphRect2; 7605 long nEmphYOff; 7606 long nEmphWidth; 7607 long nEmphHeight; 7608 sal_Bool bEmphPolyLine; 7609 FontEmphasisMark nEmphMark; 7610 7611 push( PUSH_ALL ); 7612 7613 aLine.setLength( 0 ); 7614 aLine.append( "q\n" ); 7615 7616 nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont ); 7617 if ( nEmphMark & EMPHASISMARK_POS_BELOW ) 7618 nEmphHeight = m_pReferenceDevice->mnEmphasisDescent; 7619 else 7620 nEmphHeight = m_pReferenceDevice->mnEmphasisAscent; 7621 m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly, 7622 bEmphPolyLine, 7623 aEmphRect1, 7624 aEmphRect2, 7625 nEmphYOff, 7626 nEmphWidth, 7627 nEmphMark, 7628 m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight), 7629 m_pReferenceDevice->mpFontEntry->mnOrientation ); 7630 if ( bEmphPolyLine ) 7631 { 7632 setLineColor( m_aCurrentPDFState.m_aFont.GetColor() ); 7633 setFillColor( Color( COL_TRANSPARENT ) ); 7634 } 7635 else 7636 { 7637 setFillColor( m_aCurrentPDFState.m_aFont.GetColor() ); 7638 setLineColor( Color( COL_TRANSPARENT ) ); 7639 } 7640 writeBuffer( aLine.getStr(), aLine.getLength() ); 7641 7642 Point aOffset = Point(0,0); 7643 7644 if ( nEmphMark & EMPHASISMARK_POS_BELOW ) 7645 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff; 7646 else 7647 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff; 7648 7649 long nEmphWidth2 = nEmphWidth / 2; 7650 long nEmphHeight2 = nEmphHeight / 2; 7651 aOffset += Point( nEmphWidth2, nEmphHeight2 ); 7652 7653 if ( eAlign == ALIGN_BOTTOM ) 7654 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent; 7655 else if ( eAlign == ALIGN_TOP ) 7656 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent; 7657 7658 for( int nStart = 0;;) 7659 { 7660 Point aPos; 7661 sal_GlyphId nGlyphIndex; 7662 sal_Int32 nAdvance; 7663 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) 7664 break; 7665 7666 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) 7667 { 7668 Point aAdjOffset = aOffset; 7669 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2; 7670 aAdjOffset = aRotScale.transform( aAdjOffset ); 7671 7672 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 ); 7673 7674 aPos += aAdjOffset; 7675 aPos = m_pReferenceDevice->PixelToLogic( aPos ); 7676 drawEmphasisMark( aPos.X(), aPos.Y(), 7677 aEmphPoly, bEmphPolyLine, 7678 aEmphRect1, aEmphRect2 ); 7679 } 7680 } 7681 7682 writeBuffer( "Q\n", 2 ); 7683 pop(); 7684 } 7685 7686 } 7687 7688 void PDFWriterImpl::drawEmphasisMark( long nX, long nY, 7689 const PolyPolygon& rPolyPoly, sal_Bool bPolyLine, 7690 const Rectangle& rRect1, const Rectangle& rRect2 ) 7691 { 7692 // TODO: pass nWidth as width of this mark 7693 // long nWidth = 0; 7694 7695 if ( rPolyPoly.Count() ) 7696 { 7697 if ( bPolyLine ) 7698 { 7699 Polygon aPoly = rPolyPoly.GetObject( 0 ); 7700 aPoly.Move( nX, nY ); 7701 drawPolyLine( aPoly ); 7702 } 7703 else 7704 { 7705 PolyPolygon aPolyPoly = rPolyPoly; 7706 aPolyPoly.Move( nX, nY ); 7707 drawPolyPolygon( aPolyPoly ); 7708 } 7709 } 7710 7711 if ( !rRect1.IsEmpty() ) 7712 { 7713 Rectangle aRect( Point( nX+rRect1.Left(), 7714 nY+rRect1.Top() ), rRect1.GetSize() ); 7715 drawRectangle( aRect ); 7716 } 7717 7718 if ( !rRect2.IsEmpty() ) 7719 { 7720 Rectangle aRect( Point( nX+rRect2.Left(), 7721 nY+rRect2.Top() ), rRect2.GetSize() ); 7722 7723 drawRectangle( aRect ); 7724 } 7725 } 7726 7727 void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7728 { 7729 MARK( "drawText" ); 7730 7731 updateGraphicsState(); 7732 7733 // get a layout from the OuputDevice's SalGraphics 7734 // this also enforces font substitution and sets the font on SalGraphics 7735 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos ); 7736 if( pLayout ) 7737 { 7738 drawLayout( *pLayout, rText, bTextLines ); 7739 pLayout->Release(); 7740 } 7741 } 7742 7743 void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7744 { 7745 MARK( "drawText with array" ); 7746 7747 updateGraphicsState(); 7748 7749 // get a layout from the OuputDevice's SalGraphics 7750 // this also enforces font substitution and sets the font on SalGraphics 7751 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray ); 7752 if( pLayout ) 7753 { 7754 drawLayout( *pLayout, rText, bTextLines ); 7755 pLayout->Release(); 7756 } 7757 } 7758 7759 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7760 { 7761 MARK( "drawStretchText" ); 7762 7763 updateGraphicsState(); 7764 7765 // get a layout from the OuputDevice's SalGraphics 7766 // this also enforces font substitution and sets the font on SalGraphics 7767 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth ); 7768 if( pLayout ) 7769 { 7770 drawLayout( *pLayout, rText, bTextLines ); 7771 pLayout->Release(); 7772 } 7773 } 7774 7775 void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines ) 7776 { 7777 long nWidth = rRect.GetWidth(); 7778 long nHeight = rRect.GetHeight(); 7779 7780 if ( nWidth <= 0 || nHeight <= 0 ) 7781 return; 7782 7783 MARK( "drawText with rectangle" ); 7784 7785 updateGraphicsState(); 7786 7787 // clip with rectangle 7788 OStringBuffer aLine; 7789 aLine.append( "q " ); 7790 m_aPages.back().appendRect( rRect, aLine ); 7791 aLine.append( " W* n\n" ); 7792 writeBuffer( aLine.getStr(), aLine.getLength() ); 7793 7794 // if disabled text is needed, put in here 7795 7796 Point aPos = rRect.TopLeft(); 7797 7798 long nTextHeight = m_pReferenceDevice->GetTextHeight(); 7799 xub_StrLen nMnemonicPos = STRING_NOTFOUND; 7800 7801 String aStr = rOrigStr; 7802 if ( nStyle & TEXT_DRAW_MNEMONIC ) 7803 aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos ); 7804 7805 // multiline text 7806 if ( nStyle & TEXT_DRAW_MULTILINE ) 7807 { 7808 XubString aLastLine; 7809 ImplMultiTextLineInfo aMultiLineInfo; 7810 ImplTextLineInfo* pLineInfo; 7811 long nMaxTextWidth; 7812 xub_StrLen i; 7813 xub_StrLen nLines; 7814 xub_StrLen nFormatLines; 7815 7816 if ( nTextHeight ) 7817 { 7818 ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice ); 7819 nMaxTextWidth = OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout ); 7820 nLines = (xub_StrLen)(nHeight/nTextHeight); 7821 nFormatLines = aMultiLineInfo.Count(); 7822 if ( !nLines ) 7823 nLines = 1; 7824 if ( nFormatLines > nLines ) 7825 { 7826 if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) 7827 { 7828 // handle last line 7829 nFormatLines = nLines-1; 7830 7831 pLineInfo = aMultiLineInfo.GetLine( nFormatLines ); 7832 aLastLine = aStr.Copy( pLineInfo->GetIndex() ); 7833 aLastLine.ConvertLineEnd( LINEEND_LF ); 7834 // replace line feed by space 7835 xub_StrLen nLastLineLen = aLastLine.Len(); 7836 for ( i = 0; i < nLastLineLen; i++ ) 7837 { 7838 if ( aLastLine.GetChar( i ) == _LF ) 7839 aLastLine.SetChar( i, ' ' ); 7840 } 7841 aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle ); 7842 nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM); 7843 nStyle |= TEXT_DRAW_TOP; 7844 } 7845 } 7846 7847 // vertical alignment 7848 if ( nStyle & TEXT_DRAW_BOTTOM ) 7849 aPos.Y() += nHeight-(nFormatLines*nTextHeight); 7850 else if ( nStyle & TEXT_DRAW_VCENTER ) 7851 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2; 7852 7853 // draw all lines excluding the last 7854 for ( i = 0; i < nFormatLines; i++ ) 7855 { 7856 pLineInfo = aMultiLineInfo.GetLine( i ); 7857 if ( nStyle & TEXT_DRAW_RIGHT ) 7858 aPos.X() += nWidth-pLineInfo->GetWidth(); 7859 else if ( nStyle & TEXT_DRAW_CENTER ) 7860 aPos.X() += (nWidth-pLineInfo->GetWidth())/2; 7861 xub_StrLen nIndex = pLineInfo->GetIndex(); 7862 xub_StrLen nLineLen = pLineInfo->GetLen(); 7863 drawText( aPos, aStr, nIndex, nLineLen, bTextLines ); 7864 // mnemonics should not appear in documents, 7865 // if the need arises, put them in here 7866 aPos.Y() += nTextHeight; 7867 aPos.X() = rRect.Left(); 7868 } 7869 7870 7871 // output last line left adjusted since it was shortened 7872 if ( aLastLine.Len() ) 7873 drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines ); 7874 } 7875 } 7876 else 7877 { 7878 long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); 7879 7880 // Evt. Text kuerzen 7881 if ( nTextWidth > nWidth ) 7882 { 7883 if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) ) 7884 { 7885 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle ); 7886 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT); 7887 nStyle |= TEXT_DRAW_LEFT; 7888 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); 7889 } 7890 } 7891 7892 // vertical alignment 7893 if ( nStyle & TEXT_DRAW_RIGHT ) 7894 aPos.X() += nWidth-nTextWidth; 7895 else if ( nStyle & TEXT_DRAW_CENTER ) 7896 aPos.X() += (nWidth-nTextWidth)/2; 7897 7898 if ( nStyle & TEXT_DRAW_BOTTOM ) 7899 aPos.Y() += nHeight-nTextHeight; 7900 else if ( nStyle & TEXT_DRAW_VCENTER ) 7901 aPos.Y() += (nHeight-nTextHeight)/2; 7902 7903 // mnemonics should be inserted here if the need arises 7904 7905 // draw the actual text 7906 drawText( aPos, aStr, 0, STRING_LEN, bTextLines ); 7907 } 7908 7909 // reset clip region to original value 7910 aLine.setLength( 0 ); 7911 aLine.append( "Q\n" ); 7912 writeBuffer( aLine.getStr(), aLine.getLength() ); 7913 } 7914 7915 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop ) 7916 { 7917 MARK( "drawLine" ); 7918 7919 updateGraphicsState(); 7920 7921 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7922 return; 7923 7924 OStringBuffer aLine; 7925 m_aPages.back().appendPoint( rStart, aLine ); 7926 aLine.append( " m " ); 7927 m_aPages.back().appendPoint( rStop, aLine ); 7928 aLine.append( " l S\n" ); 7929 7930 writeBuffer( aLine.getStr(), aLine.getLength() ); 7931 } 7932 7933 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo ) 7934 { 7935 MARK( "drawLine with LineInfo" ); 7936 updateGraphicsState(); 7937 7938 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7939 return; 7940 7941 if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 ) 7942 { 7943 drawLine( rStart, rStop ); 7944 return; 7945 } 7946 7947 OStringBuffer aLine; 7948 7949 aLine.append( "q " ); 7950 if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) 7951 { 7952 m_aPages.back().appendPoint( rStart, aLine ); 7953 aLine.append( " m " ); 7954 m_aPages.back().appendPoint( rStop, aLine ); 7955 aLine.append( " l S Q\n" ); 7956 7957 writeBuffer( aLine.getStr(), aLine.getLength() ); 7958 } 7959 else 7960 { 7961 PDFWriter::ExtLineInfo aInfo; 7962 convertLineInfoToExtLineInfo( rInfo, aInfo ); 7963 Point aPolyPoints[2] = { rStart, rStop }; 7964 Polygon aPoly( 2, aPolyPoints ); 7965 drawPolyLine( aPoly, aInfo ); 7966 } 7967 } 7968 7969 void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth ) 7970 { 7971 Point aDiff( rStop-rStart ); 7972 double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) ); 7973 if( fLen < 1.0 ) 7974 return; 7975 7976 MARK( "drawWaveLine" ); 7977 updateGraphicsState(); 7978 7979 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7980 return; 7981 7982 OStringBuffer aLine( 512 ); 7983 aLine.append( "q " ); 7984 m_aPages.back().appendMappedLength( nLineWidth, aLine, true ); 7985 aLine.append( " w " ); 7986 7987 appendDouble( (double)aDiff.X()/fLen, aLine ); 7988 aLine.append( ' ' ); 7989 appendDouble( -(double)aDiff.Y()/fLen, aLine ); 7990 aLine.append( ' ' ); 7991 appendDouble( (double)aDiff.Y()/fLen, aLine ); 7992 aLine.append( ' ' ); 7993 appendDouble( (double)aDiff.X()/fLen, aLine ); 7994 aLine.append( ' ' ); 7995 m_aPages.back().appendPoint( rStart, aLine ); 7996 aLine.append( " cm " ); 7997 m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine ); 7998 aLine.append( "Q\n" ); 7999 writeBuffer( aLine.getStr(), aLine.getLength() ); 8000 } 8001 8002 #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x ) 8003 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x ) 8004 8005 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ) 8006 { 8007 // note: units in pFontEntry are ref device pixel 8008 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8009 long nLineHeight = 0; 8010 long nLinePos = 0; 8011 8012 appendStrokingColor( aColor, aLine ); 8013 aLine.append( "\n" ); 8014 8015 if ( bIsAbove ) 8016 { 8017 if ( !pFontEntry->maMetric.mnAboveWUnderlineSize ) 8018 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8019 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize ); 8020 nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset ); 8021 } 8022 else 8023 { 8024 if ( !pFontEntry->maMetric.mnWUnderlineSize ) 8025 m_pReferenceDevice->ImplInitTextLineSize(); 8026 nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize ); 8027 nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset ); 8028 } 8029 if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) ) 8030 nLineHeight = 3; 8031 8032 long nLineWidth = getReferenceDevice()->mnDPIX/450; 8033 if ( ! nLineWidth ) 8034 nLineWidth = 1; 8035 8036 if ( eTextLine == UNDERLINE_BOLDWAVE ) 8037 nLineWidth = 3*nLineWidth; 8038 8039 m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine ); 8040 aLine.append( " w " ); 8041 8042 if ( eTextLine == UNDERLINE_DOUBLEWAVE ) 8043 { 8044 long nOrgLineHeight = nLineHeight; 8045 nLineHeight /= 3; 8046 if ( nLineHeight < 2 ) 8047 { 8048 if ( nOrgLineHeight > 1 ) 8049 nLineHeight = 2; 8050 else 8051 nLineHeight = 1; 8052 } 8053 long nLineDY = nOrgLineHeight-(nLineHeight*2); 8054 if ( nLineDY < nLineWidth ) 8055 nLineDY = nLineWidth; 8056 long nLineDY2 = nLineDY/2; 8057 if ( !nLineDY2 ) 8058 nLineDY2 = 1; 8059 8060 nLinePos -= nLineWidth-nLineDY2; 8061 8062 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); 8063 8064 nLinePos += nLineWidth+nLineDY; 8065 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); 8066 } 8067 else 8068 { 8069 if ( eTextLine != UNDERLINE_BOLDWAVE ) 8070 nLinePos -= nLineWidth/2; 8071 m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine ); 8072 } 8073 } 8074 8075 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ) 8076 { 8077 // note: units in pFontEntry are ref device pixel 8078 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8079 long nLineHeight = 0; 8080 long nLinePos = 0; 8081 long nLinePos2 = 0; 8082 8083 if ( eTextLine > UNDERLINE_BOLDWAVE ) 8084 eTextLine = UNDERLINE_SINGLE; 8085 8086 switch ( eTextLine ) 8087 { 8088 case UNDERLINE_SINGLE: 8089 case UNDERLINE_DOTTED: 8090 case UNDERLINE_DASH: 8091 case UNDERLINE_LONGDASH: 8092 case UNDERLINE_DASHDOT: 8093 case UNDERLINE_DASHDOTDOT: 8094 if ( bIsAbove ) 8095 { 8096 if ( !pFontEntry->maMetric.mnAboveUnderlineSize ) 8097 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8098 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize ); 8099 nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset ); 8100 } 8101 else 8102 { 8103 if ( !pFontEntry->maMetric.mnUnderlineSize ) 8104 m_pReferenceDevice->ImplInitTextLineSize(); 8105 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize ); 8106 nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset ); 8107 } 8108 break; 8109 case UNDERLINE_BOLD: 8110 case UNDERLINE_BOLDDOTTED: 8111 case UNDERLINE_BOLDDASH: 8112 case UNDERLINE_BOLDLONGDASH: 8113 case UNDERLINE_BOLDDASHDOT: 8114 case UNDERLINE_BOLDDASHDOTDOT: 8115 if ( bIsAbove ) 8116 { 8117 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize ) 8118 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8119 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize ); 8120 nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset ); 8121 } 8122 else 8123 { 8124 if ( !pFontEntry->maMetric.mnBUnderlineSize ) 8125 m_pReferenceDevice->ImplInitTextLineSize(); 8126 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize ); 8127 nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset ); 8128 nLinePos += nLineHeight/2; 8129 } 8130 break; 8131 case UNDERLINE_DOUBLE: 8132 if ( bIsAbove ) 8133 { 8134 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize ) 8135 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8136 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize ); 8137 nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 ); 8138 nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 ); 8139 } 8140 else 8141 { 8142 if ( !pFontEntry->maMetric.mnDUnderlineSize ) 8143 m_pReferenceDevice->ImplInitTextLineSize(); 8144 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize ); 8145 nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 ); 8146 nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 ); 8147 } 8148 default: 8149 break; 8150 } 8151 8152 if ( nLineHeight ) 8153 { 8154 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true ); 8155 aLine.append( " w " ); 8156 appendStrokingColor( aColor, aLine ); 8157 aLine.append( "\n" ); 8158 8159 switch ( eTextLine ) 8160 { 8161 case UNDERLINE_DOTTED: 8162 case UNDERLINE_BOLDDOTTED: 8163 aLine.append( "[ " ); 8164 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8165 aLine.append( " ] 0 d\n" ); 8166 break; 8167 case UNDERLINE_DASH: 8168 case UNDERLINE_LONGDASH: 8169 case UNDERLINE_BOLDDASH: 8170 case UNDERLINE_BOLDLONGDASH: 8171 { 8172 sal_Int32 nDashLength = 4*nLineHeight; 8173 sal_Int32 nVoidLength = 2*nLineHeight; 8174 if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) ) 8175 nDashLength = 8*nLineHeight; 8176 8177 aLine.append( "[ " ); 8178 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 8179 aLine.append( ' ' ); 8180 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8181 aLine.append( " ] 0 d\n" ); 8182 } 8183 break; 8184 case UNDERLINE_DASHDOT: 8185 case UNDERLINE_BOLDDASHDOT: 8186 { 8187 sal_Int32 nDashLength = 4*nLineHeight; 8188 sal_Int32 nVoidLength = 2*nLineHeight; 8189 aLine.append( "[ " ); 8190 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 8191 aLine.append( ' ' ); 8192 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8193 aLine.append( ' ' ); 8194 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8195 aLine.append( ' ' ); 8196 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8197 aLine.append( " ] 0 d\n" ); 8198 } 8199 break; 8200 case UNDERLINE_DASHDOTDOT: 8201 case UNDERLINE_BOLDDASHDOTDOT: 8202 { 8203 sal_Int32 nDashLength = 4*nLineHeight; 8204 sal_Int32 nVoidLength = 2*nLineHeight; 8205 aLine.append( "[ " ); 8206 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 8207 aLine.append( ' ' ); 8208 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8209 aLine.append( ' ' ); 8210 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8211 aLine.append( ' ' ); 8212 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8213 aLine.append( ' ' ); 8214 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8215 aLine.append( ' ' ); 8216 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8217 aLine.append( " ] 0 d\n" ); 8218 } 8219 break; 8220 default: 8221 break; 8222 } 8223 8224 aLine.append( "0 " ); 8225 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8226 aLine.append( " m " ); 8227 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); 8228 aLine.append( ' ' ); 8229 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8230 aLine.append( " l S\n" ); 8231 if ( eTextLine == UNDERLINE_DOUBLE ) 8232 { 8233 aLine.append( "0 " ); 8234 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8235 aLine.append( " m " ); 8236 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); 8237 aLine.append( ' ' ); 8238 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8239 aLine.append( " l S\n" ); 8240 } 8241 } 8242 } 8243 8244 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor ) 8245 { 8246 // note: units in pFontEntry are ref device pixel 8247 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8248 long nLineHeight = 0; 8249 long nLinePos = 0; 8250 long nLinePos2 = 0; 8251 8252 if ( eStrikeout > STRIKEOUT_X ) 8253 eStrikeout = STRIKEOUT_SINGLE; 8254 8255 switch ( eStrikeout ) 8256 { 8257 case STRIKEOUT_SINGLE: 8258 if ( !pFontEntry->maMetric.mnStrikeoutSize ) 8259 m_pReferenceDevice->ImplInitTextLineSize(); 8260 nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize ); 8261 nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset ); 8262 break; 8263 case STRIKEOUT_BOLD: 8264 if ( !pFontEntry->maMetric.mnBStrikeoutSize ) 8265 m_pReferenceDevice->ImplInitTextLineSize(); 8266 nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize ); 8267 nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset ); 8268 break; 8269 case STRIKEOUT_DOUBLE: 8270 if ( !pFontEntry->maMetric.mnDStrikeoutSize ) 8271 m_pReferenceDevice->ImplInitTextLineSize(); 8272 nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize ); 8273 nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 ); 8274 nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 ); 8275 break; 8276 default: 8277 break; 8278 } 8279 8280 if ( nLineHeight ) 8281 { 8282 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true ); 8283 aLine.append( " w " ); 8284 appendStrokingColor( aColor, aLine ); 8285 aLine.append( "\n" ); 8286 8287 aLine.append( "0 " ); 8288 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8289 aLine.append( " m " ); 8290 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true ); 8291 aLine.append( ' ' ); 8292 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8293 aLine.append( " l S\n" ); 8294 8295 if ( eStrikeout == STRIKEOUT_DOUBLE ) 8296 { 8297 aLine.append( "0 " ); 8298 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8299 aLine.append( " m " ); 8300 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true ); 8301 aLine.append( ' ' ); 8302 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8303 aLine.append( " l S\n" ); 8304 } 8305 } 8306 } 8307 8308 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout ) 8309 { 8310 String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" ); 8311 String aStrikeout = aStrikeoutChar; 8312 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth ) 8313 aStrikeout.Append( aStrikeout ); 8314 8315 // do not get broader than nWidth modulo 1 character 8316 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth ) 8317 aStrikeout.Erase( 0, 1 ); 8318 aStrikeout.Append( aStrikeoutChar ); 8319 sal_Bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow(); 8320 if ( bShadow ) 8321 { 8322 Font aFont = m_aCurrentPDFState.m_aFont; 8323 aFont.SetShadow( sal_False ); 8324 setFont( aFont ); 8325 updateGraphicsState(); 8326 } 8327 8328 // strikeout string is left aligned non-CTL text 8329 sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode(); 8330 m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED ); 8331 drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false ); 8332 m_pReferenceDevice->SetLayoutMode( nOrigTLM ); 8333 8334 if ( bShadow ) 8335 { 8336 Font aFont = m_aCurrentPDFState.m_aFont; 8337 aFont.SetShadow( sal_True ); 8338 setFont( aFont ); 8339 updateGraphicsState(); 8340 } 8341 } 8342 8343 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove ) 8344 { 8345 if ( !nWidth || 8346 ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) && 8347 ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) && 8348 ((eOverline == UNDERLINE_NONE)||(eOverline == UNDERLINE_DONTKNOW)) ) ) 8349 return; 8350 8351 MARK( "drawTextLine" ); 8352 updateGraphicsState(); 8353 8354 // note: units in pFontEntry are ref device pixel 8355 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8356 Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor; 8357 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 8358 Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor(); 8359 bool bStrikeoutDone = false; 8360 bool bUnderlineDone = false; 8361 bool bOverlineDone = false; 8362 8363 if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) ) 8364 { 8365 drawStrikeoutChar( rPos, nWidth, eStrikeout ); 8366 bStrikeoutDone = true; 8367 } 8368 8369 Point aPos( rPos ); 8370 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); 8371 if( eAlign == ALIGN_TOP ) 8372 aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent ); 8373 else if( eAlign == ALIGN_BOTTOM ) 8374 aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent ); 8375 8376 OStringBuffer aLine( 512 ); 8377 // save GS 8378 aLine.append( "q " ); 8379 8380 // rotate and translate matrix 8381 double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0; 8382 Matrix3 aMat; 8383 aMat.rotate( fAngle ); 8384 aMat.translate( aPos.X(), aPos.Y() ); 8385 aMat.append( m_aPages.back(), aLine ); 8386 aLine.append( " cm\n" ); 8387 8388 if ( aUnderlineColor.GetTransparency() != 0 ) 8389 aUnderlineColor = aStrikeoutColor; 8390 8391 if ( (eUnderline == UNDERLINE_SMALLWAVE) || 8392 (eUnderline == UNDERLINE_WAVE) || 8393 (eUnderline == UNDERLINE_DOUBLEWAVE) || 8394 (eUnderline == UNDERLINE_BOLDWAVE) ) 8395 { 8396 drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 8397 bUnderlineDone = true; 8398 } 8399 8400 if ( (eOverline == UNDERLINE_SMALLWAVE) || 8401 (eOverline == UNDERLINE_WAVE) || 8402 (eOverline == UNDERLINE_DOUBLEWAVE) || 8403 (eOverline == UNDERLINE_BOLDWAVE) ) 8404 { 8405 drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); 8406 bOverlineDone = true; 8407 } 8408 8409 if ( !bUnderlineDone ) 8410 { 8411 drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 8412 } 8413 8414 if ( !bOverlineDone ) 8415 { 8416 drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); 8417 } 8418 8419 if ( !bStrikeoutDone ) 8420 { 8421 drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor ); 8422 } 8423 8424 aLine.append( "Q\n" ); 8425 writeBuffer( aLine.getStr(), aLine.getLength() ); 8426 } 8427 8428 void PDFWriterImpl::drawPolygon( const Polygon& rPoly ) 8429 { 8430 MARK( "drawPolygon" ); 8431 8432 updateGraphicsState(); 8433 8434 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8435 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8436 return; 8437 8438 int nPoints = rPoly.GetSize(); 8439 OStringBuffer aLine( 20 * nPoints ); 8440 m_aPages.back().appendPolygon( rPoly, aLine ); 8441 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8442 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8443 aLine.append( "B*\n" ); 8444 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8445 aLine.append( "S\n" ); 8446 else 8447 aLine.append( "f*\n" ); 8448 8449 writeBuffer( aLine.getStr(), aLine.getLength() ); 8450 } 8451 8452 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly ) 8453 { 8454 MARK( "drawPolyPolygon" ); 8455 8456 updateGraphicsState(); 8457 8458 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8459 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8460 return; 8461 8462 int nPolygons = rPolyPoly.Count(); 8463 8464 OStringBuffer aLine( 40 * nPolygons ); 8465 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 8466 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8467 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8468 aLine.append( "B*\n" ); 8469 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8470 aLine.append( "S\n" ); 8471 else 8472 aLine.append( "f*\n" ); 8473 8474 writeBuffer( aLine.getStr(), aLine.getLength() ); 8475 } 8476 8477 void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent ) 8478 { 8479 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" ); 8480 nTransparentPercent = nTransparentPercent % 100; 8481 8482 MARK( "drawTransparent" ); 8483 8484 updateGraphicsState(); 8485 8486 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8487 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8488 return; 8489 8490 if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 ) 8491 { 8492 m_aErrors.insert( m_bIsPDF_A1 ? 8493 PDFWriter::Warning_Transparency_Omitted_PDFA : 8494 PDFWriter::Warning_Transparency_Omitted_PDF13 ); 8495 8496 drawPolyPolygon( rPolyPoly ); 8497 return; 8498 } 8499 8500 // create XObject 8501 m_aTransparentObjects.push_back( TransparencyEmit() ); 8502 // FIXME: polygons with beziers may yield incorrect bound rect 8503 m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect(); 8504 // convert rectangle to default user space 8505 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8506 m_aTransparentObjects.back().m_nObject = createObject(); 8507 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8508 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; 8509 m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 ); 8510 // create XObject's content stream 8511 OStringBuffer aContent( 256 ); 8512 m_aPages.back().appendPolyPolygon( rPolyPoly, aContent ); 8513 if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) && 8514 m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) ) 8515 aContent.append( " B*\n" ); 8516 else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) ) 8517 aContent.append( " S\n" ); 8518 else 8519 aContent.append( " f*\n" ); 8520 m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() ); 8521 8522 OStringBuffer aObjName( 16 ); 8523 aObjName.append( "Tr" ); 8524 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8525 OString aTrName( aObjName.makeStringAndClear() ); 8526 aObjName.append( "EGS" ); 8527 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8528 OString aExtName( aObjName.makeStringAndClear() ); 8529 8530 OStringBuffer aLine( 80 ); 8531 // insert XObject 8532 aLine.append( "q /" ); 8533 aLine.append( aExtName ); 8534 aLine.append( " gs /" ); 8535 aLine.append( aTrName ); 8536 aLine.append( " Do Q\n" ); 8537 writeBuffer( aLine.getStr(), aLine.getLength() ); 8538 8539 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8540 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8541 } 8542 8543 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject ) 8544 { 8545 if( nObject >= 0 ) 8546 { 8547 switch( eKind ) 8548 { 8549 case ResXObject: 8550 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject; 8551 if( ! m_aOutputStreams.empty() ) 8552 m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject; 8553 break; 8554 case ResExtGState: 8555 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject; 8556 if( ! m_aOutputStreams.empty() ) 8557 m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject; 8558 break; 8559 case ResShading: 8560 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject; 8561 if( ! m_aOutputStreams.empty() ) 8562 m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject; 8563 break; 8564 case ResPattern: 8565 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject; 8566 if( ! m_aOutputStreams.empty() ) 8567 m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject; 8568 break; 8569 } 8570 } 8571 } 8572 8573 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect ) 8574 { 8575 push( PUSH_ALL ); 8576 8577 // force reemitting clip region 8578 clearClipRegion(); 8579 updateGraphicsState(); 8580 8581 m_aOutputStreams.push_front( StreamRedirect() ); 8582 m_aOutputStreams.front().m_pStream = pStream; 8583 m_aOutputStreams.front().m_aMapMode = m_aMapMode; 8584 8585 if( !rTargetRect.IsEmpty() ) 8586 { 8587 m_aOutputStreams.front().m_aTargetRect = 8588 lcl_convert( m_aGraphicsStack.front().m_aMapMode, 8589 m_aMapMode, 8590 getReferenceDevice(), 8591 rTargetRect ); 8592 Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft(); 8593 long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight()); 8594 aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom()); 8595 m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta ); 8596 } 8597 8598 // setup graphics state for independent object stream 8599 8600 // force reemitting colors 8601 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); 8602 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); 8603 } 8604 8605 Rectangle PDFWriterImpl::getRedirectTargetRect() const 8606 { 8607 return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect; 8608 } 8609 8610 SvStream* PDFWriterImpl::endRedirect() 8611 { 8612 SvStream* pStream = NULL; 8613 if( ! m_aOutputStreams.empty() ) 8614 { 8615 pStream = m_aOutputStreams.front().m_pStream; 8616 m_aMapMode = m_aOutputStreams.front().m_aMapMode; 8617 m_aOutputStreams.pop_front(); 8618 } 8619 8620 pop(); 8621 // force reemitting colors and clip region 8622 clearClipRegion(); 8623 m_aCurrentPDFState.m_bClipRegion = m_aGraphicsStack.front().m_bClipRegion; 8624 m_aCurrentPDFState.m_aClipRegion = m_aGraphicsStack.front().m_aClipRegion; 8625 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); 8626 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); 8627 8628 updateGraphicsState(); 8629 8630 return pStream; 8631 } 8632 8633 void PDFWriterImpl::beginTransparencyGroup() 8634 { 8635 updateGraphicsState(); 8636 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8637 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() ); 8638 } 8639 8640 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent ) 8641 { 8642 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" ); 8643 nTransparentPercent = nTransparentPercent % 100; 8644 8645 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8646 { 8647 // create XObject 8648 m_aTransparentObjects.push_back( TransparencyEmit() ); 8649 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox; 8650 // convert rectangle to default user space 8651 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8652 m_aTransparentObjects.back().m_nObject = createObject(); 8653 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; 8654 // get XObject's content stream 8655 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect()); 8656 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8657 8658 OStringBuffer aObjName( 16 ); 8659 aObjName.append( "Tr" ); 8660 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8661 OString aTrName( aObjName.makeStringAndClear() ); 8662 aObjName.append( "EGS" ); 8663 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8664 OString aExtName( aObjName.makeStringAndClear() ); 8665 8666 OStringBuffer aLine( 80 ); 8667 // insert XObject 8668 aLine.append( "q /" ); 8669 aLine.append( aExtName ); 8670 aLine.append( " gs /" ); 8671 aLine.append( aTrName ); 8672 aLine.append( " Do Q\n" ); 8673 writeBuffer( aLine.getStr(), aLine.getLength() ); 8674 8675 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8676 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8677 } 8678 } 8679 8680 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask ) 8681 { 8682 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8683 { 8684 // create XObject 8685 m_aTransparentObjects.push_back( TransparencyEmit() ); 8686 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox; 8687 // convert rectangle to default user space 8688 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8689 m_aTransparentObjects.back().m_nObject = createObject(); 8690 m_aTransparentObjects.back().m_fAlpha = 0.0; 8691 // get XObject's content stream 8692 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect()); 8693 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8694 8695 // draw soft mask 8696 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() ); 8697 drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask ); 8698 m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect()); 8699 8700 OStringBuffer aObjName( 16 ); 8701 aObjName.append( "Tr" ); 8702 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8703 OString aTrName( aObjName.makeStringAndClear() ); 8704 aObjName.append( "EGS" ); 8705 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8706 OString aExtName( aObjName.makeStringAndClear() ); 8707 8708 OStringBuffer aLine( 80 ); 8709 // insert XObject 8710 aLine.append( "q /" ); 8711 aLine.append( aExtName ); 8712 aLine.append( " gs /" ); 8713 aLine.append( aTrName ); 8714 aLine.append( " Do Q\n" ); 8715 writeBuffer( aLine.getStr(), aLine.getLength() ); 8716 8717 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8718 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8719 } 8720 } 8721 8722 void PDFWriterImpl::drawRectangle( const Rectangle& rRect ) 8723 { 8724 MARK( "drawRectangle" ); 8725 8726 updateGraphicsState(); 8727 8728 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8729 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8730 return; 8731 8732 OStringBuffer aLine( 40 ); 8733 m_aPages.back().appendRect( rRect, aLine ); 8734 8735 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8736 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8737 aLine.append( " B*\n" ); 8738 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8739 aLine.append( " S\n" ); 8740 else 8741 aLine.append( " f*\n" ); 8742 8743 writeBuffer( aLine.getStr(), aLine.getLength() ); 8744 } 8745 8746 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound ) 8747 { 8748 MARK( "drawRectangle with rounded edges" ); 8749 8750 if( !nHorzRound && !nVertRound ) 8751 drawRectangle( rRect ); 8752 8753 updateGraphicsState(); 8754 8755 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8756 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8757 return; 8758 8759 if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 ) 8760 nHorzRound = rRect.GetWidth()/2; 8761 if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 ) 8762 nVertRound = rRect.GetHeight()/2; 8763 8764 Point aPoints[16]; 8765 const double kappa = 0.5522847498; 8766 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5); 8767 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5); 8768 8769 aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() ); 8770 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); 8771 aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() ); 8772 aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() ); 8773 8774 aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound ); 8775 aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky ); 8776 aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound ); 8777 aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky ); 8778 8779 aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 ); 8780 aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() ); 8781 aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() ); 8782 aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() ); 8783 8784 aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound ); 8785 aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky ); 8786 aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound ); 8787 aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky ); 8788 8789 8790 OStringBuffer aLine( 80 ); 8791 m_aPages.back().appendPoint( aPoints[1], aLine ); 8792 aLine.append( " m " ); 8793 m_aPages.back().appendPoint( aPoints[2], aLine ); 8794 aLine.append( " l " ); 8795 m_aPages.back().appendPoint( aPoints[3], aLine ); 8796 aLine.append( ' ' ); 8797 m_aPages.back().appendPoint( aPoints[4], aLine ); 8798 aLine.append( ' ' ); 8799 m_aPages.back().appendPoint( aPoints[5], aLine ); 8800 aLine.append( " c\n" ); 8801 m_aPages.back().appendPoint( aPoints[6], aLine ); 8802 aLine.append( " l " ); 8803 m_aPages.back().appendPoint( aPoints[7], aLine ); 8804 aLine.append( ' ' ); 8805 m_aPages.back().appendPoint( aPoints[8], aLine ); 8806 aLine.append( ' ' ); 8807 m_aPages.back().appendPoint( aPoints[9], aLine ); 8808 aLine.append( " c\n" ); 8809 m_aPages.back().appendPoint( aPoints[10], aLine ); 8810 aLine.append( " l " ); 8811 m_aPages.back().appendPoint( aPoints[11], aLine ); 8812 aLine.append( ' ' ); 8813 m_aPages.back().appendPoint( aPoints[12], aLine ); 8814 aLine.append( ' ' ); 8815 m_aPages.back().appendPoint( aPoints[13], aLine ); 8816 aLine.append( " c\n" ); 8817 m_aPages.back().appendPoint( aPoints[14], aLine ); 8818 aLine.append( " l " ); 8819 m_aPages.back().appendPoint( aPoints[15], aLine ); 8820 aLine.append( ' ' ); 8821 m_aPages.back().appendPoint( aPoints[0], aLine ); 8822 aLine.append( ' ' ); 8823 m_aPages.back().appendPoint( aPoints[1], aLine ); 8824 aLine.append( " c " ); 8825 8826 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8827 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8828 aLine.append( "b*\n" ); 8829 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8830 aLine.append( "s\n" ); 8831 else 8832 aLine.append( "f*\n" ); 8833 8834 writeBuffer( aLine.getStr(), aLine.getLength() ); 8835 } 8836 8837 void PDFWriterImpl::drawEllipse( const Rectangle& rRect ) 8838 { 8839 MARK( "drawEllipse" ); 8840 8841 updateGraphicsState(); 8842 8843 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8844 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8845 return; 8846 8847 Point aPoints[12]; 8848 const double kappa = 0.5522847498; 8849 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5); 8850 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5); 8851 8852 aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() ); 8853 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); 8854 aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() ); 8855 8856 aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 ); 8857 aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky ); 8858 aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky ); 8859 8860 aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 ); 8861 aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() ); 8862 aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() ); 8863 8864 aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 ); 8865 aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky ); 8866 aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky ); 8867 8868 OStringBuffer aLine( 80 ); 8869 m_aPages.back().appendPoint( aPoints[1], aLine ); 8870 aLine.append( " m " ); 8871 m_aPages.back().appendPoint( aPoints[2], aLine ); 8872 aLine.append( ' ' ); 8873 m_aPages.back().appendPoint( aPoints[3], aLine ); 8874 aLine.append( ' ' ); 8875 m_aPages.back().appendPoint( aPoints[4], aLine ); 8876 aLine.append( " c\n" ); 8877 m_aPages.back().appendPoint( aPoints[5], aLine ); 8878 aLine.append( ' ' ); 8879 m_aPages.back().appendPoint( aPoints[6], aLine ); 8880 aLine.append( ' ' ); 8881 m_aPages.back().appendPoint( aPoints[7], aLine ); 8882 aLine.append( " c\n" ); 8883 m_aPages.back().appendPoint( aPoints[8], aLine ); 8884 aLine.append( ' ' ); 8885 m_aPages.back().appendPoint( aPoints[9], aLine ); 8886 aLine.append( ' ' ); 8887 m_aPages.back().appendPoint( aPoints[10], aLine ); 8888 aLine.append( " c\n" ); 8889 m_aPages.back().appendPoint( aPoints[11], aLine ); 8890 aLine.append( ' ' ); 8891 m_aPages.back().appendPoint( aPoints[0], aLine ); 8892 aLine.append( ' ' ); 8893 m_aPages.back().appendPoint( aPoints[1], aLine ); 8894 aLine.append( " c " ); 8895 8896 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8897 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8898 aLine.append( "b*\n" ); 8899 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8900 aLine.append( "s\n" ); 8901 else 8902 aLine.append( "f*\n" ); 8903 8904 writeBuffer( aLine.getStr(), aLine.getLength() ); 8905 } 8906 8907 static double calcAngle( const Rectangle& rRect, const Point& rPoint ) 8908 { 8909 Point aOrigin((rRect.Left()+rRect.Right()+1)/2, 8910 (rRect.Top()+rRect.Bottom()+1)/2); 8911 Point aPoint = rPoint - aOrigin; 8912 8913 double fX = (double)aPoint.X(); 8914 double fY = (double)-aPoint.Y(); 8915 8916 if( rRect.GetWidth() > rRect.GetHeight() ) 8917 fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight()); 8918 else if( rRect.GetHeight() > rRect.GetWidth() ) 8919 fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth()); 8920 return atan2( fY, fX ); 8921 } 8922 8923 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord ) 8924 { 8925 MARK( "drawArc" ); 8926 8927 updateGraphicsState(); 8928 8929 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8930 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8931 return; 8932 8933 // calculate start and stop angles 8934 const double fStartAngle = calcAngle( rRect, rStart ); 8935 double fStopAngle = calcAngle( rRect, rStop ); 8936 while( fStopAngle < fStartAngle ) 8937 fStopAngle += 2.0*M_PI; 8938 const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1; 8939 const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments; 8940 const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0); 8941 const double halfWidth = (double)rRect.GetWidth()/2.0; 8942 const double halfHeight = (double)rRect.GetHeight()/2.0; 8943 8944 const Point aCenter( (rRect.Left()+rRect.Right()+1)/2, 8945 (rRect.Top()+rRect.Bottom()+1)/2 ); 8946 8947 OStringBuffer aLine( 30*nFragments ); 8948 Point aPoint( (int)(halfWidth * cos(fStartAngle) ), 8949 -(int)(halfHeight * sin(fStartAngle) ) ); 8950 aPoint += aCenter; 8951 m_aPages.back().appendPoint( aPoint, aLine ); 8952 aLine.append( " m " ); 8953 if( !basegfx::fTools::equal(fStartAngle, fStopAngle) ) 8954 { 8955 for( int i = 0; i < nFragments; i++ ) 8956 { 8957 const double fStartFragment = fStartAngle + (double)i*fFragmentDelta; 8958 const double fStopFragment = fStartFragment + fFragmentDelta; 8959 aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ), 8960 -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) ); 8961 aPoint += aCenter; 8962 m_aPages.back().appendPoint( aPoint, aLine ); 8963 aLine.append( ' ' ); 8964 8965 aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ), 8966 -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) ); 8967 aPoint += aCenter; 8968 m_aPages.back().appendPoint( aPoint, aLine ); 8969 aLine.append( ' ' ); 8970 8971 aPoint = Point( (int)(halfWidth * cos(fStopFragment) ), 8972 -(int)(halfHeight * sin(fStopFragment) ) ); 8973 aPoint += aCenter; 8974 m_aPages.back().appendPoint( aPoint, aLine ); 8975 aLine.append( " c\n" ); 8976 } 8977 } 8978 if( bWithChord || bWithPie ) 8979 { 8980 if( bWithPie ) 8981 { 8982 m_aPages.back().appendPoint( aCenter, aLine ); 8983 aLine.append( " l " ); 8984 } 8985 aLine.append( "h " ); 8986 } 8987 if( ! bWithChord && ! bWithPie ) 8988 aLine.append( "S\n" ); 8989 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8990 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8991 aLine.append( "B*\n" ); 8992 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8993 aLine.append( "S\n" ); 8994 else 8995 aLine.append( "f*\n" ); 8996 8997 writeBuffer( aLine.getStr(), aLine.getLength() ); 8998 } 8999 9000 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly ) 9001 { 9002 MARK( "drawPolyLine" ); 9003 9004 sal_uInt16 nPoints = rPoly.GetSize(); 9005 if( nPoints < 2 ) 9006 return; 9007 9008 updateGraphicsState(); 9009 9010 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9011 return; 9012 9013 OStringBuffer aLine( 20 * nPoints ); 9014 m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] ); 9015 aLine.append( "S\n" ); 9016 9017 writeBuffer( aLine.getStr(), aLine.getLength() ); 9018 } 9019 9020 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo ) 9021 { 9022 MARK( "drawPolyLine with LineInfo" ); 9023 9024 updateGraphicsState(); 9025 9026 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9027 return; 9028 9029 OStringBuffer aLine; 9030 aLine.append( "q " ); 9031 if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) 9032 { 9033 writeBuffer( aLine.getStr(), aLine.getLength() ); 9034 drawPolyLine( rPoly ); 9035 writeBuffer( "Q\n", 2 ); 9036 } 9037 else 9038 { 9039 PDFWriter::ExtLineInfo aInfo; 9040 convertLineInfoToExtLineInfo( rInfo, aInfo ); 9041 drawPolyLine( rPoly, aInfo ); 9042 } 9043 } 9044 9045 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut ) 9046 { 9047 DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" ); 9048 rOut.m_fLineWidth = rIn.GetWidth(); 9049 rOut.m_fTransparency = 0.0; 9050 rOut.m_eCap = PDFWriter::capButt; 9051 rOut.m_eJoin = PDFWriter::joinMiter; 9052 rOut.m_fMiterLimit = 10; 9053 rOut.m_aDashArray.clear(); 9054 9055 int nDashes = rIn.GetDashCount(); 9056 int nDashLen = rIn.GetDashLen(); 9057 int nDistance = rIn.GetDistance(); 9058 for( int n = 0; n < nDashes; n++ ) 9059 { 9060 rOut.m_aDashArray.push_back( nDashLen ); 9061 rOut.m_aDashArray.push_back( nDistance ); 9062 } 9063 int nDots = rIn.GetDotCount(); 9064 int nDotLen = rIn.GetDotLen(); 9065 for( int n = 0; n < nDots; n++ ) 9066 { 9067 rOut.m_aDashArray.push_back( nDotLen ); 9068 rOut.m_aDashArray.push_back( nDistance ); 9069 } 9070 } 9071 9072 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo ) 9073 { 9074 MARK( "drawPolyLine with ExtLineInfo" ); 9075 9076 updateGraphicsState(); 9077 9078 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9079 return; 9080 9081 if( rInfo.m_fTransparency >= 1.0 ) 9082 return; 9083 9084 if( rInfo.m_fTransparency != 0.0 ) 9085 beginTransparencyGroup(); 9086 9087 OStringBuffer aLine; 9088 aLine.append( "q " ); 9089 m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine ); 9090 aLine.append( " w" ); 9091 if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader 9092 { 9093 switch( rInfo.m_eCap ) 9094 { 9095 default: 9096 case PDFWriter::capButt: aLine.append( " 0 J" );break; 9097 case PDFWriter::capRound: aLine.append( " 1 J" );break; 9098 case PDFWriter::capSquare: aLine.append( " 2 J" );break; 9099 } 9100 switch( rInfo.m_eJoin ) 9101 { 9102 default: 9103 case PDFWriter::joinMiter: 9104 { 9105 double fLimit = rInfo.m_fMiterLimit; 9106 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit ) 9107 fLimit = fLimit / rInfo.m_fLineWidth; 9108 if( fLimit < 1.0 ) 9109 fLimit = 1.0; 9110 aLine.append( " 0 j " ); 9111 appendDouble( fLimit, aLine ); 9112 aLine.append( " M" ); 9113 } 9114 break; 9115 case PDFWriter::joinRound: aLine.append( " 1 j" );break; 9116 case PDFWriter::joinBevel: aLine.append( " 2 j" );break; 9117 } 9118 if( rInfo.m_aDashArray.size() > 0 ) 9119 { 9120 aLine.append( " [ " ); 9121 for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin(); 9122 it != rInfo.m_aDashArray.end(); ++it ) 9123 { 9124 m_aPages.back().appendMappedLength( *it, aLine ); 9125 aLine.append( ' ' ); 9126 } 9127 aLine.append( "] 0 d" ); 9128 } 9129 aLine.append( "\n" ); 9130 writeBuffer( aLine.getStr(), aLine.getLength() ); 9131 drawPolyLine( rPoly ); 9132 } 9133 else 9134 { 9135 basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon()); 9136 basegfx::B2DPolyPolygon aPolyPoly; 9137 9138 basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly); 9139 9140 // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments. 9141 // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality) 9142 // this line needs to be removed and the loop below adapted accordingly 9143 aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly); 9144 9145 const sal_uInt32 nPolygonCount(aPolyPoly.count()); 9146 9147 for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ ) 9148 { 9149 aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " ); 9150 aPoly = aPolyPoly.getB2DPolygon( nPoly ); 9151 const sal_uInt32 nPointCount(aPoly.count()); 9152 9153 if(nPointCount) 9154 { 9155 const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1); 9156 basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0)); 9157 9158 for(sal_uInt32 a(0); a < nEdgeCount; a++) 9159 { 9160 if( a > 0 ) 9161 aLine.append( " " ); 9162 const sal_uInt32 nNextIndex((a + 1) % nPointCount); 9163 const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex)); 9164 9165 m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()), 9166 FRound(aCurrent.getY()) ), 9167 aLine ); 9168 aLine.append( " m " ); 9169 m_aPages.back().appendPoint( Point( FRound(aNext.getX()), 9170 FRound(aNext.getY()) ), 9171 aLine ); 9172 aLine.append( " l" ); 9173 9174 // prepare next edge 9175 aCurrent = aNext; 9176 } 9177 } 9178 } 9179 aLine.append( " S " ); 9180 writeBuffer( aLine.getStr(), aLine.getLength() ); 9181 } 9182 writeBuffer( "Q\n", 2 ); 9183 9184 if( rInfo.m_fTransparency != 0.0 ) 9185 { 9186 // FIXME: actually this may be incorrect with bezier polygons 9187 Rectangle aBoundRect( rPoly.GetBoundRect() ); 9188 // avoid clipping with thick lines 9189 if( rInfo.m_fLineWidth > 0.0 ) 9190 { 9191 sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth); 9192 aBoundRect.Top() -= nLW; 9193 aBoundRect.Left() -= nLW; 9194 aBoundRect.Right() += nLW; 9195 aBoundRect.Bottom() += nLW; 9196 } 9197 endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) ); 9198 } 9199 } 9200 9201 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor ) 9202 { 9203 MARK( "drawPixel" ); 9204 9205 Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor ); 9206 9207 if( aColor == Color( COL_TRANSPARENT ) ) 9208 return; 9209 9210 // pixels are drawn in line color, so have to set 9211 // the nonstroking color to line color 9212 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; 9213 setFillColor( aColor ); 9214 9215 updateGraphicsState(); 9216 9217 OStringBuffer aLine( 20 ); 9218 m_aPages.back().appendPoint( rPoint, aLine ); 9219 aLine.append( ' ' ); 9220 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine ); 9221 aLine.append( ' ' ); 9222 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine ); 9223 aLine.append( " re f\n" ); 9224 writeBuffer( aLine.getStr(), aLine.getLength() ); 9225 9226 setFillColor( aOldFillColor ); 9227 } 9228 9229 void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors ) 9230 { 9231 MARK( "drawPixel with Polygon" ); 9232 9233 updateGraphicsState(); 9234 9235 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors ) 9236 return; 9237 9238 sal_uInt16 nPoints = rPoints.GetSize(); 9239 OStringBuffer aLine( nPoints*40 ); 9240 aLine.append( "q " ); 9241 if( ! pColors ) 9242 { 9243 appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine ); 9244 aLine.append( ' ' ); 9245 } 9246 9247 OStringBuffer aPixel(32); 9248 aPixel.append( ' ' ); 9249 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel ); 9250 aPixel.append( ' ' ); 9251 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel ); 9252 OString aPixelStr = aPixel.makeStringAndClear(); 9253 for( sal_uInt16 i = 0; i < nPoints; i++ ) 9254 { 9255 if( pColors ) 9256 { 9257 if( pColors[i] == Color( COL_TRANSPARENT ) ) 9258 continue; 9259 9260 appendNonStrokingColor( pColors[i], aLine ); 9261 aLine.append( ' ' ); 9262 } 9263 m_aPages.back().appendPoint( rPoints[i], aLine ); 9264 aLine.append( aPixelStr ); 9265 aLine.append( " re f\n" ); 9266 } 9267 aLine.append( "Q\n" ); 9268 writeBuffer( aLine.getStr(), aLine.getLength() ); 9269 } 9270 9271 class AccessReleaser 9272 { 9273 BitmapReadAccess* m_pAccess; 9274 public: 9275 AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){} 9276 ~AccessReleaser() { delete m_pAccess; } 9277 }; 9278 9279 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject ) 9280 { 9281 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9282 9283 bool bFlateFilter = compressStream( rObject.m_pContentStream ); 9284 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END ); 9285 sal_uLong nSize = rObject.m_pContentStream->Tell(); 9286 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN ); 9287 #if OSL_DEBUG_LEVEL > 1 9288 emitComment( "PDFWriterImpl::writeTransparentObject" ); 9289 #endif 9290 OStringBuffer aLine( 512 ); 9291 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9292 aLine.append( rObject.m_nObject ); 9293 aLine.append( " 0 obj\n" 9294 "<</Type/XObject\n" 9295 "/Subtype/Form\n" 9296 "/BBox[ " ); 9297 appendFixedInt( rObject.m_aBoundRect.Left(), aLine ); 9298 aLine.append( ' ' ); 9299 appendFixedInt( rObject.m_aBoundRect.Top(), aLine ); 9300 aLine.append( ' ' ); 9301 appendFixedInt( rObject.m_aBoundRect.Right(), aLine ); 9302 aLine.append( ' ' ); 9303 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine ); 9304 aLine.append( " ]\n" ); 9305 if( ! rObject.m_pSoftMaskStream ) 9306 { 9307 if( ! m_bIsPDF_A1 ) 9308 { 9309 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" ); 9310 } 9311 } 9312 /* #i42884# the PDF reference recommends that each Form XObject 9313 * should have a resource dict; alas if that is the same object 9314 * as the one of the page it triggers an endless recursion in 9315 * acroread 5 (6 and up have that fixed). Since we have only one 9316 * resource dict anyway, let's use the one from the page by NOT 9317 * emitting a Resources entry. 9318 */ 9319 #if 0 9320 aLine.append( " /Resources " ); 9321 aLine.append( getResourceDictObj() ); 9322 aLine.append( " 0 R\n" ); 9323 #endif 9324 9325 aLine.append( "/Length " ); 9326 aLine.append( (sal_Int32)(nSize) ); 9327 aLine.append( "\n" ); 9328 if( bFlateFilter ) 9329 aLine.append( "/Filter/FlateDecode\n" ); 9330 aLine.append( ">>\n" 9331 "stream\n" ); 9332 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9333 checkAndEnableStreamEncryption( rObject.m_nObject ); 9334 CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) ); 9335 disableStreamEncryption(); 9336 aLine.setLength( 0 ); 9337 aLine.append( "\n" 9338 "endstream\n" 9339 "endobj\n\n" ); 9340 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9341 9342 // write ExtGState dict for this XObject 9343 aLine.setLength( 0 ); 9344 aLine.append( rObject.m_nExtGStateObject ); 9345 aLine.append( " 0 obj\n" 9346 "<<" ); 9347 if( ! rObject.m_pSoftMaskStream ) 9348 { 9349 //i59651 9350 if( m_bIsPDF_A1 ) 9351 { 9352 aLine.append( "/CA 1.0/ca 1.0" ); 9353 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9354 } 9355 else 9356 { 9357 aLine.append( "/CA " ); 9358 appendDouble( rObject.m_fAlpha, aLine ); 9359 aLine.append( "\n" 9360 " /ca " ); 9361 appendDouble( rObject.m_fAlpha, aLine ); 9362 } 9363 aLine.append( "\n" ); 9364 } 9365 else 9366 { 9367 if( m_bIsPDF_A1 ) 9368 { 9369 aLine.append( "/SMask/None" ); 9370 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9371 } 9372 else 9373 { 9374 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END ); 9375 sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell(); 9376 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN ); 9377 sal_Int32 nMaskObject = createObject(); 9378 aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " ); 9379 aLine.append( nMaskObject ); 9380 aLine.append( " 0 R>>\n" ); 9381 9382 OStringBuffer aMask; 9383 aMask.append( nMaskObject ); 9384 aMask.append( " 0 obj\n" 9385 "<</Type/XObject\n" 9386 "/Subtype/Form\n" 9387 "/BBox[" ); 9388 appendFixedInt( rObject.m_aBoundRect.Left(), aMask ); 9389 aMask.append( ' ' ); 9390 appendFixedInt( rObject.m_aBoundRect.Top(), aMask ); 9391 aMask.append( ' ' ); 9392 appendFixedInt( rObject.m_aBoundRect.Right(), aMask ); 9393 aMask.append( ' ' ); 9394 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask ); 9395 aMask.append( "]\n" ); 9396 9397 /* #i42884# see above */ 9398 #if 0 9399 aLine.append( "/Resources " ); 9400 aMask.append( getResourceDictObj() ); 9401 aMask.append( " 0 R\n" ); 9402 #endif 9403 9404 aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" ); 9405 aMask.append( "/Length " ); 9406 aMask.append( nMaskSize ); 9407 aMask.append( ">>\n" 9408 "stream\n" ); 9409 CHECK_RETURN( updateObject( nMaskObject ) ); 9410 checkAndEnableStreamEncryption( nMaskObject ); 9411 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) ); 9412 CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) ); 9413 disableStreamEncryption(); 9414 aMask.setLength( 0 ); 9415 aMask.append( "\nendstream\n" 9416 "endobj\n\n" ); 9417 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) ); 9418 } 9419 } 9420 aLine.append( ">>\n" 9421 "endobj\n\n" ); 9422 CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) ); 9423 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9424 9425 return true; 9426 } 9427 9428 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject ) 9429 { 9430 sal_Int32 nFunctionObject = createObject(); 9431 CHECK_RETURN( updateObject( nFunctionObject ) ); 9432 9433 VirtualDevice aDev; 9434 aDev.SetOutputSizePixel( rObject.m_aSize ); 9435 aDev.SetMapMode( MapMode( MAP_PIXEL ) ); 9436 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 9437 aDev.SetDrawMode( aDev.GetDrawMode() | 9438 ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT | 9439 DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) ); 9440 aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient ); 9441 9442 Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize ); 9443 BitmapReadAccess* pAccess = aSample.AcquireReadAccess(); 9444 AccessReleaser aReleaser( pAccess ); 9445 9446 Size aSize = aSample.GetSizePixel(); 9447 9448 sal_Int32 nStreamLengthObject = createObject(); 9449 #if OSL_DEBUG_LEVEL > 1 9450 emitComment( "PDFWriterImpl::writeGradientFunction" ); 9451 #endif 9452 OStringBuffer aLine( 120 ); 9453 aLine.append( nFunctionObject ); 9454 aLine.append( " 0 obj\n" 9455 "<</FunctionType 0\n" 9456 "/Domain[ 0 1 0 1 ]\n" 9457 "/Size[ " ); 9458 aLine.append( (sal_Int32)aSize.Width() ); 9459 aLine.append( ' ' ); 9460 aLine.append( (sal_Int32)aSize.Height() ); 9461 aLine.append( " ]\n" 9462 "/BitsPerSample 8\n" 9463 "/Range[ 0 1 0 1 0 1 ]\n" 9464 "/Order 3\n" 9465 "/Length " ); 9466 aLine.append( nStreamLengthObject ); 9467 aLine.append( " 0 R\n" 9468 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9469 "/Filter/FlateDecode" 9470 #endif 9471 ">>\n" 9472 "stream\n" ); 9473 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9474 9475 sal_uInt64 nStartStreamPos = 0; 9476 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) ); 9477 9478 checkAndEnableStreamEncryption( nFunctionObject ); 9479 beginCompression(); 9480 for( int y = aSize.Height()-1; y >= 0; y-- ) 9481 { 9482 for( int x = 0; x < aSize.Width(); x++ ) 9483 { 9484 sal_uInt8 aCol[3]; 9485 BitmapColor aColor = pAccess->GetColor( y, x ); 9486 aCol[0] = aColor.GetRed(); 9487 aCol[1] = aColor.GetGreen(); 9488 aCol[2] = aColor.GetBlue(); 9489 CHECK_RETURN( writeBuffer( aCol, 3 ) ); 9490 } 9491 } 9492 endCompression(); 9493 disableStreamEncryption(); 9494 9495 sal_uInt64 nEndStreamPos = 0; 9496 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) ); 9497 9498 aLine.setLength( 0 ); 9499 aLine.append( "\nendstream\nendobj\n\n" ); 9500 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9501 9502 // write stream length 9503 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 9504 aLine.setLength( 0 ); 9505 aLine.append( nStreamLengthObject ); 9506 aLine.append( " 0 obj\n" ); 9507 aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) ); 9508 aLine.append( "\nendobj\n\n" ); 9509 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9510 9511 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9512 aLine.setLength( 0 ); 9513 aLine.append( rObject.m_nObject ); 9514 aLine.append( " 0 obj\n" 9515 "<</ShadingType 1\n" 9516 "/ColorSpace/DeviceRGB\n" 9517 "/AntiAlias true\n" 9518 "/Domain[ 0 1 0 1 ]\n" 9519 "/Matrix[ " ); 9520 aLine.append( (sal_Int32)aSize.Width() ); 9521 aLine.append( " 0 0 " ); 9522 aLine.append( (sal_Int32)aSize.Height() ); 9523 aLine.append( " 0 0 ]\n" 9524 "/Function " ); 9525 aLine.append( nFunctionObject ); 9526 aLine.append( " 0 R\n" 9527 ">>\n" 9528 "endobj\n\n" ); 9529 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9530 9531 return true; 9532 } 9533 9534 bool PDFWriterImpl::writeJPG( JPGEmit& rObject ) 9535 { 9536 CHECK_RETURN( rObject.m_pStream ); 9537 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9538 9539 sal_Int32 nLength = 0; 9540 rObject.m_pStream->Seek( STREAM_SEEK_TO_END ); 9541 nLength = rObject.m_pStream->Tell(); 9542 rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN ); 9543 9544 sal_Int32 nMaskObject = 0; 9545 if( !!rObject.m_aMask ) 9546 { 9547 if( rObject.m_aMask.GetBitCount() == 1 || 9548 ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651 9549 ) 9550 { 9551 nMaskObject = createObject(); 9552 } 9553 else if( m_bIsPDF_A1 ) 9554 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9555 else if( m_aContext.Version < PDFWriter::PDF_1_4 ) 9556 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 ); 9557 9558 } 9559 #if OSL_DEBUG_LEVEL > 1 9560 emitComment( "PDFWriterImpl::writeJPG" ); 9561 #endif 9562 9563 OStringBuffer aLine(200); 9564 aLine.append( rObject.m_nObject ); 9565 aLine.append( " 0 obj\n" 9566 "<</Type/XObject/Subtype/Image/Width " ); 9567 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() ); 9568 aLine.append( " /Height " ); 9569 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() ); 9570 aLine.append( " /BitsPerComponent 8 " ); 9571 if( rObject.m_bTrueColor ) 9572 aLine.append( "/ColorSpace/DeviceRGB" ); 9573 else 9574 aLine.append( "/ColorSpace/DeviceGray" ); 9575 aLine.append( "/Filter/DCTDecode/Length " ); 9576 aLine.append( nLength ); 9577 if( nMaskObject ) 9578 { 9579 aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " ); 9580 aLine.append( nMaskObject ); 9581 aLine.append( " 0 R " ); 9582 } 9583 aLine.append( ">>\nstream\n" ); 9584 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9585 9586 checkAndEnableStreamEncryption( rObject.m_nObject ); 9587 CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) ); 9588 disableStreamEncryption(); 9589 9590 aLine.setLength( 0 ); 9591 aLine.append( "\nendstream\nendobj\n\n" ); 9592 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9593 9594 if( nMaskObject ) 9595 { 9596 BitmapEmit aEmit; 9597 aEmit.m_nObject = nMaskObject; 9598 if( rObject.m_aMask.GetBitCount() == 1 ) 9599 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask ); 9600 else if( rObject.m_aMask.GetBitCount() == 8 ) 9601 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) ); 9602 writeBitmapObject( aEmit, true ); 9603 } 9604 9605 return true; 9606 } 9607 9608 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask ) 9609 { 9610 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9611 9612 Bitmap aBitmap; 9613 Color aTransparentColor( COL_TRANSPARENT ); 9614 bool bWriteMask = false; 9615 if( ! bMask ) 9616 { 9617 aBitmap = rObject.m_aBitmap.GetBitmap(); 9618 if( rObject.m_aBitmap.IsAlpha() ) 9619 { 9620 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 9621 bWriteMask = true; 9622 // else draw without alpha channel 9623 } 9624 else 9625 { 9626 switch( rObject.m_aBitmap.GetTransparentType() ) 9627 { 9628 case TRANSPARENT_NONE: 9629 // comes from drawMask function 9630 if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask ) 9631 bMask = true; 9632 break; 9633 case TRANSPARENT_COLOR: 9634 aTransparentColor = rObject.m_aBitmap.GetTransparentColor(); 9635 break; 9636 case TRANSPARENT_BITMAP: 9637 bWriteMask = true; 9638 break; 9639 } 9640 } 9641 } 9642 else 9643 { 9644 if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() ) 9645 { 9646 aBitmap = rObject.m_aBitmap.GetMask(); 9647 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); 9648 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); 9649 } 9650 else if( aBitmap.GetBitCount() != 8 ) 9651 { 9652 aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap(); 9653 aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS ); 9654 DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" ); 9655 } 9656 } 9657 9658 BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess(); 9659 AccessReleaser aReleaser( pAccess ); 9660 9661 bool bTrueColor; 9662 sal_Int32 nBitsPerComponent; 9663 switch( aBitmap.GetBitCount() ) 9664 { 9665 case 1: 9666 case 2: 9667 case 4: 9668 case 8: 9669 bTrueColor = false; 9670 nBitsPerComponent = aBitmap.GetBitCount(); 9671 break; 9672 default: 9673 bTrueColor = true; 9674 nBitsPerComponent = 8; 9675 break; 9676 } 9677 9678 sal_Int32 nStreamLengthObject = createObject(); 9679 sal_Int32 nMaskObject = 0; 9680 9681 #if OSL_DEBUG_LEVEL > 1 9682 emitComment( "PDFWriterImpl::writeBitmapObject" ); 9683 #endif 9684 OStringBuffer aLine(1024); 9685 aLine.append( rObject.m_nObject ); 9686 aLine.append( " 0 obj\n" 9687 "<</Type/XObject/Subtype/Image/Width " ); 9688 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); 9689 aLine.append( "/Height " ); 9690 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() ); 9691 aLine.append( "/BitsPerComponent " ); 9692 aLine.append( nBitsPerComponent ); 9693 aLine.append( "/Length " ); 9694 aLine.append( nStreamLengthObject ); 9695 aLine.append( " 0 R\n" ); 9696 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9697 if( nBitsPerComponent != 1 ) 9698 { 9699 aLine.append( "/Filter/FlateDecode" ); 9700 } 9701 else 9702 { 9703 aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " ); 9704 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); 9705 aLine.append( ">>\n" ); 9706 } 9707 #endif 9708 if( ! bMask ) 9709 { 9710 aLine.append( "/ColorSpace" ); 9711 if( bTrueColor ) 9712 aLine.append( "/DeviceRGB\n" ); 9713 else if( aBitmap.HasGreyPalette() ) 9714 { 9715 aLine.append( "/DeviceGray\n" ); 9716 if( aBitmap.GetBitCount() == 1 ) 9717 { 9718 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette 9719 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); 9720 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" ); 9721 if( nBlackIndex == 1 ) 9722 aLine.append( "/Decode[1 0]\n" ); 9723 } 9724 } 9725 else 9726 { 9727 aLine.append( "[ /Indexed/DeviceRGB " ); 9728 aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) ); 9729 aLine.append( "\n<" ); 9730 if( m_aContext.Encryption.Encrypt() ) 9731 { 9732 enableStringEncryption( rObject.m_nObject ); 9733 //check encryption buffer size 9734 if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) ) 9735 { 9736 int nChar = 0; 9737 //fill the encryption buffer 9738 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9739 { 9740 const BitmapColor& rColor = pAccess->GetPaletteColor( i ); 9741 m_pEncryptionBuffer[nChar++] = rColor.GetRed(); 9742 m_pEncryptionBuffer[nChar++] = rColor.GetGreen(); 9743 m_pEncryptionBuffer[nChar++] = rColor.GetBlue(); 9744 } 9745 //encrypt the colorspace lookup table 9746 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar ); 9747 //now queue the data for output 9748 nChar = 0; 9749 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9750 { 9751 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9752 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9753 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9754 } 9755 } 9756 } 9757 else //no encryption requested (PDF/A-1a program flow drops here) 9758 { 9759 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9760 { 9761 const BitmapColor& rColor = pAccess->GetPaletteColor( i ); 9762 appendHex( rColor.GetRed(), aLine ); 9763 appendHex( rColor.GetGreen(), aLine ); 9764 appendHex( rColor.GetBlue(), aLine ); 9765 } 9766 } 9767 aLine.append( ">\n]\n" ); 9768 } 9769 } 9770 else 9771 { 9772 if( aBitmap.GetBitCount() == 1 ) 9773 { 9774 aLine.append( "/ImageMask true\n" ); 9775 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); 9776 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" ); 9777 if( nBlackIndex ) 9778 aLine.append( "/Decode[ 1 0 ]\n" ); 9779 else 9780 aLine.append( "/Decode[ 0 1 ]\n" ); 9781 } 9782 else if( aBitmap.GetBitCount() == 8 ) 9783 { 9784 aLine.append( "/ColorSpace/DeviceGray\n" 9785 "/Decode [ 1 0 ]\n" ); 9786 } 9787 } 9788 9789 if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651 9790 { 9791 if( bWriteMask ) 9792 { 9793 nMaskObject = createObject(); 9794 if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 ) 9795 aLine.append( "/SMask " ); 9796 else 9797 aLine.append( "/Mask " ); 9798 aLine.append( nMaskObject ); 9799 aLine.append( " 0 R\n" ); 9800 } 9801 else if( aTransparentColor != Color( COL_TRANSPARENT ) ) 9802 { 9803 aLine.append( "/Mask[ " ); 9804 if( bTrueColor ) 9805 { 9806 aLine.append( (sal_Int32)aTransparentColor.GetRed() ); 9807 aLine.append( ' ' ); 9808 aLine.append( (sal_Int32)aTransparentColor.GetRed() ); 9809 aLine.append( ' ' ); 9810 aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); 9811 aLine.append( ' ' ); 9812 aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); 9813 aLine.append( ' ' ); 9814 aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); 9815 aLine.append( ' ' ); 9816 aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); 9817 } 9818 else 9819 { 9820 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) ); 9821 aLine.append( nIndex ); 9822 } 9823 aLine.append( " ]\n" ); 9824 } 9825 } 9826 else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) ) 9827 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9828 9829 aLine.append( ">>\n" 9830 "stream\n" ); 9831 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9832 sal_uInt64 nStartPos = 0; 9833 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) ); 9834 9835 checkAndEnableStreamEncryption( rObject.m_nObject ); 9836 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9837 if( nBitsPerComponent == 1 ) 9838 { 9839 writeG4Stream( pAccess ); 9840 } 9841 else 9842 #endif 9843 { 9844 beginCompression(); 9845 if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB ) 9846 { 9847 const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U ); 9848 9849 for( int i = 0; i < pAccess->Height(); i++ ) 9850 { 9851 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) ); 9852 } 9853 } 9854 else 9855 { 9856 const int nScanLineBytes = pAccess->Width()*3; 9857 boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] ); 9858 for( int y = 0; y < pAccess->Height(); y++ ) 9859 { 9860 for( int x = 0; x < pAccess->Width(); x++ ) 9861 { 9862 BitmapColor aColor = pAccess->GetColor( y, x ); 9863 pCol[3*x+0] = aColor.GetRed(); 9864 pCol[3*x+1] = aColor.GetGreen(); 9865 pCol[3*x+2] = aColor.GetBlue(); 9866 } 9867 CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) ); 9868 } 9869 } 9870 endCompression(); 9871 } 9872 disableStreamEncryption(); 9873 9874 sal_uInt64 nEndPos = 0; 9875 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) ); 9876 aLine.setLength( 0 ); 9877 aLine.append( "\nendstream\nendobj\n\n" ); 9878 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9879 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 9880 aLine.setLength( 0 ); 9881 aLine.append( nStreamLengthObject ); 9882 aLine.append( " 0 obj\n" ); 9883 aLine.append( (sal_Int64)(nEndPos-nStartPos) ); 9884 aLine.append( "\nendobj\n\n" ); 9885 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9886 9887 if( nMaskObject ) 9888 { 9889 BitmapEmit aEmit; 9890 aEmit.m_nObject = nMaskObject; 9891 aEmit.m_aBitmap = rObject.m_aBitmap; 9892 return writeBitmapObject( aEmit, true ); 9893 } 9894 9895 return true; 9896 } 9897 9898 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask ) 9899 { 9900 MARK( "drawJPGBitmap" ); 9901 9902 OStringBuffer aLine( 80 ); 9903 updateGraphicsState(); 9904 9905 // #i40055# sanity check 9906 if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) ) 9907 return; 9908 if( ! (rSizePixel.Width() && rSizePixel.Height()) ) 9909 return; 9910 9911 rDCTData.Seek( 0 ); 9912 if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 9913 { 9914 // need to convert to grayscale; 9915 // load stream to bitmap and draw the bitmap instead 9916 Graphic aGraphic; 9917 GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG ); 9918 Bitmap aBmp( aGraphic.GetBitmap() ); 9919 if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() ) 9920 { 9921 BitmapEx aBmpEx( aBmp, rMask ); 9922 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx ); 9923 } 9924 else 9925 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp ); 9926 return; 9927 } 9928 9929 SvMemoryStream* pStream = new SvMemoryStream; 9930 *pStream << rDCTData; 9931 pStream->Seek( STREAM_SEEK_TO_END ); 9932 9933 BitmapID aID; 9934 aID.m_aPixelSize = rSizePixel; 9935 aID.m_nSize = pStream->Tell(); 9936 pStream->Seek( STREAM_SEEK_TO_BEGIN ); 9937 aID.m_nChecksum = rtl_crc32( 0, pStream->GetData(), aID.m_nSize ); 9938 if( ! rMask.IsEmpty() ) 9939 aID.m_nMaskChecksum = rMask.GetChecksum(); 9940 9941 std::list< JPGEmit >::const_iterator it; 9942 for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it ) 9943 ; 9944 if( it == m_aJPGs.end() ) 9945 { 9946 m_aJPGs.push_front( JPGEmit() ); 9947 JPGEmit& rEmit = m_aJPGs.front(); 9948 rEmit.m_nObject = createObject(); 9949 rEmit.m_aID = aID; 9950 rEmit.m_pStream = pStream; 9951 rEmit.m_bTrueColor = bIsTrueColor; 9952 if( !! rMask && rMask.GetSizePixel() == rSizePixel ) 9953 rEmit.m_aMask = rMask; 9954 9955 it = m_aJPGs.begin(); 9956 } 9957 else 9958 delete pStream; 9959 9960 aLine.append( "q " ); 9961 sal_Int32 nCheckWidth = 0; 9962 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth ); 9963 aLine.append( " 0 0 " ); 9964 sal_Int32 nCheckHeight = 0; 9965 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight ); 9966 aLine.append( ' ' ); 9967 m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine ); 9968 aLine.append( " cm\n/Im" ); 9969 aLine.append( it->m_nObject ); 9970 aLine.append( " Do Q\n" ); 9971 if( nCheckWidth == 0 || nCheckHeight == 0 ) 9972 { 9973 // #i97512# avoid invalid current matrix 9974 aLine.setLength( 0 ); 9975 aLine.append( "\n%jpeg image /Im" ); 9976 aLine.append( it->m_nObject ); 9977 aLine.append( " scaled to zero size, omitted\n" ); 9978 } 9979 writeBuffer( aLine.getStr(), aLine.getLength() ); 9980 9981 OStringBuffer aObjName( 16 ); 9982 aObjName.append( "Im" ); 9983 aObjName.append( it->m_nObject ); 9984 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject ); 9985 9986 } 9987 9988 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor ) 9989 { 9990 OStringBuffer aLine( 80 ); 9991 updateGraphicsState(); 9992 9993 aLine.append( "q " ); 9994 if( rFillColor != Color( COL_TRANSPARENT ) ) 9995 { 9996 appendNonStrokingColor( rFillColor, aLine ); 9997 aLine.append( ' ' ); 9998 } 9999 sal_Int32 nCheckWidth = 0; 10000 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth ); 10001 aLine.append( " 0 0 " ); 10002 sal_Int32 nCheckHeight = 0; 10003 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight ); 10004 aLine.append( ' ' ); 10005 m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine ); 10006 aLine.append( " cm\n/Im" ); 10007 aLine.append( rBitmap.m_nObject ); 10008 aLine.append( " Do Q\n" ); 10009 if( nCheckWidth == 0 || nCheckHeight == 0 ) 10010 { 10011 // #i97512# avoid invalid current matrix 10012 aLine.setLength( 0 ); 10013 aLine.append( "\n%bitmap image /Im" ); 10014 aLine.append( rBitmap.m_nObject ); 10015 aLine.append( " scaled to zero size, omitted\n" ); 10016 } 10017 writeBuffer( aLine.getStr(), aLine.getLength() ); 10018 } 10019 10020 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask ) 10021 { 10022 BitmapEx aBitmap( i_rBitmap ); 10023 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 10024 { 10025 BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS; 10026 int nDepth = aBitmap.GetBitmap().GetBitCount(); 10027 if( nDepth <= 4 ) 10028 eConv = BMP_CONVERSION_4BIT_GREYS; 10029 if( nDepth > 1 ) 10030 aBitmap.Convert( eConv ); 10031 } 10032 BitmapID aID; 10033 aID.m_aPixelSize = aBitmap.GetSizePixel(); 10034 aID.m_nSize = aBitmap.GetBitCount(); 10035 aID.m_nChecksum = aBitmap.GetBitmap().GetChecksum(); 10036 aID.m_nMaskChecksum = 0; 10037 if( aBitmap.IsAlpha() ) 10038 aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum(); 10039 else 10040 { 10041 Bitmap aMask = aBitmap.GetMask(); 10042 if( ! aMask.IsEmpty() ) 10043 aID.m_nMaskChecksum = aMask.GetChecksum(); 10044 } 10045 std::list< BitmapEmit >::const_iterator it; 10046 for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it ) 10047 { 10048 if( aID == it->m_aID ) 10049 break; 10050 } 10051 if( it == m_aBitmaps.end() ) 10052 { 10053 m_aBitmaps.push_front( BitmapEmit() ); 10054 m_aBitmaps.front().m_aID = aID; 10055 m_aBitmaps.front().m_aBitmap = aBitmap; 10056 m_aBitmaps.front().m_nObject = createObject(); 10057 m_aBitmaps.front().m_bDrawMask = bDrawMask; 10058 it = m_aBitmaps.begin(); 10059 } 10060 10061 OStringBuffer aObjName( 16 ); 10062 aObjName.append( "Im" ); 10063 aObjName.append( it->m_nObject ); 10064 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject ); 10065 10066 return *it; 10067 } 10068 10069 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap ) 10070 { 10071 MARK( "drawBitmap (Bitmap)" ); 10072 10073 // #i40055# sanity check 10074 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10075 return; 10076 10077 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) ); 10078 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); 10079 } 10080 10081 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap ) 10082 { 10083 MARK( "drawBitmap (BitmapEx)" ); 10084 10085 // #i40055# sanity check 10086 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10087 return; 10088 10089 const BitmapEmit& rEmit = createBitmapEmit( rBitmap ); 10090 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); 10091 } 10092 10093 void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor ) 10094 { 10095 MARK( "drawMask" ); 10096 10097 // #i40055# sanity check 10098 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10099 return; 10100 10101 Bitmap aBitmap( rBitmap ); 10102 if( aBitmap.GetBitCount() > 1 ) 10103 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); 10104 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); 10105 10106 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true ); 10107 drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor ); 10108 } 10109 10110 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize ) 10111 { 10112 Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10113 MapMode( MAP_POINT ), 10114 getReferenceDevice(), 10115 rSize ) ); 10116 // check if we already have this gradient 10117 std::list<GradientEmit>::iterator it; 10118 // rounding to point will generally lose some pixels 10119 // round up to point boundary 10120 aPtSize.Width()++; 10121 aPtSize.Height()++; 10122 for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it ) 10123 { 10124 if( it->m_aGradient == rGradient ) 10125 { 10126 if( it->m_aSize == aPtSize ) 10127 break; 10128 } 10129 } 10130 if( it == m_aGradients.end() ) 10131 { 10132 m_aGradients.push_front( GradientEmit() ); 10133 m_aGradients.front().m_aGradient = rGradient; 10134 m_aGradients.front().m_nObject = createObject(); 10135 m_aGradients.front().m_aSize = aPtSize; 10136 it = m_aGradients.begin(); 10137 } 10138 10139 OStringBuffer aObjName( 16 ); 10140 aObjName.append( 'P' ); 10141 aObjName.append( it->m_nObject ); 10142 pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject ); 10143 10144 return it->m_nObject; 10145 } 10146 10147 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient ) 10148 { 10149 MARK( "drawGradient (Rectangle)" ); 10150 10151 if( m_aContext.Version == PDFWriter::PDF_1_2 ) 10152 { 10153 drawRectangle( rRect ); 10154 return; 10155 } 10156 10157 sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() ); 10158 10159 Point aTranslate( rRect.BottomLeft() ); 10160 aTranslate += Point( 0, 1 ); 10161 10162 updateGraphicsState(); 10163 10164 OStringBuffer aLine( 80 ); 10165 aLine.append( "q 1 0 0 1 " ); 10166 m_aPages.back().appendPoint( aTranslate, aLine ); 10167 aLine.append( " cm " ); 10168 // if a stroke is appended reset the clip region before stroke 10169 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10170 aLine.append( "q " ); 10171 aLine.append( "0 0 " ); 10172 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); 10173 aLine.append( ' ' ); 10174 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true ); 10175 aLine.append( " re W n\n" ); 10176 10177 aLine.append( "/P" ); 10178 aLine.append( nGradient ); 10179 aLine.append( " sh " ); 10180 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10181 { 10182 aLine.append( "Q 0 0 " ); 10183 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); 10184 aLine.append( ' ' ); 10185 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true ); 10186 aLine.append( " re S " ); 10187 } 10188 aLine.append( "Q\n" ); 10189 writeBuffer( aLine.getStr(), aLine.getLength() ); 10190 } 10191 10192 void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient ) 10193 { 10194 MARK( "drawGradient (PolyPolygon)" ); 10195 10196 if( m_aContext.Version == PDFWriter::PDF_1_2 ) 10197 { 10198 drawPolyPolygon( rPolyPoly ); 10199 return; 10200 } 10201 10202 Rectangle aBoundRect = rPolyPoly.GetBoundRect(); 10203 sal_Int32 nGradient = createGradient( rGradient, aBoundRect.GetSize() ); 10204 10205 updateGraphicsState(); 10206 10207 Point aTranslate = aBoundRect.BottomLeft(); 10208 int nPolygons = rPolyPoly.Count(); 10209 10210 OStringBuffer aLine( 80*nPolygons ); 10211 aLine.append( "q " ); 10212 // set PolyPolygon as clip path 10213 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 10214 aLine.append( "W* n\n" ); 10215 aLine.append( "1 0 0 1 " ); 10216 m_aPages.back().appendPoint( aTranslate, aLine ); 10217 aLine.append( " cm\n" ); 10218 aLine.append( "/P" ); 10219 aLine.append( nGradient ); 10220 aLine.append( " sh Q\n" ); 10221 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10222 { 10223 // and draw the surrounding path 10224 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 10225 aLine.append( "S\n" ); 10226 } 10227 writeBuffer( aLine.getStr(), aLine.getLength() ); 10228 } 10229 10230 void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch ) 10231 { 10232 MARK( "drawHatch" ); 10233 10234 updateGraphicsState(); 10235 10236 if( rPolyPoly.Count() ) 10237 { 10238 PolyPolygon aPolyPoly( rPolyPoly ); 10239 10240 aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME ); 10241 push( PUSH_LINECOLOR ); 10242 setLineColor( rHatch.GetColor() ); 10243 getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, sal_False ); 10244 pop(); 10245 } 10246 } 10247 10248 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall ) 10249 { 10250 MARK( "drawWallpaper" ); 10251 10252 bool bDrawColor = false; 10253 bool bDrawGradient = false; 10254 bool bDrawBitmap = false; 10255 10256 BitmapEx aBitmap; 10257 Point aBmpPos = rRect.TopLeft(); 10258 Size aBmpSize; 10259 if( rWall.IsBitmap() ) 10260 { 10261 aBitmap = rWall.GetBitmap(); 10262 aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(), 10263 getMapMode(), 10264 getReferenceDevice(), 10265 aBitmap.GetPrefSize() ); 10266 Rectangle aRect( rRect ); 10267 if( rWall.IsRect() ) 10268 { 10269 aRect = rWall.GetRect(); 10270 aBmpPos = aRect.TopLeft(); 10271 aBmpSize = aRect.GetSize(); 10272 } 10273 if( rWall.GetStyle() != WALLPAPER_SCALE ) 10274 { 10275 if( rWall.GetStyle() != WALLPAPER_TILE ) 10276 { 10277 bDrawBitmap = true; 10278 if( rWall.IsGradient() ) 10279 bDrawGradient = true; 10280 else 10281 bDrawColor = true; 10282 switch( rWall.GetStyle() ) 10283 { 10284 case WALLPAPER_TOPLEFT: 10285 break; 10286 case WALLPAPER_TOP: 10287 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10288 break; 10289 case WALLPAPER_LEFT: 10290 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10291 break; 10292 case WALLPAPER_TOPRIGHT: 10293 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10294 break; 10295 case WALLPAPER_CENTER: 10296 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10297 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10298 break; 10299 case WALLPAPER_RIGHT: 10300 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10301 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10302 break; 10303 case WALLPAPER_BOTTOMLEFT: 10304 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10305 break; 10306 case WALLPAPER_BOTTOM: 10307 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10308 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10309 break; 10310 case WALLPAPER_BOTTOMRIGHT: 10311 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10312 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10313 break; 10314 default: ; 10315 } 10316 } 10317 else 10318 { 10319 // push the bitmap 10320 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) ); 10321 10322 // convert to page coordinates; this needs to be done here 10323 // since the emit does not know the page anymore 10324 Rectangle aConvertRect( aBmpPos, aBmpSize ); 10325 m_aPages.back().convertRect( aConvertRect ); 10326 10327 OStringBuffer aNameBuf(16); 10328 aNameBuf.append( "Im" ); 10329 aNameBuf.append( rEmit.m_nObject ); 10330 OString aImageName( aNameBuf.makeStringAndClear() ); 10331 10332 // push the pattern 10333 OStringBuffer aTilingStream( 32 ); 10334 appendFixedInt( aConvertRect.GetWidth(), aTilingStream ); 10335 aTilingStream.append( " 0 0 " ); 10336 appendFixedInt( aConvertRect.GetHeight(), aTilingStream ); 10337 aTilingStream.append( " 0 0 cm\n/" ); 10338 aTilingStream.append( aImageName ); 10339 aTilingStream.append( " Do\n" ); 10340 10341 m_aTilings.push_back( TilingEmit() ); 10342 m_aTilings.back().m_nObject = createObject(); 10343 m_aTilings.back().m_aRectangle = Rectangle( Point( 0, 0 ), aConvertRect.GetSize() ); 10344 m_aTilings.back().m_pTilingStream = new SvMemoryStream(); 10345 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() ); 10346 // phase the tiling so wallpaper begins on upper left 10347 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor; 10348 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor; 10349 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject; 10350 10351 updateGraphicsState(); 10352 10353 OStringBuffer aObjName( 16 ); 10354 aObjName.append( 'P' ); 10355 aObjName.append( m_aTilings.back().m_nObject ); 10356 OString aPatternName( aObjName.makeStringAndClear() ); 10357 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject ); 10358 10359 // fill a rRect with the pattern 10360 OStringBuffer aLine( 100 ); 10361 aLine.append( "q /Pattern cs /" ); 10362 aLine.append( aPatternName ); 10363 aLine.append( " scn " ); 10364 m_aPages.back().appendRect( rRect, aLine ); 10365 aLine.append( " f Q\n" ); 10366 writeBuffer( aLine.getStr(), aLine.getLength() ); 10367 } 10368 } 10369 else 10370 { 10371 aBmpPos = aRect.TopLeft(); 10372 aBmpSize = aRect.GetSize(); 10373 bDrawBitmap = true; 10374 } 10375 10376 if( aBitmap.IsTransparent() ) 10377 { 10378 if( rWall.IsGradient() ) 10379 bDrawGradient = true; 10380 else 10381 bDrawColor = true; 10382 } 10383 } 10384 else if( rWall.IsGradient() ) 10385 bDrawGradient = true; 10386 else 10387 bDrawColor = true; 10388 10389 if( bDrawGradient ) 10390 { 10391 drawGradient( rRect, rWall.GetGradient() ); 10392 } 10393 if( bDrawColor ) 10394 { 10395 Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor; 10396 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; 10397 setLineColor( Color( COL_TRANSPARENT ) ); 10398 setFillColor( rWall.GetColor() ); 10399 drawRectangle( rRect ); 10400 setLineColor( aOldLineColor ); 10401 setFillColor( aOldFillColor ); 10402 } 10403 if( bDrawBitmap ) 10404 { 10405 // set temporary clip region since aBmpPos and aBmpSize 10406 // may be outside rRect 10407 OStringBuffer aLine( 20 ); 10408 aLine.append( "q " ); 10409 m_aPages.back().appendRect( rRect, aLine ); 10410 aLine.append( " W n\n" ); 10411 writeBuffer( aLine.getStr(), aLine.getLength() ); 10412 drawBitmap( aBmpPos, aBmpSize, aBitmap ); 10413 writeBuffer( "Q\n", 2 ); 10414 } 10415 } 10416 10417 void PDFWriterImpl::beginPattern( const Rectangle& rCellRect ) 10418 { 10419 beginRedirect( new SvMemoryStream(), rCellRect ); 10420 } 10421 10422 sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform ) 10423 { 10424 Rectangle aConvertRect( getRedirectTargetRect() ); 10425 DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" ); 10426 10427 // get scaling between current mapmode and PDF output 10428 Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) ); 10429 double fSX = (double(aScaling.Width()) / 10000.0); 10430 double fSY = (double(aScaling.Height()) / 10000.0); 10431 10432 // transform translation part of matrix 10433 Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] ); 10434 aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation ); 10435 10436 sal_Int32 nTilingId = m_aTilings.size(); 10437 m_aTilings.push_back( TilingEmit() ); 10438 TilingEmit& rTile = m_aTilings.back(); 10439 rTile.m_nObject = createObject(); 10440 rTile.m_aResources = m_aOutputStreams.front().m_aResourceDict; 10441 rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX; 10442 rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY; 10443 rTile.m_aTransform.matrix[2] = aTranslation.Width(); 10444 rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX; 10445 rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY; 10446 rTile.m_aTransform.matrix[5] = -aTranslation.Height(); 10447 // caution: endRedirect pops the stream, so do this last 10448 rTile.m_pTilingStream = dynamic_cast<SvMemoryStream*>(endRedirect()); 10449 // FIXME: bound rect will not work with rotated matrix 10450 rTile.m_aRectangle = Rectangle( Point(0,0), aConvertRect.GetSize() ); 10451 rTile.m_aCellSize = aConvertRect.GetSize(); 10452 10453 OStringBuffer aObjName( 16 ); 10454 aObjName.append( 'P' ); 10455 aObjName.append( rTile.m_nObject ); 10456 pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject ); 10457 return nTilingId; 10458 } 10459 10460 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill ) 10461 { 10462 if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() ) 10463 return; 10464 10465 m_aPages.back().endStream(); 10466 sal_Int32 nXObject = createObject(); 10467 OStringBuffer aNameBuf( 16 ); 10468 aNameBuf.append( "Pol" ); 10469 aNameBuf.append( nXObject ); 10470 OString aObjName( aNameBuf.makeStringAndClear() ); 10471 Rectangle aObjRect; 10472 if( updateObject( nXObject ) ) 10473 { 10474 // get bounding rect of object 10475 PolyPolygon aSubDiv; 10476 rPolyPoly.AdaptiveSubdivide( aSubDiv ); 10477 aObjRect = aSubDiv.GetBoundRect(); 10478 Rectangle aConvObjRect( aObjRect ); 10479 m_aPages.back().convertRect( aConvObjRect ); 10480 10481 // move polypolygon to bottom left of page 10482 PolyPolygon aLocalPath( rPolyPoly ); 10483 sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72; 10484 sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72; 10485 Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode ); 10486 sal_Int32 nXOff = aObjRect.Left(); 10487 sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom(); 10488 aLocalPath.Move( -nXOff, nYOff ); 10489 10490 // prepare XObject's content stream 10491 OStringBuffer aStream( 512 ); 10492 aStream.append( "/Pattern cs /P" ); 10493 aStream.append( m_aTilings[ nPattern ].m_nObject ); 10494 aStream.append( " scn\n" ); 10495 m_aPages.back().appendPolyPolygon( aLocalPath, aStream ); 10496 aStream.append( bEOFill ? "f*" : "f" ); 10497 SvMemoryStream aMemStream( aStream.getLength() ); 10498 aMemStream.Write( aStream.getStr(), aStream.getLength() ); 10499 bool bDeflate = compressStream( &aMemStream ); 10500 aMemStream.Seek( STREAM_SEEK_TO_END ); 10501 sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell(); 10502 aMemStream.Seek( STREAM_SEEK_TO_BEGIN ); 10503 10504 // add new XObject to global resource dict 10505 m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject; 10506 10507 // write XObject 10508 OStringBuffer aLine( 512 ); 10509 aLine.append( nXObject ); 10510 aLine.append( " 0 obj\n" 10511 "<</Type/XObject/Subtype/Form/BBox[0 0 " ); 10512 appendFixedInt( aConvObjRect.GetWidth(), aLine ); 10513 aLine.append( ' ' ); 10514 appendFixedInt( aConvObjRect.GetHeight(), aLine ); 10515 aLine.append( "]/Length " ); 10516 aLine.append( nStreamLen ); 10517 if( bDeflate ) 10518 aLine.append( "/Filter/FlateDecode" ); 10519 aLine.append( ">>\n" 10520 "stream\n" ); 10521 writeBuffer( aLine.getStr(), aLine.getLength() ); 10522 checkAndEnableStreamEncryption( nXObject ); 10523 writeBuffer( aMemStream.GetData(), nStreamLen ); 10524 disableStreamEncryption(); 10525 writeBuffer( "\nendstream\nendobj\n\n", 19 ); 10526 } 10527 m_aPages.back().beginStream(); 10528 OStringBuffer aLine( 80 ); 10529 aLine.append( "q 1 0 0 1 " ); 10530 m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine ); 10531 aLine.append( " cm/" ); 10532 aLine.append( aObjName ); 10533 aLine.append( " Do Q\n" ); 10534 writeBuffer( aLine.getStr(), aLine.getLength() ); 10535 } 10536 10537 void PDFWriterImpl::updateGraphicsState() 10538 { 10539 OStringBuffer aLine( 256 ); 10540 GraphicsState& rNewState = m_aGraphicsStack.front(); 10541 // first set clip region since it might invalidate everything else 10542 10543 if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) ) 10544 { 10545 rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion; 10546 10547 if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion || 10548 ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) ) 10549 { 10550 if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() ) 10551 { 10552 aLine.append( "Q " ); 10553 // invalidate everything but the clip region 10554 m_aCurrentPDFState = GraphicsState(); 10555 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion); 10556 } 10557 if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() ) 10558 { 10559 // clip region is always stored in private PDF mapmode 10560 MapMode aNewMapMode = rNewState.m_aMapMode; 10561 rNewState.m_aMapMode = m_aMapMode; 10562 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10563 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; 10564 10565 aLine.append( "q " ); 10566 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine ); 10567 aLine.append( "W* n\n" ); 10568 rNewState.m_aMapMode = aNewMapMode; 10569 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10570 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; 10571 } 10572 } 10573 } 10574 10575 if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) ) 10576 { 10577 rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode; 10578 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10579 } 10580 10581 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) ) 10582 { 10583 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont; 10584 getReferenceDevice()->SetFont( rNewState.m_aFont ); 10585 getReferenceDevice()->ImplNewFont(); 10586 } 10587 10588 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) ) 10589 { 10590 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode; 10591 getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode ); 10592 } 10593 10594 if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) ) 10595 { 10596 rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage; 10597 getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage ); 10598 } 10599 10600 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) ) 10601 { 10602 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor; 10603 if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor && 10604 rNewState.m_aLineColor != Color( COL_TRANSPARENT ) ) 10605 { 10606 appendStrokingColor( rNewState.m_aLineColor, aLine ); 10607 aLine.append( "\n" ); 10608 } 10609 } 10610 10611 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) ) 10612 { 10613 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor; 10614 if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor && 10615 rNewState.m_aFillColor != Color( COL_TRANSPARENT ) ) 10616 { 10617 appendNonStrokingColor( rNewState.m_aFillColor, aLine ); 10618 aLine.append( "\n" ); 10619 } 10620 } 10621 10622 if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) ) 10623 { 10624 rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent; 10625 if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent ) 10626 { 10627 // TODO: switch extended graphicsstate 10628 } 10629 } 10630 10631 // everything is up to date now 10632 m_aCurrentPDFState = m_aGraphicsStack.front(); 10633 if( aLine.getLength() ) 10634 writeBuffer( aLine.getStr(), aLine.getLength() ); 10635 } 10636 10637 /* #i47544# imitate OutputDevice behaviour: 10638 * if a font with a nontransparent color is set, it overwrites the current 10639 * text color. OTOH setting the text color will overwrite the color of the font. 10640 */ 10641 void PDFWriterImpl::setFont( const Font& rFont ) 10642 { 10643 Color aColor = rFont.GetColor(); 10644 if( aColor == Color( COL_TRANSPARENT ) ) 10645 aColor = m_aGraphicsStack.front().m_aFont.GetColor(); 10646 m_aGraphicsStack.front().m_aFont = rFont; 10647 m_aGraphicsStack.front().m_aFont.SetColor( aColor ); 10648 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont; 10649 } 10650 10651 void PDFWriterImpl::push( sal_uInt16 nFlags ) 10652 { 10653 OSL_ENSURE( m_aGraphicsStack.size() > 0, "invalid graphics stack" ); 10654 m_aGraphicsStack.push_front( m_aGraphicsStack.front() ); 10655 m_aGraphicsStack.front().m_nFlags = nFlags; 10656 } 10657 10658 void PDFWriterImpl::pop() 10659 { 10660 OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" ); 10661 if( m_aGraphicsStack.size() < 2 ) 10662 return; 10663 10664 GraphicsState aState = m_aGraphicsStack.front(); 10665 m_aGraphicsStack.pop_front(); 10666 GraphicsState& rOld = m_aGraphicsStack.front(); 10667 10668 // move those parameters back that were not pushed 10669 // in the first place 10670 if( ! (aState.m_nFlags & PUSH_LINECOLOR) ) 10671 setLineColor( aState.m_aLineColor ); 10672 if( ! (aState.m_nFlags & PUSH_FILLCOLOR) ) 10673 setFillColor( aState.m_aFillColor ); 10674 if( ! (aState.m_nFlags & PUSH_FONT) ) 10675 setFont( aState.m_aFont ); 10676 if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) ) 10677 setTextColor( aState.m_aFont.GetColor() ); 10678 if( ! (aState.m_nFlags & PUSH_MAPMODE) ) 10679 setMapMode( aState.m_aMapMode ); 10680 if( ! (aState.m_nFlags & PUSH_CLIPREGION) ) 10681 { 10682 // do not use setClipRegion here 10683 // it would convert again assuming the current mapmode 10684 rOld.m_aClipRegion = aState.m_aClipRegion; 10685 rOld.m_bClipRegion = aState.m_bClipRegion; 10686 } 10687 if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) ) 10688 setTextLineColor( aState.m_aTextLineColor ); 10689 if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) ) 10690 setOverlineColor( aState.m_aOverlineColor ); 10691 if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) ) 10692 setTextAlign( aState.m_aFont.GetAlign() ); 10693 if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) ) 10694 setTextFillColor( aState.m_aFont.GetFillColor() ); 10695 if( ! (aState.m_nFlags & PUSH_REFPOINT) ) 10696 { 10697 // what ? 10698 } 10699 // invalidate graphics state 10700 m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U); 10701 } 10702 10703 void PDFWriterImpl::setMapMode( const MapMode& rMapMode ) 10704 { 10705 m_aGraphicsStack.front().m_aMapMode = rMapMode; 10706 getReferenceDevice()->SetMapMode( rMapMode ); 10707 m_aCurrentPDFState.m_aMapMode = rMapMode; 10708 } 10709 10710 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion ) 10711 { 10712 basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ); 10713 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); 10714 m_aGraphicsStack.front().m_aClipRegion = aRegion; 10715 m_aGraphicsStack.front().m_bClipRegion = true; 10716 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10717 } 10718 10719 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY ) 10720 { 10721 if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() ) 10722 { 10723 Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10724 m_aMapMode, 10725 getReferenceDevice(), 10726 Point( nX, nY ) ) ); 10727 aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10728 m_aMapMode, 10729 getReferenceDevice(), 10730 Point() ); 10731 basegfx::B2DHomMatrix aMat; 10732 aMat.translate( aPoint.X(), aPoint.Y() ); 10733 m_aGraphicsStack.front().m_aClipRegion.transform( aMat ); 10734 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10735 } 10736 } 10737 10738 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect ) 10739 { 10740 basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect( 10741 basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); 10742 return intersectClipRegion( aRect ); 10743 } 10744 10745 10746 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion ) 10747 { 10748 basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) ); 10749 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); 10750 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10751 if( m_aGraphicsStack.front().m_bClipRegion ) 10752 { 10753 basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) ); 10754 aRegion = basegfx::tools::prepareForPolygonOperation( aRegion ); 10755 m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion ); 10756 } 10757 else 10758 { 10759 m_aGraphicsStack.front().m_aClipRegion = aRegion; 10760 m_aGraphicsStack.front().m_bClipRegion = true; 10761 } 10762 return true; 10763 } 10764 10765 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr ) 10766 { 10767 if( nPageNr < 0 ) 10768 nPageNr = m_nCurrentPage; 10769 10770 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10771 return; 10772 10773 m_aNotes.push_back( PDFNoteEntry() ); 10774 m_aNotes.back().m_nObject = createObject(); 10775 m_aNotes.back().m_aContents = rNote; 10776 m_aNotes.back().m_aRect = rRect; 10777 // convert to default user space now, since the mapmode may change 10778 m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect ); 10779 10780 // insert note to page's annotation list 10781 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject ); 10782 } 10783 10784 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr ) 10785 { 10786 if( nPageNr < 0 ) 10787 nPageNr = m_nCurrentPage; 10788 10789 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10790 return -1; 10791 10792 sal_Int32 nRet = m_aLinks.size(); 10793 10794 m_aLinks.push_back( PDFLink() ); 10795 m_aLinks.back().m_nObject = createObject(); 10796 m_aLinks.back().m_nPage = nPageNr; 10797 m_aLinks.back().m_aRect = rRect; 10798 // convert to default user space now, since the mapmode may change 10799 m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect ); 10800 10801 // insert link to page's annotation list 10802 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject ); 10803 10804 return nRet; 10805 } 10806 10807 //--->i56629 10808 sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10809 { 10810 if( nPageNr < 0 ) 10811 nPageNr = m_nCurrentPage; 10812 10813 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10814 return -1; 10815 10816 sal_Int32 nRet = m_aNamedDests.size(); 10817 10818 m_aNamedDests.push_back( PDFNamedDest() ); 10819 m_aNamedDests.back().m_aDestName = sDestName; 10820 m_aNamedDests.back().m_nPage = nPageNr; 10821 m_aNamedDests.back().m_eType = eType; 10822 m_aNamedDests.back().m_aRect = rRect; 10823 // convert to default user space now, since the mapmode may change 10824 m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect ); 10825 10826 return nRet; 10827 } 10828 //<---i56629 10829 10830 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10831 { 10832 if( nPageNr < 0 ) 10833 nPageNr = m_nCurrentPage; 10834 10835 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10836 return -1; 10837 10838 sal_Int32 nRet = m_aDests.size(); 10839 10840 m_aDests.push_back( PDFDest() ); 10841 m_aDests.back().m_nPage = nPageNr; 10842 m_aDests.back().m_eType = eType; 10843 m_aDests.back().m_aRect = rRect; 10844 // convert to default user space now, since the mapmode may change 10845 m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect ); 10846 10847 return nRet; 10848 } 10849 10850 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10851 { 10852 return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType ); 10853 } 10854 10855 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId ) 10856 { 10857 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) 10858 return -1; 10859 if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() ) 10860 return -2; 10861 10862 m_aLinks[ nLinkId ].m_nDest = nDestId; 10863 10864 return 0; 10865 } 10866 10867 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL ) 10868 { 10869 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) 10870 return -1; 10871 10872 m_aLinks[ nLinkId ].m_nDest = -1; 10873 10874 using namespace ::com::sun::star; 10875 10876 if (!m_xTrans.is()) 10877 { 10878 uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() ); 10879 if( xFact.is() ) 10880 { 10881 m_xTrans = uno::Reference < util::XURLTransformer >( 10882 xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY ); 10883 } 10884 } 10885 10886 util::URL aURL; 10887 aURL.Complete = rURL; 10888 10889 if (m_xTrans.is()) 10890 m_xTrans->parseStrict( aURL ); 10891 10892 m_aLinks[ nLinkId ].m_aURL = aURL.Complete; 10893 10894 return 0; 10895 } 10896 10897 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId ) 10898 { 10899 m_aLinkPropertyMap[ nPropertyId ] = nLinkId; 10900 } 10901 10902 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID ) 10903 { 10904 // create new item 10905 sal_Int32 nNewItem = m_aOutline.size(); 10906 m_aOutline.push_back( PDFOutlineEntry() ); 10907 10908 // set item attributes 10909 setOutlineItemParent( nNewItem, nParent ); 10910 setOutlineItemText( nNewItem, rText ); 10911 setOutlineItemDest( nNewItem, nDestID ); 10912 10913 return nNewItem; 10914 } 10915 10916 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent ) 10917 { 10918 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) 10919 return -1; 10920 10921 int nRet = 0; 10922 10923 if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem ) 10924 { 10925 nNewParent = 0; 10926 nRet = -2; 10927 } 10928 // remove item from previous parent 10929 sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID; 10930 if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() ) 10931 { 10932 PDFOutlineEntry& rParent = m_aOutline[ nParentID ]; 10933 10934 for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin(); 10935 it != rParent.m_aChildren.end(); ++it ) 10936 { 10937 if( *it == nItem ) 10938 { 10939 rParent.m_aChildren.erase( it ); 10940 break; 10941 } 10942 } 10943 } 10944 10945 // insert item to new parent's list of children 10946 m_aOutline[ nNewParent ].m_aChildren.push_back( nItem ); 10947 10948 return nRet; 10949 } 10950 10951 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText ) 10952 { 10953 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) 10954 return -1; 10955 10956 m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText ); 10957 return 0; 10958 } 10959 10960 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID ) 10961 { 10962 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist 10963 return -1; 10964 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist 10965 return -2; 10966 m_aOutline[nItem].m_nDestID = nDestID; 10967 return 0; 10968 } 10969 10970 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType ) 10971 { 10972 static std::map< PDFWriter::StructElement, const char* > aTagStrings; 10973 if( aTagStrings.empty() ) 10974 { 10975 aTagStrings[ PDFWriter::NonStructElement] = "NonStruct"; 10976 aTagStrings[ PDFWriter::Document ] = "Document"; 10977 aTagStrings[ PDFWriter::Part ] = "Part"; 10978 aTagStrings[ PDFWriter::Article ] = "Art"; 10979 aTagStrings[ PDFWriter::Section ] = "Sect"; 10980 aTagStrings[ PDFWriter::Division ] = "Div"; 10981 aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote"; 10982 aTagStrings[ PDFWriter::Caption ] = "Caption"; 10983 aTagStrings[ PDFWriter::TOC ] = "TOC"; 10984 aTagStrings[ PDFWriter::TOCI ] = "TOCI"; 10985 aTagStrings[ PDFWriter::Index ] = "Index"; 10986 aTagStrings[ PDFWriter::Paragraph ] = "P"; 10987 aTagStrings[ PDFWriter::Heading ] = "H"; 10988 aTagStrings[ PDFWriter::H1 ] = "H1"; 10989 aTagStrings[ PDFWriter::H2 ] = "H2"; 10990 aTagStrings[ PDFWriter::H3 ] = "H3"; 10991 aTagStrings[ PDFWriter::H4 ] = "H4"; 10992 aTagStrings[ PDFWriter::H5 ] = "H5"; 10993 aTagStrings[ PDFWriter::H6 ] = "H6"; 10994 aTagStrings[ PDFWriter::List ] = "L"; 10995 aTagStrings[ PDFWriter::ListItem ] = "LI"; 10996 aTagStrings[ PDFWriter::LILabel ] = "Lbl"; 10997 aTagStrings[ PDFWriter::LIBody ] = "LBody"; 10998 aTagStrings[ PDFWriter::Table ] = "Table"; 10999 aTagStrings[ PDFWriter::TableRow ] = "TR"; 11000 aTagStrings[ PDFWriter::TableHeader ] = "TH"; 11001 aTagStrings[ PDFWriter::TableData ] = "TD"; 11002 aTagStrings[ PDFWriter::Span ] = "Span"; 11003 aTagStrings[ PDFWriter::Quote ] = "Quote"; 11004 aTagStrings[ PDFWriter::Note ] = "Note"; 11005 aTagStrings[ PDFWriter::Reference ] = "Reference"; 11006 aTagStrings[ PDFWriter::BibEntry ] = "BibEntry"; 11007 aTagStrings[ PDFWriter::Code ] = "Code"; 11008 aTagStrings[ PDFWriter::Link ] = "Link"; 11009 aTagStrings[ PDFWriter::Figure ] = "Figure"; 11010 aTagStrings[ PDFWriter::Formula ] = "Formula"; 11011 aTagStrings[ PDFWriter::Form ] = "Form"; 11012 } 11013 11014 std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType ); 11015 11016 return it != aTagStrings.end() ? it->second : "Div"; 11017 } 11018 11019 void PDFWriterImpl::beginStructureElementMCSeq() 11020 { 11021 if( m_bEmitStructure && 11022 m_nCurrentStructElement > 0 && // StructTreeRoot 11023 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence 11024 ) 11025 { 11026 PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ]; 11027 OStringBuffer aLine( 128 ); 11028 sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size(); 11029 aLine.append( "/" ); 11030 if( rEle.m_aAlias.getLength() > 0 ) 11031 aLine.append( rEle.m_aAlias ); 11032 else 11033 aLine.append( getStructureTag( rEle.m_eType ) ); 11034 aLine.append( "<</MCID " ); 11035 aLine.append( nMCID ); 11036 aLine.append( ">>BDC\n" ); 11037 writeBuffer( aLine.getStr(), aLine.getLength() ); 11038 11039 // update the element's content list 11040 #if OSL_DEBUG_LEVEL > 1 11041 fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n", 11042 nMCID, 11043 m_aPages[ m_nCurrentPage ].m_nPageObject, 11044 rEle.m_nFirstPageObject ); 11045 #endif 11046 rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) ); 11047 // update the page's mcid parent list 11048 m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject ); 11049 // mark element MC sequence as open 11050 rEle.m_bOpenMCSeq = true; 11051 } 11052 // handle artifacts 11053 else if( ! m_bEmitStructure && m_aContext.Tagged && 11054 m_nCurrentStructElement > 0 && 11055 m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement && 11056 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence 11057 ) 11058 { 11059 OStringBuffer aLine( 128 ); 11060 aLine.append( "/Artifact BMC\n" ); 11061 writeBuffer( aLine.getStr(), aLine.getLength() ); 11062 // mark element MC sequence as open 11063 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true; 11064 } 11065 } 11066 11067 void PDFWriterImpl::endStructureElementMCSeq() 11068 { 11069 if( m_nCurrentStructElement > 0 && // StructTreeRoot 11070 ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) && 11071 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence 11072 ) 11073 { 11074 writeBuffer( "EMC\n", 4 ); 11075 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false; 11076 } 11077 } 11078 11079 bool PDFWriterImpl::checkEmitStructure() 11080 { 11081 bool bEmit = false; 11082 if( m_aContext.Tagged ) 11083 { 11084 bEmit = true; 11085 sal_Int32 nEle = m_nCurrentStructElement; 11086 while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) ) 11087 { 11088 if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement ) 11089 { 11090 bEmit = false; 11091 break; 11092 } 11093 nEle = m_aStructure[ nEle ].m_nParentElement; 11094 } 11095 } 11096 return bEmit; 11097 } 11098 11099 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias ) 11100 { 11101 if( m_nCurrentPage < 0 ) 11102 return -1; 11103 11104 if( ! m_aContext.Tagged ) 11105 return -1; 11106 11107 // close eventual current MC sequence 11108 endStructureElementMCSeq(); 11109 11110 if( m_nCurrentStructElement == 0 && 11111 eType != PDFWriter::Document && eType != PDFWriter::NonStructElement ) 11112 { 11113 // struct tree root hit, but not beginning document 11114 // this might happen with setCurrentStructureElement 11115 // silently insert structure into document again if one properly exists 11116 if( ! m_aStructure[ 0 ].m_aChildren.empty() ) 11117 { 11118 PDFWriter::StructElement childType = PDFWriter::NonStructElement; 11119 sal_Int32 nNewCurElement = 0; 11120 const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren; 11121 for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin(); 11122 childType != PDFWriter::Document && it != rRootChildren.end(); ++it ) 11123 { 11124 nNewCurElement = *it; 11125 childType = m_aStructure[ nNewCurElement ].m_eType; 11126 } 11127 if( childType == PDFWriter::Document ) 11128 { 11129 m_nCurrentStructElement = nNewCurElement; 11130 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" ); 11131 } 11132 else { 11133 DBG_ERROR( "document structure in disorder !" ); 11134 } 11135 } 11136 else { 11137 DBG_ERROR( "PDF document structure MUST be contained in a Document element" ); 11138 } 11139 } 11140 11141 sal_Int32 nNewId = sal_Int32(m_aStructure.size()); 11142 m_aStructure.push_back( PDFStructureElement() ); 11143 PDFStructureElement& rEle = m_aStructure.back(); 11144 rEle.m_eType = eType; 11145 rEle.m_nOwnElement = nNewId; 11146 rEle.m_nParentElement = m_nCurrentStructElement; 11147 rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject; 11148 m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId ); 11149 m_nCurrentStructElement = nNewId; 11150 11151 // handle alias names 11152 if( rAlias.getLength() && eType != PDFWriter::NonStructElement ) 11153 { 11154 OStringBuffer aNameBuf( rAlias.getLength() ); 11155 appendName( rAlias, aNameBuf ); 11156 OString aAliasName( aNameBuf.makeStringAndClear() ); 11157 rEle.m_aAlias = aAliasName; 11158 m_aRoleMap[ aAliasName ] = getStructureTag( eType ); 11159 } 11160 11161 #if OSL_DEBUG_LEVEL > 1 11162 OStringBuffer aLine( "beginStructureElement " ); 11163 aLine.append( m_nCurrentStructElement ); 11164 aLine.append( ": " ); 11165 aLine.append( getStructureTag( eType ) ); 11166 if( rEle.m_aAlias.getLength() ) 11167 { 11168 aLine.append( " aliased as \"" ); 11169 aLine.append( rEle.m_aAlias ); 11170 aLine.append( '\"' ); 11171 } 11172 emitComment( aLine.getStr() ); 11173 #endif 11174 11175 // check whether to emit structure henceforth 11176 m_bEmitStructure = checkEmitStructure(); 11177 11178 if( m_bEmitStructure ) // don't create nonexistant objects 11179 { 11180 rEle.m_nObject = createObject(); 11181 // update parent's kids list 11182 m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject ); 11183 } 11184 return nNewId; 11185 } 11186 11187 void PDFWriterImpl::endStructureElement() 11188 { 11189 if( m_nCurrentPage < 0 ) 11190 return; 11191 11192 if( ! m_aContext.Tagged ) 11193 return; 11194 11195 if( m_nCurrentStructElement == 0 ) 11196 { 11197 // hit the struct tree root, that means there is an endStructureElement 11198 // without corresponding beginStructureElement 11199 return; 11200 } 11201 11202 // end the marked content sequence 11203 endStructureElementMCSeq(); 11204 11205 #if OSL_DEBUG_LEVEL > 1 11206 OStringBuffer aLine( "endStructureElement " ); 11207 aLine.append( m_nCurrentStructElement ); 11208 aLine.append( ": " ); 11209 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); 11210 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() ) 11211 { 11212 aLine.append( " aliased as \"" ); 11213 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); 11214 aLine.append( '\"' ); 11215 } 11216 #endif 11217 11218 // "end" the structure element, the parent becomes current element 11219 m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement; 11220 11221 // check whether to emit structure henceforth 11222 m_bEmitStructure = checkEmitStructure(); 11223 11224 #if OSL_DEBUG_LEVEL > 1 11225 if( m_bEmitStructure ) 11226 emitComment( aLine.getStr() ); 11227 #endif 11228 } 11229 11230 //---> i94258 11231 /* 11232 * This function adds an internal structure list container to overcome the 8191 elements array limitation 11233 * in kids element emission. 11234 * Recursive function 11235 * 11236 */ 11237 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle ) 11238 { 11239 if( rEle.m_eType == PDFWriter::NonStructElement && 11240 rEle.m_nOwnElement != rEle.m_nParentElement ) 11241 return; 11242 11243 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it ) 11244 { 11245 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) ) 11246 { 11247 PDFStructureElement& rChild = m_aStructure[ *it ]; 11248 if( rChild.m_eType != PDFWriter::NonStructElement ) 11249 { 11250 //triggered when a child of the rEle element is found 11251 if( rChild.m_nParentElement == rEle.m_nOwnElement ) 11252 addInternalStructureContainer( rChild );//examine the child 11253 else 11254 { 11255 DBG_ERROR( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" ); 11256 #if OSL_DEBUG_LEVEL > 1 11257 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it ); 11258 #endif 11259 } 11260 } 11261 } 11262 else 11263 { 11264 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" ); 11265 #if OSL_DEBUG_LEVEL > 1 11266 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it ); 11267 #endif 11268 } 11269 } 11270 11271 if( rEle.m_nOwnElement != rEle.m_nParentElement ) 11272 { 11273 if( !rEle.m_aKids.empty() ) 11274 { 11275 if( rEle.m_aKids.size() > ncMaxPDFArraySize ) { 11276 //then we need to add the containers for the kids elements 11277 // a list to be used for the new kid element 11278 std::list< PDFStructureElementKid > aNewKids; 11279 std::list< sal_Int32 > aNewChildren; 11280 11281 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?) 11282 OStringBuffer aNameBuf( "Div" ); 11283 OString aAliasName( aNameBuf.makeStringAndClear() ); 11284 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division ); 11285 11286 while( rEle.m_aKids.size() > ncMaxPDFArraySize ) 11287 { 11288 sal_Int32 nCurrentStructElement = rEle.m_nOwnElement; 11289 sal_Int32 nNewId = sal_Int32(m_aStructure.size()); 11290 m_aStructure.push_back( PDFStructureElement() ); 11291 PDFStructureElement& rEleNew = m_aStructure.back(); 11292 rEleNew.m_aAlias = aAliasName; 11293 rEleNew.m_eType = PDFWriter::Division; // a new Div type container 11294 rEleNew.m_nOwnElement = nNewId; 11295 rEleNew.m_nParentElement = nCurrentStructElement; 11296 //inherit the same page as the first child to be reparented 11297 rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject; 11298 rEleNew.m_nObject = createObject();//assign a PDF object number 11299 //add the object to the kid list of the parent 11300 aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) ); 11301 aNewChildren.push_back( nNewId ); 11302 11303 std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() ); 11304 std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() ); 11305 advance( aChildEndIt, ncMaxPDFArraySize ); 11306 advance( aKidEndIt, ncMaxPDFArraySize ); 11307 11308 rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(), 11309 rEle.m_aKids, 11310 rEle.m_aKids.begin(), 11311 aKidEndIt ); 11312 rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(), 11313 rEle.m_aChildren, 11314 rEle.m_aChildren.begin(), 11315 aChildEndIt ); 11316 // set the kid's new parent 11317 for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin(); 11318 it != rEleNew.m_aChildren.end(); ++it ) 11319 { 11320 m_aStructure[ *it ].m_nParentElement = nNewId; 11321 } 11322 } 11323 //finally add the new kids resulting from the container added 11324 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() ); 11325 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() ); 11326 } 11327 } 11328 } 11329 } 11330 //<--- i94258 11331 11332 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle ) 11333 { 11334 bool bSuccess = false; 11335 11336 if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) ) 11337 { 11338 // end eventual previous marked content sequence 11339 endStructureElementMCSeq(); 11340 11341 m_nCurrentStructElement = nEle; 11342 m_bEmitStructure = checkEmitStructure(); 11343 #if OSL_DEBUG_LEVEL > 1 11344 OStringBuffer aLine( "setCurrentStructureElement " ); 11345 aLine.append( m_nCurrentStructElement ); 11346 aLine.append( ": " ); 11347 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); 11348 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() ) 11349 { 11350 aLine.append( " aliased as \"" ); 11351 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); 11352 aLine.append( '\"' ); 11353 } 11354 if( ! m_bEmitStructure ) 11355 aLine.append( " (inside NonStruct)" ); 11356 emitComment( aLine.getStr() ); 11357 #endif 11358 bSuccess = true; 11359 } 11360 11361 return bSuccess; 11362 } 11363 11364 sal_Int32 PDFWriterImpl::getCurrentStructureElement() 11365 { 11366 return m_nCurrentStructElement; 11367 } 11368 11369 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal ) 11370 { 11371 if( !m_aContext.Tagged ) 11372 return false; 11373 11374 bool bInsert = false; 11375 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11376 { 11377 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11378 switch( eAttr ) 11379 { 11380 case PDFWriter::Placement: 11381 if( eVal == PDFWriter::Block || 11382 eVal == PDFWriter::Inline || 11383 eVal == PDFWriter::Before || 11384 eVal == PDFWriter::Start || 11385 eVal == PDFWriter::End ) 11386 bInsert = true; 11387 break; 11388 case PDFWriter::WritingMode: 11389 if( eVal == PDFWriter::LrTb || 11390 eVal == PDFWriter::RlTb || 11391 eVal == PDFWriter::TbRl ) 11392 { 11393 bInsert = true; 11394 } 11395 break; 11396 case PDFWriter::TextAlign: 11397 if( eVal == PDFWriter::Start || 11398 eVal == PDFWriter::Center || 11399 eVal == PDFWriter::End || 11400 eVal == PDFWriter::Justify ) 11401 { 11402 if( eType == PDFWriter::Paragraph || 11403 eType == PDFWriter::Heading || 11404 eType == PDFWriter::H1 || 11405 eType == PDFWriter::H2 || 11406 eType == PDFWriter::H3 || 11407 eType == PDFWriter::H4 || 11408 eType == PDFWriter::H5 || 11409 eType == PDFWriter::H6 || 11410 eType == PDFWriter::List || 11411 eType == PDFWriter::ListItem || 11412 eType == PDFWriter::LILabel || 11413 eType == PDFWriter::LIBody || 11414 eType == PDFWriter::Table || 11415 eType == PDFWriter::TableRow || 11416 eType == PDFWriter::TableHeader || 11417 eType == PDFWriter::TableData ) 11418 { 11419 bInsert = true; 11420 } 11421 } 11422 break; 11423 case PDFWriter::Width: 11424 case PDFWriter::Height: 11425 if( eVal == PDFWriter::Auto ) 11426 { 11427 if( eType == PDFWriter::Figure || 11428 eType == PDFWriter::Formula || 11429 eType == PDFWriter::Form || 11430 eType == PDFWriter::Table || 11431 eType == PDFWriter::TableHeader || 11432 eType == PDFWriter::TableData ) 11433 { 11434 bInsert = true; 11435 } 11436 } 11437 break; 11438 case PDFWriter::BlockAlign: 11439 if( eVal == PDFWriter::Before || 11440 eVal == PDFWriter::Middle || 11441 eVal == PDFWriter::After || 11442 eVal == PDFWriter::Justify ) 11443 { 11444 if( eType == PDFWriter::TableHeader || 11445 eType == PDFWriter::TableData ) 11446 { 11447 bInsert = true; 11448 } 11449 } 11450 break; 11451 case PDFWriter::InlineAlign: 11452 if( eVal == PDFWriter::Start || 11453 eVal == PDFWriter::Center || 11454 eVal == PDFWriter::End ) 11455 { 11456 if( eType == PDFWriter::TableHeader || 11457 eType == PDFWriter::TableData ) 11458 { 11459 bInsert = true; 11460 } 11461 } 11462 break; 11463 case PDFWriter::LineHeight: 11464 if( eVal == PDFWriter::Normal || 11465 eVal == PDFWriter::Auto ) 11466 { 11467 // only for ILSE and BLSE 11468 if( eType == PDFWriter::Paragraph || 11469 eType == PDFWriter::Heading || 11470 eType == PDFWriter::H1 || 11471 eType == PDFWriter::H2 || 11472 eType == PDFWriter::H3 || 11473 eType == PDFWriter::H4 || 11474 eType == PDFWriter::H5 || 11475 eType == PDFWriter::H6 || 11476 eType == PDFWriter::List || 11477 eType == PDFWriter::ListItem || 11478 eType == PDFWriter::LILabel || 11479 eType == PDFWriter::LIBody || 11480 eType == PDFWriter::Table || 11481 eType == PDFWriter::TableRow || 11482 eType == PDFWriter::TableHeader || 11483 eType == PDFWriter::TableData || 11484 eType == PDFWriter::Span || 11485 eType == PDFWriter::Quote || 11486 eType == PDFWriter::Note || 11487 eType == PDFWriter::Reference || 11488 eType == PDFWriter::BibEntry || 11489 eType == PDFWriter::Code || 11490 eType == PDFWriter::Link ) 11491 { 11492 bInsert = true; 11493 } 11494 } 11495 break; 11496 case PDFWriter::TextDecorationType: 11497 if( eVal == PDFWriter::NONE || 11498 eVal == PDFWriter::Underline || 11499 eVal == PDFWriter::Overline || 11500 eVal == PDFWriter::LineThrough ) 11501 { 11502 // only for ILSE and BLSE 11503 if( eType == PDFWriter::Paragraph || 11504 eType == PDFWriter::Heading || 11505 eType == PDFWriter::H1 || 11506 eType == PDFWriter::H2 || 11507 eType == PDFWriter::H3 || 11508 eType == PDFWriter::H4 || 11509 eType == PDFWriter::H5 || 11510 eType == PDFWriter::H6 || 11511 eType == PDFWriter::List || 11512 eType == PDFWriter::ListItem || 11513 eType == PDFWriter::LILabel || 11514 eType == PDFWriter::LIBody || 11515 eType == PDFWriter::Table || 11516 eType == PDFWriter::TableRow || 11517 eType == PDFWriter::TableHeader || 11518 eType == PDFWriter::TableData || 11519 eType == PDFWriter::Span || 11520 eType == PDFWriter::Quote || 11521 eType == PDFWriter::Note || 11522 eType == PDFWriter::Reference || 11523 eType == PDFWriter::BibEntry || 11524 eType == PDFWriter::Code || 11525 eType == PDFWriter::Link ) 11526 { 11527 bInsert = true; 11528 } 11529 } 11530 break; 11531 case PDFWriter::ListNumbering: 11532 if( eVal == PDFWriter::NONE || 11533 eVal == PDFWriter::Disc || 11534 eVal == PDFWriter::Circle || 11535 eVal == PDFWriter::Square || 11536 eVal == PDFWriter::Decimal || 11537 eVal == PDFWriter::UpperRoman || 11538 eVal == PDFWriter::LowerRoman || 11539 eVal == PDFWriter::UpperAlpha || 11540 eVal == PDFWriter::LowerAlpha ) 11541 { 11542 if( eType == PDFWriter::List ) 11543 bInsert = true; 11544 } 11545 break; 11546 default: break; 11547 } 11548 } 11549 11550 if( bInsert ) 11551 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal ); 11552 #if OSL_DEBUG_LEVEL > 1 11553 else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11554 fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n", 11555 getAttributeTag( eAttr ), 11556 getAttributeValueTag( eVal ), 11557 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ), 11558 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() 11559 ); 11560 #endif 11561 11562 return bInsert; 11563 } 11564 11565 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue ) 11566 { 11567 if( ! m_aContext.Tagged ) 11568 return false; 11569 11570 bool bInsert = false; 11571 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11572 { 11573 if( eAttr == PDFWriter::Language ) 11574 { 11575 m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue ); 11576 return true; 11577 } 11578 11579 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11580 switch( eAttr ) 11581 { 11582 case PDFWriter::SpaceBefore: 11583 case PDFWriter::SpaceAfter: 11584 case PDFWriter::StartIndent: 11585 case PDFWriter::EndIndent: 11586 // just for BLSE 11587 if( eType == PDFWriter::Paragraph || 11588 eType == PDFWriter::Heading || 11589 eType == PDFWriter::H1 || 11590 eType == PDFWriter::H2 || 11591 eType == PDFWriter::H3 || 11592 eType == PDFWriter::H4 || 11593 eType == PDFWriter::H5 || 11594 eType == PDFWriter::H6 || 11595 eType == PDFWriter::List || 11596 eType == PDFWriter::ListItem || 11597 eType == PDFWriter::LILabel || 11598 eType == PDFWriter::LIBody || 11599 eType == PDFWriter::Table || 11600 eType == PDFWriter::TableRow || 11601 eType == PDFWriter::TableHeader || 11602 eType == PDFWriter::TableData ) 11603 { 11604 bInsert = true; 11605 } 11606 break; 11607 case PDFWriter::TextIndent: 11608 // paragraph like BLSE and additional elements 11609 if( eType == PDFWriter::Paragraph || 11610 eType == PDFWriter::Heading || 11611 eType == PDFWriter::H1 || 11612 eType == PDFWriter::H2 || 11613 eType == PDFWriter::H3 || 11614 eType == PDFWriter::H4 || 11615 eType == PDFWriter::H5 || 11616 eType == PDFWriter::H6 || 11617 eType == PDFWriter::LILabel || 11618 eType == PDFWriter::LIBody || 11619 eType == PDFWriter::TableHeader || 11620 eType == PDFWriter::TableData ) 11621 { 11622 bInsert = true; 11623 } 11624 break; 11625 case PDFWriter::Width: 11626 case PDFWriter::Height: 11627 if( eType == PDFWriter::Figure || 11628 eType == PDFWriter::Formula || 11629 eType == PDFWriter::Form || 11630 eType == PDFWriter::Table || 11631 eType == PDFWriter::TableHeader || 11632 eType == PDFWriter::TableData ) 11633 { 11634 bInsert = true; 11635 } 11636 break; 11637 case PDFWriter::LineHeight: 11638 case PDFWriter::BaselineShift: 11639 // only for ILSE and BLSE 11640 if( eType == PDFWriter::Paragraph || 11641 eType == PDFWriter::Heading || 11642 eType == PDFWriter::H1 || 11643 eType == PDFWriter::H2 || 11644 eType == PDFWriter::H3 || 11645 eType == PDFWriter::H4 || 11646 eType == PDFWriter::H5 || 11647 eType == PDFWriter::H6 || 11648 eType == PDFWriter::List || 11649 eType == PDFWriter::ListItem || 11650 eType == PDFWriter::LILabel || 11651 eType == PDFWriter::LIBody || 11652 eType == PDFWriter::Table || 11653 eType == PDFWriter::TableRow || 11654 eType == PDFWriter::TableHeader || 11655 eType == PDFWriter::TableData || 11656 eType == PDFWriter::Span || 11657 eType == PDFWriter::Quote || 11658 eType == PDFWriter::Note || 11659 eType == PDFWriter::Reference || 11660 eType == PDFWriter::BibEntry || 11661 eType == PDFWriter::Code || 11662 eType == PDFWriter::Link ) 11663 { 11664 bInsert = true; 11665 } 11666 break; 11667 case PDFWriter::RowSpan: 11668 case PDFWriter::ColSpan: 11669 // only for table cells 11670 if( eType == PDFWriter::TableHeader || 11671 eType == PDFWriter::TableData ) 11672 { 11673 bInsert = true; 11674 } 11675 break; 11676 case PDFWriter::LinkAnnotation: 11677 if( eType == PDFWriter::Link ) 11678 bInsert = true; 11679 break; 11680 default: break; 11681 } 11682 } 11683 11684 if( bInsert ) 11685 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue ); 11686 #if OSL_DEBUG_LEVEL > 1 11687 else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11688 fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n", 11689 getAttributeTag( eAttr ), 11690 (int)nValue, 11691 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ), 11692 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() ); 11693 #endif 11694 11695 return bInsert; 11696 } 11697 11698 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect ) 11699 { 11700 sal_Int32 nPageNr = m_nCurrentPage; 11701 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged ) 11702 return; 11703 11704 11705 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11706 { 11707 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11708 if( eType == PDFWriter::Figure || 11709 eType == PDFWriter::Formula || 11710 eType == PDFWriter::Form || 11711 eType == PDFWriter::Table ) 11712 { 11713 m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect; 11714 // convert to default user space now, since the mapmode may change 11715 m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox ); 11716 } 11717 } 11718 } 11719 11720 void PDFWriterImpl::setActualText( const String& rText ) 11721 { 11722 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) 11723 { 11724 m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText; 11725 } 11726 } 11727 11728 void PDFWriterImpl::setAlternateText( const String& rText ) 11729 { 11730 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) 11731 { 11732 m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText; 11733 } 11734 } 11735 11736 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr ) 11737 { 11738 if( nPageNr < 0 ) 11739 nPageNr = m_nCurrentPage; 11740 11741 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11742 return; 11743 11744 m_aPages[ nPageNr ].m_nDuration = nSeconds; 11745 } 11746 11747 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr ) 11748 { 11749 if( nPageNr < 0 ) 11750 nPageNr = m_nCurrentPage; 11751 11752 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11753 return; 11754 11755 m_aPages[ nPageNr ].m_eTransition = eType; 11756 m_aPages[ nPageNr ].m_nTransTime = nMilliSec; 11757 } 11758 11759 void PDFWriterImpl::ensureUniqueRadioOnValues() 11760 { 11761 // loop over radio groups 11762 for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin(); 11763 group != m_aRadioGroupWidgets.end(); ++group ) 11764 { 11765 PDFWidget& rGroupWidget = m_aWidgets[ group->second ]; 11766 // check whether all kids have a unique OnValue 11767 std::hash_map< OUString, sal_Int32, OUStringHash > aOnValues; 11768 int nChildren = rGroupWidget.m_aKidsIndex.size(); 11769 bool bIsUnique = true; 11770 for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ ) 11771 { 11772 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11773 const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue; 11774 #if OSL_DEBUG_LEVEL > 1 11775 fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() ); 11776 #endif 11777 if( aOnValues.find( rVal ) == aOnValues.end() ) 11778 { 11779 aOnValues[ rVal ] = 1; 11780 } 11781 else 11782 { 11783 bIsUnique = false; 11784 } 11785 } 11786 if( ! bIsUnique ) 11787 { 11788 #if OSL_DEBUG_LEVEL > 1 11789 fprintf( stderr, "enforcing unique OnValues\n" ); 11790 #endif 11791 // make unique by using ascending OnValues 11792 for( int nKid = 0; nKid < nChildren; nKid++ ) 11793 { 11794 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11795 PDFWidget& rKid = m_aWidgets[nKidIndex]; 11796 rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) ); 11797 if( ! rKid.m_aValue.equalsAscii( "Off" ) ) 11798 rKid.m_aValue = rKid.m_aOnValue; 11799 } 11800 } 11801 // finally move the "Yes" appearance to the OnValue appearance 11802 for( int nKid = 0; nKid < nChildren; nKid++ ) 11803 { 11804 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11805 PDFWidget& rKid = m_aWidgets[nKidIndex]; 11806 PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" ); 11807 if( app_it != rKid.m_aAppearances.end() ) 11808 { 11809 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" ); 11810 if( stream_it != app_it->second.end() ) 11811 { 11812 SvMemoryStream* pStream = stream_it->second; 11813 app_it->second.erase( stream_it ); 11814 OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 ); 11815 appendName( rKid.m_aOnValue, aBuf ); 11816 (app_it->second)[ aBuf.makeStringAndClear() ] = pStream; 11817 } 11818 #if OSL_DEBUG_LEVEL > 1 11819 else 11820 fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" ); 11821 #endif 11822 } 11823 // update selected radio button 11824 if( ! rKid.m_aValue.equalsAscii( "Off" ) ) 11825 { 11826 rGroupWidget.m_aValue = rKid.m_aValue; 11827 } 11828 } 11829 } 11830 } 11831 11832 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn ) 11833 { 11834 sal_Int32 nRadioGroupWidget = -1; 11835 11836 std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup ); 11837 11838 if( it == m_aRadioGroupWidgets.end() ) 11839 { 11840 m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget = 11841 sal_Int32(m_aWidgets.size()); 11842 11843 // new group, insert the radiobutton 11844 m_aWidgets.push_back( PDFWidget() ); 11845 m_aWidgets.back().m_nObject = createObject(); 11846 m_aWidgets.back().m_nPage = m_nCurrentPage; 11847 m_aWidgets.back().m_eType = PDFWriter::RadioButton; 11848 m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup; 11849 m_aWidgets.back().m_nFlags |= 0x0000C000; // NoToggleToOff and Radio bits 11850 11851 createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn ); 11852 } 11853 else 11854 nRadioGroupWidget = it->second; 11855 11856 return nRadioGroupWidget; 11857 } 11858 11859 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr ) 11860 { 11861 if( nPageNr < 0 ) 11862 nPageNr = m_nCurrentPage; 11863 11864 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11865 return -1; 11866 11867 sal_Int32 nNewWidget = m_aWidgets.size(); 11868 m_aWidgets.push_back( PDFWidget() ); 11869 11870 m_aWidgets.back().m_nObject = createObject(); 11871 m_aWidgets.back().m_aRect = rControl.Location; 11872 m_aWidgets.back().m_nPage = nPageNr; 11873 m_aWidgets.back().m_eType = rControl.getType(); 11874 11875 sal_Int32 nRadioGroupWidget = -1; 11876 // for unknown reasons the radio buttons of a radio group must not have a 11877 // field name, else the buttons are in fact check boxes - 11878 // that is multiple buttons of the radio group can be selected 11879 if( rControl.getType() == PDFWriter::RadioButton ) 11880 nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) ); 11881 else 11882 { 11883 createWidgetFieldName( nNewWidget, rControl ); 11884 } 11885 11886 // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid 11887 PDFWidget& rNewWidget = m_aWidgets[nNewWidget]; 11888 rNewWidget.m_aDescription = rControl.Description; 11889 rNewWidget.m_aText = rControl.Text; 11890 rNewWidget.m_nTextStyle = rControl.TextStyle & 11891 ( TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP | 11892 TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM | 11893 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 11894 rNewWidget.m_nTabOrder = rControl.TabOrder; 11895 11896 // various properties are set via the flags (/Ff) property of the field dict 11897 if( rControl.ReadOnly ) 11898 rNewWidget.m_nFlags |= 1; 11899 if( rControl.getType() == PDFWriter::PushButton ) 11900 { 11901 const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl); 11902 if( rNewWidget.m_nTextStyle == 0 ) 11903 rNewWidget.m_nTextStyle = 11904 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | 11905 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 11906 11907 rNewWidget.m_nFlags |= 0x00010000; 11908 if( rBtn.URL.getLength() ) 11909 rNewWidget.m_aListEntries.push_back( rBtn.URL ); 11910 rNewWidget.m_bSubmit = rBtn.Submit; 11911 rNewWidget.m_bSubmitGet = rBtn.SubmitGet; 11912 rNewWidget.m_nDest = rBtn.Dest; 11913 createDefaultPushButtonAppearance( rNewWidget, rBtn ); 11914 } 11915 else if( rControl.getType() == PDFWriter::RadioButton ) 11916 { 11917 const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl); 11918 if( rNewWidget.m_nTextStyle == 0 ) 11919 rNewWidget.m_nTextStyle = 11920 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 11921 /* PDF sees a RadioButton group as one radio button with 11922 * children which are in turn check boxes 11923 * 11924 * so we need to create a radio button on demand for a new group 11925 * and insert a checkbox for each RadioButtonWidget as its child 11926 */ 11927 rNewWidget.m_eType = PDFWriter::CheckBox; 11928 rNewWidget.m_nRadioGroup = rBtn.RadioGroup; 11929 11930 DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" ); 11931 11932 PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget]; 11933 rRadioButton.m_aKids.push_back( rNewWidget.m_nObject ); 11934 rRadioButton.m_aKidsIndex.push_back( nNewWidget ); 11935 rNewWidget.m_nParent = rRadioButton.m_nObject; 11936 11937 rNewWidget.m_aValue = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) ); 11938 rNewWidget.m_aOnValue = rBtn.OnValue; 11939 if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected ) 11940 { 11941 rNewWidget.m_aValue = rNewWidget.m_aOnValue; 11942 rRadioButton.m_aValue = rNewWidget.m_aOnValue; 11943 } 11944 createDefaultRadioButtonAppearance( rNewWidget, rBtn ); 11945 11946 // union rect of radio group 11947 Rectangle aRect = rNewWidget.m_aRect; 11948 m_aPages[ nPageNr ].convertRect( aRect ); 11949 rRadioButton.m_aRect.Union( aRect ); 11950 } 11951 else if( rControl.getType() == PDFWriter::CheckBox ) 11952 { 11953 const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl); 11954 if( rNewWidget.m_nTextStyle == 0 ) 11955 rNewWidget.m_nTextStyle = 11956 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 11957 11958 rNewWidget.m_aValue = OUString::createFromAscii( rBox.Checked ? "Yes" : "Off" ); 11959 // create default appearance before m_aRect gets transformed 11960 createDefaultCheckBoxAppearance( rNewWidget, rBox ); 11961 } 11962 else if( rControl.getType() == PDFWriter::ListBox ) 11963 { 11964 if( rNewWidget.m_nTextStyle == 0 ) 11965 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER; 11966 11967 const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl); 11968 rNewWidget.m_aListEntries = rLstBox.Entries; 11969 rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries; 11970 rNewWidget.m_aValue = rLstBox.Text; 11971 if( rLstBox.DropDown ) 11972 rNewWidget.m_nFlags |= 0x00020000; 11973 if( rLstBox.Sort ) 11974 rNewWidget.m_nFlags |= 0x00080000; 11975 if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 ) 11976 rNewWidget.m_nFlags |= 0x00200000; 11977 11978 createDefaultListBoxAppearance( rNewWidget, rLstBox ); 11979 } 11980 else if( rControl.getType() == PDFWriter::ComboBox ) 11981 { 11982 if( rNewWidget.m_nTextStyle == 0 ) 11983 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER; 11984 11985 const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl); 11986 rNewWidget.m_aValue = rBox.Text; 11987 rNewWidget.m_aListEntries = rBox.Entries; 11988 rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag 11989 if( rBox.Sort ) 11990 rNewWidget.m_nFlags |= 0x00080000; 11991 11992 PDFWriter::ListBoxWidget aLBox; 11993 aLBox.Name = rBox.Name; 11994 aLBox.Description = rBox.Description; 11995 aLBox.Text = rBox.Text; 11996 aLBox.TextStyle = rBox.TextStyle; 11997 aLBox.ReadOnly = rBox.ReadOnly; 11998 aLBox.Border = rBox.Border; 11999 aLBox.BorderColor = rBox.BorderColor; 12000 aLBox.Background = rBox.Background; 12001 aLBox.BackgroundColor = rBox.BackgroundColor; 12002 aLBox.TextFont = rBox.TextFont; 12003 aLBox.TextColor = rBox.TextColor; 12004 aLBox.DropDown = true; 12005 aLBox.Sort = rBox.Sort; 12006 aLBox.MultiSelect = false; 12007 aLBox.Entries = rBox.Entries; 12008 12009 createDefaultListBoxAppearance( rNewWidget, aLBox ); 12010 } 12011 else if( rControl.getType() == PDFWriter::Edit ) 12012 { 12013 if( rNewWidget.m_nTextStyle == 0 ) 12014 rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER; 12015 12016 const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl); 12017 if( rEdit.MultiLine ) 12018 { 12019 rNewWidget.m_nFlags |= 0x00001000; 12020 rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 12021 } 12022 if( rEdit.Password ) 12023 rNewWidget.m_nFlags |= 0x00002000; 12024 if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 ) 12025 rNewWidget.m_nFlags |= 0x00100000; 12026 rNewWidget.m_nMaxLen = rEdit.MaxLen; 12027 rNewWidget.m_aValue = rEdit.Text; 12028 12029 createDefaultEditAppearance( rNewWidget, rEdit ); 12030 } 12031 12032 // convert to default user space now, since the mapmode may change 12033 // note: create default appearances before m_aRect gets transformed 12034 m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect ); 12035 12036 // insert widget to page's annotation list 12037 m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject ); 12038 12039 // mark page as having widgets 12040 m_aPages[ nPageNr ].m_bHasWidgets = true; 12041 12042 return nNewWidget; 12043 } 12044 12045 void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl ) 12046 { 12047 if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() ) 12048 return; 12049 12050 PDFWidget& rWidget = m_aWidgets[ nControl ]; 12051 m_nCurrentControl = nControl; 12052 12053 SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 ); 12054 // back conversion of control rect to current MapMode; necessary because 12055 // MapMode between createControl and beginControlAppearance 12056 // could have changed; therefore the widget rectangle is 12057 // already converted 12058 Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ), 12059 rWidget.m_aRect.GetSize() ); 12060 aBack = lcl_convert( m_aMapMode, 12061 m_aGraphicsStack.front().m_aMapMode, 12062 getReferenceDevice(), 12063 aBack ); 12064 beginRedirect( pControlStream, aBack ); 12065 writeBuffer( "/Tx BMC\n", 8 ); 12066 } 12067 12068 bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState ) 12069 { 12070 bool bRet = false; 12071 if( ! m_aOutputStreams.empty() ) 12072 writeBuffer( "\nEMC\n", 5 ); 12073 SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect()); 12074 if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() ) 12075 { 12076 PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ]; 12077 OString aState, aStyle; 12078 switch( rWidget.m_eType ) 12079 { 12080 case PDFWriter::PushButton: 12081 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12082 { 12083 aState = (eState == PDFWriter::Up) ? "N" : "D"; 12084 aStyle = "Standard"; 12085 } 12086 break; 12087 case PDFWriter::CheckBox: 12088 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12089 { 12090 aState = "N"; 12091 aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes"; 12092 /* cf PDFReference 3rd ed. V1.4 p539: 12093 recommended name for on state is "Yes", 12094 recommended name for off state is "Off" 12095 */ 12096 } 12097 break; 12098 case PDFWriter::RadioButton: 12099 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12100 { 12101 aState = "N"; 12102 if( eState == PDFWriter::Up ) 12103 aStyle = "Off"; 12104 else 12105 { 12106 OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 ); 12107 appendName( rWidget.m_aOnValue, aBuf ); 12108 aStyle = aBuf.makeStringAndClear(); 12109 } 12110 } 12111 break; 12112 case PDFWriter::Edit: 12113 aState = "N"; 12114 aStyle = "Standard"; 12115 break; 12116 case PDFWriter::ListBox: 12117 case PDFWriter::ComboBox: 12118 case PDFWriter::Hierarchy: 12119 break; 12120 } 12121 if( aState.getLength() && aStyle.getLength() ) 12122 { 12123 // delete eventual existing stream 12124 PDFAppearanceStreams::iterator it = 12125 rWidget.m_aAppearances[ aState ].find( aStyle ); 12126 if( it != rWidget.m_aAppearances[ aState ].end() ) 12127 delete it->second; 12128 rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance; 12129 bRet = true; 12130 } 12131 } 12132 12133 if( ! bRet ) 12134 delete pAppearance; 12135 12136 m_nCurrentControl = -1; 12137 12138 return bRet; 12139 } 12140 12141 void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress ) 12142 { 12143 if( pStream ) 12144 { 12145 m_aAdditionalStreams.push_back( PDFAddStream() ); 12146 PDFAddStream& rStream = m_aAdditionalStreams.back(); 12147 rStream.m_aMimeType = rMimeType.Len() 12148 ? OUString( rMimeType ) 12149 : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) ); 12150 rStream.m_pStream = pStream; 12151 rStream.m_bCompress = bCompress; 12152 } 12153 } 12154 12155 12156 12157