1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_vcl.hxx" 26 27 #define _USE_MATH_DEFINES 28 #include <math.h> 29 #include <algorithm> 30 31 #include <tools/urlobj.hxx> 32 33 #include <pdfwriter_impl.hxx> 34 35 #include <basegfx/polygon/b2dpolygon.hxx> 36 #include <basegfx/polygon/b2dpolypolygon.hxx> 37 #include <basegfx/polygon/b2dpolygontools.hxx> 38 #include <basegfx/polygon/b2dpolypolygontools.hxx> 39 #include <basegfx/polygon/b2dpolypolygoncutter.hxx> 40 #include <basegfx/matrix/b2dhommatrix.hxx> 41 42 #include <osl/thread.h> 43 #include <osl/file.h> 44 45 #include <rtl/crc.h> 46 #include <rtl/digest.h> 47 #include <rtl/ustrbuf.hxx> 48 49 #include <tools/debug.hxx> 50 #include <tools/zcodec.hxx> 51 #include <tools/stream.hxx> 52 53 #include <i18npool/mslangid.hxx> 54 55 #include <vcl/virdev.hxx> 56 #include <vcl/bmpacc.hxx> 57 #include <vcl/bitmapex.hxx> 58 #include <vcl/image.hxx> 59 #include <vcl/metric.hxx> 60 #include <vcl/svapp.hxx> 61 #include <vcl/lineinfo.hxx> 62 #include "vcl/cvtgrf.hxx" 63 #include "vcl/strhelper.hxx" 64 65 #include <fontsubset.hxx> 66 #include <outdev.h> 67 #include <sallayout.hxx> 68 #include <textlayout.hxx> 69 #include <salgdi.hxx> 70 71 #include <icc/sRGB-IEC61966-2.1.hxx> 72 73 #include <comphelper/processfactory.hxx> 74 75 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 76 #include <com/sun/star/util/URL.hpp> 77 78 #include "cppuhelper/implbase1.hxx" 79 80 using namespace vcl; 81 using namespace rtl; 82 83 #if (OSL_DEBUG_LEVEL < 2) 84 #define COMPRESS_PAGES 85 #else 86 #define DEBUG_DISABLE_PDFCOMPRESSION // also do not compress streams 87 #endif 88 89 #ifdef DO_TEST_PDF 90 class PDFTestOutputStream : public PDFOutputStream 91 { 92 public: 93 virtual ~PDFTestOutputStream(); 94 virtual void write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream ); 95 }; 96 97 PDFTestOutputStream::~PDFTestOutputStream() 98 { 99 } 100 101 void PDFTestOutputStream::write( const com::sun::star::uno::Reference< com::sun::star::io::XOutputStream >& xStream ) 102 { 103 OString aStr( "lalala\ntest\ntest\ntest" ); 104 com::sun::star::uno::Sequence< sal_Int8 > aData( aStr.getLength() ); 105 rtl_copyMemory( aData.getArray(), aStr.getStr(), aStr.getLength() ); 106 xStream->writeBytes( aData ); 107 } 108 109 // this test code cannot be used to test PDF/A-1 because it forces 110 // control item (widgets) to bypass the structure controlling 111 // the embedding of such elements in actual run 112 void doTestCode() 113 { 114 static const char* pHome = getenv( "HOME" ); 115 rtl::OUString aTestFile( RTL_CONSTASCII_USTRINGPARAM( "file://" ) ); 116 aTestFile += rtl::OUString( pHome, strlen( pHome ), RTL_TEXTENCODING_MS_1252 ); 117 aTestFile += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/pdf_export_test.pdf" ) ); 118 119 PDFWriter::PDFWriterContext aContext; 120 aContext.URL = aTestFile; 121 aContext.Version = PDFWriter::PDF_1_4; 122 aContext.Tagged = true; 123 aContext.InitialPage = 2; 124 aContext.DocumentInfo.Title = OUString( RTL_CONSTASCII_USTRINGPARAM( "PDF export test document" ) ); 125 aContext.DocumentInfo.Producer = OUString( RTL_CONSTASCII_USTRINGPARAM( "VCL" ) ); 126 127 PDFWriter aWriter( aContext ); 128 aWriter.NewPage( 595, 842 ); 129 aWriter.BeginStructureElement( PDFWriter::Document ); 130 // set duration of 3 sec for first page 131 aWriter.SetAutoAdvanceTime( 3 ); 132 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); 133 134 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 135 aWriter.SetLineColor( Color( COL_LIGHTGREEN ) ); 136 aWriter.DrawRect( Rectangle( Point( 2000, 200 ), Size( 8000, 3000 ) ), 5000, 2000 ); 137 138 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) ); 139 aWriter.SetTextColor( Color( COL_BLACK ) ); 140 aWriter.SetLineColor( Color( COL_BLACK ) ); 141 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) ); 142 143 Rectangle aRect( Point( 5000, 5000 ), Size( 6000, 3000 ) ); 144 aWriter.DrawRect( aRect ); 145 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 1" ) ) ); 146 sal_Int32 nFirstLink = aWriter.CreateLink( aRect ); 147 PDFNote aNote; 148 aNote.Title = String( RTL_CONSTASCII_USTRINGPARAM( "A small test note" ) ); 149 aNote.Contents = String( RTL_CONSTASCII_USTRINGPARAM( "There is no business like show business like no business i know. Everything about it is appealing." ) ); 150 aWriter.CreateNote( Rectangle( Point( aRect.Right(), aRect.Top() ), Size( 6000, 3000 ) ), aNote ); 151 152 Rectangle aTargetRect( Point( 3000, 23000 ), Size( 12000, 6000 ) ); 153 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 154 aWriter.DrawRect( aTargetRect ); 155 aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest second link" ) ) ); 156 sal_Int32 nSecondDest = aWriter.CreateDest( aTargetRect ); 157 158 aWriter.BeginStructureElement( PDFWriter::Section ); 159 aWriter.BeginStructureElement( PDFWriter::Heading ); 160 aWriter.DrawText( Point(4500, 9000), String( RTL_CONSTASCII_USTRINGPARAM( "A small structure test" ) ) ); 161 aWriter.EndStructureElement(); 162 aWriter.BeginStructureElement( PDFWriter::Paragraph ); 163 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb ); 164 aWriter.SetStructureAttribute( PDFWriter::TextDecorationType, PDFWriter::Underline ); 165 aWriter.DrawText( Rectangle( Point( 4500, 10000 ), Size( 12000, 6000 ) ), 166 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." ) ), 167 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK 168 ); 169 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." ) ) ); 170 aWriter.SetAlternateText( String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph contains some lengthy nonsense to test structural element emission of PDFWriter." ) ) ); 171 aWriter.EndStructureElement(); 172 sal_Int32 nLongPara = aWriter.BeginStructureElement( PDFWriter::Paragraph ); 173 aWriter.SetStructureAttribute( PDFWriter::WritingMode, PDFWriter::LrTb ); 174 aWriter.DrawText( Rectangle( Point( 4500, 19000 ), Size( 12000, 1000 ) ), 175 String( RTL_CONSTASCII_USTRINGPARAM( "This paragraph is nothing special either but ends on the next page structurewise" ) ), 176 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK 177 ); 178 179 aWriter.NewPage( 595, 842 ); 180 // test AddStream interface 181 aWriter.AddStream( String( RTL_CONSTASCII_USTRINGPARAM( "text/plain" ) ), new PDFTestOutputStream(), true ); 182 // set transitional mode 183 aWriter.SetPageTransition( PDFWriter::WipeRightToLeft, 1500 ); 184 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); 185 aWriter.SetTextColor( Color( COL_BLACK ) ); 186 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) ); 187 aWriter.DrawText( Rectangle( Point( 4500, 1500 ), Size( 12000, 3000 ) ), 188 String( RTL_CONSTASCII_USTRINGPARAM( "Here's where all things come to an end ... well at least the paragaph from the last page." ) ), 189 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK 190 ); 191 aWriter.EndStructureElement(); 192 193 aWriter.SetFillColor( Color( COL_LIGHTBLUE ) ); 194 // disable structure 195 aWriter.BeginStructureElement( PDFWriter::NonStructElement ); 196 aWriter.DrawRect( aRect ); 197 aWriter.BeginStructureElement( PDFWriter::Paragraph ); 198 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "Link annot 2" ) ) ); 199 sal_Int32 nSecondLink = aWriter.CreateLink( aRect ); 200 201 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 202 aWriter.BeginStructureElement( PDFWriter::ListItem ); 203 aWriter.DrawRect( aTargetRect ); 204 aWriter.DrawText( aTargetRect, String( RTL_CONSTASCII_USTRINGPARAM( "Dest first link" ) ) ); 205 sal_Int32 nFirstDest = aWriter.CreateDest( aTargetRect ); 206 // enable structure 207 aWriter.EndStructureElement(); 208 // add something to the long paragraph as an afterthought 209 sal_Int32 nSaveStruct = aWriter.GetCurrentStructureElement(); 210 aWriter.SetCurrentStructureElement( nLongPara ); 211 aWriter.DrawText( Rectangle( Point( 4500,4500 ), Size( 12000, 1000 ) ), 212 String( RTL_CONSTASCII_USTRINGPARAM( "Add something to the longish paragraph above." ) ), 213 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 214 aWriter.SetCurrentStructureElement( nSaveStruct ); 215 aWriter.EndStructureElement(); 216 aWriter.EndStructureElement(); 217 aWriter.BeginStructureElement( PDFWriter::Figure ); 218 aWriter.BeginStructureElement( PDFWriter::Caption ); 219 aWriter.DrawText( Point( 4500, 9000 ), String( RTL_CONSTASCII_USTRINGPARAM( "Some drawing stuff inside the structure" ) ) ); 220 aWriter.EndStructureElement(); 221 222 // test clipping 223 basegfx::B2DPolyPolygon aClip; 224 basegfx::B2DPolygon aClipPoly; 225 aClipPoly.append( basegfx::B2DPoint( 8250, 9600 ) ); 226 aClipPoly.append( basegfx::B2DPoint( 16500, 11100 ) ); 227 aClipPoly.append( basegfx::B2DPoint( 8250, 12600 ) ); 228 aClipPoly.append( basegfx::B2DPoint( 4500, 11100 ) ); 229 aClipPoly.setClosed( true ); 230 //aClipPoly.flip(); 231 aClip.append( aClipPoly ); 232 233 aWriter.Push( PUSH_CLIPREGION | PUSH_FILLCOLOR ); 234 aWriter.SetClipRegion( aClip ); 235 aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) ); 236 aWriter.MoveClipRegion( 1000, 500 ); 237 aWriter.SetFillColor( Color( COL_RED ) ); 238 aWriter.DrawEllipse( Rectangle( Point( 4500, 9600 ), Size( 12000, 3000 ) ) ); 239 aWriter.Pop(); 240 // test transparency 241 // draw background 242 Rectangle aTranspRect( Point( 7500, 13500 ), Size( 9000, 6000 ) ); 243 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 244 aWriter.DrawRect( aTranspRect ); 245 aWriter.BeginTransparencyGroup(); 246 247 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 248 aWriter.DrawEllipse( aTranspRect ); 249 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) ); 250 aWriter.DrawText( aTranspRect, 251 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ), 252 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 253 254 aWriter.EndTransparencyGroup( aTranspRect, 50 ); 255 256 // prepare an alpha mask 257 Bitmap aTransMask( Size( 256, 256 ), 8, &Bitmap::GetGreyPalette( 256 ) ); 258 BitmapWriteAccess* pAcc = aTransMask.AcquireWriteAccess(); 259 for( int nX = 0; nX < 256; nX++ ) 260 for( int nY = 0; nY < 256; nY++ ) 261 pAcc->SetPixel( nX, nY, BitmapColor( (sal_uInt8)((nX+nY)/2) ) ); 262 aTransMask.ReleaseAccess( pAcc ); 263 aTransMask.SetPrefMapMode( MAP_MM ); 264 aTransMask.SetPrefSize( Size( 10, 10 ) ); 265 266 aWriter.DrawBitmap( Point( 600, 13500 ), Size( 3000, 3000 ), aTransMask ); 267 268 aTranspRect = Rectangle( Point( 4200, 13500 ), Size( 3000, 3000 ) ); 269 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 270 aWriter.DrawRect( aTranspRect ); 271 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 272 aWriter.DrawEllipse( aTranspRect ); 273 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) ); 274 aWriter.DrawText( aTranspRect, 275 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ), 276 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 277 aTranspRect = Rectangle( Point( 1500, 16500 ), Size( 4800, 3000 ) ); 278 aWriter.SetFillColor( Color( COL_LIGHTRED ) ); 279 aWriter.DrawRect( aTranspRect ); 280 aWriter.BeginTransparencyGroup(); 281 aWriter.SetFillColor( Color( COL_LIGHTGREEN ) ); 282 aWriter.DrawEllipse( aTranspRect ); 283 aWriter.SetTextColor( Color( COL_LIGHTBLUE ) ); 284 aWriter.DrawText( aTranspRect, 285 String( RTL_CONSTASCII_USTRINGPARAM( "Some transparent text" ) ), 286 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 287 aWriter.EndTransparencyGroup( aTranspRect, aTransMask ); 288 289 Bitmap aImageBmp( Size( 256, 256 ), 24 ); 290 pAcc = aImageBmp.AcquireWriteAccess(); 291 pAcc->SetFillColor( Color( 0xff, 0, 0xff ) ); 292 pAcc->FillRect( Rectangle( Point( 0, 0 ), Size( 256, 256 ) ) ); 293 aImageBmp.ReleaseAccess( pAcc ); 294 BitmapEx aBmpEx( aImageBmp, AlphaMask( aTransMask ) ); 295 aWriter.DrawBitmapEx( Point( 1500, 19500 ), Size( 4800, 3000 ), aBmpEx ); 296 297 298 aWriter.EndStructureElement(); 299 aWriter.EndStructureElement(); 300 301 LineInfo aLI( LINE_DASH, 3 ); 302 aLI.SetDashCount( 2 ); 303 aLI.SetDashLen( 50 ); 304 aLI.SetDotCount( 2 ); 305 aLI.SetDotLen( 25 ); 306 aLI.SetDistance( 15 ); 307 Point aLIPoints[] = { Point( 4000, 10000 ), 308 Point( 8000, 12000 ), 309 Point( 3000, 19000 ) }; 310 Polygon aLIPoly( 3, aLIPoints ); 311 aWriter.SetLineColor( Color( COL_BLUE ) ); 312 aWriter.SetFillColor(); 313 aWriter.DrawPolyLine( aLIPoly, aLI ); 314 315 aLI.SetDashCount( 4 ); 316 aLIPoly.Move( 1000, 1000 ); 317 aWriter.DrawPolyLine( aLIPoly, aLI ); 318 319 aWriter.NewPage( 595, 842 ); 320 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); 321 Wallpaper aWall( aTransMask ); 322 aWall.SetStyle( WALLPAPER_TILE ); 323 aWriter.DrawWallpaper( Rectangle( Point( 4400, 4200 ), Size( 10200, 6300 ) ), aWall ); 324 325 aWriter.Push( PUSH_ALL ); 326 aWriter.BeginPattern(Rectangle(Point(0,0),Size(2000,1000))); 327 aWriter.SetFillColor( Color( COL_RED ) ); 328 aWriter.SetLineColor( Color( COL_LIGHTBLUE ) ); 329 Point aFillPoints[] = { Point( 1000, 0 ), 330 Point( 0, 1000 ), 331 Point( 2000, 1000 ) }; 332 aWriter.DrawPolygon( Polygon( 3, aFillPoints ) ); 333 aWriter.DrawBitmap( Point( 200, 200 ), Size( 1600, 600 ), aTransMask ); 334 aWriter.DrawText( Rectangle( Point( 200, 200 ), Size( 1600, 600 ) ), String( RTL_CONSTASCII_USTRINGPARAM( "Pattern" ) ) ); 335 sal_Int32 nPattern = aWriter.EndPattern( SvtGraphicFill::Transform() ); 336 aWriter.Pop(); 337 Rectangle aPolyRect( Point( 3800, 11200 ), Size( 10200, 6300 ) ); 338 aWriter.DrawPolyPolygon( PolyPolygon( Polygon( aPolyRect ) ), nPattern, true ); 339 aWriter.SetFillColor(); 340 aWriter.SetLineColor( Color( COL_LIGHTBLUE ) ); 341 aWriter.DrawRect( aPolyRect ); 342 343 aWriter.NewPage( 595, 842 ); 344 aWriter.SetMapMode( MapMode( MAP_100TH_MM ) ); 345 aWriter.SetFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ), Size( 0, 500 ) ) ); 346 aWriter.SetTextColor( Color( COL_BLACK ) ); 347 aRect = Rectangle( Point( 4500, 6000 ), Size( 6000, 1500 ) ); 348 aWriter.DrawRect( aRect ); 349 aWriter.DrawText( aRect, String( RTL_CONSTASCII_USTRINGPARAM( "www.heise.de" ) ) ); 350 sal_Int32 nURILink = aWriter.CreateLink( aRect ); 351 aWriter.SetLinkURL( nURILink, OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) ) ); 352 353 aWriter.SetLinkDest( nFirstLink, nFirstDest ); 354 aWriter.SetLinkDest( nSecondLink, nSecondDest ); 355 356 // include a button 357 PDFWriter::PushButtonWidget aBtn; 358 aBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testButton" ) ); 359 aBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test button" ) ); 360 aBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "hit me" ) ); 361 aBtn.Location = Rectangle( Point( 4500, 9000 ), Size( 4500, 3000 ) ); 362 aBtn.Border = aBtn.Background = true; 363 aWriter.CreateControl( aBtn ); 364 365 // include a uri button 366 PDFWriter::PushButtonWidget aUriBtn; 367 aUriBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "wwwButton" ) ); 368 aUriBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A URI button" ) ); 369 aUriBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to www" ) ); 370 aUriBtn.Location = Rectangle( Point( 9500, 9000 ), Size( 4500, 3000 ) ); 371 aUriBtn.Border = aUriBtn.Background = true; 372 aUriBtn.URL = OUString( RTL_CONSTASCII_USTRINGPARAM( "http://www.heise.de" ) ); 373 aWriter.CreateControl( aUriBtn ); 374 375 // include a dest button 376 PDFWriter::PushButtonWidget aDstBtn; 377 aDstBtn.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "destButton" ) ); 378 aDstBtn.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A Dest button" ) ); 379 aDstBtn.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "to paragraph" ) ); 380 aDstBtn.Location = Rectangle( Point( 14500, 9000 ), Size( 4500, 3000 ) ); 381 aDstBtn.Border = aDstBtn.Background = true; 382 aDstBtn.Dest = nFirstDest; 383 aWriter.CreateControl( aDstBtn ); 384 385 PDFWriter::CheckBoxWidget aCBox; 386 aCBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox" ) ); 387 aCBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test check box" ) ); 388 aCBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me" ) ); 389 aCBox.Location = Rectangle( Point( 4500, 13500 ), Size( 3000, 750 ) ); 390 aCBox.Checked = true; 391 aCBox.Border = aCBox.Background = false; 392 aWriter.CreateControl( aCBox ); 393 394 PDFWriter::CheckBoxWidget aCBox2; 395 aCBox2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "textCheckBox2" ) ); 396 aCBox2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "Another test check box" ) ); 397 aCBox2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "check me right" ) ); 398 aCBox2.Location = Rectangle( Point( 4500, 14250 ), Size( 3000, 750 ) ); 399 aCBox2.Checked = true; 400 aCBox2.Border = aCBox2.Background = false; 401 aCBox2.ButtonIsLeft = false; 402 aWriter.CreateControl( aCBox2 ); 403 404 PDFWriter::RadioButtonWidget aRB1; 405 aRB1.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_1" ) ); 406 aRB1.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 1" ) ); 407 aRB1.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Despair" ) ); 408 aRB1.Location = Rectangle( Point( 4500, 15000 ), Size( 6000, 1000 ) ); 409 aRB1.Selected = true; 410 aRB1.RadioGroup = 1; 411 aRB1.Border = aRB1.Background = true; 412 aRB1.ButtonIsLeft = false; 413 aRB1.BorderColor = Color( COL_LIGHTGREEN ); 414 aRB1.BackgroundColor = Color( COL_LIGHTBLUE ); 415 aRB1.TextColor = Color( COL_LIGHTRED ); 416 aRB1.TextFont = Font( String( RTL_CONSTASCII_USTRINGPARAM( "Courier" ) ), Size( 0, 800 ) ); 417 aWriter.CreateControl( aRB1 ); 418 419 PDFWriter::RadioButtonWidget aRB2; 420 aRB2.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb2_1" ) ); 421 aRB2.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 2 button 1" ) ); 422 aRB2.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Joy" ) ); 423 aRB2.Location = Rectangle( Point( 10500, 15000 ), Size( 3000, 1000 ) ); 424 aRB2.Selected = true; 425 aRB2.RadioGroup = 2; 426 aWriter.CreateControl( aRB2 ); 427 428 PDFWriter::RadioButtonWidget aRB3; 429 aRB3.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "rb1_2" ) ); 430 aRB3.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "radio 1 button 2" ) ); 431 aRB3.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "Desperation" ) ); 432 aRB3.Location = Rectangle( Point( 4500, 16000 ), Size( 3000, 1000 ) ); 433 aRB3.Selected = true; 434 aRB3.RadioGroup = 1; 435 aWriter.CreateControl( aRB3 ); 436 437 PDFWriter::EditWidget aEditBox; 438 aEditBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testEdit" ) ); 439 aEditBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "A test edit field" ) ); 440 aEditBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "A little test text" ) ); 441 aEditBox.TextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER; 442 aEditBox.Location = Rectangle( Point( 10000, 18000 ), Size( 5000, 1500 ) ); 443 aEditBox.MaxLen = 100; 444 aEditBox.Border = aEditBox.Background = true; 445 aEditBox.BorderColor = Color( COL_BLACK ); 446 aWriter.CreateControl( aEditBox ); 447 448 // normal list box 449 PDFWriter::ListBoxWidget aLstBox; 450 aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testListBox" ) ); 451 aLstBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) ); 452 aLstBox.Description = OUString( RTL_CONSTASCII_USTRINGPARAM( "select me" ) ); 453 aLstBox.Location = Rectangle( Point( 4500, 18000 ), Size( 3000, 1500 ) ); 454 aLstBox.Sort = true; 455 aLstBox.MultiSelect = true; 456 aLstBox.Border = aLstBox.Background = true; 457 aLstBox.BorderColor = Color( COL_BLACK ); 458 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "One" ) ) ); 459 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Two" ) ) ); 460 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Three" ) ) ); 461 aLstBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Four" ) ) ); 462 aLstBox.SelectedEntries.push_back( 1 ); 463 aLstBox.SelectedEntries.push_back( 2 ); 464 aWriter.CreateControl( aLstBox ); 465 466 // dropdown list box 467 aLstBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testDropDownListBox" ) ); 468 aLstBox.DropDown = true; 469 aLstBox.Location = Rectangle( Point( 4500, 19500 ), Size( 3000, 500 ) ); 470 aWriter.CreateControl( aLstBox ); 471 472 // combo box 473 PDFWriter::ComboBoxWidget aComboBox; 474 aComboBox.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "testComboBox" ) ); 475 aComboBox.Text = OUString( RTL_CONSTASCII_USTRINGPARAM( "test a combobox" ) ); 476 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Larry" ) ) ); 477 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Curly" ) ) ); 478 aComboBox.Entries.push_back( OUString( RTL_CONSTASCII_USTRINGPARAM( "Moe" ) ) ); 479 aComboBox.Location = Rectangle( Point( 4500, 20000 ), Size( 3000, 500 ) ); 480 aWriter.CreateControl( aComboBox ); 481 482 // test outlines 483 sal_Int32 nPage1OL = aWriter.CreateOutlineItem(); 484 aWriter.SetOutlineItemText( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 1" ) ) ); 485 aWriter.SetOutlineItemDest( nPage1OL, nSecondDest ); 486 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2" ) ), nSecondDest ); 487 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 revisited" ) ), nSecondDest ); 488 aWriter.CreateOutlineItem( nPage1OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 2 again" ) ), nSecondDest ); 489 sal_Int32 nPage2OL = aWriter.CreateOutlineItem(); 490 aWriter.SetOutlineItemText( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Page 2" ) ) ); 491 aWriter.CreateOutlineItem( nPage2OL, OUString( RTL_CONSTASCII_USTRINGPARAM( "Dest 1" ) ), nFirstDest ); 492 493 aWriter.EndStructureElement(); // close document 494 aWriter.Emit(); 495 } 496 #endif 497 498 static const sal_Int32 nLog10Divisor = 1; 499 static const double fDivisor = 10.0; 500 501 static inline double pixelToPoint( sal_Int32 px ) { return double(px)/fDivisor; } 502 static inline double pixelToPoint( double px ) { return px/fDivisor; } 503 static inline sal_Int32 pointToPixel( double pt ) { return sal_Int32(pt*fDivisor); } 504 505 const sal_uInt8 PDFWriterImpl::s_nPadString[32] = 506 { 507 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, 508 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A 509 }; 510 511 static void appendHex( sal_Int8 nInt, OStringBuffer& rBuffer ) 512 { 513 static const sal_Char pHexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', 514 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 515 rBuffer.append( pHexDigits[ (nInt >> 4) & 15 ] ); 516 rBuffer.append( pHexDigits[ nInt & 15 ] ); 517 } 518 519 static void appendName( const OUString& rStr, OStringBuffer& rBuffer ) 520 { 521 // FIXME i59651 add a check for max length of 127 chars? Per PDF spec 1.4, appendix C.1 522 // I guess than when reading the #xx sequence it will count for a single character. 523 OString aStr( OUStringToOString( rStr, RTL_TEXTENCODING_UTF8 ) ); 524 const sal_Char* pStr = aStr.getStr(); 525 int nLen = aStr.getLength(); 526 for( int i = 0; i < nLen; i++ ) 527 { 528 /* #i16920# PDF recommendation: output UTF8, any byte 529 * outside the interval [33(=ASCII'!');126(=ASCII'~')] 530 * should be escaped hexadecimal 531 * for the sake of ghostscript which also reads PDF 532 * but has a narrower acceptance rate we only pass 533 * alphanumerics and '-' literally. 534 */ 535 if( (pStr[i] >= 'A' && pStr[i] <= 'Z' ) || 536 (pStr[i] >= 'a' && pStr[i] <= 'z' ) || 537 (pStr[i] >= '0' && pStr[i] <= '9' ) || 538 pStr[i] == '-' ) 539 { 540 rBuffer.append( pStr[i] ); 541 } 542 else 543 { 544 rBuffer.append( '#' ); 545 appendHex( (sal_Int8)pStr[i], rBuffer ); 546 } 547 } 548 } 549 550 static void appendName( const sal_Char* pStr, OStringBuffer& rBuffer ) 551 { 552 //FIXME i59651 see above 553 while( pStr && *pStr ) 554 { 555 if( (*pStr >= 'A' && *pStr <= 'Z' ) || 556 (*pStr >= 'a' && *pStr <= 'z' ) || 557 (*pStr >= '0' && *pStr <= '9' ) || 558 *pStr == '-' ) 559 { 560 rBuffer.append( *pStr ); 561 } 562 else 563 { 564 rBuffer.append( '#' ); 565 appendHex( (sal_Int8)*pStr, rBuffer ); 566 } 567 pStr++; 568 } 569 } 570 571 //used only to emit encoded passwords 572 static void appendLiteralString( const sal_Char* pStr, sal_Int32 nLength, OStringBuffer& rBuffer ) 573 { 574 while( nLength ) 575 { 576 switch( *pStr ) 577 { 578 case '\n' : 579 rBuffer.append( "\\n" ); 580 break; 581 case '\r' : 582 rBuffer.append( "\\r" ); 583 break; 584 case '\t' : 585 rBuffer.append( "\\t" ); 586 break; 587 case '\b' : 588 rBuffer.append( "\\b" ); 589 break; 590 case '\f' : 591 rBuffer.append( "\\f" ); 592 break; 593 case '(' : 594 case ')' : 595 case '\\' : 596 rBuffer.append( "\\" ); 597 rBuffer.append( (sal_Char) *pStr ); 598 break; 599 default: 600 rBuffer.append( (sal_Char) *pStr ); 601 break; 602 } 603 pStr++; 604 nLength--; 605 } 606 } 607 608 /**--->i56629 609 * Convert a string before using it. 610 * 611 * This string conversion function is needed because the destination name 612 * in a PDF file seen through an Internet browser should be 613 * specially crafted, in order to be used directly by the browser. 614 * In this way the fragment part of a hyperlink to a PDF file (e.g. something 615 * as 'test1/test2/a-file.pdf#thefragment) will be (hopefully) interpreted by the 616 * PDF reader (currently only Adobe Reader plug-in seems to be working that way) called 617 * from inside the Internet browser as: 'open the file test1/test2/a-file.pdf 618 * and go to named destination thefragment using default zoom'. 619 * The conversion is needed because in case of a fragment in the form: Slide%201 620 * (meaning Slide 1) as it is converted obeying the Inet rules, it will become Slide25201 621 * using this conversion, in both the generated named destinations, fragment and GoToR 622 * destination. 623 * 624 * The names for destinations are name objects and so they don't need to be encrypted 625 * even though they expose the content of PDF file (e.g. guessing the PDF content from the 626 * destination name). 627 * 628 * Fhurter limitation: it is advisable to use standard ASCII characters for 629 * OOo bookmarks. 630 */ 631 static void appendDestinationName( const rtl::OUString& rString, OStringBuffer& rBuffer ) 632 { 633 const sal_Unicode* pStr = rString.getStr(); 634 sal_Int32 nLen = rString.getLength(); 635 for( int i = 0; i < nLen; i++ ) 636 { 637 sal_Unicode aChar = pStr[i]; 638 if( (aChar >= '0' && aChar <= '9' ) || 639 (aChar >= 'a' && aChar <= 'z' ) || 640 (aChar >= 'A' && aChar <= 'Z' ) || 641 aChar == '-' ) 642 { 643 rBuffer.append((sal_Char)aChar); 644 } 645 else 646 { 647 sal_Int8 aValueHigh = sal_Int8(aChar >> 8); 648 if(aValueHigh > 0) 649 appendHex( aValueHigh, rBuffer ); 650 appendHex( (sal_Int8)(aChar & 255 ), rBuffer ); 651 } 652 } 653 } 654 //<--- i56629 655 656 static void appendUnicodeTextString( const rtl::OUString& rString, OStringBuffer& rBuffer ) 657 { 658 rBuffer.append( "FEFF" ); 659 const sal_Unicode* pStr = rString.getStr(); 660 sal_Int32 nLen = rString.getLength(); 661 for( int i = 0; i < nLen; i++ ) 662 { 663 sal_Unicode aChar = pStr[i]; 664 appendHex( (sal_Int8)(aChar >> 8), rBuffer ); 665 appendHex( (sal_Int8)(aChar & 255 ), rBuffer ); 666 } 667 } 668 669 void PDFWriterImpl::createWidgetFieldName( sal_Int32 i_nWidgetIndex, const PDFWriter::AnyWidget& i_rControl ) 670 { 671 /* #i80258# previously we use appendName here 672 however we need a slightly different coding scheme than the normal 673 name encoding for field names 674 */ 675 const OUString& rName = (m_aContext.Version > PDFWriter::PDF_1_2) ? i_rControl.Name : i_rControl.Text; 676 OString aStr( OUStringToOString( rName, RTL_TEXTENCODING_UTF8 ) ); 677 const sal_Char* pStr = aStr.getStr(); 678 int nLen = aStr.getLength(); 679 680 OStringBuffer aBuffer( rName.getLength()+64 ); 681 for( int i = 0; i < nLen; i++ ) 682 { 683 /* #i16920# PDF recommendation: output UTF8, any byte 684 * outside the interval [32(=ASCII' ');126(=ASCII'~')] 685 * should be escaped hexadecimal 686 */ 687 if( (pStr[i] >= 32 && pStr[i] <= 126 ) ) 688 aBuffer.append( pStr[i] ); 689 else 690 { 691 aBuffer.append( '#' ); 692 appendHex( (sal_Int8)pStr[i], aBuffer ); 693 } 694 } 695 696 OString aFullName( aBuffer.makeStringAndClear() ); 697 698 /* #i82785# create hierarchical fields down to the for each dot in i_rName */ 699 sal_Int32 nTokenIndex = 0, nLastTokenIndex = 0; 700 OString aPartialName; 701 OString aDomain; 702 do 703 { 704 nLastTokenIndex = nTokenIndex; 705 aPartialName = aFullName.getToken( 0, '.', nTokenIndex ); 706 if( nTokenIndex != -1 ) 707 { 708 // find or create a hierarchical field 709 // first find the fully qualified name up to this field 710 aDomain = aFullName.copy( 0, nTokenIndex-1 ); 711 std::hash_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain ); 712 if( it == m_aFieldNameMap.end() ) 713 { 714 // create new hierarchy field 715 sal_Int32 nNewWidget = m_aWidgets.size(); 716 m_aWidgets.push_back( PDFWidget() ); 717 m_aWidgets[nNewWidget].m_nObject = createObject(); 718 m_aWidgets[nNewWidget].m_eType = PDFWriter::Hierarchy; 719 m_aWidgets[nNewWidget].m_aName = aPartialName; 720 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject; 721 m_aFieldNameMap[aDomain] = nNewWidget; 722 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[nNewWidget].m_nObject; 723 if( nLastTokenIndex > 0 ) 724 { 725 // this field is not a root field and 726 // needs to be inserted to its parent 727 OString aParentDomain( aDomain.copy( 0, nLastTokenIndex-1 ) ); 728 it = m_aFieldNameMap.find( aParentDomain ); 729 OSL_ENSURE( it != m_aFieldNameMap.end(), "field name not found" ); 730 if( it != m_aFieldNameMap.end() ) 731 { 732 OSL_ENSURE( it->second < sal_Int32(m_aWidgets.size()), "invalid field number entry" ); 733 if( it->second < sal_Int32(m_aWidgets.size()) ) 734 { 735 PDFWidget& rParentField( m_aWidgets[it->second] ); 736 rParentField.m_aKids.push_back( m_aWidgets[nNewWidget].m_nObject ); 737 rParentField.m_aKidsIndex.push_back( nNewWidget ); 738 m_aWidgets[nNewWidget].m_nParent = rParentField.m_nObject; 739 } 740 } 741 } 742 } 743 else if( m_aWidgets[it->second].m_eType != PDFWriter::Hierarchy ) 744 { 745 // this is invalid, someone tries to have a terminal field as parent 746 // example: a button with the name foo.bar exists and 747 // another button is named foo.bar.no 748 // workaround: put the second terminal field as much up in the hierarchy as 749 // necessary to have a non-terminal field as parent (or none at all) 750 // since it->second already is terminal, we just need to use its parent 751 aDomain = OString(); 752 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 ); 753 if( nLastTokenIndex > 0 ) 754 { 755 aDomain = aFullName.copy( 0, nLastTokenIndex-1 ); 756 OStringBuffer aBuf( aDomain.getLength() + 1 + aPartialName.getLength() ); 757 aBuf.append( aDomain ); 758 aBuf.append( '.' ); 759 aBuf.append( aPartialName ); 760 aFullName = aBuf.makeStringAndClear(); 761 } 762 else 763 aFullName = aPartialName; 764 break; 765 } 766 } 767 } while( nTokenIndex != -1 ); 768 769 // insert widget into its hierarchy field 770 if( aDomain.getLength() ) 771 { 772 std::hash_map< rtl::OString, sal_Int32, rtl::OStringHash >::const_iterator it = m_aFieldNameMap.find( aDomain ); 773 if( it != m_aFieldNameMap.end() ) 774 { 775 OSL_ENSURE( it->second >= 0 && it->second < sal_Int32( m_aWidgets.size() ), "invalid field index" ); 776 if( it->second >= 0 && it->second < sal_Int32(m_aWidgets.size()) ) 777 { 778 m_aWidgets[i_nWidgetIndex].m_nParent = m_aWidgets[it->second].m_nObject; 779 m_aWidgets[it->second].m_aKids.push_back( m_aWidgets[i_nWidgetIndex].m_nObject); 780 m_aWidgets[it->second].m_aKidsIndex.push_back( i_nWidgetIndex ); 781 } 782 } 783 } 784 785 if( aPartialName.getLength() == 0 ) 786 { 787 // how funny, an empty field name 788 if( i_rControl.getType() == PDFWriter::RadioButton ) 789 { 790 aPartialName = "RadioGroup"; 791 aPartialName += OString::valueOf( static_cast<const PDFWriter::RadioButtonWidget&>(i_rControl).RadioGroup ); 792 } 793 else 794 aPartialName = OString( "Widget" ); 795 } 796 797 if( ! m_aContext.AllowDuplicateFieldNames ) 798 { 799 std::hash_map<OString, sal_Int32, OStringHash>::iterator it = m_aFieldNameMap.find( aFullName ); 800 801 if( it != m_aFieldNameMap.end() ) // not unique 802 { 803 std::hash_map< OString, sal_Int32, OStringHash >::const_iterator check_it; 804 OString aTry; 805 sal_Int32 nTry = 2; 806 do 807 { 808 OStringBuffer aUnique( aFullName.getLength() + 16 ); 809 aUnique.append( aFullName ); 810 aUnique.append( '_' ); 811 aUnique.append( nTry++ ); 812 aTry = aUnique.makeStringAndClear(); 813 check_it = m_aFieldNameMap.find( aTry ); 814 } while( check_it != m_aFieldNameMap.end() ); 815 aFullName = aTry; 816 m_aFieldNameMap[ aFullName ] = i_nWidgetIndex; 817 aPartialName = aFullName.copy( aFullName.lastIndexOf( '.' )+1 ); 818 } 819 else 820 m_aFieldNameMap[ aFullName ] = i_nWidgetIndex; 821 } 822 823 // finally 824 m_aWidgets[i_nWidgetIndex].m_aName = aPartialName; 825 } 826 827 static void appendFixedInt( sal_Int32 nValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = nLog10Divisor ) 828 { 829 if( nValue < 0 ) 830 { 831 rBuffer.append( '-' ); 832 nValue = -nValue; 833 } 834 sal_Int32 nFactor = 1, nDiv = nPrecision; 835 while( nDiv-- ) 836 nFactor *= 10; 837 838 sal_Int32 nInt = nValue / nFactor; 839 rBuffer.append( nInt ); 840 if( nFactor > 1 ) 841 { 842 sal_Int32 nDecimal = nValue % nFactor; 843 if( nDecimal ) 844 { 845 rBuffer.append( '.' ); 846 // omit trailing zeros 847 while( (nDecimal % 10) == 0 ) 848 nDecimal /= 10; 849 rBuffer.append( nDecimal ); 850 } 851 } 852 } 853 854 855 // appends a double. PDF does not accept exponential format, only fixed point 856 static void appendDouble( double fValue, OStringBuffer& rBuffer, sal_Int32 nPrecision = 5 ) 857 { 858 bool bNeg = false; 859 if( fValue < 0.0 ) 860 { 861 bNeg = true; 862 fValue=-fValue; 863 } 864 865 sal_Int64 nInt = (sal_Int64)fValue; 866 fValue -= (double)nInt; 867 // optimizing hardware may lead to a value of 1.0 after the subtraction 868 if( fValue == 1.0 || log10( 1.0-fValue ) <= -nPrecision ) 869 { 870 nInt++; 871 fValue = 0.0; 872 } 873 sal_Int64 nFrac = 0; 874 if( fValue ) 875 { 876 fValue *= pow( 10.0, (double)nPrecision ); 877 nFrac = (sal_Int64)fValue; 878 } 879 if( bNeg && ( nInt || nFrac ) ) 880 rBuffer.append( '-' ); 881 rBuffer.append( nInt ); 882 if( nFrac ) 883 { 884 int i; 885 rBuffer.append( '.' ); 886 sal_Int64 nBound = (sal_Int64)(pow( 10.0, nPrecision - 1.0 )+0.5); 887 for ( i = 0; ( i < nPrecision ) && nFrac; i++ ) 888 { 889 sal_Int64 nNumb = nFrac / nBound; 890 nFrac -= nNumb * nBound; 891 rBuffer.append( nNumb ); 892 nBound /= 10; 893 } 894 } 895 } 896 897 898 static void appendColor( const Color& rColor, OStringBuffer& rBuffer, bool bConvertToGrey = false ) 899 { 900 901 if( rColor != Color( COL_TRANSPARENT ) ) 902 { 903 if( bConvertToGrey ) 904 { 905 sal_uInt8 cByte = rColor.GetLuminance(); 906 appendDouble( (double)cByte / 255.0, rBuffer ); 907 } 908 else 909 { 910 appendDouble( (double)rColor.GetRed() / 255.0, rBuffer ); 911 rBuffer.append( ' ' ); 912 appendDouble( (double)rColor.GetGreen() / 255.0, rBuffer ); 913 rBuffer.append( ' ' ); 914 appendDouble( (double)rColor.GetBlue() / 255.0, rBuffer ); 915 } 916 } 917 } 918 919 void PDFWriterImpl::appendStrokingColor( const Color& rColor, OStringBuffer& rBuffer ) 920 { 921 if( rColor != Color( COL_TRANSPARENT ) ) 922 { 923 bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale; 924 appendColor( rColor, rBuffer, bGrey ); 925 rBuffer.append( bGrey ? " G" : " RG" ); 926 } 927 } 928 929 void PDFWriterImpl::appendNonStrokingColor( const Color& rColor, OStringBuffer& rBuffer ) 930 { 931 if( rColor != Color( COL_TRANSPARENT ) ) 932 { 933 bool bGrey = m_aContext.ColorMode == PDFWriter::DrawGreyscale; 934 appendColor( rColor, rBuffer, bGrey ); 935 rBuffer.append( bGrey ? " g" : " rg" ); 936 } 937 } 938 939 // matrix helper class 940 // TODO: use basegfx matrix class instead or derive from it 941 namespace vcl // TODO: use anonymous namespace to keep this class local 942 { 943 /* for sparse matrices of the form (2D linear transformations) 944 * f[0] f[1] 0 945 * f[2] f[3] 0 946 * f[4] f[5] 1 947 */ 948 class Matrix3 949 { 950 double f[6]; 951 952 void set( double *pn ) { for( int i = 0 ; i < 6; i++ ) f[i] = pn[i]; } 953 public: 954 Matrix3(); 955 ~Matrix3() {} 956 957 void skew( double alpha, double beta ); 958 void scale( double sx, double sy ); 959 void rotate( double angle ); 960 void translate( double tx, double ty ); 961 bool invert(); 962 963 void append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack = NULL ); 964 965 Point transform( const Point& rPoint ) const; 966 }; 967 } 968 969 Matrix3::Matrix3() 970 { 971 // initialize to unity 972 f[0] = 1.0; 973 f[1] = 0.0; 974 f[2] = 0.0; 975 f[3] = 1.0; 976 f[4] = 0.0; 977 f[5] = 0.0; 978 } 979 980 Point Matrix3::transform( const Point& rOrig ) const 981 { 982 double x = (double)rOrig.X(), y = (double)rOrig.Y(); 983 return Point( (int)(x*f[0] + y*f[2] + f[4]), (int)(x*f[1] + y*f[3] + f[5]) ); 984 } 985 986 void Matrix3::skew( double alpha, double beta ) 987 { 988 double fn[6]; 989 double tb = tan( beta ); 990 fn[0] = f[0] + f[2]*tb; 991 fn[1] = f[1]; 992 fn[2] = f[2] + f[3]*tb; 993 fn[3] = f[3]; 994 fn[4] = f[4] + f[5]*tb; 995 fn[5] = f[5]; 996 if( alpha != 0.0 ) 997 { 998 double ta = tan( alpha ); 999 fn[1] += f[0]*ta; 1000 fn[3] += f[2]*ta; 1001 fn[5] += f[4]*ta; 1002 } 1003 set( fn ); 1004 } 1005 1006 void Matrix3::scale( double sx, double sy ) 1007 { 1008 double fn[6]; 1009 fn[0] = sx*f[0]; 1010 fn[1] = sy*f[1]; 1011 fn[2] = sx*f[2]; 1012 fn[3] = sy*f[3]; 1013 fn[4] = sx*f[4]; 1014 fn[5] = sy*f[5]; 1015 set( fn ); 1016 } 1017 1018 void Matrix3::rotate( double angle ) 1019 { 1020 double fn[6]; 1021 double fSin = sin(angle); 1022 double fCos = cos(angle); 1023 fn[0] = f[0]*fCos - f[1]*fSin; 1024 fn[1] = f[0]*fSin + f[1]*fCos; 1025 fn[2] = f[2]*fCos - f[3]*fSin; 1026 fn[3] = f[2]*fSin + f[3]*fCos; 1027 fn[4] = f[4]*fCos - f[5]*fSin; 1028 fn[5] = f[4]*fSin + f[5]*fCos; 1029 set( fn ); 1030 } 1031 1032 void Matrix3::translate( double tx, double ty ) 1033 { 1034 f[4] += tx; 1035 f[5] += ty; 1036 } 1037 1038 bool Matrix3::invert() 1039 { 1040 // short circuit trivial cases 1041 if( f[1]==f[2] && f[1]==0.0 && f[0]==f[3] && f[0]==1.0 ) 1042 { 1043 f[4] = -f[4]; 1044 f[5] = -f[5]; 1045 return true; 1046 } 1047 1048 // check determinant 1049 const double fDet = f[0]*f[3]-f[1]*f[2]; 1050 if( fDet == 0.0 ) 1051 return false; 1052 1053 // invert the matrix 1054 double fn[6]; 1055 fn[0] = +f[3] / fDet; 1056 fn[1] = -f[1] / fDet; 1057 fn[2] = -f[2] / fDet; 1058 fn[3] = +f[0] / fDet; 1059 1060 // apply inversion to translation 1061 fn[4] = -(f[4]*fn[0] + f[5]*fn[2]); 1062 fn[5] = -(f[4]*fn[1] + f[5]*fn[3]); 1063 1064 set( fn ); 1065 return true; 1066 } 1067 1068 void Matrix3::append( PDFWriterImpl::PDFPage& rPage, OStringBuffer& rBuffer, Point* pBack ) 1069 { 1070 appendDouble( f[0], rBuffer ); 1071 rBuffer.append( ' ' ); 1072 appendDouble( f[1], rBuffer ); 1073 rBuffer.append( ' ' ); 1074 appendDouble( f[2], rBuffer ); 1075 rBuffer.append( ' ' ); 1076 appendDouble( f[3], rBuffer ); 1077 rBuffer.append( ' ' ); 1078 rPage.appendPoint( Point( (long)f[4], (long)f[5] ), rBuffer, false, pBack ); 1079 } 1080 1081 static void appendResourceMap( OStringBuffer& rBuf, const char* pPrefix, const PDFWriterImpl::ResourceMap& rList ) 1082 { 1083 if( rList.empty() ) 1084 return; 1085 rBuf.append( '/' ); 1086 rBuf.append( pPrefix ); 1087 rBuf.append( "<<" ); 1088 int ni = 0; 1089 for( PDFWriterImpl::ResourceMap::const_iterator it = rList.begin(); it != rList.end(); ++it ) 1090 { 1091 if( it->first.getLength() && it->second > 0 ) 1092 { 1093 rBuf.append( '/' ); 1094 rBuf.append( it->first ); 1095 rBuf.append( ' ' ); 1096 rBuf.append( it->second ); 1097 rBuf.append( " 0 R" ); 1098 if( ((++ni) & 7) == 0 ) 1099 rBuf.append( '\n' ); 1100 } 1101 } 1102 rBuf.append( ">>\n" ); 1103 } 1104 1105 void PDFWriterImpl::ResourceDict::append( OStringBuffer& rBuf, sal_Int32 nFontDictObject ) 1106 { 1107 rBuf.append( "<</Font " ); 1108 rBuf.append( nFontDictObject ); 1109 rBuf.append( " 0 R\n" ); 1110 appendResourceMap( rBuf, "XObject", m_aXObjects ); 1111 appendResourceMap( rBuf, "ExtGState", m_aExtGStates ); 1112 appendResourceMap( rBuf, "Shading", m_aShadings ); 1113 appendResourceMap( rBuf, "Pattern", m_aPatterns ); 1114 rBuf.append( "/ProcSet[/PDF/Text" ); 1115 if( !m_aXObjects.empty() ) 1116 rBuf.append( "/ImageC/ImageI/ImageB" ); 1117 rBuf.append( "]\n>>\n" ); 1118 }; 1119 1120 PDFWriterImpl::PDFPage::PDFPage( PDFWriterImpl* pWriter, sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation ) 1121 : 1122 m_pWriter( pWriter ), 1123 m_nPageWidth( nPageWidth ), 1124 m_nPageHeight( nPageHeight ), 1125 m_eOrientation( eOrientation ), 1126 m_nPageObject( 0 ), // invalid object number 1127 m_nPageIndex( -1 ), // invalid index 1128 m_nStreamLengthObject( 0 ), 1129 m_nBeginStreamPos( 0 ), 1130 m_eTransition( PDFWriter::Regular ), 1131 m_nTransTime( 0 ), 1132 m_nDuration( 0 ), 1133 m_bHasWidgets( false ) 1134 { 1135 // object ref must be only ever updated in emit() 1136 m_nPageObject = m_pWriter->createObject(); 1137 } 1138 1139 PDFWriterImpl::PDFPage::~PDFPage() 1140 { 1141 } 1142 1143 void PDFWriterImpl::PDFPage::beginStream() 1144 { 1145 #if OSL_DEBUG_LEVEL > 1 1146 { 1147 OStringBuffer aLine( "PDFWriterImpl::PDFPage::beginStream, +" ); 1148 m_pWriter->emitComment( aLine.getStr() ); 1149 } 1150 #endif 1151 m_aStreamObjects.push_back(m_pWriter->createObject()); 1152 if( ! m_pWriter->updateObject( m_aStreamObjects.back() ) ) 1153 return; 1154 1155 m_nStreamLengthObject = m_pWriter->createObject(); 1156 // write content stream header 1157 OStringBuffer aLine; 1158 aLine.append( m_aStreamObjects.back() ); 1159 aLine.append( " 0 obj\n<</Length " ); 1160 aLine.append( m_nStreamLengthObject ); 1161 aLine.append( " 0 R" ); 1162 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION ) 1163 aLine.append( "/Filter/FlateDecode" ); 1164 #endif 1165 aLine.append( ">>\nstream\n" ); 1166 if( ! m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ) ) 1167 return; 1168 if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &m_nBeginStreamPos ) ) 1169 { 1170 osl_closeFile( m_pWriter->m_aFile ); 1171 m_pWriter->m_bOpen = false; 1172 } 1173 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION ) 1174 m_pWriter->beginCompression(); 1175 #endif 1176 m_pWriter->checkAndEnableStreamEncryption( m_aStreamObjects.back() ); 1177 } 1178 1179 void PDFWriterImpl::PDFPage::endStream() 1180 { 1181 #if defined ( COMPRESS_PAGES ) && !defined ( DEBUG_DISABLE_PDFCOMPRESSION ) 1182 m_pWriter->endCompression(); 1183 #endif 1184 sal_uInt64 nEndStreamPos; 1185 if( osl_File_E_None != osl_getFilePos( m_pWriter->m_aFile, &nEndStreamPos ) ) 1186 { 1187 osl_closeFile( m_pWriter->m_aFile ); 1188 m_pWriter->m_bOpen = false; 1189 return; 1190 } 1191 m_pWriter->disableStreamEncryption(); 1192 if( ! m_pWriter->writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) 1193 return; 1194 // emit stream length object 1195 if( ! m_pWriter->updateObject( m_nStreamLengthObject ) ) 1196 return; 1197 OStringBuffer aLine; 1198 aLine.append( m_nStreamLengthObject ); 1199 aLine.append( " 0 obj\n" ); 1200 aLine.append( (sal_Int64)(nEndStreamPos-m_nBeginStreamPos) ); 1201 aLine.append( "\nendobj\n\n" ); 1202 m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ); 1203 } 1204 1205 bool PDFWriterImpl::PDFPage::emit(sal_Int32 nParentObject ) 1206 { 1207 // emit page object 1208 if( ! m_pWriter->updateObject( m_nPageObject ) ) 1209 return false; 1210 OStringBuffer aLine; 1211 1212 aLine.append( m_nPageObject ); 1213 aLine.append( " 0 obj\n" 1214 "<</Type/Page/Parent " ); 1215 aLine.append( nParentObject ); 1216 aLine.append( " 0 R" ); 1217 aLine.append( "/Resources " ); 1218 aLine.append( m_pWriter->getResourceDictObj() ); 1219 aLine.append( " 0 R" ); 1220 if( m_nPageWidth && m_nPageHeight ) 1221 { 1222 aLine.append( "/MediaBox[0 0 " ); 1223 aLine.append( m_nPageWidth ); 1224 aLine.append( ' ' ); 1225 aLine.append( m_nPageHeight ); 1226 aLine.append( "]" ); 1227 } 1228 switch( m_eOrientation ) 1229 { 1230 case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break; 1231 case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break; 1232 case PDFWriter::Portrait: aLine.append( "/Rotate 0\n" );break; 1233 1234 case PDFWriter::Inherit: 1235 default: 1236 break; 1237 } 1238 int nAnnots = m_aAnnotations.size(); 1239 if( nAnnots > 0 ) 1240 { 1241 aLine.append( "/Annots[\n" ); 1242 for( int i = 0; i < nAnnots; i++ ) 1243 { 1244 aLine.append( m_aAnnotations[i] ); 1245 aLine.append( " 0 R" ); 1246 aLine.append( ((i+1)%15) ? " " : "\n" ); 1247 } 1248 aLine.append( "]\n" ); 1249 } 1250 #if 0 1251 // FIXME: implement tab order as Structure Tree 1252 if( m_bHasWidgets && m_pWriter->getVersion() >= PDFWriter::PDF_1_5 ) 1253 aLine.append( " /Tabs /S\n" ); 1254 #endif 1255 if( m_aMCIDParents.size() > 0 ) 1256 { 1257 OStringBuffer aStructParents( 1024 ); 1258 aStructParents.append( "[ " ); 1259 int nParents = m_aMCIDParents.size(); 1260 for( int i = 0; i < nParents; i++ ) 1261 { 1262 aStructParents.append( m_aMCIDParents[i] ); 1263 aStructParents.append( " 0 R" ); 1264 aStructParents.append( ((i%10) == 9) ? "\n" : " " ); 1265 } 1266 aStructParents.append( "]" ); 1267 m_pWriter->m_aStructParentTree.push_back( aStructParents.makeStringAndClear() ); 1268 1269 aLine.append( "/StructParents " ); 1270 aLine.append( sal_Int32(m_pWriter->m_aStructParentTree.size()-1) ); 1271 aLine.append( "\n" ); 1272 } 1273 if( m_nDuration > 0 ) 1274 { 1275 aLine.append( "/Dur " ); 1276 aLine.append( (sal_Int32)m_nDuration ); 1277 aLine.append( "\n" ); 1278 } 1279 if( m_eTransition != PDFWriter::Regular && m_nTransTime > 0 ) 1280 { 1281 // transition duration 1282 aLine.append( "/Trans<</D " ); 1283 appendDouble( (double)m_nTransTime/1000.0, aLine, 3 ); 1284 aLine.append( "\n" ); 1285 const char *pStyle = NULL, *pDm = NULL, *pM = NULL, *pDi = NULL; 1286 switch( m_eTransition ) 1287 { 1288 case PDFWriter::SplitHorizontalInward: 1289 pStyle = "Split"; pDm = "H"; pM = "I"; break; 1290 case PDFWriter::SplitHorizontalOutward: 1291 pStyle = "Split"; pDm = "H"; pM = "O"; break; 1292 case PDFWriter::SplitVerticalInward: 1293 pStyle = "Split"; pDm = "V"; pM = "I"; break; 1294 case PDFWriter::SplitVerticalOutward: 1295 pStyle = "Split"; pDm = "V"; pM = "O"; break; 1296 case PDFWriter::BlindsHorizontal: 1297 pStyle = "Blinds"; pDm = "H"; break; 1298 case PDFWriter::BlindsVertical: 1299 pStyle = "Blinds"; pDm = "V"; break; 1300 case PDFWriter::BoxInward: 1301 pStyle = "Box"; pM = "I"; break; 1302 case PDFWriter::BoxOutward: 1303 pStyle = "Box"; pM = "O"; break; 1304 case PDFWriter::WipeLeftToRight: 1305 pStyle = "Wipe"; pDi = "0"; break; 1306 case PDFWriter::WipeBottomToTop: 1307 pStyle = "Wipe"; pDi = "90"; break; 1308 case PDFWriter::WipeRightToLeft: 1309 pStyle = "Wipe"; pDi = "180"; break; 1310 case PDFWriter::WipeTopToBottom: 1311 pStyle = "Wipe"; pDi = "270"; break; 1312 case PDFWriter::Dissolve: 1313 pStyle = "Dissolve"; break; 1314 case PDFWriter::GlitterLeftToRight: 1315 pStyle = "Glitter"; pDi = "0"; break; 1316 case PDFWriter::GlitterTopToBottom: 1317 pStyle = "Glitter"; pDi = "270"; break; 1318 case PDFWriter::GlitterTopLeftToBottomRight: 1319 pStyle = "Glitter"; pDi = "315"; break; 1320 case PDFWriter::Regular: 1321 break; 1322 } 1323 // transition style 1324 if( pStyle ) 1325 { 1326 aLine.append( "/S/" ); 1327 aLine.append( pStyle ); 1328 aLine.append( "\n" ); 1329 } 1330 if( pDm ) 1331 { 1332 aLine.append( "/Dm/" ); 1333 aLine.append( pDm ); 1334 aLine.append( "\n" ); 1335 } 1336 if( pM ) 1337 { 1338 aLine.append( "/M/" ); 1339 aLine.append( pM ); 1340 aLine.append( "\n" ); 1341 } 1342 if( pDi ) 1343 { 1344 aLine.append( "/Di " ); 1345 aLine.append( pDi ); 1346 aLine.append( "\n" ); 1347 } 1348 aLine.append( ">>\n" ); 1349 } 1350 if( m_pWriter->getVersion() > PDFWriter::PDF_1_3 && ! m_pWriter->m_bIsPDF_A1 ) 1351 { 1352 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/I true>>" ); 1353 } 1354 aLine.append( "/Contents" ); 1355 unsigned int nStreamObjects = m_aStreamObjects.size(); 1356 if( nStreamObjects > 1 ) 1357 aLine.append( '[' ); 1358 for( unsigned int i = 0; i < m_aStreamObjects.size(); i++ ) 1359 { 1360 aLine.append( ' ' ); 1361 aLine.append( m_aStreamObjects[i] ); 1362 aLine.append( " 0 R" ); 1363 } 1364 if( nStreamObjects > 1 ) 1365 aLine.append( ']' ); 1366 aLine.append( ">>\nendobj\n\n" ); 1367 return m_pWriter->writeBuffer( aLine.getStr(), aLine.getLength() ); 1368 } 1369 1370 namespace vcl 1371 { 1372 template < class GEOMETRY > 1373 GEOMETRY lcl_convert( const MapMode& _rSource, const MapMode& _rDest, OutputDevice* _pPixelConversion, const GEOMETRY& _rObject ) 1374 { 1375 GEOMETRY aPoint; 1376 if ( MAP_PIXEL == _rSource.GetMapUnit() ) 1377 { 1378 aPoint = _pPixelConversion->PixelToLogic( _rObject, _rDest ); 1379 } 1380 else 1381 { 1382 aPoint = OutputDevice::LogicToLogic( _rObject, _rSource, _rDest ); 1383 } 1384 return aPoint; 1385 } 1386 } 1387 1388 void PDFWriterImpl::PDFPage::appendPoint( const Point& rPoint, OStringBuffer& rBuffer, bool bNeg, Point* pOutPoint ) const 1389 { 1390 if( pOutPoint ) 1391 { 1392 Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1393 m_pWriter->m_aMapMode, 1394 m_pWriter->getReferenceDevice(), 1395 rPoint ) ); 1396 *pOutPoint = aPoint; 1397 } 1398 1399 Point aPoint( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1400 m_pWriter->m_aMapMode, 1401 m_pWriter->getReferenceDevice(), 1402 rPoint ) ); 1403 1404 sal_Int32 nValue = aPoint.X(); 1405 if( bNeg ) 1406 nValue = -nValue; 1407 1408 appendFixedInt( nValue, rBuffer ); 1409 1410 rBuffer.append( ' ' ); 1411 1412 nValue = pointToPixel(getHeight()) - aPoint.Y(); 1413 if( bNeg ) 1414 nValue = -nValue; 1415 1416 appendFixedInt( nValue, rBuffer ); 1417 } 1418 1419 void PDFWriterImpl::PDFPage::appendPixelPoint( const basegfx::B2DPoint& rPoint, OStringBuffer& rBuffer ) const 1420 { 1421 double fValue = pixelToPoint(rPoint.getX()); 1422 1423 appendDouble( fValue, rBuffer, nLog10Divisor ); 1424 1425 rBuffer.append( ' ' ); 1426 1427 fValue = double(getHeight()) - pixelToPoint(rPoint.getY()); 1428 1429 appendDouble( fValue, rBuffer, nLog10Divisor ); 1430 } 1431 1432 void PDFWriterImpl::PDFPage::appendRect( const Rectangle& rRect, OStringBuffer& rBuffer ) const 1433 { 1434 appendPoint( rRect.BottomLeft() + Point( 0, 1 ), rBuffer ); 1435 rBuffer.append( ' ' ); 1436 appendMappedLength( (sal_Int32)rRect.GetWidth(), rBuffer, false ); 1437 rBuffer.append( ' ' ); 1438 appendMappedLength( (sal_Int32)rRect.GetHeight(), rBuffer, true ); 1439 rBuffer.append( " re" ); 1440 } 1441 1442 void PDFWriterImpl::PDFPage::convertRect( Rectangle& rRect ) const 1443 { 1444 Point aLL = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1445 m_pWriter->m_aMapMode, 1446 m_pWriter->getReferenceDevice(), 1447 rRect.BottomLeft() + Point( 0, 1 ) 1448 ); 1449 Size aSize = lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1450 m_pWriter->m_aMapMode, 1451 m_pWriter->getReferenceDevice(), 1452 rRect.GetSize() ); 1453 rRect.Left() = aLL.X(); 1454 rRect.Right() = aLL.X() + aSize.Width(); 1455 rRect.Top() = pointToPixel(getHeight()) - aLL.Y(); 1456 rRect.Bottom() = rRect.Top() + aSize.Height(); 1457 } 1458 1459 void PDFWriterImpl::PDFPage::appendPolygon( const Polygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const 1460 { 1461 sal_uInt16 nPoints = rPoly.GetSize(); 1462 /* 1463 * #108582# applications do weird things 1464 */ 1465 sal_uInt32 nBufLen = rBuffer.getLength(); 1466 if( nPoints > 0 ) 1467 { 1468 const sal_uInt8* pFlagArray = rPoly.GetConstFlagAry(); 1469 appendPoint( rPoly[0], rBuffer ); 1470 rBuffer.append( " m\n" ); 1471 for( sal_uInt16 i = 1; i < nPoints; i++ ) 1472 { 1473 if( pFlagArray && pFlagArray[i] == POLY_CONTROL && nPoints-i > 2 ) 1474 { 1475 // bezier 1476 DBG_ASSERT( pFlagArray[i+1] == POLY_CONTROL && pFlagArray[i+2] != POLY_CONTROL, "unexpected sequence of control points" ); 1477 appendPoint( rPoly[i], rBuffer ); 1478 rBuffer.append( " " ); 1479 appendPoint( rPoly[i+1], rBuffer ); 1480 rBuffer.append( " " ); 1481 appendPoint( rPoly[i+2], rBuffer ); 1482 rBuffer.append( " c" ); 1483 i += 2; // add additionally consumed points 1484 } 1485 else 1486 { 1487 // line 1488 appendPoint( rPoly[i], rBuffer ); 1489 rBuffer.append( " l" ); 1490 } 1491 if( (rBuffer.getLength() - nBufLen) > 65 ) 1492 { 1493 rBuffer.append( "\n" ); 1494 nBufLen = rBuffer.getLength(); 1495 } 1496 else 1497 rBuffer.append( " " ); 1498 } 1499 if( bClose ) 1500 rBuffer.append( "h\n" ); 1501 } 1502 } 1503 1504 void PDFWriterImpl::PDFPage::appendPolygon( const basegfx::B2DPolygon& rPoly, OStringBuffer& rBuffer, bool bClose ) const 1505 { 1506 basegfx::B2DPolygon aPoly( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1507 m_pWriter->m_aMapMode, 1508 m_pWriter->getReferenceDevice(), 1509 rPoly ) ); 1510 1511 if( basegfx::tools::isRectangle( aPoly ) ) 1512 { 1513 basegfx::B2DRange aRange( aPoly.getB2DRange() ); 1514 basegfx::B2DPoint aBL( aRange.getMinX(), aRange.getMaxY() ); 1515 appendPixelPoint( aBL, rBuffer ); 1516 rBuffer.append( ' ' ); 1517 appendMappedLength( aRange.getWidth(), rBuffer, false, NULL, nLog10Divisor ); 1518 rBuffer.append( ' ' ); 1519 appendMappedLength( aRange.getHeight(), rBuffer, true, NULL, nLog10Divisor ); 1520 rBuffer.append( " re\n" ); 1521 return; 1522 } 1523 sal_uInt32 nPoints = aPoly.count(); 1524 if( nPoints > 0 ) 1525 { 1526 sal_uInt32 nBufLen = rBuffer.getLength(); 1527 basegfx::B2DPoint aLastPoint( aPoly.getB2DPoint( 0 ) ); 1528 appendPixelPoint( aLastPoint, rBuffer ); 1529 rBuffer.append( " m\n" ); 1530 for( sal_uInt32 i = 1; i <= nPoints; i++ ) 1531 { 1532 if( i != nPoints || aPoly.isClosed() ) 1533 { 1534 sal_uInt32 nCurPoint = i % nPoints; 1535 sal_uInt32 nLastPoint = i-1; 1536 basegfx::B2DPoint aPoint( aPoly.getB2DPoint( nCurPoint ) ); 1537 if( aPoly.isNextControlPointUsed( nLastPoint ) && 1538 aPoly.isPrevControlPointUsed( nCurPoint ) ) 1539 { 1540 appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer ); 1541 rBuffer.append( ' ' ); 1542 appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer ); 1543 rBuffer.append( ' ' ); 1544 appendPixelPoint( aPoint, rBuffer ); 1545 rBuffer.append( " c" ); 1546 } 1547 else if( aPoly.isNextControlPointUsed( nLastPoint ) ) 1548 { 1549 appendPixelPoint( aPoly.getNextControlPoint( nLastPoint ), rBuffer ); 1550 rBuffer.append( ' ' ); 1551 appendPixelPoint( aPoint, rBuffer ); 1552 rBuffer.append( " y" ); 1553 } 1554 else if( aPoly.isPrevControlPointUsed( nCurPoint ) ) 1555 { 1556 appendPixelPoint( aPoly.getPrevControlPoint( nCurPoint ), rBuffer ); 1557 rBuffer.append( ' ' ); 1558 appendPixelPoint( aPoint, rBuffer ); 1559 rBuffer.append( " v" ); 1560 } 1561 else 1562 { 1563 appendPixelPoint( aPoint, rBuffer ); 1564 rBuffer.append( " l" ); 1565 } 1566 if( (rBuffer.getLength() - nBufLen) > 65 ) 1567 { 1568 rBuffer.append( "\n" ); 1569 nBufLen = rBuffer.getLength(); 1570 } 1571 else 1572 rBuffer.append( " " ); 1573 } 1574 } 1575 if( bClose ) 1576 rBuffer.append( "h\n" ); 1577 } 1578 } 1579 1580 void PDFWriterImpl::PDFPage::appendPolyPolygon( const PolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const 1581 { 1582 sal_uInt16 nPolygons = rPolyPoly.Count(); 1583 for( sal_uInt16 n = 0; n < nPolygons; n++ ) 1584 appendPolygon( rPolyPoly[n], rBuffer, bClose ); 1585 } 1586 1587 void PDFWriterImpl::PDFPage::appendPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPoly, OStringBuffer& rBuffer, bool bClose ) const 1588 { 1589 sal_uInt32 nPolygons = rPolyPoly.count(); 1590 for( sal_uInt32 n = 0; n < nPolygons; n++ ) 1591 appendPolygon( rPolyPoly.getB2DPolygon( n ), rBuffer, bClose ); 1592 } 1593 1594 void PDFWriterImpl::PDFPage::appendMappedLength( sal_Int32 nLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength ) const 1595 { 1596 sal_Int32 nValue = nLength; 1597 if ( nLength < 0 ) 1598 { 1599 rBuffer.append( '-' ); 1600 nValue = -nLength; 1601 } 1602 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1603 m_pWriter->m_aMapMode, 1604 m_pWriter->getReferenceDevice(), 1605 Size( nValue, nValue ) ) ); 1606 nValue = bVertical ? aSize.Height() : aSize.Width(); 1607 if( pOutLength ) 1608 *pOutLength = ((nLength < 0 ) ? -nValue : nValue); 1609 1610 appendFixedInt( nValue, rBuffer, 1 ); 1611 } 1612 1613 void PDFWriterImpl::PDFPage::appendMappedLength( double fLength, OStringBuffer& rBuffer, bool bVertical, sal_Int32* pOutLength, sal_Int32 nPrecision ) const 1614 { 1615 Size aSize( lcl_convert( m_pWriter->m_aGraphicsStack.front().m_aMapMode, 1616 m_pWriter->m_aMapMode, 1617 m_pWriter->getReferenceDevice(), 1618 Size( 1000, 1000 ) ) ); 1619 if( pOutLength ) 1620 *pOutLength = (sal_Int32)(fLength*(double)(bVertical ? aSize.Height() : aSize.Width())/1000.0); 1621 fLength *= pixelToPoint((double)(bVertical ? aSize.Height() : aSize.Width()) / 1000.0); 1622 appendDouble( fLength, rBuffer, nPrecision ); 1623 } 1624 1625 bool PDFWriterImpl::PDFPage::appendLineInfo( const LineInfo& rInfo, OStringBuffer& rBuffer ) const 1626 { 1627 if(LINE_DASH == rInfo.GetStyle() && rInfo.GetDashLen() != rInfo.GetDotLen()) 1628 { 1629 // dashed and non-degraded case, check for implementation limits of dash array 1630 // in PDF reader apps (e.g. acroread) 1631 if(2 * (rInfo.GetDashCount() + rInfo.GetDotCount()) > 10) 1632 { 1633 return false; 1634 } 1635 } 1636 1637 if(basegfx::B2DLINEJOIN_NONE != rInfo.GetLineJoin()) 1638 { 1639 // LineJoin used, ExtLineInfo required 1640 return false; 1641 } 1642 1643 if(com::sun::star::drawing::LineCap_BUTT != rInfo.GetLineCap()) 1644 { 1645 // LineCap used, ExtLineInfo required 1646 return false; 1647 } 1648 1649 if( rInfo.GetStyle() == LINE_DASH ) 1650 { 1651 rBuffer.append( "[ " ); 1652 if( rInfo.GetDashLen() == rInfo.GetDotLen() ) // degraded case 1653 { 1654 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer ); 1655 rBuffer.append( ' ' ); 1656 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); 1657 rBuffer.append( ' ' ); 1658 } 1659 else 1660 { 1661 for( int n = 0; n < rInfo.GetDashCount(); n++ ) 1662 { 1663 appendMappedLength( (sal_Int32)rInfo.GetDashLen(), rBuffer ); 1664 rBuffer.append( ' ' ); 1665 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); 1666 rBuffer.append( ' ' ); 1667 } 1668 for( int m = 0; m < rInfo.GetDotCount(); m++ ) 1669 { 1670 appendMappedLength( (sal_Int32)rInfo.GetDotLen(), rBuffer ); 1671 rBuffer.append( ' ' ); 1672 appendMappedLength( (sal_Int32)rInfo.GetDistance(), rBuffer ); 1673 rBuffer.append( ' ' ); 1674 } 1675 } 1676 rBuffer.append( "] 0 d\n" ); 1677 } 1678 1679 if( rInfo.GetWidth() > 1 ) 1680 { 1681 appendMappedLength( (sal_Int32)rInfo.GetWidth(), rBuffer ); 1682 rBuffer.append( " w\n" ); 1683 } 1684 else if( rInfo.GetWidth() == 0 ) 1685 { 1686 // "pixel" line 1687 appendDouble( 72.0/double(m_pWriter->getReferenceDevice()->ImplGetDPIX()), rBuffer ); 1688 rBuffer.append( " w\n" ); 1689 } 1690 1691 return true; 1692 } 1693 1694 void PDFWriterImpl::PDFPage::appendWaveLine( sal_Int32 nWidth, sal_Int32 nY, sal_Int32 nDelta, OStringBuffer& rBuffer ) const 1695 { 1696 if( nWidth <= 0 ) 1697 return; 1698 if( nDelta < 1 ) 1699 nDelta = 1; 1700 1701 rBuffer.append( "0 " ); 1702 appendMappedLength( nY, rBuffer, true ); 1703 rBuffer.append( " m\n" ); 1704 for( sal_Int32 n = 0; n < nWidth; ) 1705 { 1706 n += nDelta; 1707 appendMappedLength( n, rBuffer, false ); 1708 rBuffer.append( ' ' ); 1709 appendMappedLength( nDelta+nY, rBuffer, true ); 1710 rBuffer.append( ' ' ); 1711 n += nDelta; 1712 appendMappedLength( n, rBuffer, false ); 1713 rBuffer.append( ' ' ); 1714 appendMappedLength( nY, rBuffer, true ); 1715 rBuffer.append( " v " ); 1716 if( n < nWidth ) 1717 { 1718 n += nDelta; 1719 appendMappedLength( n, rBuffer, false ); 1720 rBuffer.append( ' ' ); 1721 appendMappedLength( nY-nDelta, rBuffer, true ); 1722 rBuffer.append( ' ' ); 1723 n += nDelta; 1724 appendMappedLength( n, rBuffer, false ); 1725 rBuffer.append( ' ' ); 1726 appendMappedLength( nY, rBuffer, true ); 1727 rBuffer.append( " v\n" ); 1728 } 1729 } 1730 rBuffer.append( "S\n" ); 1731 } 1732 1733 /* 1734 * class PDFWriterImpl 1735 */ 1736 1737 PDFWriterImpl::PDFWriterImpl( const PDFWriter::PDFWriterContext& rContext, 1738 const com::sun::star::uno::Reference< com::sun::star::beans::XMaterialHolder >& xEnc, 1739 PDFWriter& i_rOuterFace) 1740 : 1741 m_pReferenceDevice( NULL ), 1742 m_aMapMode( MAP_POINT, Point(), Fraction( 1L, pointToPixel(1) ), Fraction( 1L, pointToPixel(1) ) ), 1743 m_nCurrentStructElement( 0 ), 1744 m_bEmitStructure( true ), 1745 m_bNewMCID( false ), 1746 m_nCurrentControl( -1 ), 1747 m_bEmbedStandardFonts( false ), 1748 m_nNextFID( 1 ), 1749 m_nInheritedPageWidth( 595 ), // default A4 1750 m_nInheritedPageHeight( 842 ), // default A4 1751 m_eInheritedOrientation( PDFWriter::Portrait ), 1752 m_nCurrentPage( -1 ), 1753 m_nResourceDict( -1 ), 1754 m_nFontDictObject( -1 ), 1755 m_pCodec( NULL ), 1756 m_aDocDigest( rtl_digest_createMD5() ), 1757 m_aCipher( (rtlCipher)NULL ), 1758 m_aDigest( NULL ), 1759 m_bEncryptThisStream( false ), 1760 m_pEncryptionBuffer( NULL ), 1761 m_nEncryptionBufferSize( 0 ), 1762 m_bIsPDF_A1( false ), 1763 m_rOuterFace( i_rOuterFace ) 1764 { 1765 #ifdef DO_TEST_PDF 1766 static bool bOnce = true; 1767 if( bOnce ) 1768 { 1769 bOnce = false; 1770 doTestCode(); 1771 } 1772 #endif 1773 m_aContext = rContext; 1774 m_aStructure.push_back( PDFStructureElement() ); 1775 m_aStructure[0].m_nOwnElement = 0; 1776 m_aStructure[0].m_nParentElement = 0; 1777 1778 Font aFont; 1779 aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) ); 1780 aFont.SetSize( Size( 0, 12 ) ); 1781 1782 GraphicsState aState; 1783 aState.m_aMapMode = m_aMapMode; 1784 aState.m_aFont = aFont; 1785 m_aGraphicsStack.push_front( aState ); 1786 1787 oslFileError aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ); 1788 if( aError != osl_File_E_None ) 1789 { 1790 if( aError == osl_File_E_EXIST ) 1791 { 1792 aError = osl_openFile( m_aContext.URL.pData, &m_aFile, osl_File_OpenFlag_Write ); 1793 if( aError == osl_File_E_None ) 1794 aError = osl_setFileSize( m_aFile, 0 ); 1795 } 1796 } 1797 if( aError != osl_File_E_None ) 1798 return; 1799 1800 m_bOpen = true; 1801 1802 // setup DocInfo 1803 setupDocInfo(); 1804 1805 /* prepare the cypher engine, can be done in CTOR, free in DTOR */ 1806 m_aCipher = rtl_cipher_createARCFOUR( rtl_Cipher_ModeStream ); 1807 m_aDigest = rtl_digest_createMD5(); 1808 1809 /* the size of the Codec default maximum */ 1810 checkEncryptionBufferSize( 0x4000 ); 1811 1812 if( xEnc.is() ) 1813 prepareEncryption( xEnc ); 1814 1815 if( m_aContext.Encryption.Encrypt() ) 1816 { 1817 // sanity check 1818 if( m_aContext.Encryption.OValue.size() != ENCRYPTED_PWD_SIZE || 1819 m_aContext.Encryption.UValue.size() != ENCRYPTED_PWD_SIZE || 1820 m_aContext.Encryption.EncryptionKey.size() != MAXIMUM_RC4_KEY_LENGTH 1821 ) 1822 { 1823 // the field lengths are invalid ? This was not setup by initEncryption. 1824 // do not encrypt after all 1825 m_aContext.Encryption.OValue.clear(); 1826 m_aContext.Encryption.UValue.clear(); 1827 OSL_ENSURE( 0, "encryption data failed sanity check, encryption disabled" ); 1828 } 1829 else // setup key lengths 1830 m_nAccessPermissions = computeAccessPermissions( m_aContext.Encryption, m_nKeyLength, m_nRC4KeyLength ); 1831 } 1832 1833 // write header 1834 OStringBuffer aBuffer( 20 ); 1835 aBuffer.append( "%PDF-" ); 1836 switch( m_aContext.Version ) 1837 { 1838 case PDFWriter::PDF_1_2: aBuffer.append( "1.2" );break; 1839 case PDFWriter::PDF_1_3: aBuffer.append( "1.3" );break; 1840 case PDFWriter::PDF_A_1: 1841 default: 1842 case PDFWriter::PDF_1_4: aBuffer.append( "1.4" );break; 1843 case PDFWriter::PDF_1_5: aBuffer.append( "1.5" );break; 1844 } 1845 // append something binary as comment (suggested in PDF Reference) 1846 aBuffer.append( "\n%äüöß\n" ); 1847 if( !writeBuffer( aBuffer.getStr(), aBuffer.getLength() ) ) 1848 { 1849 osl_closeFile( m_aFile ); 1850 m_bOpen = false; 1851 return; 1852 } 1853 1854 // insert outline root 1855 m_aOutline.push_back( PDFOutlineEntry() ); 1856 1857 m_bIsPDF_A1 = (m_aContext.Version == PDFWriter::PDF_A_1); 1858 if( m_bIsPDF_A1 ) 1859 m_aContext.Version = PDFWriter::PDF_1_4; //meaning we need PDF 1.4, PDF/A flavour 1860 1861 m_bEmbedStandardFonts = m_aContext.EmbedStandardFonts; 1862 } 1863 1864 PDFWriterImpl::~PDFWriterImpl() 1865 { 1866 if( m_aDocDigest ) 1867 rtl_digest_destroyMD5( m_aDocDigest ); 1868 delete static_cast<VirtualDevice*>(m_pReferenceDevice); 1869 1870 if( m_aCipher ) 1871 rtl_cipher_destroyARCFOUR( m_aCipher ); 1872 if( m_aDigest ) 1873 rtl_digest_destroyMD5( m_aDigest ); 1874 1875 rtl_freeMemory( m_pEncryptionBuffer ); 1876 } 1877 1878 void PDFWriterImpl::setupDocInfo() 1879 { 1880 std::vector< sal_uInt8 > aId; 1881 computeDocumentIdentifier( aId, m_aContext.DocumentInfo, m_aCreationDateString, m_aCreationMetaDateString ); 1882 if( m_aContext.Encryption.DocumentIdentifier.empty() ) 1883 m_aContext.Encryption.DocumentIdentifier = aId; 1884 } 1885 1886 void PDFWriterImpl::computeDocumentIdentifier( std::vector< sal_uInt8 >& o_rIdentifier, 1887 const vcl::PDFWriter::PDFDocInfo& i_rDocInfo, 1888 rtl::OString& o_rCString1, 1889 rtl::OString& o_rCString2 1890 ) 1891 { 1892 o_rIdentifier.clear(); 1893 1894 //build the document id 1895 rtl::OString aInfoValuesOut; 1896 OStringBuffer aID( 1024 ); 1897 if( i_rDocInfo.Title.Len() ) 1898 appendUnicodeTextString( i_rDocInfo.Title, aID ); 1899 if( i_rDocInfo.Author.Len() ) 1900 appendUnicodeTextString( i_rDocInfo.Author, aID ); 1901 if( i_rDocInfo.Subject.Len() ) 1902 appendUnicodeTextString( i_rDocInfo.Subject, aID ); 1903 if( i_rDocInfo.Keywords.Len() ) 1904 appendUnicodeTextString( i_rDocInfo.Keywords, aID ); 1905 if( i_rDocInfo.Creator.Len() ) 1906 appendUnicodeTextString( i_rDocInfo.Creator, aID ); 1907 if( i_rDocInfo.Producer.Len() ) 1908 appendUnicodeTextString( i_rDocInfo.Producer, aID ); 1909 1910 TimeValue aTVal, aGMT; 1911 oslDateTime aDT; 1912 osl_getSystemTime( &aGMT ); 1913 osl_getLocalTimeFromSystemTime( &aGMT, &aTVal ); 1914 osl_getDateTimeFromTimeValue( &aTVal, &aDT ); 1915 rtl::OStringBuffer aCreationDateString(64), aCreationMetaDateString(64); 1916 aCreationDateString.append( "D:" ); 1917 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); 1918 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); 1919 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); 1920 aCreationDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) ); 1921 aCreationDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); 1922 aCreationDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) ); 1923 aCreationDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); 1924 aCreationDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) ); 1925 aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); 1926 aCreationDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); 1927 aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); 1928 aCreationDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); 1929 aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); 1930 aCreationDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); 1931 1932 //--> i59651, we fill the Metadata date string as well, if PDF/A is requested 1933 // according to ISO 19005-1:2005 6.7.3 the date is corrected for 1934 // local time zone offset UTC only, whereas Acrobat 8 seems 1935 // to use the localtime notation only 1936 // according to a raccomandation in XMP Specification (Jan 2004, page 75) 1937 // the Acrobat way seems the right approach 1938 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/1000)%10)) ); 1939 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/100)%10)) ); 1940 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year/10)%10)) ); 1941 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Year)%10)) ); 1942 aCreationMetaDateString.append( "-" ); 1943 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month/10)%10)) ); 1944 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Month)%10)) ); 1945 aCreationMetaDateString.append( "-" ); 1946 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day/10)%10)) ); 1947 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Day)%10)) ); 1948 aCreationMetaDateString.append( "T" ); 1949 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours/10)%10)) ); 1950 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Hours)%10)) ); 1951 aCreationMetaDateString.append( ":" ); 1952 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes/10)%10)) ); 1953 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Minutes)%10)) ); 1954 aCreationMetaDateString.append( ":" ); 1955 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds/10)%10)) ); 1956 aCreationMetaDateString.append( (sal_Char)('0' + ((aDT.Seconds)%10)) ); 1957 1958 sal_uInt32 nDelta = 0; 1959 if( aGMT.Seconds > aTVal.Seconds ) 1960 { 1961 aCreationDateString.append( "-" ); 1962 nDelta = aGMT.Seconds-aTVal.Seconds; 1963 aCreationMetaDateString.append( "-" ); 1964 } 1965 else if( aGMT.Seconds < aTVal.Seconds ) 1966 { 1967 aCreationDateString.append( "+" ); 1968 nDelta = aTVal.Seconds-aGMT.Seconds; 1969 aCreationMetaDateString.append( "+" ); 1970 } 1971 else 1972 { 1973 aCreationDateString.append( "Z" ); 1974 aCreationMetaDateString.append( "Z" ); 1975 1976 } 1977 if( nDelta ) 1978 { 1979 aCreationDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); 1980 aCreationDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); 1981 aCreationDateString.append( "'" ); 1982 aCreationDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) ); 1983 aCreationDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) ); 1984 1985 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/36000)%10)) ); 1986 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/3600)%10)) ); 1987 aCreationMetaDateString.append( ":" ); 1988 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/600)%6)) ); 1989 aCreationMetaDateString.append( (sal_Char)('0' + ((nDelta/60)%10)) ); 1990 } 1991 aCreationDateString.append( "'" ); 1992 aID.append( aCreationDateString.getStr(), aCreationDateString.getLength() ); 1993 1994 aInfoValuesOut = aID.makeStringAndClear(); 1995 o_rCString1 = aCreationDateString.makeStringAndClear(); 1996 o_rCString2 = aCreationMetaDateString.makeStringAndClear(); 1997 1998 rtlDigest aDigest = rtl_digest_createMD5(); 1999 OSL_ENSURE( aDigest != NULL, "PDFWriterImpl::computeDocumentIdentifier: cannot obtain a digest object !" ); 2000 if( aDigest ) 2001 { 2002 rtlDigestError nError = rtl_digest_updateMD5( aDigest, &aGMT, sizeof( aGMT ) ); 2003 if( nError == rtl_Digest_E_None ) 2004 nError = rtl_digest_updateMD5( aDigest, aInfoValuesOut.getStr(), aInfoValuesOut.getLength() ); 2005 if( nError == rtl_Digest_E_None ) 2006 { 2007 o_rIdentifier = std::vector< sal_uInt8 >( 16, 0 ); 2008 //the binary form of the doc id is needed for encryption stuff 2009 rtl_digest_getMD5( aDigest, &o_rIdentifier[0], 16 ); 2010 } 2011 } 2012 } 2013 2014 /* i12626 methods */ 2015 /* 2016 check if the Unicode string must be encrypted or not, perform the requested task, 2017 append the string as unicode hex, encrypted if needed 2018 */ 2019 inline void PDFWriterImpl::appendUnicodeTextStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer ) 2020 { 2021 rOutBuffer.append( "<" ); 2022 if( m_aContext.Encryption.Encrypt() ) 2023 { 2024 const sal_Unicode* pStr = rInString.getStr(); 2025 sal_Int32 nLen = rInString.getLength(); 2026 //prepare a unicode string, encrypt it 2027 if( checkEncryptionBufferSize( nLen*2 ) ) 2028 { 2029 enableStringEncryption( nInObjectNumber ); 2030 register sal_uInt8 *pCopy = m_pEncryptionBuffer; 2031 sal_Int32 nChars = 2; 2032 *pCopy++ = 0xFE; 2033 *pCopy++ = 0xFF; 2034 // we need to prepare a byte stream from the unicode string buffer 2035 for( register int i = 0; i < nLen; i++ ) 2036 { 2037 register sal_Unicode aUnChar = pStr[i]; 2038 *pCopy++ = (sal_uInt8)( aUnChar >> 8 ); 2039 *pCopy++ = (sal_uInt8)( aUnChar & 255 ); 2040 nChars += 2; 2041 } 2042 //encrypt in place 2043 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChars, m_pEncryptionBuffer, nChars ); 2044 //now append, hexadecimal (appendHex), the encrypted result 2045 for(register int i = 0; i < nChars; i++) 2046 appendHex( m_pEncryptionBuffer[i], rOutBuffer ); 2047 } 2048 } 2049 else 2050 appendUnicodeTextString( rInString, rOutBuffer ); 2051 rOutBuffer.append( ">" ); 2052 } 2053 2054 inline void PDFWriterImpl::appendLiteralStringEncrypt( rtl::OStringBuffer& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer ) 2055 { 2056 rOutBuffer.append( "(" ); 2057 sal_Int32 nChars = rInString.getLength(); 2058 //check for encryption, if ok, encrypt the string, then convert with appndLiteralString 2059 if( m_aContext.Encryption.Encrypt() && checkEncryptionBufferSize( nChars ) ) 2060 { 2061 //encrypt the string in a buffer, then append it 2062 enableStringEncryption( nInObjectNumber ); 2063 rtl_cipher_encodeARCFOUR( m_aCipher, rInString.getStr(), nChars, m_pEncryptionBuffer, nChars ); 2064 appendLiteralString( (const sal_Char*)m_pEncryptionBuffer, nChars, rOutBuffer ); 2065 } 2066 else 2067 appendLiteralString( rInString.getStr(), nChars , rOutBuffer ); 2068 rOutBuffer.append( ")" ); 2069 } 2070 2071 inline void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer ) 2072 { 2073 rtl::OStringBuffer aBufferString( rInString ); 2074 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer); 2075 } 2076 2077 void PDFWriterImpl::appendLiteralStringEncrypt( const rtl::OUString& rInString, const sal_Int32 nInObjectNumber, rtl::OStringBuffer& rOutBuffer, rtl_TextEncoding nEnc ) 2078 { 2079 rtl::OString aBufferString( rtl::OUStringToOString( rInString, nEnc ) ); 2080 sal_Int32 nLen = aBufferString.getLength(); 2081 rtl::OStringBuffer aBuf( nLen ); 2082 const sal_Char* pT = aBufferString.getStr(); 2083 2084 for( sal_Int32 i = 0; i < nLen; i++, pT++ ) 2085 { 2086 if( (*pT & 0x80) == 0 ) 2087 aBuf.append( *pT ); 2088 else 2089 { 2090 aBuf.append( '<' ); 2091 appendHex( *pT, aBuf ); 2092 aBuf.append( '>' ); 2093 } 2094 } 2095 aBufferString = aBuf.makeStringAndClear(); 2096 appendLiteralStringEncrypt( aBufferString, nInObjectNumber, rOutBuffer); 2097 } 2098 2099 /* end i12626 methods */ 2100 2101 void PDFWriterImpl::emitComment( const char* pComment ) 2102 { 2103 OStringBuffer aLine( 64 ); 2104 aLine.append( "% " ); 2105 aLine.append( (const sal_Char*)pComment ); 2106 aLine.append( "\n" ); 2107 writeBuffer( aLine.getStr(), aLine.getLength() ); 2108 } 2109 2110 bool PDFWriterImpl::compressStream( SvMemoryStream* pStream ) 2111 { 2112 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 2113 pStream->Seek( STREAM_SEEK_TO_END ); 2114 sal_uLong nEndPos = pStream->Tell(); 2115 pStream->Seek( STREAM_SEEK_TO_BEGIN ); 2116 ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 ); 2117 SvMemoryStream aStream; 2118 pCodec->BeginCompression(); 2119 pCodec->Write( aStream, (const sal_uInt8*)pStream->GetData(), nEndPos ); 2120 pCodec->EndCompression(); 2121 delete pCodec; 2122 nEndPos = aStream.Tell(); 2123 pStream->Seek( STREAM_SEEK_TO_BEGIN ); 2124 aStream.Seek( STREAM_SEEK_TO_BEGIN ); 2125 pStream->SetStreamSize( nEndPos ); 2126 pStream->Write( aStream.GetData(), nEndPos ); 2127 return true; 2128 #else 2129 (void)pStream; 2130 return false; 2131 #endif 2132 } 2133 2134 void PDFWriterImpl::beginCompression() 2135 { 2136 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 2137 m_pCodec = new ZCodec( 0x4000, 0x4000 ); 2138 m_pMemStream = new SvMemoryStream(); 2139 m_pCodec->BeginCompression(); 2140 #endif 2141 } 2142 2143 void PDFWriterImpl::endCompression() 2144 { 2145 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 2146 if( m_pCodec ) 2147 { 2148 m_pCodec->EndCompression(); 2149 delete m_pCodec; 2150 m_pCodec = NULL; 2151 sal_uInt64 nLen = m_pMemStream->Tell(); 2152 m_pMemStream->Seek( 0 ); 2153 writeBuffer( m_pMemStream->GetData(), nLen ); 2154 delete m_pMemStream; 2155 m_pMemStream = NULL; 2156 } 2157 #endif 2158 } 2159 2160 bool PDFWriterImpl::writeBuffer( const void* pBuffer, sal_uInt64 nBytes ) 2161 { 2162 if( ! m_bOpen ) // we are already down the drain 2163 return false; 2164 2165 if( ! nBytes ) // huh ? 2166 return true; 2167 2168 if( m_aOutputStreams.begin() != m_aOutputStreams.end() ) 2169 { 2170 m_aOutputStreams.front().m_pStream->Seek( STREAM_SEEK_TO_END ); 2171 m_aOutputStreams.front().m_pStream->Write( pBuffer, sal::static_int_cast<sal_Size>(nBytes) ); 2172 return true; 2173 } 2174 2175 sal_uInt64 nWritten; 2176 if( m_pCodec ) 2177 { 2178 m_pCodec->Write( *m_pMemStream, static_cast<const sal_uInt8*>(pBuffer), (sal_uLong)nBytes ); 2179 nWritten = nBytes; 2180 } 2181 else 2182 { 2183 sal_Bool buffOK = sal_True; 2184 if( m_bEncryptThisStream ) 2185 { 2186 /* implement the encryption part of the PDF spec encryption algorithm 3.1 */ 2187 if( ( buffOK = checkEncryptionBufferSize( static_cast<sal_Int32>(nBytes) ) ) != sal_False ) 2188 rtl_cipher_encodeARCFOUR( m_aCipher, 2189 (sal_uInt8*)pBuffer, static_cast<sal_Size>(nBytes), 2190 m_pEncryptionBuffer, static_cast<sal_Size>(nBytes) ); 2191 } 2192 2193 const void* pWriteBuffer = ( m_bEncryptThisStream && buffOK ) ? m_pEncryptionBuffer : pBuffer; 2194 if( m_aDocDigest ) 2195 rtl_digest_updateMD5( m_aDocDigest, pWriteBuffer, static_cast<sal_uInt32>(nBytes) ); 2196 2197 if( osl_writeFile( m_aFile, 2198 pWriteBuffer, 2199 nBytes, &nWritten ) != osl_File_E_None ) 2200 nWritten = 0; 2201 2202 if( nWritten != nBytes ) 2203 { 2204 osl_closeFile( m_aFile ); 2205 m_bOpen = false; 2206 } 2207 } 2208 2209 return nWritten == nBytes; 2210 } 2211 2212 OutputDevice* PDFWriterImpl::getReferenceDevice() 2213 { 2214 if( ! m_pReferenceDevice ) 2215 { 2216 VirtualDevice* pVDev = new VirtualDevice( 0 ); 2217 2218 m_pReferenceDevice = pVDev; 2219 2220 if( m_aContext.DPIx == 0 || m_aContext.DPIy == 0 ) 2221 pVDev->SetReferenceDevice( VirtualDevice::REFDEV_MODE_PDF1 ); 2222 else 2223 pVDev->SetReferenceDevice( m_aContext.DPIx, m_aContext.DPIy ); 2224 2225 pVDev->SetOutputSizePixel( Size( 640, 480 ) ); 2226 pVDev->SetMapMode( MAP_MM ); 2227 2228 m_pReferenceDevice->mpPDFWriter = this; 2229 m_pReferenceDevice->ImplUpdateFontData( sal_True ); 2230 } 2231 return m_pReferenceDevice; 2232 } 2233 2234 class ImplPdfBuiltinFontData : public ImplFontData 2235 { 2236 private: 2237 const PDFWriterImpl::BuiltinFont& mrBuiltin; 2238 2239 public: 2240 enum {PDF_FONT_MAGIC = 0xBDFF0A1C }; 2241 ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& ); 2242 const PDFWriterImpl::BuiltinFont* GetBuiltinFont() const { return &mrBuiltin; } 2243 2244 virtual ImplFontData* Clone() const { return new ImplPdfBuiltinFontData(*this); } 2245 virtual ImplFontEntry* CreateFontInstance( ImplFontSelectData& ) const; 2246 virtual sal_IntPtr GetFontId() const { return reinterpret_cast<sal_IntPtr>(&mrBuiltin); } 2247 }; 2248 2249 inline const ImplPdfBuiltinFontData* GetPdfFontData( const ImplFontData* pFontData ) 2250 { 2251 const ImplPdfBuiltinFontData* pFD = NULL; 2252 if( pFontData && pFontData->CheckMagic( ImplPdfBuiltinFontData::PDF_FONT_MAGIC ) ) 2253 pFD = static_cast<const ImplPdfBuiltinFontData*>( pFontData ); 2254 return pFD; 2255 } 2256 2257 static ImplDevFontAttributes GetDevFontAttributes( const PDFWriterImpl::BuiltinFont& rBuiltin ) 2258 { 2259 ImplDevFontAttributes aDFA; 2260 aDFA.maName = String::CreateFromAscii( rBuiltin.m_pName ); 2261 aDFA.maStyleName = String::CreateFromAscii( rBuiltin.m_pStyleName ); 2262 aDFA.meFamily = rBuiltin.m_eFamily; 2263 aDFA.mbSymbolFlag = (rBuiltin.m_eCharSet != RTL_TEXTENCODING_MS_1252 ); 2264 aDFA.mePitch = rBuiltin.m_ePitch; 2265 aDFA.meWeight = rBuiltin.m_eWeight; 2266 aDFA.meItalic = rBuiltin.m_eItalic; 2267 aDFA.meWidthType = rBuiltin.m_eWidthType; 2268 2269 aDFA.mbOrientation = true; 2270 aDFA.mbDevice = true; 2271 aDFA.mnQuality = 50000; 2272 aDFA.mbSubsettable = false; 2273 aDFA.mbEmbeddable = false; 2274 return aDFA; 2275 } 2276 2277 ImplPdfBuiltinFontData::ImplPdfBuiltinFontData( const PDFWriterImpl::BuiltinFont& rBuiltin ) 2278 : ImplFontData( GetDevFontAttributes(rBuiltin), PDF_FONT_MAGIC ), 2279 mrBuiltin( rBuiltin ) 2280 {} 2281 2282 ImplFontEntry* ImplPdfBuiltinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const 2283 { 2284 ImplFontEntry* pEntry = new ImplFontEntry( rFSD ); 2285 return pEntry; 2286 } 2287 2288 ImplDevFontList* PDFWriterImpl::filterDevFontList( ImplDevFontList* pFontList ) 2289 { 2290 DBG_ASSERT( m_aSubsets.size() == 0, "Fonts changing during PDF generation, document will be invalid" ); 2291 ImplDevFontList* pFiltered = pFontList->Clone( true, true ); 2292 2293 // append the PDF builtin fonts 2294 if( !m_bIsPDF_A1 && !m_bEmbedStandardFonts) 2295 for( unsigned int i = 0; i < sizeof(m_aBuiltinFonts)/sizeof(m_aBuiltinFonts[0]); i++ ) 2296 { 2297 ImplFontData* pNewData = new ImplPdfBuiltinFontData( m_aBuiltinFonts[i] ); 2298 pFiltered->Add( pNewData ); 2299 } 2300 return pFiltered; 2301 } 2302 2303 bool PDFWriterImpl::isBuiltinFont( const ImplFontData* pFont ) const 2304 { 2305 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont ); 2306 return (pFD != NULL); 2307 } 2308 2309 void PDFWriterImpl::getFontMetric( ImplFontSelectData* pSelect, ImplFontMetricData* pMetric ) const 2310 { 2311 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData ); 2312 if( !pFD ) 2313 return; 2314 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); 2315 2316 pMetric->mnOrientation = sal::static_int_cast<short>(pSelect->mnOrientation); 2317 pMetric->meFamily = pBuiltinFont->m_eFamily; 2318 pMetric->mePitch = pBuiltinFont->m_ePitch; 2319 pMetric->meWeight = pBuiltinFont->m_eWeight; 2320 pMetric->meItalic = pBuiltinFont->m_eItalic; 2321 pMetric->mbSymbolFlag = pFD->IsSymbolFont(); 2322 pMetric->mnWidth = pSelect->mnHeight; 2323 pMetric->mnAscent = ( pSelect->mnHeight * +pBuiltinFont->m_nAscent + 500 ) / 1000; 2324 pMetric->mnDescent = ( pSelect->mnHeight * -pBuiltinFont->m_nDescent + 500 ) / 1000; 2325 pMetric->mnIntLeading = 0; 2326 pMetric->mnExtLeading = 0; 2327 pMetric->mnSlant = 0; 2328 pMetric->mbScalableFont = true; 2329 pMetric->mbDevice = true; 2330 } 2331 2332 // ----------------------------------------------------------------------- 2333 2334 namespace vcl { 2335 2336 class PDFSalLayout : public GenericSalLayout 2337 { 2338 PDFWriterImpl& mrPDFWriterImpl; 2339 const PDFWriterImpl::BuiltinFont& mrBuiltinFont; 2340 bool mbIsSymbolFont; 2341 long mnPixelPerEM; 2342 String maOrigText; 2343 2344 public: 2345 PDFSalLayout( PDFWriterImpl&, 2346 const PDFWriterImpl::BuiltinFont&, 2347 long nPixelPerEM, int nOrientation ); 2348 2349 void SetText( const String& rText ) { maOrigText = rText; } 2350 virtual bool LayoutText( ImplLayoutArgs& ); 2351 virtual void InitFont() const; 2352 virtual void DrawText( SalGraphics& ) const; 2353 }; 2354 2355 } 2356 2357 // ----------------------------------------------------------------------- 2358 2359 PDFSalLayout::PDFSalLayout( PDFWriterImpl& rPDFWriterImpl, 2360 const PDFWriterImpl::BuiltinFont& rBuiltinFont, 2361 long nPixelPerEM, int nOrientation ) 2362 : mrPDFWriterImpl( rPDFWriterImpl ), 2363 mrBuiltinFont( rBuiltinFont ), 2364 mnPixelPerEM( nPixelPerEM ) 2365 { 2366 mbIsSymbolFont = (rBuiltinFont.m_eCharSet != RTL_TEXTENCODING_MS_1252); 2367 SetOrientation( nOrientation ); 2368 } 2369 2370 // ----------------------------------------------------------------------- 2371 2372 bool PDFSalLayout::LayoutText( ImplLayoutArgs& rArgs ) 2373 { 2374 const String aText( rArgs.mpStr+rArgs.mnMinCharPos, sal::static_int_cast<xub_StrLen>(rArgs.mnEndCharPos-rArgs.mnMinCharPos) ); 2375 SetText( aText ); 2376 SetUnitsPerPixel( 1000 ); 2377 2378 rtl_UnicodeToTextConverter aConv = rtl_createTextToUnicodeConverter( mrBuiltinFont.m_eCharSet ); 2379 2380 Point aNewPos( 0, 0 ); 2381 bool bRightToLeft; 2382 for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); ) 2383 { 2384 // TODO: handle unicode surrogates 2385 // on the other hand the PDF builtin fonts don't support them anyway 2386 sal_Unicode cChar = rArgs.mpStr[ nCharPos ]; 2387 if( bRightToLeft ) 2388 cChar = static_cast<sal_Unicode>(GetMirroredChar( cChar )); 2389 2390 if( 1 ) // TODO: shortcut for ASCII? 2391 { 2392 sal_Char aBuf[4]; 2393 sal_uInt32 nInfo; 2394 sal_Size nSrcCvtChars; 2395 2396 sal_Size nConv = rtl_convertUnicodeToText( aConv, 2397 NULL, 2398 &cChar, 1, 2399 aBuf, sizeof(aBuf)/sizeof(*aBuf), 2400 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR, 2401 &nInfo, &nSrcCvtChars ); 2402 // check whether conversion was possible 2403 // else fallback font is needed as the standard fonts 2404 // are handled via WinAnsi encoding 2405 if( nConv > 0 ) 2406 cChar = ((sal_Unicode)aBuf[0]) & 0x00ff; 2407 } 2408 if( cChar & 0xff00 ) 2409 { 2410 cChar = 0; // NotDef glyph 2411 rArgs.NeedFallback( nCharPos, bRightToLeft ); 2412 } 2413 2414 long nGlyphWidth = (long)mrBuiltinFont.m_aWidths[cChar] * mnPixelPerEM; 2415 long nGlyphFlags = 0; // builtin fonts don't have diacritic glyphs 2416 if( bRightToLeft ) 2417 nGlyphFlags |= GlyphItem::IS_RTL_GLYPH; 2418 // TODO: get kerning from builtin fonts 2419 GlyphItem aGI( nCharPos, cChar, aNewPos, nGlyphFlags, nGlyphWidth ); 2420 AppendGlyph( aGI ); 2421 2422 aNewPos.X() += nGlyphWidth; 2423 } 2424 2425 rtl_destroyUnicodeToTextConverter( aConv ); 2426 2427 return true; 2428 } 2429 2430 // ----------------------------------------------------------------------- 2431 2432 void PDFSalLayout::InitFont() const 2433 { 2434 // TODO: recreate font with all its attributes 2435 } 2436 2437 // ----------------------------------------------------------------------- 2438 2439 void PDFSalLayout::DrawText( SalGraphics& ) const 2440 { 2441 mrPDFWriterImpl.drawLayout( *const_cast<PDFSalLayout*>(this), maOrigText, true ); 2442 } 2443 2444 // ----------------------------------------------------------------------- 2445 2446 SalLayout* PDFWriterImpl::GetTextLayout( ImplLayoutArgs& rArgs, ImplFontSelectData* pSelect ) 2447 { 2448 DBG_ASSERT( (pSelect->mpFontData != NULL), 2449 "PDFWriterImpl::GetTextLayout mpFontData is NULL" ); 2450 2451 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pSelect->mpFontData ); 2452 if( !pFD ) 2453 return NULL; 2454 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); 2455 2456 long nPixelPerEM = pSelect->mnWidth ? pSelect->mnWidth : pSelect->mnHeight; 2457 int nOrientation = pSelect->mnOrientation; 2458 PDFSalLayout* pLayout = new PDFSalLayout( *this, *pBuiltinFont, nPixelPerEM, nOrientation ); 2459 pLayout->SetText( rArgs.mpStr ); 2460 return pLayout; 2461 } 2462 2463 sal_Int32 PDFWriterImpl::newPage( sal_Int32 nPageWidth, sal_Int32 nPageHeight, PDFWriter::Orientation eOrientation ) 2464 { 2465 endPage(); 2466 m_nCurrentPage = m_aPages.size(); 2467 m_aPages.push_back( PDFPage(this, nPageWidth, nPageHeight, eOrientation ) ); 2468 m_aPages.back().m_nPageIndex = m_nCurrentPage; 2469 m_aPages.back().beginStream(); 2470 2471 // setup global graphics state 2472 // linewidth is "1 pixel" by default 2473 OStringBuffer aBuf( 16 ); 2474 appendDouble( 72.0/double(getReferenceDevice()->ImplGetDPIX()), aBuf ); 2475 aBuf.append( " w\n" ); 2476 writeBuffer( aBuf.getStr(), aBuf.getLength() ); 2477 2478 return m_nCurrentPage; 2479 } 2480 2481 void PDFWriterImpl::endPage() 2482 { 2483 if( m_aPages.begin() != m_aPages.end() ) 2484 { 2485 // close eventual MC sequence 2486 endStructureElementMCSeq(); 2487 2488 // sanity check 2489 if( m_aOutputStreams.begin() != m_aOutputStreams.end() ) 2490 { 2491 DBG_ERROR( "redirection across pages !!!" ); 2492 m_aOutputStreams.clear(); // leak ! 2493 m_aMapMode.SetOrigin( Point() ); 2494 } 2495 2496 m_aGraphicsStack.clear(); 2497 m_aGraphicsStack.push_back( GraphicsState() ); 2498 2499 // this should pop the PDF graphics stack if necessary 2500 updateGraphicsState(); 2501 2502 m_aPages.back().endStream(); 2503 2504 // reset the default font 2505 Font aFont; 2506 aFont.SetName( String( RTL_CONSTASCII_USTRINGPARAM( "Times" ) ) ); 2507 aFont.SetSize( Size( 0, 12 ) ); 2508 2509 m_aCurrentPDFState = m_aGraphicsStack.front(); 2510 m_aGraphicsStack.front().m_aFont = aFont; 2511 2512 for( std::list<BitmapEmit>::iterator it = m_aBitmaps.begin(); 2513 it != m_aBitmaps.end(); ++it ) 2514 { 2515 if( ! it->m_aBitmap.IsEmpty() ) 2516 { 2517 writeBitmapObject( *it ); 2518 it->m_aBitmap = BitmapEx(); 2519 } 2520 } 2521 for( std::list<JPGEmit>::iterator jpeg = m_aJPGs.begin(); jpeg != m_aJPGs.end(); ++jpeg ) 2522 { 2523 if( jpeg->m_pStream ) 2524 { 2525 writeJPG( *jpeg ); 2526 delete jpeg->m_pStream; 2527 jpeg->m_pStream = NULL; 2528 jpeg->m_aMask = Bitmap(); 2529 } 2530 } 2531 for( std::list<TransparencyEmit>::iterator t = m_aTransparentObjects.begin(); 2532 t != m_aTransparentObjects.end(); ++t ) 2533 { 2534 if( t->m_pContentStream ) 2535 { 2536 writeTransparentObject( *t ); 2537 delete t->m_pContentStream; 2538 t->m_pContentStream = NULL; 2539 } 2540 } 2541 } 2542 } 2543 2544 sal_Int32 PDFWriterImpl::createObject() 2545 { 2546 m_aObjects.push_back( ~0U ); 2547 return m_aObjects.size(); 2548 } 2549 2550 bool PDFWriterImpl::updateObject( sal_Int32 n ) 2551 { 2552 if( ! m_bOpen ) 2553 return false; 2554 2555 sal_uInt64 nOffset = ~0U; 2556 oslFileError aError = osl_getFilePos( m_aFile, &nOffset ); 2557 DBG_ASSERT( aError == osl_File_E_None, "could not register object" ); 2558 if( aError != osl_File_E_None ) 2559 { 2560 osl_closeFile( m_aFile ); 2561 m_bOpen = false; 2562 } 2563 m_aObjects[ n-1 ] = nOffset; 2564 return aError == osl_File_E_None; 2565 } 2566 2567 #define CHECK_RETURN( x ) if( !(x) ) return 0 2568 2569 sal_Int32 PDFWriterImpl::emitStructParentTree( sal_Int32 nObject ) 2570 { 2571 if( nObject > 0 ) 2572 { 2573 OStringBuffer aLine( 1024 ); 2574 2575 aLine.append( nObject ); 2576 aLine.append( " 0 obj\n" 2577 "<</Nums[\n" ); 2578 sal_Int32 nTreeItems = m_aStructParentTree.size(); 2579 for( sal_Int32 n = 0; n < nTreeItems; n++ ) 2580 { 2581 aLine.append( n ); 2582 aLine.append( ' ' ); 2583 aLine.append( m_aStructParentTree[n] ); 2584 aLine.append( "\n" ); 2585 } 2586 aLine.append( "]>>\nendobj\n\n" ); 2587 CHECK_RETURN( updateObject( nObject ) ); 2588 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 2589 } 2590 return nObject; 2591 } 2592 2593 const sal_Char* PDFWriterImpl::getAttributeTag( PDFWriter::StructAttribute eAttr ) 2594 { 2595 static std::map< PDFWriter::StructAttribute, const char* > aAttributeStrings; 2596 // fill maps once 2597 if( aAttributeStrings.empty() ) 2598 { 2599 aAttributeStrings[ PDFWriter::Placement ] = "Placement"; 2600 aAttributeStrings[ PDFWriter::WritingMode ] = "WritingMode"; 2601 aAttributeStrings[ PDFWriter::SpaceBefore ] = "SpaceBefore"; 2602 aAttributeStrings[ PDFWriter::SpaceAfter ] = "SpaceAfter"; 2603 aAttributeStrings[ PDFWriter::StartIndent ] = "StartIndent"; 2604 aAttributeStrings[ PDFWriter::EndIndent ] = "EndIndent"; 2605 aAttributeStrings[ PDFWriter::TextIndent ] = "TextIndent"; 2606 aAttributeStrings[ PDFWriter::TextAlign ] = "TextAlign"; 2607 aAttributeStrings[ PDFWriter::Width ] = "Width"; 2608 aAttributeStrings[ PDFWriter::Height ] = "Height"; 2609 aAttributeStrings[ PDFWriter::BlockAlign ] = "BlockAlign"; 2610 aAttributeStrings[ PDFWriter::InlineAlign ] = "InlineAlign"; 2611 aAttributeStrings[ PDFWriter::LineHeight ] = "LineHeight"; 2612 aAttributeStrings[ PDFWriter::BaselineShift ] = "BaselineShift"; 2613 aAttributeStrings[ PDFWriter::TextDecorationType ] = "TextDecorationType"; 2614 aAttributeStrings[ PDFWriter::ListNumbering ] = "ListNumbering"; 2615 aAttributeStrings[ PDFWriter::RowSpan ] = "RowSpan"; 2616 aAttributeStrings[ PDFWriter::ColSpan ] = "ColSpan"; 2617 aAttributeStrings[ PDFWriter::LinkAnnotation ] = "LinkAnnotation"; 2618 } 2619 2620 std::map< PDFWriter::StructAttribute, const char* >::const_iterator it = 2621 aAttributeStrings.find( eAttr ); 2622 2623 #if OSL_DEBUG_LEVEL > 1 2624 if( it == aAttributeStrings.end() ) 2625 fprintf( stderr, "invalid PDFWriter::StructAttribute %d\n", eAttr ); 2626 #endif 2627 2628 return it != aAttributeStrings.end() ? it->second : ""; 2629 } 2630 2631 const sal_Char* PDFWriterImpl::getAttributeValueTag( PDFWriter::StructAttributeValue eVal ) 2632 { 2633 static std::map< PDFWriter::StructAttributeValue, const char* > aValueStrings; 2634 2635 if( aValueStrings.empty() ) 2636 { 2637 aValueStrings[ PDFWriter::NONE ] = "None"; 2638 aValueStrings[ PDFWriter::Block ] = "Block"; 2639 aValueStrings[ PDFWriter::Inline ] = "Inline"; 2640 aValueStrings[ PDFWriter::Before ] = "Before"; 2641 aValueStrings[ PDFWriter::After ] = "After"; 2642 aValueStrings[ PDFWriter::Start ] = "Start"; 2643 aValueStrings[ PDFWriter::End ] = "End"; 2644 aValueStrings[ PDFWriter::LrTb ] = "LrTb"; 2645 aValueStrings[ PDFWriter::RlTb ] = "RlTb"; 2646 aValueStrings[ PDFWriter::TbRl ] = "TbRl"; 2647 aValueStrings[ PDFWriter::Center ] = "Center"; 2648 aValueStrings[ PDFWriter::Justify ] = "Justify"; 2649 aValueStrings[ PDFWriter::Auto ] = "Auto"; 2650 aValueStrings[ PDFWriter::Middle ] = "Middle"; 2651 aValueStrings[ PDFWriter::Normal ] = "Normal"; 2652 aValueStrings[ PDFWriter::Underline ] = "Underline"; 2653 aValueStrings[ PDFWriter::Overline ] = "Overline"; 2654 aValueStrings[ PDFWriter::LineThrough ] = "LineThrough"; 2655 aValueStrings[ PDFWriter::Disc ] = "Disc"; 2656 aValueStrings[ PDFWriter::Circle ] = "Circle"; 2657 aValueStrings[ PDFWriter::Square ] = "Square"; 2658 aValueStrings[ PDFWriter::Decimal ] = "Decimal"; 2659 aValueStrings[ PDFWriter::UpperRoman ] = "UpperRoman"; 2660 aValueStrings[ PDFWriter::LowerRoman ] = "LowerRoman"; 2661 aValueStrings[ PDFWriter::UpperAlpha ] = "UpperAlpha"; 2662 aValueStrings[ PDFWriter::LowerAlpha ] = "LowerAlpha"; 2663 } 2664 2665 std::map< PDFWriter::StructAttributeValue, const char* >::const_iterator it = 2666 aValueStrings.find( eVal ); 2667 2668 #if OSL_DEBUG_LEVEL > 1 2669 if( it == aValueStrings.end() ) 2670 fprintf( stderr, "invalid PDFWriter::StructAttributeValue %d\n", eVal ); 2671 #endif 2672 2673 return it != aValueStrings.end() ? it->second : ""; 2674 } 2675 2676 static void appendStructureAttributeLine( PDFWriter::StructAttribute i_eAttr, const PDFWriterImpl::PDFStructureAttribute& i_rVal, OStringBuffer& o_rLine, bool i_bIsFixedInt ) 2677 { 2678 o_rLine.append( "/" ); 2679 o_rLine.append( PDFWriterImpl::getAttributeTag( i_eAttr ) ); 2680 2681 if( i_rVal.eValue != PDFWriter::Invalid ) 2682 { 2683 o_rLine.append( "/" ); 2684 o_rLine.append( PDFWriterImpl::getAttributeValueTag( i_rVal.eValue ) ); 2685 } 2686 else 2687 { 2688 // numerical value 2689 o_rLine.append( " " ); 2690 if( i_bIsFixedInt ) 2691 appendFixedInt( i_rVal.nValue, o_rLine ); 2692 else 2693 o_rLine.append( i_rVal.nValue ); 2694 } 2695 o_rLine.append( "\n" ); 2696 } 2697 2698 OString PDFWriterImpl::emitStructureAttributes( PDFStructureElement& i_rEle ) 2699 { 2700 // create layout, list and table attribute sets 2701 OStringBuffer aLayout(256), aList(64), aTable(64); 2702 for( PDFStructAttributes::const_iterator it = i_rEle.m_aAttributes.begin(); 2703 it != i_rEle.m_aAttributes.end(); ++it ) 2704 { 2705 if( it->first == PDFWriter::ListNumbering ) 2706 appendStructureAttributeLine( it->first, it->second, aList, true ); 2707 else if( it->first == PDFWriter::RowSpan || 2708 it->first == PDFWriter::ColSpan ) 2709 appendStructureAttributeLine( it->first, it->second, aTable, false ); 2710 else if( it->first == PDFWriter::LinkAnnotation ) 2711 { 2712 sal_Int32 nLink = it->second.nValue; 2713 std::map< sal_Int32, sal_Int32 >::const_iterator link_it = 2714 m_aLinkPropertyMap.find( nLink ); 2715 if( link_it != m_aLinkPropertyMap.end() ) 2716 nLink = link_it->second; 2717 if( nLink >= 0 && nLink < (sal_Int32)m_aLinks.size() ) 2718 { 2719 // update struct parent of link 2720 OStringBuffer aStructParentEntry( 32 ); 2721 aStructParentEntry.append( i_rEle.m_nObject ); 2722 aStructParentEntry.append( " 0 R" ); 2723 m_aStructParentTree.push_back( aStructParentEntry.makeStringAndClear() ); 2724 m_aLinks[ nLink ].m_nStructParent = m_aStructParentTree.size()-1; 2725 2726 sal_Int32 nRefObject = createObject(); 2727 OStringBuffer aRef( 256 ); 2728 aRef.append( nRefObject ); 2729 aRef.append( " 0 obj\n" 2730 "<</Type/OBJR/Obj " ); 2731 aRef.append( m_aLinks[ nLink ].m_nObject ); 2732 aRef.append( " 0 R>>\n" 2733 "endobj\n\n" 2734 ); 2735 updateObject( nRefObject ); 2736 writeBuffer( aRef.getStr(), aRef.getLength() ); 2737 2738 i_rEle.m_aKids.push_back( PDFStructureElementKid( nRefObject ) ); 2739 } 2740 else 2741 { 2742 DBG_ERROR( "unresolved link id for Link structure" ); 2743 #if OSL_DEBUG_LEVEL > 1 2744 fprintf( stderr, "unresolved link id %" SAL_PRIdINT32 " for Link structure\n", nLink ); 2745 { 2746 OStringBuffer aLine( "unresolved link id " ); 2747 aLine.append( nLink ); 2748 aLine.append( " for Link structure" ); 2749 emitComment( aLine.getStr() ); 2750 } 2751 #endif 2752 } 2753 } 2754 else 2755 appendStructureAttributeLine( it->first, it->second, aLayout, true ); 2756 } 2757 if( ! i_rEle.m_aBBox.IsEmpty() ) 2758 { 2759 aLayout.append( "/BBox[" ); 2760 appendFixedInt( i_rEle.m_aBBox.Left(), aLayout ); 2761 aLayout.append( " " ); 2762 appendFixedInt( i_rEle.m_aBBox.Top(), aLayout ); 2763 aLayout.append( " " ); 2764 appendFixedInt( i_rEle.m_aBBox.Right(), aLayout ); 2765 aLayout.append( " " ); 2766 appendFixedInt( i_rEle.m_aBBox.Bottom(), aLayout ); 2767 aLayout.append( "]\n" ); 2768 } 2769 2770 std::vector< sal_Int32 > aAttribObjects; 2771 if( aLayout.getLength() ) 2772 { 2773 aAttribObjects.push_back( createObject() ); 2774 updateObject( aAttribObjects.back() ); 2775 OStringBuffer aObj( 64 ); 2776 aObj.append( aAttribObjects.back() ); 2777 aObj.append( " 0 obj\n" 2778 "<</O/Layout\n" ); 2779 aLayout.append( ">>\nendobj\n\n" ); 2780 writeBuffer( aObj.getStr(), aObj.getLength() ); 2781 writeBuffer( aLayout.getStr(), aLayout.getLength() ); 2782 } 2783 if( aList.getLength() ) 2784 { 2785 aAttribObjects.push_back( createObject() ); 2786 updateObject( aAttribObjects.back() ); 2787 OStringBuffer aObj( 64 ); 2788 aObj.append( aAttribObjects.back() ); 2789 aObj.append( " 0 obj\n" 2790 "<</O/List\n" ); 2791 aList.append( ">>\nendobj\n\n" ); 2792 writeBuffer( aObj.getStr(), aObj.getLength() ); 2793 writeBuffer( aList.getStr(), aList.getLength() ); 2794 } 2795 if( aTable.getLength() ) 2796 { 2797 aAttribObjects.push_back( createObject() ); 2798 updateObject( aAttribObjects.back() ); 2799 OStringBuffer aObj( 64 ); 2800 aObj.append( aAttribObjects.back() ); 2801 aObj.append( " 0 obj\n" 2802 "<</O/Table\n" ); 2803 aTable.append( ">>\nendobj\n\n" ); 2804 writeBuffer( aObj.getStr(), aObj.getLength() ); 2805 writeBuffer( aTable.getStr(), aTable.getLength() ); 2806 } 2807 2808 OStringBuffer aRet( 64 ); 2809 if( aAttribObjects.size() > 1 ) 2810 aRet.append( " [" ); 2811 for( std::vector< sal_Int32 >::const_iterator at_it = aAttribObjects.begin(); 2812 at_it != aAttribObjects.end(); ++at_it ) 2813 { 2814 aRet.append( " " ); 2815 aRet.append( *at_it ); 2816 aRet.append( " 0 R" ); 2817 } 2818 if( aAttribObjects.size() > 1 ) 2819 aRet.append( " ]" ); 2820 return aRet.makeStringAndClear(); 2821 } 2822 2823 sal_Int32 PDFWriterImpl::emitStructure( PDFStructureElement& rEle ) 2824 { 2825 if( 2826 // do not emit NonStruct and its children 2827 rEle.m_eType == PDFWriter::NonStructElement && 2828 rEle.m_nOwnElement != rEle.m_nParentElement // but of course emit the struct tree root 2829 ) 2830 return 0; 2831 2832 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it ) 2833 { 2834 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) ) 2835 { 2836 PDFStructureElement& rChild = m_aStructure[ *it ]; 2837 if( rChild.m_eType != PDFWriter::NonStructElement ) 2838 { 2839 if( rChild.m_nParentElement == rEle.m_nOwnElement ) 2840 emitStructure( rChild ); 2841 else 2842 { 2843 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure element" ); 2844 #if OSL_DEBUG_LEVEL > 1 2845 fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it ); 2846 #endif 2847 } 2848 } 2849 } 2850 else 2851 { 2852 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" ); 2853 #if OSL_DEBUG_LEVEL > 1 2854 fprintf( stderr, "PDFWriterImpl::emitStructure: invalid child structure id %" SAL_PRIdINT32 "\n", *it ); 2855 #endif 2856 } 2857 } 2858 2859 OStringBuffer aLine( 512 ); 2860 aLine.append( rEle.m_nObject ); 2861 aLine.append( " 0 obj\n" 2862 "<</Type" ); 2863 sal_Int32 nParentTree = -1; 2864 if( rEle.m_nOwnElement == rEle.m_nParentElement ) 2865 { 2866 nParentTree = createObject(); 2867 CHECK_RETURN( nParentTree ); 2868 aLine.append( "/StructTreeRoot\n" ); 2869 aLine.append( "/ParentTree " ); 2870 aLine.append( nParentTree ); 2871 aLine.append( " 0 R\n" ); 2872 if( ! m_aRoleMap.empty() ) 2873 { 2874 aLine.append( "/RoleMap<<" ); 2875 for( std::hash_map<OString,OString,OStringHash>::const_iterator 2876 it = m_aRoleMap.begin(); it != m_aRoleMap.end(); ++it ) 2877 { 2878 aLine.append( '/' ); 2879 aLine.append(it->first); 2880 aLine.append( '/' ); 2881 aLine.append( it->second ); 2882 aLine.append( '\n' ); 2883 } 2884 aLine.append( ">>\n" ); 2885 } 2886 } 2887 else 2888 { 2889 aLine.append( "/StructElem\n" 2890 "/S/" ); 2891 if( rEle.m_aAlias.getLength() > 0 ) 2892 aLine.append( rEle.m_aAlias ); 2893 else 2894 aLine.append( getStructureTag( rEle.m_eType ) ); 2895 aLine.append( "\n" 2896 "/P " ); 2897 aLine.append( m_aStructure[ rEle.m_nParentElement ].m_nObject ); 2898 aLine.append( " 0 R\n" 2899 "/Pg " ); 2900 aLine.append( rEle.m_nFirstPageObject ); 2901 aLine.append( " 0 R\n" ); 2902 if( rEle.m_aActualText.getLength() ) 2903 { 2904 aLine.append( "/ActualText" ); 2905 appendUnicodeTextStringEncrypt( rEle.m_aActualText, rEle.m_nObject, aLine ); 2906 aLine.append( "\n" ); 2907 } 2908 if( rEle.m_aAltText.getLength() ) 2909 { 2910 aLine.append( "/Alt" ); 2911 appendUnicodeTextStringEncrypt( rEle.m_aAltText, rEle.m_nObject, aLine ); 2912 aLine.append( "\n" ); 2913 } 2914 } 2915 if( ! rEle.m_aBBox.IsEmpty() || rEle.m_aAttributes.size() ) 2916 { 2917 OString aAttribs = emitStructureAttributes( rEle ); 2918 if( aAttribs.getLength() ) 2919 { 2920 aLine.append( "/A" ); 2921 aLine.append( aAttribs ); 2922 aLine.append( "\n" ); 2923 } 2924 } 2925 if( rEle.m_aLocale.Language.getLength() > 0 ) 2926 { 2927 OUStringBuffer aLocBuf( 16 ); 2928 aLocBuf.append( rEle.m_aLocale.Language.toAsciiLowerCase() ); 2929 if( rEle.m_aLocale.Country.getLength() > 0 ) 2930 { 2931 aLocBuf.append( sal_Unicode('-') ); 2932 aLocBuf.append( rEle.m_aLocale.Country ); 2933 } 2934 aLine.append( "/Lang" ); 2935 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), rEle.m_nObject, aLine ); 2936 aLine.append( "\n" ); 2937 } 2938 if( ! rEle.m_aKids.empty() ) 2939 { 2940 unsigned int i = 0; 2941 aLine.append( "/K[" ); 2942 for( std::list< PDFStructureElementKid >::const_iterator it = 2943 rEle.m_aKids.begin(); it != rEle.m_aKids.end(); ++it, i++ ) 2944 { 2945 if( it->nMCID == -1 ) 2946 { 2947 aLine.append( it->nObject ); 2948 aLine.append( " 0 R" ); 2949 aLine.append( ( (i & 15) == 15 ) ? "\n" : " " ); 2950 } 2951 else 2952 { 2953 if( it->nObject == rEle.m_nFirstPageObject ) 2954 { 2955 aLine.append( it->nMCID ); 2956 aLine.append( " " ); 2957 } 2958 else 2959 { 2960 aLine.append( "<</Type/MCR/Pg " ); 2961 aLine.append( it->nObject ); 2962 aLine.append( " 0 R /MCID " ); 2963 aLine.append( it->nMCID ); 2964 aLine.append( ">>\n" ); 2965 } 2966 } 2967 } 2968 aLine.append( "]\n" ); 2969 } 2970 aLine.append( ">>\nendobj\n\n" ); 2971 2972 CHECK_RETURN( updateObject( rEle.m_nObject ) ); 2973 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 2974 2975 CHECK_RETURN( emitStructParentTree( nParentTree ) ); 2976 2977 return rEle.m_nObject; 2978 } 2979 2980 bool PDFWriterImpl::emitGradients() 2981 { 2982 for( std::list<GradientEmit>::iterator it = m_aGradients.begin(); 2983 it != m_aGradients.end(); ++it ) 2984 { 2985 CHECK_RETURN( writeGradientFunction( *it ) ); 2986 } 2987 return true; 2988 } 2989 2990 bool PDFWriterImpl::emitTilings() 2991 { 2992 OStringBuffer aTilingObj( 1024 ); 2993 2994 for( std::vector<TilingEmit>::iterator it = m_aTilings.begin(); it != m_aTilings.end(); ++it ) 2995 { 2996 DBG_ASSERT( it->m_pTilingStream, "tiling without stream" ); 2997 if( ! it->m_pTilingStream ) 2998 continue; 2999 3000 aTilingObj.setLength( 0 ); 3001 3002 #if OSL_DEBUG_LEVEL > 1 3003 emitComment( "PDFWriterImpl::emitTilings" ); 3004 #endif 3005 3006 sal_Int32 nX = (sal_Int32)it->m_aRectangle.Left(); 3007 sal_Int32 nY = (sal_Int32)it->m_aRectangle.Top(); 3008 sal_Int32 nW = (sal_Int32)it->m_aRectangle.GetWidth(); 3009 sal_Int32 nH = (sal_Int32)it->m_aRectangle.GetHeight(); 3010 if( it->m_aCellSize.Width() == 0 ) 3011 it->m_aCellSize.Width() = nW; 3012 if( it->m_aCellSize.Height() == 0 ) 3013 it->m_aCellSize.Height() = nH; 3014 3015 bool bDeflate = compressStream( it->m_pTilingStream ); 3016 it->m_pTilingStream->Seek( STREAM_SEEK_TO_END ); 3017 sal_Size nTilingStreamSize = it->m_pTilingStream->Tell(); 3018 it->m_pTilingStream->Seek( STREAM_SEEK_TO_BEGIN ); 3019 3020 // write pattern object 3021 aTilingObj.append( it->m_nObject ); 3022 aTilingObj.append( " 0 obj\n" ); 3023 aTilingObj.append( "<</Type/Pattern/PatternType 1\n" 3024 "/PaintType 1\n" 3025 "/TilingType 2\n" 3026 "/BBox[" ); 3027 appendFixedInt( nX, aTilingObj ); 3028 aTilingObj.append( ' ' ); 3029 appendFixedInt( nY, aTilingObj ); 3030 aTilingObj.append( ' ' ); 3031 appendFixedInt( nX+nW, aTilingObj ); 3032 aTilingObj.append( ' ' ); 3033 appendFixedInt( nY+nH, aTilingObj ); 3034 aTilingObj.append( "]\n" 3035 "/XStep " ); 3036 appendFixedInt( it->m_aCellSize.Width(), aTilingObj ); 3037 aTilingObj.append( "\n" 3038 "/YStep " ); 3039 appendFixedInt( it->m_aCellSize.Height(), aTilingObj ); 3040 aTilingObj.append( "\n" ); 3041 if( it->m_aTransform.matrix[0] != 1.0 || 3042 it->m_aTransform.matrix[1] != 0.0 || 3043 it->m_aTransform.matrix[3] != 0.0 || 3044 it->m_aTransform.matrix[4] != 1.0 || 3045 it->m_aTransform.matrix[2] != 0.0 || 3046 it->m_aTransform.matrix[5] != 0.0 ) 3047 { 3048 aTilingObj.append( "/Matrix [" ); 3049 // TODO: scaling, mirroring on y, etc 3050 appendDouble( it->m_aTransform.matrix[0], aTilingObj ); 3051 aTilingObj.append( ' ' ); 3052 appendDouble( it->m_aTransform.matrix[1], aTilingObj ); 3053 aTilingObj.append( ' ' ); 3054 appendDouble( it->m_aTransform.matrix[3], aTilingObj ); 3055 aTilingObj.append( ' ' ); 3056 appendDouble( it->m_aTransform.matrix[4], aTilingObj ); 3057 aTilingObj.append( ' ' ); 3058 appendDouble( it->m_aTransform.matrix[2], aTilingObj ); 3059 aTilingObj.append( ' ' ); 3060 appendDouble( it->m_aTransform.matrix[5], aTilingObj ); 3061 aTilingObj.append( "]\n" ); 3062 } 3063 aTilingObj.append( "/Resources" ); 3064 it->m_aResources.append( aTilingObj, getFontDictObject() ); 3065 if( bDeflate ) 3066 aTilingObj.append( "/Filter/FlateDecode" ); 3067 aTilingObj.append( "/Length " ); 3068 aTilingObj.append( (sal_Int32)nTilingStreamSize ); 3069 aTilingObj.append( ">>\nstream\n" ); 3070 CHECK_RETURN( updateObject( it->m_nObject ) ); 3071 CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ); 3072 checkAndEnableStreamEncryption( it->m_nObject ); 3073 nTilingStreamSize = writeBuffer( it->m_pTilingStream->GetData(), nTilingStreamSize ); 3074 delete it->m_pTilingStream; 3075 it->m_pTilingStream = NULL; 3076 if( nTilingStreamSize == 0 ) 3077 return false; 3078 disableStreamEncryption(); 3079 aTilingObj.setLength( 0 ); 3080 aTilingObj.append( "\nendstream\nendobj\n\n" ); 3081 CHECK_RETURN( writeBuffer( aTilingObj.getStr(), aTilingObj.getLength() ) ); 3082 } 3083 return true; 3084 } 3085 3086 sal_Int32 PDFWriterImpl::emitBuiltinFont( const ImplFontData* pFont, sal_Int32 nFontObject ) 3087 { 3088 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pFont ); 3089 if( !pFD ) 3090 return 0; 3091 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); 3092 3093 OStringBuffer aLine( 1024 ); 3094 3095 if( nFontObject <= 0 ) 3096 nFontObject = createObject(); 3097 CHECK_RETURN( updateObject( nFontObject ) ); 3098 aLine.append( nFontObject ); 3099 aLine.append( " 0 obj\n" 3100 "<</Type/Font/Subtype/Type1/BaseFont/" ); 3101 appendName( pBuiltinFont->m_pPSName, aLine ); 3102 aLine.append( "\n" ); 3103 if( pBuiltinFont->m_eCharSet == RTL_TEXTENCODING_MS_1252 ) 3104 aLine.append( "/Encoding/WinAnsiEncoding\n" ); 3105 aLine.append( ">>\nendobj\n\n" ); 3106 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3107 return nFontObject; 3108 } 3109 3110 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitSystemFont( const ImplFontData* pFont, EmbedFont& rEmbed ) 3111 { 3112 std::map< sal_Int32, sal_Int32 > aRet; 3113 if( isBuiltinFont( pFont ) ) 3114 { 3115 aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont ); 3116 return aRet; 3117 } 3118 3119 sal_Int32 nFontObject = 0; 3120 sal_Int32 nFontDescriptor = 0; 3121 rtl::OString aSubType( "/Type1" ); 3122 FontSubsetInfo aInfo; 3123 // fill in dummy values 3124 aInfo.m_nAscent = 1000; 3125 aInfo.m_nDescent = 200; 3126 aInfo.m_nCapHeight = 1000; 3127 aInfo.m_aFontBBox = Rectangle( Point( -200, -200 ), Size( 1700, 1700 ) ); 3128 aInfo.m_aPSName = pFont->maName; 3129 sal_Int32 pWidths[256]; 3130 rtl_zeroMemory( pWidths, sizeof(pWidths) ); 3131 if( pFont->IsEmbeddable() ) 3132 { 3133 const unsigned char* pFontData = NULL; 3134 long nFontLen = 0; 3135 sal_Ucs nEncodedCodes[256]; 3136 sal_Int32 pEncWidths[256]; 3137 if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pEncWidths, aInfo, &nFontLen )) != NULL ) 3138 { 3139 m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen ); 3140 for( int i = 0; i < 256; i++ ) 3141 { 3142 if( nEncodedCodes[i] >= 32 && nEncodedCodes[i] < 256 ) 3143 { 3144 pWidths[i] = pEncWidths[ i ]; 3145 } 3146 } 3147 } 3148 } 3149 else if( pFont->mbSubsettable ) 3150 { 3151 aSubType = rtl::OString( "/TrueType" ); 3152 Int32Vector aGlyphWidths; 3153 Ucs2UIntMap aUnicodeMap; 3154 m_pReferenceDevice->mpGraphics->GetGlyphWidths( pFont, false, aGlyphWidths, aUnicodeMap ); 3155 3156 OUString aTmpName; 3157 osl_createTempFile( NULL, NULL, &aTmpName.pData ); 3158 sal_Int32 pGlyphIDs[ 256 ]; 3159 sal_uInt8 pEncoding[ 256 ]; 3160 sal_Ucs pUnicodes[ 256 ]; 3161 sal_Int32 pDuWidths[ 256 ]; 3162 3163 memset( pGlyphIDs, 0, sizeof( pGlyphIDs ) ); 3164 memset( pEncoding, 0, sizeof( pEncoding ) ); 3165 memset( pUnicodes, 0, sizeof( pUnicodes ) ); 3166 memset( pDuWidths, 0, sizeof( pDuWidths ) ); 3167 3168 for( sal_Ucs c = 32; c < 256; c++ ) 3169 { 3170 pUnicodes[c] = c; 3171 pEncoding[c] = c; 3172 pGlyphIDs[c] = 0; 3173 if( aUnicodeMap.find( c ) != aUnicodeMap.end() ) 3174 pWidths[ c ] = aGlyphWidths[ aUnicodeMap[ c ] ]; 3175 } 3176 3177 m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, pFont, pGlyphIDs, pEncoding, pDuWidths, 256, aInfo ); 3178 osl_removeFile( aTmpName.pData ); 3179 } 3180 else 3181 { 3182 DBG_ERROR( "system font neither embeddable nor subsettable" ); 3183 } 3184 3185 // write font descriptor 3186 nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 ); 3187 if( nFontDescriptor ) 3188 { 3189 // write font object 3190 sal_Int32 nObject = createObject(); 3191 if( updateObject( nObject ) ) 3192 { 3193 OStringBuffer aLine( 1024 ); 3194 aLine.append( nObject ); 3195 aLine.append( " 0 obj\n" 3196 "<</Type/Font/Subtype" ); 3197 aLine.append( aSubType ); 3198 aLine.append( "/BaseFont/" ); 3199 appendName( aInfo.m_aPSName, aLine ); 3200 aLine.append( "\n" ); 3201 if( !pFont->mbSymbolFlag ) 3202 aLine.append( "/Encoding/WinAnsiEncoding\n" ); 3203 aLine.append( "/FirstChar 32 /LastChar 255\n" 3204 "/Widths[" ); 3205 for( int i = 32; i < 256; i++ ) 3206 { 3207 aLine.append( pWidths[i] ); 3208 aLine.append( ((i&15) == 15) ? "\n" : " " ); 3209 } 3210 aLine.append( "]\n" 3211 "/FontDescriptor " ); 3212 aLine.append( nFontDescriptor ); 3213 aLine.append( " 0 R>>\n" 3214 "endobj\n\n" ); 3215 writeBuffer( aLine.getStr(), aLine.getLength() ); 3216 3217 nFontObject = nObject; 3218 aRet[ rEmbed.m_nNormalFontID ] = nObject; 3219 } 3220 } 3221 3222 return aRet; 3223 } 3224 3225 typedef int ThreeInts[3]; 3226 static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int nByteLen, 3227 ThreeInts& rSegmentLengths ) 3228 { 3229 if( !pFontBytes || (nByteLen < 0) ) 3230 return false; 3231 const unsigned char* pPtr = pFontBytes; 3232 const unsigned char* pEnd = pFontBytes + nByteLen; 3233 3234 for( int i = 0; i < 3; ++i) { 3235 // read segment1 header 3236 if( pPtr+6 >= pEnd ) 3237 return false; 3238 if( (pPtr[0] != 0x80) || (pPtr[1] >= 0x03) ) 3239 return false; 3240 const int nLen = (pPtr[5]<<24) + (pPtr[4]<<16) + (pPtr[3]<<8) + pPtr[2]; 3241 if( nLen <= 0) 3242 return false; 3243 rSegmentLengths[i] = nLen; 3244 pPtr += nLen + 6; 3245 } 3246 3247 // read segment-end header 3248 if( pPtr+2 >= pEnd ) 3249 return false; 3250 if( (pPtr[0] != 0x80) || (pPtr[1] != 0x03) ) 3251 return false; 3252 3253 return true; 3254 } 3255 3256 struct FontException : public std::exception 3257 { 3258 }; 3259 3260 // TODO: always subset instead of embedding the full font => this method becomes obsolete then 3261 std::map< sal_Int32, sal_Int32 > PDFWriterImpl::emitEmbeddedFont( const ImplFontData* pFont, EmbedFont& rEmbed ) 3262 { 3263 std::map< sal_Int32, sal_Int32 > aRet; 3264 if( isBuiltinFont( pFont ) ) 3265 { 3266 aRet[ rEmbed.m_nNormalFontID ] = emitBuiltinFont( pFont ); 3267 return aRet; 3268 } 3269 3270 sal_Int32 nFontObject = 0; 3271 sal_Int32 nStreamObject = 0; 3272 sal_Int32 nFontDescriptor = 0; 3273 3274 // prepare font encoding 3275 const Ucs2SIntMap* pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pFont, NULL ); 3276 sal_Int32 nToUnicodeStream = 0; 3277 sal_uInt8 nEncoding[256]; 3278 sal_Ucs nEncodedCodes[256]; 3279 std::vector<sal_Ucs> aUnicodes; 3280 aUnicodes.reserve( 256 ); 3281 sal_Int32 pUnicodesPerGlyph[256]; 3282 sal_Int32 pEncToUnicodeIndex[256]; 3283 if( pEncoding ) 3284 { 3285 rtl_zeroMemory( nEncoding, sizeof(nEncoding) ); 3286 rtl_zeroMemory( nEncodedCodes, sizeof(nEncodedCodes) ); 3287 rtl_zeroMemory( pUnicodesPerGlyph, sizeof(pUnicodesPerGlyph) ); 3288 rtl_zeroMemory( pEncToUnicodeIndex, sizeof(pEncToUnicodeIndex) ); 3289 for( Ucs2SIntMap::const_iterator it = pEncoding->begin(); it != pEncoding->end(); ++it ) 3290 { 3291 if( it->second != -1 ) 3292 { 3293 sal_Int32 nCode = (sal_Int32)(it->second & 0x000000ff); 3294 nEncoding[ nCode ] = static_cast<sal_uInt8>( nCode ); 3295 nEncodedCodes[ nCode ] = it->first; 3296 pEncToUnicodeIndex[ nCode ] = static_cast<sal_Int32>(aUnicodes.size()); 3297 aUnicodes.push_back( it->first ); 3298 pUnicodesPerGlyph[ nCode ] = 1; 3299 } 3300 } 3301 } 3302 3303 FontSubsetInfo aInfo; 3304 sal_Int32 pWidths[256]; 3305 const unsigned char* pFontData = NULL; 3306 long nFontLen = 0; 3307 sal_Int32 nLength1, nLength2; 3308 try 3309 { 3310 if( (pFontData = (const unsigned char*)m_pReferenceDevice->mpGraphics->GetEmbedFontData( pFont, nEncodedCodes, pWidths, aInfo, &nFontLen )) != NULL ) 3311 { 3312 if( (aInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) == 0 ) 3313 throw FontException(); 3314 // see whether it is pfb or pfa; if it is a pfb, fill ranges 3315 // of 6 bytes that are not part of the font program 3316 std::list< int > aSections; 3317 std::list< int >::const_iterator it; 3318 int nIndex = 0; 3319 while( pFontData[nIndex] == 0x80 && nIndex < nFontLen-1 ) 3320 { 3321 aSections.push_back( nIndex ); 3322 if( pFontData[nIndex+1] == 0x03 ) 3323 break; 3324 sal_Int32 nBytes = 3325 ((sal_Int32)pFontData[nIndex+2]) | 3326 ((sal_Int32)pFontData[nIndex+3]) << 8 | 3327 ((sal_Int32)pFontData[nIndex+4]) << 16 | 3328 ((sal_Int32)pFontData[nIndex+5]) << 24; 3329 nIndex += nBytes+6; 3330 } 3331 3332 // search for eexec 3333 // TODO: use getPfbSegmentLengths() if possible to skip the search thingies below 3334 nIndex = 0; 3335 int nEndAsciiIndex; 3336 int nBeginBinaryIndex; 3337 int nEndBinaryIndex; 3338 do 3339 { 3340 while( nIndex < nFontLen-4 && 3341 ( pFontData[nIndex] != 'e' || 3342 pFontData[nIndex+1] != 'e' || 3343 pFontData[nIndex+2] != 'x' || 3344 pFontData[nIndex+3] != 'e' || 3345 pFontData[nIndex+4] != 'c' 3346 ) 3347 ) 3348 nIndex++; 3349 // check whether we are in a excluded section 3350 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) 3351 ; 3352 } while( it != aSections.end() && nIndex < nFontLen-4 ); 3353 // this should end the ascii part 3354 if( nIndex > nFontLen-5 ) 3355 throw FontException(); 3356 3357 nEndAsciiIndex = nIndex+4; 3358 // now count backwards until we can account for 512 '0' 3359 // which is the endmarker of the (hopefully) binary data 3360 // do not count the pfb header sections 3361 int nFound = 0; 3362 nIndex = nFontLen-1; 3363 while( nIndex > 0 && nFound < 512 ) 3364 { 3365 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) 3366 ; 3367 if( it == aSections.end() ) 3368 { 3369 // inside the 512 '0' block there may only be whitespace 3370 // according to T1 spec; probably it would be to simple 3371 // if all fonts complied 3372 if( pFontData[nIndex] == '0' ) 3373 nFound++; 3374 else if( nFound > 0 && 3375 pFontData[nIndex] != '\r' && 3376 pFontData[nIndex] != '\t' && 3377 pFontData[nIndex] != '\n' && 3378 pFontData[nIndex] != ' ' ) 3379 break; 3380 } 3381 nIndex--; 3382 } 3383 3384 if( nIndex < 1 || nIndex <= nEndAsciiIndex ) 3385 throw FontException(); 3386 3387 // nLength3 is the rest of the file - excluding any section headers 3388 // nIndex now points to the first of the 512 '0' characters marking the 3389 // fixed content portion 3390 sal_Int32 nLength3 = nFontLen - nIndex; 3391 for( it = aSections.begin(); it != aSections.end(); ++it ) 3392 { 3393 // special case: nIndex inside a section marker 3394 if( nIndex >= (*it) && (*it)+6 > nIndex ) 3395 nLength3 -= (*it)+6 - nIndex; 3396 else if( *it >= nIndex ) 3397 { 3398 if( *it < nFontLen - 6 ) 3399 nLength3 -= 6; 3400 else // the last section 0x8003 is only 2 bytes after all 3401 nLength3 -= (nFontLen - *it); 3402 } 3403 } 3404 3405 // there may be whitespace to ignore before the 512 '0' 3406 while( pFontData[nIndex] == '\r' || pFontData[nIndex] == '\n' ) 3407 { 3408 nIndex--; 3409 for( it = aSections.begin(); it != aSections.end() && (nIndex < *it || nIndex > ((*it) + 5) ); ++it ) 3410 ; 3411 if( it != aSections.end() ) 3412 { 3413 nIndex = (*it)-1; 3414 break; // this is surely a binary boundary, in ascii case it wouldn't matter 3415 } 3416 } 3417 nEndBinaryIndex = nIndex; 3418 3419 // search for beginning of binary section 3420 nBeginBinaryIndex = nEndAsciiIndex; 3421 do 3422 { 3423 nBeginBinaryIndex++; 3424 for( it = aSections.begin(); it != aSections.end() && (nBeginBinaryIndex < *it || nBeginBinaryIndex > ((*it) + 5) ); ++it ) 3425 ; 3426 } while( nBeginBinaryIndex < nEndBinaryIndex && 3427 ( pFontData[nBeginBinaryIndex] == '\r' || 3428 pFontData[nBeginBinaryIndex] == '\n' || 3429 it != aSections.end() ) ); 3430 3431 // it seems to be vital to copy the exact whitespace between binary data 3432 // and eexec, else a invalid font results. so make nEndAsciiIndex 3433 // always immediate in front of nBeginBinaryIndex 3434 nEndAsciiIndex = nBeginBinaryIndex-1; 3435 for( it = aSections.begin(); it != aSections.end() && (nEndAsciiIndex < *it || nEndAsciiIndex > ((*it)+5)); ++it ) 3436 ; 3437 if( it != aSections.end() ) 3438 nEndAsciiIndex = (*it)-1; 3439 3440 nLength1 = nEndAsciiIndex+1; // including the last character 3441 for( it = aSections.begin(); it != aSections.end() && *it < nEndAsciiIndex; ++it ) 3442 nLength1 -= 6; // decrease by pfb section size 3443 3444 // if the first four bytes are all ascii hex characters, then binary data 3445 // has to be converted to real binary data 3446 for( nIndex = 0; nIndex < 4 && 3447 ( ( pFontData[ nBeginBinaryIndex+nIndex ] >= '0' && pFontData[ nBeginBinaryIndex+nIndex ] <= '9' ) || 3448 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'a' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'f' ) || 3449 ( pFontData[ nBeginBinaryIndex+nIndex ] >= 'A' && pFontData[ nBeginBinaryIndex+nIndex ] <= 'F' ) 3450 ); ++nIndex ) 3451 ; 3452 bool bConvertHexData = true; 3453 if( nIndex < 4 ) 3454 { 3455 bConvertHexData = false; 3456 nLength2 = nEndBinaryIndex - nBeginBinaryIndex + 1; // include the last byte 3457 for( it = aSections.begin(); it != aSections.end(); ++it ) 3458 if( *it > nBeginBinaryIndex && *it < nEndBinaryIndex ) 3459 nLength2 -= 6; 3460 } 3461 else 3462 { 3463 // count the hex ascii characters to get nLength2 3464 nLength2 = 0; 3465 int nNextSectionIndex = 0; 3466 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) 3467 ; 3468 if( it != aSections.end() ) 3469 nNextSectionIndex = *it; 3470 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) 3471 { 3472 if( nIndex == nNextSectionIndex ) 3473 { 3474 nIndex += 6; 3475 ++it; 3476 nNextSectionIndex = (it == aSections.end() ? 0 : *it ); 3477 } 3478 if( ( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) || 3479 ( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) || 3480 ( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) ) 3481 nLength2++; 3482 } 3483 DBG_ASSERT( !(nLength2 & 1), "uneven number of hex chars in binary pfa section" ); 3484 nLength2 /= 2; 3485 } 3486 3487 // now we can actually write the font stream ! 3488 #if OSL_DEBUG_LEVEL > 1 3489 emitComment( " PDFWriterImpl::emitEmbeddedFont" ); 3490 #endif 3491 OStringBuffer aLine( 512 ); 3492 nStreamObject = createObject(); 3493 if( !updateObject(nStreamObject)) 3494 throw FontException(); 3495 sal_Int32 nStreamLengthObject = createObject(); 3496 aLine.append( nStreamObject ); 3497 aLine.append( " 0 obj\n" 3498 "<</Length " ); 3499 aLine.append( nStreamLengthObject ); 3500 aLine.append( " 0 R" 3501 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3502 "/Filter/FlateDecode" 3503 #endif 3504 "/Length1 " ); 3505 aLine.append( nLength1 ); 3506 aLine.append( " /Length2 " ); 3507 aLine.append( nLength2 ); 3508 aLine.append( " /Length3 "); 3509 aLine.append( nLength3 ); 3510 aLine.append( ">>\n" 3511 "stream\n" ); 3512 if( !writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3513 throw FontException(); 3514 3515 sal_uInt64 nBeginStreamPos = 0; 3516 osl_getFilePos( m_aFile, &nBeginStreamPos ); 3517 3518 beginCompression(); 3519 checkAndEnableStreamEncryption( nStreamObject ); 3520 3521 // write ascii section 3522 if( aSections.begin() == aSections.end() ) 3523 { 3524 if( ! writeBuffer( pFontData, nEndAsciiIndex+1 ) ) 3525 throw FontException(); 3526 } 3527 else 3528 { 3529 // first section always starts at 0 3530 it = aSections.begin(); 3531 nIndex = (*it)+6; 3532 ++it; 3533 while( *it < nEndAsciiIndex ) 3534 { 3535 if( ! writeBuffer( pFontData+nIndex, (*it)-nIndex ) ) 3536 throw FontException(); 3537 nIndex = (*it)+6; 3538 ++it; 3539 } 3540 // write partial last section 3541 if( ! writeBuffer( pFontData+nIndex, nEndAsciiIndex-nIndex+1 ) ) 3542 throw FontException(); 3543 } 3544 3545 // write binary section 3546 if( ! bConvertHexData ) 3547 { 3548 if( aSections.begin() == aSections.end() ) 3549 { 3550 if( ! writeBuffer( pFontData+nBeginBinaryIndex, nFontLen-nBeginBinaryIndex ) ) 3551 throw FontException(); 3552 } 3553 else 3554 { 3555 for( it = aSections.begin(); *it < nBeginBinaryIndex; ++it ) 3556 ; 3557 // write first partial section 3558 if( ! writeBuffer( pFontData+nBeginBinaryIndex, (*it) - nBeginBinaryIndex ) ) 3559 throw FontException(); 3560 // write following sections 3561 while( it != aSections.end() ) 3562 { 3563 nIndex = (*it)+6; 3564 ++it; 3565 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes 3566 { 3567 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; 3568 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) 3569 throw FontException(); 3570 } 3571 } 3572 } 3573 } 3574 else 3575 { 3576 boost::shared_array<unsigned char> pWriteBuffer( new unsigned char[ nLength2 ] ); 3577 rtl_zeroMemory( pWriteBuffer.get(), nLength2 ); 3578 int nWriteIndex = 0; 3579 3580 int nNextSectionIndex = 0; 3581 for( it = aSections.begin(); it != aSections.end() && *it < nBeginBinaryIndex; ++it ) 3582 ; 3583 if( it != aSections.end() ) 3584 nNextSectionIndex = *it; 3585 for( nIndex = nBeginBinaryIndex; nIndex <= nEndBinaryIndex; nIndex++ ) 3586 { 3587 if( nIndex == nNextSectionIndex ) 3588 { 3589 nIndex += 6; 3590 ++it; 3591 nNextSectionIndex = (it == aSections.end() ? nFontLen : *it ); 3592 } 3593 unsigned char cNibble = 0x80; 3594 if( pFontData[ nIndex ] >= '0' && pFontData[ nIndex ] <= '9' ) 3595 cNibble = pFontData[nIndex] - '0'; 3596 else if( pFontData[ nIndex ] >= 'a' && pFontData[ nIndex ] <= 'f' ) 3597 cNibble = pFontData[nIndex] - 'a' + 10; 3598 else if( pFontData[ nIndex ] >= 'A' && pFontData[ nIndex ] <= 'F' ) 3599 cNibble = pFontData[nIndex] - 'A' + 10; 3600 if( cNibble != 0x80 ) 3601 { 3602 if( !(nWriteIndex & 1 ) ) 3603 cNibble <<= 4; 3604 pWriteBuffer.get()[ nWriteIndex/2 ] |= cNibble; 3605 nWriteIndex++; 3606 } 3607 } 3608 if( ! writeBuffer( pWriteBuffer.get(), nLength2 ) ) 3609 throw FontException(); 3610 if( aSections.empty() ) 3611 { 3612 if( ! writeBuffer( pFontData+nIndex, nFontLen-nIndex ) ) 3613 throw FontException(); 3614 } 3615 else 3616 { 3617 // write rest of this section 3618 if( nIndex < nNextSectionIndex ) 3619 { 3620 if( ! writeBuffer( pFontData+nIndex, nNextSectionIndex - nIndex ) ) 3621 throw FontException(); 3622 } 3623 // write following sections 3624 while( it != aSections.end() ) 3625 { 3626 nIndex = (*it)+6; 3627 ++it; 3628 if( nIndex < nFontLen ) // last section marker is usually the EOF which has only 2 bytes 3629 { 3630 sal_Int32 nSectionLen = (it == aSections.end()) ? nFontLen - nIndex : (*it) - nIndex; 3631 if( ! writeBuffer( pFontData+nIndex, nSectionLen ) ) 3632 throw FontException(); 3633 } 3634 } 3635 } 3636 } 3637 endCompression(); 3638 disableStreamEncryption(); 3639 3640 3641 sal_uInt64 nEndStreamPos = 0; 3642 osl_getFilePos( m_aFile, &nEndStreamPos ); 3643 3644 // and finally close the stream 3645 aLine.setLength( 0 ); 3646 aLine.append( "\nendstream\nendobj\n\n" ); 3647 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3648 throw FontException(); 3649 3650 // write stream length object 3651 aLine.setLength( 0 ); 3652 if( ! updateObject( nStreamLengthObject ) ) 3653 throw FontException(); 3654 aLine.append( nStreamLengthObject ); 3655 aLine.append( " 0 obj\n" ); 3656 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos ) ); 3657 aLine.append( "\nendobj\n\n" ); 3658 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3659 throw FontException(); 3660 } 3661 else 3662 { 3663 rtl::OStringBuffer aErrorComment( 256 ); 3664 aErrorComment.append( "GetEmbedFontData failed for font \"" ); 3665 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); 3666 aErrorComment.append( '\"' ); 3667 if( pFont->GetSlant() == ITALIC_NORMAL ) 3668 aErrorComment.append( " italic" ); 3669 else if( pFont->GetSlant() == ITALIC_OBLIQUE ) 3670 aErrorComment.append( " oblique" ); 3671 aErrorComment.append( " weight=" ); 3672 aErrorComment.append( sal_Int32(pFont->GetWeight()) ); 3673 emitComment( aErrorComment.getStr() ); 3674 } 3675 3676 if( nStreamObject ) 3677 // write font descriptor 3678 nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, nStreamObject ); 3679 3680 if( nFontDescriptor ) 3681 { 3682 if( pEncoding ) 3683 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, sizeof(nEncoding)/sizeof(nEncoding[0]) ); 3684 3685 // write font object 3686 sal_Int32 nObject = createObject(); 3687 if( ! updateObject( nObject ) ) 3688 throw FontException(); 3689 3690 OStringBuffer aLine( 1024 ); 3691 aLine.append( nObject ); 3692 aLine.append( " 0 obj\n" 3693 "<</Type/Font/Subtype/Type1/BaseFont/" ); 3694 appendName( aInfo.m_aPSName, aLine ); 3695 aLine.append( "\n" ); 3696 if( !pFont->mbSymbolFlag && pEncoding == 0 ) 3697 aLine.append( "/Encoding/WinAnsiEncoding\n" ); 3698 if( nToUnicodeStream ) 3699 { 3700 aLine.append( "/ToUnicode " ); 3701 aLine.append( nToUnicodeStream ); 3702 aLine.append( " 0 R\n" ); 3703 } 3704 aLine.append( "/FirstChar 0 /LastChar 255\n" 3705 "/Widths[" ); 3706 for( int i = 0; i < 256; i++ ) 3707 { 3708 aLine.append( pWidths[i] ); 3709 aLine.append( ((i&15) == 15) ? "\n" : " " ); 3710 } 3711 aLine.append( "]\n" 3712 "/FontDescriptor " ); 3713 aLine.append( nFontDescriptor ); 3714 aLine.append( " 0 R>>\n" 3715 "endobj\n\n" ); 3716 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3717 throw FontException(); 3718 3719 nFontObject = nObject; 3720 3721 aRet[ rEmbed.m_nNormalFontID ] = nObject; 3722 3723 // write additional encodings 3724 for( std::list< EmbedEncoding >::iterator enc_it = rEmbed.m_aExtendedEncodings.begin(); enc_it != rEmbed.m_aExtendedEncodings.end(); ++enc_it ) 3725 { 3726 sal_Int32 aEncWidths[ 256 ]; 3727 // emit encoding dict 3728 sal_Int32 nEncObject = createObject(); 3729 if( ! updateObject( nEncObject ) ) 3730 throw FontException(); 3731 3732 OutputDevice* pRef = getReferenceDevice(); 3733 pRef->Push( PUSH_FONT | PUSH_MAPMODE ); 3734 pRef->SetMapMode( MapMode( MAP_PIXEL ) ); 3735 Font aFont( pFont->GetFamilyName(), pFont->GetStyleName(), Size( 0, 1000 ) ); 3736 aFont.SetWeight( pFont->GetWeight() ); 3737 aFont.SetItalic( pFont->GetSlant() ); 3738 aFont.SetPitch( pFont->GetPitch() ); 3739 pRef->SetFont( aFont ); 3740 pRef->ImplNewFont(); 3741 3742 aLine.setLength( 0 ); 3743 aLine.append( nEncObject ); 3744 aLine.append( " 0 obj\n" 3745 "<</Type/Encoding/Differences[ 0\n" ); 3746 int nEncoded = 0; 3747 aUnicodes.clear(); 3748 for( std::vector< EmbedCode >::iterator str_it = enc_it->m_aEncVector.begin(); str_it != enc_it->m_aEncVector.end(); ++str_it ) 3749 { 3750 String aStr( str_it->m_aUnicode ); 3751 aEncWidths[nEncoded] = pRef->GetTextWidth( aStr ); 3752 nEncodedCodes[nEncoded] = str_it->m_aUnicode; 3753 nEncoding[nEncoded] = sal::static_int_cast<sal_uInt8>(nEncoded); 3754 pEncToUnicodeIndex[nEncoded] = static_cast<sal_Int32>(aUnicodes.size()); 3755 aUnicodes.push_back( nEncodedCodes[nEncoded] ); 3756 pUnicodesPerGlyph[nEncoded] = 1; 3757 3758 aLine.append( " /" ); 3759 aLine.append( str_it->m_aName ); 3760 if( !((++nEncoded) & 15) ) 3761 aLine.append( "\n" ); 3762 } 3763 aLine.append( "]>>\n" 3764 "endobj\n\n" ); 3765 3766 pRef->Pop(); 3767 3768 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3769 throw FontException(); 3770 3771 nToUnicodeStream = createToUnicodeCMap( nEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nEncoded ); 3772 3773 nObject = createObject(); 3774 if( ! updateObject( nObject ) ) 3775 throw FontException(); 3776 3777 aLine.setLength( 0 ); 3778 aLine.append( nObject ); 3779 aLine.append( " 0 obj\n" 3780 "<</Type/Font/Subtype/Type1/BaseFont/" ); 3781 appendName( aInfo.m_aPSName, aLine ); 3782 aLine.append( "\n" ); 3783 aLine.append( "/Encoding " ); 3784 aLine.append( nEncObject ); 3785 aLine.append( " 0 R\n" ); 3786 if( nToUnicodeStream ) 3787 { 3788 aLine.append( "/ToUnicode " ); 3789 aLine.append( nToUnicodeStream ); 3790 aLine.append( " 0 R\n" ); 3791 } 3792 aLine.append( "/FirstChar 0\n" 3793 "/LastChar " ); 3794 aLine.append( (sal_Int32)(nEncoded-1) ); 3795 aLine.append( "\n" 3796 "/Widths[" ); 3797 for( int i = 0; i < nEncoded; i++ ) 3798 { 3799 aLine.append( aEncWidths[i] ); 3800 aLine.append( ((i&15) == 15) ? "\n" : " " ); 3801 } 3802 aLine.append( " ]\n" 3803 "/FontDescriptor " ); 3804 aLine.append( nFontDescriptor ); 3805 aLine.append( " 0 R>>\n" 3806 "endobj\n\n" ); 3807 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 3808 throw FontException(); 3809 3810 aRet[ enc_it->m_nFontID ] = nObject; 3811 } 3812 } 3813 } 3814 catch( FontException& ) 3815 { 3816 // these do nothing in case there was no compression or encryption ongoing 3817 endCompression(); 3818 disableStreamEncryption(); 3819 } 3820 3821 if( pFontData ) 3822 m_pReferenceDevice->mpGraphics->FreeEmbedFontData( pFontData, nFontLen ); 3823 3824 return aRet; 3825 } 3826 3827 static void appendSubsetName( int nSubsetID, const OUString& rPSName, OStringBuffer& rBuffer ) 3828 { 3829 if( nSubsetID ) 3830 { 3831 for( int i = 0; i < 6; i++ ) 3832 { 3833 int nOffset = (nSubsetID % 26); 3834 nSubsetID /= 26; 3835 rBuffer.append( (sal_Char)('A'+nOffset) ); 3836 } 3837 rBuffer.append( '+' ); 3838 } 3839 appendName( rPSName, rBuffer ); 3840 } 3841 3842 sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8* pEncoding, 3843 sal_Ucs* pUnicodes, 3844 sal_Int32* pUnicodesPerGlyph, 3845 sal_Int32* pEncToUnicodeIndex, 3846 int nGlyphs ) 3847 { 3848 int nMapped = 0, n = 0; 3849 for( n = 0; n < nGlyphs; n++ ) 3850 if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] ) 3851 nMapped++; 3852 3853 if( nMapped == 0 ) 3854 return 0; 3855 3856 sal_Int32 nStream = createObject(); 3857 CHECK_RETURN( updateObject( nStream ) ); 3858 3859 OStringBuffer aContents( 1024 ); 3860 aContents.append( 3861 "/CIDInit/ProcSet findresource begin\n" 3862 "12 dict begin\n" 3863 "begincmap\n" 3864 "/CIDSystemInfo<<\n" 3865 "/Registry (Adobe)\n" 3866 "/Ordering (UCS)\n" 3867 "/Supplement 0\n" 3868 ">> def\n" 3869 "/CMapName/Adobe-Identity-UCS def\n" 3870 "/CMapType 2 def\n" 3871 "1 begincodespacerange\n" 3872 "<00> <FF>\n" 3873 "endcodespacerange\n" 3874 ); 3875 int nCount = 0; 3876 for( n = 0; n < nGlyphs; n++ ) 3877 { 3878 if( pUnicodes[pEncToUnicodeIndex[n]] && pUnicodesPerGlyph[n] ) 3879 { 3880 if( (nCount % 100) == 0 ) 3881 { 3882 if( nCount ) 3883 aContents.append( "endbfchar\n" ); 3884 aContents.append( (sal_Int32)((nMapped-nCount > 100) ? 100 : nMapped-nCount ) ); 3885 aContents.append( " beginbfchar\n" ); 3886 } 3887 aContents.append( '<' ); 3888 appendHex( (sal_Int8)pEncoding[n], aContents ); 3889 aContents.append( "> <" ); 3890 // TODO: handle unicodes>U+FFFF 3891 sal_Int32 nIndex = pEncToUnicodeIndex[n]; 3892 for( sal_Int32 j = 0; j < pUnicodesPerGlyph[n]; j++ ) 3893 { 3894 appendHex( (sal_Int8)(pUnicodes[nIndex + j] / 256), aContents ); 3895 appendHex( (sal_Int8)(pUnicodes[nIndex + j] & 255), aContents ); 3896 } 3897 aContents.append( ">\n" ); 3898 nCount++; 3899 } 3900 } 3901 aContents.append( "endbfchar\n" 3902 "endcmap\n" 3903 "CMapName currentdict /CMap defineresource pop\n" 3904 "end\n" 3905 "end\n" ); 3906 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3907 ZCodec* pCodec = new ZCodec( 0x4000, 0x4000 ); 3908 SvMemoryStream aStream; 3909 pCodec->BeginCompression(); 3910 pCodec->Write( aStream, (const sal_uInt8*)aContents.getStr(), aContents.getLength() ); 3911 pCodec->EndCompression(); 3912 delete pCodec; 3913 #endif 3914 3915 #if OSL_DEBUG_LEVEL > 1 3916 emitComment( "PDFWriterImpl::createToUnicodeCMap" ); 3917 #endif 3918 OStringBuffer aLine( 40 ); 3919 3920 aLine.append( nStream ); 3921 aLine.append( " 0 obj\n<</Length " ); 3922 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3923 sal_Int32 nLen = (sal_Int32)aStream.Tell(); 3924 aStream.Seek( 0 ); 3925 aLine.append( nLen ); 3926 aLine.append( "/Filter/FlateDecode" ); 3927 #else 3928 aLine.append( aContents.getLength() ); 3929 #endif 3930 aLine.append( ">>\nstream\n" ); 3931 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3932 checkAndEnableStreamEncryption( nStream ); 3933 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 3934 CHECK_RETURN( writeBuffer( aStream.GetData(), nLen ) ); 3935 #else 3936 CHECK_RETURN( writeBuffer( aContents.getStr(), aContents.getLength() ) ); 3937 #endif 3938 disableStreamEncryption(); 3939 aLine.setLength( 0 ); 3940 aLine.append( "\nendstream\n" 3941 "endobj\n\n" ); 3942 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 3943 return nStream; 3944 } 3945 3946 sal_Int32 PDFWriterImpl::emitFontDescriptor( const ImplFontData* pFont, FontSubsetInfo& rInfo, sal_Int32 nSubsetID, sal_Int32 nFontStream ) 3947 { 3948 OStringBuffer aLine( 1024 ); 3949 // get font flags, see PDF reference 1.4 p. 358 3950 // possibly characters outside Adobe standard encoding 3951 // so set Symbolic flag 3952 sal_Int32 nFontFlags = (1<<2); 3953 if( pFont->GetSlant() == ITALIC_NORMAL || pFont->GetSlant() == ITALIC_OBLIQUE ) 3954 nFontFlags |= (1 << 6); 3955 if( pFont->GetPitch() == PITCH_FIXED ) 3956 nFontFlags |= 1; 3957 if( pFont->GetFamilyType() == FAMILY_SCRIPT ) 3958 nFontFlags |= (1 << 3); 3959 else if( pFont->GetFamilyType() == FAMILY_ROMAN ) 3960 nFontFlags |= (1 << 1); 3961 3962 sal_Int32 nFontDescriptor = createObject(); 3963 CHECK_RETURN( updateObject( nFontDescriptor ) ); 3964 aLine.setLength( 0 ); 3965 aLine.append( nFontDescriptor ); 3966 aLine.append( " 0 obj\n" 3967 "<</Type/FontDescriptor/FontName/" ); 3968 appendSubsetName( nSubsetID, rInfo.m_aPSName, aLine ); 3969 aLine.append( "\n" 3970 "/Flags " ); 3971 aLine.append( nFontFlags ); 3972 aLine.append( "\n" 3973 "/FontBBox[" ); 3974 // note: Top and Bottom are reversed in VCL and PDF rectangles 3975 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().X() ); 3976 aLine.append( ' ' ); 3977 aLine.append( (sal_Int32)rInfo.m_aFontBBox.TopLeft().Y() ); 3978 aLine.append( ' ' ); 3979 aLine.append( (sal_Int32)rInfo.m_aFontBBox.BottomRight().X() ); 3980 aLine.append( ' ' ); 3981 aLine.append( (sal_Int32)(rInfo.m_aFontBBox.BottomRight().Y()+1) ); 3982 aLine.append( "]/ItalicAngle " ); 3983 if( pFont->GetSlant() == ITALIC_OBLIQUE || pFont->GetSlant() == ITALIC_NORMAL ) 3984 aLine.append( "-30" ); 3985 else 3986 aLine.append( "0" ); 3987 aLine.append( "\n" 3988 "/Ascent " ); 3989 aLine.append( (sal_Int32)rInfo.m_nAscent ); 3990 aLine.append( "\n" 3991 "/Descent " ); 3992 aLine.append( (sal_Int32)-rInfo.m_nDescent ); 3993 aLine.append( "\n" 3994 "/CapHeight " ); 3995 aLine.append( (sal_Int32)rInfo.m_nCapHeight ); 3996 // According to PDF reference 1.4 StemV is required 3997 // seems a tad strange to me, but well ... 3998 aLine.append( "\n" 3999 "/StemV 80\n" ); 4000 if( nFontStream ) 4001 { 4002 aLine.append( "/FontFile" ); 4003 switch( rInfo.m_nFontType ) 4004 { 4005 case FontSubsetInfo::SFNT_TTF: 4006 aLine.append( '2' ); 4007 break; 4008 case FontSubsetInfo::TYPE1_PFA: 4009 case FontSubsetInfo::TYPE1_PFB: 4010 case FontSubsetInfo::ANY_TYPE1: 4011 break; 4012 default: 4013 DBG_ERROR( "unknown fonttype in PDF font descriptor" ); 4014 return 0; 4015 } 4016 aLine.append( ' ' ); 4017 aLine.append( nFontStream ); 4018 aLine.append( " 0 R\n" ); 4019 } 4020 aLine.append( ">>\n" 4021 "endobj\n\n" ); 4022 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4023 4024 return nFontDescriptor; 4025 } 4026 4027 void PDFWriterImpl::appendBuiltinFontsToDict( OStringBuffer& rDict ) const 4028 { 4029 for( std::map< sal_Int32, sal_Int32 >::const_iterator it = 4030 m_aBuiltinFontToObjectMap.begin(); it != m_aBuiltinFontToObjectMap.end(); ++it ) 4031 { 4032 rDict.append( m_aBuiltinFonts[it->first].getNameObject() ); 4033 rDict.append( ' ' ); 4034 rDict.append( it->second ); 4035 rDict.append( " 0 R" ); 4036 } 4037 } 4038 4039 bool PDFWriterImpl::emitFonts() 4040 { 4041 if( ! m_pReferenceDevice->ImplGetGraphics() ) 4042 return false; 4043 4044 OStringBuffer aLine( 1024 ); 4045 4046 std::map< sal_Int32, sal_Int32 > aFontIDToObject; 4047 4048 OUString aTmpName; 4049 osl_createTempFile( NULL, NULL, &aTmpName.pData ); 4050 for( FontSubsetData::iterator it = m_aSubsets.begin(); it != m_aSubsets.end(); ++it ) 4051 { 4052 for( FontEmitList::iterator lit = it->second.m_aSubsets.begin(); lit != it->second.m_aSubsets.end(); ++lit ) 4053 { 4054 sal_Int32 pGlyphIDs[ 256 ]; 4055 sal_Int32 pWidths[ 256 ]; 4056 sal_uInt8 pEncoding[ 256 ]; 4057 sal_Int32 pEncToUnicodeIndex[ 256 ]; 4058 sal_Int32 pUnicodesPerGlyph[ 256 ]; 4059 std::vector<sal_Ucs> aUnicodes; 4060 aUnicodes.reserve( 256 ); 4061 int nGlyphs = 1; 4062 // fill arrays and prepare encoding index map 4063 sal_Int32 nToUnicodeStream = 0; 4064 4065 rtl_zeroMemory( pGlyphIDs, sizeof( pGlyphIDs ) ); 4066 rtl_zeroMemory( pEncoding, sizeof( pEncoding ) ); 4067 rtl_zeroMemory( pUnicodesPerGlyph, sizeof( pUnicodesPerGlyph ) ); 4068 rtl_zeroMemory( pEncToUnicodeIndex, sizeof( pEncToUnicodeIndex ) ); 4069 for( FontEmitMapping::iterator fit = lit->m_aMapping.begin(); fit != lit->m_aMapping.end();++fit ) 4070 { 4071 sal_uInt8 nEnc = fit->second.getGlyphId(); 4072 4073 DBG_ASSERT( pGlyphIDs[nEnc] == 0 && pEncoding[nEnc] == 0, "duplicate glyph" ); 4074 DBG_ASSERT( nEnc <= lit->m_aMapping.size(), "invalid glyph encoding" ); 4075 4076 pGlyphIDs[ nEnc ] = fit->first; 4077 pEncoding[ nEnc ] = nEnc; 4078 pEncToUnicodeIndex[ nEnc ] = static_cast<sal_Int32>(aUnicodes.size()); 4079 pUnicodesPerGlyph[ nEnc ] = fit->second.countCodes(); 4080 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[ nEnc ]; n++ ) 4081 aUnicodes.push_back( fit->second.getCode( n ) ); 4082 if( fit->second.getCode(0) ) 4083 nToUnicodeStream = 1; 4084 if( nGlyphs < 256 ) 4085 nGlyphs++; 4086 else 4087 { 4088 DBG_ERROR( "too many glyphs for subset" ); 4089 } 4090 } 4091 FontSubsetInfo aSubsetInfo; 4092 if( m_pReferenceDevice->mpGraphics->CreateFontSubset( aTmpName, it->first, pGlyphIDs, pEncoding, pWidths, nGlyphs, aSubsetInfo ) ) 4093 { 4094 // create font stream 4095 oslFileHandle aFontFile; 4096 CHECK_RETURN( (osl_File_E_None == osl_openFile( aTmpName.pData, &aFontFile, osl_File_OpenFlag_Read ) ) ); 4097 // get file size 4098 sal_uInt64 nLength1; 4099 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_End, 0 ) ) ); 4100 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( aFontFile, &nLength1 ) ) ); 4101 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); 4102 4103 #if OSL_DEBUG_LEVEL > 1 4104 emitComment( "PDFWriterImpl::emitFonts" ); 4105 #endif 4106 sal_Int32 nFontStream = createObject(); 4107 sal_Int32 nStreamLengthObject = createObject(); 4108 CHECK_RETURN( updateObject( nFontStream ) ); 4109 aLine.setLength( 0 ); 4110 aLine.append( nFontStream ); 4111 aLine.append( " 0 obj\n" 4112 "<</Length " ); 4113 aLine.append( (sal_Int32)nStreamLengthObject ); 4114 aLine.append( " 0 R" 4115 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 4116 "/Filter/FlateDecode" 4117 #endif 4118 "/Length1 " ); 4119 4120 sal_uInt64 nStartPos = 0; 4121 if( aSubsetInfo.m_nFontType == FontSubsetInfo::SFNT_TTF ) 4122 { 4123 aLine.append( (sal_Int32)nLength1 ); 4124 4125 aLine.append( ">>\n" 4126 "stream\n" ); 4127 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4128 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); 4129 4130 // copy font file 4131 beginCompression(); 4132 checkAndEnableStreamEncryption( nFontStream ); 4133 sal_Bool bEOF = sal_False; 4134 do 4135 { 4136 char buf[8192]; 4137 sal_uInt64 nRead; 4138 CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, buf, sizeof( buf ), &nRead ) ) ); 4139 CHECK_RETURN( writeBuffer( buf, nRead ) ); 4140 CHECK_RETURN( (osl_File_E_None == osl_isEndOfFile( aFontFile, &bEOF ) ) ); 4141 } while( ! bEOF ); 4142 } 4143 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::CFF_FONT) != 0 ) 4144 { 4145 // TODO: implement 4146 DBG_ERROR( "PDFWriterImpl does not support CFF-font subsets yet!" ); 4147 } 4148 else if( (aSubsetInfo.m_nFontType & FontSubsetInfo::TYPE1_PFB) != 0 ) // TODO: also support PFA? 4149 { 4150 boost::shared_array<unsigned char> pBuffer( new unsigned char[ nLength1 ] ); 4151 4152 sal_uInt64 nBytesRead = 0; 4153 CHECK_RETURN( (osl_File_E_None == osl_readFile( aFontFile, pBuffer.get(), nLength1, &nBytesRead ) ) ); 4154 DBG_ASSERT( nBytesRead==nLength1, "PDF-FontSubset read incomplete!" ); 4155 CHECK_RETURN( (osl_File_E_None == osl_setFilePos( aFontFile, osl_Pos_Absolut, 0 ) ) ); 4156 // get the PFB-segment lengths 4157 ThreeInts aSegmentLengths = {0,0,0}; 4158 getPfbSegmentLengths( pBuffer.get(), (int)nBytesRead, aSegmentLengths ); 4159 // the lengths below are mandatory for PDF-exported Type1 fonts 4160 // because the PFB segment headers get stripped! WhyOhWhy. 4161 aLine.append( (sal_Int32)aSegmentLengths[0] ); 4162 aLine.append( "/Length2 " ); 4163 aLine.append( (sal_Int32)aSegmentLengths[1] ); 4164 aLine.append( "/Length3 " ); 4165 aLine.append( (sal_Int32)aSegmentLengths[2] ); 4166 4167 aLine.append( ">>\n" 4168 "stream\n" ); 4169 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4170 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos ) ) ); 4171 4172 // emit PFB-sections without section headers 4173 beginCompression(); 4174 checkAndEnableStreamEncryption( nFontStream ); 4175 CHECK_RETURN( writeBuffer( &pBuffer[6], aSegmentLengths[0] ) ); 4176 CHECK_RETURN( writeBuffer( &pBuffer[12] + aSegmentLengths[0], aSegmentLengths[1] ) ); 4177 CHECK_RETURN( writeBuffer( &pBuffer[18] + aSegmentLengths[0] + aSegmentLengths[1], aSegmentLengths[2] ) ); 4178 } 4179 else 4180 { 4181 fprintf( stderr, "PDF: CreateFontSubset result in not yet supported format=%d\n",aSubsetInfo.m_nFontType); 4182 aLine.append( "0 >>\nstream\n" ); 4183 } 4184 4185 endCompression(); 4186 disableStreamEncryption(); 4187 // close the file 4188 osl_closeFile( aFontFile ); 4189 4190 sal_uInt64 nEndPos = 0; 4191 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos ) ) ); 4192 // end the stream 4193 aLine.setLength( 0 ); 4194 aLine.append( "\nendstream\nendobj\n\n" ); 4195 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4196 4197 // emit stream length object 4198 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 4199 aLine.setLength( 0 ); 4200 aLine.append( nStreamLengthObject ); 4201 aLine.append( " 0 obj\n" ); 4202 aLine.append( (sal_Int64)(nEndPos-nStartPos) ); 4203 aLine.append( "\nendobj\n\n" ); 4204 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4205 4206 // write font descriptor 4207 sal_Int32 nFontDescriptor = emitFontDescriptor( it->first, aSubsetInfo, lit->m_nFontID, nFontStream ); 4208 4209 if( nToUnicodeStream ) 4210 nToUnicodeStream = createToUnicodeCMap( pEncoding, &aUnicodes[0], pUnicodesPerGlyph, pEncToUnicodeIndex, nGlyphs ); 4211 4212 sal_Int32 nFontObject = createObject(); 4213 CHECK_RETURN( updateObject( nFontObject ) ); 4214 aLine.setLength( 0 ); 4215 aLine.append( nFontObject ); 4216 4217 aLine.append( " 0 obj\n" ); 4218 aLine.append( ((aSubsetInfo.m_nFontType & FontSubsetInfo::ANY_TYPE1) != 0) ? 4219 "<</Type/Font/Subtype/Type1/BaseFont/" : 4220 "<</Type/Font/Subtype/TrueType/BaseFont/" ); 4221 appendSubsetName( lit->m_nFontID, aSubsetInfo.m_aPSName, aLine ); 4222 aLine.append( "\n" 4223 "/FirstChar 0\n" 4224 "/LastChar " ); 4225 aLine.append( (sal_Int32)(nGlyphs-1) ); 4226 aLine.append( "\n" 4227 "/Widths[" ); 4228 for( int i = 0; i < nGlyphs; i++ ) 4229 { 4230 aLine.append( pWidths[ i ] ); 4231 aLine.append( ((i & 15) == 15) ? "\n" : " " ); 4232 } 4233 aLine.append( "]\n" 4234 "/FontDescriptor " ); 4235 aLine.append( nFontDescriptor ); 4236 aLine.append( " 0 R\n" ); 4237 if( nToUnicodeStream ) 4238 { 4239 aLine.append( "/ToUnicode " ); 4240 aLine.append( nToUnicodeStream ); 4241 aLine.append( " 0 R\n" ); 4242 } 4243 aLine.append( ">>\n" 4244 "endobj\n\n" ); 4245 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4246 4247 aFontIDToObject[ lit->m_nFontID ] = nFontObject; 4248 } 4249 else 4250 { 4251 const ImplFontData* pFont = it->first; 4252 rtl::OStringBuffer aErrorComment( 256 ); 4253 aErrorComment.append( "CreateFontSubset failed for font \"" ); 4254 aErrorComment.append( OUStringToOString( pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) ); 4255 aErrorComment.append( '\"' ); 4256 if( pFont->GetSlant() == ITALIC_NORMAL ) 4257 aErrorComment.append( " italic" ); 4258 else if( pFont->GetSlant() == ITALIC_OBLIQUE ) 4259 aErrorComment.append( " oblique" ); 4260 aErrorComment.append( " weight=" ); 4261 aErrorComment.append( sal_Int32(pFont->GetWeight()) ); 4262 emitComment( aErrorComment.getStr() ); 4263 } 4264 } 4265 } 4266 osl_removeFile( aTmpName.pData ); 4267 4268 // emit embedded fonts 4269 for( FontEmbedData::iterator eit = m_aEmbeddedFonts.begin(); eit != m_aEmbeddedFonts.end(); ++eit ) 4270 { 4271 std::map< sal_Int32, sal_Int32 > aObjects = emitEmbeddedFont( eit->first, eit->second ); 4272 for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit ) 4273 { 4274 CHECK_RETURN( fit->second ); 4275 aFontIDToObject[ fit->first ] = fit->second; 4276 } 4277 } 4278 4279 // emit system fonts 4280 for( FontEmbedData::iterator sit = m_aSystemFonts.begin(); sit != m_aSystemFonts.end(); ++sit ) 4281 { 4282 std::map< sal_Int32, sal_Int32 > aObjects = emitSystemFont( sit->first, sit->second ); 4283 for( std::map< sal_Int32, sal_Int32 >::iterator fit = aObjects.begin(); fit != aObjects.end(); ++fit ) 4284 { 4285 CHECK_RETURN( fit->second ); 4286 aFontIDToObject[ fit->first ] = fit->second; 4287 } 4288 } 4289 4290 OStringBuffer aFontDict( 1024 ); 4291 aFontDict.append( getFontDictObject() ); 4292 aFontDict.append( " 0 obj\n" 4293 "<<" ); 4294 int ni = 0; 4295 for( std::map< sal_Int32, sal_Int32 >::iterator mit = aFontIDToObject.begin(); mit != aFontIDToObject.end(); ++mit ) 4296 { 4297 aFontDict.append( "/F" ); 4298 aFontDict.append( mit->first ); 4299 aFontDict.append( ' ' ); 4300 aFontDict.append( mit->second ); 4301 aFontDict.append( " 0 R" ); 4302 if( ((++ni) & 7) == 0 ) 4303 aFontDict.append( '\n' ); 4304 } 4305 // emit builtin font for widget apperances / variable text 4306 for( std::map< sal_Int32, sal_Int32 >::iterator it = m_aBuiltinFontToObjectMap.begin(); 4307 it != m_aBuiltinFontToObjectMap.end(); ++it ) 4308 { 4309 ImplPdfBuiltinFontData aData(m_aBuiltinFonts[it->first]); 4310 it->second = emitBuiltinFont( &aData, it->second ); 4311 } 4312 appendBuiltinFontsToDict( aFontDict ); 4313 aFontDict.append( "\n>>\nendobj\n\n" ); 4314 4315 CHECK_RETURN( updateObject( getFontDictObject() ) ); 4316 CHECK_RETURN( writeBuffer( aFontDict.getStr(), aFontDict.getLength() ) ); 4317 return true; 4318 } 4319 4320 sal_Int32 PDFWriterImpl::emitResources() 4321 { 4322 // emit shadings 4323 if( ! m_aGradients.empty() ) 4324 CHECK_RETURN( emitGradients() ); 4325 // emit tilings 4326 if( ! m_aTilings.empty() ) 4327 CHECK_RETURN( emitTilings() ); 4328 4329 // emit font dict 4330 CHECK_RETURN( emitFonts() ); 4331 4332 // emit Resource dict 4333 OStringBuffer aLine( 512 ); 4334 sal_Int32 nResourceDict = getResourceDictObj(); 4335 CHECK_RETURN( updateObject( nResourceDict ) ); 4336 aLine.setLength( 0 ); 4337 aLine.append( nResourceDict ); 4338 aLine.append( " 0 obj\n" ); 4339 m_aGlobalResourceDict.append( aLine, getFontDictObject() ); 4340 aLine.append( "endobj\n\n" ); 4341 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4342 return nResourceDict; 4343 } 4344 4345 sal_Int32 PDFWriterImpl::updateOutlineItemCount( std::vector< sal_Int32 >& rCounts, 4346 sal_Int32 nItemLevel, 4347 sal_Int32 nCurrentItemId ) 4348 { 4349 /* The /Count number of an item is 4350 positive: the number of visible subitems 4351 negative: the negative number of subitems that will become visible if 4352 the item gets opened 4353 see PDF ref 1.4 p 478 4354 */ 4355 4356 sal_Int32 nCount = 0; 4357 4358 if( m_aContext.OpenBookmarkLevels < 0 || // all levels arevisible 4359 m_aContext.OpenBookmarkLevels >= nItemLevel // this level is visible 4360 ) 4361 { 4362 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ]; 4363 sal_Int32 nChildren = rItem.m_aChildren.size(); 4364 for( sal_Int32 i = 0; i < nChildren; i++ ) 4365 nCount += updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] ); 4366 rCounts[nCurrentItemId] = nCount; 4367 // return 1 (this item) + visible sub items 4368 if( nCount < 0 ) 4369 nCount = 0; 4370 nCount++; 4371 } 4372 else 4373 { 4374 // this bookmark level is invisible 4375 PDFOutlineEntry& rItem = m_aOutline[ nCurrentItemId ]; 4376 sal_Int32 nChildren = rItem.m_aChildren.size(); 4377 rCounts[ nCurrentItemId ] = -sal_Int32(rItem.m_aChildren.size()); 4378 for( sal_Int32 i = 0; i < nChildren; i++ ) 4379 updateOutlineItemCount( rCounts, nItemLevel+1, rItem.m_aChildren[i] ); 4380 nCount = -1; 4381 } 4382 4383 return nCount; 4384 } 4385 4386 sal_Int32 PDFWriterImpl::emitOutline() 4387 { 4388 int i, nItems = m_aOutline.size(); 4389 4390 // do we have an outline at all ? 4391 if( nItems < 2 ) 4392 return 0; 4393 4394 // reserve object numbers for all outline items 4395 for( i = 0; i < nItems; ++i ) 4396 m_aOutline[i].m_nObject = createObject(); 4397 4398 // update all parent, next and prev object ids 4399 for( i = 0; i < nItems; ++i ) 4400 { 4401 PDFOutlineEntry& rItem = m_aOutline[i]; 4402 int nChildren = rItem.m_aChildren.size(); 4403 4404 if( nChildren ) 4405 { 4406 for( int n = 0; n < nChildren; ++n ) 4407 { 4408 PDFOutlineEntry& rChild = m_aOutline[ rItem.m_aChildren[n] ]; 4409 4410 rChild.m_nParentObject = rItem.m_nObject; 4411 rChild.m_nPrevObject = (n > 0) ? m_aOutline[ rItem.m_aChildren[n-1] ].m_nObject : 0; 4412 rChild.m_nNextObject = (n < nChildren-1) ? m_aOutline[ rItem.m_aChildren[n+1] ].m_nObject : 0; 4413 } 4414 4415 } 4416 } 4417 4418 // calculate Count entries for all items 4419 std::vector< sal_Int32 > aCounts( nItems ); 4420 updateOutlineItemCount( aCounts, 0, 0 ); 4421 4422 // emit hierarchy 4423 for( i = 0; i < nItems; ++i ) 4424 { 4425 PDFOutlineEntry& rItem = m_aOutline[i]; 4426 OStringBuffer aLine( 1024 ); 4427 4428 CHECK_RETURN( updateObject( rItem.m_nObject ) ); 4429 aLine.append( rItem.m_nObject ); 4430 aLine.append( " 0 obj\n" ); 4431 aLine.append( "<<" ); 4432 // number of visible children (all levels) 4433 if( i > 0 || aCounts[0] > 0 ) 4434 { 4435 aLine.append( "/Count " ); 4436 aLine.append( aCounts[i] ); 4437 } 4438 if( ! rItem.m_aChildren.empty() ) 4439 { 4440 // children list: First, Last 4441 aLine.append( "/First " ); 4442 aLine.append( m_aOutline[rItem.m_aChildren.front()].m_nObject ); 4443 aLine.append( " 0 R/Last " ); 4444 aLine.append( m_aOutline[rItem.m_aChildren.back()].m_nObject ); 4445 aLine.append( " 0 R\n" ); 4446 } 4447 if( i > 0 ) 4448 { 4449 // Title, Dest, Parent, Prev, Next 4450 aLine.append( "/Title" ); 4451 appendUnicodeTextStringEncrypt( rItem.m_aTitle, rItem.m_nObject, aLine ); 4452 aLine.append( "\n" ); 4453 // Dest is not required 4454 if( rItem.m_nDestID >= 0 && rItem.m_nDestID < (sal_Int32)m_aDests.size() ) 4455 { 4456 aLine.append( "/Dest" ); 4457 appendDest( rItem.m_nDestID, aLine ); 4458 } 4459 aLine.append( "/Parent " ); 4460 aLine.append( rItem.m_nParentObject ); 4461 aLine.append( " 0 R" ); 4462 if( rItem.m_nPrevObject ) 4463 { 4464 aLine.append( "/Prev " ); 4465 aLine.append( rItem.m_nPrevObject ); 4466 aLine.append( " 0 R" ); 4467 } 4468 if( rItem.m_nNextObject ) 4469 { 4470 aLine.append( "/Next " ); 4471 aLine.append( rItem.m_nNextObject ); 4472 aLine.append( " 0 R" ); 4473 } 4474 } 4475 aLine.append( ">>\nendobj\n\n" ); 4476 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4477 } 4478 4479 return m_aOutline[0].m_nObject; 4480 } 4481 4482 #undef CHECK_RETURN 4483 #define CHECK_RETURN( x ) if( !x ) return false 4484 4485 bool PDFWriterImpl::appendDest( sal_Int32 nDestID, OStringBuffer& rBuffer ) 4486 { 4487 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) 4488 { 4489 #if OSL_DEBUG_LEVEL > 1 4490 fprintf( stderr, "ERROR: invalid dest %d requested\n", (int)nDestID ); 4491 #endif 4492 return false; 4493 } 4494 4495 4496 const PDFDest& rDest = m_aDests[ nDestID ]; 4497 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ]; 4498 4499 rBuffer.append( '[' ); 4500 rBuffer.append( rDestPage.m_nPageObject ); 4501 rBuffer.append( " 0 R" ); 4502 4503 switch( rDest.m_eType ) 4504 { 4505 case PDFWriter::XYZ: 4506 default: 4507 rBuffer.append( "/XYZ " ); 4508 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4509 rBuffer.append( ' ' ); 4510 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4511 rBuffer.append( " 0" ); 4512 break; 4513 case PDFWriter::Fit: 4514 rBuffer.append( "/Fit" ); 4515 break; 4516 case PDFWriter::FitRectangle: 4517 rBuffer.append( "/FitR " ); 4518 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4519 rBuffer.append( ' ' ); 4520 appendFixedInt( rDest.m_aRect.Top(), rBuffer ); 4521 rBuffer.append( ' ' ); 4522 appendFixedInt( rDest.m_aRect.Right(), rBuffer ); 4523 rBuffer.append( ' ' ); 4524 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4525 break; 4526 case PDFWriter::FitHorizontal: 4527 rBuffer.append( "/FitH " ); 4528 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4529 break; 4530 case PDFWriter::FitVertical: 4531 rBuffer.append( "/FitV " ); 4532 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4533 break; 4534 case PDFWriter::FitPageBoundingBox: 4535 rBuffer.append( "/FitB" ); 4536 break; 4537 case PDFWriter::FitPageBoundingBoxHorizontal: 4538 rBuffer.append( "/FitBH " ); 4539 appendFixedInt( rDest.m_aRect.Bottom(), rBuffer ); 4540 break; 4541 case PDFWriter::FitPageBoundingBoxVertical: 4542 rBuffer.append( "/FitBV " ); 4543 appendFixedInt( rDest.m_aRect.Left(), rBuffer ); 4544 break; 4545 } 4546 rBuffer.append( ']' ); 4547 4548 return true; 4549 } 4550 4551 bool PDFWriterImpl::emitLinkAnnotations() 4552 { 4553 int nAnnots = m_aLinks.size(); 4554 for( int i = 0; i < nAnnots; i++ ) 4555 { 4556 const PDFLink& rLink = m_aLinks[i]; 4557 if( ! updateObject( rLink.m_nObject ) ) 4558 continue; 4559 4560 OStringBuffer aLine( 1024 ); 4561 aLine.append( rLink.m_nObject ); 4562 aLine.append( " 0 obj\n" ); 4563 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' 4564 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 4565 aLine.append( "<</Type/Annot" ); 4566 if( m_bIsPDF_A1 ) 4567 aLine.append( "/F 4" ); 4568 aLine.append( "/Subtype/Link/Border[0 0 0]/Rect[" ); 4569 4570 appendFixedInt( rLink.m_aRect.Left()-7, aLine );//the +7 to have a better shape of the border rectangle 4571 aLine.append( ' ' ); 4572 appendFixedInt( rLink.m_aRect.Top(), aLine ); 4573 aLine.append( ' ' ); 4574 appendFixedInt( rLink.m_aRect.Right()+7, aLine );//the +7 to have a better shape of the border rectangle 4575 aLine.append( ' ' ); 4576 appendFixedInt( rLink.m_aRect.Bottom(), aLine ); 4577 aLine.append( "]" ); 4578 if( rLink.m_nDest >= 0 ) 4579 { 4580 aLine.append( "/Dest" ); 4581 appendDest( rLink.m_nDest, aLine ); 4582 } 4583 else 4584 { 4585 /*--->i56629 4586 destination is external to the document, so 4587 we check in the following sequence: 4588 4589 if target type is neither .pdf, nor .od[tpgs], then 4590 check if relative or absolute and act accordingly (use URI or 'launch application' as requested) 4591 end processing 4592 else if target is .od[tpgs]: then 4593 if conversion of type from od[tpgs] to pdf is requested, convert it and this becomes the new target file 4594 processing continue 4595 4596 if (new)target is .pdf : then 4597 if GotToR is requested, then 4598 convert the target in GoToR where the fragment of the URI is 4599 considered the named destination in the target file, set relative or absolute as requested 4600 else strip the fragment from URL and then set URI or 'launch application' as requested 4601 */ 4602 // 4603 // FIXME: check if the decode mechanisms for URL processing throughout this implementation 4604 // are the correct one!! 4605 // 4606 // extract target file type 4607 INetURLObject aDocumentURL( m_aContext.BaseURL ); 4608 INetURLObject aTargetURL( rLink.m_aURL ); 4609 sal_Int32 nChangeFileExtensionToPDF = 0; 4610 sal_Int32 nSetGoToRMode = 0; 4611 sal_Bool bTargetHasPDFExtension = sal_False; 4612 INetProtocol eTargetProtocol = aTargetURL.GetProtocol(); 4613 sal_Bool bIsUNCPath = sal_False; 4614 // check if the protocol is a known one, or if there is no protocol at all (on target only) 4615 // if there is no protocol, make the target relative to the current document directory 4616 // getting the needed URL information from the current document path 4617 if( eTargetProtocol == INET_PROT_NOT_VALID ) 4618 { 4619 if( rLink.m_aURL.getLength() > 4 && rLink.m_aURL.compareToAscii( "\\\\\\\\", 4 ) == 0) 4620 { 4621 bIsUNCPath = sal_True; 4622 } 4623 else 4624 { 4625 INetURLObject aNewBase( aDocumentURL );//duplicate document URL 4626 aNewBase.removeSegment(); //remove last segment from it, obtaining the base URL of the 4627 //target document 4628 aNewBase.insertName( rLink.m_aURL ); 4629 aTargetURL = aNewBase;//reassign the new target URL 4630 //recompute the target protocol, with the new URL 4631 //normal URL processing resumes 4632 eTargetProtocol = aTargetURL.GetProtocol(); 4633 } 4634 } 4635 4636 rtl::OUString aFileExtension = aTargetURL.GetFileExtension(); 4637 4638 // Check if the URL ends in '/': if yes it's a directory, 4639 // it will be forced to a URI link. 4640 // possibly a malformed URI, leave it as it is, force as URI 4641 if( aTargetURL.hasFinalSlash() ) 4642 m_aContext.DefaultLinkAction = PDFWriter::URIAction; 4643 4644 if( aFileExtension.getLength() > 0 ) 4645 { 4646 if( m_aContext.ConvertOOoTargetToPDFTarget ) 4647 { 4648 //examine the file type (.odm .odt. .odp, odg, ods) 4649 if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odm" ) ) ) ) 4650 nChangeFileExtensionToPDF++; 4651 if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odt" ) ) ) ) 4652 nChangeFileExtensionToPDF++; 4653 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odp" ) ) ) ) 4654 nChangeFileExtensionToPDF++; 4655 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "odg" ) ) ) ) 4656 nChangeFileExtensionToPDF++; 4657 else if( aFileExtension.equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ods" ) ) ) ) 4658 nChangeFileExtensionToPDF++; 4659 if( nChangeFileExtensionToPDF ) 4660 aTargetURL.setExtension(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) ); 4661 } 4662 //check if extension is pdf, see if GoToR should be forced 4663 bTargetHasPDFExtension = aTargetURL.GetFileExtension().equalsIgnoreAsciiCase(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "pdf" ) ) ); 4664 if( m_aContext.ForcePDFAction && bTargetHasPDFExtension ) 4665 nSetGoToRMode++; 4666 } 4667 //prepare the URL, if relative or not 4668 INetProtocol eBaseProtocol = aDocumentURL.GetProtocol(); 4669 //queue the string common to all types of actions 4670 aLine.append( "/A<</Type/Action/S"); 4671 if( bIsUNCPath ) // handle Win UNC paths 4672 { 4673 aLine.append( "/Launch/Win<</F" ); 4674 // INetURLObject is not good with UNC paths, use original path 4675 appendLiteralStringEncrypt( rLink.m_aURL, rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 4676 aLine.append( ">>" ); 4677 } 4678 else 4679 { 4680 bool bSetRelative = false; 4681 bool bFileSpec = false; 4682 //check if relative file link is requested and if the protocol is 'file://' 4683 if( m_aContext.RelFsys && eBaseProtocol == eTargetProtocol && eTargetProtocol == INET_PROT_FILE ) 4684 bSetRelative = true; 4685 4686 rtl::OUString aFragment = aTargetURL.GetMark( INetURLObject::NO_DECODE /*DECODE_WITH_CHARSET*/ ); //fragment as is, 4687 if( nSetGoToRMode == 0 ) 4688 { 4689 switch( m_aContext.DefaultLinkAction ) 4690 { 4691 default: 4692 case PDFWriter::URIAction : 4693 case PDFWriter::URIActionDestination : 4694 aLine.append( "/URI/URI" ); 4695 break; 4696 case PDFWriter::LaunchAction: 4697 // now: 4698 // if a launch action is requested and the hyperlink target has a fragment 4699 // and the target file does not have a pdf extension, or it's not a 'file:://' protocol 4700 // then force the uri action on it 4701 // This code will permit the correct opening of application on web pages, the one that 4702 // normally have fragments (but I may be wrong...) 4703 // and will force the use of URI when the protocol is not file:// 4704 if( (aFragment.getLength() > 0 && !bTargetHasPDFExtension) || 4705 eTargetProtocol != INET_PROT_FILE ) 4706 aLine.append( "/URI/URI" ); 4707 else 4708 { 4709 aLine.append( "/Launch/F" ); 4710 bFileSpec = true; 4711 } 4712 break; 4713 } 4714 } 4715 //fragment are encoded in the same way as in the named destination processing 4716 if( nSetGoToRMode ) 4717 {//add the fragment 4718 rtl::OUString aURLNoMark = aTargetURL.GetURLNoMark( INetURLObject::DECODE_WITH_CHARSET ); 4719 aLine.append("/GoToR"); 4720 aLine.append("/F"); 4721 bFileSpec = true; 4722 appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURLNoMark, 4723 INetURLObject::WAS_ENCODED, 4724 INetURLObject::DECODE_WITH_CHARSET ) : 4725 aURLNoMark, rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 4726 if( aFragment.getLength() > 0 ) 4727 { 4728 aLine.append("/D/"); 4729 appendDestinationName( aFragment , aLine ); 4730 } 4731 } 4732 else 4733 { 4734 // change the fragment to accomodate the bookmark (only if the file extension is PDF and 4735 // the requested action is of the correct type) 4736 if(m_aContext.DefaultLinkAction == PDFWriter::URIActionDestination && 4737 bTargetHasPDFExtension && aFragment.getLength() > 0 ) 4738 { 4739 OStringBuffer aLineLoc( 1024 ); 4740 appendDestinationName( aFragment , aLineLoc ); 4741 //substitute the fragment 4742 aTargetURL.SetMark( aLineLoc.getStr() ); 4743 } 4744 rtl::OUString aURL = aTargetURL.GetMainURL( bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE ); 4745 // check if we have a URL available, if the string is empty, set it as the original one 4746 // if( aURL.getLength() == 0 ) 4747 // appendLiteralStringEncrypt( rLink.m_aURL , rLink.m_nObject, aLine ); 4748 // else 4749 appendLiteralStringEncrypt( bSetRelative ? INetURLObject::GetRelURL( m_aContext.BaseURL, aURL, 4750 INetURLObject::WAS_ENCODED, 4751 bFileSpec ? INetURLObject::DECODE_WITH_CHARSET : INetURLObject::NO_DECODE 4752 ) : 4753 aURL , rLink.m_nObject, aLine, osl_getThreadTextEncoding() ); 4754 } 4755 //<--- i56629 4756 } 4757 aLine.append( ">>\n" ); 4758 } 4759 if( rLink.m_nStructParent > 0 ) 4760 { 4761 aLine.append( "/StructParent " ); 4762 aLine.append( rLink.m_nStructParent ); 4763 } 4764 aLine.append( ">>\nendobj\n\n" ); 4765 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4766 } 4767 4768 return true; 4769 } 4770 4771 bool PDFWriterImpl::emitNoteAnnotations() 4772 { 4773 // emit note annotations 4774 int nAnnots = m_aNotes.size(); 4775 for( int i = 0; i < nAnnots; i++ ) 4776 { 4777 const PDFNoteEntry& rNote = m_aNotes[i]; 4778 if( ! updateObject( rNote.m_nObject ) ) 4779 return false; 4780 4781 OStringBuffer aLine( 1024 ); 4782 aLine.append( rNote.m_nObject ); 4783 aLine.append( " 0 obj\n" ); 4784 //i59651 key /F set bits Print to 1 rest to 0. We don't set NoZoom NoRotate to 1, since it's a 'should' 4785 // see PDF 8.4.2 and ISO 19005-1:2005 6.5.3 4786 aLine.append( "<</Type/Annot" ); 4787 if( m_bIsPDF_A1 ) 4788 aLine.append( "/F 4" ); 4789 aLine.append( "/Subtype/Text/Rect[" ); 4790 4791 appendFixedInt( rNote.m_aRect.Left(), aLine ); 4792 aLine.append( ' ' ); 4793 appendFixedInt( rNote.m_aRect.Top(), aLine ); 4794 aLine.append( ' ' ); 4795 appendFixedInt( rNote.m_aRect.Right(), aLine ); 4796 aLine.append( ' ' ); 4797 appendFixedInt( rNote.m_aRect.Bottom(), aLine ); 4798 aLine.append( "]" ); 4799 4800 // contents of the note (type text string) 4801 aLine.append( "/Contents\n" ); 4802 appendUnicodeTextStringEncrypt( rNote.m_aContents.Contents, rNote.m_nObject, aLine ); 4803 aLine.append( "\n" ); 4804 4805 // optional title 4806 if( rNote.m_aContents.Title.Len() ) 4807 { 4808 aLine.append( "/T" ); 4809 appendUnicodeTextStringEncrypt( rNote.m_aContents.Title, rNote.m_nObject, aLine ); 4810 aLine.append( "\n" ); 4811 } 4812 4813 aLine.append( ">>\nendobj\n\n" ); 4814 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 4815 } 4816 return true; 4817 } 4818 4819 Font PDFWriterImpl::replaceFont( const Font& rControlFont, const Font& rAppSetFont ) 4820 { 4821 bool bAdjustSize = false; 4822 4823 Font aFont( rControlFont ); 4824 if( ! aFont.GetName().Len() ) 4825 { 4826 aFont = rAppSetFont; 4827 if( rControlFont.GetHeight() ) 4828 aFont.SetSize( Size( 0, rControlFont.GetHeight() ) ); 4829 else 4830 bAdjustSize = true; 4831 if( rControlFont.GetItalic() != ITALIC_DONTKNOW ) 4832 aFont.SetItalic( rControlFont.GetItalic() ); 4833 if( rControlFont.GetWeight() != WEIGHT_DONTKNOW ) 4834 aFont.SetWeight( rControlFont.GetWeight() ); 4835 } 4836 else if( ! aFont.GetHeight() ) 4837 { 4838 aFont.SetSize( rAppSetFont.GetSize() ); 4839 bAdjustSize = true; 4840 } 4841 if( bAdjustSize ) 4842 { 4843 Size aFontSize = aFont.GetSize(); 4844 OutputDevice* pDefDev = Application::GetDefaultDevice(); 4845 aFontSize = OutputDevice::LogicToLogic( aFontSize, pDefDev->GetMapMode(), getMapMode() ); 4846 aFont.SetSize( aFontSize ); 4847 } 4848 return aFont; 4849 } 4850 4851 sal_Int32 PDFWriterImpl::getBestBuiltinFont( const Font& rFont ) 4852 { 4853 sal_Int32 nBest = 4; // default to Helvetica 4854 OUString aFontName( rFont.GetName() ); 4855 aFontName = aFontName.toAsciiLowerCase(); 4856 4857 if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "times" ) ) ) != -1 ) 4858 nBest = 8; 4859 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "courier" ) ) ) != -1 ) 4860 nBest = 0; 4861 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "dingbats" ) ) ) != -1 ) 4862 nBest = 13; 4863 else if( aFontName.indexOf( OUString( RTL_CONSTASCII_USTRINGPARAM( "symbol" ) ) ) != -1 ) 4864 nBest = 12; 4865 if( nBest < 12 ) 4866 { 4867 if( rFont.GetItalic() == ITALIC_OBLIQUE || rFont.GetItalic() == ITALIC_NORMAL ) 4868 nBest += 1; 4869 if( rFont.GetWeight() > WEIGHT_MEDIUM ) 4870 nBest += 2; 4871 } 4872 4873 if( m_aBuiltinFontToObjectMap.find( nBest ) == m_aBuiltinFontToObjectMap.end() ) 4874 m_aBuiltinFontToObjectMap[ nBest ] = createObject(); 4875 4876 return nBest; 4877 } 4878 4879 static inline const Color& replaceColor( const Color& rCol1, const Color& rCol2 ) 4880 { 4881 return (rCol1 == Color( COL_TRANSPARENT )) ? rCol2 : rCol1; 4882 } 4883 4884 void PDFWriterImpl::createDefaultPushButtonAppearance( PDFWidget& rButton, const PDFWriter::PushButtonWidget& rWidget ) 4885 { 4886 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 4887 4888 // save graphics state 4889 push( sal::static_int_cast<sal_uInt16>(~0U) ); 4890 4891 // transform relative to control's coordinates since an 4892 // appearance stream is a form XObject 4893 // this relies on the m_aRect member of rButton NOT already being transformed 4894 // to default user space 4895 if( rWidget.Background || rWidget.Border ) 4896 { 4897 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetLightColor() ) : Color( COL_TRANSPARENT ) ); 4898 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetDialogColor() ) : Color( COL_TRANSPARENT ) ); 4899 drawRectangle( rWidget.Location ); 4900 } 4901 // prepare font to use 4902 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetPushButtonFont() ); 4903 setFont( aFont ); 4904 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ) ); 4905 4906 drawText( rButton.m_aRect, rButton.m_aText, rButton.m_nTextStyle ); 4907 4908 // create DA string while local mapmode is still in place 4909 // (that is before endRedirect()) 4910 OStringBuffer aDA( 256 ); 4911 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetButtonTextColor() ), aDA ); 4912 Font aDummyFont( String( RTL_CONSTASCII_USTRINGPARAM( "Helvetica" ) ), aFont.GetSize() ); 4913 sal_Int32 nDummyBuiltin = getBestBuiltinFont( aDummyFont ); 4914 aDA.append( ' ' ); 4915 aDA.append( m_aBuiltinFonts[nDummyBuiltin].getNameObject() ); 4916 aDA.append( ' ' ); 4917 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); 4918 aDA.append( " Tf" ); 4919 rButton.m_aDAString = aDA.makeStringAndClear(); 4920 4921 pop(); 4922 4923 rButton.m_aAppearances[ "N" ][ "Standard" ] = new SvMemoryStream(); 4924 4925 /* seems like a bad hack but at least works in both AR5 and 6: 4926 we draw the button ourselves and tell AR 4927 the button would be totally transparent with no text 4928 4929 One would expect that simply setting a normal appearance 4930 should suffice, but no, as soon as the user actually presses 4931 the button and an action is tied to it (gasp! a button that 4932 does something) the appearance gets replaced by some crap that AR 4933 creates on the fly even if no DA or MK is given. On AR6 at least 4934 the DA and MK work as expected, but on AR5 this creates a region 4935 filled with the background color but nor text. Urgh. 4936 */ 4937 rButton.m_aMKDict = "/BC [] /BG [] /CA"; 4938 rButton.m_aMKDictCAString = ""; 4939 } 4940 4941 Font PDFWriterImpl::drawFieldBorder( PDFWidget& rIntern, 4942 const PDFWriter::AnyWidget& rWidget, 4943 const StyleSettings& rSettings ) 4944 { 4945 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetFieldFont() ); 4946 4947 if( rWidget.Background || rWidget.Border ) 4948 { 4949 if( rWidget.Border && rWidget.BorderColor == Color( COL_TRANSPARENT ) ) 4950 { 4951 sal_Int32 nDelta = getReferenceDevice()->ImplGetDPIX() / 500; 4952 if( nDelta < 1 ) 4953 nDelta = 1; 4954 setLineColor( Color( COL_TRANSPARENT ) ); 4955 Rectangle aRect = rIntern.m_aRect; 4956 setFillColor( rSettings.GetLightBorderColor() ); 4957 drawRectangle( aRect ); 4958 aRect.Left() += nDelta; aRect.Top() += nDelta; 4959 aRect.Right() -= nDelta; aRect.Bottom() -= nDelta; 4960 setFillColor( rSettings.GetFieldColor() ); 4961 drawRectangle( aRect ); 4962 setFillColor( rSettings.GetLightColor() ); 4963 drawRectangle( Rectangle( Point( aRect.Left(), aRect.Bottom()-nDelta ), aRect.BottomRight() ) ); 4964 drawRectangle( Rectangle( Point( aRect.Right()-nDelta, aRect.Top() ), aRect.BottomRight() ) ); 4965 setFillColor( rSettings.GetDarkShadowColor() ); 4966 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Left()+nDelta, aRect.Bottom() ) ) ); 4967 drawRectangle( Rectangle( aRect.TopLeft(), Point( aRect.Right(), aRect.Top()+nDelta ) ) ); 4968 } 4969 else 4970 { 4971 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetShadowColor() ) : Color( COL_TRANSPARENT ) ); 4972 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 4973 drawRectangle( rIntern.m_aRect ); 4974 } 4975 4976 if( rWidget.Border ) 4977 { 4978 // adjust edit area accounting for border 4979 sal_Int32 nDelta = aFont.GetHeight()/4; 4980 if( nDelta < 1 ) 4981 nDelta = 1; 4982 rIntern.m_aRect.Left() += nDelta; 4983 rIntern.m_aRect.Top() += nDelta; 4984 rIntern.m_aRect.Right() -= nDelta; 4985 rIntern.m_aRect.Bottom()-= nDelta; 4986 } 4987 } 4988 return aFont; 4989 } 4990 4991 void PDFWriterImpl::createDefaultEditAppearance( PDFWidget& rEdit, const PDFWriter::EditWidget& rWidget ) 4992 { 4993 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 4994 SvMemoryStream* pEditStream = new SvMemoryStream( 1024, 1024 ); 4995 4996 push( sal::static_int_cast<sal_uInt16>(~0U) ); 4997 4998 // prepare font to use, draw field border 4999 Font aFont = drawFieldBorder( rEdit, rWidget, rSettings ); 5000 sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont ); 5001 5002 // prepare DA string 5003 OStringBuffer aDA( 32 ); 5004 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA ); 5005 aDA.append( ' ' ); 5006 if( m_aContext.FieldsUseSystemFonts ) 5007 { 5008 aDA.append( "/F" ); 5009 aDA.append( nBest ); 5010 5011 OStringBuffer aDR( 32 ); 5012 aDR.append( "/Font " ); 5013 aDR.append( getFontDictObject() ); 5014 aDR.append( " 0 R" ); 5015 rEdit.m_aDRDict = aDR.makeStringAndClear(); 5016 } 5017 else 5018 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5019 aDA.append( ' ' ); 5020 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); 5021 aDA.append( " Tf" ); 5022 5023 /* create an empty appearance stream, let the viewer create 5024 the appearance at runtime. This is because AR5 seems to 5025 paint the widget appearance always, and a dynamically created 5026 appearance on top of it. AR6 is well behaved in that regard, so 5027 that behaviour seems to be a bug. Anyway this empty appearance 5028 relies on /NeedAppearances in the AcroForm dictionary set to "true" 5029 */ 5030 beginRedirect( pEditStream, rEdit.m_aRect ); 5031 OStringBuffer aAppearance( 32 ); 5032 aAppearance.append( "/Tx BMC\nEMC\n" ); 5033 writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); 5034 5035 endRedirect(); 5036 pop(); 5037 5038 rEdit.m_aAppearances[ "N" ][ "Standard" ] = pEditStream; 5039 5040 rEdit.m_aDAString = aDA.makeStringAndClear(); 5041 } 5042 5043 void PDFWriterImpl::createDefaultListBoxAppearance( PDFWidget& rBox, const PDFWriter::ListBoxWidget& rWidget ) 5044 { 5045 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 5046 SvMemoryStream* pListBoxStream = new SvMemoryStream( 1024, 1024 ); 5047 5048 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5049 5050 // prepare font to use, draw field border 5051 Font aFont = drawFieldBorder( rBox, rWidget, rSettings ); 5052 sal_Int32 nBest = m_aContext.FieldsUseSystemFonts ? getSystemFont( aFont ): getBestBuiltinFont( aFont ); 5053 5054 beginRedirect( pListBoxStream, rBox.m_aRect ); 5055 OStringBuffer aAppearance( 64 ); 5056 5057 #if 0 5058 if( ! rWidget.DropDown ) 5059 { 5060 // prepare linewidth for DA string hack, see below 5061 Size aFontSize = lcl_convert( m_aGraphicsStack.front().m_aMapMode, 5062 m_aMapMode, 5063 getReferenceDevice(), 5064 Size( 0, aFont.GetHeight() ) ); 5065 sal_Int32 nLW = aFontSize.Height() / 40; 5066 appendFixedInt( nLW > 0 ? nLW : 1, aAppearance ); 5067 aAppearance.append( " w\n" ); 5068 writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); 5069 aAppearance.setLength( 0 ); 5070 } 5071 #endif 5072 5073 setLineColor( Color( COL_TRANSPARENT ) ); 5074 setFillColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) ); 5075 drawRectangle( rBox.m_aRect ); 5076 5077 // empty appearance, see createDefaultEditAppearance for reference 5078 aAppearance.append( "/Tx BMC\nEMC\n" ); 5079 writeBuffer( aAppearance.getStr(), aAppearance.getLength() ); 5080 5081 endRedirect(); 5082 pop(); 5083 5084 rBox.m_aAppearances[ "N" ][ "Standard" ] = pListBoxStream; 5085 5086 // prepare DA string 5087 OStringBuffer aDA( 256 ); 5088 #if 0 5089 if( !rWidget.DropDown ) 5090 { 5091 /* another of AR5's peculiarities: the selected item of a choice 5092 field is highlighted using the non stroking color - same as the 5093 text color. so workaround that by using text rendering mode 2 5094 (fill, then stroke) and set the stroking color 5095 */ 5096 appendStrokingColor( replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ), aDA ); 5097 aDA.append( " 2 Tr " ); 5098 } 5099 #endif 5100 // prepare DA string 5101 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetFieldTextColor() ), aDA ); 5102 aDA.append( ' ' ); 5103 if( m_aContext.FieldsUseSystemFonts ) 5104 { 5105 aDA.append( "/F" ); 5106 aDA.append( nBest ); 5107 5108 OStringBuffer aDR( 32 ); 5109 aDR.append( "/Font " ); 5110 aDR.append( getFontDictObject() ); 5111 aDR.append( " 0 R" ); 5112 rBox.m_aDRDict = aDR.makeStringAndClear(); 5113 } 5114 else 5115 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5116 aDA.append( ' ' ); 5117 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aFont.GetHeight() ), aDA ); 5118 aDA.append( " Tf" ); 5119 rBox.m_aDAString = aDA.makeStringAndClear(); 5120 } 5121 5122 void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFWriter::CheckBoxWidget& rWidget ) 5123 { 5124 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 5125 5126 // save graphics state 5127 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5128 5129 if( rWidget.Background || rWidget.Border ) 5130 { 5131 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) ); 5132 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 5133 drawRectangle( rBox.m_aRect ); 5134 } 5135 5136 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() ); 5137 setFont( aFont ); 5138 Size aFontSize = aFont.GetSize(); 5139 if( aFontSize.Height() > rBox.m_aRect.GetHeight() ) 5140 aFontSize.Height() = rBox.m_aRect.GetHeight(); 5141 sal_Int32 nDelta = aFontSize.Height()/10; 5142 if( nDelta < 1 ) 5143 nDelta = 1; 5144 5145 Rectangle aCheckRect, aTextRect; 5146 if( rWidget.ButtonIsLeft ) 5147 { 5148 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta; 5149 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5150 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5151 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5152 5153 // #i74206# handle small controls without text area 5154 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5155 { 5156 aCheckRect.Right() -= nDelta; 5157 aCheckRect.Top() += nDelta/2; 5158 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5159 } 5160 5161 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta; 5162 aTextRect.Top() = rBox.m_aRect.Top(); 5163 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5164 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5165 } 5166 else 5167 { 5168 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height(); 5169 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5170 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5171 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5172 5173 // #i74206# handle small controls without text area 5174 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5175 { 5176 aCheckRect.Left() += nDelta; 5177 aCheckRect.Top() += nDelta/2; 5178 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5179 } 5180 5181 aTextRect.Left() = rBox.m_aRect.Left(); 5182 aTextRect.Top() = rBox.m_aRect.Top(); 5183 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5184 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5185 } 5186 setLineColor( Color( COL_BLACK ) ); 5187 setFillColor( Color( COL_TRANSPARENT ) ); 5188 OStringBuffer aLW( 32 ); 5189 aLW.append( "q " ); 5190 m_aPages[m_nCurrentPage].appendMappedLength( nDelta, aLW ); 5191 aLW.append( " w " ); 5192 writeBuffer( aLW.getStr(), aLW.getLength() ); 5193 drawRectangle( aCheckRect ); 5194 writeBuffer( " Q\n", 3 ); 5195 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 5196 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle ); 5197 5198 pop(); 5199 5200 OStringBuffer aDA( 256 ); 5201 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5202 sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) ); 5203 aDA.append( ' ' ); 5204 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5205 aDA.append( " 0 Tf" ); 5206 rBox.m_aDAString = aDA.makeStringAndClear(); 5207 rBox.m_aMKDict = "/CA"; 5208 rBox.m_aMKDictCAString = "8"; 5209 rBox.m_aRect = aCheckRect; 5210 5211 // create appearance streams 5212 sal_Char cMark = '8'; 5213 sal_Int32 nCharXOffset = 1000-m_aBuiltinFonts[13].m_aWidths[sal_Int32(cMark)]; 5214 nCharXOffset *= aCheckRect.GetHeight(); 5215 nCharXOffset /= 2000; 5216 sal_Int32 nCharYOffset = 1000- 5217 (m_aBuiltinFonts[13].m_nAscent+m_aBuiltinFonts[13].m_nDescent); // descent is negative 5218 nCharYOffset *= aCheckRect.GetHeight(); 5219 nCharYOffset /= 2000; 5220 5221 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 ); 5222 beginRedirect( pCheckStream, aCheckRect ); 5223 aDA.append( "/Tx BMC\nq BT\n" ); 5224 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5225 aDA.append( ' ' ); 5226 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5227 aDA.append( ' ' ); 5228 m_aPages[ m_nCurrentPage ].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA ); 5229 aDA.append( " Tf\n" ); 5230 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharXOffset, aDA ); 5231 aDA.append( " " ); 5232 m_aPages[ m_nCurrentPage ].appendMappedLength( nCharYOffset, aDA ); 5233 aDA.append( " Td (" ); 5234 aDA.append( cMark ); 5235 aDA.append( ") Tj\nET\nQ\nEMC\n" ); 5236 writeBuffer( aDA.getStr(), aDA.getLength() ); 5237 endRedirect(); 5238 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream; 5239 5240 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 ); 5241 beginRedirect( pUncheckStream, aCheckRect ); 5242 writeBuffer( "/Tx BMC\nEMC\n", 12 ); 5243 endRedirect(); 5244 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream; 5245 } 5246 5247 void PDFWriterImpl::createDefaultRadioButtonAppearance( PDFWidget& rBox, const PDFWriter::RadioButtonWidget& rWidget ) 5248 { 5249 const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings(); 5250 5251 // save graphics state 5252 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5253 5254 if( rWidget.Background || rWidget.Border ) 5255 { 5256 setLineColor( rWidget.Border ? replaceColor( rWidget.BorderColor, rSettings.GetCheckedColor() ) : Color( COL_TRANSPARENT ) ); 5257 setFillColor( rWidget.Background ? replaceColor( rWidget.BackgroundColor, rSettings.GetFieldColor() ) : Color( COL_TRANSPARENT ) ); 5258 drawRectangle( rBox.m_aRect ); 5259 } 5260 5261 Font aFont = replaceFont( rWidget.TextFont, rSettings.GetRadioCheckFont() ); 5262 setFont( aFont ); 5263 Size aFontSize = aFont.GetSize(); 5264 if( aFontSize.Height() > rBox.m_aRect.GetHeight() ) 5265 aFontSize.Height() = rBox.m_aRect.GetHeight(); 5266 sal_Int32 nDelta = aFontSize.Height()/10; 5267 if( nDelta < 1 ) 5268 nDelta = 1; 5269 5270 Rectangle aCheckRect, aTextRect; 5271 if( rWidget.ButtonIsLeft ) 5272 { 5273 aCheckRect.Left() = rBox.m_aRect.Left() + nDelta; 5274 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5275 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5276 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5277 5278 // #i74206# handle small controls without text area 5279 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5280 { 5281 aCheckRect.Right() -= nDelta; 5282 aCheckRect.Top() += nDelta/2; 5283 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5284 } 5285 5286 aTextRect.Left() = rBox.m_aRect.Left() + aCheckRect.GetWidth()+5*nDelta; 5287 aTextRect.Top() = rBox.m_aRect.Top(); 5288 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5289 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5290 } 5291 else 5292 { 5293 aCheckRect.Left() = rBox.m_aRect.Right() - nDelta - aFontSize.Height(); 5294 aCheckRect.Top() = rBox.m_aRect.Top() + (rBox.m_aRect.GetHeight()-aFontSize.Height())/2; 5295 aCheckRect.Right() = aCheckRect.Left() + aFontSize.Height(); 5296 aCheckRect.Bottom() = aCheckRect.Top() + aFontSize.Height(); 5297 5298 // #i74206# handle small controls without text area 5299 while( aCheckRect.GetWidth() > rBox.m_aRect.GetWidth() && aCheckRect.GetWidth() > nDelta ) 5300 { 5301 aCheckRect.Left() += nDelta; 5302 aCheckRect.Top() += nDelta/2; 5303 aCheckRect.Bottom() -= nDelta - (nDelta/2); 5304 } 5305 5306 aTextRect.Left() = rBox.m_aRect.Left(); 5307 aTextRect.Top() = rBox.m_aRect.Top(); 5308 aTextRect.Right() = aTextRect.Left() + rBox.m_aRect.GetWidth() - aCheckRect.GetWidth()-6*nDelta; 5309 aTextRect.Bottom() = rBox.m_aRect.Bottom(); 5310 } 5311 setLineColor( Color( COL_BLACK ) ); 5312 setFillColor( Color( COL_TRANSPARENT ) ); 5313 OStringBuffer aLW( 32 ); 5314 aLW.append( "q " ); 5315 m_aPages[ m_nCurrentPage ].appendMappedLength( nDelta, aLW ); 5316 aLW.append( " w " ); 5317 writeBuffer( aLW.getStr(), aLW.getLength() ); 5318 drawEllipse( aCheckRect ); 5319 writeBuffer( " Q\n", 3 ); 5320 setTextColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 5321 drawText( aTextRect, rBox.m_aText, rBox.m_nTextStyle ); 5322 5323 pop(); 5324 5325 OStringBuffer aDA( 256 ); 5326 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5327 sal_Int32 nBest = getBestBuiltinFont( Font( String( RTL_CONSTASCII_USTRINGPARAM( "ZapfDingbats" ) ), aFont.GetSize() ) ); 5328 aDA.append( ' ' ); 5329 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5330 aDA.append( " 0 Tf" ); 5331 rBox.m_aDAString = aDA.makeStringAndClear(); 5332 //to encrypt this (el) 5333 rBox.m_aMKDict = "/CA"; 5334 //after this assignement, to m_aMKDic cannot be added anything 5335 rBox.m_aMKDictCAString = "l"; 5336 5337 rBox.m_aRect = aCheckRect; 5338 5339 // create appearance streams 5340 push( sal::static_int_cast<sal_uInt16>(~0U) ); 5341 SvMemoryStream* pCheckStream = new SvMemoryStream( 256, 256 ); 5342 5343 beginRedirect( pCheckStream, aCheckRect ); 5344 aDA.append( "/Tx BMC\nq BT\n" ); 5345 appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA ); 5346 aDA.append( ' ' ); 5347 aDA.append( m_aBuiltinFonts[nBest].getNameObject() ); 5348 aDA.append( ' ' ); 5349 m_aPages[m_nCurrentPage].appendMappedLength( sal_Int32( aCheckRect.GetHeight() ), aDA ); 5350 aDA.append( " Tf\n0 0 Td\nET\nQ\n" ); 5351 writeBuffer( aDA.getStr(), aDA.getLength() ); 5352 setFillColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ) ); 5353 setLineColor( Color( COL_TRANSPARENT ) ); 5354 aCheckRect.Left() += 3*nDelta; 5355 aCheckRect.Top() += 3*nDelta; 5356 aCheckRect.Bottom() -= 3*nDelta; 5357 aCheckRect.Right() -= 3*nDelta; 5358 drawEllipse( aCheckRect ); 5359 writeBuffer( "\nEMC\n", 5 ); 5360 endRedirect(); 5361 5362 pop(); 5363 rBox.m_aAppearances[ "N" ][ "Yes" ] = pCheckStream; 5364 5365 SvMemoryStream* pUncheckStream = new SvMemoryStream( 256, 256 ); 5366 beginRedirect( pUncheckStream, aCheckRect ); 5367 writeBuffer( "/Tx BMC\nEMC\n", 12 ); 5368 endRedirect(); 5369 rBox.m_aAppearances[ "N" ][ "Off" ] = pUncheckStream; 5370 } 5371 5372 bool PDFWriterImpl::emitAppearances( PDFWidget& rWidget, OStringBuffer& rAnnotDict ) 5373 { 5374 5375 // TODO: check and insert default streams 5376 rtl::OString aStandardAppearance; 5377 switch( rWidget.m_eType ) 5378 { 5379 case PDFWriter::CheckBox: 5380 aStandardAppearance = OUStringToOString( rWidget.m_aValue, RTL_TEXTENCODING_ASCII_US ); 5381 break; 5382 default: 5383 break; 5384 } 5385 5386 if( rWidget.m_aAppearances.size() ) 5387 { 5388 rAnnotDict.append( "/AP<<\n" ); 5389 for( PDFAppearanceMap::iterator dict_it = rWidget.m_aAppearances.begin(); dict_it != rWidget.m_aAppearances.end(); ++dict_it ) 5390 { 5391 rAnnotDict.append( "/" ); 5392 rAnnotDict.append( dict_it->first ); 5393 bool bUseSubDict = (dict_it->second.size() > 1); 5394 rAnnotDict.append( bUseSubDict ? "<<" : " " ); 5395 5396 for( PDFAppearanceStreams::const_iterator stream_it = dict_it->second.begin(); 5397 stream_it != dict_it->second.end(); ++stream_it ) 5398 { 5399 SvMemoryStream* pApppearanceStream = stream_it->second; 5400 dict_it->second[ stream_it->first ] = NULL; 5401 5402 bool bDeflate = compressStream( pApppearanceStream ); 5403 5404 pApppearanceStream->Seek( STREAM_SEEK_TO_END ); 5405 sal_Int64 nStreamLen = pApppearanceStream->Tell(); 5406 pApppearanceStream->Seek( STREAM_SEEK_TO_BEGIN ); 5407 sal_Int32 nObject = createObject(); 5408 CHECK_RETURN( updateObject( nObject ) ); 5409 #if OSL_DEBUG_LEVEL > 1 5410 emitComment( "PDFWriterImpl::emitAppearances" ); 5411 #endif 5412 OStringBuffer aLine; 5413 aLine.append( nObject ); 5414 5415 aLine.append( " 0 obj\n" 5416 "<</Type/XObject\n" 5417 "/Subtype/Form\n" 5418 "/BBox[0 0 " ); 5419 appendFixedInt( rWidget.m_aRect.GetWidth()-1, aLine ); 5420 aLine.append( " " ); 5421 appendFixedInt( rWidget.m_aRect.GetHeight()-1, aLine ); 5422 aLine.append( "]\n" 5423 "/Resources " ); 5424 aLine.append( getResourceDictObj() ); 5425 aLine.append( " 0 R\n" 5426 "/Length " ); 5427 aLine.append( nStreamLen ); 5428 aLine.append( "\n" ); 5429 if( bDeflate ) 5430 aLine.append( "/Filter/FlateDecode\n" ); 5431 aLine.append( ">>\nstream\n" ); 5432 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5433 checkAndEnableStreamEncryption( nObject ); 5434 CHECK_RETURN( writeBuffer( pApppearanceStream->GetData(), nStreamLen ) ); 5435 disableStreamEncryption(); 5436 CHECK_RETURN( writeBuffer( "\nendstream\nendobj\n\n", 19 ) ); 5437 5438 if( bUseSubDict ) 5439 { 5440 rAnnotDict.append( " /" ); 5441 rAnnotDict.append( stream_it->first ); 5442 rAnnotDict.append( " " ); 5443 } 5444 rAnnotDict.append( nObject ); 5445 rAnnotDict.append( " 0 R" ); 5446 5447 delete pApppearanceStream; 5448 } 5449 5450 rAnnotDict.append( bUseSubDict ? ">>\n" : "\n" ); 5451 } 5452 rAnnotDict.append( ">>\n" ); 5453 if( aStandardAppearance.getLength() ) 5454 { 5455 rAnnotDict.append( "/AS /" ); 5456 rAnnotDict.append( aStandardAppearance ); 5457 rAnnotDict.append( "\n" ); 5458 } 5459 } 5460 5461 return true; 5462 } 5463 5464 bool PDFWriterImpl::emitWidgetAnnotations() 5465 { 5466 ensureUniqueRadioOnValues(); 5467 5468 int nAnnots = m_aWidgets.size(); 5469 for( int a = 0; a < nAnnots; a++ ) 5470 { 5471 PDFWidget& rWidget = m_aWidgets[a]; 5472 5473 OStringBuffer aLine( 1024 ); 5474 OStringBuffer aValue( 256 ); 5475 aLine.append( rWidget.m_nObject ); 5476 aLine.append( " 0 obj\n" 5477 "<<" ); 5478 if( rWidget.m_eType != PDFWriter::Hierarchy ) 5479 { 5480 // emit widget annotation only for terminal fields 5481 if( rWidget.m_aKids.empty() ) 5482 { 5483 aLine.append( "/Type/Annot/Subtype/Widget/F 4\n" 5484 "/Rect[" ); 5485 appendFixedInt( rWidget.m_aRect.Left()-1, aLine ); 5486 aLine.append( ' ' ); 5487 appendFixedInt( rWidget.m_aRect.Top()+1, aLine ); 5488 aLine.append( ' ' ); 5489 appendFixedInt( rWidget.m_aRect.Right()+1, aLine ); 5490 aLine.append( ' ' ); 5491 appendFixedInt( rWidget.m_aRect.Bottom()-1, aLine ); 5492 aLine.append( "]\n" ); 5493 } 5494 aLine.append( "/FT/" ); 5495 switch( rWidget.m_eType ) 5496 { 5497 case PDFWriter::RadioButton: 5498 case PDFWriter::CheckBox: 5499 // for radio buttons only the RadioButton field, not the 5500 // CheckBox children should have a value, else acrobat reader 5501 // does not always check the right button 5502 // of course real check boxes (not belonging to a readio group) 5503 // need their values, too 5504 if( rWidget.m_eType == PDFWriter::RadioButton || rWidget.m_nRadioGroup < 0 ) 5505 { 5506 aValue.append( "/" ); 5507 // check for radio group with all buttons unpressed 5508 if( rWidget.m_aValue.getLength() == 0 ) 5509 aValue.append( "Off" ); 5510 else 5511 appendName( rWidget.m_aValue, aValue ); 5512 } 5513 case PDFWriter::PushButton: 5514 aLine.append( "Btn" ); 5515 break; 5516 case PDFWriter::ListBox: 5517 if( rWidget.m_nFlags & 0x200000 ) // multiselect 5518 { 5519 aValue.append( "[" ); 5520 for( unsigned int i = 0; i < rWidget.m_aSelectedEntries.size(); i++ ) 5521 { 5522 sal_Int32 nEntry = rWidget.m_aSelectedEntries[i]; 5523 if( nEntry >= 0 && nEntry < sal_Int32(rWidget.m_aListEntries.size()) ) 5524 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ nEntry ], rWidget.m_nObject, aValue ); 5525 } 5526 aValue.append( "]" ); 5527 } 5528 else if( rWidget.m_aSelectedEntries.size() > 0 && 5529 rWidget.m_aSelectedEntries[0] >= 0 && 5530 rWidget.m_aSelectedEntries[0] < sal_Int32(rWidget.m_aListEntries.size()) ) 5531 { 5532 appendUnicodeTextStringEncrypt( rWidget.m_aListEntries[ rWidget.m_aSelectedEntries[0] ], rWidget.m_nObject, aValue ); 5533 } 5534 else 5535 appendUnicodeTextStringEncrypt( rtl::OUString(), rWidget.m_nObject, aValue ); 5536 aLine.append( "Ch" ); 5537 break; 5538 case PDFWriter::ComboBox: 5539 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue ); 5540 aLine.append( "Ch" ); 5541 break; 5542 case PDFWriter::Edit: 5543 aLine.append( "Tx" ); 5544 appendUnicodeTextStringEncrypt( rWidget.m_aValue, rWidget.m_nObject, aValue ); 5545 break; 5546 case PDFWriter::Hierarchy: // make the compiler happy 5547 break; 5548 } 5549 aLine.append( "\n" ); 5550 aLine.append( "/P " ); 5551 aLine.append( m_aPages[ rWidget.m_nPage ].m_nPageObject ); 5552 aLine.append( " 0 R\n" ); 5553 } 5554 if( rWidget.m_nParent ) 5555 { 5556 aLine.append( "/Parent " ); 5557 aLine.append( rWidget.m_nParent ); 5558 aLine.append( " 0 R\n" ); 5559 } 5560 if( rWidget.m_aKids.size() ) 5561 { 5562 aLine.append( "/Kids[" ); 5563 for( unsigned int i = 0; i < rWidget.m_aKids.size(); i++ ) 5564 { 5565 aLine.append( rWidget.m_aKids[i] ); 5566 aLine.append( " 0 R" ); 5567 aLine.append( ( (i&15) == 15 ) ? "\n" : " " ); 5568 } 5569 aLine.append( "]\n" ); 5570 } 5571 if( rWidget.m_aName.getLength() ) 5572 { 5573 aLine.append( "/T" ); 5574 appendLiteralStringEncrypt( rWidget.m_aName, rWidget.m_nObject, aLine ); 5575 aLine.append( "\n" ); 5576 } 5577 if( m_aContext.Version > PDFWriter::PDF_1_2 && rWidget.m_aDescription.getLength() ) 5578 { 5579 // the alternate field name should be unicode able since it is 5580 // supposed to be used in UI 5581 aLine.append( "/TU" ); 5582 appendUnicodeTextStringEncrypt( rWidget.m_aDescription, rWidget.m_nObject, aLine ); 5583 aLine.append( "\n" ); 5584 } 5585 5586 if( rWidget.m_nFlags ) 5587 { 5588 aLine.append( "/Ff " ); 5589 aLine.append( rWidget.m_nFlags ); 5590 aLine.append( "\n" ); 5591 } 5592 if( aValue.getLength() ) 5593 { 5594 OString aVal = aValue.makeStringAndClear(); 5595 aLine.append( "/V " ); 5596 aLine.append( aVal ); 5597 aLine.append( "\n" 5598 "/DV " ); 5599 aLine.append( aVal ); 5600 aLine.append( "\n" ); 5601 } 5602 if( rWidget.m_eType == PDFWriter::ListBox || rWidget.m_eType == PDFWriter::ComboBox ) 5603 { 5604 sal_Int32 nTI = -1; 5605 aLine.append( "/Opt[\n" ); 5606 sal_Int32 i = 0; 5607 for( std::vector< OUString >::const_iterator it = rWidget.m_aListEntries.begin(); it != rWidget.m_aListEntries.end(); ++it, ++i ) 5608 { 5609 appendUnicodeTextStringEncrypt( *it, rWidget.m_nObject, aLine ); 5610 aLine.append( "\n" ); 5611 if( *it == rWidget.m_aValue ) 5612 nTI = i; 5613 } 5614 aLine.append( "]\n" ); 5615 if( nTI > 0 ) 5616 { 5617 aLine.append( "/TI " ); 5618 aLine.append( nTI ); 5619 aLine.append( "\n" ); 5620 if( rWidget.m_nFlags & 0x200000 ) // Multiselect 5621 { 5622 aLine.append( "/I [" ); 5623 aLine.append( nTI ); 5624 aLine.append( "]\n" ); 5625 } 5626 } 5627 } 5628 if( rWidget.m_eType == PDFWriter::Edit && rWidget.m_nMaxLen > 0 ) 5629 { 5630 aLine.append( "/MaxLen " ); 5631 aLine.append( rWidget.m_nMaxLen ); 5632 aLine.append( "\n" ); 5633 } 5634 if( rWidget.m_eType == PDFWriter::PushButton ) 5635 { 5636 if(!m_bIsPDF_A1) 5637 { 5638 OStringBuffer aDest; 5639 if( rWidget.m_nDest != -1 && appendDest( m_aDestinationIdTranslation[ rWidget.m_nDest ], aDest ) ) 5640 { 5641 aLine.append( "/AA<</D<</Type/Action/S/GoTo/D " ); 5642 aLine.append( aDest.makeStringAndClear() ); 5643 aLine.append( ">>>>\n" ); 5644 } 5645 else if( rWidget.m_aListEntries.empty() ) 5646 { 5647 // create a reset form action 5648 aLine.append( "/AA<</D<</Type/Action/S/ResetForm>>>>\n" ); 5649 } 5650 else if( rWidget.m_bSubmit ) 5651 { 5652 // create a submit form action 5653 aLine.append( "/AA<</D<</Type/Action/S/SubmitForm/F" ); 5654 appendLiteralStringEncrypt( rWidget.m_aListEntries.front(), rWidget.m_nObject, aLine, osl_getThreadTextEncoding() ); 5655 aLine.append( "/Flags " ); 5656 5657 sal_Int32 nFlags = 0; 5658 switch( m_aContext.SubmitFormat ) 5659 { 5660 case PDFWriter::HTML: 5661 nFlags |= 4; 5662 break; 5663 case PDFWriter::XML: 5664 if( m_aContext.Version > PDFWriter::PDF_1_3 ) 5665 nFlags |= 32; 5666 break; 5667 case PDFWriter::PDF: 5668 if( m_aContext.Version > PDFWriter::PDF_1_3 ) 5669 nFlags |= 256; 5670 break; 5671 case PDFWriter::FDF: 5672 default: 5673 break; 5674 } 5675 if( rWidget.m_bSubmitGet ) 5676 nFlags |= 8; 5677 aLine.append( nFlags ); 5678 aLine.append( ">>>>\n" ); 5679 } 5680 else 5681 { 5682 // create a URI action 5683 aLine.append( "/AA<</D<</Type/Action/S/URI/URI(" ); 5684 aLine.append( OUStringToOString( rWidget.m_aListEntries.front(), RTL_TEXTENCODING_ASCII_US ) ); 5685 aLine.append( ")>>>>\n" ); 5686 } 5687 } 5688 else 5689 m_aErrors.insert( PDFWriter::Warning_FormAction_Omitted_PDFA ); 5690 } 5691 if( rWidget.m_aDAString.getLength() ) 5692 { 5693 if( rWidget.m_aDRDict.getLength() ) 5694 { 5695 aLine.append( "/DR<<" ); 5696 aLine.append( rWidget.m_aDRDict ); 5697 aLine.append( ">>\n" ); 5698 } 5699 else 5700 { 5701 aLine.append( "/DR<</Font<<" ); 5702 appendBuiltinFontsToDict( aLine ); 5703 aLine.append( ">>>>\n" ); 5704 } 5705 aLine.append( "/DA" ); 5706 appendLiteralStringEncrypt( rWidget.m_aDAString, rWidget.m_nObject, aLine ); 5707 aLine.append( "\n" ); 5708 if( rWidget.m_nTextStyle & TEXT_DRAW_CENTER ) 5709 aLine.append( "/Q 1\n" ); 5710 else if( rWidget.m_nTextStyle & TEXT_DRAW_RIGHT ) 5711 aLine.append( "/Q 2\n" ); 5712 } 5713 // appearance charactristics for terminal fields 5714 // which are supposed to have an appearance constructed 5715 // by the viewer application 5716 if( rWidget.m_aMKDict.getLength() ) 5717 { 5718 aLine.append( "/MK<<" ); 5719 aLine.append( rWidget.m_aMKDict ); 5720 //add the CA string, encrypting it 5721 appendLiteralStringEncrypt(rWidget.m_aMKDictCAString, rWidget.m_nObject, aLine); 5722 aLine.append( ">>\n" ); 5723 } 5724 5725 CHECK_RETURN( emitAppearances( rWidget, aLine ) ); 5726 5727 aLine.append( ">>\n" 5728 "endobj\n\n" ); 5729 CHECK_RETURN( updateObject( rWidget.m_nObject ) ); 5730 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5731 } 5732 return true; 5733 } 5734 5735 bool PDFWriterImpl::emitAnnotations() 5736 { 5737 if( m_aPages.size() < 1 ) 5738 return false; 5739 5740 CHECK_RETURN( emitLinkAnnotations() ); 5741 5742 CHECK_RETURN( emitNoteAnnotations() ); 5743 5744 CHECK_RETURN( emitWidgetAnnotations() ); 5745 5746 return true; 5747 } 5748 5749 #undef CHECK_RETURN 5750 #define CHECK_RETURN( x ) if( !x ) return false 5751 5752 bool PDFWriterImpl::emitCatalog() 5753 { 5754 // build page tree 5755 // currently there is only one node that contains all leaves 5756 5757 // first create a page tree node id 5758 sal_Int32 nTreeNode = createObject(); 5759 5760 // emit global resource dictionary (page emit needs it) 5761 CHECK_RETURN( emitResources() ); 5762 5763 // emit all pages 5764 for( std::vector<PDFPage>::iterator it = m_aPages.begin(); it != m_aPages.end(); ++it ) 5765 if( ! it->emit( nTreeNode ) ) 5766 return false; 5767 5768 sal_Int32 nNamedDestinationsDictionary = emitNamedDestinations(); 5769 5770 sal_Int32 nOutlineDict = emitOutline(); 5771 5772 //emit Output intent i59651 5773 sal_Int32 nOutputIntentObject = emitOutputIntent(); 5774 5775 //emit metadata 5776 sal_Int32 nMetadataObject = emitDocumentMetadata(); 5777 5778 sal_Int32 nStructureDict = 0; 5779 if(m_aStructure.size() > 1) 5780 { 5781 ///check if dummy structure containers are needed 5782 addInternalStructureContainer(m_aStructure[0]); 5783 nStructureDict = m_aStructure[0].m_nObject = createObject(); 5784 emitStructure( m_aStructure[ 0 ] ); 5785 } 5786 5787 // adjust tree node file offset 5788 if( ! updateObject( nTreeNode ) ) 5789 return false; 5790 5791 // emit tree node 5792 OStringBuffer aLine( 2048 ); 5793 aLine.append( nTreeNode ); 5794 aLine.append( " 0 obj\n" ); 5795 aLine.append( "<</Type/Pages\n" ); 5796 aLine.append( "/Resources " ); 5797 aLine.append( getResourceDictObj() ); 5798 aLine.append( " 0 R\n" ); 5799 5800 switch( m_eInheritedOrientation ) 5801 { 5802 case PDFWriter::Landscape: aLine.append( "/Rotate 90\n" );break; 5803 case PDFWriter::Seascape: aLine.append( "/Rotate -90\n" );break; 5804 5805 case PDFWriter::Inherit: // actually Inherit would be a bug, but insignificant 5806 case PDFWriter::Portrait: 5807 default: 5808 break; 5809 } 5810 sal_Int32 nMediaBoxWidth = 0; 5811 sal_Int32 nMediaBoxHeight = 0; 5812 if( m_aPages.empty() ) // sanity check, this should not happen 5813 { 5814 nMediaBoxWidth = m_nInheritedPageWidth; 5815 nMediaBoxHeight = m_nInheritedPageHeight; 5816 } 5817 else 5818 { 5819 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter ) 5820 { 5821 if( iter->m_nPageWidth > nMediaBoxWidth ) 5822 nMediaBoxWidth = iter->m_nPageWidth; 5823 if( iter->m_nPageHeight > nMediaBoxHeight ) 5824 nMediaBoxHeight = iter->m_nPageHeight; 5825 } 5826 } 5827 aLine.append( "/MediaBox[ 0 0 " ); 5828 aLine.append( nMediaBoxWidth ); 5829 aLine.append( ' ' ); 5830 aLine.append( nMediaBoxHeight ); 5831 aLine.append( " ]\n" 5832 "/Kids[ " ); 5833 unsigned int i = 0; 5834 for( std::vector<PDFPage>::const_iterator iter = m_aPages.begin(); iter != m_aPages.end(); ++iter, i++ ) 5835 { 5836 aLine.append( iter->m_nPageObject ); 5837 aLine.append( " 0 R" ); 5838 aLine.append( ( (i&15) == 15 ) ? "\n" : " " ); 5839 } 5840 aLine.append( "]\n" 5841 "/Count " ); 5842 aLine.append( (sal_Int32)m_aPages.size() ); 5843 aLine.append( ">>\n" 5844 "endobj\n\n" ); 5845 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 5846 5847 // emit annotation objects 5848 CHECK_RETURN( emitAnnotations() ); 5849 5850 // emit Catalog 5851 m_nCatalogObject = createObject(); 5852 if( ! updateObject( m_nCatalogObject ) ) 5853 return false; 5854 aLine.setLength( 0 ); 5855 aLine.append( m_nCatalogObject ); 5856 aLine.append( " 0 obj\n" 5857 "<</Type/Catalog/Pages " ); 5858 aLine.append( nTreeNode ); 5859 aLine.append( " 0 R\n" ); 5860 //--->i56629 5861 //check if there are named destinations to emit (root must be inside the catalog) 5862 if( nNamedDestinationsDictionary ) 5863 { 5864 aLine.append("/Dests "); 5865 aLine.append( nNamedDestinationsDictionary ); 5866 aLine.append( " 0 R\n" ); 5867 } 5868 //<---- 5869 if( m_aContext.PageLayout != PDFWriter::DefaultLayout ) 5870 switch( m_aContext.PageLayout ) 5871 { 5872 default : 5873 case PDFWriter::SinglePage : 5874 aLine.append( "/PageLayout/SinglePage\n" ); 5875 break; 5876 case PDFWriter::Continuous : 5877 aLine.append( "/PageLayout/OneColumn\n" ); 5878 break; 5879 case PDFWriter::ContinuousFacing : 5880 //the flag m_aContext.FirstPageLeft below is used to set the page on the left side 5881 aLine.append( "/PageLayout/TwoColumnRight\n" );//odd page on the right side 5882 break; 5883 } 5884 if( m_aContext.PDFDocumentMode != PDFWriter::ModeDefault && !m_aContext.OpenInFullScreenMode ) 5885 switch( m_aContext.PDFDocumentMode ) 5886 { 5887 default : 5888 aLine.append( "/PageMode/UseNone\n" ); 5889 break; 5890 case PDFWriter::UseOutlines : 5891 aLine.append( "/PageMode/UseOutlines\n" ); //document is opened with outline pane open 5892 break; 5893 case PDFWriter::UseThumbs : 5894 aLine.append( "/PageMode/UseThumbs\n" ); //document is opened with thumbnails pane open 5895 break; 5896 } 5897 else if( m_aContext.OpenInFullScreenMode ) 5898 aLine.append( "/PageMode/FullScreen\n" ); //document is opened full screen 5899 5900 OStringBuffer aInitPageRef; 5901 if( m_aContext.InitialPage >= 0 && m_aContext.InitialPage < (sal_Int32)m_aPages.size() ) 5902 { 5903 aInitPageRef.append( m_aPages[m_aContext.InitialPage].m_nPageObject ); 5904 aInitPageRef.append( " 0 R" ); 5905 } 5906 else 5907 aInitPageRef.append( "0" ); 5908 switch( m_aContext.PDFDocumentAction ) 5909 { 5910 case PDFWriter::ActionDefault : //do nothing, this is the Acrobat default 5911 default: 5912 if( aInitPageRef.getLength() > 1 ) 5913 { 5914 aLine.append( "/OpenAction[" ); 5915 aLine.append( aInitPageRef ); 5916 aLine.append( " /XYZ null null 0]\n" ); 5917 } 5918 break; 5919 case PDFWriter::FitInWindow : 5920 aLine.append( "/OpenAction[" ); 5921 aLine.append( aInitPageRef ); 5922 aLine.append( " /Fit]\n" ); //Open fit page 5923 break; 5924 case PDFWriter::FitWidth : 5925 aLine.append( "/OpenAction[" ); 5926 aLine.append( aInitPageRef ); 5927 aLine.append( " /FitH " ); 5928 aLine.append( m_nInheritedPageHeight );//Open fit width 5929 aLine.append( "]\n" ); 5930 break; 5931 case PDFWriter::FitVisible : 5932 aLine.append( "/OpenAction[" ); 5933 aLine.append( aInitPageRef ); 5934 aLine.append( " /FitBH " ); 5935 aLine.append( m_nInheritedPageHeight );//Open fit visible 5936 aLine.append( "]\n" ); 5937 break; 5938 case PDFWriter::ActionZoom : 5939 aLine.append( "/OpenAction[" ); 5940 aLine.append( aInitPageRef ); 5941 aLine.append( " /XYZ null null " ); 5942 if( m_aContext.Zoom >= 50 && m_aContext.Zoom <= 1600 ) 5943 aLine.append( (double)m_aContext.Zoom/100.0 ); 5944 else 5945 aLine.append( "0" ); 5946 aLine.append( "]\n" ); 5947 break; 5948 } 5949 // viewer preferences, if we had some, then emit 5950 if( m_aContext.HideViewerToolbar || 5951 ( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) || 5952 m_aContext.HideViewerMenubar || 5953 m_aContext.HideViewerWindowControls || m_aContext.FitWindow || 5954 m_aContext.CenterWindow || (m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) || 5955 m_aContext.OpenInFullScreenMode ) 5956 { 5957 aLine.append( "/ViewerPreferences<<" ); 5958 if( m_aContext.HideViewerToolbar ) 5959 aLine.append( "/HideToolbar true\n" ); 5960 if( m_aContext.HideViewerMenubar ) 5961 aLine.append( "/HideMenubar true\n" ); 5962 if( m_aContext.HideViewerWindowControls ) 5963 aLine.append( "/HideWindowUI true\n" ); 5964 if( m_aContext.FitWindow ) 5965 aLine.append( "/FitWindow true\n" ); 5966 if( m_aContext.CenterWindow ) 5967 aLine.append( "/CenterWindow true\n" ); 5968 if( m_aContext.Version > PDFWriter::PDF_1_3 && m_aContext.DocumentInfo.Title.Len() && m_aContext.DisplayPDFDocumentTitle ) 5969 aLine.append( "/DisplayDocTitle true\n" ); 5970 if( m_aContext.FirstPageLeft && m_aContext.PageLayout == PDFWriter::ContinuousFacing ) 5971 aLine.append( "/Direction/R2L\n" ); 5972 if( m_aContext.OpenInFullScreenMode ) 5973 switch( m_aContext.PDFDocumentMode ) 5974 { 5975 default : 5976 case PDFWriter::ModeDefault : 5977 aLine.append( "/NonFullScreenPageMode/UseNone\n" ); 5978 break; 5979 case PDFWriter::UseOutlines : 5980 aLine.append( "/NonFullScreenPageMode/UseOutlines\n" ); 5981 break; 5982 case PDFWriter::UseThumbs : 5983 aLine.append( "/NonFullScreenPageMode/UseThumbs\n" ); 5984 break; 5985 } 5986 aLine.append( ">>\n" ); 5987 } 5988 5989 if( nOutlineDict ) 5990 { 5991 aLine.append( "/Outlines " ); 5992 aLine.append( nOutlineDict ); 5993 aLine.append( " 0 R\n" ); 5994 } 5995 if( nStructureDict ) 5996 { 5997 aLine.append( "/StructTreeRoot " ); 5998 aLine.append( nStructureDict ); 5999 aLine.append( " 0 R\n" ); 6000 } 6001 if( m_aContext.DocumentLocale.Language.getLength() > 0 ) 6002 { 6003 OUStringBuffer aLocBuf( 16 ); 6004 aLocBuf.append( m_aContext.DocumentLocale.Language.toAsciiLowerCase() ); 6005 if( m_aContext.DocumentLocale.Country.getLength() > 0 ) 6006 { 6007 aLocBuf.append( sal_Unicode('-') ); 6008 aLocBuf.append( m_aContext.DocumentLocale.Country ); 6009 } 6010 aLine.append( "/Lang" ); 6011 appendLiteralStringEncrypt( aLocBuf.makeStringAndClear(), m_nCatalogObject, aLine ); 6012 aLine.append( "\n" ); 6013 } 6014 if( m_aContext.Tagged && m_aContext.Version > PDFWriter::PDF_1_3 ) 6015 { 6016 aLine.append( "/MarkInfo<</Marked true>>\n" ); 6017 } 6018 if( m_aWidgets.size() > 0 ) 6019 { 6020 aLine.append( "/AcroForm<</Fields[\n" ); 6021 int nWidgets = m_aWidgets.size(); 6022 int nOut = 0; 6023 for( int j = 0; j < nWidgets; j++ ) 6024 { 6025 // output only root fields 6026 if( m_aWidgets[j].m_nParent < 1 ) 6027 { 6028 aLine.append( m_aWidgets[j].m_nObject ); 6029 aLine.append( (nOut++ % 5)==4 ? " 0 R\n" : " 0 R " ); 6030 } 6031 } 6032 aLine.append( "\n]/DR " ); 6033 aLine.append( getResourceDictObj() ); 6034 aLine.append( " 0 R" ); 6035 if( m_bIsPDF_A1 ) 6036 aLine.append( ">>\n" ); 6037 else 6038 aLine.append( "/NeedAppearances true>>\n" ); 6039 } 6040 //--->i59651 6041 //check if there is a Metadata object 6042 if( nOutputIntentObject ) 6043 { 6044 aLine.append("/OutputIntents["); 6045 aLine.append( nOutputIntentObject ); 6046 aLine.append( " 0 R]" ); 6047 } 6048 if( nMetadataObject ) 6049 { 6050 aLine.append("/Metadata "); 6051 aLine.append( nMetadataObject ); 6052 aLine.append( " 0 R" ); 6053 } 6054 //<---- 6055 aLine.append( ">>\n" 6056 "endobj\n\n" ); 6057 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6058 6059 return true; 6060 } 6061 6062 sal_Int32 PDFWriterImpl::emitInfoDict( ) 6063 { 6064 sal_Int32 nObject = createObject(); 6065 6066 if( updateObject( nObject ) ) 6067 { 6068 OStringBuffer aLine( 1024 ); 6069 aLine.append( nObject ); 6070 aLine.append( " 0 obj\n" 6071 "<<" ); 6072 if( m_aContext.DocumentInfo.Title.Len() ) 6073 { 6074 aLine.append( "/Title" ); 6075 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Title, nObject, aLine ); 6076 aLine.append( "\n" ); 6077 } 6078 if( m_aContext.DocumentInfo.Author.Len() ) 6079 { 6080 aLine.append( "/Author" ); 6081 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Author, nObject, aLine ); 6082 aLine.append( "\n" ); 6083 } 6084 if( m_aContext.DocumentInfo.Subject.Len() ) 6085 { 6086 aLine.append( "/Subject" ); 6087 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Subject, nObject, aLine ); 6088 aLine.append( "\n" ); 6089 } 6090 if( m_aContext.DocumentInfo.Keywords.Len() ) 6091 { 6092 aLine.append( "/Keywords" ); 6093 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Keywords, nObject, aLine ); 6094 aLine.append( "\n" ); 6095 } 6096 if( m_aContext.DocumentInfo.Creator.Len() ) 6097 { 6098 aLine.append( "/Creator" ); 6099 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Creator, nObject, aLine ); 6100 aLine.append( "\n" ); 6101 } 6102 if( m_aContext.DocumentInfo.Producer.Len() ) 6103 { 6104 aLine.append( "/Producer" ); 6105 appendUnicodeTextStringEncrypt( m_aContext.DocumentInfo.Producer, nObject, aLine ); 6106 aLine.append( "\n" ); 6107 } 6108 6109 aLine.append( "/CreationDate" ); 6110 appendLiteralStringEncrypt( m_aCreationDateString, nObject, aLine ); 6111 aLine.append( ">>\nendobj\n\n" ); 6112 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6113 nObject = 0; 6114 } 6115 else 6116 nObject = 0; 6117 6118 return nObject; 6119 } 6120 6121 //--->i56629 6122 // Part of this function may be shared with method appendDest. 6123 // 6124 sal_Int32 PDFWriterImpl::emitNamedDestinations() 6125 { 6126 sal_Int32 nCount = m_aNamedDests.size(); 6127 if( nCount <= 0 ) 6128 return 0;//define internal error 6129 6130 //get the object number for all the destinations 6131 sal_Int32 nObject = createObject(); 6132 6133 if( updateObject( nObject ) ) 6134 { 6135 //emit the dictionary 6136 OStringBuffer aLine( 1024 ); 6137 aLine.append( nObject ); 6138 aLine.append( " 0 obj\n" 6139 "<<" ); 6140 6141 sal_Int32 nDestID; 6142 for( nDestID = 0; nDestID < nCount; nDestID++ ) 6143 { 6144 const PDFNamedDest& rDest = m_aNamedDests[ nDestID ]; 6145 // In order to correctly function both under an Internet browser and 6146 // directly with a reader (provided the reader has the feature) we 6147 // need to set the name of the destination the same way it will be encoded 6148 // in an Internet link 6149 INetURLObject aLocalURL( 6150 OUString( RTL_CONSTASCII_USTRINGPARAM( "http://ahost.ax" ) ) ); //dummy location, won't be used 6151 aLocalURL.SetMark( rDest.m_aDestName ); 6152 6153 const rtl::OUString aName = aLocalURL.GetMark( INetURLObject::NO_DECODE ); //same coding as 6154 // in link creation ( see PDFWriterImpl::emitLinkAnnotations ) 6155 const PDFPage& rDestPage = m_aPages[ rDest.m_nPage ]; 6156 6157 aLine.append( '/' ); 6158 appendDestinationName( aName, aLine ); // this conversion must be done when forming the link to target ( see in emitCatalog ) 6159 aLine.append( '[' ); // the '[' can be emitted immediately, because the appendDestinationName function 6160 //maps the preceeding character properly 6161 aLine.append( rDestPage.m_nPageObject ); 6162 aLine.append( " 0 R" ); 6163 6164 switch( rDest.m_eType ) 6165 { 6166 case PDFWriter::XYZ: 6167 default: 6168 aLine.append( "/XYZ " ); 6169 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6170 aLine.append( ' ' ); 6171 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6172 aLine.append( " 0" ); 6173 break; 6174 case PDFWriter::Fit: 6175 aLine.append( "/Fit" ); 6176 break; 6177 case PDFWriter::FitRectangle: 6178 aLine.append( "/FitR " ); 6179 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6180 aLine.append( ' ' ); 6181 appendFixedInt( rDest.m_aRect.Top(), aLine ); 6182 aLine.append( ' ' ); 6183 appendFixedInt( rDest.m_aRect.Right(), aLine ); 6184 aLine.append( ' ' ); 6185 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6186 break; 6187 case PDFWriter::FitHorizontal: 6188 aLine.append( "/FitH " ); 6189 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6190 break; 6191 case PDFWriter::FitVertical: 6192 aLine.append( "/FitV " ); 6193 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6194 break; 6195 case PDFWriter::FitPageBoundingBox: 6196 aLine.append( "/FitB" ); 6197 break; 6198 case PDFWriter::FitPageBoundingBoxHorizontal: 6199 aLine.append( "/FitBH " ); 6200 appendFixedInt( rDest.m_aRect.Bottom(), aLine ); 6201 break; 6202 case PDFWriter::FitPageBoundingBoxVertical: 6203 aLine.append( "/FitBV " ); 6204 appendFixedInt( rDest.m_aRect.Left(), aLine ); 6205 break; 6206 } 6207 aLine.append( "]\n" ); 6208 } 6209 //close 6210 6211 aLine.append( ">>\nendobj\n\n" ); 6212 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6213 nObject = 0; 6214 } 6215 else 6216 nObject = 0; 6217 6218 return nObject; 6219 } 6220 //<--- i56629 6221 6222 //--->i59651 6223 // emits the output intent dictionary 6224 6225 sal_Int32 PDFWriterImpl::emitOutputIntent() 6226 { 6227 if( !m_bIsPDF_A1 ) 6228 return 0; 6229 6230 //emit the sRGB standard profile, in ICC format, in a stream, per IEC61966-2.1 6231 6232 OStringBuffer aLine( 1024 ); 6233 sal_Int32 nICCObject = createObject(); 6234 sal_Int32 nStreamLengthObject = createObject(); 6235 6236 aLine.append( nICCObject ); 6237 // sRGB has 3 colors, hence /N 3 below (PDF 1.4 table 4.16) 6238 aLine.append( " 0 obj\n<</N 3/Length " ); 6239 aLine.append( nStreamLengthObject ); 6240 aLine.append( " 0 R" ); 6241 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 6242 aLine.append( "/Filter/FlateDecode" ); 6243 #endif 6244 aLine.append( ">>\nstream\n" ); 6245 CHECK_RETURN( updateObject( nICCObject ) ); 6246 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6247 //get file position 6248 sal_uInt64 nBeginStreamPos = 0; 6249 osl_getFilePos( m_aFile, &nBeginStreamPos ); 6250 beginCompression(); 6251 checkAndEnableStreamEncryption( nICCObject ); 6252 sal_Int32 nStreamSize = writeBuffer( nsRGB_ICC_profile, (sal_Int32) sizeof( nsRGB_ICC_profile ) ); 6253 disableStreamEncryption(); 6254 endCompression(); 6255 sal_uInt64 nEndStreamPos = 0; 6256 osl_getFilePos( m_aFile, &nEndStreamPos ); 6257 6258 if( nStreamSize == 0 ) 6259 return 0; 6260 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) 6261 return 0 ; 6262 aLine.setLength( 0 ); 6263 6264 //emit the stream length object 6265 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 6266 aLine.setLength( 0 ); 6267 aLine.append( nStreamLengthObject ); 6268 aLine.append( " 0 obj\n" ); 6269 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) ); 6270 aLine.append( "\nendobj\n\n" ); 6271 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6272 aLine.setLength( 0 ); 6273 6274 //emit the OutputIntent dictionary 6275 sal_Int32 nOIObject = createObject(); 6276 CHECK_RETURN( updateObject( nOIObject ) ); 6277 aLine.append( nOIObject ); 6278 aLine.append( " 0 obj\n" 6279 "<</Type/OutputIntent/S/GTS_PDFA1/OutputConditionIdentifier"); 6280 6281 rtl::OUString aComment( RTL_CONSTASCII_USTRINGPARAM( "sRGB IEC61966-2.1" ) ); 6282 appendLiteralStringEncrypt( aComment ,nOIObject, aLine ); 6283 aLine.append("/DestOutputProfile "); 6284 aLine.append( nICCObject ); 6285 aLine.append( " 0 R>>\nendobj\n\n" );; 6286 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6287 6288 return nOIObject; 6289 } 6290 6291 // formats the string for the XML stream 6292 static void escapeStringXML( const rtl::OUString& rStr, rtl::OUString &rValue) 6293 { 6294 const sal_Unicode* pUni = rStr.getStr(); 6295 int nLen = rStr.getLength(); 6296 for( ; nLen; nLen--, pUni++ ) 6297 { 6298 switch( *pUni ) 6299 { 6300 case sal_Unicode('&'): 6301 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "&" ) ); 6302 break; 6303 case sal_Unicode('<'): 6304 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "<" ) ); 6305 break; 6306 case sal_Unicode('>'): 6307 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ">" ) ); 6308 break; 6309 case sal_Unicode('\''): 6310 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "'" ) ); 6311 break; 6312 case sal_Unicode('"'): 6313 rValue += rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( """ ) ); 6314 break; 6315 default: 6316 rValue += rtl::OUString( *pUni ); 6317 break; 6318 } 6319 } 6320 } 6321 6322 // emits the document metadata 6323 // 6324 sal_Int32 PDFWriterImpl::emitDocumentMetadata() 6325 { 6326 if( !m_bIsPDF_A1 ) 6327 return 0; 6328 6329 //get the object number for all the destinations 6330 sal_Int32 nObject = createObject(); 6331 6332 if( updateObject( nObject ) ) 6333 { 6334 // the following string are written in UTF-8 unicode 6335 OStringBuffer aMetadataStream( 8192 ); 6336 6337 aMetadataStream.append( "<?xpacket begin=\"" ); 6338 // this lines writes Unicode “zero width non-breaking space character” (U+FEFF) (aka byte-order mark ) used 6339 // as a byte-order marker. 6340 aMetadataStream.append( OUStringToOString( OUString( sal_Unicode( 0xFEFF ) ), RTL_TEXTENCODING_UTF8 ) ); 6341 aMetadataStream.append( "\" id=\"W5M0MpCehiHzreSzNTczkc9d\"?>\n" ); 6342 aMetadataStream.append( "<x:xmpmeta xmlns:x=\"adobe:ns:meta/\">\n" ); 6343 aMetadataStream.append( " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n" ); 6344 //PDF/A part ( ISO 19005-1:2005 - 6.7.11 ) 6345 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6346 aMetadataStream.append( " xmlns:pdfaid=\"http://www.aiim.org/pdfa/ns/id/\">\n" ); 6347 aMetadataStream.append( " <pdfaid:part>1</pdfaid:part>\n" ); 6348 aMetadataStream.append( " <pdfaid:conformance>A</pdfaid:conformance>\n" ); 6349 aMetadataStream.append( " </rdf:Description>\n" ); 6350 //... Dublin Core properties go here 6351 if( m_aContext.DocumentInfo.Title.Len() || 6352 m_aContext.DocumentInfo.Author.Len() || 6353 m_aContext.DocumentInfo.Subject.Len() ) 6354 { 6355 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6356 aMetadataStream.append( " xmlns:dc=\"http://purl.org/dc/elements/1.1/\">\n" ); 6357 if( m_aContext.DocumentInfo.Title.Len() ) 6358 { 6359 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) 6360 aMetadataStream.append( " <dc:title>\n" ); 6361 aMetadataStream.append( " <rdf:Alt>\n" ); 6362 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); 6363 rtl::OUString aTitle; 6364 escapeStringXML( m_aContext.DocumentInfo.Title, aTitle ); 6365 aMetadataStream.append( OUStringToOString( aTitle, RTL_TEXTENCODING_UTF8 ) ); 6366 aMetadataStream.append( "</rdf:li>\n" ); 6367 aMetadataStream.append( " </rdf:Alt>\n" ); 6368 aMetadataStream.append( " </dc:title>\n" ); 6369 } 6370 if( m_aContext.DocumentInfo.Author.Len() ) 6371 { 6372 aMetadataStream.append( " <dc:creator>\n" ); 6373 aMetadataStream.append( " <rdf:Seq>\n" ); 6374 aMetadataStream.append( " <rdf:li>" ); 6375 rtl::OUString aAuthor; 6376 escapeStringXML( m_aContext.DocumentInfo.Author, aAuthor ); 6377 aMetadataStream.append( OUStringToOString( aAuthor , RTL_TEXTENCODING_UTF8 ) ); 6378 aMetadataStream.append( "</rdf:li>\n" ); 6379 aMetadataStream.append( " </rdf:Seq>\n" ); 6380 aMetadataStream.append( " </dc:creator>\n" ); 6381 } 6382 if( m_aContext.DocumentInfo.Subject.Len() ) 6383 { 6384 // this is according to PDF/A-1, technical corrigendum 1 (2007-04-01) 6385 aMetadataStream.append( " <dc:description>\n" ); 6386 aMetadataStream.append( " <rdf:Alt>\n" ); 6387 aMetadataStream.append( " <rdf:li xml:lang=\"x-default\">" ); 6388 rtl::OUString aSubject; 6389 escapeStringXML( m_aContext.DocumentInfo.Subject, aSubject ); 6390 aMetadataStream.append( OUStringToOString( aSubject , RTL_TEXTENCODING_UTF8 ) ); 6391 aMetadataStream.append( "</rdf:li>\n" ); 6392 aMetadataStream.append( " </rdf:Alt>\n" ); 6393 aMetadataStream.append( " </dc:description>\n" ); 6394 } 6395 aMetadataStream.append( " </rdf:Description>\n" ); 6396 } 6397 6398 //... PDF properties go here 6399 if( m_aContext.DocumentInfo.Producer.Len() || 6400 m_aContext.DocumentInfo.Keywords.Len() ) 6401 { 6402 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6403 aMetadataStream.append( " xmlns:pdf=\"http://ns.adobe.com/pdf/1.3/\">\n" ); 6404 if( m_aContext.DocumentInfo.Producer.Len() ) 6405 { 6406 aMetadataStream.append( " <pdf:Producer>" ); 6407 rtl::OUString aProducer; 6408 escapeStringXML( m_aContext.DocumentInfo.Producer, aProducer ); 6409 aMetadataStream.append( OUStringToOString( aProducer , RTL_TEXTENCODING_UTF8 ) ); 6410 aMetadataStream.append( "</pdf:Producer>\n" ); 6411 } 6412 if( m_aContext.DocumentInfo.Keywords.Len() ) 6413 { 6414 aMetadataStream.append( " <pdf:Keywords>" ); 6415 rtl::OUString aKeywords; 6416 escapeStringXML( m_aContext.DocumentInfo.Keywords, aKeywords ); 6417 aMetadataStream.append( OUStringToOString( aKeywords , RTL_TEXTENCODING_UTF8 ) ); 6418 aMetadataStream.append( "</pdf:Keywords>\n" ); 6419 } 6420 aMetadataStream.append( " </rdf:Description>\n" ); 6421 } 6422 6423 aMetadataStream.append( " <rdf:Description rdf:about=\"\"\n" ); 6424 aMetadataStream.append( " xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\">\n" ); 6425 if( m_aContext.DocumentInfo.Creator.Len() ) 6426 { 6427 aMetadataStream.append( " <xmp:CreatorTool>" ); 6428 rtl::OUString aCreator; 6429 escapeStringXML( m_aContext.DocumentInfo.Creator, aCreator ); 6430 aMetadataStream.append( OUStringToOString( aCreator , RTL_TEXTENCODING_UTF8 ) ); 6431 aMetadataStream.append( "</xmp:CreatorTool>\n" ); 6432 } 6433 //creation date 6434 aMetadataStream.append( " <xmp:CreateDate>" ); 6435 aMetadataStream.append( m_aCreationMetaDateString ); 6436 aMetadataStream.append( "</xmp:CreateDate>\n" ); 6437 6438 aMetadataStream.append( " </rdf:Description>\n" ); 6439 aMetadataStream.append( " </rdf:RDF>\n" ); 6440 aMetadataStream.append( "</x:xmpmeta>\n" ); 6441 6442 //add the padding 6443 for( sal_Int32 nSpaces = 1; nSpaces <= 2100; nSpaces++ ) 6444 { 6445 aMetadataStream.append( " " ); 6446 if( nSpaces % 100 == 0 ) 6447 aMetadataStream.append( "\n" ); 6448 } 6449 6450 aMetadataStream.append( "<?xpacket end=\"w\"?>\n" ); 6451 6452 OStringBuffer aMetadataObj( 1024 ); 6453 6454 aMetadataObj.append( nObject ); 6455 aMetadataObj.append( " 0 obj\n" ); 6456 6457 aMetadataObj.append( "<</Type/Metadata/Subtype/XML/Length " ); 6458 6459 aMetadataObj.append( (sal_Int32) aMetadataStream.getLength() ); 6460 aMetadataObj.append( ">>\nstream\n" ); 6461 CHECK_RETURN( writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) ); 6462 //emit the stream 6463 CHECK_RETURN( writeBuffer( aMetadataStream.getStr(), aMetadataStream.getLength() ) ); 6464 6465 aMetadataObj.setLength( 0 ); 6466 aMetadataObj.append( "\nendstream\nendobj\n\n" ); 6467 if( ! writeBuffer( aMetadataObj.getStr(), aMetadataObj.getLength() ) ) 6468 nObject = 0; 6469 } 6470 else 6471 nObject = 0; 6472 6473 return nObject; 6474 } 6475 //<---i59651 6476 6477 bool PDFWriterImpl::emitTrailer() 6478 { 6479 // emit doc info 6480 OString aInfoValuesOut; 6481 sal_Int32 nDocInfoObject = emitInfoDict( ); 6482 6483 sal_Int32 nSecObject = 0; 6484 6485 if( m_aContext.Encryption.Encrypt() ) 6486 { 6487 //emit the security information 6488 //must be emitted as indirect dictionary object, since 6489 //Acrobat Reader 5 works only with this kind of implementation 6490 nSecObject = createObject(); 6491 6492 if( updateObject( nSecObject ) ) 6493 { 6494 OStringBuffer aLineS( 1024 ); 6495 aLineS.append( nSecObject ); 6496 aLineS.append( " 0 obj\n" 6497 "<</Filter/Standard/V " ); 6498 // check the version 6499 if( m_aContext.Encryption.Security128bit ) 6500 aLineS.append( "2/Length 128/R 3" ); 6501 else 6502 aLineS.append( "1/R 2" ); 6503 6504 // emit the owner password, must not be encrypted 6505 aLineS.append( "/O(" ); 6506 appendLiteralString( (const sal_Char*)&m_aContext.Encryption.OValue[0], sal_Int32(m_aContext.Encryption.OValue.size()), aLineS ); 6507 aLineS.append( ")/U(" ); 6508 appendLiteralString( (const sal_Char*)&m_aContext.Encryption.UValue[0], sal_Int32(m_aContext.Encryption.UValue.size()), aLineS ); 6509 aLineS.append( ")/P " );// the permission set 6510 aLineS.append( m_nAccessPermissions ); 6511 aLineS.append( ">>\nendobj\n\n" ); 6512 if( !writeBuffer( aLineS.getStr(), aLineS.getLength() ) ) 6513 nSecObject = 0; 6514 } 6515 else 6516 nSecObject = 0; 6517 } 6518 // emit xref table 6519 // remember start 6520 sal_uInt64 nXRefOffset = 0; 6521 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nXRefOffset )) ); 6522 CHECK_RETURN( writeBuffer( "xref\n", 5 ) ); 6523 6524 sal_Int32 nObjects = m_aObjects.size(); 6525 OStringBuffer aLine; 6526 aLine.append( "0 " ); 6527 aLine.append( (sal_Int32)(nObjects+1) ); 6528 aLine.append( "\n" ); 6529 aLine.append( "0000000000 65535 f \n" ); 6530 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6531 6532 for( sal_Int32 i = 0; i < nObjects; i++ ) 6533 { 6534 aLine.setLength( 0 ); 6535 OString aOffset = OString::valueOf( (sal_Int64)m_aObjects[i] ); 6536 for( sal_Int32 j = 0; j < (10-aOffset.getLength()); j++ ) 6537 aLine.append( '0' ); 6538 aLine.append( aOffset ); 6539 aLine.append( " 00000 n \n" ); 6540 DBG_ASSERT( aLine.getLength() == 20, "invalid xref entry" ); 6541 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6542 } 6543 6544 // prepare document checksum 6545 OStringBuffer aDocChecksum( 2*RTL_DIGEST_LENGTH_MD5+1 ); 6546 if( m_aDocDigest ) 6547 { 6548 sal_uInt8 nMD5Sum[ RTL_DIGEST_LENGTH_MD5 ]; 6549 rtl_digest_getMD5( m_aDocDigest, nMD5Sum, sizeof(nMD5Sum) ); 6550 for( unsigned int i = 0; i < RTL_DIGEST_LENGTH_MD5; i++ ) 6551 appendHex( nMD5Sum[i], aDocChecksum ); 6552 } 6553 // document id set in setDocInfo method 6554 // emit trailer 6555 aLine.setLength( 0 ); 6556 aLine.append( "trailer\n" 6557 "<</Size " ); 6558 aLine.append( (sal_Int32)(nObjects+1) ); 6559 aLine.append( "/Root " ); 6560 aLine.append( m_nCatalogObject ); 6561 aLine.append( " 0 R\n" ); 6562 if( nSecObject |= 0 ) 6563 { 6564 aLine.append( "/Encrypt "); 6565 aLine.append( nSecObject ); 6566 aLine.append( " 0 R\n" ); 6567 } 6568 if( nDocInfoObject ) 6569 { 6570 aLine.append( "/Info " ); 6571 aLine.append( nDocInfoObject ); 6572 aLine.append( " 0 R\n" ); 6573 } 6574 if( ! m_aContext.Encryption.DocumentIdentifier.empty() ) 6575 { 6576 aLine.append( "/ID [ <" ); 6577 for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); 6578 it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) 6579 { 6580 appendHex( sal_Int8(*it), aLine ); 6581 } 6582 aLine.append( ">\n" 6583 "<" ); 6584 for( std::vector< sal_uInt8 >::const_iterator it = m_aContext.Encryption.DocumentIdentifier.begin(); 6585 it != m_aContext.Encryption.DocumentIdentifier.end(); ++it ) 6586 { 6587 appendHex( sal_Int8(*it), aLine ); 6588 } 6589 aLine.append( "> ]\n" ); 6590 } 6591 if( aDocChecksum.getLength() ) 6592 { 6593 aLine.append( "/DocChecksum /" ); 6594 aLine.append( aDocChecksum ); 6595 aLine.append( "\n" ); 6596 } 6597 if( m_aAdditionalStreams.size() > 0 ) 6598 { 6599 aLine.append( "/AdditionalStreams [" ); 6600 for( unsigned int i = 0; i < m_aAdditionalStreams.size(); i++ ) 6601 { 6602 aLine.append( "/" ); 6603 appendName( m_aAdditionalStreams[i].m_aMimeType, aLine ); 6604 aLine.append( " " ); 6605 aLine.append( m_aAdditionalStreams[i].m_nStreamObject ); 6606 aLine.append( " 0 R\n" ); 6607 } 6608 aLine.append( "]\n" ); 6609 } 6610 aLine.append( ">>\n" 6611 "startxref\n" ); 6612 aLine.append( (sal_Int64)nXRefOffset ); 6613 aLine.append( "\n" 6614 "%%EOF\n" ); 6615 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 6616 6617 return true; 6618 } 6619 6620 struct AnnotationSortEntry 6621 { 6622 sal_Int32 nTabOrder; 6623 sal_Int32 nObject; 6624 sal_Int32 nWidgetIndex; 6625 6626 AnnotationSortEntry( sal_Int32 nTab, sal_Int32 nObj, sal_Int32 nI ) : 6627 nTabOrder( nTab ), 6628 nObject( nObj ), 6629 nWidgetIndex( nI ) 6630 {} 6631 }; 6632 6633 struct AnnotSortContainer 6634 { 6635 std::set< sal_Int32 > aObjects; 6636 std::vector< AnnotationSortEntry > aSortedAnnots; 6637 }; 6638 6639 struct AnnotSorterLess 6640 { 6641 std::vector< PDFWriterImpl::PDFWidget >& m_rWidgets; 6642 6643 AnnotSorterLess( std::vector< PDFWriterImpl::PDFWidget >& rWidgets ) : m_rWidgets( rWidgets ) {} 6644 6645 bool operator()( const AnnotationSortEntry& rLeft, const AnnotationSortEntry& rRight ) 6646 { 6647 if( rLeft.nTabOrder < rRight.nTabOrder ) 6648 return true; 6649 if( rRight.nTabOrder < rLeft.nTabOrder ) 6650 return false; 6651 if( rLeft.nWidgetIndex < 0 && rRight.nWidgetIndex < 0 ) 6652 return false; 6653 if( rRight.nWidgetIndex < 0 ) 6654 return true; 6655 if( rLeft.nWidgetIndex < 0 ) 6656 return false; 6657 // remember: widget rects are in PDF coordinates, so they are ordered down up 6658 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() > 6659 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() ) 6660 return true; 6661 if( m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Top() > 6662 m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Top() ) 6663 return false; 6664 if( m_rWidgets[ rLeft.nWidgetIndex ].m_aRect.Left() < 6665 m_rWidgets[ rRight.nWidgetIndex ].m_aRect.Left() ) 6666 return true; 6667 return false; 6668 } 6669 }; 6670 6671 void PDFWriterImpl::sortWidgets() 6672 { 6673 // sort widget annotations on each page as per their 6674 // TabOrder attribute 6675 std::hash_map< sal_Int32, AnnotSortContainer > sorted; 6676 int nWidgets = m_aWidgets.size(); 6677 for( int nW = 0; nW < nWidgets; nW++ ) 6678 { 6679 const PDFWidget& rWidget = m_aWidgets[nW]; 6680 if( rWidget.m_nPage >= 0 ) 6681 { 6682 AnnotSortContainer& rCont = sorted[ rWidget.m_nPage ]; 6683 // optimize vector allocation 6684 if( rCont.aSortedAnnots.empty() ) 6685 rCont.aSortedAnnots.reserve( m_aPages[ rWidget.m_nPage ].m_aAnnotations.size() ); 6686 // insert widget to tab sorter 6687 // RadioButtons are not page annotations, only their individual check boxes are 6688 if( rWidget.m_eType != PDFWriter::RadioButton ) 6689 { 6690 rCont.aObjects.insert( rWidget.m_nObject ); 6691 rCont.aSortedAnnots.push_back( AnnotationSortEntry( rWidget.m_nTabOrder, rWidget.m_nObject, nW ) ); 6692 } 6693 } 6694 } 6695 for( std::hash_map< sal_Int32, AnnotSortContainer >::iterator it = sorted.begin(); it != sorted.end(); ++it ) 6696 { 6697 // append entries for non widget annotations 6698 PDFPage& rPage = m_aPages[ it->first ]; 6699 unsigned int nAnnots = rPage.m_aAnnotations.size(); 6700 for( unsigned int nA = 0; nA < nAnnots; nA++ ) 6701 if( it->second.aObjects.find( rPage.m_aAnnotations[nA] ) == it->second.aObjects.end()) 6702 it->second.aSortedAnnots.push_back( AnnotationSortEntry( 10000, rPage.m_aAnnotations[nA], -1 ) ); 6703 6704 AnnotSorterLess aLess( m_aWidgets ); 6705 std::stable_sort( it->second.aSortedAnnots.begin(), it->second.aSortedAnnots.end(), aLess ); 6706 // sanity check 6707 if( it->second.aSortedAnnots.size() == nAnnots) 6708 { 6709 for( unsigned int nA = 0; nA < nAnnots; nA++ ) 6710 rPage.m_aAnnotations[nA] = it->second.aSortedAnnots[nA].nObject; 6711 } 6712 else 6713 { 6714 DBG_ASSERT( 0, "wrong number of sorted annotations" ); 6715 #if OSL_DEBUG_LEVEL > 0 6716 fprintf( stderr, "PDFWriterImpl::sortWidgets(): wrong number of sorted assertions on page nr %ld\n" 6717 " %ld sorted and %ld unsorted\n", (long int)it->first, (long int)it->second.aSortedAnnots.size(), (long int)nAnnots ); 6718 #endif 6719 } 6720 } 6721 6722 // FIXME: implement tab order in structure tree for PDF 1.5 6723 } 6724 6725 namespace vcl { 6726 class PDFStreamIf : 6727 public cppu::WeakImplHelper1< com::sun::star::io::XOutputStream > 6728 { 6729 PDFWriterImpl* m_pWriter; 6730 bool m_bWrite; 6731 public: 6732 PDFStreamIf( PDFWriterImpl* pWriter ) : m_pWriter( pWriter ), m_bWrite( true ) {} 6733 virtual ~PDFStreamIf(); 6734 6735 virtual void SAL_CALL writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw(); 6736 virtual void SAL_CALL flush() throw(); 6737 virtual void SAL_CALL closeOutput() throw(); 6738 }; 6739 } 6740 6741 PDFStreamIf::~PDFStreamIf() 6742 { 6743 } 6744 6745 void SAL_CALL PDFStreamIf::writeBytes( const com::sun::star::uno::Sequence< sal_Int8 >& aData ) throw() 6746 { 6747 if( m_bWrite ) 6748 { 6749 sal_Int32 nBytes = aData.getLength(); 6750 if( nBytes > 0 ) 6751 m_pWriter->writeBuffer( aData.getConstArray(), nBytes ); 6752 } 6753 } 6754 6755 void SAL_CALL PDFStreamIf::flush() throw() 6756 { 6757 } 6758 6759 void SAL_CALL PDFStreamIf::closeOutput() throw() 6760 { 6761 m_bWrite = false; 6762 } 6763 6764 bool PDFWriterImpl::emitAdditionalStreams() 6765 { 6766 unsigned int nStreams = m_aAdditionalStreams.size(); 6767 for( unsigned int i = 0; i < nStreams; i++ ) 6768 { 6769 PDFAddStream& rStream = m_aAdditionalStreams[i]; 6770 rStream.m_nStreamObject = createObject(); 6771 sal_Int32 nSizeObject = createObject(); 6772 6773 if( ! updateObject( rStream.m_nStreamObject ) ) 6774 return false; 6775 6776 OStringBuffer aLine; 6777 aLine.append( rStream.m_nStreamObject ); 6778 aLine.append( " 0 obj\n<</Length " ); 6779 aLine.append( nSizeObject ); 6780 aLine.append( " 0 R" ); 6781 if( rStream.m_bCompress ) 6782 aLine.append( "/Filter/FlateDecode" ); 6783 aLine.append( ">>\nstream\n" ); 6784 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6785 return false; 6786 sal_uInt64 nBeginStreamPos = 0, nEndStreamPos = 0; 6787 if( osl_File_E_None != osl_getFilePos( m_aFile, &nBeginStreamPos ) ) 6788 { 6789 osl_closeFile( m_aFile ); 6790 m_bOpen = false; 6791 } 6792 if( rStream.m_bCompress ) 6793 beginCompression(); 6794 6795 checkAndEnableStreamEncryption( rStream.m_nStreamObject ); 6796 com::sun::star::uno::Reference< com::sun::star::io::XOutputStream > xStream( new PDFStreamIf( this ) ); 6797 rStream.m_pStream->write( xStream ); 6798 xStream.clear(); 6799 delete rStream.m_pStream; 6800 rStream.m_pStream = NULL; 6801 disableStreamEncryption(); 6802 6803 if( rStream.m_bCompress ) 6804 endCompression(); 6805 6806 if( osl_File_E_None != osl_getFilePos( m_aFile, &nEndStreamPos ) ) 6807 { 6808 osl_closeFile( m_aFile ); 6809 m_bOpen = false; 6810 return false; 6811 } 6812 if( ! writeBuffer( "\nendstream\nendobj\n\n", 19 ) ) 6813 return false ; 6814 // emit stream length object 6815 if( ! updateObject( nSizeObject ) ) 6816 return false; 6817 aLine.setLength( 0 ); 6818 aLine.append( nSizeObject ); 6819 aLine.append( " 0 obj\n" ); 6820 aLine.append( (sal_Int64)(nEndStreamPos-nBeginStreamPos) ); 6821 aLine.append( "\nendobj\n\n" ); 6822 if( ! writeBuffer( aLine.getStr(), aLine.getLength() ) ) 6823 return false; 6824 } 6825 return true; 6826 } 6827 6828 bool PDFWriterImpl::emit() 6829 { 6830 endPage(); 6831 6832 // resort structure tree and annotations if necessary 6833 // needed for widget tab order 6834 sortWidgets(); 6835 6836 // emit additional streams 6837 CHECK_RETURN( emitAdditionalStreams() ); 6838 6839 // emit catalog 6840 CHECK_RETURN( emitCatalog() ); 6841 6842 // emit trailer 6843 CHECK_RETURN( emitTrailer() ); 6844 6845 osl_closeFile( m_aFile ); 6846 m_bOpen = false; 6847 6848 return true; 6849 } 6850 6851 std::set< PDFWriter::ErrorCode > PDFWriterImpl::getErrors() 6852 { 6853 return m_aErrors; 6854 } 6855 6856 sal_Int32 PDFWriterImpl::getSystemFont( const Font& i_rFont ) 6857 { 6858 getReferenceDevice()->Push(); 6859 getReferenceDevice()->SetFont( i_rFont ); 6860 getReferenceDevice()->ImplNewFont(); 6861 6862 const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData; 6863 sal_Int32 nFontID = 0; 6864 FontEmbedData::iterator it = m_aSystemFonts.find( pDevFont ); 6865 if( it != m_aSystemFonts.end() ) 6866 nFontID = it->second.m_nNormalFontID; 6867 else 6868 { 6869 nFontID = m_nNextFID++; 6870 m_aSystemFonts[ pDevFont ] = EmbedFont(); 6871 m_aSystemFonts[ pDevFont ].m_nNormalFontID = nFontID; 6872 } 6873 6874 getReferenceDevice()->Pop(); 6875 getReferenceDevice()->ImplNewFont(); 6876 6877 return nFontID; 6878 } 6879 6880 void PDFWriterImpl::registerGlyphs( int nGlyphs, 6881 sal_GlyphId* pGlyphs, 6882 sal_Int32* pGlyphWidths, 6883 sal_Ucs* pUnicodes, 6884 sal_Int32* pUnicodesPerGlyph, 6885 sal_uInt8* pMappedGlyphs, 6886 sal_Int32* pMappedFontObjects, 6887 const ImplFontData* pFallbackFonts[] ) 6888 { 6889 const ImplFontData* pDevFont = m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData; 6890 sal_Ucs* pCurUnicode = pUnicodes; 6891 for( int i = 0; i < nGlyphs; pCurUnicode += pUnicodesPerGlyph[i] , i++ ) 6892 { 6893 const int nFontGlyphId = pGlyphs[i] & (GF_IDXMASK | GF_ISCHAR | GF_GSUB); 6894 const ImplFontData* pCurrentFont = pFallbackFonts[i] ? pFallbackFonts[i] : pDevFont; 6895 6896 if( isBuiltinFont( pCurrentFont ) ) 6897 { 6898 sal_Int32 nFontID = 0; 6899 FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont ); 6900 if( it != m_aEmbeddedFonts.end() ) 6901 nFontID = it->second.m_nNormalFontID; 6902 else 6903 { 6904 nFontID = m_nNextFID++; 6905 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont(); 6906 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID; 6907 } 6908 6909 pGlyphWidths[ i ] = 0; 6910 pMappedGlyphs[ i ] = sal::static_int_cast<sal_Int8>( nFontGlyphId ); 6911 pMappedFontObjects[ i ] = nFontID; 6912 const ImplPdfBuiltinFontData* pFD = GetPdfFontData( pCurrentFont ); 6913 if( pFD ) 6914 { 6915 const BuiltinFont* pBuiltinFont = pFD->GetBuiltinFont(); 6916 pGlyphWidths[i] = pBuiltinFont->m_aWidths[ nFontGlyphId & 0x00ff ]; 6917 } 6918 } 6919 else if( pCurrentFont->mbSubsettable ) 6920 { 6921 FontSubset& rSubset = m_aSubsets[ pCurrentFont ]; 6922 // search for font specific glyphID 6923 FontMapping::iterator it = rSubset.m_aMapping.find( nFontGlyphId ); 6924 if( it != rSubset.m_aMapping.end() ) 6925 { 6926 pMappedFontObjects[i] = it->second.m_nFontID; 6927 pMappedGlyphs[i] = it->second.m_nSubsetGlyphID; 6928 } 6929 else 6930 { 6931 // create new subset if necessary 6932 if( rSubset.m_aSubsets.empty() 6933 || (rSubset.m_aSubsets.back().m_aMapping.size() > 254) ) 6934 { 6935 rSubset.m_aSubsets.push_back( FontEmit( m_nNextFID++ ) ); 6936 } 6937 6938 // copy font id 6939 pMappedFontObjects[i] = rSubset.m_aSubsets.back().m_nFontID; 6940 // create new glyph in subset 6941 sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(rSubset.m_aSubsets.back().m_aMapping.size()+1); 6942 pMappedGlyphs[i] = nNewId; 6943 6944 // add new glyph to emitted font subset 6945 GlyphEmit& rNewGlyphEmit = rSubset.m_aSubsets.back().m_aMapping[ nFontGlyphId ]; 6946 rNewGlyphEmit.setGlyphId( nNewId ); 6947 for( sal_Int32 n = 0; n < pUnicodesPerGlyph[i]; n++ ) 6948 rNewGlyphEmit.addCode( pCurUnicode[n] ); 6949 6950 // add new glyph to font mapping 6951 Glyph& rNewGlyph = rSubset.m_aMapping[ nFontGlyphId ]; 6952 rNewGlyph.m_nFontID = pMappedFontObjects[i]; 6953 rNewGlyph.m_nSubsetGlyphID = nNewId; 6954 } 6955 getReferenceDevice()->ImplGetGraphics(); 6956 const bool bVertical = ((pGlyphs[i] & GF_ROTMASK) != 0); 6957 pGlyphWidths[i] = m_aFontCache.getGlyphWidth( pCurrentFont, 6958 nFontGlyphId, 6959 bVertical, 6960 m_pReferenceDevice->mpGraphics ); 6961 } 6962 else if( pCurrentFont->IsEmbeddable() ) 6963 { 6964 sal_Int32 nFontID = 0; 6965 FontEmbedData::iterator it = m_aEmbeddedFonts.find( pCurrentFont ); 6966 if( it != m_aEmbeddedFonts.end() ) 6967 nFontID = it->second.m_nNormalFontID; 6968 else 6969 { 6970 nFontID = m_nNextFID++; 6971 m_aEmbeddedFonts[ pCurrentFont ] = EmbedFont(); 6972 m_aEmbeddedFonts[ pCurrentFont ].m_nNormalFontID = nFontID; 6973 } 6974 EmbedFont& rEmbedFont = m_aEmbeddedFonts[pCurrentFont]; 6975 6976 const Ucs2SIntMap* pEncoding = NULL; 6977 const Ucs2OStrMap* pNonEncoded = NULL; 6978 getReferenceDevice()->ImplGetGraphics(); 6979 pEncoding = m_pReferenceDevice->mpGraphics->GetFontEncodingVector( pCurrentFont, &pNonEncoded ); 6980 6981 Ucs2SIntMap::const_iterator enc_it; 6982 Ucs2OStrMap::const_iterator nonenc_it; 6983 6984 sal_Int32 nCurFontID = nFontID; 6985 sal_Ucs cChar = *pCurUnicode; 6986 if( pEncoding ) 6987 { 6988 enc_it = pEncoding->find( cChar ); 6989 if( enc_it != pEncoding->end() && enc_it->second > 0 ) 6990 { 6991 DBG_ASSERT( (enc_it->second & 0xffffff00) == 0, "Invalid character code" ); 6992 cChar = (sal_Ucs)enc_it->second; 6993 } 6994 else if( (enc_it == pEncoding->end() || enc_it->second == -1) && 6995 pNonEncoded && 6996 (nonenc_it = pNonEncoded->find( cChar )) != pNonEncoded->end() ) 6997 { 6998 nCurFontID = 0; 6999 // find non encoded glyph 7000 for( std::list< EmbedEncoding >::iterator nec_it = rEmbedFont.m_aExtendedEncodings.begin(); nec_it != rEmbedFont.m_aExtendedEncodings.end(); ++nec_it ) 7001 { 7002 if( nec_it->m_aCMap.find( cChar ) != nec_it->m_aCMap.end() ) 7003 { 7004 nCurFontID = nec_it->m_nFontID; 7005 cChar = (sal_Ucs)nec_it->m_aCMap[ cChar ]; 7006 break; 7007 } 7008 } 7009 if( nCurFontID == 0 ) // new nonencoded glyph 7010 { 7011 if( rEmbedFont.m_aExtendedEncodings.empty() || rEmbedFont.m_aExtendedEncodings.back().m_aEncVector.size() == 255 ) 7012 { 7013 rEmbedFont.m_aExtendedEncodings.push_back( EmbedEncoding() ); 7014 rEmbedFont.m_aExtendedEncodings.back().m_nFontID = m_nNextFID++; 7015 } 7016 EmbedEncoding& rEncoding = rEmbedFont.m_aExtendedEncodings.back(); 7017 rEncoding.m_aEncVector.push_back( EmbedCode() ); 7018 rEncoding.m_aEncVector.back().m_aUnicode = cChar; 7019 rEncoding.m_aEncVector.back().m_aName = nonenc_it->second; 7020 rEncoding.m_aCMap[ cChar ] = (sal_Int8)(rEncoding.m_aEncVector.size()-1); 7021 nCurFontID = rEncoding.m_nFontID; 7022 cChar = (sal_Ucs)rEncoding.m_aCMap[ cChar ]; 7023 } 7024 } 7025 else 7026 pEncoding = NULL; 7027 } 7028 if( ! pEncoding ) 7029 { 7030 if( cChar & 0xff00 ) 7031 { 7032 // some characters can be used by conversion 7033 if( cChar >= 0xf000 && cChar <= 0xf0ff ) // symbol encoding in private use area 7034 cChar -= 0xf000; 7035 else 7036 { 7037 String aString(cChar); 7038 ByteString aChar( aString, RTL_TEXTENCODING_MS_1252 ); 7039 cChar = ((sal_Ucs)aChar.GetChar( 0 )) & 0x00ff; 7040 } 7041 } 7042 } 7043 7044 pMappedGlyphs[ i ] = (sal_Int8)cChar; 7045 pMappedFontObjects[ i ] = nCurFontID; 7046 pGlyphWidths[ i ] = m_aFontCache.getGlyphWidth( pCurrentFont, 7047 (pEncoding ? *pCurUnicode : cChar) | GF_ISCHAR, 7048 false, 7049 m_pReferenceDevice->mpGraphics ); 7050 } 7051 } 7052 } 7053 7054 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const String& rText, bool bTextLines ) 7055 { 7056 push( PUSH_ALL ); 7057 7058 FontRelief eRelief = m_aCurrentPDFState.m_aFont.GetRelief(); 7059 7060 Color aTextColor = m_aCurrentPDFState.m_aFont.GetColor(); 7061 Color aTextLineColor = m_aCurrentPDFState.m_aTextLineColor; 7062 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 7063 Color aReliefColor( COL_LIGHTGRAY ); 7064 if( aTextColor == COL_BLACK ) 7065 aTextColor = Color( COL_WHITE ); 7066 if( aTextLineColor == COL_BLACK ) 7067 aTextLineColor = Color( COL_WHITE ); 7068 if( aOverlineColor == COL_BLACK ) 7069 aOverlineColor = Color( COL_WHITE ); 7070 if( aTextColor == COL_WHITE ) 7071 aReliefColor = Color( COL_BLACK ); 7072 7073 Font aSetFont = m_aCurrentPDFState.m_aFont; 7074 aSetFont.SetRelief( RELIEF_NONE ); 7075 aSetFont.SetShadow( sal_False ); 7076 7077 aSetFont.SetColor( aReliefColor ); 7078 setTextLineColor( aReliefColor ); 7079 setOverlineColor( aReliefColor ); 7080 setFont( aSetFont ); 7081 long nOff = 1 + getReferenceDevice()->mnDPIX/300; 7082 if( eRelief == RELIEF_ENGRAVED ) 7083 nOff = -nOff; 7084 7085 rLayout.DrawOffset() += Point( nOff, nOff ); 7086 updateGraphicsState(); 7087 drawLayout( rLayout, rText, bTextLines ); 7088 7089 rLayout.DrawOffset() -= Point( nOff, nOff ); 7090 setTextLineColor( aTextLineColor ); 7091 setOverlineColor( aOverlineColor ); 7092 aSetFont.SetColor( aTextColor ); 7093 setFont( aSetFont ); 7094 updateGraphicsState(); 7095 drawLayout( rLayout, rText, bTextLines ); 7096 7097 // clean up the mess 7098 pop(); 7099 } 7100 7101 void PDFWriterImpl::drawShadow( SalLayout& rLayout, const String& rText, bool bTextLines ) 7102 { 7103 Font aSaveFont = m_aCurrentPDFState.m_aFont; 7104 Color aSaveTextLineColor = m_aCurrentPDFState.m_aTextLineColor; 7105 Color aSaveOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 7106 7107 Font& rFont = m_aCurrentPDFState.m_aFont; 7108 if( rFont.GetColor() == Color( COL_BLACK ) || rFont.GetColor().GetLuminance() < 8 ) 7109 rFont.SetColor( Color( COL_LIGHTGRAY ) ); 7110 else 7111 rFont.SetColor( Color( COL_BLACK ) ); 7112 rFont.SetShadow( sal_False ); 7113 rFont.SetOutline( sal_False ); 7114 setFont( rFont ); 7115 setTextLineColor( rFont.GetColor() ); 7116 setOverlineColor( rFont.GetColor() ); 7117 updateGraphicsState(); 7118 7119 long nOff = 1 + ((m_pReferenceDevice->mpFontEntry->mnLineHeight-24)/24); 7120 if( rFont.IsOutline() ) 7121 nOff++; 7122 rLayout.DrawBase() += Point( nOff, nOff ); 7123 drawLayout( rLayout, rText, bTextLines ); 7124 rLayout.DrawBase() -= Point( nOff, nOff ); 7125 7126 setFont( aSaveFont ); 7127 setTextLineColor( aSaveTextLineColor ); 7128 setOverlineColor( aSaveOverlineColor ); 7129 updateGraphicsState(); 7130 } 7131 7132 void PDFWriterImpl::drawVerticalGlyphs( 7133 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs, 7134 OStringBuffer& rLine, 7135 const Point& rAlignOffset, 7136 const Matrix3& rRotScale, 7137 double fAngle, 7138 double fXScale, 7139 double fSkew, 7140 sal_Int32 nFontHeight ) 7141 { 7142 long nXOffset = 0; 7143 Point aCurPos( rGlyphs[0].m_aPos ); 7144 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos ); 7145 aCurPos += rAlignOffset; 7146 for( size_t i = 0; i < rGlyphs.size(); i++ ) 7147 { 7148 // have to emit each glyph on its own 7149 double fDeltaAngle = 0.0; 7150 double fYScale = 1.0; 7151 double fTempXScale = fXScale; 7152 double fSkewB = fSkew; 7153 double fSkewA = 0.0; 7154 7155 Point aDeltaPos; 7156 if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTL ) 7157 { 7158 fDeltaAngle = M_PI/2.0; 7159 aDeltaPos.X() = m_pReferenceDevice->GetFontMetric().GetAscent(); 7160 aDeltaPos.Y() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent() * fXScale); 7161 fYScale = fXScale; 7162 fTempXScale = 1.0; 7163 fSkewA = -fSkewB; 7164 fSkewB = 0.0; 7165 } 7166 else if( ( rGlyphs[i].m_nGlyphId & GF_ROTMASK ) == GF_ROTR ) 7167 { 7168 fDeltaAngle = -M_PI/2.0; 7169 aDeltaPos.X() = (int)((double)m_pReferenceDevice->GetFontMetric().GetDescent()*fXScale); 7170 aDeltaPos.Y() = -m_pReferenceDevice->GetFontMetric().GetAscent(); 7171 fYScale = fXScale; 7172 fTempXScale = 1.0; 7173 fSkewA = fSkewB; 7174 fSkewB = 0.0; 7175 } 7176 aDeltaPos += (m_pReferenceDevice->PixelToLogic( Point( (int)((double)nXOffset/fXScale), 0 ) ) - m_pReferenceDevice->PixelToLogic( Point() ) ); 7177 if( i < rGlyphs.size()-1 ) 7178 nXOffset += rGlyphs[i+1].m_aPos.Y() - rGlyphs[i].m_aPos.Y(); 7179 if( ! rGlyphs[i].m_nGlyphId ) 7180 continue; 7181 7182 aDeltaPos = rRotScale.transform( aDeltaPos ); 7183 7184 Matrix3 aMat; 7185 if( fSkewB != 0.0 || fSkewA != 0.0 ) 7186 aMat.skew( fSkewA, fSkewB ); 7187 aMat.scale( fTempXScale, fYScale ); 7188 aMat.rotate( fAngle+fDeltaAngle ); 7189 aMat.translate( aCurPos.X()+aDeltaPos.X(), aCurPos.Y()+aDeltaPos.Y() ); 7190 aMat.append( m_aPages.back(), rLine ); 7191 rLine.append( " Tm" ); 7192 if( i == 0 || rGlyphs[i-1].m_nMappedFontId != rGlyphs[i].m_nMappedFontId ) 7193 { 7194 rLine.append( " /F" ); 7195 rLine.append( rGlyphs[i].m_nMappedFontId ); 7196 rLine.append( ' ' ); 7197 m_aPages.back().appendMappedLength( nFontHeight, rLine, true ); 7198 rLine.append( " Tf" ); 7199 } 7200 rLine.append( "<" ); 7201 appendHex( rGlyphs[i].m_nMappedGlyphId, rLine ); 7202 rLine.append( ">Tj\n" ); 7203 } 7204 } 7205 7206 void PDFWriterImpl::drawHorizontalGlyphs( 7207 const std::vector<PDFWriterImpl::PDFGlyph>& rGlyphs, 7208 OStringBuffer& rLine, 7209 const Point& rAlignOffset, 7210 double fAngle, 7211 double fXScale, 7212 double fSkew, 7213 sal_Int32 nFontHeight, 7214 sal_Int32 nPixelFontHeight 7215 ) 7216 { 7217 // horizontal (= normal) case 7218 7219 // fill in run end indices 7220 // end is marked by index of the first glyph of the next run 7221 // a run is marked by same mapped font id and same Y position 7222 std::vector< sal_uInt32 > aRunEnds; 7223 aRunEnds.reserve( rGlyphs.size() ); 7224 for( size_t i = 1; i < rGlyphs.size(); i++ ) 7225 { 7226 if( rGlyphs[i].m_nMappedFontId != rGlyphs[i-1].m_nMappedFontId || 7227 rGlyphs[i].m_aPos.Y() != rGlyphs[i-1].m_aPos.Y() ) 7228 { 7229 aRunEnds.push_back(i); 7230 } 7231 } 7232 // last run ends at last glyph 7233 aRunEnds.push_back( rGlyphs.size() ); 7234 7235 // loop over runs of the same font 7236 sal_uInt32 nBeginRun = 0; 7237 for( size_t nRun = 0; nRun < aRunEnds.size(); nRun++ ) 7238 { 7239 // setup text matrix 7240 Point aCurPos = rGlyphs[nBeginRun].m_aPos; 7241 // back transformation to current coordinate system 7242 aCurPos = m_pReferenceDevice->PixelToLogic( aCurPos ); 7243 aCurPos += rAlignOffset; 7244 // the first run can be set with "Td" operator 7245 // subsequent use of that operator would move 7246 // the texline matrix relative to what was set before 7247 // making use of that would drive us into rounding issues 7248 Matrix3 aMat; 7249 if( nRun == 0 && fAngle == 0.0 && fXScale == 1.0 && fSkew == 0.0 ) 7250 { 7251 m_aPages.back().appendPoint( aCurPos, rLine, false ); 7252 rLine.append( " Td " ); 7253 } 7254 else 7255 { 7256 if( fSkew != 0.0 ) 7257 aMat.skew( 0.0, fSkew ); 7258 aMat.scale( fXScale, 1.0 ); 7259 aMat.rotate( fAngle ); 7260 aMat.translate( aCurPos.X(), aCurPos.Y() ); 7261 aMat.append( m_aPages.back(), rLine ); 7262 rLine.append( " Tm\n" ); 7263 } 7264 // set up correct font 7265 rLine.append( "/F" ); 7266 rLine.append( rGlyphs[nBeginRun].m_nMappedFontId ); 7267 rLine.append( ' ' ); 7268 m_aPages.back().appendMappedLength( nFontHeight, rLine, true ); 7269 rLine.append( " Tf" ); 7270 7271 // output glyphs using Tj or TJ 7272 OStringBuffer aKernedLine( 256 ), aUnkernedLine( 256 ); 7273 aKernedLine.append( "[<" ); 7274 aUnkernedLine.append( '<' ); 7275 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aKernedLine ); 7276 appendHex( rGlyphs[nBeginRun].m_nMappedGlyphId, aUnkernedLine ); 7277 7278 aMat.invert(); 7279 bool bNeedKern = false; 7280 for( sal_uInt32 nPos = nBeginRun+1; nPos < aRunEnds[nRun]; nPos++ ) 7281 { 7282 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aUnkernedLine ); 7283 // check if default glyph positioning is sufficient 7284 const Point aThisPos = aMat.transform( rGlyphs[nPos].m_aPos ); 7285 const Point aPrevPos = aMat.transform( rGlyphs[nPos-1].m_aPos ); 7286 double fAdvance = aThisPos.X() - aPrevPos.X(); 7287 fAdvance *= 1000.0 / nPixelFontHeight; 7288 const sal_Int32 nAdjustment = (sal_Int32)(rGlyphs[nPos-1].m_nNativeWidth - fAdvance + 0.5); 7289 if( nAdjustment != 0 ) 7290 { 7291 // apply individual glyph positioning 7292 bNeedKern = true; 7293 aKernedLine.append( ">" ); 7294 aKernedLine.append( nAdjustment ); 7295 aKernedLine.append( "<" ); 7296 } 7297 appendHex( rGlyphs[nPos].m_nMappedGlyphId, aKernedLine ); 7298 } 7299 aKernedLine.append( ">]TJ\n" ); 7300 aUnkernedLine.append( ">Tj\n" ); 7301 rLine.append( bNeedKern ? aKernedLine : aUnkernedLine ); 7302 7303 // set beginning of next run 7304 nBeginRun = aRunEnds[nRun]; 7305 } 7306 } 7307 7308 void PDFWriterImpl::drawLayout( SalLayout& rLayout, const String& rText, bool bTextLines ) 7309 { 7310 // relief takes precedence over shadow (see outdev3.cxx) 7311 if( m_aCurrentPDFState.m_aFont.GetRelief() != RELIEF_NONE ) 7312 { 7313 drawRelief( rLayout, rText, bTextLines ); 7314 return; 7315 } 7316 else if( m_aCurrentPDFState.m_aFont.IsShadow() ) 7317 drawShadow( rLayout, rText, bTextLines ); 7318 7319 OStringBuffer aLine( 512 ); 7320 7321 const int nMaxGlyphs = 256; 7322 7323 sal_GlyphId pGlyphs[nMaxGlyphs]; 7324 sal_Int32 pGlyphWidths[nMaxGlyphs]; 7325 sal_uInt8 pMappedGlyphs[nMaxGlyphs]; 7326 sal_Int32 pMappedFontObjects[nMaxGlyphs]; 7327 std::vector<sal_Ucs> aUnicodes; 7328 aUnicodes.reserve( nMaxGlyphs ); 7329 sal_Int32 pUnicodesPerGlyph[nMaxGlyphs]; 7330 int pCharPosAry[nMaxGlyphs]; 7331 sal_Int32 nAdvanceWidths[nMaxGlyphs]; 7332 const ImplFontData* pFallbackFonts[nMaxGlyphs]; 7333 bool bVertical = m_aCurrentPDFState.m_aFont.IsVertical(); 7334 int nGlyphs; 7335 int nIndex = 0; 7336 int nMinCharPos = 0, nMaxCharPos = rText.Len()-1; 7337 double fXScale = 1.0; 7338 double fSkew = 0.0; 7339 sal_Int32 nPixelFontHeight = m_pReferenceDevice->mpFontEntry->maFontSelData.mnHeight; 7340 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); 7341 7342 // transform font height back to current units 7343 // note: the layout calculates in outdevs device pixel !! 7344 sal_Int32 nFontHeight = m_pReferenceDevice->ImplDevicePixelToLogicHeight( nPixelFontHeight ); 7345 if( m_aCurrentPDFState.m_aFont.GetWidth() ) 7346 { 7347 Font aFont( m_aCurrentPDFState.m_aFont ); 7348 aFont.SetWidth( 0 ); 7349 FontMetric aMetric = m_pReferenceDevice->GetFontMetric( aFont ); 7350 if( aMetric.GetWidth() != m_aCurrentPDFState.m_aFont.GetWidth() ) 7351 { 7352 fXScale = 7353 (double)m_aCurrentPDFState.m_aFont.GetWidth() / 7354 (double)aMetric.GetWidth(); 7355 } 7356 // force state before GetFontMetric 7357 m_pReferenceDevice->ImplNewFont(); 7358 } 7359 7360 // perform artificial italics if necessary 7361 if( ( m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_NORMAL || 7362 m_aCurrentPDFState.m_aFont.GetItalic() == ITALIC_OBLIQUE ) && 7363 !( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_NORMAL || 7364 m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetSlant() == ITALIC_OBLIQUE ) 7365 ) 7366 { 7367 fSkew = M_PI/12.0; 7368 } 7369 7370 // if the mapmode is distorted we need to adjust for that also 7371 if( m_aCurrentPDFState.m_aMapMode.GetScaleX() != m_aCurrentPDFState.m_aMapMode.GetScaleY() ) 7372 { 7373 fXScale *= double(m_aCurrentPDFState.m_aMapMode.GetScaleX()) / double(m_aCurrentPDFState.m_aMapMode.GetScaleY()); 7374 } 7375 7376 int nAngle = m_aCurrentPDFState.m_aFont.GetOrientation(); 7377 // normalize angles 7378 while( nAngle < 0 ) 7379 nAngle += 3600; 7380 nAngle = nAngle % 3600; 7381 double fAngle = (double)nAngle * M_PI / 1800.0; 7382 7383 Matrix3 aRotScale; 7384 aRotScale.scale( fXScale, 1.0 ); 7385 if( fAngle != 0.0 ) 7386 aRotScale.rotate( -fAngle ); 7387 7388 bool bPop = false; 7389 bool bABold = false; 7390 // artificial bold necessary ? 7391 if( m_pReferenceDevice->mpFontEntry->maFontSelData.mpFontData->GetWeight() <= WEIGHT_MEDIUM && 7392 m_pReferenceDevice->mpFontEntry->maFontSelData.GetWeight() > WEIGHT_MEDIUM ) 7393 { 7394 if( ! bPop ) 7395 aLine.append( "q " ); 7396 bPop = true; 7397 bABold = true; 7398 } 7399 // setup text colors (if necessary) 7400 Color aStrokeColor( COL_TRANSPARENT ); 7401 Color aNonStrokeColor( COL_TRANSPARENT ); 7402 7403 if( m_aCurrentPDFState.m_aFont.IsOutline() ) 7404 { 7405 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7406 aNonStrokeColor = Color( COL_WHITE ); 7407 } 7408 else 7409 aNonStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7410 if( bABold ) 7411 aStrokeColor = m_aCurrentPDFState.m_aFont.GetColor(); 7412 7413 if( aStrokeColor != Color( COL_TRANSPARENT ) && aStrokeColor != m_aCurrentPDFState.m_aLineColor ) 7414 { 7415 if( ! bPop ) 7416 aLine.append( "q " ); 7417 bPop = true; 7418 appendStrokingColor( aStrokeColor, aLine ); 7419 aLine.append( "\n" ); 7420 } 7421 if( aNonStrokeColor != Color( COL_TRANSPARENT ) && aNonStrokeColor != m_aCurrentPDFState.m_aFillColor ) 7422 { 7423 if( ! bPop ) 7424 aLine.append( "q " ); 7425 bPop = true; 7426 appendNonStrokingColor( aNonStrokeColor, aLine ); 7427 aLine.append( "\n" ); 7428 } 7429 7430 // begin text object 7431 aLine.append( "BT\n" ); 7432 // outline attribute ? 7433 if( m_aCurrentPDFState.m_aFont.IsOutline() || bABold ) 7434 { 7435 // set correct text mode, set stroke width 7436 aLine.append( "2 Tr " ); // fill, then stroke 7437 7438 if( m_aCurrentPDFState.m_aFont.IsOutline() ) 7439 { 7440 // unclear what to do in case of outline and artificial bold 7441 // for the time being outline wins 7442 aLine.append( "0.25 w \n" ); 7443 } 7444 else 7445 { 7446 double fW = (double)m_aCurrentPDFState.m_aFont.GetHeight() / 30.0; 7447 m_aPages.back().appendMappedLength( fW, aLine ); 7448 aLine.append ( " w\n" ); 7449 } 7450 } 7451 7452 FontMetric aRefDevFontMetric = m_pReferenceDevice->GetFontMetric(); 7453 7454 // collect the glyphs into a single array 7455 const int nTmpMaxGlyphs = rLayout.GetOrientation() ? 1 : nMaxGlyphs; // #i97991# temporary workaround for #i87686# 7456 std::vector< PDFGlyph > aGlyphs; 7457 aGlyphs.reserve( nTmpMaxGlyphs ); 7458 // first get all the glyphs and register them; coordinates still in Pixel 7459 Point aGNGlyphPos; 7460 while( (nGlyphs = rLayout.GetNextGlyphs( nTmpMaxGlyphs, pGlyphs, aGNGlyphPos, nIndex, nAdvanceWidths, pCharPosAry )) != 0 ) 7461 { 7462 aUnicodes.clear(); 7463 for( int i = 0; i < nGlyphs; i++ ) 7464 { 7465 pFallbackFonts[i] = rLayout.GetFallbackFontData( pGlyphs[i] ); 7466 7467 // default case: 1 glyph is one unicode 7468 pUnicodesPerGlyph[i] = 1; 7469 if( (pGlyphs[i] & GF_ISCHAR) ) 7470 { 7471 aUnicodes.push_back( static_cast<sal_Ucs>(pGlyphs[i] & GF_IDXMASK) ); 7472 } 7473 else if( pCharPosAry[i] >= nMinCharPos && pCharPosAry[i] <= nMaxCharPos ) 7474 { 7475 int nChars = 1; 7476 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]) ) ); 7477 pUnicodesPerGlyph[i] = 1; 7478 // try to handle ligatures and such 7479 if( i < nGlyphs-1 ) 7480 { 7481 nChars = pCharPosAry[i+1] - pCharPosAry[i]; 7482 // #i115618# fix for simple RTL+CTL cases 7483 // TODO: sanitize for RTL ligatures, more complex CTL, etc. 7484 if( nChars < 0 ) 7485 nChars = -nChars; 7486 else if( nChars == 0 ) 7487 nChars = 1; 7488 pUnicodesPerGlyph[i] = nChars; 7489 for( int n = 1; n < nChars; n++ ) 7490 aUnicodes.push_back( rText.GetChar( sal::static_int_cast<xub_StrLen>(pCharPosAry[i]+n) ) ); 7491 } 7492 // #i36691# hack that is needed because currently the pGlyphs[] 7493 // argument is ignored for embeddable fonts and so the layout 7494 // engine's glyph work is ignored (i.e. char mirroring) 7495 // TODO: a real solution would be to map the layout engine's 7496 // glyphid (i.e. FreeType's synthetic glyphid for a Type1 font) 7497 // back to unicode and then to embeddable font's encoding 7498 if( getReferenceDevice()->GetLayoutMode() & TEXT_LAYOUT_BIDI_RTL ) 7499 { 7500 size_t nI = aUnicodes.size()-1; 7501 for( int n = 0; n < nChars; n++, nI-- ) 7502 aUnicodes[nI] = static_cast<sal_Ucs>(GetMirroredChar(aUnicodes[nI])); 7503 } 7504 } 7505 else 7506 aUnicodes.push_back( 0 ); 7507 // note: in case of ctl one character may result 7508 // in multiple glyphs. The current SalLayout 7509 // implementations set -1 then to indicate that no direct 7510 // mapping is possible 7511 } 7512 7513 registerGlyphs( nGlyphs, pGlyphs, pGlyphWidths, &aUnicodes[0], pUnicodesPerGlyph, pMappedGlyphs, pMappedFontObjects, pFallbackFonts ); 7514 7515 for( int i = 0; i < nGlyphs; i++ ) 7516 { 7517 aGlyphs.push_back( PDFGlyph( aGNGlyphPos, 7518 pGlyphWidths[i], 7519 pGlyphs[i], 7520 pMappedFontObjects[i], 7521 pMappedGlyphs[i] ) ); 7522 if( bVertical ) 7523 aGNGlyphPos.Y() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel(); 7524 else 7525 aGNGlyphPos.X() += nAdvanceWidths[i]/rLayout.GetUnitsPerPixel(); 7526 } 7527 } 7528 7529 Point aAlignOffset; 7530 if ( eAlign == ALIGN_BOTTOM ) 7531 aAlignOffset.Y() -= aRefDevFontMetric.GetDescent(); 7532 else if ( eAlign == ALIGN_TOP ) 7533 aAlignOffset.Y() += aRefDevFontMetric.GetAscent(); 7534 if( aAlignOffset.X() || aAlignOffset.Y() ) 7535 aAlignOffset = aRotScale.transform( aAlignOffset ); 7536 7537 /* #159153# do not emit an empty glyph vector; this can happen if e.g. the original 7538 string contained only on of the UTF16 BOMs 7539 */ 7540 if( ! aGlyphs.empty() ) 7541 { 7542 if( bVertical ) 7543 drawVerticalGlyphs( aGlyphs, aLine, aAlignOffset, aRotScale, fAngle, fXScale, fSkew, nFontHeight ); 7544 else 7545 drawHorizontalGlyphs( aGlyphs, aLine, aAlignOffset, fAngle, fXScale, fSkew, nFontHeight, nPixelFontHeight ); 7546 } 7547 7548 // end textobject 7549 aLine.append( "ET\n" ); 7550 if( bPop ) 7551 aLine.append( "Q\n" ); 7552 7553 writeBuffer( aLine.getStr(), aLine.getLength() ); 7554 7555 // draw eventual textlines 7556 FontStrikeout eStrikeout = m_aCurrentPDFState.m_aFont.GetStrikeout(); 7557 FontUnderline eUnderline = m_aCurrentPDFState.m_aFont.GetUnderline(); 7558 FontUnderline eOverline = m_aCurrentPDFState.m_aFont.GetOverline(); 7559 if( bTextLines && 7560 ( 7561 ( eUnderline != UNDERLINE_NONE && eUnderline != UNDERLINE_DONTKNOW ) || 7562 ( eOverline != UNDERLINE_NONE && eOverline != UNDERLINE_DONTKNOW ) || 7563 ( eStrikeout != STRIKEOUT_NONE && eStrikeout != STRIKEOUT_DONTKNOW ) 7564 ) 7565 ) 7566 { 7567 sal_Bool bUnderlineAbove = OutputDevice::ImplIsUnderlineAbove( m_aCurrentPDFState.m_aFont ); 7568 if( m_aCurrentPDFState.m_aFont.IsWordLineMode() ) 7569 { 7570 Point aPos, aStartPt; 7571 sal_Int32 nWidth = 0, nAdvance=0; 7572 for( int nStart = 0;;) 7573 { 7574 sal_GlyphId nGlyphIndex; 7575 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) 7576 break; 7577 7578 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) 7579 { 7580 if( !nWidth ) 7581 aStartPt = aPos; 7582 7583 nWidth += nAdvance; 7584 } 7585 else if( nWidth > 0 ) 7586 { 7587 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7588 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7589 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7590 nWidth = 0; 7591 } 7592 } 7593 7594 if( nWidth > 0 ) 7595 { 7596 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7597 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7598 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7599 } 7600 } 7601 else 7602 { 7603 Point aStartPt = rLayout.GetDrawPosition(); 7604 int nWidth = rLayout.GetTextWidth() / rLayout.GetUnitsPerPixel(); 7605 drawTextLine( m_pReferenceDevice->PixelToLogic( aStartPt ), 7606 m_pReferenceDevice->ImplDevicePixelToLogicWidth( nWidth ), 7607 eStrikeout, eUnderline, eOverline, bUnderlineAbove ); 7608 } 7609 } 7610 7611 // write eventual emphasis marks 7612 if( m_aCurrentPDFState.m_aFont.GetEmphasisMark() & EMPHASISMARK_STYLE ) 7613 { 7614 PolyPolygon aEmphPoly; 7615 Rectangle aEmphRect1; 7616 Rectangle aEmphRect2; 7617 long nEmphYOff; 7618 long nEmphWidth; 7619 long nEmphHeight; 7620 sal_Bool bEmphPolyLine; 7621 FontEmphasisMark nEmphMark; 7622 7623 push( PUSH_ALL ); 7624 7625 aLine.setLength( 0 ); 7626 aLine.append( "q\n" ); 7627 7628 nEmphMark = m_pReferenceDevice->ImplGetEmphasisMarkStyle( m_aCurrentPDFState.m_aFont ); 7629 if ( nEmphMark & EMPHASISMARK_POS_BELOW ) 7630 nEmphHeight = m_pReferenceDevice->mnEmphasisDescent; 7631 else 7632 nEmphHeight = m_pReferenceDevice->mnEmphasisAscent; 7633 m_pReferenceDevice->ImplGetEmphasisMark( aEmphPoly, 7634 bEmphPolyLine, 7635 aEmphRect1, 7636 aEmphRect2, 7637 nEmphYOff, 7638 nEmphWidth, 7639 nEmphMark, 7640 m_pReferenceDevice->ImplDevicePixelToLogicWidth(nEmphHeight), 7641 m_pReferenceDevice->mpFontEntry->mnOrientation ); 7642 if ( bEmphPolyLine ) 7643 { 7644 setLineColor( m_aCurrentPDFState.m_aFont.GetColor() ); 7645 setFillColor( Color( COL_TRANSPARENT ) ); 7646 } 7647 else 7648 { 7649 setFillColor( m_aCurrentPDFState.m_aFont.GetColor() ); 7650 setLineColor( Color( COL_TRANSPARENT ) ); 7651 } 7652 writeBuffer( aLine.getStr(), aLine.getLength() ); 7653 7654 Point aOffset = Point(0,0); 7655 7656 if ( nEmphMark & EMPHASISMARK_POS_BELOW ) 7657 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnDescent + nEmphYOff; 7658 else 7659 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnAscent + nEmphYOff; 7660 7661 long nEmphWidth2 = nEmphWidth / 2; 7662 long nEmphHeight2 = nEmphHeight / 2; 7663 aOffset += Point( nEmphWidth2, nEmphHeight2 ); 7664 7665 if ( eAlign == ALIGN_BOTTOM ) 7666 aOffset.Y() -= m_pReferenceDevice->mpFontEntry->maMetric.mnDescent; 7667 else if ( eAlign == ALIGN_TOP ) 7668 aOffset.Y() += m_pReferenceDevice->mpFontEntry->maMetric.mnAscent; 7669 7670 for( int nStart = 0;;) 7671 { 7672 Point aPos; 7673 sal_GlyphId nGlyphIndex; 7674 sal_Int32 nAdvance; 7675 if( !rLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) ) 7676 break; 7677 7678 if( !rLayout.IsSpacingGlyph( nGlyphIndex ) ) 7679 { 7680 Point aAdjOffset = aOffset; 7681 aAdjOffset.X() += (nAdvance - nEmphWidth) / 2; 7682 aAdjOffset = aRotScale.transform( aAdjOffset ); 7683 7684 aAdjOffset -= Point( nEmphWidth2, nEmphHeight2 ); 7685 7686 aPos += aAdjOffset; 7687 aPos = m_pReferenceDevice->PixelToLogic( aPos ); 7688 drawEmphasisMark( aPos.X(), aPos.Y(), 7689 aEmphPoly, bEmphPolyLine, 7690 aEmphRect1, aEmphRect2 ); 7691 } 7692 } 7693 7694 writeBuffer( "Q\n", 2 ); 7695 pop(); 7696 } 7697 7698 } 7699 7700 void PDFWriterImpl::drawEmphasisMark( long nX, long nY, 7701 const PolyPolygon& rPolyPoly, sal_Bool bPolyLine, 7702 const Rectangle& rRect1, const Rectangle& rRect2 ) 7703 { 7704 // TODO: pass nWidth as width of this mark 7705 // long nWidth = 0; 7706 7707 if ( rPolyPoly.Count() ) 7708 { 7709 if ( bPolyLine ) 7710 { 7711 Polygon aPoly = rPolyPoly.GetObject( 0 ); 7712 aPoly.Move( nX, nY ); 7713 drawPolyLine( aPoly ); 7714 } 7715 else 7716 { 7717 PolyPolygon aPolyPoly = rPolyPoly; 7718 aPolyPoly.Move( nX, nY ); 7719 drawPolyPolygon( aPolyPoly ); 7720 } 7721 } 7722 7723 if ( !rRect1.IsEmpty() ) 7724 { 7725 Rectangle aRect( Point( nX+rRect1.Left(), 7726 nY+rRect1.Top() ), rRect1.GetSize() ); 7727 drawRectangle( aRect ); 7728 } 7729 7730 if ( !rRect2.IsEmpty() ) 7731 { 7732 Rectangle aRect( Point( nX+rRect2.Left(), 7733 nY+rRect2.Top() ), rRect2.GetSize() ); 7734 7735 drawRectangle( aRect ); 7736 } 7737 } 7738 7739 void PDFWriterImpl::drawText( const Point& rPos, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7740 { 7741 MARK( "drawText" ); 7742 7743 updateGraphicsState(); 7744 7745 // get a layout from the OuputDevice's SalGraphics 7746 // this also enforces font substitution and sets the font on SalGraphics 7747 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos ); 7748 if( pLayout ) 7749 { 7750 drawLayout( *pLayout, rText, bTextLines ); 7751 pLayout->Release(); 7752 } 7753 } 7754 7755 void PDFWriterImpl::drawTextArray( const Point& rPos, const String& rText, const sal_Int32* pDXArray, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7756 { 7757 MARK( "drawText with array" ); 7758 7759 updateGraphicsState(); 7760 7761 // get a layout from the OuputDevice's SalGraphics 7762 // this also enforces font substitution and sets the font on SalGraphics 7763 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray ); 7764 if( pLayout ) 7765 { 7766 drawLayout( *pLayout, rText, bTextLines ); 7767 pLayout->Release(); 7768 } 7769 } 7770 7771 void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const String& rText, xub_StrLen nIndex, xub_StrLen nLen, bool bTextLines ) 7772 { 7773 MARK( "drawStretchText" ); 7774 7775 updateGraphicsState(); 7776 7777 // get a layout from the OuputDevice's SalGraphics 7778 // this also enforces font substitution and sets the font on SalGraphics 7779 SalLayout* pLayout = m_pReferenceDevice->ImplLayout( rText, nIndex, nLen, rPos, nWidth ); 7780 if( pLayout ) 7781 { 7782 drawLayout( *pLayout, rText, bTextLines ); 7783 pLayout->Release(); 7784 } 7785 } 7786 7787 void PDFWriterImpl::drawText( const Rectangle& rRect, const String& rOrigStr, sal_uInt16 nStyle, bool bTextLines ) 7788 { 7789 long nWidth = rRect.GetWidth(); 7790 long nHeight = rRect.GetHeight(); 7791 7792 if ( nWidth <= 0 || nHeight <= 0 ) 7793 return; 7794 7795 MARK( "drawText with rectangle" ); 7796 7797 updateGraphicsState(); 7798 7799 // clip with rectangle 7800 OStringBuffer aLine; 7801 aLine.append( "q " ); 7802 m_aPages.back().appendRect( rRect, aLine ); 7803 aLine.append( " W* n\n" ); 7804 writeBuffer( aLine.getStr(), aLine.getLength() ); 7805 7806 // if disabled text is needed, put in here 7807 7808 Point aPos = rRect.TopLeft(); 7809 7810 long nTextHeight = m_pReferenceDevice->GetTextHeight(); 7811 xub_StrLen nMnemonicPos = STRING_NOTFOUND; 7812 7813 String aStr = rOrigStr; 7814 if ( nStyle & TEXT_DRAW_MNEMONIC ) 7815 aStr = m_pReferenceDevice->GetNonMnemonicString( aStr, nMnemonicPos ); 7816 7817 // multiline text 7818 if ( nStyle & TEXT_DRAW_MULTILINE ) 7819 { 7820 XubString aLastLine; 7821 ImplMultiTextLineInfo aMultiLineInfo; 7822 ImplTextLineInfo* pLineInfo; 7823 long nMaxTextWidth; 7824 xub_StrLen i; 7825 xub_StrLen nLines; 7826 xub_StrLen nFormatLines; 7827 7828 if ( nTextHeight ) 7829 { 7830 ::vcl::DefaultTextLayout aLayout( *m_pReferenceDevice ); 7831 nMaxTextWidth = OutputDevice::ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle, aLayout ); 7832 nLines = (xub_StrLen)(nHeight/nTextHeight); 7833 nFormatLines = aMultiLineInfo.Count(); 7834 if ( !nLines ) 7835 nLines = 1; 7836 if ( nFormatLines > nLines ) 7837 { 7838 if ( nStyle & TEXT_DRAW_ENDELLIPSIS ) 7839 { 7840 // handle last line 7841 nFormatLines = nLines-1; 7842 7843 pLineInfo = aMultiLineInfo.GetLine( nFormatLines ); 7844 aLastLine = aStr.Copy( pLineInfo->GetIndex() ); 7845 aLastLine.ConvertLineEnd( LINEEND_LF ); 7846 // replace line feed by space 7847 xub_StrLen nLastLineLen = aLastLine.Len(); 7848 for ( i = 0; i < nLastLineLen; i++ ) 7849 { 7850 if ( aLastLine.GetChar( i ) == _LF ) 7851 aLastLine.SetChar( i, ' ' ); 7852 } 7853 aLastLine = m_pReferenceDevice->GetEllipsisString( aLastLine, nWidth, nStyle ); 7854 nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM); 7855 nStyle |= TEXT_DRAW_TOP; 7856 } 7857 } 7858 7859 // vertical alignment 7860 if ( nStyle & TEXT_DRAW_BOTTOM ) 7861 aPos.Y() += nHeight-(nFormatLines*nTextHeight); 7862 else if ( nStyle & TEXT_DRAW_VCENTER ) 7863 aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2; 7864 7865 // draw all lines excluding the last 7866 for ( i = 0; i < nFormatLines; i++ ) 7867 { 7868 pLineInfo = aMultiLineInfo.GetLine( i ); 7869 if ( nStyle & TEXT_DRAW_RIGHT ) 7870 aPos.X() += nWidth-pLineInfo->GetWidth(); 7871 else if ( nStyle & TEXT_DRAW_CENTER ) 7872 aPos.X() += (nWidth-pLineInfo->GetWidth())/2; 7873 xub_StrLen nIndex = pLineInfo->GetIndex(); 7874 xub_StrLen nLineLen = pLineInfo->GetLen(); 7875 drawText( aPos, aStr, nIndex, nLineLen, bTextLines ); 7876 // mnemonics should not appear in documents, 7877 // if the need arises, put them in here 7878 aPos.Y() += nTextHeight; 7879 aPos.X() = rRect.Left(); 7880 } 7881 7882 7883 // output last line left adjusted since it was shortened 7884 if ( aLastLine.Len() ) 7885 drawText( aPos, aLastLine, 0, STRING_LEN, bTextLines ); 7886 } 7887 } 7888 else 7889 { 7890 long nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); 7891 7892 // Evt. Text kuerzen 7893 if ( nTextWidth > nWidth ) 7894 { 7895 if ( nStyle & (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS) ) 7896 { 7897 aStr = m_pReferenceDevice->GetEllipsisString( aStr, nWidth, nStyle ); 7898 nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT); 7899 nStyle |= TEXT_DRAW_LEFT; 7900 nTextWidth = m_pReferenceDevice->GetTextWidth( aStr ); 7901 } 7902 } 7903 7904 // vertical alignment 7905 if ( nStyle & TEXT_DRAW_RIGHT ) 7906 aPos.X() += nWidth-nTextWidth; 7907 else if ( nStyle & TEXT_DRAW_CENTER ) 7908 aPos.X() += (nWidth-nTextWidth)/2; 7909 7910 if ( nStyle & TEXT_DRAW_BOTTOM ) 7911 aPos.Y() += nHeight-nTextHeight; 7912 else if ( nStyle & TEXT_DRAW_VCENTER ) 7913 aPos.Y() += (nHeight-nTextHeight)/2; 7914 7915 // mnemonics should be inserted here if the need arises 7916 7917 // draw the actual text 7918 drawText( aPos, aStr, 0, STRING_LEN, bTextLines ); 7919 } 7920 7921 // reset clip region to original value 7922 aLine.setLength( 0 ); 7923 aLine.append( "Q\n" ); 7924 writeBuffer( aLine.getStr(), aLine.getLength() ); 7925 } 7926 7927 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop ) 7928 { 7929 MARK( "drawLine" ); 7930 7931 updateGraphicsState(); 7932 7933 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7934 return; 7935 7936 OStringBuffer aLine; 7937 m_aPages.back().appendPoint( rStart, aLine ); 7938 aLine.append( " m " ); 7939 m_aPages.back().appendPoint( rStop, aLine ); 7940 aLine.append( " l S\n" ); 7941 7942 writeBuffer( aLine.getStr(), aLine.getLength() ); 7943 } 7944 7945 void PDFWriterImpl::drawLine( const Point& rStart, const Point& rStop, const LineInfo& rInfo ) 7946 { 7947 MARK( "drawLine with LineInfo" ); 7948 updateGraphicsState(); 7949 7950 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7951 return; 7952 7953 if( rInfo.GetStyle() == LINE_SOLID && rInfo.GetWidth() < 2 ) 7954 { 7955 drawLine( rStart, rStop ); 7956 return; 7957 } 7958 7959 OStringBuffer aLine; 7960 7961 aLine.append( "q " ); 7962 if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) 7963 { 7964 m_aPages.back().appendPoint( rStart, aLine ); 7965 aLine.append( " m " ); 7966 m_aPages.back().appendPoint( rStop, aLine ); 7967 aLine.append( " l S Q\n" ); 7968 7969 writeBuffer( aLine.getStr(), aLine.getLength() ); 7970 } 7971 else 7972 { 7973 PDFWriter::ExtLineInfo aInfo; 7974 convertLineInfoToExtLineInfo( rInfo, aInfo ); 7975 Point aPolyPoints[2] = { rStart, rStop }; 7976 Polygon aPoly( 2, aPolyPoints ); 7977 drawPolyLine( aPoly, aInfo ); 7978 } 7979 } 7980 7981 void PDFWriterImpl::drawWaveLine( const Point& rStart, const Point& rStop, sal_Int32 nDelta, sal_Int32 nLineWidth ) 7982 { 7983 Point aDiff( rStop-rStart ); 7984 double fLen = sqrt( (double)(aDiff.X()*aDiff.X() + aDiff.Y()*aDiff.Y()) ); 7985 if( fLen < 1.0 ) 7986 return; 7987 7988 MARK( "drawWaveLine" ); 7989 updateGraphicsState(); 7990 7991 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 7992 return; 7993 7994 OStringBuffer aLine( 512 ); 7995 aLine.append( "q " ); 7996 m_aPages.back().appendMappedLength( nLineWidth, aLine, true ); 7997 aLine.append( " w " ); 7998 7999 appendDouble( (double)aDiff.X()/fLen, aLine ); 8000 aLine.append( ' ' ); 8001 appendDouble( -(double)aDiff.Y()/fLen, aLine ); 8002 aLine.append( ' ' ); 8003 appendDouble( (double)aDiff.Y()/fLen, aLine ); 8004 aLine.append( ' ' ); 8005 appendDouble( (double)aDiff.X()/fLen, aLine ); 8006 aLine.append( ' ' ); 8007 m_aPages.back().appendPoint( rStart, aLine ); 8008 aLine.append( " cm " ); 8009 m_aPages.back().appendWaveLine( (sal_Int32)fLen, 0, nDelta, aLine ); 8010 aLine.append( "Q\n" ); 8011 writeBuffer( aLine.getStr(), aLine.getLength() ); 8012 } 8013 8014 #define WCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicWidth( x ) 8015 #define HCONV( x ) m_pReferenceDevice->ImplDevicePixelToLogicHeight( x ) 8016 8017 void PDFWriterImpl::drawWaveTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ) 8018 { 8019 // note: units in pFontEntry are ref device pixel 8020 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8021 long nLineHeight = 0; 8022 long nLinePos = 0; 8023 8024 appendStrokingColor( aColor, aLine ); 8025 aLine.append( "\n" ); 8026 8027 if ( bIsAbove ) 8028 { 8029 if ( !pFontEntry->maMetric.mnAboveWUnderlineSize ) 8030 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8031 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveWUnderlineSize ); 8032 nLinePos = HCONV( pFontEntry->maMetric.mnAboveWUnderlineOffset ); 8033 } 8034 else 8035 { 8036 if ( !pFontEntry->maMetric.mnWUnderlineSize ) 8037 m_pReferenceDevice->ImplInitTextLineSize(); 8038 nLineHeight = HCONV( pFontEntry->maMetric.mnWUnderlineSize ); 8039 nLinePos = HCONV( pFontEntry->maMetric.mnWUnderlineOffset ); 8040 } 8041 if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) ) 8042 nLineHeight = 3; 8043 8044 long nLineWidth = getReferenceDevice()->mnDPIX/450; 8045 if ( ! nLineWidth ) 8046 nLineWidth = 1; 8047 8048 if ( eTextLine == UNDERLINE_BOLDWAVE ) 8049 nLineWidth = 3*nLineWidth; 8050 8051 m_aPages.back().appendMappedLength( (sal_Int32)nLineWidth, aLine ); 8052 aLine.append( " w " ); 8053 8054 if ( eTextLine == UNDERLINE_DOUBLEWAVE ) 8055 { 8056 long nOrgLineHeight = nLineHeight; 8057 nLineHeight /= 3; 8058 if ( nLineHeight < 2 ) 8059 { 8060 if ( nOrgLineHeight > 1 ) 8061 nLineHeight = 2; 8062 else 8063 nLineHeight = 1; 8064 } 8065 long nLineDY = nOrgLineHeight-(nLineHeight*2); 8066 if ( nLineDY < nLineWidth ) 8067 nLineDY = nLineWidth; 8068 long nLineDY2 = nLineDY/2; 8069 if ( !nLineDY2 ) 8070 nLineDY2 = 1; 8071 8072 nLinePos -= nLineWidth-nLineDY2; 8073 8074 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); 8075 8076 nLinePos += nLineWidth+nLineDY; 8077 m_aPages.back().appendWaveLine( nWidth, -nLinePos, 2*nLineHeight, aLine ); 8078 } 8079 else 8080 { 8081 if ( eTextLine != UNDERLINE_BOLDWAVE ) 8082 nLinePos -= nLineWidth/2; 8083 m_aPages.back().appendWaveLine( nWidth, -nLinePos, nLineHeight, aLine ); 8084 } 8085 } 8086 8087 void PDFWriterImpl::drawStraightTextLine( OStringBuffer& aLine, long nWidth, FontUnderline eTextLine, Color aColor, bool bIsAbove ) 8088 { 8089 // note: units in pFontEntry are ref device pixel 8090 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8091 long nLineHeight = 0; 8092 long nLinePos = 0; 8093 long nLinePos2 = 0; 8094 8095 if ( eTextLine > UNDERLINE_BOLDWAVE ) 8096 eTextLine = UNDERLINE_SINGLE; 8097 8098 switch ( eTextLine ) 8099 { 8100 case UNDERLINE_SINGLE: 8101 case UNDERLINE_DOTTED: 8102 case UNDERLINE_DASH: 8103 case UNDERLINE_LONGDASH: 8104 case UNDERLINE_DASHDOT: 8105 case UNDERLINE_DASHDOTDOT: 8106 if ( bIsAbove ) 8107 { 8108 if ( !pFontEntry->maMetric.mnAboveUnderlineSize ) 8109 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8110 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveUnderlineSize ); 8111 nLinePos = HCONV( pFontEntry->maMetric.mnAboveUnderlineOffset ); 8112 } 8113 else 8114 { 8115 if ( !pFontEntry->maMetric.mnUnderlineSize ) 8116 m_pReferenceDevice->ImplInitTextLineSize(); 8117 nLineHeight = HCONV( pFontEntry->maMetric.mnUnderlineSize ); 8118 nLinePos = HCONV( pFontEntry->maMetric.mnUnderlineOffset ); 8119 } 8120 break; 8121 case UNDERLINE_BOLD: 8122 case UNDERLINE_BOLDDOTTED: 8123 case UNDERLINE_BOLDDASH: 8124 case UNDERLINE_BOLDLONGDASH: 8125 case UNDERLINE_BOLDDASHDOT: 8126 case UNDERLINE_BOLDDASHDOTDOT: 8127 if ( bIsAbove ) 8128 { 8129 if ( !pFontEntry->maMetric.mnAboveBUnderlineSize ) 8130 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8131 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveBUnderlineSize ); 8132 nLinePos = HCONV( pFontEntry->maMetric.mnAboveBUnderlineOffset ); 8133 } 8134 else 8135 { 8136 if ( !pFontEntry->maMetric.mnBUnderlineSize ) 8137 m_pReferenceDevice->ImplInitTextLineSize(); 8138 nLineHeight = HCONV( pFontEntry->maMetric.mnBUnderlineSize ); 8139 nLinePos = HCONV( pFontEntry->maMetric.mnBUnderlineOffset ); 8140 nLinePos += nLineHeight/2; 8141 } 8142 break; 8143 case UNDERLINE_DOUBLE: 8144 if ( bIsAbove ) 8145 { 8146 if ( !pFontEntry->maMetric.mnAboveDUnderlineSize ) 8147 m_pReferenceDevice->ImplInitAboveTextLineSize(); 8148 nLineHeight = HCONV( pFontEntry->maMetric.mnAboveDUnderlineSize ); 8149 nLinePos = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset1 ); 8150 nLinePos2 = HCONV( pFontEntry->maMetric.mnAboveDUnderlineOffset2 ); 8151 } 8152 else 8153 { 8154 if ( !pFontEntry->maMetric.mnDUnderlineSize ) 8155 m_pReferenceDevice->ImplInitTextLineSize(); 8156 nLineHeight = HCONV( pFontEntry->maMetric.mnDUnderlineSize ); 8157 nLinePos = HCONV( pFontEntry->maMetric.mnDUnderlineOffset1 ); 8158 nLinePos2 = HCONV( pFontEntry->maMetric.mnDUnderlineOffset2 ); 8159 } 8160 default: 8161 break; 8162 } 8163 8164 if ( nLineHeight ) 8165 { 8166 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true ); 8167 aLine.append( " w " ); 8168 appendStrokingColor( aColor, aLine ); 8169 aLine.append( "\n" ); 8170 8171 switch ( eTextLine ) 8172 { 8173 case UNDERLINE_DOTTED: 8174 case UNDERLINE_BOLDDOTTED: 8175 aLine.append( "[ " ); 8176 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8177 aLine.append( " ] 0 d\n" ); 8178 break; 8179 case UNDERLINE_DASH: 8180 case UNDERLINE_LONGDASH: 8181 case UNDERLINE_BOLDDASH: 8182 case UNDERLINE_BOLDLONGDASH: 8183 { 8184 sal_Int32 nDashLength = 4*nLineHeight; 8185 sal_Int32 nVoidLength = 2*nLineHeight; 8186 if ( ( eTextLine == UNDERLINE_LONGDASH ) || ( eTextLine == UNDERLINE_BOLDLONGDASH ) ) 8187 nDashLength = 8*nLineHeight; 8188 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( " ] 0 d\n" ); 8194 } 8195 break; 8196 case UNDERLINE_DASHDOT: 8197 case UNDERLINE_BOLDDASHDOT: 8198 { 8199 sal_Int32 nDashLength = 4*nLineHeight; 8200 sal_Int32 nVoidLength = 2*nLineHeight; 8201 aLine.append( "[ " ); 8202 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 8203 aLine.append( ' ' ); 8204 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8205 aLine.append( ' ' ); 8206 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8207 aLine.append( ' ' ); 8208 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8209 aLine.append( " ] 0 d\n" ); 8210 } 8211 break; 8212 case UNDERLINE_DASHDOTDOT: 8213 case UNDERLINE_BOLDDASHDOTDOT: 8214 { 8215 sal_Int32 nDashLength = 4*nLineHeight; 8216 sal_Int32 nVoidLength = 2*nLineHeight; 8217 aLine.append( "[ " ); 8218 m_aPages.back().appendMappedLength( nDashLength, aLine, false ); 8219 aLine.append( ' ' ); 8220 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8221 aLine.append( ' ' ); 8222 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8223 aLine.append( ' ' ); 8224 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8225 aLine.append( ' ' ); 8226 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, false ); 8227 aLine.append( ' ' ); 8228 m_aPages.back().appendMappedLength( nVoidLength, aLine, false ); 8229 aLine.append( " ] 0 d\n" ); 8230 } 8231 break; 8232 default: 8233 break; 8234 } 8235 8236 aLine.append( "0 " ); 8237 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8238 aLine.append( " m " ); 8239 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); 8240 aLine.append( ' ' ); 8241 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8242 aLine.append( " l S\n" ); 8243 if ( eTextLine == UNDERLINE_DOUBLE ) 8244 { 8245 aLine.append( "0 " ); 8246 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8247 aLine.append( " m " ); 8248 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, false ); 8249 aLine.append( ' ' ); 8250 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8251 aLine.append( " l S\n" ); 8252 } 8253 } 8254 } 8255 8256 void PDFWriterImpl::drawStrikeoutLine( OStringBuffer& aLine, long nWidth, FontStrikeout eStrikeout, Color aColor ) 8257 { 8258 // note: units in pFontEntry are ref device pixel 8259 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8260 long nLineHeight = 0; 8261 long nLinePos = 0; 8262 long nLinePos2 = 0; 8263 8264 if ( eStrikeout > STRIKEOUT_X ) 8265 eStrikeout = STRIKEOUT_SINGLE; 8266 8267 switch ( eStrikeout ) 8268 { 8269 case STRIKEOUT_SINGLE: 8270 if ( !pFontEntry->maMetric.mnStrikeoutSize ) 8271 m_pReferenceDevice->ImplInitTextLineSize(); 8272 nLineHeight = HCONV( pFontEntry->maMetric.mnStrikeoutSize ); 8273 nLinePos = HCONV( pFontEntry->maMetric.mnStrikeoutOffset ); 8274 break; 8275 case STRIKEOUT_BOLD: 8276 if ( !pFontEntry->maMetric.mnBStrikeoutSize ) 8277 m_pReferenceDevice->ImplInitTextLineSize(); 8278 nLineHeight = HCONV( pFontEntry->maMetric.mnBStrikeoutSize ); 8279 nLinePos = HCONV( pFontEntry->maMetric.mnBStrikeoutOffset ); 8280 break; 8281 case STRIKEOUT_DOUBLE: 8282 if ( !pFontEntry->maMetric.mnDStrikeoutSize ) 8283 m_pReferenceDevice->ImplInitTextLineSize(); 8284 nLineHeight = HCONV( pFontEntry->maMetric.mnDStrikeoutSize ); 8285 nLinePos = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset1 ); 8286 nLinePos2 = HCONV( pFontEntry->maMetric.mnDStrikeoutOffset2 ); 8287 break; 8288 default: 8289 break; 8290 } 8291 8292 if ( nLineHeight ) 8293 { 8294 m_aPages.back().appendMappedLength( (sal_Int32)nLineHeight, aLine, true ); 8295 aLine.append( " w " ); 8296 appendStrokingColor( aColor, aLine ); 8297 aLine.append( "\n" ); 8298 8299 aLine.append( "0 " ); 8300 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8301 aLine.append( " m " ); 8302 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true ); 8303 aLine.append( ' ' ); 8304 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos), aLine, true ); 8305 aLine.append( " l S\n" ); 8306 8307 if ( eStrikeout == STRIKEOUT_DOUBLE ) 8308 { 8309 aLine.append( "0 " ); 8310 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8311 aLine.append( " m " ); 8312 m_aPages.back().appendMappedLength( (sal_Int32)nWidth, aLine, true ); 8313 aLine.append( ' ' ); 8314 m_aPages.back().appendMappedLength( (sal_Int32)(-nLinePos2-nLineHeight), aLine, true ); 8315 aLine.append( " l S\n" ); 8316 } 8317 } 8318 } 8319 8320 void PDFWriterImpl::drawStrikeoutChar( const Point& rPos, long nWidth, FontStrikeout eStrikeout ) 8321 { 8322 String aStrikeoutChar = String::CreateFromAscii( eStrikeout == STRIKEOUT_SLASH ? "/" : "X" ); 8323 String aStrikeout = aStrikeoutChar; 8324 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) < nWidth ) 8325 aStrikeout.Append( aStrikeout ); 8326 8327 // do not get broader than nWidth modulo 1 character 8328 while( m_pReferenceDevice->GetTextWidth( aStrikeout ) >= nWidth ) 8329 aStrikeout.Erase( 0, 1 ); 8330 aStrikeout.Append( aStrikeoutChar ); 8331 sal_Bool bShadow = m_aCurrentPDFState.m_aFont.IsShadow(); 8332 if ( bShadow ) 8333 { 8334 Font aFont = m_aCurrentPDFState.m_aFont; 8335 aFont.SetShadow( sal_False ); 8336 setFont( aFont ); 8337 updateGraphicsState(); 8338 } 8339 8340 // strikeout string is left aligned non-CTL text 8341 sal_uLong nOrigTLM = m_pReferenceDevice->GetLayoutMode(); 8342 m_pReferenceDevice->SetLayoutMode( TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_COMPLEX_DISABLED ); 8343 drawText( rPos, aStrikeout, 0, aStrikeout.Len(), false ); 8344 m_pReferenceDevice->SetLayoutMode( nOrigTLM ); 8345 8346 if ( bShadow ) 8347 { 8348 Font aFont = m_aCurrentPDFState.m_aFont; 8349 aFont.SetShadow( sal_True ); 8350 setFont( aFont ); 8351 updateGraphicsState(); 8352 } 8353 } 8354 8355 void PDFWriterImpl::drawTextLine( const Point& rPos, long nWidth, FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, bool bUnderlineAbove ) 8356 { 8357 if ( !nWidth || 8358 ( ((eStrikeout == STRIKEOUT_NONE)||(eStrikeout == STRIKEOUT_DONTKNOW)) && 8359 ((eUnderline == UNDERLINE_NONE)||(eUnderline == UNDERLINE_DONTKNOW)) && 8360 ((eOverline == UNDERLINE_NONE)||(eOverline == UNDERLINE_DONTKNOW)) ) ) 8361 return; 8362 8363 MARK( "drawTextLine" ); 8364 updateGraphicsState(); 8365 8366 // note: units in pFontEntry are ref device pixel 8367 ImplFontEntry* pFontEntry = m_pReferenceDevice->mpFontEntry; 8368 Color aUnderlineColor = m_aCurrentPDFState.m_aTextLineColor; 8369 Color aOverlineColor = m_aCurrentPDFState.m_aOverlineColor; 8370 Color aStrikeoutColor = m_aCurrentPDFState.m_aFont.GetColor(); 8371 bool bStrikeoutDone = false; 8372 bool bUnderlineDone = false; 8373 bool bOverlineDone = false; 8374 8375 if ( (eStrikeout == STRIKEOUT_SLASH) || (eStrikeout == STRIKEOUT_X) ) 8376 { 8377 drawStrikeoutChar( rPos, nWidth, eStrikeout ); 8378 bStrikeoutDone = true; 8379 } 8380 8381 Point aPos( rPos ); 8382 TextAlign eAlign = m_aCurrentPDFState.m_aFont.GetAlign(); 8383 if( eAlign == ALIGN_TOP ) 8384 aPos.Y() += HCONV( pFontEntry->maMetric.mnAscent ); 8385 else if( eAlign == ALIGN_BOTTOM ) 8386 aPos.Y() -= HCONV( pFontEntry->maMetric.mnDescent ); 8387 8388 OStringBuffer aLine( 512 ); 8389 // save GS 8390 aLine.append( "q " ); 8391 8392 // rotate and translate matrix 8393 double fAngle = (double)m_aCurrentPDFState.m_aFont.GetOrientation() * M_PI / 1800.0; 8394 Matrix3 aMat; 8395 aMat.rotate( fAngle ); 8396 aMat.translate( aPos.X(), aPos.Y() ); 8397 aMat.append( m_aPages.back(), aLine ); 8398 aLine.append( " cm\n" ); 8399 8400 if ( aUnderlineColor.GetTransparency() != 0 ) 8401 aUnderlineColor = aStrikeoutColor; 8402 8403 if ( (eUnderline == UNDERLINE_SMALLWAVE) || 8404 (eUnderline == UNDERLINE_WAVE) || 8405 (eUnderline == UNDERLINE_DOUBLEWAVE) || 8406 (eUnderline == UNDERLINE_BOLDWAVE) ) 8407 { 8408 drawWaveTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 8409 bUnderlineDone = true; 8410 } 8411 8412 if ( (eOverline == UNDERLINE_SMALLWAVE) || 8413 (eOverline == UNDERLINE_WAVE) || 8414 (eOverline == UNDERLINE_DOUBLEWAVE) || 8415 (eOverline == UNDERLINE_BOLDWAVE) ) 8416 { 8417 drawWaveTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); 8418 bOverlineDone = true; 8419 } 8420 8421 if ( !bUnderlineDone ) 8422 { 8423 drawStraightTextLine( aLine, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove ); 8424 } 8425 8426 if ( !bOverlineDone ) 8427 { 8428 drawStraightTextLine( aLine, nWidth, eOverline, aOverlineColor, true ); 8429 } 8430 8431 if ( !bStrikeoutDone ) 8432 { 8433 drawStrikeoutLine( aLine, nWidth, eStrikeout, aStrikeoutColor ); 8434 } 8435 8436 aLine.append( "Q\n" ); 8437 writeBuffer( aLine.getStr(), aLine.getLength() ); 8438 } 8439 8440 void PDFWriterImpl::drawPolygon( const Polygon& rPoly ) 8441 { 8442 MARK( "drawPolygon" ); 8443 8444 updateGraphicsState(); 8445 8446 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8447 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8448 return; 8449 8450 int nPoints = rPoly.GetSize(); 8451 OStringBuffer aLine( 20 * nPoints ); 8452 m_aPages.back().appendPolygon( rPoly, aLine ); 8453 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8454 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8455 aLine.append( "B*\n" ); 8456 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8457 aLine.append( "S\n" ); 8458 else 8459 aLine.append( "f*\n" ); 8460 8461 writeBuffer( aLine.getStr(), aLine.getLength() ); 8462 } 8463 8464 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly ) 8465 { 8466 MARK( "drawPolyPolygon" ); 8467 8468 updateGraphicsState(); 8469 8470 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8471 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8472 return; 8473 8474 int nPolygons = rPolyPoly.Count(); 8475 8476 OStringBuffer aLine( 40 * nPolygons ); 8477 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 8478 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8479 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8480 aLine.append( "B*\n" ); 8481 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8482 aLine.append( "S\n" ); 8483 else 8484 aLine.append( "f*\n" ); 8485 8486 writeBuffer( aLine.getStr(), aLine.getLength() ); 8487 } 8488 8489 void PDFWriterImpl::drawTransparent( const PolyPolygon& rPolyPoly, sal_uInt32 nTransparentPercent ) 8490 { 8491 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" ); 8492 nTransparentPercent = nTransparentPercent % 100; 8493 8494 MARK( "drawTransparent" ); 8495 8496 updateGraphicsState(); 8497 8498 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8499 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8500 return; 8501 8502 if( m_bIsPDF_A1 || m_aContext.Version < PDFWriter::PDF_1_4 ) 8503 { 8504 m_aErrors.insert( m_bIsPDF_A1 ? 8505 PDFWriter::Warning_Transparency_Omitted_PDFA : 8506 PDFWriter::Warning_Transparency_Omitted_PDF13 ); 8507 8508 drawPolyPolygon( rPolyPoly ); 8509 return; 8510 } 8511 8512 // create XObject 8513 m_aTransparentObjects.push_back( TransparencyEmit() ); 8514 // FIXME: polygons with beziers may yield incorrect bound rect 8515 m_aTransparentObjects.back().m_aBoundRect = rPolyPoly.GetBoundRect(); 8516 // convert rectangle to default user space 8517 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8518 m_aTransparentObjects.back().m_nObject = createObject(); 8519 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8520 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; 8521 m_aTransparentObjects.back().m_pContentStream = new SvMemoryStream( 256, 256 ); 8522 // create XObject's content stream 8523 OStringBuffer aContent( 256 ); 8524 m_aPages.back().appendPolyPolygon( rPolyPoly, aContent ); 8525 if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) && 8526 m_aCurrentPDFState.m_aFillColor != Color( COL_TRANSPARENT ) ) 8527 aContent.append( " B*\n" ); 8528 else if( m_aCurrentPDFState.m_aLineColor != Color( COL_TRANSPARENT ) ) 8529 aContent.append( " S\n" ); 8530 else 8531 aContent.append( " f*\n" ); 8532 m_aTransparentObjects.back().m_pContentStream->Write( aContent.getStr(), aContent.getLength() ); 8533 8534 OStringBuffer aObjName( 16 ); 8535 aObjName.append( "Tr" ); 8536 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8537 OString aTrName( aObjName.makeStringAndClear() ); 8538 aObjName.append( "EGS" ); 8539 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8540 OString aExtName( aObjName.makeStringAndClear() ); 8541 8542 OStringBuffer aLine( 80 ); 8543 // insert XObject 8544 aLine.append( "q /" ); 8545 aLine.append( aExtName ); 8546 aLine.append( " gs /" ); 8547 aLine.append( aTrName ); 8548 aLine.append( " Do Q\n" ); 8549 writeBuffer( aLine.getStr(), aLine.getLength() ); 8550 8551 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8552 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8553 } 8554 8555 void PDFWriterImpl::pushResource( ResourceKind eKind, const OString& rResource, sal_Int32 nObject ) 8556 { 8557 if( nObject >= 0 ) 8558 { 8559 switch( eKind ) 8560 { 8561 case ResXObject: 8562 m_aGlobalResourceDict.m_aXObjects[ rResource ] = nObject; 8563 if( ! m_aOutputStreams.empty() ) 8564 m_aOutputStreams.front().m_aResourceDict.m_aXObjects[ rResource ] = nObject; 8565 break; 8566 case ResExtGState: 8567 m_aGlobalResourceDict.m_aExtGStates[ rResource ] = nObject; 8568 if( ! m_aOutputStreams.empty() ) 8569 m_aOutputStreams.front().m_aResourceDict.m_aExtGStates[ rResource ] = nObject; 8570 break; 8571 case ResShading: 8572 m_aGlobalResourceDict.m_aShadings[ rResource ] = nObject; 8573 if( ! m_aOutputStreams.empty() ) 8574 m_aOutputStreams.front().m_aResourceDict.m_aShadings[ rResource ] = nObject; 8575 break; 8576 case ResPattern: 8577 m_aGlobalResourceDict.m_aPatterns[ rResource ] = nObject; 8578 if( ! m_aOutputStreams.empty() ) 8579 m_aOutputStreams.front().m_aResourceDict.m_aPatterns[ rResource ] = nObject; 8580 break; 8581 } 8582 } 8583 } 8584 8585 void PDFWriterImpl::beginRedirect( SvStream* pStream, const Rectangle& rTargetRect ) 8586 { 8587 push( PUSH_ALL ); 8588 8589 // force reemitting clip region 8590 clearClipRegion(); 8591 updateGraphicsState(); 8592 8593 m_aOutputStreams.push_front( StreamRedirect() ); 8594 m_aOutputStreams.front().m_pStream = pStream; 8595 m_aOutputStreams.front().m_aMapMode = m_aMapMode; 8596 8597 if( !rTargetRect.IsEmpty() ) 8598 { 8599 m_aOutputStreams.front().m_aTargetRect = 8600 lcl_convert( m_aGraphicsStack.front().m_aMapMode, 8601 m_aMapMode, 8602 getReferenceDevice(), 8603 rTargetRect ); 8604 Point aDelta = m_aOutputStreams.front().m_aTargetRect.BottomLeft(); 8605 long nPageHeight = pointToPixel(m_aPages[m_nCurrentPage].getHeight()); 8606 aDelta.Y() = -(nPageHeight - m_aOutputStreams.front().m_aTargetRect.Bottom()); 8607 m_aMapMode.SetOrigin( m_aMapMode.GetOrigin() + aDelta ); 8608 } 8609 8610 // setup graphics state for independent object stream 8611 8612 // force reemitting colors 8613 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); 8614 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); 8615 } 8616 8617 Rectangle PDFWriterImpl::getRedirectTargetRect() const 8618 { 8619 return m_aOutputStreams.empty() ? Rectangle() : m_aOutputStreams.front().m_aTargetRect; 8620 } 8621 8622 SvStream* PDFWriterImpl::endRedirect() 8623 { 8624 SvStream* pStream = NULL; 8625 if( ! m_aOutputStreams.empty() ) 8626 { 8627 pStream = m_aOutputStreams.front().m_pStream; 8628 m_aMapMode = m_aOutputStreams.front().m_aMapMode; 8629 m_aOutputStreams.pop_front(); 8630 } 8631 8632 pop(); 8633 // force reemitting colors and clip region 8634 clearClipRegion(); 8635 m_aCurrentPDFState.m_bClipRegion = m_aGraphicsStack.front().m_bClipRegion; 8636 m_aCurrentPDFState.m_aClipRegion = m_aGraphicsStack.front().m_aClipRegion; 8637 m_aCurrentPDFState.m_aLineColor = Color( COL_TRANSPARENT ); 8638 m_aCurrentPDFState.m_aFillColor = Color( COL_TRANSPARENT ); 8639 8640 updateGraphicsState(); 8641 8642 return pStream; 8643 } 8644 8645 void PDFWriterImpl::beginTransparencyGroup() 8646 { 8647 updateGraphicsState(); 8648 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8649 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() ); 8650 } 8651 8652 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, sal_uInt32 nTransparentPercent ) 8653 { 8654 DBG_ASSERT( nTransparentPercent <= 100, "invalid alpha value" ); 8655 nTransparentPercent = nTransparentPercent % 100; 8656 8657 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8658 { 8659 // create XObject 8660 m_aTransparentObjects.push_back( TransparencyEmit() ); 8661 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox; 8662 // convert rectangle to default user space 8663 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8664 m_aTransparentObjects.back().m_nObject = createObject(); 8665 m_aTransparentObjects.back().m_fAlpha = (double)(100-nTransparentPercent) / 100.0; 8666 // get XObject's content stream 8667 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect()); 8668 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8669 8670 OStringBuffer aObjName( 16 ); 8671 aObjName.append( "Tr" ); 8672 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8673 OString aTrName( aObjName.makeStringAndClear() ); 8674 aObjName.append( "EGS" ); 8675 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8676 OString aExtName( aObjName.makeStringAndClear() ); 8677 8678 OStringBuffer aLine( 80 ); 8679 // insert XObject 8680 aLine.append( "q /" ); 8681 aLine.append( aExtName ); 8682 aLine.append( " gs /" ); 8683 aLine.append( aTrName ); 8684 aLine.append( " Do Q\n" ); 8685 writeBuffer( aLine.getStr(), aLine.getLength() ); 8686 8687 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8688 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8689 } 8690 } 8691 8692 void PDFWriterImpl::endTransparencyGroup( const Rectangle& rBoundingBox, const Bitmap& rAlphaMask ) 8693 { 8694 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 8695 { 8696 // create XObject 8697 m_aTransparentObjects.push_back( TransparencyEmit() ); 8698 m_aTransparentObjects.back().m_aBoundRect = rBoundingBox; 8699 // convert rectangle to default user space 8700 m_aPages.back().convertRect( m_aTransparentObjects.back().m_aBoundRect ); 8701 m_aTransparentObjects.back().m_nObject = createObject(); 8702 m_aTransparentObjects.back().m_fAlpha = 0.0; 8703 // get XObject's content stream 8704 m_aTransparentObjects.back().m_pContentStream = static_cast<SvMemoryStream*>(endRedirect()); 8705 m_aTransparentObjects.back().m_nExtGStateObject = createObject(); 8706 8707 // draw soft mask 8708 beginRedirect( new SvMemoryStream( 1024, 1024 ), Rectangle() ); 8709 drawBitmap( rBoundingBox.TopLeft(), rBoundingBox.GetSize(), rAlphaMask ); 8710 m_aTransparentObjects.back().m_pSoftMaskStream = static_cast<SvMemoryStream*>(endRedirect()); 8711 8712 OStringBuffer aObjName( 16 ); 8713 aObjName.append( "Tr" ); 8714 aObjName.append( m_aTransparentObjects.back().m_nObject ); 8715 OString aTrName( aObjName.makeStringAndClear() ); 8716 aObjName.append( "EGS" ); 8717 aObjName.append( m_aTransparentObjects.back().m_nExtGStateObject ); 8718 OString aExtName( aObjName.makeStringAndClear() ); 8719 8720 OStringBuffer aLine( 80 ); 8721 // insert XObject 8722 aLine.append( "q /" ); 8723 aLine.append( aExtName ); 8724 aLine.append( " gs /" ); 8725 aLine.append( aTrName ); 8726 aLine.append( " Do Q\n" ); 8727 writeBuffer( aLine.getStr(), aLine.getLength() ); 8728 8729 pushResource( ResXObject, aTrName, m_aTransparentObjects.back().m_nObject ); 8730 pushResource( ResExtGState, aExtName, m_aTransparentObjects.back().m_nExtGStateObject ); 8731 } 8732 } 8733 8734 void PDFWriterImpl::drawRectangle( const Rectangle& rRect ) 8735 { 8736 MARK( "drawRectangle" ); 8737 8738 updateGraphicsState(); 8739 8740 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8741 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8742 return; 8743 8744 OStringBuffer aLine( 40 ); 8745 m_aPages.back().appendRect( rRect, aLine ); 8746 8747 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8748 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8749 aLine.append( " B*\n" ); 8750 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8751 aLine.append( " S\n" ); 8752 else 8753 aLine.append( " f*\n" ); 8754 8755 writeBuffer( aLine.getStr(), aLine.getLength() ); 8756 } 8757 8758 void PDFWriterImpl::drawRectangle( const Rectangle& rRect, sal_uInt32 nHorzRound, sal_uInt32 nVertRound ) 8759 { 8760 MARK( "drawRectangle with rounded edges" ); 8761 8762 if( !nHorzRound && !nVertRound ) 8763 drawRectangle( rRect ); 8764 8765 updateGraphicsState(); 8766 8767 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8768 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8769 return; 8770 8771 if( nHorzRound > (sal_uInt32)rRect.GetWidth()/2 ) 8772 nHorzRound = rRect.GetWidth()/2; 8773 if( nVertRound > (sal_uInt32)rRect.GetHeight()/2 ) 8774 nVertRound = rRect.GetHeight()/2; 8775 8776 Point aPoints[16]; 8777 const double kappa = 0.5522847498; 8778 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)nHorzRound)+0.5); 8779 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)nVertRound)+0.5); 8780 8781 aPoints[1] = Point( rRect.TopLeft().X() + nHorzRound, rRect.TopLeft().Y() ); 8782 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); 8783 aPoints[2] = Point( rRect.TopRight().X()+1 - nHorzRound, aPoints[1].Y() ); 8784 aPoints[3] = Point( aPoints[2].X()+kx, aPoints[2].Y() ); 8785 8786 aPoints[5] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y()+nVertRound ); 8787 aPoints[4] = Point( aPoints[5].X(), aPoints[5].Y()-ky ); 8788 aPoints[6] = Point( aPoints[5].X(), rRect.BottomRight().Y()+1 - nVertRound ); 8789 aPoints[7] = Point( aPoints[6].X(), aPoints[6].Y()+ky ); 8790 8791 aPoints[9] = Point( rRect.BottomRight().X()+1-nHorzRound, rRect.BottomRight().Y()+1 ); 8792 aPoints[8] = Point( aPoints[9].X()+kx, aPoints[9].Y() ); 8793 aPoints[10] = Point( rRect.BottomLeft().X() + nHorzRound, aPoints[9].Y() ); 8794 aPoints[11] = Point( aPoints[10].X()-kx, aPoints[10].Y() ); 8795 8796 aPoints[13] = Point( rRect.BottomLeft().X(), rRect.BottomLeft().Y()+1-nVertRound ); 8797 aPoints[12] = Point( aPoints[13].X(), aPoints[13].Y()+ky ); 8798 aPoints[14] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y()+nVertRound ); 8799 aPoints[15] = Point( aPoints[14].X(), aPoints[14].Y()-ky ); 8800 8801 8802 OStringBuffer aLine( 80 ); 8803 m_aPages.back().appendPoint( aPoints[1], aLine ); 8804 aLine.append( " m " ); 8805 m_aPages.back().appendPoint( aPoints[2], aLine ); 8806 aLine.append( " l " ); 8807 m_aPages.back().appendPoint( aPoints[3], aLine ); 8808 aLine.append( ' ' ); 8809 m_aPages.back().appendPoint( aPoints[4], aLine ); 8810 aLine.append( ' ' ); 8811 m_aPages.back().appendPoint( aPoints[5], aLine ); 8812 aLine.append( " c\n" ); 8813 m_aPages.back().appendPoint( aPoints[6], aLine ); 8814 aLine.append( " l " ); 8815 m_aPages.back().appendPoint( aPoints[7], aLine ); 8816 aLine.append( ' ' ); 8817 m_aPages.back().appendPoint( aPoints[8], aLine ); 8818 aLine.append( ' ' ); 8819 m_aPages.back().appendPoint( aPoints[9], aLine ); 8820 aLine.append( " c\n" ); 8821 m_aPages.back().appendPoint( aPoints[10], aLine ); 8822 aLine.append( " l " ); 8823 m_aPages.back().appendPoint( aPoints[11], aLine ); 8824 aLine.append( ' ' ); 8825 m_aPages.back().appendPoint( aPoints[12], aLine ); 8826 aLine.append( ' ' ); 8827 m_aPages.back().appendPoint( aPoints[13], aLine ); 8828 aLine.append( " c\n" ); 8829 m_aPages.back().appendPoint( aPoints[14], aLine ); 8830 aLine.append( " l " ); 8831 m_aPages.back().appendPoint( aPoints[15], aLine ); 8832 aLine.append( ' ' ); 8833 m_aPages.back().appendPoint( aPoints[0], aLine ); 8834 aLine.append( ' ' ); 8835 m_aPages.back().appendPoint( aPoints[1], aLine ); 8836 aLine.append( " c " ); 8837 8838 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8839 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8840 aLine.append( "b*\n" ); 8841 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8842 aLine.append( "s\n" ); 8843 else 8844 aLine.append( "f*\n" ); 8845 8846 writeBuffer( aLine.getStr(), aLine.getLength() ); 8847 } 8848 8849 void PDFWriterImpl::drawEllipse( const Rectangle& rRect ) 8850 { 8851 MARK( "drawEllipse" ); 8852 8853 updateGraphicsState(); 8854 8855 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8856 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8857 return; 8858 8859 Point aPoints[12]; 8860 const double kappa = 0.5522847498; 8861 const sal_uInt32 kx = (sal_uInt32)((kappa*(double)rRect.GetWidth()/2.0)+0.5); 8862 const sal_uInt32 ky = (sal_uInt32)((kappa*(double)rRect.GetHeight()/2.0)+0.5); 8863 8864 aPoints[1] = Point( rRect.TopLeft().X() + rRect.GetWidth()/2, rRect.TopLeft().Y() ); 8865 aPoints[0] = Point( aPoints[1].X() - kx, aPoints[1].Y() ); 8866 aPoints[2] = Point( aPoints[1].X() + kx, aPoints[1].Y() ); 8867 8868 aPoints[4] = Point( rRect.TopRight().X()+1, rRect.TopRight().Y() + rRect.GetHeight()/2 ); 8869 aPoints[3] = Point( aPoints[4].X(), aPoints[4].Y() - ky ); 8870 aPoints[5] = Point( aPoints[4].X(), aPoints[4].Y() + ky ); 8871 8872 aPoints[7] = Point( rRect.BottomLeft().X() + rRect.GetWidth()/2, rRect.BottomLeft().Y()+1 ); 8873 aPoints[6] = Point( aPoints[7].X() + kx, aPoints[7].Y() ); 8874 aPoints[8] = Point( aPoints[7].X() - kx, aPoints[7].Y() ); 8875 8876 aPoints[10] = Point( rRect.TopLeft().X(), rRect.TopLeft().Y() + rRect.GetHeight()/2 ); 8877 aPoints[9] = Point( aPoints[10].X(), aPoints[10].Y() + ky ); 8878 aPoints[11] = Point( aPoints[10].X(), aPoints[10].Y() - ky ); 8879 8880 OStringBuffer aLine( 80 ); 8881 m_aPages.back().appendPoint( aPoints[1], aLine ); 8882 aLine.append( " m " ); 8883 m_aPages.back().appendPoint( aPoints[2], aLine ); 8884 aLine.append( ' ' ); 8885 m_aPages.back().appendPoint( aPoints[3], aLine ); 8886 aLine.append( ' ' ); 8887 m_aPages.back().appendPoint( aPoints[4], aLine ); 8888 aLine.append( " c\n" ); 8889 m_aPages.back().appendPoint( aPoints[5], aLine ); 8890 aLine.append( ' ' ); 8891 m_aPages.back().appendPoint( aPoints[6], aLine ); 8892 aLine.append( ' ' ); 8893 m_aPages.back().appendPoint( aPoints[7], aLine ); 8894 aLine.append( " c\n" ); 8895 m_aPages.back().appendPoint( aPoints[8], aLine ); 8896 aLine.append( ' ' ); 8897 m_aPages.back().appendPoint( aPoints[9], aLine ); 8898 aLine.append( ' ' ); 8899 m_aPages.back().appendPoint( aPoints[10], aLine ); 8900 aLine.append( " c\n" ); 8901 m_aPages.back().appendPoint( aPoints[11], aLine ); 8902 aLine.append( ' ' ); 8903 m_aPages.back().appendPoint( aPoints[0], aLine ); 8904 aLine.append( ' ' ); 8905 m_aPages.back().appendPoint( aPoints[1], aLine ); 8906 aLine.append( " c " ); 8907 8908 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 8909 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 8910 aLine.append( "b*\n" ); 8911 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 8912 aLine.append( "s\n" ); 8913 else 8914 aLine.append( "f*\n" ); 8915 8916 writeBuffer( aLine.getStr(), aLine.getLength() ); 8917 } 8918 8919 static double calcAngle( const Rectangle& rRect, const Point& rPoint ) 8920 { 8921 Point aOrigin((rRect.Left()+rRect.Right()+1)/2, 8922 (rRect.Top()+rRect.Bottom()+1)/2); 8923 Point aPoint = rPoint - aOrigin; 8924 8925 double fX = (double)aPoint.X(); 8926 double fY = (double)-aPoint.Y(); 8927 8928 if( rRect.GetWidth() > rRect.GetHeight() ) 8929 fY = fY*((double)rRect.GetWidth()/(double)rRect.GetHeight()); 8930 else if( rRect.GetHeight() > rRect.GetWidth() ) 8931 fX = fX*((double)rRect.GetHeight()/(double)rRect.GetWidth()); 8932 return atan2( fY, fX ); 8933 } 8934 8935 void PDFWriterImpl::drawArc( const Rectangle& rRect, const Point& rStart, const Point& rStop, bool bWithPie, bool bWithChord ) 8936 { 8937 MARK( "drawArc" ); 8938 8939 updateGraphicsState(); 8940 8941 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && 8942 m_aGraphicsStack.front().m_aFillColor == Color( COL_TRANSPARENT ) ) 8943 return; 8944 8945 // calculate start and stop angles 8946 const double fStartAngle = calcAngle( rRect, rStart ); 8947 double fStopAngle = calcAngle( rRect, rStop ); 8948 while( fStopAngle < fStartAngle ) 8949 fStopAngle += 2.0*M_PI; 8950 const int nFragments = (int)((fStopAngle-fStartAngle)/(M_PI/2.0))+1; 8951 const double fFragmentDelta = (fStopAngle-fStartAngle)/(double)nFragments; 8952 const double kappa = fabs( 4.0 * (1.0-cos(fFragmentDelta/2.0))/sin(fFragmentDelta/2.0) / 3.0); 8953 const double halfWidth = (double)rRect.GetWidth()/2.0; 8954 const double halfHeight = (double)rRect.GetHeight()/2.0; 8955 8956 const Point aCenter( (rRect.Left()+rRect.Right()+1)/2, 8957 (rRect.Top()+rRect.Bottom()+1)/2 ); 8958 8959 OStringBuffer aLine( 30*nFragments ); 8960 Point aPoint( (int)(halfWidth * cos(fStartAngle) ), 8961 -(int)(halfHeight * sin(fStartAngle) ) ); 8962 aPoint += aCenter; 8963 m_aPages.back().appendPoint( aPoint, aLine ); 8964 aLine.append( " m " ); 8965 if( !basegfx::fTools::equal(fStartAngle, fStopAngle) ) 8966 { 8967 for( int i = 0; i < nFragments; i++ ) 8968 { 8969 const double fStartFragment = fStartAngle + (double)i*fFragmentDelta; 8970 const double fStopFragment = fStartFragment + fFragmentDelta; 8971 aPoint = Point( (int)(halfWidth * (cos(fStartFragment) - kappa*sin(fStartFragment) ) ), 8972 -(int)(halfHeight * (sin(fStartFragment) + kappa*cos(fStartFragment) ) ) ); 8973 aPoint += aCenter; 8974 m_aPages.back().appendPoint( aPoint, aLine ); 8975 aLine.append( ' ' ); 8976 8977 aPoint = Point( (int)(halfWidth * (cos(fStopFragment) + kappa*sin(fStopFragment) ) ), 8978 -(int)(halfHeight * (sin(fStopFragment) - kappa*cos(fStopFragment) ) ) ); 8979 aPoint += aCenter; 8980 m_aPages.back().appendPoint( aPoint, aLine ); 8981 aLine.append( ' ' ); 8982 8983 aPoint = Point( (int)(halfWidth * cos(fStopFragment) ), 8984 -(int)(halfHeight * sin(fStopFragment) ) ); 8985 aPoint += aCenter; 8986 m_aPages.back().appendPoint( aPoint, aLine ); 8987 aLine.append( " c\n" ); 8988 } 8989 } 8990 if( bWithChord || bWithPie ) 8991 { 8992 if( bWithPie ) 8993 { 8994 m_aPages.back().appendPoint( aCenter, aLine ); 8995 aLine.append( " l " ); 8996 } 8997 aLine.append( "h " ); 8998 } 8999 if( ! bWithChord && ! bWithPie ) 9000 aLine.append( "S\n" ); 9001 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) && 9002 m_aGraphicsStack.front().m_aFillColor != Color( COL_TRANSPARENT ) ) 9003 aLine.append( "B*\n" ); 9004 else if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 9005 aLine.append( "S\n" ); 9006 else 9007 aLine.append( "f*\n" ); 9008 9009 writeBuffer( aLine.getStr(), aLine.getLength() ); 9010 } 9011 9012 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly ) 9013 { 9014 MARK( "drawPolyLine" ); 9015 9016 sal_uInt16 nPoints = rPoly.GetSize(); 9017 if( nPoints < 2 ) 9018 return; 9019 9020 updateGraphicsState(); 9021 9022 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9023 return; 9024 9025 OStringBuffer aLine( 20 * nPoints ); 9026 m_aPages.back().appendPolygon( rPoly, aLine, rPoly[0] == rPoly[nPoints-1] ); 9027 aLine.append( "S\n" ); 9028 9029 writeBuffer( aLine.getStr(), aLine.getLength() ); 9030 } 9031 9032 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const LineInfo& rInfo ) 9033 { 9034 MARK( "drawPolyLine with LineInfo" ); 9035 9036 updateGraphicsState(); 9037 9038 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9039 return; 9040 9041 OStringBuffer aLine; 9042 aLine.append( "q " ); 9043 if( m_aPages.back().appendLineInfo( rInfo, aLine ) ) 9044 { 9045 writeBuffer( aLine.getStr(), aLine.getLength() ); 9046 drawPolyLine( rPoly ); 9047 writeBuffer( "Q\n", 2 ); 9048 } 9049 else 9050 { 9051 PDFWriter::ExtLineInfo aInfo; 9052 convertLineInfoToExtLineInfo( rInfo, aInfo ); 9053 drawPolyLine( rPoly, aInfo ); 9054 } 9055 } 9056 9057 void PDFWriterImpl::convertLineInfoToExtLineInfo( const LineInfo& rIn, PDFWriter::ExtLineInfo& rOut ) 9058 { 9059 DBG_ASSERT( rIn.GetStyle() == LINE_DASH, "invalid conversion" ); 9060 rOut.m_fLineWidth = rIn.GetWidth(); 9061 rOut.m_fTransparency = 0.0; 9062 rOut.m_eCap = PDFWriter::capButt; 9063 rOut.m_eJoin = PDFWriter::joinMiter; 9064 rOut.m_fMiterLimit = 10; 9065 rOut.m_aDashArray.clear(); 9066 9067 // add DashDot to DashArray 9068 const int nDashes = rIn.GetDashCount(); 9069 const int nDashLen = rIn.GetDashLen(); 9070 const int nDistance = rIn.GetDistance(); 9071 9072 for( int n = 0; n < nDashes; n++ ) 9073 { 9074 rOut.m_aDashArray.push_back( nDashLen ); 9075 rOut.m_aDashArray.push_back( nDistance ); 9076 } 9077 9078 const int nDots = rIn.GetDotCount(); 9079 const int nDotLen = rIn.GetDotLen(); 9080 9081 for( int n = 0; n < nDots; n++ ) 9082 { 9083 rOut.m_aDashArray.push_back( nDotLen ); 9084 rOut.m_aDashArray.push_back( nDistance ); 9085 } 9086 9087 // add LineJoin 9088 switch(rIn.GetLineJoin()) 9089 { 9090 case basegfx::B2DLINEJOIN_BEVEL : 9091 { 9092 rOut.m_eJoin = PDFWriter::joinBevel; 9093 break; 9094 } 9095 default : // basegfx::B2DLINEJOIN_NONE : 9096 // Pdf has no 'none' lineJoin, default is miter 9097 case basegfx::B2DLINEJOIN_MIDDLE : 9098 case basegfx::B2DLINEJOIN_MITER : 9099 { 9100 rOut.m_eJoin = PDFWriter::joinMiter; 9101 break; 9102 } 9103 case basegfx::B2DLINEJOIN_ROUND : 9104 { 9105 rOut.m_eJoin = PDFWriter::joinRound; 9106 break; 9107 } 9108 } 9109 9110 // add LineCap 9111 switch(rIn.GetLineCap()) 9112 { 9113 default: /* com::sun::star::drawing::LineCap_BUTT */ 9114 { 9115 rOut.m_eCap = PDFWriter::capButt; 9116 break; 9117 } 9118 case com::sun::star::drawing::LineCap_ROUND: 9119 { 9120 rOut.m_eCap = PDFWriter::capRound; 9121 break; 9122 } 9123 case com::sun::star::drawing::LineCap_SQUARE: 9124 { 9125 rOut.m_eCap = PDFWriter::capSquare; 9126 break; 9127 } 9128 } 9129 } 9130 9131 void PDFWriterImpl::drawPolyLine( const Polygon& rPoly, const PDFWriter::ExtLineInfo& rInfo ) 9132 { 9133 MARK( "drawPolyLine with ExtLineInfo" ); 9134 9135 updateGraphicsState(); 9136 9137 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) ) 9138 return; 9139 9140 if( rInfo.m_fTransparency >= 1.0 ) 9141 return; 9142 9143 if( rInfo.m_fTransparency != 0.0 ) 9144 beginTransparencyGroup(); 9145 9146 OStringBuffer aLine; 9147 aLine.append( "q " ); 9148 m_aPages.back().appendMappedLength( rInfo.m_fLineWidth, aLine ); 9149 aLine.append( " w" ); 9150 if( rInfo.m_aDashArray.size() < 10 ) // implmentation limit of acrobat reader 9151 { 9152 switch( rInfo.m_eCap ) 9153 { 9154 default: 9155 case PDFWriter::capButt: aLine.append( " 0 J" );break; 9156 case PDFWriter::capRound: aLine.append( " 1 J" );break; 9157 case PDFWriter::capSquare: aLine.append( " 2 J" );break; 9158 } 9159 switch( rInfo.m_eJoin ) 9160 { 9161 default: 9162 case PDFWriter::joinMiter: 9163 { 9164 double fLimit = rInfo.m_fMiterLimit; 9165 if( rInfo.m_fLineWidth < rInfo.m_fMiterLimit ) 9166 fLimit = fLimit / rInfo.m_fLineWidth; 9167 if( fLimit < 1.0 ) 9168 fLimit = 1.0; 9169 aLine.append( " 0 j " ); 9170 appendDouble( fLimit, aLine ); 9171 aLine.append( " M" ); 9172 } 9173 break; 9174 case PDFWriter::joinRound: aLine.append( " 1 j" );break; 9175 case PDFWriter::joinBevel: aLine.append( " 2 j" );break; 9176 } 9177 if( rInfo.m_aDashArray.size() > 0 ) 9178 { 9179 aLine.append( " [ " ); 9180 for( std::vector<double>::const_iterator it = rInfo.m_aDashArray.begin(); 9181 it != rInfo.m_aDashArray.end(); ++it ) 9182 { 9183 m_aPages.back().appendMappedLength( *it, aLine ); 9184 aLine.append( ' ' ); 9185 } 9186 aLine.append( "] 0 d" ); 9187 } 9188 aLine.append( "\n" ); 9189 writeBuffer( aLine.getStr(), aLine.getLength() ); 9190 drawPolyLine( rPoly ); 9191 } 9192 else 9193 { 9194 basegfx::B2DPolygon aPoly(rPoly.getB2DPolygon()); 9195 basegfx::B2DPolyPolygon aPolyPoly; 9196 9197 basegfx::tools::applyLineDashing(aPoly, rInfo.m_aDashArray, &aPolyPoly); 9198 9199 // Old applyLineDashing subdivided the polygon. New one will create bezier curve segments. 9200 // To mimic old behaviour, apply subdivide here. If beziers shall be written (better quality) 9201 // this line needs to be removed and the loop below adapted accordingly 9202 aPolyPoly = basegfx::tools::adaptiveSubdivideByAngle(aPolyPoly); 9203 9204 const sal_uInt32 nPolygonCount(aPolyPoly.count()); 9205 9206 for( sal_uInt32 nPoly = 0; nPoly < nPolygonCount; nPoly++ ) 9207 { 9208 aLine.append( (nPoly != 0 && (nPoly & 7) == 0) ? "\n" : " " ); 9209 aPoly = aPolyPoly.getB2DPolygon( nPoly ); 9210 const sal_uInt32 nPointCount(aPoly.count()); 9211 9212 if(nPointCount) 9213 { 9214 const sal_uInt32 nEdgeCount(aPoly.isClosed() ? nPointCount : nPointCount - 1); 9215 basegfx::B2DPoint aCurrent(aPoly.getB2DPoint(0)); 9216 9217 for(sal_uInt32 a(0); a < nEdgeCount; a++) 9218 { 9219 if( a > 0 ) 9220 aLine.append( " " ); 9221 const sal_uInt32 nNextIndex((a + 1) % nPointCount); 9222 const basegfx::B2DPoint aNext(aPoly.getB2DPoint(nNextIndex)); 9223 9224 m_aPages.back().appendPoint( Point( FRound(aCurrent.getX()), 9225 FRound(aCurrent.getY()) ), 9226 aLine ); 9227 aLine.append( " m " ); 9228 m_aPages.back().appendPoint( Point( FRound(aNext.getX()), 9229 FRound(aNext.getY()) ), 9230 aLine ); 9231 aLine.append( " l" ); 9232 9233 // prepare next edge 9234 aCurrent = aNext; 9235 } 9236 } 9237 } 9238 aLine.append( " S " ); 9239 writeBuffer( aLine.getStr(), aLine.getLength() ); 9240 } 9241 writeBuffer( "Q\n", 2 ); 9242 9243 if( rInfo.m_fTransparency != 0.0 ) 9244 { 9245 // FIXME: actually this may be incorrect with bezier polygons 9246 Rectangle aBoundRect( rPoly.GetBoundRect() ); 9247 // avoid clipping with thick lines 9248 if( rInfo.m_fLineWidth > 0.0 ) 9249 { 9250 sal_Int32 nLW = sal_Int32(rInfo.m_fLineWidth); 9251 aBoundRect.Top() -= nLW; 9252 aBoundRect.Left() -= nLW; 9253 aBoundRect.Right() += nLW; 9254 aBoundRect.Bottom() += nLW; 9255 } 9256 endTransparencyGroup( aBoundRect, (sal_uInt16)(100.0*rInfo.m_fTransparency) ); 9257 } 9258 } 9259 9260 void PDFWriterImpl::drawPixel( const Point& rPoint, const Color& rColor ) 9261 { 9262 MARK( "drawPixel" ); 9263 9264 Color aColor = ( rColor == Color( COL_TRANSPARENT ) ? m_aGraphicsStack.front().m_aLineColor : rColor ); 9265 9266 if( aColor == Color( COL_TRANSPARENT ) ) 9267 return; 9268 9269 // pixels are drawn in line color, so have to set 9270 // the nonstroking color to line color 9271 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; 9272 setFillColor( aColor ); 9273 9274 updateGraphicsState(); 9275 9276 OStringBuffer aLine( 20 ); 9277 m_aPages.back().appendPoint( rPoint, aLine ); 9278 aLine.append( ' ' ); 9279 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aLine ); 9280 aLine.append( ' ' ); 9281 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aLine ); 9282 aLine.append( " re f\n" ); 9283 writeBuffer( aLine.getStr(), aLine.getLength() ); 9284 9285 setFillColor( aOldFillColor ); 9286 } 9287 9288 void PDFWriterImpl::drawPixel( const Polygon& rPoints, const Color* pColors ) 9289 { 9290 MARK( "drawPixel with Polygon" ); 9291 9292 updateGraphicsState(); 9293 9294 if( m_aGraphicsStack.front().m_aLineColor == Color( COL_TRANSPARENT ) && ! pColors ) 9295 return; 9296 9297 sal_uInt16 nPoints = rPoints.GetSize(); 9298 OStringBuffer aLine( nPoints*40 ); 9299 aLine.append( "q " ); 9300 if( ! pColors ) 9301 { 9302 appendNonStrokingColor( m_aGraphicsStack.front().m_aLineColor, aLine ); 9303 aLine.append( ' ' ); 9304 } 9305 9306 OStringBuffer aPixel(32); 9307 aPixel.append( ' ' ); 9308 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIX()), aPixel ); 9309 aPixel.append( ' ' ); 9310 appendDouble( 1.0/double(getReferenceDevice()->ImplGetDPIY()), aPixel ); 9311 OString aPixelStr = aPixel.makeStringAndClear(); 9312 for( sal_uInt16 i = 0; i < nPoints; i++ ) 9313 { 9314 if( pColors ) 9315 { 9316 if( pColors[i] == Color( COL_TRANSPARENT ) ) 9317 continue; 9318 9319 appendNonStrokingColor( pColors[i], aLine ); 9320 aLine.append( ' ' ); 9321 } 9322 m_aPages.back().appendPoint( rPoints[i], aLine ); 9323 aLine.append( aPixelStr ); 9324 aLine.append( " re f\n" ); 9325 } 9326 aLine.append( "Q\n" ); 9327 writeBuffer( aLine.getStr(), aLine.getLength() ); 9328 } 9329 9330 class AccessReleaser 9331 { 9332 BitmapReadAccess* m_pAccess; 9333 public: 9334 AccessReleaser( BitmapReadAccess* pAccess ) : m_pAccess( pAccess ){} 9335 ~AccessReleaser() { delete m_pAccess; } 9336 }; 9337 9338 bool PDFWriterImpl::writeTransparentObject( TransparencyEmit& rObject ) 9339 { 9340 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9341 9342 bool bFlateFilter = compressStream( rObject.m_pContentStream ); 9343 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_END ); 9344 sal_uLong nSize = rObject.m_pContentStream->Tell(); 9345 rObject.m_pContentStream->Seek( STREAM_SEEK_TO_BEGIN ); 9346 #if OSL_DEBUG_LEVEL > 1 9347 emitComment( "PDFWriterImpl::writeTransparentObject" ); 9348 #endif 9349 OStringBuffer aLine( 512 ); 9350 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9351 aLine.append( rObject.m_nObject ); 9352 aLine.append( " 0 obj\n" 9353 "<</Type/XObject\n" 9354 "/Subtype/Form\n" 9355 "/BBox[ " ); 9356 appendFixedInt( rObject.m_aBoundRect.Left(), aLine ); 9357 aLine.append( ' ' ); 9358 appendFixedInt( rObject.m_aBoundRect.Top(), aLine ); 9359 aLine.append( ' ' ); 9360 appendFixedInt( rObject.m_aBoundRect.Right(), aLine ); 9361 aLine.append( ' ' ); 9362 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aLine ); 9363 aLine.append( " ]\n" ); 9364 if( ! rObject.m_pSoftMaskStream ) 9365 { 9366 if( ! m_bIsPDF_A1 ) 9367 { 9368 aLine.append( "/Group<</S/Transparency/CS/DeviceRGB/K true>>\n" ); 9369 } 9370 } 9371 /* #i42884# the PDF reference recommends that each Form XObject 9372 * should have a resource dict; alas if that is the same object 9373 * as the one of the page it triggers an endless recursion in 9374 * acroread 5 (6 and up have that fixed). Since we have only one 9375 * resource dict anyway, let's use the one from the page by NOT 9376 * emitting a Resources entry. 9377 */ 9378 #if 0 9379 aLine.append( " /Resources " ); 9380 aLine.append( getResourceDictObj() ); 9381 aLine.append( " 0 R\n" ); 9382 #endif 9383 9384 aLine.append( "/Length " ); 9385 aLine.append( (sal_Int32)(nSize) ); 9386 aLine.append( "\n" ); 9387 if( bFlateFilter ) 9388 aLine.append( "/Filter/FlateDecode\n" ); 9389 aLine.append( ">>\n" 9390 "stream\n" ); 9391 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9392 checkAndEnableStreamEncryption( rObject.m_nObject ); 9393 CHECK_RETURN( writeBuffer( rObject.m_pContentStream->GetData(), nSize ) ); 9394 disableStreamEncryption(); 9395 aLine.setLength( 0 ); 9396 aLine.append( "\n" 9397 "endstream\n" 9398 "endobj\n\n" ); 9399 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9400 9401 // write ExtGState dict for this XObject 9402 aLine.setLength( 0 ); 9403 aLine.append( rObject.m_nExtGStateObject ); 9404 aLine.append( " 0 obj\n" 9405 "<<" ); 9406 if( ! rObject.m_pSoftMaskStream ) 9407 { 9408 //i59651 9409 if( m_bIsPDF_A1 ) 9410 { 9411 aLine.append( "/CA 1.0/ca 1.0" ); 9412 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9413 } 9414 else 9415 { 9416 aLine.append( "/CA " ); 9417 appendDouble( rObject.m_fAlpha, aLine ); 9418 aLine.append( "\n" 9419 " /ca " ); 9420 appendDouble( rObject.m_fAlpha, aLine ); 9421 } 9422 aLine.append( "\n" ); 9423 } 9424 else 9425 { 9426 if( m_bIsPDF_A1 ) 9427 { 9428 aLine.append( "/SMask/None" ); 9429 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9430 } 9431 else 9432 { 9433 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_END ); 9434 sal_Int32 nMaskSize = (sal_Int32)rObject.m_pSoftMaskStream->Tell(); 9435 rObject.m_pSoftMaskStream->Seek( STREAM_SEEK_TO_BEGIN ); 9436 sal_Int32 nMaskObject = createObject(); 9437 aLine.append( "/SMask<</Type/Mask/S/Luminosity/G " ); 9438 aLine.append( nMaskObject ); 9439 aLine.append( " 0 R>>\n" ); 9440 9441 OStringBuffer aMask; 9442 aMask.append( nMaskObject ); 9443 aMask.append( " 0 obj\n" 9444 "<</Type/XObject\n" 9445 "/Subtype/Form\n" 9446 "/BBox[" ); 9447 appendFixedInt( rObject.m_aBoundRect.Left(), aMask ); 9448 aMask.append( ' ' ); 9449 appendFixedInt( rObject.m_aBoundRect.Top(), aMask ); 9450 aMask.append( ' ' ); 9451 appendFixedInt( rObject.m_aBoundRect.Right(), aMask ); 9452 aMask.append( ' ' ); 9453 appendFixedInt( rObject.m_aBoundRect.Bottom()+1, aMask ); 9454 aMask.append( "]\n" ); 9455 9456 /* #i42884# see above */ 9457 #if 0 9458 aLine.append( "/Resources " ); 9459 aMask.append( getResourceDictObj() ); 9460 aMask.append( " 0 R\n" ); 9461 #endif 9462 9463 aMask.append( "/Group<</S/Transparency/CS/DeviceRGB>>\n" ); 9464 aMask.append( "/Length " ); 9465 aMask.append( nMaskSize ); 9466 aMask.append( ">>\n" 9467 "stream\n" ); 9468 CHECK_RETURN( updateObject( nMaskObject ) ); 9469 checkAndEnableStreamEncryption( nMaskObject ); 9470 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) ); 9471 CHECK_RETURN( writeBuffer( rObject.m_pSoftMaskStream->GetData(), nMaskSize ) ); 9472 disableStreamEncryption(); 9473 aMask.setLength( 0 ); 9474 aMask.append( "\nendstream\n" 9475 "endobj\n\n" ); 9476 CHECK_RETURN( writeBuffer( aMask.getStr(), aMask.getLength() ) ); 9477 } 9478 } 9479 aLine.append( ">>\n" 9480 "endobj\n\n" ); 9481 CHECK_RETURN( updateObject( rObject.m_nExtGStateObject ) ); 9482 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9483 9484 return true; 9485 } 9486 9487 bool PDFWriterImpl::writeGradientFunction( GradientEmit& rObject ) 9488 { 9489 sal_Int32 nFunctionObject = createObject(); 9490 CHECK_RETURN( updateObject( nFunctionObject ) ); 9491 9492 VirtualDevice aDev; 9493 aDev.SetOutputSizePixel( rObject.m_aSize ); 9494 aDev.SetMapMode( MapMode( MAP_PIXEL ) ); 9495 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 9496 aDev.SetDrawMode( aDev.GetDrawMode() | 9497 ( DRAWMODE_GRAYLINE | DRAWMODE_GRAYFILL | DRAWMODE_GRAYTEXT | 9498 DRAWMODE_GRAYBITMAP | DRAWMODE_GRAYGRADIENT ) ); 9499 aDev.DrawGradient( Rectangle( Point( 0, 0 ), rObject.m_aSize ), rObject.m_aGradient ); 9500 9501 Bitmap aSample = aDev.GetBitmap( Point( 0, 0 ), rObject.m_aSize ); 9502 BitmapReadAccess* pAccess = aSample.AcquireReadAccess(); 9503 AccessReleaser aReleaser( pAccess ); 9504 9505 Size aSize = aSample.GetSizePixel(); 9506 9507 sal_Int32 nStreamLengthObject = createObject(); 9508 #if OSL_DEBUG_LEVEL > 1 9509 emitComment( "PDFWriterImpl::writeGradientFunction" ); 9510 #endif 9511 OStringBuffer aLine( 120 ); 9512 aLine.append( nFunctionObject ); 9513 aLine.append( " 0 obj\n" 9514 "<</FunctionType 0\n" 9515 "/Domain[ 0 1 0 1 ]\n" 9516 "/Size[ " ); 9517 aLine.append( (sal_Int32)aSize.Width() ); 9518 aLine.append( ' ' ); 9519 aLine.append( (sal_Int32)aSize.Height() ); 9520 aLine.append( " ]\n" 9521 "/BitsPerSample 8\n" 9522 "/Range[ 0 1 0 1 0 1 ]\n" 9523 "/Order 3\n" 9524 "/Length " ); 9525 aLine.append( nStreamLengthObject ); 9526 aLine.append( " 0 R\n" 9527 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9528 "/Filter/FlateDecode" 9529 #endif 9530 ">>\n" 9531 "stream\n" ); 9532 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9533 9534 sal_uInt64 nStartStreamPos = 0; 9535 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartStreamPos )) ); 9536 9537 checkAndEnableStreamEncryption( nFunctionObject ); 9538 beginCompression(); 9539 for( int y = aSize.Height()-1; y >= 0; y-- ) 9540 { 9541 for( int x = 0; x < aSize.Width(); x++ ) 9542 { 9543 sal_uInt8 aCol[3]; 9544 BitmapColor aColor = pAccess->GetColor( y, x ); 9545 aCol[0] = aColor.GetRed(); 9546 aCol[1] = aColor.GetGreen(); 9547 aCol[2] = aColor.GetBlue(); 9548 CHECK_RETURN( writeBuffer( aCol, 3 ) ); 9549 } 9550 } 9551 endCompression(); 9552 disableStreamEncryption(); 9553 9554 sal_uInt64 nEndStreamPos = 0; 9555 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndStreamPos )) ); 9556 9557 aLine.setLength( 0 ); 9558 aLine.append( "\nendstream\nendobj\n\n" ); 9559 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9560 9561 // write stream length 9562 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 9563 aLine.setLength( 0 ); 9564 aLine.append( nStreamLengthObject ); 9565 aLine.append( " 0 obj\n" ); 9566 aLine.append( (sal_Int64)(nEndStreamPos-nStartStreamPos) ); 9567 aLine.append( "\nendobj\n\n" ); 9568 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9569 9570 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9571 aLine.setLength( 0 ); 9572 aLine.append( rObject.m_nObject ); 9573 aLine.append( " 0 obj\n" 9574 "<</ShadingType 1\n" 9575 "/ColorSpace/DeviceRGB\n" 9576 "/AntiAlias true\n" 9577 "/Domain[ 0 1 0 1 ]\n" 9578 "/Matrix[ " ); 9579 aLine.append( (sal_Int32)aSize.Width() ); 9580 aLine.append( " 0 0 " ); 9581 aLine.append( (sal_Int32)aSize.Height() ); 9582 aLine.append( " 0 0 ]\n" 9583 "/Function " ); 9584 aLine.append( nFunctionObject ); 9585 aLine.append( " 0 R\n" 9586 ">>\n" 9587 "endobj\n\n" ); 9588 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9589 9590 return true; 9591 } 9592 9593 bool PDFWriterImpl::writeJPG( JPGEmit& rObject ) 9594 { 9595 CHECK_RETURN( rObject.m_pStream ); 9596 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9597 9598 sal_Int32 nLength = 0; 9599 rObject.m_pStream->Seek( STREAM_SEEK_TO_END ); 9600 nLength = rObject.m_pStream->Tell(); 9601 rObject.m_pStream->Seek( STREAM_SEEK_TO_BEGIN ); 9602 9603 sal_Int32 nMaskObject = 0; 9604 if( !!rObject.m_aMask ) 9605 { 9606 if( rObject.m_aMask.GetBitCount() == 1 || 9607 ( rObject.m_aMask.GetBitCount() == 8 && m_aContext.Version >= PDFWriter::PDF_1_4 && !m_bIsPDF_A1 )//i59651 9608 ) 9609 { 9610 nMaskObject = createObject(); 9611 } 9612 else if( m_bIsPDF_A1 ) 9613 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9614 else if( m_aContext.Version < PDFWriter::PDF_1_4 ) 9615 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDF13 ); 9616 9617 } 9618 #if OSL_DEBUG_LEVEL > 1 9619 emitComment( "PDFWriterImpl::writeJPG" ); 9620 #endif 9621 9622 OStringBuffer aLine(200); 9623 aLine.append( rObject.m_nObject ); 9624 aLine.append( " 0 obj\n" 9625 "<</Type/XObject/Subtype/Image/Width " ); 9626 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Width() ); 9627 aLine.append( " /Height " ); 9628 aLine.append( (sal_Int32)rObject.m_aID.m_aPixelSize.Height() ); 9629 aLine.append( " /BitsPerComponent 8 " ); 9630 if( rObject.m_bTrueColor ) 9631 aLine.append( "/ColorSpace/DeviceRGB" ); 9632 else 9633 aLine.append( "/ColorSpace/DeviceGray" ); 9634 aLine.append( "/Filter/DCTDecode/Length " ); 9635 aLine.append( nLength ); 9636 if( nMaskObject ) 9637 { 9638 aLine.append( rObject.m_aMask.GetBitCount() == 1 ? " /Mask " : " /SMask " ); 9639 aLine.append( nMaskObject ); 9640 aLine.append( " 0 R " ); 9641 } 9642 aLine.append( ">>\nstream\n" ); 9643 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9644 9645 checkAndEnableStreamEncryption( rObject.m_nObject ); 9646 CHECK_RETURN( writeBuffer( rObject.m_pStream->GetData(), nLength ) ); 9647 disableStreamEncryption(); 9648 9649 aLine.setLength( 0 ); 9650 aLine.append( "\nendstream\nendobj\n\n" ); 9651 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9652 9653 if( nMaskObject ) 9654 { 9655 BitmapEmit aEmit; 9656 aEmit.m_nObject = nMaskObject; 9657 if( rObject.m_aMask.GetBitCount() == 1 ) 9658 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, rObject.m_aMask ); 9659 else if( rObject.m_aMask.GetBitCount() == 8 ) 9660 aEmit.m_aBitmap = BitmapEx( rObject.m_aMask, AlphaMask( rObject.m_aMask ) ); 9661 writeBitmapObject( aEmit, true ); 9662 } 9663 9664 return true; 9665 } 9666 9667 bool PDFWriterImpl::writeBitmapObject( BitmapEmit& rObject, bool bMask ) 9668 { 9669 CHECK_RETURN( updateObject( rObject.m_nObject ) ); 9670 9671 Bitmap aBitmap; 9672 Color aTransparentColor( COL_TRANSPARENT ); 9673 bool bWriteMask = false; 9674 if( ! bMask ) 9675 { 9676 aBitmap = rObject.m_aBitmap.GetBitmap(); 9677 if( rObject.m_aBitmap.IsAlpha() ) 9678 { 9679 if( m_aContext.Version >= PDFWriter::PDF_1_4 ) 9680 bWriteMask = true; 9681 // else draw without alpha channel 9682 } 9683 else 9684 { 9685 switch( rObject.m_aBitmap.GetTransparentType() ) 9686 { 9687 case TRANSPARENT_NONE: 9688 // comes from drawMask function 9689 if( aBitmap.GetBitCount() == 1 && rObject.m_bDrawMask ) 9690 bMask = true; 9691 break; 9692 case TRANSPARENT_COLOR: 9693 aTransparentColor = rObject.m_aBitmap.GetTransparentColor(); 9694 break; 9695 case TRANSPARENT_BITMAP: 9696 bWriteMask = true; 9697 break; 9698 } 9699 } 9700 } 9701 else 9702 { 9703 if( m_aContext.Version < PDFWriter::PDF_1_4 || ! rObject.m_aBitmap.IsAlpha() ) 9704 { 9705 aBitmap = rObject.m_aBitmap.GetMask(); 9706 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); 9707 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); 9708 } 9709 else if( aBitmap.GetBitCount() != 8 ) 9710 { 9711 aBitmap = rObject.m_aBitmap.GetAlpha().GetBitmap(); 9712 aBitmap.Convert( BMP_CONVERSION_8BIT_GREYS ); 9713 DBG_ASSERT( aBitmap.GetBitCount() == 8, "alpha mask conversion failed" ); 9714 } 9715 } 9716 9717 BitmapReadAccess* pAccess = aBitmap.AcquireReadAccess(); 9718 AccessReleaser aReleaser( pAccess ); 9719 9720 bool bTrueColor; 9721 sal_Int32 nBitsPerComponent; 9722 switch( aBitmap.GetBitCount() ) 9723 { 9724 case 1: 9725 case 2: 9726 case 4: 9727 case 8: 9728 bTrueColor = false; 9729 nBitsPerComponent = aBitmap.GetBitCount(); 9730 break; 9731 default: 9732 bTrueColor = true; 9733 nBitsPerComponent = 8; 9734 break; 9735 } 9736 9737 sal_Int32 nStreamLengthObject = createObject(); 9738 sal_Int32 nMaskObject = 0; 9739 9740 #if OSL_DEBUG_LEVEL > 1 9741 emitComment( "PDFWriterImpl::writeBitmapObject" ); 9742 #endif 9743 OStringBuffer aLine(1024); 9744 aLine.append( rObject.m_nObject ); 9745 aLine.append( " 0 obj\n" 9746 "<</Type/XObject/Subtype/Image/Width " ); 9747 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); 9748 aLine.append( "/Height " ); 9749 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Height() ); 9750 aLine.append( "/BitsPerComponent " ); 9751 aLine.append( nBitsPerComponent ); 9752 aLine.append( "/Length " ); 9753 aLine.append( nStreamLengthObject ); 9754 aLine.append( " 0 R\n" ); 9755 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9756 if( nBitsPerComponent != 1 ) 9757 { 9758 aLine.append( "/Filter/FlateDecode" ); 9759 } 9760 else 9761 { 9762 aLine.append( "/Filter/CCITTFaxDecode/DecodeParms<</K -1/BlackIs1 true/Columns " ); 9763 aLine.append( (sal_Int32)aBitmap.GetSizePixel().Width() ); 9764 aLine.append( ">>\n" ); 9765 } 9766 #endif 9767 if( ! bMask ) 9768 { 9769 aLine.append( "/ColorSpace" ); 9770 if( bTrueColor ) 9771 aLine.append( "/DeviceRGB\n" ); 9772 else if( aBitmap.HasGreyPalette() ) 9773 { 9774 aLine.append( "/DeviceGray\n" ); 9775 if( aBitmap.GetBitCount() == 1 ) 9776 { 9777 // #i47395# 1 bit bitmaps occasionally have an inverted grey palette 9778 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); 9779 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" ); 9780 if( nBlackIndex == 1 ) 9781 aLine.append( "/Decode[1 0]\n" ); 9782 } 9783 } 9784 else 9785 { 9786 aLine.append( "[ /Indexed/DeviceRGB " ); 9787 aLine.append( (sal_Int32)(pAccess->GetPaletteEntryCount()-1) ); 9788 aLine.append( "\n<" ); 9789 if( m_aContext.Encryption.Encrypt() ) 9790 { 9791 enableStringEncryption( rObject.m_nObject ); 9792 //check encryption buffer size 9793 if( checkEncryptionBufferSize( pAccess->GetPaletteEntryCount()*3 ) ) 9794 { 9795 int nChar = 0; 9796 //fill the encryption buffer 9797 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9798 { 9799 const BitmapColor& rColor = pAccess->GetPaletteColor( i ); 9800 m_pEncryptionBuffer[nChar++] = rColor.GetRed(); 9801 m_pEncryptionBuffer[nChar++] = rColor.GetGreen(); 9802 m_pEncryptionBuffer[nChar++] = rColor.GetBlue(); 9803 } 9804 //encrypt the colorspace lookup table 9805 rtl_cipher_encodeARCFOUR( m_aCipher, m_pEncryptionBuffer, nChar, m_pEncryptionBuffer, nChar ); 9806 //now queue the data for output 9807 nChar = 0; 9808 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9809 { 9810 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9811 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9812 appendHex(m_pEncryptionBuffer[nChar++], aLine ); 9813 } 9814 } 9815 } 9816 else //no encryption requested (PDF/A-1a program flow drops here) 9817 { 9818 for( sal_uInt16 i = 0; i < pAccess->GetPaletteEntryCount(); i++ ) 9819 { 9820 const BitmapColor& rColor = pAccess->GetPaletteColor( i ); 9821 appendHex( rColor.GetRed(), aLine ); 9822 appendHex( rColor.GetGreen(), aLine ); 9823 appendHex( rColor.GetBlue(), aLine ); 9824 } 9825 } 9826 aLine.append( ">\n]\n" ); 9827 } 9828 } 9829 else 9830 { 9831 if( aBitmap.GetBitCount() == 1 ) 9832 { 9833 aLine.append( "/ImageMask true\n" ); 9834 sal_Int32 nBlackIndex = pAccess->GetBestPaletteIndex( BitmapColor( Color( COL_BLACK ) ) ); 9835 DBG_ASSERT( nBlackIndex == 0 || nBlackIndex == 1, "wrong black index" ); 9836 if( nBlackIndex ) 9837 aLine.append( "/Decode[ 1 0 ]\n" ); 9838 else 9839 aLine.append( "/Decode[ 0 1 ]\n" ); 9840 } 9841 else if( aBitmap.GetBitCount() == 8 ) 9842 { 9843 aLine.append( "/ColorSpace/DeviceGray\n" 9844 "/Decode [ 1 0 ]\n" ); 9845 } 9846 } 9847 9848 if( ! bMask && m_aContext.Version > PDFWriter::PDF_1_2 && !m_bIsPDF_A1 )//i59651 9849 { 9850 if( bWriteMask ) 9851 { 9852 nMaskObject = createObject(); 9853 if( rObject.m_aBitmap.IsAlpha() && m_aContext.Version > PDFWriter::PDF_1_3 ) 9854 aLine.append( "/SMask " ); 9855 else 9856 aLine.append( "/Mask " ); 9857 aLine.append( nMaskObject ); 9858 aLine.append( " 0 R\n" ); 9859 } 9860 else if( aTransparentColor != Color( COL_TRANSPARENT ) ) 9861 { 9862 aLine.append( "/Mask[ " ); 9863 if( bTrueColor ) 9864 { 9865 aLine.append( (sal_Int32)aTransparentColor.GetRed() ); 9866 aLine.append( ' ' ); 9867 aLine.append( (sal_Int32)aTransparentColor.GetRed() ); 9868 aLine.append( ' ' ); 9869 aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); 9870 aLine.append( ' ' ); 9871 aLine.append( (sal_Int32)aTransparentColor.GetGreen() ); 9872 aLine.append( ' ' ); 9873 aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); 9874 aLine.append( ' ' ); 9875 aLine.append( (sal_Int32)aTransparentColor.GetBlue() ); 9876 } 9877 else 9878 { 9879 sal_Int32 nIndex = pAccess->GetBestPaletteIndex( BitmapColor( aTransparentColor ) ); 9880 aLine.append( nIndex ); 9881 } 9882 aLine.append( " ]\n" ); 9883 } 9884 } 9885 else if( m_bIsPDF_A1 && (bWriteMask || aTransparentColor != Color( COL_TRANSPARENT )) ) 9886 m_aErrors.insert( PDFWriter::Warning_Transparency_Omitted_PDFA ); 9887 9888 aLine.append( ">>\n" 9889 "stream\n" ); 9890 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9891 sal_uInt64 nStartPos = 0; 9892 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nStartPos )) ); 9893 9894 checkAndEnableStreamEncryption( rObject.m_nObject ); 9895 #ifndef DEBUG_DISABLE_PDFCOMPRESSION 9896 if( nBitsPerComponent == 1 ) 9897 { 9898 writeG4Stream( pAccess ); 9899 } 9900 else 9901 #endif 9902 { 9903 beginCompression(); 9904 if( ! bTrueColor || pAccess->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB ) 9905 { 9906 const int nScanLineBytes = 1 + ( pAccess->GetBitCount() * ( pAccess->Width() - 1 ) / 8U ); 9907 9908 for( int i = 0; i < pAccess->Height(); i++ ) 9909 { 9910 CHECK_RETURN( writeBuffer( pAccess->GetScanline( i ), nScanLineBytes ) ); 9911 } 9912 } 9913 else 9914 { 9915 const int nScanLineBytes = pAccess->Width()*3; 9916 boost::shared_array<sal_uInt8> pCol( new sal_uInt8[ nScanLineBytes ] ); 9917 for( int y = 0; y < pAccess->Height(); y++ ) 9918 { 9919 for( int x = 0; x < pAccess->Width(); x++ ) 9920 { 9921 BitmapColor aColor = pAccess->GetColor( y, x ); 9922 pCol[3*x+0] = aColor.GetRed(); 9923 pCol[3*x+1] = aColor.GetGreen(); 9924 pCol[3*x+2] = aColor.GetBlue(); 9925 } 9926 CHECK_RETURN( writeBuffer( pCol.get(), nScanLineBytes ) ); 9927 } 9928 } 9929 endCompression(); 9930 } 9931 disableStreamEncryption(); 9932 9933 sal_uInt64 nEndPos = 0; 9934 CHECK_RETURN( (osl_File_E_None == osl_getFilePos( m_aFile, &nEndPos )) ); 9935 aLine.setLength( 0 ); 9936 aLine.append( "\nendstream\nendobj\n\n" ); 9937 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9938 CHECK_RETURN( updateObject( nStreamLengthObject ) ); 9939 aLine.setLength( 0 ); 9940 aLine.append( nStreamLengthObject ); 9941 aLine.append( " 0 obj\n" ); 9942 aLine.append( (sal_Int64)(nEndPos-nStartPos) ); 9943 aLine.append( "\nendobj\n\n" ); 9944 CHECK_RETURN( writeBuffer( aLine.getStr(), aLine.getLength() ) ); 9945 9946 if( nMaskObject ) 9947 { 9948 BitmapEmit aEmit; 9949 aEmit.m_nObject = nMaskObject; 9950 aEmit.m_aBitmap = rObject.m_aBitmap; 9951 return writeBitmapObject( aEmit, true ); 9952 } 9953 9954 return true; 9955 } 9956 9957 void PDFWriterImpl::drawJPGBitmap( SvStream& rDCTData, bool bIsTrueColor, const Size& rSizePixel, const Rectangle& rTargetArea, const Bitmap& rMask ) 9958 { 9959 MARK( "drawJPGBitmap" ); 9960 9961 OStringBuffer aLine( 80 ); 9962 updateGraphicsState(); 9963 9964 // #i40055# sanity check 9965 if( ! (rTargetArea.GetWidth() && rTargetArea.GetHeight() ) ) 9966 return; 9967 if( ! (rSizePixel.Width() && rSizePixel.Height()) ) 9968 return; 9969 9970 rDCTData.Seek( 0 ); 9971 if( bIsTrueColor && m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 9972 { 9973 // need to convert to grayscale; 9974 // load stream to bitmap and draw the bitmap instead 9975 Graphic aGraphic; 9976 GraphicConverter::Import( rDCTData, aGraphic, CVT_JPG ); 9977 Bitmap aBmp( aGraphic.GetBitmap() ); 9978 if( !!rMask && rMask.GetSizePixel() == aBmp.GetSizePixel() ) 9979 { 9980 BitmapEx aBmpEx( aBmp, rMask ); 9981 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmpEx ); 9982 } 9983 else 9984 drawBitmap( rTargetArea.TopLeft(), rTargetArea.GetSize(), aBmp ); 9985 return; 9986 } 9987 9988 SvMemoryStream* pStream = new SvMemoryStream; 9989 *pStream << rDCTData; 9990 pStream->Seek( STREAM_SEEK_TO_END ); 9991 9992 BitmapID aID; 9993 aID.m_aPixelSize = rSizePixel; 9994 aID.m_nSize = pStream->Tell(); 9995 pStream->Seek( STREAM_SEEK_TO_BEGIN ); 9996 aID.m_nChecksum = rtl_crc32( 0, pStream->GetData(), aID.m_nSize ); 9997 if( ! rMask.IsEmpty() ) 9998 aID.m_nMaskChecksum = rMask.GetChecksum(); 9999 10000 std::list< JPGEmit >::const_iterator it; 10001 for( it = m_aJPGs.begin(); it != m_aJPGs.end() && ! (aID == it->m_aID); ++it ) 10002 ; 10003 if( it == m_aJPGs.end() ) 10004 { 10005 m_aJPGs.push_front( JPGEmit() ); 10006 JPGEmit& rEmit = m_aJPGs.front(); 10007 rEmit.m_nObject = createObject(); 10008 rEmit.m_aID = aID; 10009 rEmit.m_pStream = pStream; 10010 rEmit.m_bTrueColor = bIsTrueColor; 10011 if( !! rMask && rMask.GetSizePixel() == rSizePixel ) 10012 rEmit.m_aMask = rMask; 10013 10014 it = m_aJPGs.begin(); 10015 } 10016 else 10017 delete pStream; 10018 10019 aLine.append( "q " ); 10020 sal_Int32 nCheckWidth = 0; 10021 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetWidth(), aLine, false, &nCheckWidth ); 10022 aLine.append( " 0 0 " ); 10023 sal_Int32 nCheckHeight = 0; 10024 m_aPages.back().appendMappedLength( (sal_Int32)rTargetArea.GetHeight(), aLine, true, &nCheckHeight ); 10025 aLine.append( ' ' ); 10026 m_aPages.back().appendPoint( rTargetArea.BottomLeft(), aLine ); 10027 aLine.append( " cm\n/Im" ); 10028 aLine.append( it->m_nObject ); 10029 aLine.append( " Do Q\n" ); 10030 if( nCheckWidth == 0 || nCheckHeight == 0 ) 10031 { 10032 // #i97512# avoid invalid current matrix 10033 aLine.setLength( 0 ); 10034 aLine.append( "\n%jpeg image /Im" ); 10035 aLine.append( it->m_nObject ); 10036 aLine.append( " scaled to zero size, omitted\n" ); 10037 } 10038 writeBuffer( aLine.getStr(), aLine.getLength() ); 10039 10040 OStringBuffer aObjName( 16 ); 10041 aObjName.append( "Im" ); 10042 aObjName.append( it->m_nObject ); 10043 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject ); 10044 10045 } 10046 10047 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEmit& rBitmap, const Color& rFillColor ) 10048 { 10049 OStringBuffer aLine( 80 ); 10050 updateGraphicsState(); 10051 10052 aLine.append( "q " ); 10053 if( rFillColor != Color( COL_TRANSPARENT ) ) 10054 { 10055 appendNonStrokingColor( rFillColor, aLine ); 10056 aLine.append( ' ' ); 10057 } 10058 sal_Int32 nCheckWidth = 0; 10059 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Width(), aLine, false, &nCheckWidth ); 10060 aLine.append( " 0 0 " ); 10061 sal_Int32 nCheckHeight = 0; 10062 m_aPages.back().appendMappedLength( (sal_Int32)rDestSize.Height(), aLine, true, &nCheckHeight ); 10063 aLine.append( ' ' ); 10064 m_aPages.back().appendPoint( rDestPoint + Point( 0, rDestSize.Height()-1 ), aLine ); 10065 aLine.append( " cm\n/Im" ); 10066 aLine.append( rBitmap.m_nObject ); 10067 aLine.append( " Do Q\n" ); 10068 if( nCheckWidth == 0 || nCheckHeight == 0 ) 10069 { 10070 // #i97512# avoid invalid current matrix 10071 aLine.setLength( 0 ); 10072 aLine.append( "\n%bitmap image /Im" ); 10073 aLine.append( rBitmap.m_nObject ); 10074 aLine.append( " scaled to zero size, omitted\n" ); 10075 } 10076 writeBuffer( aLine.getStr(), aLine.getLength() ); 10077 } 10078 10079 const PDFWriterImpl::BitmapEmit& PDFWriterImpl::createBitmapEmit( const BitmapEx& i_rBitmap, bool bDrawMask ) 10080 { 10081 BitmapEx aBitmap( i_rBitmap ); 10082 if( m_aContext.ColorMode == PDFWriter::DrawGreyscale ) 10083 { 10084 BmpConversion eConv = BMP_CONVERSION_8BIT_GREYS; 10085 int nDepth = aBitmap.GetBitmap().GetBitCount(); 10086 if( nDepth <= 4 ) 10087 eConv = BMP_CONVERSION_4BIT_GREYS; 10088 if( nDepth > 1 ) 10089 aBitmap.Convert( eConv ); 10090 } 10091 BitmapID aID; 10092 aID.m_aPixelSize = aBitmap.GetSizePixel(); 10093 aID.m_nSize = aBitmap.GetBitCount(); 10094 aID.m_nChecksum = aBitmap.GetBitmap().GetChecksum(); 10095 aID.m_nMaskChecksum = 0; 10096 if( aBitmap.IsAlpha() ) 10097 aID.m_nMaskChecksum = aBitmap.GetAlpha().GetChecksum(); 10098 else 10099 { 10100 Bitmap aMask = aBitmap.GetMask(); 10101 if( ! aMask.IsEmpty() ) 10102 aID.m_nMaskChecksum = aMask.GetChecksum(); 10103 } 10104 std::list< BitmapEmit >::const_iterator it; 10105 for( it = m_aBitmaps.begin(); it != m_aBitmaps.end(); ++it ) 10106 { 10107 if( aID == it->m_aID ) 10108 break; 10109 } 10110 if( it == m_aBitmaps.end() ) 10111 { 10112 m_aBitmaps.push_front( BitmapEmit() ); 10113 m_aBitmaps.front().m_aID = aID; 10114 m_aBitmaps.front().m_aBitmap = aBitmap; 10115 m_aBitmaps.front().m_nObject = createObject(); 10116 m_aBitmaps.front().m_bDrawMask = bDrawMask; 10117 it = m_aBitmaps.begin(); 10118 } 10119 10120 OStringBuffer aObjName( 16 ); 10121 aObjName.append( "Im" ); 10122 aObjName.append( it->m_nObject ); 10123 pushResource( ResXObject, aObjName.makeStringAndClear(), it->m_nObject ); 10124 10125 return *it; 10126 } 10127 10128 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap ) 10129 { 10130 MARK( "drawBitmap (Bitmap)" ); 10131 10132 // #i40055# sanity check 10133 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10134 return; 10135 10136 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( rBitmap ) ); 10137 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); 10138 } 10139 10140 void PDFWriterImpl::drawBitmap( const Point& rDestPoint, const Size& rDestSize, const BitmapEx& rBitmap ) 10141 { 10142 MARK( "drawBitmap (BitmapEx)" ); 10143 10144 // #i40055# sanity check 10145 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10146 return; 10147 10148 const BitmapEmit& rEmit = createBitmapEmit( rBitmap ); 10149 drawBitmap( rDestPoint, rDestSize, rEmit, Color( COL_TRANSPARENT ) ); 10150 } 10151 10152 void PDFWriterImpl::drawMask( const Point& rDestPoint, const Size& rDestSize, const Bitmap& rBitmap, const Color& rFillColor ) 10153 { 10154 MARK( "drawMask" ); 10155 10156 // #i40055# sanity check 10157 if( ! (rDestSize.Width() && rDestSize.Height()) ) 10158 return; 10159 10160 Bitmap aBitmap( rBitmap ); 10161 if( aBitmap.GetBitCount() > 1 ) 10162 aBitmap.Convert( BMP_CONVERSION_1BIT_THRESHOLD ); 10163 DBG_ASSERT( aBitmap.GetBitCount() == 1, "mask conversion failed" ); 10164 10165 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ), true ); 10166 drawBitmap( rDestPoint, rDestSize, rEmit, rFillColor ); 10167 } 10168 10169 sal_Int32 PDFWriterImpl::createGradient( const Gradient& rGradient, const Size& rSize ) 10170 { 10171 Size aPtSize( lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10172 MapMode( MAP_POINT ), 10173 getReferenceDevice(), 10174 rSize ) ); 10175 // check if we already have this gradient 10176 std::list<GradientEmit>::iterator it; 10177 // rounding to point will generally lose some pixels 10178 // round up to point boundary 10179 aPtSize.Width()++; 10180 aPtSize.Height()++; 10181 for( it = m_aGradients.begin(); it != m_aGradients.end(); ++it ) 10182 { 10183 if( it->m_aGradient == rGradient ) 10184 { 10185 if( it->m_aSize == aPtSize ) 10186 break; 10187 } 10188 } 10189 if( it == m_aGradients.end() ) 10190 { 10191 m_aGradients.push_front( GradientEmit() ); 10192 m_aGradients.front().m_aGradient = rGradient; 10193 m_aGradients.front().m_nObject = createObject(); 10194 m_aGradients.front().m_aSize = aPtSize; 10195 it = m_aGradients.begin(); 10196 } 10197 10198 OStringBuffer aObjName( 16 ); 10199 aObjName.append( 'P' ); 10200 aObjName.append( it->m_nObject ); 10201 pushResource( ResShading, aObjName.makeStringAndClear(), it->m_nObject ); 10202 10203 return it->m_nObject; 10204 } 10205 10206 void PDFWriterImpl::drawGradient( const Rectangle& rRect, const Gradient& rGradient ) 10207 { 10208 MARK( "drawGradient (Rectangle)" ); 10209 10210 if( m_aContext.Version == PDFWriter::PDF_1_2 ) 10211 { 10212 drawRectangle( rRect ); 10213 return; 10214 } 10215 10216 sal_Int32 nGradient = createGradient( rGradient, rRect.GetSize() ); 10217 10218 Point aTranslate( rRect.BottomLeft() ); 10219 aTranslate += Point( 0, 1 ); 10220 10221 updateGraphicsState(); 10222 10223 OStringBuffer aLine( 80 ); 10224 aLine.append( "q 1 0 0 1 " ); 10225 m_aPages.back().appendPoint( aTranslate, aLine ); 10226 aLine.append( " cm " ); 10227 // if a stroke is appended reset the clip region before stroke 10228 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10229 aLine.append( "q " ); 10230 aLine.append( "0 0 " ); 10231 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); 10232 aLine.append( ' ' ); 10233 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true ); 10234 aLine.append( " re W n\n" ); 10235 10236 aLine.append( "/P" ); 10237 aLine.append( nGradient ); 10238 aLine.append( " sh " ); 10239 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10240 { 10241 aLine.append( "Q 0 0 " ); 10242 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetWidth(), aLine, false ); 10243 aLine.append( ' ' ); 10244 m_aPages.back().appendMappedLength( (sal_Int32)rRect.GetHeight(), aLine, true ); 10245 aLine.append( " re S " ); 10246 } 10247 aLine.append( "Q\n" ); 10248 writeBuffer( aLine.getStr(), aLine.getLength() ); 10249 } 10250 10251 void PDFWriterImpl::drawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient ) 10252 { 10253 MARK( "drawGradient (PolyPolygon)" ); 10254 10255 if( m_aContext.Version == PDFWriter::PDF_1_2 ) 10256 { 10257 drawPolyPolygon( rPolyPoly ); 10258 return; 10259 } 10260 10261 Rectangle aBoundRect = rPolyPoly.GetBoundRect(); 10262 sal_Int32 nGradient = createGradient( rGradient, aBoundRect.GetSize() ); 10263 10264 updateGraphicsState(); 10265 10266 Point aTranslate = aBoundRect.BottomLeft(); 10267 int nPolygons = rPolyPoly.Count(); 10268 10269 OStringBuffer aLine( 80*nPolygons ); 10270 aLine.append( "q " ); 10271 // set PolyPolygon as clip path 10272 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 10273 aLine.append( "W* n\n" ); 10274 aLine.append( "1 0 0 1 " ); 10275 m_aPages.back().appendPoint( aTranslate, aLine ); 10276 aLine.append( " cm\n" ); 10277 aLine.append( "/P" ); 10278 aLine.append( nGradient ); 10279 aLine.append( " sh Q\n" ); 10280 if( m_aGraphicsStack.front().m_aLineColor != Color( COL_TRANSPARENT ) ) 10281 { 10282 // and draw the surrounding path 10283 m_aPages.back().appendPolyPolygon( rPolyPoly, aLine ); 10284 aLine.append( "S\n" ); 10285 } 10286 writeBuffer( aLine.getStr(), aLine.getLength() ); 10287 } 10288 10289 void PDFWriterImpl::drawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch ) 10290 { 10291 MARK( "drawHatch" ); 10292 10293 updateGraphicsState(); 10294 10295 if( rPolyPoly.Count() ) 10296 { 10297 PolyPolygon aPolyPoly( rPolyPoly ); 10298 10299 aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME ); 10300 push( PUSH_LINECOLOR ); 10301 setLineColor( rHatch.GetColor() ); 10302 getReferenceDevice()->ImplDrawHatch( aPolyPoly, rHatch, sal_False ); 10303 pop(); 10304 } 10305 } 10306 10307 void PDFWriterImpl::drawWallpaper( const Rectangle& rRect, const Wallpaper& rWall ) 10308 { 10309 MARK( "drawWallpaper" ); 10310 10311 bool bDrawColor = false; 10312 bool bDrawGradient = false; 10313 bool bDrawBitmap = false; 10314 10315 BitmapEx aBitmap; 10316 Point aBmpPos = rRect.TopLeft(); 10317 Size aBmpSize; 10318 if( rWall.IsBitmap() ) 10319 { 10320 aBitmap = rWall.GetBitmap(); 10321 aBmpSize = lcl_convert( aBitmap.GetPrefMapMode(), 10322 getMapMode(), 10323 getReferenceDevice(), 10324 aBitmap.GetPrefSize() ); 10325 Rectangle aRect( rRect ); 10326 if( rWall.IsRect() ) 10327 { 10328 aRect = rWall.GetRect(); 10329 aBmpPos = aRect.TopLeft(); 10330 aBmpSize = aRect.GetSize(); 10331 } 10332 if( rWall.GetStyle() != WALLPAPER_SCALE ) 10333 { 10334 if( rWall.GetStyle() != WALLPAPER_TILE ) 10335 { 10336 bDrawBitmap = true; 10337 if( rWall.IsGradient() ) 10338 bDrawGradient = true; 10339 else 10340 bDrawColor = true; 10341 switch( rWall.GetStyle() ) 10342 { 10343 case WALLPAPER_TOPLEFT: 10344 break; 10345 case WALLPAPER_TOP: 10346 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10347 break; 10348 case WALLPAPER_LEFT: 10349 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10350 break; 10351 case WALLPAPER_TOPRIGHT: 10352 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10353 break; 10354 case WALLPAPER_CENTER: 10355 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10356 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10357 break; 10358 case WALLPAPER_RIGHT: 10359 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10360 aBmpPos.Y() += (aRect.GetHeight()-aBmpSize.Height())/2; 10361 break; 10362 case WALLPAPER_BOTTOMLEFT: 10363 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10364 break; 10365 case WALLPAPER_BOTTOM: 10366 aBmpPos.X() += (aRect.GetWidth()-aBmpSize.Width())/2; 10367 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10368 break; 10369 case WALLPAPER_BOTTOMRIGHT: 10370 aBmpPos.X() += aRect.GetWidth()-aBmpSize.Width(); 10371 aBmpPos.Y() += aRect.GetHeight()-aBmpSize.Height(); 10372 break; 10373 default: ; 10374 } 10375 } 10376 else 10377 { 10378 // push the bitmap 10379 const BitmapEmit& rEmit = createBitmapEmit( BitmapEx( aBitmap ) ); 10380 10381 // convert to page coordinates; this needs to be done here 10382 // since the emit does not know the page anymore 10383 Rectangle aConvertRect( aBmpPos, aBmpSize ); 10384 m_aPages.back().convertRect( aConvertRect ); 10385 10386 OStringBuffer aNameBuf(16); 10387 aNameBuf.append( "Im" ); 10388 aNameBuf.append( rEmit.m_nObject ); 10389 OString aImageName( aNameBuf.makeStringAndClear() ); 10390 10391 // push the pattern 10392 OStringBuffer aTilingStream( 32 ); 10393 appendFixedInt( aConvertRect.GetWidth(), aTilingStream ); 10394 aTilingStream.append( " 0 0 " ); 10395 appendFixedInt( aConvertRect.GetHeight(), aTilingStream ); 10396 aTilingStream.append( " 0 0 cm\n/" ); 10397 aTilingStream.append( aImageName ); 10398 aTilingStream.append( " Do\n" ); 10399 10400 m_aTilings.push_back( TilingEmit() ); 10401 m_aTilings.back().m_nObject = createObject(); 10402 m_aTilings.back().m_aRectangle = Rectangle( Point( 0, 0 ), aConvertRect.GetSize() ); 10403 m_aTilings.back().m_pTilingStream = new SvMemoryStream(); 10404 m_aTilings.back().m_pTilingStream->Write( aTilingStream.getStr(), aTilingStream.getLength() ); 10405 // phase the tiling so wallpaper begins on upper left 10406 m_aTilings.back().m_aTransform.matrix[2] = double(aConvertRect.Left() % aConvertRect.GetWidth()) / fDivisor; 10407 m_aTilings.back().m_aTransform.matrix[5] = double(aConvertRect.Top() % aConvertRect.GetHeight()) / fDivisor; 10408 m_aTilings.back().m_aResources.m_aXObjects[aImageName] = rEmit.m_nObject; 10409 10410 updateGraphicsState(); 10411 10412 OStringBuffer aObjName( 16 ); 10413 aObjName.append( 'P' ); 10414 aObjName.append( m_aTilings.back().m_nObject ); 10415 OString aPatternName( aObjName.makeStringAndClear() ); 10416 pushResource( ResPattern, aPatternName, m_aTilings.back().m_nObject ); 10417 10418 // fill a rRect with the pattern 10419 OStringBuffer aLine( 100 ); 10420 aLine.append( "q /Pattern cs /" ); 10421 aLine.append( aPatternName ); 10422 aLine.append( " scn " ); 10423 m_aPages.back().appendRect( rRect, aLine ); 10424 aLine.append( " f Q\n" ); 10425 writeBuffer( aLine.getStr(), aLine.getLength() ); 10426 } 10427 } 10428 else 10429 { 10430 aBmpPos = aRect.TopLeft(); 10431 aBmpSize = aRect.GetSize(); 10432 bDrawBitmap = true; 10433 } 10434 10435 if( aBitmap.IsTransparent() ) 10436 { 10437 if( rWall.IsGradient() ) 10438 bDrawGradient = true; 10439 else 10440 bDrawColor = true; 10441 } 10442 } 10443 else if( rWall.IsGradient() ) 10444 bDrawGradient = true; 10445 else 10446 bDrawColor = true; 10447 10448 if( bDrawGradient ) 10449 { 10450 drawGradient( rRect, rWall.GetGradient() ); 10451 } 10452 if( bDrawColor ) 10453 { 10454 Color aOldLineColor = m_aGraphicsStack.front().m_aLineColor; 10455 Color aOldFillColor = m_aGraphicsStack.front().m_aFillColor; 10456 setLineColor( Color( COL_TRANSPARENT ) ); 10457 setFillColor( rWall.GetColor() ); 10458 drawRectangle( rRect ); 10459 setLineColor( aOldLineColor ); 10460 setFillColor( aOldFillColor ); 10461 } 10462 if( bDrawBitmap ) 10463 { 10464 // set temporary clip region since aBmpPos and aBmpSize 10465 // may be outside rRect 10466 OStringBuffer aLine( 20 ); 10467 aLine.append( "q " ); 10468 m_aPages.back().appendRect( rRect, aLine ); 10469 aLine.append( " W n\n" ); 10470 writeBuffer( aLine.getStr(), aLine.getLength() ); 10471 drawBitmap( aBmpPos, aBmpSize, aBitmap ); 10472 writeBuffer( "Q\n", 2 ); 10473 } 10474 } 10475 10476 void PDFWriterImpl::beginPattern( const Rectangle& rCellRect ) 10477 { 10478 beginRedirect( new SvMemoryStream(), rCellRect ); 10479 } 10480 10481 sal_Int32 PDFWriterImpl::endPattern( const SvtGraphicFill::Transform& rTransform ) 10482 { 10483 Rectangle aConvertRect( getRedirectTargetRect() ); 10484 DBG_ASSERT( aConvertRect.GetWidth() != 0 && aConvertRect.GetHeight() != 0, "empty cell rectangle in pattern" ); 10485 10486 // get scaling between current mapmode and PDF output 10487 Size aScaling( lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), Size( 10000, 10000 ) ) ); 10488 double fSX = (double(aScaling.Width()) / 10000.0); 10489 double fSY = (double(aScaling.Height()) / 10000.0); 10490 10491 // transform translation part of matrix 10492 Size aTranslation( (long)rTransform.matrix[2], (long)rTransform.matrix[5] ); 10493 aTranslation = lcl_convert( m_aGraphicsStack.front().m_aMapMode, m_aMapMode, getReferenceDevice(), aTranslation ); 10494 10495 sal_Int32 nTilingId = m_aTilings.size(); 10496 m_aTilings.push_back( TilingEmit() ); 10497 TilingEmit& rTile = m_aTilings.back(); 10498 rTile.m_nObject = createObject(); 10499 rTile.m_aResources = m_aOutputStreams.front().m_aResourceDict; 10500 rTile.m_aTransform.matrix[0] = rTransform.matrix[0] * fSX; 10501 rTile.m_aTransform.matrix[1] = rTransform.matrix[1] * fSY; 10502 rTile.m_aTransform.matrix[2] = aTranslation.Width(); 10503 rTile.m_aTransform.matrix[3] = rTransform.matrix[3] * fSX; 10504 rTile.m_aTransform.matrix[4] = rTransform.matrix[4] * fSY; 10505 rTile.m_aTransform.matrix[5] = -aTranslation.Height(); 10506 // caution: endRedirect pops the stream, so do this last 10507 rTile.m_pTilingStream = dynamic_cast<SvMemoryStream*>(endRedirect()); 10508 // FIXME: bound rect will not work with rotated matrix 10509 rTile.m_aRectangle = Rectangle( Point(0,0), aConvertRect.GetSize() ); 10510 rTile.m_aCellSize = aConvertRect.GetSize(); 10511 10512 OStringBuffer aObjName( 16 ); 10513 aObjName.append( 'P' ); 10514 aObjName.append( rTile.m_nObject ); 10515 pushResource( ResPattern, aObjName.makeStringAndClear(), rTile.m_nObject ); 10516 return nTilingId; 10517 } 10518 10519 void PDFWriterImpl::drawPolyPolygon( const PolyPolygon& rPolyPoly, sal_Int32 nPattern, bool bEOFill ) 10520 { 10521 if( nPattern < 0 || nPattern >= (sal_Int32)m_aTilings.size() ) 10522 return; 10523 10524 m_aPages.back().endStream(); 10525 sal_Int32 nXObject = createObject(); 10526 OStringBuffer aNameBuf( 16 ); 10527 aNameBuf.append( "Pol" ); 10528 aNameBuf.append( nXObject ); 10529 OString aObjName( aNameBuf.makeStringAndClear() ); 10530 Rectangle aObjRect; 10531 if( updateObject( nXObject ) ) 10532 { 10533 // get bounding rect of object 10534 PolyPolygon aSubDiv; 10535 rPolyPoly.AdaptiveSubdivide( aSubDiv ); 10536 aObjRect = aSubDiv.GetBoundRect(); 10537 Rectangle aConvObjRect( aObjRect ); 10538 m_aPages.back().convertRect( aConvObjRect ); 10539 10540 // move polypolygon to bottom left of page 10541 PolyPolygon aLocalPath( rPolyPoly ); 10542 sal_Int32 nPgWd = getReferenceDevice()->ImplGetDPIX() * m_aPages.back().getWidth() / 72; 10543 sal_Int32 nPgHt = getReferenceDevice()->ImplGetDPIY() * m_aPages.back().getHeight() / 72; 10544 Size aLogicPgSz = getReferenceDevice()->PixelToLogic( Size( nPgWd, nPgHt ), m_aGraphicsStack.front().m_aMapMode ); 10545 sal_Int32 nXOff = aObjRect.Left(); 10546 sal_Int32 nYOff = aLogicPgSz.Height() - aObjRect.Bottom(); 10547 aLocalPath.Move( -nXOff, nYOff ); 10548 10549 // prepare XObject's content stream 10550 OStringBuffer aStream( 512 ); 10551 aStream.append( "/Pattern cs /P" ); 10552 aStream.append( m_aTilings[ nPattern ].m_nObject ); 10553 aStream.append( " scn\n" ); 10554 m_aPages.back().appendPolyPolygon( aLocalPath, aStream ); 10555 aStream.append( bEOFill ? "f*" : "f" ); 10556 SvMemoryStream aMemStream( aStream.getLength() ); 10557 aMemStream.Write( aStream.getStr(), aStream.getLength() ); 10558 bool bDeflate = compressStream( &aMemStream ); 10559 aMemStream.Seek( STREAM_SEEK_TO_END ); 10560 sal_Int32 nStreamLen = (sal_Int32)aMemStream.Tell(); 10561 aMemStream.Seek( STREAM_SEEK_TO_BEGIN ); 10562 10563 // add new XObject to global resource dict 10564 m_aGlobalResourceDict.m_aXObjects[ aObjName ] = nXObject; 10565 10566 // write XObject 10567 OStringBuffer aLine( 512 ); 10568 aLine.append( nXObject ); 10569 aLine.append( " 0 obj\n" 10570 "<</Type/XObject/Subtype/Form/BBox[0 0 " ); 10571 appendFixedInt( aConvObjRect.GetWidth(), aLine ); 10572 aLine.append( ' ' ); 10573 appendFixedInt( aConvObjRect.GetHeight(), aLine ); 10574 aLine.append( "]/Length " ); 10575 aLine.append( nStreamLen ); 10576 if( bDeflate ) 10577 aLine.append( "/Filter/FlateDecode" ); 10578 aLine.append( ">>\n" 10579 "stream\n" ); 10580 writeBuffer( aLine.getStr(), aLine.getLength() ); 10581 checkAndEnableStreamEncryption( nXObject ); 10582 writeBuffer( aMemStream.GetData(), nStreamLen ); 10583 disableStreamEncryption(); 10584 writeBuffer( "\nendstream\nendobj\n\n", 19 ); 10585 } 10586 m_aPages.back().beginStream(); 10587 OStringBuffer aLine( 80 ); 10588 aLine.append( "q 1 0 0 1 " ); 10589 m_aPages.back().appendPoint( aObjRect.BottomLeft(), aLine ); 10590 aLine.append( " cm/" ); 10591 aLine.append( aObjName ); 10592 aLine.append( " Do Q\n" ); 10593 writeBuffer( aLine.getStr(), aLine.getLength() ); 10594 } 10595 10596 void PDFWriterImpl::updateGraphicsState() 10597 { 10598 OStringBuffer aLine( 256 ); 10599 GraphicsState& rNewState = m_aGraphicsStack.front(); 10600 // first set clip region since it might invalidate everything else 10601 10602 if( (rNewState.m_nUpdateFlags & GraphicsState::updateClipRegion) ) 10603 { 10604 rNewState.m_nUpdateFlags &= ~GraphicsState::updateClipRegion; 10605 10606 if( m_aCurrentPDFState.m_bClipRegion != rNewState.m_bClipRegion || 10607 ( rNewState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion != rNewState.m_aClipRegion ) ) 10608 { 10609 if( m_aCurrentPDFState.m_bClipRegion && m_aCurrentPDFState.m_aClipRegion.count() ) 10610 { 10611 aLine.append( "Q " ); 10612 // invalidate everything but the clip region 10613 m_aCurrentPDFState = GraphicsState(); 10614 rNewState.m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~GraphicsState::updateClipRegion); 10615 } 10616 if( rNewState.m_bClipRegion && rNewState.m_aClipRegion.count() ) 10617 { 10618 // clip region is always stored in private PDF mapmode 10619 MapMode aNewMapMode = rNewState.m_aMapMode; 10620 rNewState.m_aMapMode = m_aMapMode; 10621 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10622 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; 10623 10624 aLine.append( "q " ); 10625 m_aPages.back().appendPolyPolygon( rNewState.m_aClipRegion, aLine ); 10626 aLine.append( "W* n\n" ); 10627 rNewState.m_aMapMode = aNewMapMode; 10628 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10629 m_aCurrentPDFState.m_aMapMode = rNewState.m_aMapMode; 10630 } 10631 } 10632 } 10633 10634 if( (rNewState.m_nUpdateFlags & GraphicsState::updateMapMode) ) 10635 { 10636 rNewState.m_nUpdateFlags &= ~GraphicsState::updateMapMode; 10637 getReferenceDevice()->SetMapMode( rNewState.m_aMapMode ); 10638 } 10639 10640 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFont) ) 10641 { 10642 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFont; 10643 getReferenceDevice()->SetFont( rNewState.m_aFont ); 10644 getReferenceDevice()->ImplNewFont(); 10645 } 10646 10647 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLayoutMode) ) 10648 { 10649 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLayoutMode; 10650 getReferenceDevice()->SetLayoutMode( rNewState.m_nLayoutMode ); 10651 } 10652 10653 if( (rNewState.m_nUpdateFlags & GraphicsState::updateDigitLanguage) ) 10654 { 10655 rNewState.m_nUpdateFlags &= ~GraphicsState::updateDigitLanguage; 10656 getReferenceDevice()->SetDigitLanguage( rNewState.m_aDigitLanguage ); 10657 } 10658 10659 if( (rNewState.m_nUpdateFlags & GraphicsState::updateLineColor) ) 10660 { 10661 rNewState.m_nUpdateFlags &= ~GraphicsState::updateLineColor; 10662 if( m_aCurrentPDFState.m_aLineColor != rNewState.m_aLineColor && 10663 rNewState.m_aLineColor != Color( COL_TRANSPARENT ) ) 10664 { 10665 appendStrokingColor( rNewState.m_aLineColor, aLine ); 10666 aLine.append( "\n" ); 10667 } 10668 } 10669 10670 if( (rNewState.m_nUpdateFlags & GraphicsState::updateFillColor) ) 10671 { 10672 rNewState.m_nUpdateFlags &= ~GraphicsState::updateFillColor; 10673 if( m_aCurrentPDFState.m_aFillColor != rNewState.m_aFillColor && 10674 rNewState.m_aFillColor != Color( COL_TRANSPARENT ) ) 10675 { 10676 appendNonStrokingColor( rNewState.m_aFillColor, aLine ); 10677 aLine.append( "\n" ); 10678 } 10679 } 10680 10681 if( (rNewState.m_nUpdateFlags & GraphicsState::updateTransparentPercent) ) 10682 { 10683 rNewState.m_nUpdateFlags &= ~GraphicsState::updateTransparentPercent; 10684 if( m_aContext.Version >= PDFWriter::PDF_1_4 && m_aCurrentPDFState.m_nTransparentPercent != rNewState.m_nTransparentPercent ) 10685 { 10686 // TODO: switch extended graphicsstate 10687 } 10688 } 10689 10690 // everything is up to date now 10691 m_aCurrentPDFState = m_aGraphicsStack.front(); 10692 if( aLine.getLength() ) 10693 writeBuffer( aLine.getStr(), aLine.getLength() ); 10694 } 10695 10696 /* #i47544# imitate OutputDevice behaviour: 10697 * if a font with a nontransparent color is set, it overwrites the current 10698 * text color. OTOH setting the text color will overwrite the color of the font. 10699 */ 10700 void PDFWriterImpl::setFont( const Font& rFont ) 10701 { 10702 Color aColor = rFont.GetColor(); 10703 if( aColor == Color( COL_TRANSPARENT ) ) 10704 aColor = m_aGraphicsStack.front().m_aFont.GetColor(); 10705 m_aGraphicsStack.front().m_aFont = rFont; 10706 m_aGraphicsStack.front().m_aFont.SetColor( aColor ); 10707 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateFont; 10708 } 10709 10710 void PDFWriterImpl::push( sal_uInt16 nFlags ) 10711 { 10712 OSL_ENSURE( m_aGraphicsStack.size() > 0, "invalid graphics stack" ); 10713 m_aGraphicsStack.push_front( m_aGraphicsStack.front() ); 10714 m_aGraphicsStack.front().m_nFlags = nFlags; 10715 } 10716 10717 void PDFWriterImpl::pop() 10718 { 10719 OSL_ENSURE( m_aGraphicsStack.size() > 1, "pop without push" ); 10720 if( m_aGraphicsStack.size() < 2 ) 10721 return; 10722 10723 GraphicsState aState = m_aGraphicsStack.front(); 10724 m_aGraphicsStack.pop_front(); 10725 GraphicsState& rOld = m_aGraphicsStack.front(); 10726 10727 // move those parameters back that were not pushed 10728 // in the first place 10729 if( ! (aState.m_nFlags & PUSH_LINECOLOR) ) 10730 setLineColor( aState.m_aLineColor ); 10731 if( ! (aState.m_nFlags & PUSH_FILLCOLOR) ) 10732 setFillColor( aState.m_aFillColor ); 10733 if( ! (aState.m_nFlags & PUSH_FONT) ) 10734 setFont( aState.m_aFont ); 10735 if( ! (aState.m_nFlags & PUSH_TEXTCOLOR) ) 10736 setTextColor( aState.m_aFont.GetColor() ); 10737 if( ! (aState.m_nFlags & PUSH_MAPMODE) ) 10738 setMapMode( aState.m_aMapMode ); 10739 if( ! (aState.m_nFlags & PUSH_CLIPREGION) ) 10740 { 10741 // do not use setClipRegion here 10742 // it would convert again assuming the current mapmode 10743 rOld.m_aClipRegion = aState.m_aClipRegion; 10744 rOld.m_bClipRegion = aState.m_bClipRegion; 10745 } 10746 if( ! (aState.m_nFlags & PUSH_TEXTLINECOLOR ) ) 10747 setTextLineColor( aState.m_aTextLineColor ); 10748 if( ! (aState.m_nFlags & PUSH_OVERLINECOLOR ) ) 10749 setOverlineColor( aState.m_aOverlineColor ); 10750 if( ! (aState.m_nFlags & PUSH_TEXTALIGN ) ) 10751 setTextAlign( aState.m_aFont.GetAlign() ); 10752 if( ! (aState.m_nFlags & PUSH_TEXTFILLCOLOR) ) 10753 setTextFillColor( aState.m_aFont.GetFillColor() ); 10754 if( ! (aState.m_nFlags & PUSH_REFPOINT) ) 10755 { 10756 // what ? 10757 } 10758 // invalidate graphics state 10759 m_aGraphicsStack.front().m_nUpdateFlags = sal::static_int_cast<sal_uInt16>(~0U); 10760 } 10761 10762 void PDFWriterImpl::setMapMode( const MapMode& rMapMode ) 10763 { 10764 m_aGraphicsStack.front().m_aMapMode = rMapMode; 10765 getReferenceDevice()->SetMapMode( rMapMode ); 10766 m_aCurrentPDFState.m_aMapMode = rMapMode; 10767 } 10768 10769 void PDFWriterImpl::setClipRegion( const basegfx::B2DPolyPolygon& rRegion ) 10770 { 10771 basegfx::B2DPolyPolygon aRegion = getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ); 10772 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); 10773 m_aGraphicsStack.front().m_aClipRegion = aRegion; 10774 m_aGraphicsStack.front().m_bClipRegion = true; 10775 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10776 } 10777 10778 void PDFWriterImpl::moveClipRegion( sal_Int32 nX, sal_Int32 nY ) 10779 { 10780 if( m_aGraphicsStack.front().m_bClipRegion && m_aGraphicsStack.front().m_aClipRegion.count() ) 10781 { 10782 Point aPoint( lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10783 m_aMapMode, 10784 getReferenceDevice(), 10785 Point( nX, nY ) ) ); 10786 aPoint -= lcl_convert( m_aGraphicsStack.front().m_aMapMode, 10787 m_aMapMode, 10788 getReferenceDevice(), 10789 Point() ); 10790 basegfx::B2DHomMatrix aMat; 10791 aMat.translate( aPoint.X(), aPoint.Y() ); 10792 m_aGraphicsStack.front().m_aClipRegion.transform( aMat ); 10793 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10794 } 10795 } 10796 10797 bool PDFWriterImpl::intersectClipRegion( const Rectangle& rRect ) 10798 { 10799 basegfx::B2DPolyPolygon aRect( basegfx::tools::createPolygonFromRect( 10800 basegfx::B2DRectangle( rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() ) ) ); 10801 return intersectClipRegion( aRect ); 10802 } 10803 10804 10805 bool PDFWriterImpl::intersectClipRegion( const basegfx::B2DPolyPolygon& rRegion ) 10806 { 10807 basegfx::B2DPolyPolygon aRegion( getReferenceDevice()->LogicToPixel( rRegion, m_aGraphicsStack.front().m_aMapMode ) ); 10808 aRegion = getReferenceDevice()->PixelToLogic( aRegion, m_aMapMode ); 10809 m_aGraphicsStack.front().m_nUpdateFlags |= GraphicsState::updateClipRegion; 10810 if( m_aGraphicsStack.front().m_bClipRegion ) 10811 { 10812 basegfx::B2DPolyPolygon aOld( basegfx::tools::prepareForPolygonOperation( m_aGraphicsStack.front().m_aClipRegion ) ); 10813 aRegion = basegfx::tools::prepareForPolygonOperation( aRegion ); 10814 m_aGraphicsStack.front().m_aClipRegion = basegfx::tools::solvePolygonOperationAnd( aOld, aRegion ); 10815 } 10816 else 10817 { 10818 m_aGraphicsStack.front().m_aClipRegion = aRegion; 10819 m_aGraphicsStack.front().m_bClipRegion = true; 10820 } 10821 return true; 10822 } 10823 10824 void PDFWriterImpl::createNote( const Rectangle& rRect, const PDFNote& rNote, sal_Int32 nPageNr ) 10825 { 10826 if( nPageNr < 0 ) 10827 nPageNr = m_nCurrentPage; 10828 10829 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10830 return; 10831 10832 m_aNotes.push_back( PDFNoteEntry() ); 10833 m_aNotes.back().m_nObject = createObject(); 10834 m_aNotes.back().m_aContents = rNote; 10835 m_aNotes.back().m_aRect = rRect; 10836 // convert to default user space now, since the mapmode may change 10837 m_aPages[nPageNr].convertRect( m_aNotes.back().m_aRect ); 10838 10839 // insert note to page's annotation list 10840 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aNotes.back().m_nObject ); 10841 } 10842 10843 sal_Int32 PDFWriterImpl::createLink( const Rectangle& rRect, sal_Int32 nPageNr ) 10844 { 10845 if( nPageNr < 0 ) 10846 nPageNr = m_nCurrentPage; 10847 10848 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10849 return -1; 10850 10851 sal_Int32 nRet = m_aLinks.size(); 10852 10853 m_aLinks.push_back( PDFLink() ); 10854 m_aLinks.back().m_nObject = createObject(); 10855 m_aLinks.back().m_nPage = nPageNr; 10856 m_aLinks.back().m_aRect = rRect; 10857 // convert to default user space now, since the mapmode may change 10858 m_aPages[nPageNr].convertRect( m_aLinks.back().m_aRect ); 10859 10860 // insert link to page's annotation list 10861 m_aPages[ nPageNr ].m_aAnnotations.push_back( m_aLinks.back().m_nObject ); 10862 10863 return nRet; 10864 } 10865 10866 //--->i56629 10867 sal_Int32 PDFWriterImpl::createNamedDest( const rtl::OUString& sDestName, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10868 { 10869 if( nPageNr < 0 ) 10870 nPageNr = m_nCurrentPage; 10871 10872 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10873 return -1; 10874 10875 sal_Int32 nRet = m_aNamedDests.size(); 10876 10877 m_aNamedDests.push_back( PDFNamedDest() ); 10878 m_aNamedDests.back().m_aDestName = sDestName; 10879 m_aNamedDests.back().m_nPage = nPageNr; 10880 m_aNamedDests.back().m_eType = eType; 10881 m_aNamedDests.back().m_aRect = rRect; 10882 // convert to default user space now, since the mapmode may change 10883 m_aPages[nPageNr].convertRect( m_aNamedDests.back().m_aRect ); 10884 10885 return nRet; 10886 } 10887 //<---i56629 10888 10889 sal_Int32 PDFWriterImpl::createDest( const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10890 { 10891 if( nPageNr < 0 ) 10892 nPageNr = m_nCurrentPage; 10893 10894 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 10895 return -1; 10896 10897 sal_Int32 nRet = m_aDests.size(); 10898 10899 m_aDests.push_back( PDFDest() ); 10900 m_aDests.back().m_nPage = nPageNr; 10901 m_aDests.back().m_eType = eType; 10902 m_aDests.back().m_aRect = rRect; 10903 // convert to default user space now, since the mapmode may change 10904 m_aPages[nPageNr].convertRect( m_aDests.back().m_aRect ); 10905 10906 return nRet; 10907 } 10908 10909 sal_Int32 PDFWriterImpl::registerDestReference( sal_Int32 nDestId, const Rectangle& rRect, sal_Int32 nPageNr, PDFWriter::DestAreaType eType ) 10910 { 10911 return m_aDestinationIdTranslation[ nDestId ] = createDest( rRect, nPageNr, eType ); 10912 } 10913 10914 sal_Int32 PDFWriterImpl::setLinkDest( sal_Int32 nLinkId, sal_Int32 nDestId ) 10915 { 10916 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) 10917 return -1; 10918 if( nDestId < 0 || nDestId >= (sal_Int32)m_aDests.size() ) 10919 return -2; 10920 10921 m_aLinks[ nLinkId ].m_nDest = nDestId; 10922 10923 return 0; 10924 } 10925 10926 sal_Int32 PDFWriterImpl::setLinkURL( sal_Int32 nLinkId, const OUString& rURL ) 10927 { 10928 if( nLinkId < 0 || nLinkId >= (sal_Int32)m_aLinks.size() ) 10929 return -1; 10930 10931 m_aLinks[ nLinkId ].m_nDest = -1; 10932 10933 using namespace ::com::sun::star; 10934 10935 if (!m_xTrans.is()) 10936 { 10937 uno::Reference< lang::XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() ); 10938 if( xFact.is() ) 10939 { 10940 m_xTrans = uno::Reference < util::XURLTransformer >( 10941 xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ) ) ), uno::UNO_QUERY ); 10942 } 10943 } 10944 10945 util::URL aURL; 10946 aURL.Complete = rURL; 10947 10948 if (m_xTrans.is()) 10949 m_xTrans->parseStrict( aURL ); 10950 10951 m_aLinks[ nLinkId ].m_aURL = aURL.Complete; 10952 10953 return 0; 10954 } 10955 10956 void PDFWriterImpl::setLinkPropertyId( sal_Int32 nLinkId, sal_Int32 nPropertyId ) 10957 { 10958 m_aLinkPropertyMap[ nPropertyId ] = nLinkId; 10959 } 10960 10961 sal_Int32 PDFWriterImpl::createOutlineItem( sal_Int32 nParent, const OUString& rText, sal_Int32 nDestID ) 10962 { 10963 // create new item 10964 sal_Int32 nNewItem = m_aOutline.size(); 10965 m_aOutline.push_back( PDFOutlineEntry() ); 10966 10967 // set item attributes 10968 setOutlineItemParent( nNewItem, nParent ); 10969 setOutlineItemText( nNewItem, rText ); 10970 setOutlineItemDest( nNewItem, nDestID ); 10971 10972 return nNewItem; 10973 } 10974 10975 sal_Int32 PDFWriterImpl::setOutlineItemParent( sal_Int32 nItem, sal_Int32 nNewParent ) 10976 { 10977 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) 10978 return -1; 10979 10980 int nRet = 0; 10981 10982 if( nNewParent < 0 || nNewParent >= (sal_Int32)m_aOutline.size() || nNewParent == nItem ) 10983 { 10984 nNewParent = 0; 10985 nRet = -2; 10986 } 10987 // remove item from previous parent 10988 sal_Int32 nParentID = m_aOutline[ nItem ].m_nParentID; 10989 if( nParentID >= 0 && nParentID < (sal_Int32)m_aOutline.size() ) 10990 { 10991 PDFOutlineEntry& rParent = m_aOutline[ nParentID ]; 10992 10993 for( std::vector<sal_Int32>::iterator it = rParent.m_aChildren.begin(); 10994 it != rParent.m_aChildren.end(); ++it ) 10995 { 10996 if( *it == nItem ) 10997 { 10998 rParent.m_aChildren.erase( it ); 10999 break; 11000 } 11001 } 11002 } 11003 11004 // insert item to new parent's list of children 11005 m_aOutline[ nNewParent ].m_aChildren.push_back( nItem ); 11006 11007 return nRet; 11008 } 11009 11010 sal_Int32 PDFWriterImpl::setOutlineItemText( sal_Int32 nItem, const OUString& rText ) 11011 { 11012 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) 11013 return -1; 11014 11015 m_aOutline[ nItem ].m_aTitle = psp::WhitespaceToSpace( rText ); 11016 return 0; 11017 } 11018 11019 sal_Int32 PDFWriterImpl::setOutlineItemDest( sal_Int32 nItem, sal_Int32 nDestID ) 11020 { 11021 if( nItem < 1 || nItem >= (sal_Int32)m_aOutline.size() ) // item does not exist 11022 return -1; 11023 if( nDestID < 0 || nDestID >= (sal_Int32)m_aDests.size() ) // dest does not exist 11024 return -2; 11025 m_aOutline[nItem].m_nDestID = nDestID; 11026 return 0; 11027 } 11028 11029 const sal_Char* PDFWriterImpl::getStructureTag( PDFWriter::StructElement eType ) 11030 { 11031 static std::map< PDFWriter::StructElement, const char* > aTagStrings; 11032 if( aTagStrings.empty() ) 11033 { 11034 aTagStrings[ PDFWriter::NonStructElement] = "NonStruct"; 11035 aTagStrings[ PDFWriter::Document ] = "Document"; 11036 aTagStrings[ PDFWriter::Part ] = "Part"; 11037 aTagStrings[ PDFWriter::Article ] = "Art"; 11038 aTagStrings[ PDFWriter::Section ] = "Sect"; 11039 aTagStrings[ PDFWriter::Division ] = "Div"; 11040 aTagStrings[ PDFWriter::BlockQuote ] = "BlockQuote"; 11041 aTagStrings[ PDFWriter::Caption ] = "Caption"; 11042 aTagStrings[ PDFWriter::TOC ] = "TOC"; 11043 aTagStrings[ PDFWriter::TOCI ] = "TOCI"; 11044 aTagStrings[ PDFWriter::Index ] = "Index"; 11045 aTagStrings[ PDFWriter::Paragraph ] = "P"; 11046 aTagStrings[ PDFWriter::Heading ] = "H"; 11047 aTagStrings[ PDFWriter::H1 ] = "H1"; 11048 aTagStrings[ PDFWriter::H2 ] = "H2"; 11049 aTagStrings[ PDFWriter::H3 ] = "H3"; 11050 aTagStrings[ PDFWriter::H4 ] = "H4"; 11051 aTagStrings[ PDFWriter::H5 ] = "H5"; 11052 aTagStrings[ PDFWriter::H6 ] = "H6"; 11053 aTagStrings[ PDFWriter::List ] = "L"; 11054 aTagStrings[ PDFWriter::ListItem ] = "LI"; 11055 aTagStrings[ PDFWriter::LILabel ] = "Lbl"; 11056 aTagStrings[ PDFWriter::LIBody ] = "LBody"; 11057 aTagStrings[ PDFWriter::Table ] = "Table"; 11058 aTagStrings[ PDFWriter::TableRow ] = "TR"; 11059 aTagStrings[ PDFWriter::TableHeader ] = "TH"; 11060 aTagStrings[ PDFWriter::TableData ] = "TD"; 11061 aTagStrings[ PDFWriter::Span ] = "Span"; 11062 aTagStrings[ PDFWriter::Quote ] = "Quote"; 11063 aTagStrings[ PDFWriter::Note ] = "Note"; 11064 aTagStrings[ PDFWriter::Reference ] = "Reference"; 11065 aTagStrings[ PDFWriter::BibEntry ] = "BibEntry"; 11066 aTagStrings[ PDFWriter::Code ] = "Code"; 11067 aTagStrings[ PDFWriter::Link ] = "Link"; 11068 aTagStrings[ PDFWriter::Figure ] = "Figure"; 11069 aTagStrings[ PDFWriter::Formula ] = "Formula"; 11070 aTagStrings[ PDFWriter::Form ] = "Form"; 11071 } 11072 11073 std::map< PDFWriter::StructElement, const char* >::const_iterator it = aTagStrings.find( eType ); 11074 11075 return it != aTagStrings.end() ? it->second : "Div"; 11076 } 11077 11078 void PDFWriterImpl::beginStructureElementMCSeq() 11079 { 11080 if( m_bEmitStructure && 11081 m_nCurrentStructElement > 0 && // StructTreeRoot 11082 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence 11083 ) 11084 { 11085 PDFStructureElement& rEle = m_aStructure[ m_nCurrentStructElement ]; 11086 OStringBuffer aLine( 128 ); 11087 sal_Int32 nMCID = m_aPages[ m_nCurrentPage ].m_aMCIDParents.size(); 11088 aLine.append( "/" ); 11089 if( rEle.m_aAlias.getLength() > 0 ) 11090 aLine.append( rEle.m_aAlias ); 11091 else 11092 aLine.append( getStructureTag( rEle.m_eType ) ); 11093 aLine.append( "<</MCID " ); 11094 aLine.append( nMCID ); 11095 aLine.append( ">>BDC\n" ); 11096 writeBuffer( aLine.getStr(), aLine.getLength() ); 11097 11098 // update the element's content list 11099 #if OSL_DEBUG_LEVEL > 1 11100 fprintf( stderr, "beginning marked content id %" SAL_PRIdINT32 " on page object %" SAL_PRIdINT32 ", structure first page = %" SAL_PRIdINT32 "\n", 11101 nMCID, 11102 m_aPages[ m_nCurrentPage ].m_nPageObject, 11103 rEle.m_nFirstPageObject ); 11104 #endif 11105 rEle.m_aKids.push_back( PDFStructureElementKid( nMCID, m_aPages[m_nCurrentPage].m_nPageObject ) ); 11106 // update the page's mcid parent list 11107 m_aPages[ m_nCurrentPage ].m_aMCIDParents.push_back( rEle.m_nObject ); 11108 // mark element MC sequence as open 11109 rEle.m_bOpenMCSeq = true; 11110 } 11111 // handle artifacts 11112 else if( ! m_bEmitStructure && m_aContext.Tagged && 11113 m_nCurrentStructElement > 0 && 11114 m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement && 11115 ! m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // already opened sequence 11116 ) 11117 { 11118 OStringBuffer aLine( 128 ); 11119 aLine.append( "/Artifact BMC\n" ); 11120 writeBuffer( aLine.getStr(), aLine.getLength() ); 11121 // mark element MC sequence as open 11122 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = true; 11123 } 11124 } 11125 11126 void PDFWriterImpl::endStructureElementMCSeq() 11127 { 11128 if( m_nCurrentStructElement > 0 && // StructTreeRoot 11129 ( m_bEmitStructure || m_aStructure[ m_nCurrentStructElement ].m_eType == PDFWriter::NonStructElement ) && 11130 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq // must have an opened MC sequence 11131 ) 11132 { 11133 writeBuffer( "EMC\n", 4 ); 11134 m_aStructure[ m_nCurrentStructElement ].m_bOpenMCSeq = false; 11135 } 11136 } 11137 11138 bool PDFWriterImpl::checkEmitStructure() 11139 { 11140 bool bEmit = false; 11141 if( m_aContext.Tagged ) 11142 { 11143 bEmit = true; 11144 sal_Int32 nEle = m_nCurrentStructElement; 11145 while( nEle > 0 && nEle < sal_Int32(m_aStructure.size()) ) 11146 { 11147 if( m_aStructure[ nEle ].m_eType == PDFWriter::NonStructElement ) 11148 { 11149 bEmit = false; 11150 break; 11151 } 11152 nEle = m_aStructure[ nEle ].m_nParentElement; 11153 } 11154 } 11155 return bEmit; 11156 } 11157 11158 sal_Int32 PDFWriterImpl::beginStructureElement( PDFWriter::StructElement eType, const rtl::OUString& rAlias ) 11159 { 11160 if( m_nCurrentPage < 0 ) 11161 return -1; 11162 11163 if( ! m_aContext.Tagged ) 11164 return -1; 11165 11166 // close eventual current MC sequence 11167 endStructureElementMCSeq(); 11168 11169 if( m_nCurrentStructElement == 0 && 11170 eType != PDFWriter::Document && eType != PDFWriter::NonStructElement ) 11171 { 11172 // struct tree root hit, but not beginning document 11173 // this might happen with setCurrentStructureElement 11174 // silently insert structure into document again if one properly exists 11175 if( ! m_aStructure[ 0 ].m_aChildren.empty() ) 11176 { 11177 PDFWriter::StructElement childType = PDFWriter::NonStructElement; 11178 sal_Int32 nNewCurElement = 0; 11179 const std::list< sal_Int32 >& rRootChildren = m_aStructure[0].m_aChildren; 11180 for( std::list< sal_Int32 >::const_iterator it = rRootChildren.begin(); 11181 childType != PDFWriter::Document && it != rRootChildren.end(); ++it ) 11182 { 11183 nNewCurElement = *it; 11184 childType = m_aStructure[ nNewCurElement ].m_eType; 11185 } 11186 if( childType == PDFWriter::Document ) 11187 { 11188 m_nCurrentStructElement = nNewCurElement; 11189 DBG_ASSERT( 0, "Structure element inserted to StructTreeRoot that is not a document" ); 11190 } 11191 else { 11192 DBG_ERROR( "document structure in disorder !" ); 11193 } 11194 } 11195 else { 11196 DBG_ERROR( "PDF document structure MUST be contained in a Document element" ); 11197 } 11198 } 11199 11200 sal_Int32 nNewId = sal_Int32(m_aStructure.size()); 11201 m_aStructure.push_back( PDFStructureElement() ); 11202 PDFStructureElement& rEle = m_aStructure.back(); 11203 rEle.m_eType = eType; 11204 rEle.m_nOwnElement = nNewId; 11205 rEle.m_nParentElement = m_nCurrentStructElement; 11206 rEle.m_nFirstPageObject = m_aPages[ m_nCurrentPage ].m_nPageObject; 11207 m_aStructure[ m_nCurrentStructElement ].m_aChildren.push_back( nNewId ); 11208 m_nCurrentStructElement = nNewId; 11209 11210 // handle alias names 11211 if( rAlias.getLength() && eType != PDFWriter::NonStructElement ) 11212 { 11213 OStringBuffer aNameBuf( rAlias.getLength() ); 11214 appendName( rAlias, aNameBuf ); 11215 OString aAliasName( aNameBuf.makeStringAndClear() ); 11216 rEle.m_aAlias = aAliasName; 11217 m_aRoleMap[ aAliasName ] = getStructureTag( eType ); 11218 } 11219 11220 #if OSL_DEBUG_LEVEL > 1 11221 OStringBuffer aLine( "beginStructureElement " ); 11222 aLine.append( m_nCurrentStructElement ); 11223 aLine.append( ": " ); 11224 aLine.append( getStructureTag( eType ) ); 11225 if( rEle.m_aAlias.getLength() ) 11226 { 11227 aLine.append( " aliased as \"" ); 11228 aLine.append( rEle.m_aAlias ); 11229 aLine.append( '\"' ); 11230 } 11231 emitComment( aLine.getStr() ); 11232 #endif 11233 11234 // check whether to emit structure henceforth 11235 m_bEmitStructure = checkEmitStructure(); 11236 11237 if( m_bEmitStructure ) // don't create nonexistant objects 11238 { 11239 rEle.m_nObject = createObject(); 11240 // update parent's kids list 11241 m_aStructure[ rEle.m_nParentElement ].m_aKids.push_back( rEle.m_nObject ); 11242 } 11243 return nNewId; 11244 } 11245 11246 void PDFWriterImpl::endStructureElement() 11247 { 11248 if( m_nCurrentPage < 0 ) 11249 return; 11250 11251 if( ! m_aContext.Tagged ) 11252 return; 11253 11254 if( m_nCurrentStructElement == 0 ) 11255 { 11256 // hit the struct tree root, that means there is an endStructureElement 11257 // without corresponding beginStructureElement 11258 return; 11259 } 11260 11261 // end the marked content sequence 11262 endStructureElementMCSeq(); 11263 11264 #if OSL_DEBUG_LEVEL > 1 11265 OStringBuffer aLine( "endStructureElement " ); 11266 aLine.append( m_nCurrentStructElement ); 11267 aLine.append( ": " ); 11268 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); 11269 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() ) 11270 { 11271 aLine.append( " aliased as \"" ); 11272 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); 11273 aLine.append( '\"' ); 11274 } 11275 #endif 11276 11277 // "end" the structure element, the parent becomes current element 11278 m_nCurrentStructElement = m_aStructure[ m_nCurrentStructElement ].m_nParentElement; 11279 11280 // check whether to emit structure henceforth 11281 m_bEmitStructure = checkEmitStructure(); 11282 11283 #if OSL_DEBUG_LEVEL > 1 11284 if( m_bEmitStructure ) 11285 emitComment( aLine.getStr() ); 11286 #endif 11287 } 11288 11289 //---> i94258 11290 /* 11291 * This function adds an internal structure list container to overcome the 8191 elements array limitation 11292 * in kids element emission. 11293 * Recursive function 11294 * 11295 */ 11296 void PDFWriterImpl::addInternalStructureContainer( PDFStructureElement& rEle ) 11297 { 11298 if( rEle.m_eType == PDFWriter::NonStructElement && 11299 rEle.m_nOwnElement != rEle.m_nParentElement ) 11300 return; 11301 11302 for( std::list< sal_Int32 >::const_iterator it = rEle.m_aChildren.begin(); it != rEle.m_aChildren.end(); ++it ) 11303 { 11304 if( *it > 0 && *it < sal_Int32(m_aStructure.size()) ) 11305 { 11306 PDFStructureElement& rChild = m_aStructure[ *it ]; 11307 if( rChild.m_eType != PDFWriter::NonStructElement ) 11308 { 11309 //triggered when a child of the rEle element is found 11310 if( rChild.m_nParentElement == rEle.m_nOwnElement ) 11311 addInternalStructureContainer( rChild );//examine the child 11312 else 11313 { 11314 DBG_ERROR( "PDFWriterImpl::addInternalStructureContainer: invalid child structure element" ); 11315 #if OSL_DEBUG_LEVEL > 1 11316 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure elemnt with id %" SAL_PRIdINT32 "\n", *it ); 11317 #endif 11318 } 11319 } 11320 } 11321 else 11322 { 11323 DBG_ERROR( "PDFWriterImpl::emitStructure: invalid child structure id" ); 11324 #if OSL_DEBUG_LEVEL > 1 11325 fprintf( stderr, "PDFWriterImpl::addInternalStructureContainer: invalid child structure id %" SAL_PRIdINT32 "\n", *it ); 11326 #endif 11327 } 11328 } 11329 11330 if( rEle.m_nOwnElement != rEle.m_nParentElement ) 11331 { 11332 if( !rEle.m_aKids.empty() ) 11333 { 11334 if( rEle.m_aKids.size() > ncMaxPDFArraySize ) { 11335 //then we need to add the containers for the kids elements 11336 // a list to be used for the new kid element 11337 std::list< PDFStructureElementKid > aNewKids; 11338 std::list< sal_Int32 > aNewChildren; 11339 11340 // add Div in RoleMap, in case no one else did (TODO: is it needed? Is it dangerous?) 11341 OStringBuffer aNameBuf( "Div" ); 11342 OString aAliasName( aNameBuf.makeStringAndClear() ); 11343 m_aRoleMap[ aAliasName ] = getStructureTag( PDFWriter::Division ); 11344 11345 while( rEle.m_aKids.size() > ncMaxPDFArraySize ) 11346 { 11347 sal_Int32 nCurrentStructElement = rEle.m_nOwnElement; 11348 sal_Int32 nNewId = sal_Int32(m_aStructure.size()); 11349 m_aStructure.push_back( PDFStructureElement() ); 11350 PDFStructureElement& rEleNew = m_aStructure.back(); 11351 rEleNew.m_aAlias = aAliasName; 11352 rEleNew.m_eType = PDFWriter::Division; // a new Div type container 11353 rEleNew.m_nOwnElement = nNewId; 11354 rEleNew.m_nParentElement = nCurrentStructElement; 11355 //inherit the same page as the first child to be reparented 11356 rEleNew.m_nFirstPageObject = m_aStructure[ rEle.m_aChildren.front() ].m_nFirstPageObject; 11357 rEleNew.m_nObject = createObject();//assign a PDF object number 11358 //add the object to the kid list of the parent 11359 aNewKids.push_back( PDFStructureElementKid( rEleNew.m_nObject ) ); 11360 aNewChildren.push_back( nNewId ); 11361 11362 std::list< sal_Int32 >::iterator aChildEndIt( rEle.m_aChildren.begin() ); 11363 std::list< PDFStructureElementKid >::iterator aKidEndIt( rEle.m_aKids.begin() ); 11364 advance( aChildEndIt, ncMaxPDFArraySize ); 11365 advance( aKidEndIt, ncMaxPDFArraySize ); 11366 11367 rEleNew.m_aKids.splice( rEleNew.m_aKids.begin(), 11368 rEle.m_aKids, 11369 rEle.m_aKids.begin(), 11370 aKidEndIt ); 11371 rEleNew.m_aChildren.splice( rEleNew.m_aChildren.begin(), 11372 rEle.m_aChildren, 11373 rEle.m_aChildren.begin(), 11374 aChildEndIt ); 11375 // set the kid's new parent 11376 for( std::list< sal_Int32 >::const_iterator it = rEleNew.m_aChildren.begin(); 11377 it != rEleNew.m_aChildren.end(); ++it ) 11378 { 11379 m_aStructure[ *it ].m_nParentElement = nNewId; 11380 } 11381 } 11382 //finally add the new kids resulting from the container added 11383 rEle.m_aKids.insert( rEle.m_aKids.begin(), aNewKids.begin(), aNewKids.end() ); 11384 rEle.m_aChildren.insert( rEle.m_aChildren.begin(), aNewChildren.begin(), aNewChildren.end() ); 11385 } 11386 } 11387 } 11388 } 11389 //<--- i94258 11390 11391 bool PDFWriterImpl::setCurrentStructureElement( sal_Int32 nEle ) 11392 { 11393 bool bSuccess = false; 11394 11395 if( m_aContext.Tagged && nEle >= 0 && nEle < sal_Int32(m_aStructure.size()) ) 11396 { 11397 // end eventual previous marked content sequence 11398 endStructureElementMCSeq(); 11399 11400 m_nCurrentStructElement = nEle; 11401 m_bEmitStructure = checkEmitStructure(); 11402 #if OSL_DEBUG_LEVEL > 1 11403 OStringBuffer aLine( "setCurrentStructureElement " ); 11404 aLine.append( m_nCurrentStructElement ); 11405 aLine.append( ": " ); 11406 aLine.append( getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ) ); 11407 if( m_aStructure[ m_nCurrentStructElement ].m_aAlias.getLength() ) 11408 { 11409 aLine.append( " aliased as \"" ); 11410 aLine.append( m_aStructure[ m_nCurrentStructElement ].m_aAlias ); 11411 aLine.append( '\"' ); 11412 } 11413 if( ! m_bEmitStructure ) 11414 aLine.append( " (inside NonStruct)" ); 11415 emitComment( aLine.getStr() ); 11416 #endif 11417 bSuccess = true; 11418 } 11419 11420 return bSuccess; 11421 } 11422 11423 sal_Int32 PDFWriterImpl::getCurrentStructureElement() 11424 { 11425 return m_nCurrentStructElement; 11426 } 11427 11428 bool PDFWriterImpl::setStructureAttribute( enum PDFWriter::StructAttribute eAttr, enum PDFWriter::StructAttributeValue eVal ) 11429 { 11430 if( !m_aContext.Tagged ) 11431 return false; 11432 11433 bool bInsert = false; 11434 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11435 { 11436 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11437 switch( eAttr ) 11438 { 11439 case PDFWriter::Placement: 11440 if( eVal == PDFWriter::Block || 11441 eVal == PDFWriter::Inline || 11442 eVal == PDFWriter::Before || 11443 eVal == PDFWriter::Start || 11444 eVal == PDFWriter::End ) 11445 bInsert = true; 11446 break; 11447 case PDFWriter::WritingMode: 11448 if( eVal == PDFWriter::LrTb || 11449 eVal == PDFWriter::RlTb || 11450 eVal == PDFWriter::TbRl ) 11451 { 11452 bInsert = true; 11453 } 11454 break; 11455 case PDFWriter::TextAlign: 11456 if( eVal == PDFWriter::Start || 11457 eVal == PDFWriter::Center || 11458 eVal == PDFWriter::End || 11459 eVal == PDFWriter::Justify ) 11460 { 11461 if( eType == PDFWriter::Paragraph || 11462 eType == PDFWriter::Heading || 11463 eType == PDFWriter::H1 || 11464 eType == PDFWriter::H2 || 11465 eType == PDFWriter::H3 || 11466 eType == PDFWriter::H4 || 11467 eType == PDFWriter::H5 || 11468 eType == PDFWriter::H6 || 11469 eType == PDFWriter::List || 11470 eType == PDFWriter::ListItem || 11471 eType == PDFWriter::LILabel || 11472 eType == PDFWriter::LIBody || 11473 eType == PDFWriter::Table || 11474 eType == PDFWriter::TableRow || 11475 eType == PDFWriter::TableHeader || 11476 eType == PDFWriter::TableData ) 11477 { 11478 bInsert = true; 11479 } 11480 } 11481 break; 11482 case PDFWriter::Width: 11483 case PDFWriter::Height: 11484 if( eVal == PDFWriter::Auto ) 11485 { 11486 if( eType == PDFWriter::Figure || 11487 eType == PDFWriter::Formula || 11488 eType == PDFWriter::Form || 11489 eType == PDFWriter::Table || 11490 eType == PDFWriter::TableHeader || 11491 eType == PDFWriter::TableData ) 11492 { 11493 bInsert = true; 11494 } 11495 } 11496 break; 11497 case PDFWriter::BlockAlign: 11498 if( eVal == PDFWriter::Before || 11499 eVal == PDFWriter::Middle || 11500 eVal == PDFWriter::After || 11501 eVal == PDFWriter::Justify ) 11502 { 11503 if( eType == PDFWriter::TableHeader || 11504 eType == PDFWriter::TableData ) 11505 { 11506 bInsert = true; 11507 } 11508 } 11509 break; 11510 case PDFWriter::InlineAlign: 11511 if( eVal == PDFWriter::Start || 11512 eVal == PDFWriter::Center || 11513 eVal == PDFWriter::End ) 11514 { 11515 if( eType == PDFWriter::TableHeader || 11516 eType == PDFWriter::TableData ) 11517 { 11518 bInsert = true; 11519 } 11520 } 11521 break; 11522 case PDFWriter::LineHeight: 11523 if( eVal == PDFWriter::Normal || 11524 eVal == PDFWriter::Auto ) 11525 { 11526 // only for ILSE and BLSE 11527 if( eType == PDFWriter::Paragraph || 11528 eType == PDFWriter::Heading || 11529 eType == PDFWriter::H1 || 11530 eType == PDFWriter::H2 || 11531 eType == PDFWriter::H3 || 11532 eType == PDFWriter::H4 || 11533 eType == PDFWriter::H5 || 11534 eType == PDFWriter::H6 || 11535 eType == PDFWriter::List || 11536 eType == PDFWriter::ListItem || 11537 eType == PDFWriter::LILabel || 11538 eType == PDFWriter::LIBody || 11539 eType == PDFWriter::Table || 11540 eType == PDFWriter::TableRow || 11541 eType == PDFWriter::TableHeader || 11542 eType == PDFWriter::TableData || 11543 eType == PDFWriter::Span || 11544 eType == PDFWriter::Quote || 11545 eType == PDFWriter::Note || 11546 eType == PDFWriter::Reference || 11547 eType == PDFWriter::BibEntry || 11548 eType == PDFWriter::Code || 11549 eType == PDFWriter::Link ) 11550 { 11551 bInsert = true; 11552 } 11553 } 11554 break; 11555 case PDFWriter::TextDecorationType: 11556 if( eVal == PDFWriter::NONE || 11557 eVal == PDFWriter::Underline || 11558 eVal == PDFWriter::Overline || 11559 eVal == PDFWriter::LineThrough ) 11560 { 11561 // only for ILSE and BLSE 11562 if( eType == PDFWriter::Paragraph || 11563 eType == PDFWriter::Heading || 11564 eType == PDFWriter::H1 || 11565 eType == PDFWriter::H2 || 11566 eType == PDFWriter::H3 || 11567 eType == PDFWriter::H4 || 11568 eType == PDFWriter::H5 || 11569 eType == PDFWriter::H6 || 11570 eType == PDFWriter::List || 11571 eType == PDFWriter::ListItem || 11572 eType == PDFWriter::LILabel || 11573 eType == PDFWriter::LIBody || 11574 eType == PDFWriter::Table || 11575 eType == PDFWriter::TableRow || 11576 eType == PDFWriter::TableHeader || 11577 eType == PDFWriter::TableData || 11578 eType == PDFWriter::Span || 11579 eType == PDFWriter::Quote || 11580 eType == PDFWriter::Note || 11581 eType == PDFWriter::Reference || 11582 eType == PDFWriter::BibEntry || 11583 eType == PDFWriter::Code || 11584 eType == PDFWriter::Link ) 11585 { 11586 bInsert = true; 11587 } 11588 } 11589 break; 11590 case PDFWriter::ListNumbering: 11591 if( eVal == PDFWriter::NONE || 11592 eVal == PDFWriter::Disc || 11593 eVal == PDFWriter::Circle || 11594 eVal == PDFWriter::Square || 11595 eVal == PDFWriter::Decimal || 11596 eVal == PDFWriter::UpperRoman || 11597 eVal == PDFWriter::LowerRoman || 11598 eVal == PDFWriter::UpperAlpha || 11599 eVal == PDFWriter::LowerAlpha ) 11600 { 11601 if( eType == PDFWriter::List ) 11602 bInsert = true; 11603 } 11604 break; 11605 default: break; 11606 } 11607 } 11608 11609 if( bInsert ) 11610 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( eVal ); 11611 #if OSL_DEBUG_LEVEL > 1 11612 else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11613 fprintf( stderr, "rejecting setStructureAttribute( %s, %s ) on %s (%s) element\n", 11614 getAttributeTag( eAttr ), 11615 getAttributeValueTag( eVal ), 11616 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ), 11617 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() 11618 ); 11619 #endif 11620 11621 return bInsert; 11622 } 11623 11624 bool PDFWriterImpl::setStructureAttributeNumerical( enum PDFWriter::StructAttribute eAttr, sal_Int32 nValue ) 11625 { 11626 if( ! m_aContext.Tagged ) 11627 return false; 11628 11629 bool bInsert = false; 11630 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11631 { 11632 if( eAttr == PDFWriter::Language ) 11633 { 11634 m_aStructure[ m_nCurrentStructElement ].m_aLocale = MsLangId::convertLanguageToLocale( (LanguageType)nValue ); 11635 return true; 11636 } 11637 11638 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11639 switch( eAttr ) 11640 { 11641 case PDFWriter::SpaceBefore: 11642 case PDFWriter::SpaceAfter: 11643 case PDFWriter::StartIndent: 11644 case PDFWriter::EndIndent: 11645 // just for BLSE 11646 if( eType == PDFWriter::Paragraph || 11647 eType == PDFWriter::Heading || 11648 eType == PDFWriter::H1 || 11649 eType == PDFWriter::H2 || 11650 eType == PDFWriter::H3 || 11651 eType == PDFWriter::H4 || 11652 eType == PDFWriter::H5 || 11653 eType == PDFWriter::H6 || 11654 eType == PDFWriter::List || 11655 eType == PDFWriter::ListItem || 11656 eType == PDFWriter::LILabel || 11657 eType == PDFWriter::LIBody || 11658 eType == PDFWriter::Table || 11659 eType == PDFWriter::TableRow || 11660 eType == PDFWriter::TableHeader || 11661 eType == PDFWriter::TableData ) 11662 { 11663 bInsert = true; 11664 } 11665 break; 11666 case PDFWriter::TextIndent: 11667 // paragraph like BLSE and additional elements 11668 if( eType == PDFWriter::Paragraph || 11669 eType == PDFWriter::Heading || 11670 eType == PDFWriter::H1 || 11671 eType == PDFWriter::H2 || 11672 eType == PDFWriter::H3 || 11673 eType == PDFWriter::H4 || 11674 eType == PDFWriter::H5 || 11675 eType == PDFWriter::H6 || 11676 eType == PDFWriter::LILabel || 11677 eType == PDFWriter::LIBody || 11678 eType == PDFWriter::TableHeader || 11679 eType == PDFWriter::TableData ) 11680 { 11681 bInsert = true; 11682 } 11683 break; 11684 case PDFWriter::Width: 11685 case PDFWriter::Height: 11686 if( eType == PDFWriter::Figure || 11687 eType == PDFWriter::Formula || 11688 eType == PDFWriter::Form || 11689 eType == PDFWriter::Table || 11690 eType == PDFWriter::TableHeader || 11691 eType == PDFWriter::TableData ) 11692 { 11693 bInsert = true; 11694 } 11695 break; 11696 case PDFWriter::LineHeight: 11697 case PDFWriter::BaselineShift: 11698 // only for ILSE and BLSE 11699 if( eType == PDFWriter::Paragraph || 11700 eType == PDFWriter::Heading || 11701 eType == PDFWriter::H1 || 11702 eType == PDFWriter::H2 || 11703 eType == PDFWriter::H3 || 11704 eType == PDFWriter::H4 || 11705 eType == PDFWriter::H5 || 11706 eType == PDFWriter::H6 || 11707 eType == PDFWriter::List || 11708 eType == PDFWriter::ListItem || 11709 eType == PDFWriter::LILabel || 11710 eType == PDFWriter::LIBody || 11711 eType == PDFWriter::Table || 11712 eType == PDFWriter::TableRow || 11713 eType == PDFWriter::TableHeader || 11714 eType == PDFWriter::TableData || 11715 eType == PDFWriter::Span || 11716 eType == PDFWriter::Quote || 11717 eType == PDFWriter::Note || 11718 eType == PDFWriter::Reference || 11719 eType == PDFWriter::BibEntry || 11720 eType == PDFWriter::Code || 11721 eType == PDFWriter::Link ) 11722 { 11723 bInsert = true; 11724 } 11725 break; 11726 case PDFWriter::RowSpan: 11727 case PDFWriter::ColSpan: 11728 // only for table cells 11729 if( eType == PDFWriter::TableHeader || 11730 eType == PDFWriter::TableData ) 11731 { 11732 bInsert = true; 11733 } 11734 break; 11735 case PDFWriter::LinkAnnotation: 11736 if( eType == PDFWriter::Link ) 11737 bInsert = true; 11738 break; 11739 default: break; 11740 } 11741 } 11742 11743 if( bInsert ) 11744 m_aStructure[ m_nCurrentStructElement ].m_aAttributes[ eAttr ] = PDFStructureAttribute( nValue ); 11745 #if OSL_DEBUG_LEVEL > 1 11746 else if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11747 fprintf( stderr, "rejecting setStructureAttributeNumerical( %s, %d ) on %s (%s) element\n", 11748 getAttributeTag( eAttr ), 11749 (int)nValue, 11750 getStructureTag( m_aStructure[ m_nCurrentStructElement ].m_eType ), 11751 m_aStructure[ m_nCurrentStructElement ].m_aAlias.getStr() ); 11752 #endif 11753 11754 return bInsert; 11755 } 11756 11757 void PDFWriterImpl::setStructureBoundingBox( const Rectangle& rRect ) 11758 { 11759 sal_Int32 nPageNr = m_nCurrentPage; 11760 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() || !m_aContext.Tagged ) 11761 return; 11762 11763 11764 if( m_nCurrentStructElement > 0 && m_bEmitStructure ) 11765 { 11766 PDFWriter::StructElement eType = m_aStructure[ m_nCurrentStructElement ].m_eType; 11767 if( eType == PDFWriter::Figure || 11768 eType == PDFWriter::Formula || 11769 eType == PDFWriter::Form || 11770 eType == PDFWriter::Table ) 11771 { 11772 m_aStructure[ m_nCurrentStructElement ].m_aBBox = rRect; 11773 // convert to default user space now, since the mapmode may change 11774 m_aPages[nPageNr].convertRect( m_aStructure[ m_nCurrentStructElement ].m_aBBox ); 11775 } 11776 } 11777 } 11778 11779 void PDFWriterImpl::setActualText( const String& rText ) 11780 { 11781 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) 11782 { 11783 m_aStructure[ m_nCurrentStructElement ].m_aActualText = rText; 11784 } 11785 } 11786 11787 void PDFWriterImpl::setAlternateText( const String& rText ) 11788 { 11789 if( m_aContext.Tagged && m_nCurrentStructElement > 0 && m_bEmitStructure ) 11790 { 11791 m_aStructure[ m_nCurrentStructElement ].m_aAltText = rText; 11792 } 11793 } 11794 11795 void PDFWriterImpl::setAutoAdvanceTime( sal_uInt32 nSeconds, sal_Int32 nPageNr ) 11796 { 11797 if( nPageNr < 0 ) 11798 nPageNr = m_nCurrentPage; 11799 11800 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11801 return; 11802 11803 m_aPages[ nPageNr ].m_nDuration = nSeconds; 11804 } 11805 11806 void PDFWriterImpl::setPageTransition( PDFWriter::PageTransition eType, sal_uInt32 nMilliSec, sal_Int32 nPageNr ) 11807 { 11808 if( nPageNr < 0 ) 11809 nPageNr = m_nCurrentPage; 11810 11811 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11812 return; 11813 11814 m_aPages[ nPageNr ].m_eTransition = eType; 11815 m_aPages[ nPageNr ].m_nTransTime = nMilliSec; 11816 } 11817 11818 void PDFWriterImpl::ensureUniqueRadioOnValues() 11819 { 11820 // loop over radio groups 11821 for( std::map<sal_Int32,sal_Int32>::const_iterator group = m_aRadioGroupWidgets.begin(); 11822 group != m_aRadioGroupWidgets.end(); ++group ) 11823 { 11824 PDFWidget& rGroupWidget = m_aWidgets[ group->second ]; 11825 // check whether all kids have a unique OnValue 11826 std::hash_map< OUString, sal_Int32, OUStringHash > aOnValues; 11827 int nChildren = rGroupWidget.m_aKidsIndex.size(); 11828 bool bIsUnique = true; 11829 for( int nKid = 0; nKid < nChildren && bIsUnique; nKid++ ) 11830 { 11831 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11832 const OUString& rVal = m_aWidgets[nKidIndex].m_aOnValue; 11833 #if OSL_DEBUG_LEVEL > 1 11834 fprintf( stderr, "OnValue: %s\n", OUStringToOString( rVal, RTL_TEXTENCODING_UTF8 ).getStr() ); 11835 #endif 11836 if( aOnValues.find( rVal ) == aOnValues.end() ) 11837 { 11838 aOnValues[ rVal ] = 1; 11839 } 11840 else 11841 { 11842 bIsUnique = false; 11843 } 11844 } 11845 if( ! bIsUnique ) 11846 { 11847 #if OSL_DEBUG_LEVEL > 1 11848 fprintf( stderr, "enforcing unique OnValues\n" ); 11849 #endif 11850 // make unique by using ascending OnValues 11851 for( int nKid = 0; nKid < nChildren; nKid++ ) 11852 { 11853 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11854 PDFWidget& rKid = m_aWidgets[nKidIndex]; 11855 rKid.m_aOnValue = OUString::valueOf( sal_Int32(nKid+1) ); 11856 if( ! rKid.m_aValue.equalsAscii( "Off" ) ) 11857 rKid.m_aValue = rKid.m_aOnValue; 11858 } 11859 } 11860 // finally move the "Yes" appearance to the OnValue appearance 11861 for( int nKid = 0; nKid < nChildren; nKid++ ) 11862 { 11863 int nKidIndex = rGroupWidget.m_aKidsIndex[nKid]; 11864 PDFWidget& rKid = m_aWidgets[nKidIndex]; 11865 PDFAppearanceMap::iterator app_it = rKid.m_aAppearances.find( "N" ); 11866 if( app_it != rKid.m_aAppearances.end() ) 11867 { 11868 PDFAppearanceStreams::iterator stream_it = app_it->second.find( "Yes" ); 11869 if( stream_it != app_it->second.end() ) 11870 { 11871 SvMemoryStream* pStream = stream_it->second; 11872 app_it->second.erase( stream_it ); 11873 OStringBuffer aBuf( rKid.m_aOnValue.getLength()*2 ); 11874 appendName( rKid.m_aOnValue, aBuf ); 11875 (app_it->second)[ aBuf.makeStringAndClear() ] = pStream; 11876 } 11877 #if OSL_DEBUG_LEVEL > 1 11878 else 11879 fprintf( stderr, "error: RadioButton without \"Yes\" stream\n" ); 11880 #endif 11881 } 11882 // update selected radio button 11883 if( ! rKid.m_aValue.equalsAscii( "Off" ) ) 11884 { 11885 rGroupWidget.m_aValue = rKid.m_aValue; 11886 } 11887 } 11888 } 11889 } 11890 11891 sal_Int32 PDFWriterImpl::findRadioGroupWidget( const PDFWriter::RadioButtonWidget& rBtn ) 11892 { 11893 sal_Int32 nRadioGroupWidget = -1; 11894 11895 std::map< sal_Int32, sal_Int32 >::const_iterator it = m_aRadioGroupWidgets.find( rBtn.RadioGroup ); 11896 11897 if( it == m_aRadioGroupWidgets.end() ) 11898 { 11899 m_aRadioGroupWidgets[ rBtn.RadioGroup ] = nRadioGroupWidget = 11900 sal_Int32(m_aWidgets.size()); 11901 11902 // new group, insert the radiobutton 11903 m_aWidgets.push_back( PDFWidget() ); 11904 m_aWidgets.back().m_nObject = createObject(); 11905 m_aWidgets.back().m_nPage = m_nCurrentPage; 11906 m_aWidgets.back().m_eType = PDFWriter::RadioButton; 11907 m_aWidgets.back().m_nRadioGroup = rBtn.RadioGroup; 11908 m_aWidgets.back().m_nFlags |= 0x0000C000; // NoToggleToOff and Radio bits 11909 11910 createWidgetFieldName( sal_Int32(m_aWidgets.size()-1), rBtn ); 11911 } 11912 else 11913 nRadioGroupWidget = it->second; 11914 11915 return nRadioGroupWidget; 11916 } 11917 11918 sal_Int32 PDFWriterImpl::createControl( const PDFWriter::AnyWidget& rControl, sal_Int32 nPageNr ) 11919 { 11920 if( nPageNr < 0 ) 11921 nPageNr = m_nCurrentPage; 11922 11923 if( nPageNr < 0 || nPageNr >= (sal_Int32)m_aPages.size() ) 11924 return -1; 11925 11926 sal_Int32 nNewWidget = m_aWidgets.size(); 11927 m_aWidgets.push_back( PDFWidget() ); 11928 11929 m_aWidgets.back().m_nObject = createObject(); 11930 m_aWidgets.back().m_aRect = rControl.Location; 11931 m_aWidgets.back().m_nPage = nPageNr; 11932 m_aWidgets.back().m_eType = rControl.getType(); 11933 11934 sal_Int32 nRadioGroupWidget = -1; 11935 // for unknown reasons the radio buttons of a radio group must not have a 11936 // field name, else the buttons are in fact check boxes - 11937 // that is multiple buttons of the radio group can be selected 11938 if( rControl.getType() == PDFWriter::RadioButton ) 11939 nRadioGroupWidget = findRadioGroupWidget( static_cast<const PDFWriter::RadioButtonWidget&>(rControl) ); 11940 else 11941 { 11942 createWidgetFieldName( nNewWidget, rControl ); 11943 } 11944 11945 // caution: m_aWidgets must not be changed after here or rNewWidget may be invalid 11946 PDFWidget& rNewWidget = m_aWidgets[nNewWidget]; 11947 rNewWidget.m_aDescription = rControl.Description; 11948 rNewWidget.m_aText = rControl.Text; 11949 rNewWidget.m_nTextStyle = rControl.TextStyle & 11950 ( TEXT_DRAW_LEFT | TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT | TEXT_DRAW_TOP | 11951 TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM | 11952 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 11953 rNewWidget.m_nTabOrder = rControl.TabOrder; 11954 11955 // various properties are set via the flags (/Ff) property of the field dict 11956 if( rControl.ReadOnly ) 11957 rNewWidget.m_nFlags |= 1; 11958 if( rControl.getType() == PDFWriter::PushButton ) 11959 { 11960 const PDFWriter::PushButtonWidget& rBtn = static_cast<const PDFWriter::PushButtonWidget&>(rControl); 11961 if( rNewWidget.m_nTextStyle == 0 ) 11962 rNewWidget.m_nTextStyle = 11963 TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | 11964 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 11965 11966 rNewWidget.m_nFlags |= 0x00010000; 11967 if( rBtn.URL.getLength() ) 11968 rNewWidget.m_aListEntries.push_back( rBtn.URL ); 11969 rNewWidget.m_bSubmit = rBtn.Submit; 11970 rNewWidget.m_bSubmitGet = rBtn.SubmitGet; 11971 rNewWidget.m_nDest = rBtn.Dest; 11972 createDefaultPushButtonAppearance( rNewWidget, rBtn ); 11973 } 11974 else if( rControl.getType() == PDFWriter::RadioButton ) 11975 { 11976 const PDFWriter::RadioButtonWidget& rBtn = static_cast<const PDFWriter::RadioButtonWidget&>(rControl); 11977 if( rNewWidget.m_nTextStyle == 0 ) 11978 rNewWidget.m_nTextStyle = 11979 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 11980 /* PDF sees a RadioButton group as one radio button with 11981 * children which are in turn check boxes 11982 * 11983 * so we need to create a radio button on demand for a new group 11984 * and insert a checkbox for each RadioButtonWidget as its child 11985 */ 11986 rNewWidget.m_eType = PDFWriter::CheckBox; 11987 rNewWidget.m_nRadioGroup = rBtn.RadioGroup; 11988 11989 DBG_ASSERT( nRadioGroupWidget >= 0 && nRadioGroupWidget < (sal_Int32)m_aWidgets.size(), "no radio group parent" ); 11990 11991 PDFWidget& rRadioButton = m_aWidgets[nRadioGroupWidget]; 11992 rRadioButton.m_aKids.push_back( rNewWidget.m_nObject ); 11993 rRadioButton.m_aKidsIndex.push_back( nNewWidget ); 11994 rNewWidget.m_nParent = rRadioButton.m_nObject; 11995 11996 rNewWidget.m_aValue = OUString( RTL_CONSTASCII_USTRINGPARAM( "Off" ) ); 11997 rNewWidget.m_aOnValue = rBtn.OnValue; 11998 if( ! rRadioButton.m_aValue.getLength() && rBtn.Selected ) 11999 { 12000 rNewWidget.m_aValue = rNewWidget.m_aOnValue; 12001 rRadioButton.m_aValue = rNewWidget.m_aOnValue; 12002 } 12003 createDefaultRadioButtonAppearance( rNewWidget, rBtn ); 12004 12005 // union rect of radio group 12006 Rectangle aRect = rNewWidget.m_aRect; 12007 m_aPages[ nPageNr ].convertRect( aRect ); 12008 rRadioButton.m_aRect.Union( aRect ); 12009 } 12010 else if( rControl.getType() == PDFWriter::CheckBox ) 12011 { 12012 const PDFWriter::CheckBoxWidget& rBox = static_cast<const PDFWriter::CheckBoxWidget&>(rControl); 12013 if( rNewWidget.m_nTextStyle == 0 ) 12014 rNewWidget.m_nTextStyle = 12015 TEXT_DRAW_VCENTER | TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 12016 12017 rNewWidget.m_aValue = OUString::createFromAscii( rBox.Checked ? "Yes" : "Off" ); 12018 // create default appearance before m_aRect gets transformed 12019 createDefaultCheckBoxAppearance( rNewWidget, rBox ); 12020 } 12021 else if( rControl.getType() == PDFWriter::ListBox ) 12022 { 12023 if( rNewWidget.m_nTextStyle == 0 ) 12024 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER; 12025 12026 const PDFWriter::ListBoxWidget& rLstBox = static_cast<const PDFWriter::ListBoxWidget&>(rControl); 12027 rNewWidget.m_aListEntries = rLstBox.Entries; 12028 rNewWidget.m_aSelectedEntries = rLstBox.SelectedEntries; 12029 rNewWidget.m_aValue = rLstBox.Text; 12030 if( rLstBox.DropDown ) 12031 rNewWidget.m_nFlags |= 0x00020000; 12032 if( rLstBox.Sort ) 12033 rNewWidget.m_nFlags |= 0x00080000; 12034 if( rLstBox.MultiSelect && !rLstBox.DropDown && (int)m_aContext.Version > (int)PDFWriter::PDF_1_3 ) 12035 rNewWidget.m_nFlags |= 0x00200000; 12036 12037 createDefaultListBoxAppearance( rNewWidget, rLstBox ); 12038 } 12039 else if( rControl.getType() == PDFWriter::ComboBox ) 12040 { 12041 if( rNewWidget.m_nTextStyle == 0 ) 12042 rNewWidget.m_nTextStyle = TEXT_DRAW_VCENTER; 12043 12044 const PDFWriter::ComboBoxWidget& rBox = static_cast<const PDFWriter::ComboBoxWidget&>(rControl); 12045 rNewWidget.m_aValue = rBox.Text; 12046 rNewWidget.m_aListEntries = rBox.Entries; 12047 rNewWidget.m_nFlags |= 0x00060000; // combo and edit flag 12048 if( rBox.Sort ) 12049 rNewWidget.m_nFlags |= 0x00080000; 12050 12051 PDFWriter::ListBoxWidget aLBox; 12052 aLBox.Name = rBox.Name; 12053 aLBox.Description = rBox.Description; 12054 aLBox.Text = rBox.Text; 12055 aLBox.TextStyle = rBox.TextStyle; 12056 aLBox.ReadOnly = rBox.ReadOnly; 12057 aLBox.Border = rBox.Border; 12058 aLBox.BorderColor = rBox.BorderColor; 12059 aLBox.Background = rBox.Background; 12060 aLBox.BackgroundColor = rBox.BackgroundColor; 12061 aLBox.TextFont = rBox.TextFont; 12062 aLBox.TextColor = rBox.TextColor; 12063 aLBox.DropDown = true; 12064 aLBox.Sort = rBox.Sort; 12065 aLBox.MultiSelect = false; 12066 aLBox.Entries = rBox.Entries; 12067 12068 createDefaultListBoxAppearance( rNewWidget, aLBox ); 12069 } 12070 else if( rControl.getType() == PDFWriter::Edit ) 12071 { 12072 if( rNewWidget.m_nTextStyle == 0 ) 12073 rNewWidget.m_nTextStyle = TEXT_DRAW_LEFT | TEXT_DRAW_VCENTER; 12074 12075 const PDFWriter::EditWidget& rEdit = static_cast<const PDFWriter::EditWidget&>(rControl); 12076 if( rEdit.MultiLine ) 12077 { 12078 rNewWidget.m_nFlags |= 0x00001000; 12079 rNewWidget.m_nTextStyle |= TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK; 12080 } 12081 if( rEdit.Password ) 12082 rNewWidget.m_nFlags |= 0x00002000; 12083 if( rEdit.FileSelect && m_aContext.Version > PDFWriter::PDF_1_3 ) 12084 rNewWidget.m_nFlags |= 0x00100000; 12085 rNewWidget.m_nMaxLen = rEdit.MaxLen; 12086 rNewWidget.m_aValue = rEdit.Text; 12087 12088 createDefaultEditAppearance( rNewWidget, rEdit ); 12089 } 12090 12091 // convert to default user space now, since the mapmode may change 12092 // note: create default appearances before m_aRect gets transformed 12093 m_aPages[ nPageNr ].convertRect( rNewWidget.m_aRect ); 12094 12095 // insert widget to page's annotation list 12096 m_aPages[ nPageNr ].m_aAnnotations.push_back( rNewWidget.m_nObject ); 12097 12098 // mark page as having widgets 12099 m_aPages[ nPageNr ].m_bHasWidgets = true; 12100 12101 return nNewWidget; 12102 } 12103 12104 void PDFWriterImpl::beginControlAppearance( sal_Int32 nControl ) 12105 { 12106 if( nControl < 0 || nControl >= (sal_Int32)m_aWidgets.size() ) 12107 return; 12108 12109 PDFWidget& rWidget = m_aWidgets[ nControl ]; 12110 m_nCurrentControl = nControl; 12111 12112 SvMemoryStream* pControlStream = new SvMemoryStream( 1024, 1024 ); 12113 // back conversion of control rect to current MapMode; necessary because 12114 // MapMode between createControl and beginControlAppearance 12115 // could have changed; therefore the widget rectangle is 12116 // already converted 12117 Rectangle aBack( Point( rWidget.m_aRect.Left(), pointToPixel(m_aPages[m_nCurrentPage].getHeight()) - rWidget.m_aRect.Top() - rWidget.m_aRect.GetHeight() ), 12118 rWidget.m_aRect.GetSize() ); 12119 aBack = lcl_convert( m_aMapMode, 12120 m_aGraphicsStack.front().m_aMapMode, 12121 getReferenceDevice(), 12122 aBack ); 12123 beginRedirect( pControlStream, aBack ); 12124 writeBuffer( "/Tx BMC\n", 8 ); 12125 } 12126 12127 bool PDFWriterImpl::endControlAppearance( PDFWriter::WidgetState eState ) 12128 { 12129 bool bRet = false; 12130 if( ! m_aOutputStreams.empty() ) 12131 writeBuffer( "\nEMC\n", 5 ); 12132 SvMemoryStream* pAppearance = static_cast<SvMemoryStream*>(endRedirect()); 12133 if( pAppearance && m_nCurrentControl >= 0 && m_nCurrentControl < (sal_Int32)m_aWidgets.size() ) 12134 { 12135 PDFWidget& rWidget = m_aWidgets[ m_nCurrentControl ]; 12136 OString aState, aStyle; 12137 switch( rWidget.m_eType ) 12138 { 12139 case PDFWriter::PushButton: 12140 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12141 { 12142 aState = (eState == PDFWriter::Up) ? "N" : "D"; 12143 aStyle = "Standard"; 12144 } 12145 break; 12146 case PDFWriter::CheckBox: 12147 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12148 { 12149 aState = "N"; 12150 aStyle = (eState == PDFWriter::Up) ? "Off" : "Yes"; 12151 /* cf PDFReference 3rd ed. V1.4 p539: 12152 recommended name for on state is "Yes", 12153 recommended name for off state is "Off" 12154 */ 12155 } 12156 break; 12157 case PDFWriter::RadioButton: 12158 if( eState == PDFWriter::Up || eState == PDFWriter::Down ) 12159 { 12160 aState = "N"; 12161 if( eState == PDFWriter::Up ) 12162 aStyle = "Off"; 12163 else 12164 { 12165 OStringBuffer aBuf( rWidget.m_aOnValue.getLength()*2 ); 12166 appendName( rWidget.m_aOnValue, aBuf ); 12167 aStyle = aBuf.makeStringAndClear(); 12168 } 12169 } 12170 break; 12171 case PDFWriter::Edit: 12172 aState = "N"; 12173 aStyle = "Standard"; 12174 break; 12175 case PDFWriter::ListBox: 12176 case PDFWriter::ComboBox: 12177 case PDFWriter::Hierarchy: 12178 break; 12179 } 12180 if( aState.getLength() && aStyle.getLength() ) 12181 { 12182 // delete eventual existing stream 12183 PDFAppearanceStreams::iterator it = 12184 rWidget.m_aAppearances[ aState ].find( aStyle ); 12185 if( it != rWidget.m_aAppearances[ aState ].end() ) 12186 delete it->second; 12187 rWidget.m_aAppearances[ aState ][ aStyle ] = pAppearance; 12188 bRet = true; 12189 } 12190 } 12191 12192 if( ! bRet ) 12193 delete pAppearance; 12194 12195 m_nCurrentControl = -1; 12196 12197 return bRet; 12198 } 12199 12200 void PDFWriterImpl::addStream( const String& rMimeType, PDFOutputStream* pStream, bool bCompress ) 12201 { 12202 if( pStream ) 12203 { 12204 m_aAdditionalStreams.push_back( PDFAddStream() ); 12205 PDFAddStream& rStream = m_aAdditionalStreams.back(); 12206 rStream.m_aMimeType = rMimeType.Len() 12207 ? OUString( rMimeType ) 12208 : OUString( RTL_CONSTASCII_USTRINGPARAM( "application/octet-stream" ) ); 12209 rStream.m_pStream = pStream; 12210 rStream.m_bCompress = bCompress; 12211 } 12212 } 12213 12214 12215 12216